diff options
Diffstat (limited to 'src/game')
289 files changed, 46287 insertions, 25131 deletions
diff --git a/src/game/AccountMgr.cpp b/src/game/AccountMgr.cpp index 6d27df2cb73..a39203f3755 100644 --- a/src/game/AccountMgr.cpp +++ b/src/game/AccountMgr.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,11 +25,7 @@ #include "Policies/SingletonImp.h" #include "Util.h" -#ifdef DO_POSTGRESQL -extern DatabasePostgre LoginDatabase; -#else -extern DatabaseMysql LoginDatabase; -#endif +extern DatabaseType LoginDatabase; INSTANTIATE_SINGLETON_1(AccountMgr); diff --git a/src/game/AccountMgr.h b/src/game/AccountMgr.h index febd5c6216b..adb1bdfa6b9 100644 --- a/src/game/AccountMgr.h +++ b/src/game/AccountMgr.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/AchievementMgr.cpp b/src/game/AchievementMgr.cpp new file mode 100644 index 00000000000..26df402e5aa --- /dev/null +++ b/src/game/AchievementMgr.cpp @@ -0,0 +1,1803 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "AchievementMgr.h" +#include "Common.h" +#include "Player.h" +#include "WorldPacket.h" +#include "DBCEnums.h" +#include "GameEventMgr.h" +#include "ObjectMgr.h" +#include "Guild.h" +#include "Database/DatabaseEnv.h" +#include "World.h" +#include "SpellMgr.h" +#include "ArenaTeam.h" +#include "ProgressBar.h" +#include "GridNotifiersImpl.h" +#include "CellImpl.h" +#include "Language.h" + +#include "Policies/SingletonImp.h" + +INSTANTIATE_SINGLETON_1(AchievementGlobalMgr); + +namespace MaNGOS +{ + class AchievementChatBuilder + { + public: + AchievementChatBuilder(Player const& pl, ChatMsg msgtype, int32 textId, uint32 ach_id) + : i_player(pl), i_msgtype(msgtype), i_textId(textId), i_achievementId(ach_id) {} + void operator()(WorldPacket& data, int32 loc_idx) + { + char const* text = objmgr.GetMangosString(i_textId,loc_idx); + + data << uint8(i_msgtype); + data << uint32(LANG_UNIVERSAL); + data << uint64(i_player.GetGUID()); + data << uint32(5); + data << uint64(i_player.GetGUID()); + data << uint32(strlen(text)+1); + data << text; + data << uint8(0); + data << uint32(i_achievementId); + } + + private: + Player const& i_player; + ChatMsg i_msgtype; + int32 i_textId; + uint32 i_achievementId; + }; +} // namespace MaNGOS + + +bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) +{ + if(dataType >= MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE) + { + sLog.outErrorDb( "Table `achievement_criteria_data` for criteria (Entry: %u) have wrong data type (%u), ignore.", criteria->ID,dataType); + return false; + } + + switch(criteria->requiredType) + { + case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: + case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: + break; + default: + sLog.outErrorDb( "Table `achievement_criteria_data` have data for not supported criteria type (Entry: %u Type: %u), ignore.", criteria->ID, criteria->requiredType); + return false; + } + + switch(dataType) + { + case ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE: + return true; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE: + if(!creature.id || !objmgr.GetCreatureTemplate(creature.id)) + { + sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE (%u) have not existed creature id in value1 (%u), ignore.", + criteria->ID, criteria->requiredType,dataType,creature.id); + return false; + } + return true; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE: + if(!classRace.class_id && !classRace.race_id) + { + sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_CLASS_RACE (%u) must have not 0 in one from value fields, ignore.", + criteria->ID, criteria->requiredType,dataType); + return false; + } + if(classRace.class_id && ((1 << (classRace.class_id-1)) & CLASSMASK_ALL_PLAYABLE)==0) + { + sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE (%u) have not existed class in value1 (%u), ignore.", + criteria->ID, criteria->requiredType,dataType,classRace.class_id); + return false; + } + if(classRace.race_id && ((1 << (classRace.race_id-1)) & RACEMASK_ALL_PLAYABLE)==0) + { + sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE (%u) have not existed race in value2 (%u), ignore.", + criteria->ID, criteria->requiredType,dataType,classRace.race_id); + return false; + } + return true; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH: + if(health.percent < 1 || health.percent > 100) + { + sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_LESS_HEALTH (%u) have wrong percent value in value1 (%u), ignore.", + criteria->ID, criteria->requiredType,dataType,health.percent); + return false; + } + return true; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD: + return true; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA: + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA: + { + SpellEntry const* spellEntry = sSpellStore.LookupEntry(aura.spell_id); + if(!spellEntry) + { + sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) have wrong spell id in value1 (%u), ignore.", + criteria->ID, criteria->requiredType,(dataType==ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA?"ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA":"ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"),dataType,aura.spell_id); + return false; + } + if(aura.effect_idx >= 3) + { + sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) have wrong spell effect index in value2 (%u), ignore.", + criteria->ID, criteria->requiredType,(dataType==ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA?"ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA":"ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"),dataType,aura.effect_idx); + return false; + } + if(!spellEntry->EffectApplyAuraName[aura.effect_idx]) + { + sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) have non-aura spell effect (ID: %u Effect: %u), ignore.", + criteria->ID, criteria->requiredType,(dataType==ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA?"ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA":"ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"),dataType,aura.spell_id,aura.effect_idx); + return false; + } + return true; + } + case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA: + if(!GetAreaEntryByAreaID(area.id)) + { + sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA (%u) have wrong area id in value1 (%u), ignore.", + criteria->ID, criteria->requiredType,dataType,area.id); + return false; + } + return true; + default: + sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) have data for not supported data type (%u), ignore.", criteria->ID, criteria->requiredType,dataType); + return false; + } + return false; +} + +bool AchievementCriteriaData::Meets(Player const* source, Unit const* target) const +{ + switch(dataType) + { + case ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE: + return true; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE: + if (!target || target->GetTypeId()!=TYPEID_UNIT) + return false; + if (target->GetEntry() != creature.id) + return false; + return true; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE: + if (!target || target->GetTypeId()!=TYPEID_PLAYER) + return false; + if(classRace.class_id && classRace.class_id != ((Player*)target)->getClass()) + return false; + if(classRace.race_id && classRace.race_id != ((Player*)target)->getRace()) + return false; + return true; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH: + if (!target || target->GetTypeId()!=TYPEID_PLAYER) + return false; + return target->GetHealth()*100 <= health.percent*target->GetMaxHealth(); + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD: + return target && target->GetTypeId() == TYPEID_PLAYER && !target->isAlive() && ((Player*)target)->GetDeathTimer() != 0; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA: + return source->HasAura(aura.spell_id,aura.effect_idx); + case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA: + { + uint32 zone_id,area_id; + source->GetZoneAndAreaId(zone_id,area_id); + return area.id==zone_id || area.id==area_id; + } + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA: + return target && target->HasAura(aura.spell_id,aura.effect_idx); + } + + return false; +} + +bool AchievementCriteriaDataSet::Meets(Player const* source, Unit const* target) const +{ + for(Storage::const_iterator itr = storage.begin(); itr != storage.end(); ++itr) + if(!itr->Meets(source,target)) + return false; + + return true; +} + +AchievementMgr::AchievementMgr(Player *player) +{ + m_player = player; +} + +AchievementMgr::~AchievementMgr() +{ +} + +void AchievementMgr::Reset() +{ + for(CompletedAchievementMap::iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter) + { + WorldPacket data(SMSG_ACHIEVEMENT_DELETED,4); + data << uint32(iter->first); + m_player->SendDirectMessage(&data); + } + + for(CriteriaProgressMap::iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter) + { + WorldPacket data(SMSG_CRITERIA_DELETED,4); + data << uint32(iter->first); + m_player->SendDirectMessage(&data); + } + + m_completedAchievements.clear(); + m_criteriaProgress.clear(); + DeleteFromDB(m_player->GetGUIDLow()); + + // re-fill data + CheckAllAchievementCriteria(); +} + +void AchievementMgr::DeleteFromDB(uint32 lowguid) +{ + CharacterDatabase.BeginTransaction (); + CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE guid = %u",lowguid); + CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE guid = %u",lowguid); + CharacterDatabase.CommitTransaction (); +} + +void AchievementMgr::SaveToDB() +{ + if(!m_completedAchievements.empty()) + { + bool need_execute = false; + std::ostringstream ssdel; + std::ostringstream ssins; + for(CompletedAchievementMap::iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter) + { + if(!iter->second.changed) + continue; + + /// first new/changed record prefix + if(!need_execute) + { + ssdel << "DELETE FROM character_achievement WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND achievement IN ("; + ssins << "INSERT INTO character_achievement (guid, achievement, date) VALUES "; + need_execute = true; + } + /// next new/changed record prefix + else + { + ssdel << ", "; + ssins << ", "; + } + + // new/changed record data + ssdel << iter->first; + ssins << "("<<GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << uint64(iter->second.date) << ")"; + + /// mark as saved in db + iter->second.changed = false; + } + + if(need_execute) + ssdel << ")"; + + if(need_execute) + { + CharacterDatabase.Execute( ssdel.str().c_str() ); + CharacterDatabase.Execute( ssins.str().c_str() ); + } + } + + if(!m_criteriaProgress.empty()) + { + /// prepare deleting and insert + bool need_execute_del = false; + bool need_execute_ins = false; + std::ostringstream ssdel; + std::ostringstream ssins; + for(CriteriaProgressMap::iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter) + { + if(!iter->second.changed) + continue; + + // deleted data (including 0 progress state) + { + /// first new/changed record prefix (for any counter value) + if(!need_execute_del) + { + ssdel << "DELETE FROM character_achievement_progress WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND criteria IN ("; + need_execute_del = true; + } + /// next new/changed record prefix + else + ssdel << ", "; + + // new/changed record data + ssdel << iter->first; + } + + // store data only for real progress + if(iter->second.counter != 0) + { + /// first new/changed record prefix + if(!need_execute_ins) + { + ssins << "INSERT INTO character_achievement_progress (guid, criteria, counter, date) VALUES "; + need_execute_ins = true; + } + /// next new/changed record prefix + else + ssins << ", "; + + // new/changed record data + ssins << "(" << GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << iter->second.counter << ", " << iter->second.date << ")"; + } + + /// mark as updated in db + iter->second.changed = false; + } + + if(need_execute_del) // DELETE ... IN (.... _)_ + ssdel << ")"; + + if(need_execute_del || need_execute_ins) + { + if(need_execute_del) + CharacterDatabase.Execute( ssdel.str().c_str() ); + if(need_execute_ins) + CharacterDatabase.Execute( ssins.str().c_str() ); + } + } +} + +void AchievementMgr::LoadFromDB(QueryResult *achievementResult, QueryResult *criteriaResult) +{ + if(achievementResult) + { + do + { + Field *fields = achievementResult->Fetch(); + CompletedAchievementData& ca = m_completedAchievements[fields[0].GetUInt32()]; + ca.date = time_t(fields[1].GetUInt64()); + ca.changed = false; + } while(achievementResult->NextRow()); + delete achievementResult; + } + + if(criteriaResult) + { + do + { + Field *fields = criteriaResult->Fetch(); + + uint32 id = fields[0].GetUInt32(); + uint32 counter = fields[1].GetUInt32(); + time_t date = time_t(fields[2].GetUInt64()); + + AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(id); + if (!criteria || (criteria->timeLimit && time_t(date + criteria->timeLimit) < time(NULL))) + continue; + + CriteriaProgress& progress = m_criteriaProgress[id]; + progress.counter = counter; + progress.date = date; + progress.changed = false; + } while(criteriaResult->NextRow()); + delete criteriaResult; + } + +} + +void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement) +{ + if(GetPlayer()->GetSession()->PlayerLoading()) + return; + + #ifdef MANGOS_DEBUG + if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0) + sLog.outDebug("AchievementMgr::SendAchievementEarned(%u)", achievement->ID); + #endif + + if(Guild* guild = objmgr.GetGuildById(GetPlayer()->GetGuildId())) + { + MaNGOS::AchievementChatBuilder say_builder(*GetPlayer(), CHAT_MSG_GUILD_ACHIEVEMENT, LANG_ACHIEVEMENT_EARNED,achievement->ID); + MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> say_do(say_builder); + guild->BroadcastWorker(say_do,GetPlayer()); + } + + if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_KILL|ACHIEVEMENT_FLAG_REALM_FIRST_REACH)) + { + // broadcast realm first reached + WorldPacket data(SMSG_SERVER_FIRST_ACHIEVEMENT, strlen(GetPlayer()->GetName())+1+8+4+4); + data << GetPlayer()->GetName(); + data << uint64(GetPlayer()->GetGUID()); + data << uint32(achievement->ID); + data << uint32(0); // 1=link supplied string as player name, 0=display plain string + sWorld.SendGlobalMessage(&data); + } + else + { + CellPair p = MaNGOS::ComputeCellPair(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY()); + + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + MaNGOS::AchievementChatBuilder say_builder(*GetPlayer(), CHAT_MSG_ACHIEVEMENT, LANG_ACHIEVEMENT_EARNED,achievement->ID); + MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> say_do(say_builder); + MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> > say_worker(GetPlayer(),sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY),say_do); + TypeContainerVisitor<MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> >, WorldTypeMapContainer > message(say_worker); + CellLock<GridReadGuard> cell_lock(cell, p); + cell_lock->Visit(cell_lock, message, *GetPlayer()->GetMap()); + } + + WorldPacket data(SMSG_ACHIEVEMENT_EARNED, 8+4+8); + data.append(GetPlayer()->GetPackGUID()); + data << uint32(achievement->ID); + data << uint32(secsToTimeBitFields(time(NULL))); + data << uint32(0); + GetPlayer()->SendMessageToSetInRange(&data, sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY), true); +} + +void AchievementMgr::SendCriteriaUpdate(uint32 id, CriteriaProgress const* progress) +{ + WorldPacket data(SMSG_CRITERIA_UPDATE, 8+4+8); + data << uint32(id); + + // the counter is packed like a packed Guid + data.appendPackGUID(progress->counter); + + data.append(GetPlayer()->GetPackGUID()); + data << uint32(0); + data << uint32(secsToTimeBitFields(progress->date)); + data << uint32(0); // timer 1 + data << uint32(0); // timer 2 + GetPlayer()->SendDirectMessage(&data); +} + +/** + * called at player login. The player might have fulfilled some achievements when the achievement system wasn't working yet + */ +void AchievementMgr::CheckAllAchievementCriteria() +{ + // suppress sending packets + for(uint32 i=0; i<ACHIEVEMENT_CRITERIA_TYPE_TOTAL; ++i) + UpdateAchievementCriteria(AchievementCriteriaTypes(i)); +} + +static const uint32 achievIdByArenaSlot[MAX_ARENA_SLOT] = { 1057, 1107, 1108 }; +static const uint32 achievIdForDangeon[][4] = +{ + // ach_cr_id,is_dungeon,is_raid,is_heroic_dungeon + { 321, true, true, true }, + { 916, false, true, false }, + { 917, false, true, false }, + { 918, true, false, false }, + { 2219, false, false, true }, + { 0, false, false, false } +}; + +/** + * this function will be called whenever the user might have done a criteria relevant action + */ +void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1, uint32 miscvalue2, Unit *unit, uint32 time) +{ + if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0) + 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) + { + AchievementCriteriaEntry const *achievementCriteria = (*i); + + if (achievementCriteria->groupFlag & ACHIEVEMENT_CRITERIA_GROUP_NOT_IN_GROUP && GetPlayer()->GetGroup()) + continue; + + AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement); + if (!achievement) + continue; + + if ((achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_HORDE && GetPlayer()->GetTeam() != HORDE) || + (achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_ALLIANCE && GetPlayer()->GetTeam() != ALLIANCE)) + continue; + + // don't update already completed criteria + if (IsCompletedCriteria(achievementCriteria,achievement)) + continue; + + switch (type) + { + // std. case: increment at 1 + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + // std case: increment at miscvalue1 + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS: + case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL: + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE); + break; + // std case: high value at miscvalue1 + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD: /* FIXME: for online player only currently */ + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_HIGHEST); + break; + + // specialized cases + + case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + if(achievementCriteria->kill_creature.creatureID != miscvalue1) + continue; + + // LOT achievement->ID required special custom checks + switch(achievement->ID) + { + // Just heroic + case 489: case 490: case 491: case 492: case 493: case 494: case 495: + case 496: case 497: case 498: case 499: case 500: case 563: case 565: + case 567: case 569: case 573: case 575: case 577: case 623: case 625: + case 667: case 668: case 669: case 670: case 671: case 672: case 673: + case 674: case 675: case 676: case 677: case 678: case 679: case 680: + case 681: case 682: case 1367: case 1368: case 1378: case 1379: + case 1380: case 1381: case 1382: case 1383: case 1384: case 1385: + case 1386: case 1387: case 1388: case 1389: case 1390: case 1393: + case 1394: case 1400: case 1402: case 1504: case 1505: case 1506: + case 1507: case 1508: case 1509: case 1510: case 1511: case 1512: + case 1513: case 1514: case 1515: case 1721: case 1754: case 1756: + case 1768: case 1817: case 1865: + if(GetPlayer()->GetDifficulty()!=DIFFICULTY_HEROIC) + continue; + break; + // Heroic + other + case 579: case 1296: case 1297: case 1816: case 1834: case 1857: case 1859: + case 1860: case 1861: case 1862: case 1864: case 1866: case 1867: case 1868: + case 1870: case 1871: case 1872: case 1873: case 1875: case 1877: case 1919: + case 2036: case 2037: case 2038: case 2039: case 2040: case 2041: case 2042: + case 2043: case 2044: case 2045: case 2046: case 2048: case 2052: case 2053: + case 2054: case 2056: case 2057: case 2058: case 2139: case 2140: case 2147: + case 2149: case 2150: case 2151: case 2152: case 2154: case 2155: case 2156: + case 2157: case 2179: case 2181: case 2183: case 2185: case 2186: + if(GetPlayer()->GetDifficulty()!=DIFFICULTY_HEROIC) + continue; + // FIX ME: mark as fail always until implement + continue; + // Normal + other + case 578: case 624: case 1790: case 1856: case 1858: case 1869: case 1874: + case 1996: case 1997: case 2047: case 2049: case 2050: case 2051: case 2146: + case 2148: case 2153: case 2178: case 2180: case 2182: case 2184: case 2187: + if(GetPlayer()->GetDifficulty()!=DIFFICULTY_NORMAL) + continue; + // FIX ME: mark as fail always until implement + continue; + // Just Normal + default: + if(GetPlayer()->GetDifficulty()!=DIFFICULTY_NORMAL) + continue; + break; + }; + + SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL: + SetCriteriaProgress(achievementCriteria, GetPlayer()->getLevel()); + break; + case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL: + // update at loading or specific skill update + if(miscvalue1 && miscvalue1 != achievementCriteria->reach_skill_level.skillID) + continue; + if(uint32 skillvalue = GetPlayer()->GetBaseSkillValue(achievementCriteria->reach_skill_level.skillID)) + SetCriteriaProgress(achievementCriteria, skillvalue); + break; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL: + // update at loading or specific skill update + if(miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_level.skillID) + continue; + if(uint32 maxSkillvalue = GetPlayer()->GetPureMaxSkillValue(achievementCriteria->learn_skill_level.skillID)) + SetCriteriaProgress(achievementCriteria, maxSkillvalue); + break; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT: + if(m_completedAchievements.find(achievementCriteria->complete_achievement.linkedAchievement) != m_completedAchievements.end()) + SetCriteriaProgress(achievementCriteria, 1); + break; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT: + { + uint32 counter =0; + for(QuestStatusMap::iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); itr++) + if(itr->second.m_rewarded) + counter++; + SetCriteriaProgress(achievementCriteria, counter); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE: + { + // speedup for non-login case + if(miscvalue1 && miscvalue1 != achievementCriteria->complete_quests_in_zone.zoneID) + continue; + + uint32 counter =0; + for(QuestStatusMap::iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); itr++) + { + Quest const* quest = objmgr.GetQuestTemplate(itr->first); + if(itr->second.m_rewarded && quest->GetZoneOrSort() >= 0 && uint32(quest->GetZoneOrSort()) == achievementCriteria->complete_quests_in_zone.zoneID) + counter++; + } + SetCriteriaProgress(achievementCriteria, counter); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + if(GetPlayer()->GetMapId() != achievementCriteria->complete_battleground.mapID) + continue; + SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + if(GetPlayer()->GetMapId() != achievementCriteria->death_at_map.mapID) + continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_DEATH: + { + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + // skip wrong arena achievements, if not achievIdByArenaSlot then normal total death counter + bool notfit = false; + for(int i = 0; i < MAX_ARENA_SLOT; ++i) + { + if(achievIdByArenaSlot[i] == achievement->ID) + { + BattleGround* bg = GetPlayer()->GetBattleGround(); + if(!bg || !bg->isArena() || ArenaTeam::GetSlotByType(bg->GetArenaType()) != i) + notfit = true; + + break; + } + } + if(notfit) + continue; + + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON: + { + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + + Map const* map = GetPlayer()->GetMap(); + if(!map->IsDungeon()) + continue; + + // search case + bool found = false; + for(int i = 0; achievIdForDangeon[i][0]; ++i) + { + if(achievIdForDangeon[i][0] == achievement->ID) + { + if(map->IsRaid()) + { + // if raid accepted (ignore difficulty) + if(!achievIdForDangeon[i][2]) + break; // for + } + else if(GetPlayer()->GetDifficulty()==DIFFICULTY_NORMAL) + { + // dungeon in normal mode accepted + if(!achievIdForDangeon[i][1]) + break; // for + } + else + { + // dungeon in heroic mode accepted + if(!achievIdForDangeon[i][3]) + break; // for + } + + found = true; + break; // for + } + } + if(!found) + continue; + + //FIXME: work only for instances where max==min for players + if(((InstanceMap*)map)->GetMaxPlayers() != achievementCriteria->death_in_dungeon.manLimit) + continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + + } + case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + if(miscvalue1 != achievementCriteria->killed_by_creature.creatureEntry) + continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + + // if team check required: must kill by opposition faction + if(achievement->ID==318 && miscvalue2==GetPlayer()->GetTeam()) + continue; + + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: + { + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + if(achievement->ID == 1260) + { + if(Player::GetDrunkenstateByValue(GetPlayer()->GetDrunkValue()) != DRUNKEN_SMASHED) + continue; + if(!IsHolidayActive(HOLIDAY_BREWFEST)) + continue; + } + // miscvalue1 is the ingame fallheight*100 as stored in dbc + SetCriteriaProgress(achievementCriteria, miscvalue1); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + if(miscvalue2 != achievementCriteria->death_from.type) + continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: + if(GetPlayer()->GetQuestRewardStatus(achievementCriteria->complete_quest.questID)) + SetCriteriaProgress(achievementCriteria, 1); + break; + case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: + case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: + if (!miscvalue1 || miscvalue1 != achievementCriteria->be_spell_target.spellID) + continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: + if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID) + continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: + // spell always provide and at login spell learning. + if(miscvalue1 && miscvalue1!=achievementCriteria->learn_spell.spellID) + continue; + if(GetPlayer()->HasSpell(miscvalue1)) + SetCriteriaProgress(achievementCriteria, 1); + break; + case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: + // speedup for non-login case + if(miscvalue1 && achievementCriteria->own_item.itemID != miscvalue1) + continue; + SetCriteriaProgress(achievementCriteria, GetPlayer()->GetItemCount(achievementCriteria->own_item.itemID, true)); + break; + case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + if(achievementCriteria->use_item.itemID != miscvalue1) + continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM: + // You _have_ to loot that item, just owning it when logging in does _not_ count! + if(!miscvalue1) + continue; + if(miscvalue1 != achievementCriteria->own_item.itemID) + continue; + SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA: + { + WorldMapOverlayEntry const* worldOverlayEntry = sWorldMapOverlayStore.LookupEntry(achievementCriteria->explore_area.areaReference); + if(!worldOverlayEntry) + break; + + bool matchFound = false; + for (int i = 0; i < 3; ++i) + { + int32 exploreFlag = GetAreaFlagByAreaID(worldOverlayEntry->areatableID[i]); + if(exploreFlag < 0) + break; + + uint32 playerIndexOffset = uint32(exploreFlag) / 32; + uint32 mask = 1<< (uint32(exploreFlag) % 32); + + if(GetPlayer()->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + playerIndexOffset) & mask) + { + matchFound = true; + break; + } + } + + if(matchFound) + SetCriteriaProgress(achievementCriteria, 1); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT: + SetCriteriaProgress(achievementCriteria, GetPlayer()->GetByteValue(PLAYER_BYTES_2, 2)+1); + break; + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION: + { + // skip faction check only at loading + if (miscvalue1 && miscvalue1 != achievementCriteria->gain_reputation.factionID) + continue; + + int32 reputation = GetPlayer()->GetReputationMgr().GetReputation(achievementCriteria->gain_reputation.factionID); + if (reputation > 0) + SetCriteriaProgress(achievementCriteria, reputation); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION: + { + SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetExaltedFactionCount()); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP: + { + // skip for login case + if(!miscvalue1) + continue; + SetCriteriaProgress(achievementCriteria, 1); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: + { + // miscvalue1 = itemid + // miscvalue2 = diced value + if(!miscvalue1) + continue; + if(miscvalue2 != achievementCriteria->roll_greed_on_loot.rollValue) + continue; + ItemPrototype const *pProto = objmgr.GetItemPrototype( miscvalue1 ); + + uint32 requiredItemLevel = 0; + if (achievementCriteria->ID == 2412 || achievementCriteria->ID == 2358) + requiredItemLevel = 185; + + if(!pProto || pProto->ItemLevel <requiredItemLevel) + continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: + { + // miscvalue1 = emote + if(!miscvalue1) + continue; + if(miscvalue1 != achievementCriteria->do_emote.emoteID) + continue; + if(achievementCriteria->do_emote.count) + { + // those requirements couldn't be found in the dbc + AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); + if(!data) + continue; + + if(!data->Meets(GetPlayer(),unit)) + continue; + } + + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM: + // miscvalue1 = item_id + if(!miscvalue1) + continue; + if(miscvalue1 != achievementCriteria->equip_item.itemID) + continue; + + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT: + // miscvalue1 = go entry + if(!miscvalue1) + continue; + if(miscvalue1 != achievementCriteria->use_gameobject.goEntry) + continue; + + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: + { + // spell always provide and at login spell learning. + if(!miscvalue1) + continue; + // rescan only when change possible + SkillLineAbilityMap::const_iterator skillIter0 = spellmgr.GetBeginSkillLineAbilityMap(miscvalue1); + if(skillIter0 == spellmgr.GetEndSkillLineAbilityMap(miscvalue1)) + continue; + if(skillIter0->second->skillId != achievementCriteria->learn_skilline_spell.skillLine) + continue; + + uint32 spellCount = 0; + for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin(); + spellIter != GetPlayer()->GetSpellMap().end(); + ++spellIter) + { + for(SkillLineAbilityMap::const_iterator skillIter = spellmgr.GetBeginSkillLineAbilityMap(spellIter->first); + skillIter != spellmgr.GetEndSkillLineAbilityMap(spellIter->first); + ++skillIter) + { + if(skillIter->second->skillId == achievementCriteria->learn_skilline_spell.skillLine) + spellCount++; + } + } + SetCriteriaProgress(achievementCriteria, spellCount); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION: + SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetReveredFactionCount()); + break; + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION: + SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetHonoredFactionCount()); + break; + case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS: + SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetVisibleFactionCount()); + break; + case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: + { + if (!miscvalue1) + continue; + + if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID) + continue; + + // those requirements couldn't be found in the dbc + AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); + if(!data) + continue; + + if(!data->Meets(GetPlayer(),unit)) + continue; + + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + } + // std case: not exist in DBC, not triggered in code as result + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_POWER: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_STAT: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_RATING: + break; + // FIXME: not triggered in code as result, need to implement + case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY: + case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID: + case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE: + case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA: + case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA: + case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA: + case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL: + case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING: + case ACHIEVEMENT_CRITERIA_TYPE_REACH_TEAM_RATING: + case ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK: + case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM: + case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS: + case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE: + case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: + case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS: + case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS: + case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS: + case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL: + case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT: + case ACHIEVEMENT_CRITERIA_TYPE_EARNED_PVP_TITLE: + case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL: + case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS: + case ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION: + case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED: + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM: + case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM: + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED: + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_DEALT: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_RECEIVED: + case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CASTED: + case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_HEALING_RECEIVED: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED: + case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED: + case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN: + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE: + case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL: + case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS: + case ACHIEVEMENT_CRITERIA_TYPE_TOTAL: + break; // Not implemented yet :( + } + if(IsCompletedCriteria(achievementCriteria,achievement)) + CompletedCriteria(achievementCriteria,achievement); + + // check again the completeness for SUMM and REQ COUNT achievements, + // as they don't depend on the completed criteria but on the sum of the progress of each individual criteria + if (achievement->flags & ACHIEVEMENT_FLAG_SUMM) + { + if (IsCompletedAchievement(achievement)) + CompletedAchievement(achievement); + } + + if(AchievementEntryList const* achRefList = achievementmgr.GetAchievementByReferencedId(achievement->ID)) + { + for(AchievementEntryList::const_iterator itr = achRefList->begin(); itr != achRefList->end(); ++itr) + if(IsCompletedAchievement(*itr)) + CompletedAchievement(*itr); + } + } +} + +static const uint32 achievIdByClass[MAX_CLASSES] = { 0, 459, 465 , 462, 458, 464, 461, 467, 460, 463, 0, 466 }; +static const uint32 achievIdByRace[MAX_RACES] = { 0, 1408, 1410, 1407, 1409, 1413, 1411, 1404, 1412, 0, 1405, 1406 }; + +bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement) +{ + // counter can never complete + if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER) + return false; + + if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL)) + { + // someone on this realm has already completed that achievement + if(achievementmgr.IsRealmCompleted(achievement)) + return false; + } + + CriteriaProgressMap::const_iterator itr = m_criteriaProgress.find(achievementCriteria->ID); + if(itr == m_criteriaProgress.end()) + return false; + + CriteriaProgress const* progress = &itr->second; + + switch(achievementCriteria->requiredType) + { + case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: + return progress->counter >= achievementCriteria->kill_creature.creatureCount; + case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL: + { + // skip wrong class achievements + for(int i = 1; i < MAX_CLASSES; ++i) + if(achievIdByClass[i] == achievement->ID && i != GetPlayer()->getClass()) + return false; + + // skip wrong race achievements + for(int i = 1; i < MAX_RACES; ++i) + if(achievIdByRace[i] == achievement->ID && i != GetPlayer()->getRace()) + return false; + + // appropriate class/race or not class/race specific + return progress->counter >= achievementCriteria->reach_level.level; + } + case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL: + return progress->counter >= achievementCriteria->reach_skill_level.skillLevel; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL: + return progress->counter >= (achievementCriteria->learn_skill_level.skillLevel * 75); + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT: + return progress->counter >= 1; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT: + return progress->counter >= achievementCriteria->complete_quest_count.totalQuestCount; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE: + return progress->counter >= achievementCriteria->complete_quests_in_zone.questCount; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: + return progress->counter >= achievementCriteria->complete_daily_quest.questCount; + case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: + return progress->counter >= achievementCriteria->fall_without_dying.fallHeight; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: + return progress->counter >= 1; + case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: + case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: + return progress->counter >= achievementCriteria->be_spell_target.spellCount; + case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: + case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: + return progress->counter >= achievementCriteria->cast_spell.castCount; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: + return progress->counter >= 1; + case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: + return progress->counter >= achievementCriteria->own_item.itemCount; + case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: + return progress->counter >= achievementCriteria->use_item.itemCount; + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM: + return progress->counter >= achievementCriteria->loot_item.itemCount; + case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA: + return progress->counter >= 1; + case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT: + return progress->counter >= achievementCriteria->buy_bank_slot.numberOfSlots; + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION: + return progress->counter >= achievementCriteria->gain_reputation.reputationAmount; + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION: + return progress->counter >= achievementCriteria->gain_exalted_reputation.numberOfExaltedFactions; + case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP: + return progress->counter >= achievementCriteria->visit_barber.numberOfVisits; + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: + return progress->counter >= achievementCriteria->roll_greed_on_loot.count; + case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: + return progress->counter >= achievementCriteria->do_emote.count; + case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM: + return progress->counter >= achievementCriteria->equip_item.count; + case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD: + return progress->counter >= achievementCriteria->quest_reward_money.goldInCopper; + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY: + return progress->counter >= achievementCriteria->loot_money.goldInCopper; + case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT: + return progress->counter >= achievementCriteria->use_gameobject.useCount; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: + return progress->counter >= achievementCriteria->learn_skilline_spell.spellCount; + + // handle all statistic-only criteria here + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND: + case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP: + case ACHIEVEMENT_CRITERIA_TYPE_DEATH: + case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON: + case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE: + case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER: + case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD: + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION: + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION: + case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR: + return false; + } + return false; +} + +void AchievementMgr::CompletedCriteria(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement) +{ + // counter can never complete + if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER) + return; + + // already completed and stored + if (m_completedAchievements.find(achievement->ID)!=m_completedAchievements.end()) + return; + + if (IsCompletedAchievement(achievement)) + CompletedAchievement(achievement); +} + +bool AchievementMgr::IsCompletedAchievement(AchievementEntry const* entry) +{ + // counter can never complete + if(entry->flags & ACHIEVEMENT_FLAG_COUNTER) + return false; + + // for achievement with referenced achievement criterias get from referenced and counter from self + uint32 achievmentForTestId = entry->refAchievement ? entry->refAchievement : entry->ID; + uint32 achievmentForTestCount = entry->count; + + AchievementCriteriaEntryList const* cList = achievementmgr.GetAchievementCriteriaByAchievement(achievmentForTestId); + if(!cList) + return false; + uint32 count = 0; + + // For SUMM achievements, we have to count the progress of each criteria of the achievement. + // Oddly, the target count is NOT countained in the achievement, but in each individual criteria + if (entry->flags & ACHIEVEMENT_FLAG_SUMM) + { + for(AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr) + { + AchievementCriteriaEntry const* criteria = *itr; + + CriteriaProgressMap::const_iterator itrProgress = m_criteriaProgress.find(criteria->ID); + if(itrProgress == m_criteriaProgress.end()) + continue; + + CriteriaProgress const* progress = &itrProgress->second; + count += progress->counter; + + // for counters, field4 contains the main count requirement + if (count >= criteria->raw.count) + return true; + } + return false; + } + + // Default case - need complete all or + bool completed_all = true; + for(AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr) + { + AchievementCriteriaEntry const* criteria = *itr; + + bool completed = IsCompletedCriteria(criteria,entry); + + // found an uncompleted criteria, but DONT return false yet - there might be a completed criteria with ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL + if(completed) + ++count; + else + completed_all = false; + + // completed as have req. count of completed criterias + if(achievmentForTestCount > 0 && achievmentForTestCount <= count) + return true; + } + + // all criterias completed requirement + if(completed_all && achievmentForTestCount==0) + return true; + + return false; +} + +void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype) +{ + if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0) + sLog.outDetail("AchievementMgr::SetCriteriaProgress(%u, %u) for (GUID:%u)", entry->ID, changeValue, m_player->GetGUIDLow()); + + CriteriaProgress *progress = NULL; + + CriteriaProgressMap::iterator iter = m_criteriaProgress.find(entry->ID); + + if(iter == m_criteriaProgress.end()) + { + // not create record for 0 counter + if(changeValue == 0) + return; + + progress = &m_criteriaProgress[entry->ID]; + progress->counter = changeValue; + progress->date = time(NULL); + } + else + { + progress = &iter->second; + + uint32 newValue; + switch(ptype) + { + case PROGRESS_SET: + newValue = changeValue; + break; + case PROGRESS_ACCUMULATE: + { + // avoid overflow + uint32 max_value = std::numeric_limits<uint32>::max(); + newValue = max_value - progress->counter > changeValue ? progress->counter + changeValue : max_value; + break; + } + case PROGRESS_HIGHEST: + newValue = progress->counter < changeValue ? changeValue : progress->counter; + break; + } + + // not update (not mark as changed) if counter will have same value + if(progress->counter == newValue) + return; + + progress->counter = newValue; + } + + progress->changed = true; + + if(entry->timeLimit) + { + time_t now = time(NULL); + if(time_t(progress->date + entry->timeLimit) < now) + progress->counter = 1; + + // also it seems illogical, the timeframe will be extended at every criteria update + progress->date = now; + } + SendCriteriaUpdate(entry->ID,progress); +} + +void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement) +{ + sLog.outDetail("AchievementMgr::CompletedAchievement(%u)", achievement->ID); + if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER || m_completedAchievements.find(achievement->ID)!=m_completedAchievements.end()) + return; + + SendAchievementEarned(achievement); + CompletedAchievementData& ca = m_completedAchievements[achievement->ID]; + ca.date = time(NULL); + ca.changed = true; + + // don't insert for ACHIEVEMENT_FLAG_REALM_FIRST_KILL since otherwise only the first group member would reach that achievement + // TODO: where do set this instead? + if(!(achievement->flags & ACHIEVEMENT_FLAG_REALM_FIRST_KILL)) + achievementmgr.SetRealmCompleted(achievement); + + UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT); + + // reward items and titles if any + AchievementReward const* reward = achievementmgr.GetAchievementReward(achievement); + + // no rewards + if(!reward) + return; + + // titles + if(uint32 titleId = reward->titleId[GetPlayer()->GetTeam() == HORDE?0:1]) + { + if(CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId)) + GetPlayer()->SetTitle(titleEntry); + } + + // mail + if(reward->sender) + { + Item* item = reward->itemId ? Item::CreateItem(reward->itemId,1,GetPlayer ()) : NULL; + + MailItemsInfo mi; + if(item) + { + // save new item before send + item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted + + // item + mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item); + } + + int loc_idx = GetPlayer()->GetSession()->GetSessionDbLocaleIndex(); + + // subject and text + std::string subject = reward->subject; + std::string text = reward->text; + if ( loc_idx >= 0 ) + { + if(AchievementRewardLocale const* loc = achievementmgr.GetAchievementRewardLocale(achievement)) + { + if (loc->subject.size() > size_t(loc_idx) && !loc->subject[loc_idx].empty()) + subject = loc->subject[loc_idx]; + if (loc->text.size() > size_t(loc_idx) && !loc->text[loc_idx].empty()) + text = loc->text[loc_idx]; + } + } + + uint32 itemTextId = objmgr.CreateItemText( text ); + + WorldSession::SendMailTo(GetPlayer(), MAIL_CREATURE, MAIL_STATIONERY_NORMAL, reward->sender, GetPlayer()->GetGUIDLow(), subject, itemTextId , &mi, 0, 0, MAIL_CHECK_MASK_NONE); + } +} + +void AchievementMgr::SendAllAchievementData() +{ + // since we don't know the exact size of the packed GUIDs this is just an approximation + WorldPacket data(SMSG_ALL_ACHIEVEMENT_DATA, 4*2+m_completedAchievements.size()*4*2+m_completedAchievements.size()*7*4); + BuildAllDataPacket(&data); + GetPlayer()->GetSession()->SendPacket(&data); +} + +void AchievementMgr::SendRespondInspectAchievements(Player* player) +{ + // since we don't know the exact size of the packed GUIDs this is just an approximation + WorldPacket data(SMSG_RESPOND_INSPECT_ACHIEVEMENTS, 4+4*2+m_completedAchievements.size()*4*2+m_completedAchievements.size()*7*4); + data.append(GetPlayer()->GetPackGUID()); + BuildAllDataPacket(&data); + player->GetSession()->SendPacket(&data); +} + +/** + * used by both SMSG_ALL_ACHIEVEMENT_DATA and SMSG_RESPOND_INSPECT_ACHIEVEMENT + */ +void AchievementMgr::BuildAllDataPacket(WorldPacket *data) +{ + for(CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter) + { + *data << uint32(iter->first); + *data << uint32(secsToTimeBitFields(iter->second.date)); + } + *data << int32(-1); + + for(CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter) + { + *data << uint32(iter->first); + data->appendPackGUID(iter->second.counter); + data->append(GetPlayer()->GetPackGUID()); + *data << uint32(0); + *data << uint32(secsToTimeBitFields(iter->second.date)); + *data << uint32(0); + *data << uint32(0); + } + + *data << int32(-1); +} + +//========================================================== +AchievementCriteriaEntryList const& AchievementGlobalMgr::GetAchievementCriteriaByType(AchievementCriteriaTypes type) +{ + return m_AchievementCriteriasByType[type]; +} + +void AchievementGlobalMgr::LoadAchievementCriteriaList() +{ + if(sAchievementCriteriaStore.GetNumRows()==0) + { + barGoLink bar(1); + bar.step(); + + sLog.outString(); + sLog.outErrorDb(">> Loaded 0 achievement criteria."); + return; + } + + barGoLink bar( sAchievementCriteriaStore.GetNumRows() ); + for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId) + { + bar.step(); + + AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId); + if(!criteria) + continue; + + m_AchievementCriteriasByType[criteria->requiredType].push_back(criteria); + m_AchievementCriteriaListByAchievement[criteria->referredAchievement].push_back(criteria); + } + + sLog.outString(); + sLog.outString(">> Loaded %lu achievement criteria.",(unsigned long)m_AchievementCriteriasByType->size()); +} + +void AchievementGlobalMgr::LoadAchievementReferenceList() +{ + if(sAchievementStore.GetNumRows()==0) + { + barGoLink bar(1); + bar.step(); + + sLog.outString(); + sLog.outErrorDb(">> Loaded 0 achievement references."); + return; + } + + uint32 count = 0; + barGoLink bar( sAchievementStore.GetNumRows() ); + for (uint32 entryId = 0; entryId < sAchievementStore.GetNumRows(); ++entryId) + { + bar.step(); + + AchievementEntry const* achievement = sAchievementStore.LookupEntry(entryId); + if(!achievement || !achievement->refAchievement) + continue; + + m_AchievementListByReferencedId[achievement->refAchievement].push_back(achievement); + ++count; + } + + sLog.outString(); + sLog.outString(">> Loaded %u achievement references.",count); +} + +void AchievementGlobalMgr::LoadAchievementCriteriaData() +{ + m_criteriaDataMap.clear(); // need for reload case + + QueryResult *result = WorldDatabase.Query("SELECT criteria_id, type, value1, value2 FROM achievement_criteria_data"); + + if(!result) + { + barGoLink bar(1); + bar.step(); + + sLog.outString(); + sLog.outString(">> Loaded 0 additional achievement criteria data. DB table `achievement_criteria_data` is empty."); + return; + } + + uint32 count = 0; + barGoLink bar(result->GetRowCount()); + do + { + bar.step(); + Field *fields = result->Fetch(); + uint32 criteria_id = fields[0].GetUInt32(); + + AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(criteria_id); + + if (!criteria) + { + sLog.outErrorDb( "Table `achievement_criteria_data` have data for not existed criteria (Entry: %u), ignore.", criteria_id); + continue; + } + + AchievementCriteriaData data(fields[1].GetUInt32(),fields[2].GetUInt32(),fields[3].GetUInt32()); + + if(!data.IsValid(criteria)) + continue; + + // this will allocate empty data set storage + AchievementCriteriaDataSet& dataSet = m_criteriaDataMap[criteria_id]; + + // add real data only for not NONE data types + if(data.dataType!=ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE) + dataSet.Add(data); + + // counting data by and data types + ++count; + } while(result->NextRow()); + + delete result; + + // post loading checks + for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId) + { + AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId); + if(!criteria) + continue; + + switch(criteria->requiredType) + { + case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: + if(!GetCriteriaDataSet(criteria)) + sLog.outErrorDb( "Table `achievement_criteria_data` not have expected data for for criteria (Entry: %u Type: %u).", criteria->ID, criteria->requiredType); + break; + case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: // need skip generic cases + if(criteria->do_emote.count && !GetCriteriaDataSet(criteria)) + sLog.outErrorDb( "Table `achievement_criteria_data` not have expected data for for criteria (Entry: %u Type: %u).", criteria->ID, criteria->requiredType); + break; + default: // unexpected case processed in IsValid check + break; + } + + } + + sLog.outString(); + sLog.outString(">> Loaded %u additional achievement criteria data.",count); +} + +void AchievementGlobalMgr::LoadCompletedAchievements() +{ + QueryResult *result = CharacterDatabase.Query("SELECT achievement FROM character_achievement GROUP BY achievement"); + + if(!result) + { + barGoLink bar(1); + bar.step(); + + sLog.outString(); + sLog.outString(">> Loaded 0 realm completed achievements . DB table `character_achievement` is empty."); + return; + } + + barGoLink bar(result->GetRowCount()); + do + { + bar.step(); + Field *fields = result->Fetch(); + m_allCompletedAchievements.insert(fields[0].GetUInt32()); + } while(result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString(">> Loaded %lu realm completed achievements.",(unsigned long)m_allCompletedAchievements.size()); +} + +void AchievementGlobalMgr::LoadRewards() +{ + m_achievementRewards.clear(); // need for reload case + + // 0 1 2 3 4 5 6 + QueryResult *result = WorldDatabase.Query("SELECT entry, title_A, title_H, item, sender, subject, text FROM achievement_reward"); + + if(!result) + { + barGoLink bar(1); + + bar.step(); + + sLog.outString(); + sLog.outErrorDb(">> Loaded 0 achievement rewards. DB table `achievement_reward` is empty."); + return; + } + + uint32 count = 0; + barGoLink bar(result->GetRowCount()); + + do + { + bar.step(); + + Field *fields = result->Fetch(); + uint32 entry = fields[0].GetUInt32(); + if (!sAchievementStore.LookupEntry(entry)) + { + sLog.outErrorDb( "Table `achievement_reward` has wrong achievement (Entry: %u), ignore", entry); + continue; + } + + AchievementReward reward; + reward.titleId[0] = fields[1].GetUInt32(); + reward.titleId[1] = fields[2].GetUInt32(); + reward.itemId = fields[3].GetUInt32(); + reward.sender = fields[4].GetUInt32(); + reward.subject = fields[5].GetCppString(); + reward.text = fields[6].GetCppString(); + + if ((reward.titleId[0]==0)!=(reward.titleId[1]==0)) + sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has title (A: %u H: %u) only for one from teams.", entry, reward.titleId[0], reward.titleId[1]); + + // must be title or mail at least + if (!reward.titleId[0] && !reward.titleId[1] && !reward.sender) + { + sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have title or item reward data, ignore.", entry); + continue; + } + + if (reward.titleId[0]) + { + CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[0]); + if (!titleEntry) + { + sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[0]); + reward.titleId[0] = 0; + } + } + + if (reward.titleId[1]) + { + CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[1]); + if (!titleEntry) + { + sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[1]); + reward.titleId[1] = 0; + } + } + + //check mail data before item for report including wrong item case + if (reward.sender) + { + if (!objmgr.GetCreatureTemplate(reward.sender)) + { + sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid creature entry %u as sender, mail reward skipped.", entry, reward.sender); + reward.sender = 0; + } + } + else + { + if (reward.itemId) + sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have item reward, item will not rewarded", entry); + + if (!reward.subject.empty()) + sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail subject.", entry); + + if (!reward.text.empty()) + sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail text.", entry); + } + + if (reward.itemId) + { + if (!objmgr.GetItemPrototype(reward.itemId)) + { + sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid item id %u, reward mail will be without item.", entry, reward.itemId); + reward.itemId = 0; + } + } + + m_achievementRewards[entry] = reward; + ++count; + + } while (result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u achievement rewards", count ); +} + +void AchievementGlobalMgr::LoadRewardLocales() +{ + m_achievementRewardLocales.clear(); // need for reload case + + QueryResult *result = WorldDatabase.Query("SELECT entry,subject_loc1,text_loc1,subject_loc2,text_loc2,subject_loc3,text_loc3,subject_loc4,text_loc4,subject_loc5,text_loc5,subject_loc6,text_loc6,subject_loc7,text_loc7,subject_loc8,text_loc8 FROM locales_achievement_reward"); + + if(!result) + { + barGoLink bar(1); + + bar.step(); + + sLog.outString(); + sLog.outString(">> Loaded 0 achievement reward locale strings. DB table `locales_achievement_reward` is empty."); + return; + } + + barGoLink bar(result->GetRowCount()); + + do + { + Field *fields = result->Fetch(); + bar.step(); + + uint32 entry = fields[0].GetUInt32(); + + if(m_achievementRewards.find(entry)==m_achievementRewards.end()) + { + sLog.outErrorDb( "Table `locales_achievement_reward` (Entry: %u) has locale strings for not existed achievement reward .", entry); + continue; + } + + AchievementRewardLocale& data = m_achievementRewardLocales[entry]; + + for(int i = 1; i < MAX_LOCALE; ++i) + { + std::string str = fields[1+2*(i-1)].GetCppString(); + if(!str.empty()) + { + int idx = objmgr.GetOrNewIndexForLocale(LocaleConstant(i)); + if(idx >= 0) + { + if(data.subject.size() <= size_t(idx)) + data.subject.resize(idx+1); + + data.subject[idx] = str; + } + } + str = fields[1+2*(i-1)+1].GetCppString(); + if(!str.empty()) + { + int idx = objmgr.GetOrNewIndexForLocale(LocaleConstant(i)); + if(idx >= 0) + { + if(data.text.size() <= size_t(idx)) + data.text.resize(idx+1); + + data.text[idx] = str; + } + } + } + } while (result->NextRow()); + + delete result; + + + sLog.outString(); + sLog.outString( ">> Loaded %lu achievement reward locale strings", (unsigned long)m_achievementRewardLocales.size() ); +} diff --git a/src/game/AchievementMgr.h b/src/game/AchievementMgr.h new file mode 100644 index 00000000000..391afedbb46 --- /dev/null +++ b/src/game/AchievementMgr.h @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __MANGOS_ACHIEVEMENTMGR_H +#define __MANGOS_ACHIEVEMENTMGR_H + +#include "Common.h" +#include "Policies/Singleton.h" +#include "Database/DatabaseEnv.h" +#include "DBCEnums.h" +#include "DBCStores.h" + +#include <map> +#include <string> + +typedef std::list<AchievementCriteriaEntry const*> AchievementCriteriaEntryList; +typedef std::list<AchievementEntry const*> AchievementEntryList; + +typedef std::map<uint32,AchievementCriteriaEntryList> AchievementCriteriaListByAchievement; +typedef std::map<uint32,AchievementEntryList> AchievementListByReferencedId; + +struct CriteriaProgress +{ + uint32 counter; + time_t date; + bool changed; +}; + +enum AchievementCriteriaDataType +{ // value1 value2 comment + ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE = 0, // 0 0 + ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE = 1, // creature_id 0 + ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE = 2, // class_id race_id + ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH= 3, // health_percent 0 + ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD = 4, // 0 0 not corpse (not released body) + ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA = 5, // spell_id effect_idx + ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA = 6, // area id 0 + ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA = 7, // spell_id effect_idx +}; + +#define MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE 8 // maximum value in AchievementCriteriaDataType enum + +class Player; +class Unit; + +struct AchievementCriteriaData +{ + AchievementCriteriaDataType dataType; + union + { + // ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE + struct + { + uint32 id; + } creature; + // ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE + struct + { + uint32 class_id; + uint32 race_id; + } classRace; + // ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH + struct + { + uint32 percent; + } health; + // ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA + // ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA + struct + { + uint32 spell_id; + uint32 effect_idx; + } aura; + // ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA + struct + { + uint32 id; + } area; + // ... + struct + { + uint32 value1; + uint32 value2; + } raw; + }; + + AchievementCriteriaData() : dataType(ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE) + { + raw.value1 = 0; + raw.value2 = 0; + } + + AchievementCriteriaData(uint32 _dataType, uint32 _value1, uint32 _value2) : dataType(AchievementCriteriaDataType(_dataType)) + { + raw.value1 = _value1; + raw.value2 = _value2; + } + + bool IsValid(AchievementCriteriaEntry const* criteria); + bool Meets(Player const* source, Unit const* target) const; +}; + +struct AchievementCriteriaDataSet +{ + typedef std::vector<AchievementCriteriaData> Storage; + void Add(AchievementCriteriaData const& data) { storage.push_back(data); } + bool Meets(Player const* source, Unit const* target) const; + private: + Storage storage; +}; + + +typedef std::map<uint32,AchievementCriteriaDataSet> AchievementCriteriaDataMap; + +struct AchievementReward +{ + uint32 titleId[2]; + uint32 itemId; + uint32 sender; + std::string subject; + std::string text; +}; + +typedef std::map<uint32,AchievementReward> AchievementRewards; + +struct AchievementRewardLocale +{ + std::vector<std::string> subject; + std::vector<std::string> text; +}; + +typedef std::map<uint32,AchievementRewardLocale> AchievementRewardLocales; + + +struct CompletedAchievementData +{ + time_t date; + bool changed; +}; + +typedef UNORDERED_MAP<uint32, CriteriaProgress> CriteriaProgressMap; +typedef UNORDERED_MAP<uint32, CompletedAchievementData> CompletedAchievementMap; + +class Unit; +class Player; +class WorldPacket; + +class AchievementMgr +{ + public: + AchievementMgr(Player* pl); + ~AchievementMgr(); + + void Reset(); + static void DeleteFromDB(uint32 lowguid); + void LoadFromDB(QueryResult *achievementResult, QueryResult *criteriaResult); + void SaveToDB(); + void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1=0, uint32 miscvalue2=0, Unit *unit=NULL, uint32 time=0); + void CheckAllAchievementCriteria(); + void SendAllAchievementData(); + void SendRespondInspectAchievements(Player* player); + Player* GetPlayer() { return m_player;} + + private: + enum ProgressType { PROGRESS_SET, PROGRESS_ACCUMULATE, PROGRESS_HIGHEST }; + void SendAchievementEarned(AchievementEntry const* achievement); + void SendCriteriaUpdate(uint32 id, CriteriaProgress const* progress); + void SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype = PROGRESS_SET); + void CompletedCriteria(AchievementCriteriaEntry const* entry, AchievementEntry const* achievement); + void CompletedAchievement(AchievementEntry const* entry); + bool IsCompletedCriteria(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement); + bool IsCompletedAchievement(AchievementEntry const* entry); + void CompleteAchievementsWithRefs(AchievementEntry const* entry); + void BuildAllDataPacket(WorldPacket *data); + + Player* m_player; + CriteriaProgressMap m_criteriaProgress; + CompletedAchievementMap m_completedAchievements; +}; + +class AchievementGlobalMgr +{ + public: + AchievementCriteriaEntryList const& GetAchievementCriteriaByType(AchievementCriteriaTypes type); + AchievementCriteriaEntryList const* GetAchievementCriteriaByAchievement(uint32 id) + { + AchievementCriteriaListByAchievement::const_iterator itr = m_AchievementCriteriaListByAchievement.find(id); + return itr != m_AchievementCriteriaListByAchievement.end() ? &itr->second : NULL; + } + + AchievementEntryList const* GetAchievementByReferencedId(uint32 id) const + { + AchievementListByReferencedId::const_iterator itr = m_AchievementListByReferencedId.find(id); + return itr != m_AchievementListByReferencedId.end() ? &itr->second : NULL; + } + + AchievementReward const* GetAchievementReward(AchievementEntry const* achievement) const + { + AchievementRewards::const_iterator iter = m_achievementRewards.find(achievement->ID); + return iter!=m_achievementRewards.end() ? &iter->second : NULL; + } + + AchievementRewardLocale const* GetAchievementRewardLocale(AchievementEntry const* achievement) const + { + AchievementRewardLocales::const_iterator iter = m_achievementRewardLocales.find(achievement->ID); + return iter!=m_achievementRewardLocales.end() ? &iter->second : NULL; + } + + AchievementCriteriaDataSet const* GetCriteriaDataSet(AchievementCriteriaEntry const *achievementCriteria) + { + AchievementCriteriaDataMap::const_iterator iter = m_criteriaDataMap.find(achievementCriteria->ID); + return iter!=m_criteriaDataMap.end() ? &iter->second : NULL; + } + + bool IsRealmCompleted(AchievementEntry const* achievement) const + { + return m_allCompletedAchievements.find(achievement->ID) != m_allCompletedAchievements.end(); + } + + void SetRealmCompleted(AchievementEntry const* achievement) + { + m_allCompletedAchievements.insert(achievement->ID); + } + + void LoadAchievementCriteriaList(); + void LoadAchievementCriteriaData(); + void LoadAchievementReferenceList(); + void LoadCompletedAchievements(); + void LoadRewards(); + void LoadRewardLocales(); + private: + AchievementCriteriaDataMap m_criteriaDataMap; + + // store achievement criterias by type to speed up lookup + AchievementCriteriaEntryList m_AchievementCriteriasByType[ACHIEVEMENT_CRITERIA_TYPE_TOTAL]; + // store achievement criterias by achievement to speed up lookup + AchievementCriteriaListByAchievement m_AchievementCriteriaListByAchievement; + // store achievements by referenced achievement id to speed up lookup + AchievementListByReferencedId m_AchievementListByReferencedId; + + typedef std::set<uint32> AllCompletedAchievements; + AllCompletedAchievements m_allCompletedAchievements; + + AchievementRewards m_achievementRewards; + AchievementRewardLocales m_achievementRewardLocales; +}; + +#define achievementmgr Trinity::Singleton<AchievementGlobalMgr>::Instance() + +#endif diff --git a/src/game/AddonHandler.cpp b/src/game/AddonHandler.cpp index 63582113914..4dd25c56b26 100644 --- a/src/game/AddonHandler.cpp +++ b/src/game/AddonHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -66,63 +66,84 @@ bool AddonHandler::BuildAddonPacket(WorldPacket *Source, WorldPacket *Target) if (Source->rpos() + 4 > Source->size()) return false; - *Source >> TempValue; //get real size of the packed structure + *Source >> TempValue; // get real size of the packed structure // empty addon packet, nothing process, can't be received from real client if(!TempValue) return false; - AddonRealSize = TempValue; //temp value because ZLIB only excepts uLongf + AddonRealSize = TempValue; // temp value because ZLIB only excepts uLongf - CurrentPosition = Source->rpos(); //get the position of the pointer in the structure + CurrentPosition = Source->rpos(); // get the position of the pointer in the structure - AddOnPacked.resize(AddonRealSize); //resize target for zlib action + AddOnPacked.resize(AddonRealSize); // resize target for zlib action if (!uncompress(const_cast<uint8*>(AddOnPacked.contents()), &AddonRealSize, const_cast<uint8*>((*Source).contents() + CurrentPosition), (*Source).size() - CurrentPosition)!= Z_OK) { Target->Initialize(SMSG_ADDON_INFO); - while(AddOnPacked.rpos() < AddOnPacked.size()) + uint32 addonsCount; + AddOnPacked >> addonsCount; // addons count? + + for(uint32 i = 0; i < addonsCount; ++i) { - std::string AddonNames; - uint8 unk6; - uint32 crc, unk7; + std::string addonName; + uint8 enabled; + uint32 crc, unk2; // check next addon data format correctness - if(AddOnPacked.rpos()+1+4+4+1 > AddOnPacked.size()) + if(AddOnPacked.rpos()+1 > AddOnPacked.size()) return false; - AddOnPacked >> AddonNames; + AddOnPacked >> addonName; // recheck next addon data format correctness - if(AddOnPacked.rpos()+4+4+1 > AddOnPacked.size()) + if(AddOnPacked.rpos()+1+4+4 > AddOnPacked.size()) return false; - AddOnPacked >> crc >> unk7 >> unk6; + AddOnPacked >> enabled >> crc >> unk2; - //sLog.outDebug("ADDON: Name:%s CRC:%x Unknown1 :%x Unknown2 :%x", AddonNames.c_str(), crc, unk7, unk6); + sLog.outDebug("ADDON: Name: %s, Enabled: 0x%x, CRC: 0x%x, Unknown2: 0x%x", addonName.c_str(), enabled, crc, unk2); - *Target << (uint8)2; + uint8 state = (enabled ? 2 : 1); + *Target << uint8(state); - uint8 unk1 = 1; - *Target << (uint8)unk1; + uint8 unk1 = (enabled ? 1 : 0); + *Target << uint8(unk1); if (unk1) { - uint8 unk2 = crc != 0x1c776d01LL; //If addon is Standard addon CRC - *Target << (uint8)unk2; + uint8 unk2 = (crc != 0x4c1c776d); // If addon is Standard addon CRC + *Target << uint8(unk2); if (unk2) Target->append(tdata, sizeof(tdata)); - *Target << (uint32)0; + *Target << uint32(0); } - uint8 unk3 = 0; - *Target << (uint8)unk3; + uint8 unk3 = (enabled ? 0 : 1); + *Target << uint8(unk3); if (unk3) { - // String, 256 + // String, 256 (null terminated?) + *Target << uint8(0); } } + + uint32 unk4; + AddOnPacked >> unk4; + + uint32 count = 0; + *Target << uint32(count); + /*for(uint32 i = 0; i < count; ++i) + { + uint32 + string (16 bytes) + string (16 bytes) + uint32 + }*/ + + if(AddOnPacked.rpos() != AddOnPacked.size()) + sLog.outDebug("packet under read!"); } else { diff --git a/src/game/AddonHandler.h b/src/game/AddonHandler.h index 6255bd551e2..fd9ad395924 100644 --- a/src/game/AddonHandler.h +++ b/src/game/AddonHandler.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/AggressorAI.cpp b/src/game/AggressorAI.cpp index a001ea5af22..37a876bb059 100644 --- a/src/game/AggressorAI.cpp +++ b/src/game/AggressorAI.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,7 +21,6 @@ #include "AggressorAI.h" #include "Errors.h" #include "Creature.h" -#include "Player.h" #include "ObjectAccessor.h" #include "VMapFactory.h" #include "World.h" @@ -38,78 +37,78 @@ AggressorAI::Permissible(const Creature *creature) return PERMIT_BASE_NO; } -AggressorAI::AggressorAI(Creature *c) : CreatureAI(c), i_creature(*c), i_victimGuid(0), i_state(STATE_NORMAL), i_tracker(TIME_INTERVAL_LOOK) +AggressorAI::AggressorAI(Creature *c) : CreatureAI(c), i_victimGuid(0), i_state(STATE_NORMAL), i_tracker(TIME_INTERVAL_LOOK) { } void AggressorAI::EnterEvadeMode() { - if( !i_creature.isAlive() ) + if( !m_creature->isAlive() ) { - DEBUG_LOG("Creature stopped attacking cuz his dead [guid=%u]", i_creature.GetGUIDLow()); + DEBUG_LOG("Creature stopped attacking cuz his dead [guid=%u]", m_creature->GetGUIDLow()); i_victimGuid = 0; - i_creature.CombatStop(); - i_creature.DeleteThreatList(); + m_creature->CombatStop(); + m_creature->DeleteThreatList(); return; } - Unit* victim = ObjectAccessor::GetUnit(i_creature, i_victimGuid ); + Unit* victim = ObjectAccessor::GetUnit(*m_creature, i_victimGuid ); if( !victim ) { - DEBUG_LOG("Creature stopped attacking because victim is non exist [guid=%u]", i_creature.GetGUIDLow()); + DEBUG_LOG("Creature stopped attacking because victim is non exist [guid=%u]", m_creature->GetGUIDLow()); } else if( !victim->isAlive() ) { - DEBUG_LOG("Creature stopped attacking cuz his victim is dead [guid=%u]", i_creature.GetGUIDLow()); + DEBUG_LOG("Creature stopped attacking cuz his victim is dead [guid=%u]", m_creature->GetGUIDLow()); } else if( victim->HasStealthAura() ) { - DEBUG_LOG("Creature stopped attacking cuz his victim is stealth [guid=%u]", i_creature.GetGUIDLow()); + DEBUG_LOG("Creature stopped attacking cuz his victim is stealth [guid=%u]", m_creature->GetGUIDLow()); } else if( victim->isInFlight() ) { - DEBUG_LOG("Creature stopped attacking cuz his victim is fly away [guid=%u]", i_creature.GetGUIDLow()); + DEBUG_LOG("Creature stopped attacking cuz his victim is fly away [guid=%u]", m_creature->GetGUIDLow()); } else { - DEBUG_LOG("Creature stopped attacking due to target out run him [guid=%u]", i_creature.GetGUIDLow()); + DEBUG_LOG("Creature stopped attacking due to target out run him [guid=%u]", m_creature->GetGUIDLow()); //i_state = STATE_LOOK_AT_VICTIM; //i_tracker.Reset(TIME_INTERVAL_LOOK); } - if(!i_creature.GetCharmerOrOwner()) + if(!m_creature->GetCharmerOrOwner()) { - i_creature.RemoveAllAuras(); + m_creature->RemoveAllAuras(); // Remove TargetedMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead - if( i_creature.GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE ) - i_creature.GetMotionMaster()->MoveTargetedHome(); + if( m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE ) + m_creature->GetMotionMaster()->MoveTargetedHome(); } - else if (i_creature.GetOwner() && i_creature.GetOwner()->isAlive()) - i_creature.GetMotionMaster()->MoveFollow(i_creature.GetOwner(),PET_FOLLOW_DIST,PET_FOLLOW_ANGLE); + else if (m_creature->GetOwner() && m_creature->GetOwner()->isAlive()) + m_creature->GetMotionMaster()->MoveFollow(m_creature->GetOwner(),PET_FOLLOW_DIST,PET_FOLLOW_ANGLE); - i_creature.DeleteThreatList(); + m_creature->DeleteThreatList(); i_victimGuid = 0; - i_creature.CombatStop(); - i_creature.SetLootRecipient(NULL); + m_creature->CombatStop(); + m_creature->SetLootRecipient(NULL); } void AggressorAI::UpdateAI(const uint32 /*diff*/) { - // update i_victimGuid if i_creature.getVictim() !=0 and changed + // update i_victimGuid if m_creature->getVictim() !=0 and changed if(!UpdateVictim()) return; - i_victimGuid = i_creature.getVictim()->GetGUID(); + i_victimGuid = m_creature->getVictim()->GetGUID(); - if( i_creature.isAttackReady() ) + if( m_creature->isAttackReady() ) { - if( i_creature.IsWithinMeleeRange(i_creature.getVictim())) + if( m_creature->IsWithinMeleeRange(m_creature->getVictim())) { - i_creature.AttackerStateUpdate(i_creature.getVictim()); - i_creature.resetAttackTimer(); + m_creature->AttackerStateUpdate(m_creature->getVictim()); + m_creature->resetAttackTimer(); } } } diff --git a/src/game/AggressorAI.h b/src/game/AggressorAI.h index 97dccbcd329..983498716e6 100644 --- a/src/game/AggressorAI.h +++ b/src/game/AggressorAI.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,7 +36,7 @@ class TRINITY_DLL_DECL AggressorAI : public CreatureAI public: - AggressorAI(Creature *c); + explicit AggressorAI(Creature *c); void EnterEvadeMode(); @@ -44,7 +44,6 @@ class TRINITY_DLL_DECL AggressorAI : public CreatureAI static int Permissible(const Creature *); private: - Creature &i_creature; uint64 i_victimGuid; AggressorState i_state; TimeTracker i_tracker; diff --git a/src/game/AnimalRandomMovementGenerator.h b/src/game/AnimalRandomMovementGenerator.h index c24162d2708..9c64edcaa44 100644 --- a/src/game/AnimalRandomMovementGenerator.h +++ b/src/game/AnimalRandomMovementGenerator.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/ArenaTeam.cpp b/src/game/ArenaTeam.cpp index 747ad811b85..96e279f7d83 100644 --- a/src/game/ArenaTeam.cpp +++ b/src/game/ArenaTeam.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://getmangos.com/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -304,8 +304,11 @@ void ArenaTeam::Roster(WorldSession *session) { Player *pl = NULL; + uint8 unk308 = 0; + WorldPacket data(SMSG_ARENA_TEAM_ROSTER, 100); data << uint32(GetId()); // arena team id + data << uint8(unk308); // 308 unknown value but affect packet structure data << uint32(GetMembersSize()); // members count data << uint32(GetType()); // arena team type? @@ -313,18 +316,24 @@ void ArenaTeam::Roster(WorldSession *session) { pl = objmgr.GetPlayer(itr->guid); - data << uint64(itr->guid); // guid - data << uint8((pl ? 1 : 0)); // online flag - data << itr->name; // member name + data << uint64(itr->guid); // guid + data << uint8((pl ? 1 : 0)); // online flag + data << itr->name; // member name data << uint32((itr->guid == GetCaptain() ? 0 : 1));// captain flag 0 captain 1 member - data << uint8((pl ? pl->getLevel() : 0)); // unknown, level? - data << uint8(itr->Class); // class - data << uint32(itr->games_week); // played this week - data << uint32(itr->wins_week); // wins this week - data << uint32(itr->games_season); // played this season - data << uint32(itr->wins_season); // wins this season - data << uint32(itr->personal_rating); // personal rating + data << uint8((pl ? pl->getLevel() : 0)); // unknown, level? + data << uint8(itr->Class); // class + data << uint32(itr->games_week); // played this week + data << uint32(itr->wins_week); // wins this week + data << uint32(itr->games_season); // played this season + data << uint32(itr->wins_season); // wins this season + data << uint32(itr->personal_rating); // personal rating + if(unk308) + { + data << float(0.0); // 308 unk + data << float(0.0); // 308 unk + } } + session->SendPacket(&data); sLog.outDebug("WORLD: Sent SMSG_ARENA_TEAM_ROSTER"); } @@ -566,6 +575,28 @@ void ArenaTeam::MemberLost(Player * plr, uint32 againstRating) } } +void ArenaTeam::OfflineMemberLost(uint64 guid, uint32 againstRating) +{ + // called for offline player after ending rated arena match! + for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) + { + if(itr->guid == guid) + { + // update personal rating + float chance = GetChanceAgainst(itr->personal_rating, againstRating); + int32 mod = (int32)ceil(32.0f * (0.0f - chance)); + if (int32(itr->personal_rating) + mod < 0) + itr->personal_rating = 0; + else + itr->personal_rating += mod; + // update personal played stats + itr->games_week +=1; + itr->games_season +=1; + return; + } + } +} + void ArenaTeam::MemberWon(Player * plr, uint32 againstRating) { // called for each participant after winning a match @@ -623,11 +654,13 @@ void ArenaTeam::SaveToDB() { // save team and member stats to db // called after a match has ended, or when calculating arena_points + CharacterDatabase.BeginTransaction(); CharacterDatabase.PExecute("UPDATE arena_team_stats SET rating = '%u',games = '%u',played = '%u',rank = '%u',wins = '%u',wins2 = '%u' WHERE arenateamid = '%u'", stats.rating, stats.games_week, stats.games_season, stats.rank, stats.wins_week, stats.wins_season, GetId()); for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) { - CharacterDatabase.PExecute("UPDATE arena_team_member SET played_week = '%u', wons_week = '%u', played_season = '%u', wons_season = '%u', personal_rating = '%u' WHERE arenateamid = '%u' AND guid = '%u'", itr->games_week, itr->wins_week, itr->games_season, itr->wins_season, itr->personal_rating, Id, itr->guid); + CharacterDatabase.PExecute("UPDATE arena_team_member SET played_week = '%u', wons_week = '%u', played_season = '%u', wons_season = '%u', personal_rating = '%u' WHERE arenateamid = '%u' AND guid = '%u'", itr->games_week, itr->wins_week, itr->games_season, itr->wins_season, itr->personal_rating, Id, GUID_LOPART(itr->guid)); } + CharacterDatabase.CommitTransaction(); } void ArenaTeam::FinishWeek() @@ -641,6 +674,19 @@ void ArenaTeam::FinishWeek() } } +bool ArenaTeam::IsFighting() const +{ + for (MemberList::const_iterator itr = members.begin(); itr != members.end(); ++itr) + { + if (Player *p = objmgr.GetPlayer(itr->guid)) + { + if (p->GetMap()->IsBattleArena()) + return true; + } + } + return false; +} + /* arenateam fields (id from 2.3.3 client): 1414 - arena team id 2v2 diff --git a/src/game/ArenaTeam.h b/src/game/ArenaTeam.h index 9641c1e5992..60c80f972d2 100644 --- a/src/game/ArenaTeam.h +++ b/src/game/ArenaTeam.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -98,7 +98,7 @@ struct ArenaTeamMember void ModifyPersonalRating(Player* plr, int32 mod, uint32 slot) { - if (personal_rating + mod < 0) + if (int32(personal_rating) + mod < 0) personal_rating = 0; else personal_rating += mod; @@ -179,18 +179,7 @@ class ArenaTeam return NULL; } - bool IsFighting() const - { - for (MemberList::const_iterator itr = members.begin(); itr != members.end(); ++itr) - { - if (Player *p = objmgr.GetPlayer(itr->guid)) - { - if (p->GetMap()->IsBattleArena()) - return true; - } - } - return false; - } + bool IsFighting() const; bool LoadArenaTeamFromDB(uint32 ArenaTeamId); void LoadMembersFromDB(uint32 ArenaTeamId); @@ -211,6 +200,7 @@ class ArenaTeam void MemberWon(Player * plr, uint32 againstRating); int32 LostAgainst(uint32 againstRating); void MemberLost(Player * plr, uint32 againstRating); + void OfflineMemberLost(uint64 guid, uint32 againstRating); void UpdateArenaPointsHelper(std::map<uint32, uint32> & PlayerPoints); diff --git a/src/game/ArenaTeamHandler.cpp b/src/game/ArenaTeamHandler.cpp index 9ba2e65ec92..cebdb71411f 100644 --- a/src/game/ArenaTeamHandler.cpp +++ b/src/game/ArenaTeamHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,7 +27,6 @@ #include "ArenaTeam.h" #include "World.h" #include "SocialMgr.h" -#include "Language.h" void WorldSession::HandleInspectArenaStatsOpcode(WorldPacket & recv_data) { @@ -173,7 +172,7 @@ void WorldSession::HandleArenaTeamInviteAcceptOpcode(WorldPacket & /*recv_data*/ if(!at) return; - if(_player->GetArenaTeamIdFromDB(_player->GetGUIDLow(), at->GetType())) + if(_player->GetArenaTeamId(at->GetSlot())) { SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S,"","",ERR_ALREADY_IN_ARENA_TEAM); // already in arena team that size return; diff --git a/src/game/AuctionHouseBot.cpp b/src/game/AuctionHouseBot.cpp index cb3244ee456..643e18c42d8 100644 --- a/src/game/AuctionHouseBot.cpp +++ b/src/game/AuctionHouseBot.cpp @@ -4,8 +4,8 @@ #include "Database/DatabaseEnv.h" #include "Item.h" #include "Log.h" -#include "AuctionHouseMgr.h" #include "ObjectMgr.h" +#include "AuctionHouseMgr.h" #include "Player.h" #include "World.h" #include "WorldSession.h" @@ -15,7 +15,7 @@ using namespace std; -static bool debug_Out = sConfig.GetIntDefault("AuctionHouseBot.DEBUG", 0); +static bool debug_Out = sConfig.GetBoolDefault("AuctionHouseBot.DEBUG", false); static vector<uint32> npcItems; static vector<uint32> lootItems; @@ -816,13 +816,13 @@ void AuctionHouseBot() /////////////////////////////////////////////////////////////////////////////// void AuctionHouseBotInit() { - AHBSeller = sConfig.GetBoolDefault("AuctionHouseBot.EnableSeller", 0); - AHBBuyer = sConfig.GetBoolDefault("AuctionHouseBot.EnableBuyer", 0); - No_Bind = sConfig.GetBoolDefault("AuctionHouseBot.No_Bind", 1); - Bind_When_Picked_Up = sConfig.GetBoolDefault("AuctionHouseBot.Bind_When_Picked_Up", 0); - Bind_When_Equipped = sConfig.GetBoolDefault("AuctionHouseBot.Bind_When_Equipped", 1); - Bind_When_Use = sConfig.GetBoolDefault("AuctionHouseBot.Bind_When_Use", 1); - Bind_Quest_Item = sConfig.GetBoolDefault("AuctionHouseBot.Bind_Quest_Item", 0); + AHBSeller = sConfig.GetBoolDefault("AuctionHouseBot.EnableSeller", false); + AHBBuyer = sConfig.GetBoolDefault("AuctionHouseBot.EnableBuyer", false); + No_Bind = sConfig.GetBoolDefault("AuctionHouseBot.No_Bind", true); + Bind_When_Picked_Up = sConfig.GetBoolDefault("AuctionHouseBot.Bind_When_Picked_Up", false); + Bind_When_Equipped = sConfig.GetBoolDefault("AuctionHouseBot.Bind_When_Equipped", true); + Bind_When_Use = sConfig.GetBoolDefault("AuctionHouseBot.Bind_When_Use", true); + Bind_Quest_Item = sConfig.GetBoolDefault("AuctionHouseBot.Bind_Quest_Item", false); if(!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) { @@ -833,9 +833,9 @@ void AuctionHouseBotInit() if (AHBSeller) { - Vendor_Items = sConfig.GetBoolDefault("AuctionHouseBot.VendorItems", 0); - Loot_Items = sConfig.GetBoolDefault("AuctionHouseBot.LootItems", 1); - Other_Items = sConfig.GetBoolDefault("AuctionHouseBot.OtherItems", 0); + Vendor_Items = sConfig.GetBoolDefault("AuctionHouseBot.VendorItems", false); + Loot_Items = sConfig.GetBoolDefault("AuctionHouseBot.LootItems", true); + Other_Items = sConfig.GetBoolDefault("AuctionHouseBot.OtherItems", false); QueryResult* results = (QueryResult*) NULL; char npcQuery[] = "SELECT distinct `item` FROM `npc_vendor`"; @@ -1056,20 +1056,20 @@ void AuctionHouseBotInit() } sLog.outString("AuctionHouseBot:"); - sLog.outString("loaded %d grey trade goods", greyTradeGoodsBin.size()); - sLog.outString("loaded %d white trade goods", whiteTradeGoodsBin.size()); - sLog.outString("loaded %d green trade goods", greenTradeGoodsBin.size()); - sLog.outString("loaded %d blue trade goods", blueTradeGoodsBin.size()); - sLog.outString("loaded %d purple trade goods", purpleTradeGoodsBin.size()); - sLog.outString("loaded %d orange trade goods", orangeTradeGoodsBin.size()); - sLog.outString("loaded %d yellow trade goods", yellowTradeGoodsBin.size()); - sLog.outString("loaded %d grey items", greyItemsBin.size()); - sLog.outString("loaded %d white items", whiteItemsBin.size()); - sLog.outString("loaded %d green items", greenItemsBin.size()); - sLog.outString("loaded %d blue items", blueItemsBin.size()); - sLog.outString("loaded %d purple items", purpleItemsBin.size()); - sLog.outString("loaded %d orange items", orangeItemsBin.size()); - sLog.outString("loaded %d yellow items", yellowItemsBin.size()); + sLog.outString("loaded %u grey trade goods", greyTradeGoodsBin.size()); + sLog.outString("loaded %u white trade goods", whiteTradeGoodsBin.size()); + sLog.outString("loaded %u green trade goods", greenTradeGoodsBin.size()); + sLog.outString("loaded %u blue trade goods", blueTradeGoodsBin.size()); + sLog.outString("loaded %u purple trade goods", purpleTradeGoodsBin.size()); + sLog.outString("loaded %u orange trade goods", orangeTradeGoodsBin.size()); + sLog.outString("loaded %u yellow trade goods", yellowTradeGoodsBin.size()); + sLog.outString("loaded %u grey items", greyItemsBin.size()); + sLog.outString("loaded %u white items", whiteItemsBin.size()); + sLog.outString("loaded %u green items", greenItemsBin.size()); + sLog.outString("loaded %u blue items", blueItemsBin.size()); + sLog.outString("loaded %u purple items", purpleItemsBin.size()); + sLog.outString("loaded %u orange items", orangeItemsBin.size()); + sLog.outString("loaded %u yellow items", yellowItemsBin.size()); } sLog.outString("AuctionHouseBot by Paradox (original by ChrisK) has been loaded."); sLog.outString("AuctionHouseBot now includes AHBuyer by Kerbe and Paradox"); diff --git a/src/game/AuctionHouseHandler.cpp b/src/game/AuctionHouseHandler.cpp index b36d1870b86..4d657fd6c84 100644 --- a/src/game/AuctionHouseHandler.cpp +++ b/src/game/AuctionHouseHandler.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * @@ -50,7 +50,7 @@ void WorldSession::HandleAuctionHelloOpcode( WorldPacket & recv_data ) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); SendAuctionHello(guid, unit); } @@ -199,7 +199,7 @@ void WorldSession::HandleAuctionSellItem( WorldPacket & recv_data ) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); Item *it = pl->GetItemByGuid( item ); //do not allow to sell already auctioned items @@ -304,7 +304,7 @@ void WorldSession::HandleAuctionPlaceBid( WorldPacket & recv_data ) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap( pCreature->getFaction() ); @@ -367,6 +367,7 @@ void WorldSession::HandleAuctionPlaceBid( WorldPacket & recv_data ) } auction->bidder = pl->GetGUIDLow(); auction->bid = price; + GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, price); // after this update we should save player's money ... CharacterDatabase.PExecute("UPDATE auctionhouse SET buyguid = '%u',lastbid = '%u' WHERE id = '%u'", auction->bidder, auction->bid, auction->Id); @@ -390,6 +391,7 @@ void WorldSession::HandleAuctionPlaceBid( WorldPacket & recv_data ) } auction->bidder = pl->GetGUIDLow(); auction->bid = auction->buyout; + GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, auction->buyout); auctionmgr.SendAuctionSalePendingMail( auction ); auctionmgr.SendAuctionSuccessfulMail( auction ); @@ -428,7 +430,7 @@ void WorldSession::HandleAuctionRemoveItem( WorldPacket & recv_data ) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap( pCreature->getFaction() ); @@ -500,7 +502,7 @@ void WorldSession::HandleAuctionListBidderItems( WorldPacket & recv_data ) recv_data >> outbiddedCount; if (recv_data.size() != (16 + outbiddedCount * 4 )) { - sLog.outError("Client sent bad opcode!!! with count: %u and size : %d (mustbe: %d", outbiddedCount, recv_data.size(),(16 + outbiddedCount * 4 )); + sLog.outError("Client sent bad opcode!!! with count: %u and size : %lu (must be: %u)", outbiddedCount, (unsigned long)recv_data.size(),(16 + outbiddedCount * 4 )); outbiddedCount = 0; } @@ -513,7 +515,7 @@ void WorldSession::HandleAuctionListBidderItems( WorldPacket & recv_data ) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap( pCreature->getFaction() ); @@ -562,7 +564,7 @@ void WorldSession::HandleAuctionListOwnerItems( WorldPacket & recv_data ) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap( pCreature->getFaction() ); @@ -609,7 +611,7 @@ void WorldSession::HandleAuctionListItems( WorldPacket & recv_data ) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap( pCreature->getFaction() ); @@ -638,3 +640,22 @@ void WorldSession::HandleAuctionListItems( WorldPacket & recv_data ) SendPacket(&data); } +void WorldSession::HandleAuctionListPendingSales( WorldPacket & recv_data ) +{ + sLog.outDebug("CMSG_AUCTION_LIST_PENDING_SALES"); + recv_data.hexlike(); + + uint32 count = 0; + + WorldPacket data(SMSG_AUCTION_LIST_PENDING_SALES, 4); + data << uint32(count); // count + /*for(uint32 i = 0; i < count; ++i) + { + data << ""; // string + data << ""; // string + data << uint32(0); + data << uint32(0); + data << float(0); + }*/ + SendPacket(&data); +} diff --git a/src/game/AuctionHouseMgr.cpp b/src/game/AuctionHouseMgr.cpp index 1554013af99..11b75a0be4e 100644 --- a/src/game/AuctionHouseMgr.cpp +++ b/src/game/AuctionHouseMgr.cpp @@ -1,25 +1,25 @@ /* -* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ #include "Common.h" #include "Database/DatabaseEnv.h" -#include "Database/DBCStores.h" #include "Database/SQLStorage.h" +#include "DBCStores.h" #include "ProgressBar.h" #include "AccountMgr.h" @@ -223,6 +223,8 @@ void AuctionHouseMgr::SendAuctionSuccessfulMail( AuctionEntry * auction ) if (owner) { + //FIXME: what do if owner offline + owner->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD, auction->bid); //send auction owner notification, bidder must be current! owner->GetSession()->SendAuctionOwnerNotification( auction ); } @@ -257,7 +259,7 @@ void AuctionHouseMgr::SendAuctionExpiredMail( AuctionEntry * auction ) if ( owner ) owner->GetSession()->SendAuctionOwnerNotification( auction ); else - RemoveAItem(pItem->GetGUIDLow()); // we have to remove the item, before we delete it !! + RemoveAItem(pItem->GetGUIDLow()); // we have to remove the item, before we delete it !! MailItemsInfo mi; mi.AddItem(auction->item_guidlow, auction->item_template, pItem); @@ -283,7 +285,7 @@ void AuctionHouseMgr::LoadAuctionItems() { barGoLink bar(1); bar.step(); - sLog.outString(""); + sLog.outString(); sLog.outString(">> Loaded 0 auction items"); return; } @@ -298,8 +300,8 @@ void AuctionHouseMgr::LoadAuctionItems() bar.step(); fields = result->Fetch(); - uint32 item_guid = fields[1].GetUInt32(); - uint32 item_template = fields[2].GetUInt32(); + uint32 item_guid = fields[1].GetUInt32(); + uint32 item_template = fields[2].GetUInt32(); ItemPrototype const *proto = objmgr.GetItemPrototype(item_template); @@ -334,7 +336,7 @@ void AuctionHouseMgr::LoadAuctions() { barGoLink bar(1); bar.step(); - sLog.outString(""); + sLog.outString(); sLog.outString(">> Loaded 0 auctions. DB table `auctionhouse` is empty."); return; } @@ -347,7 +349,7 @@ void AuctionHouseMgr::LoadAuctions() { barGoLink bar(1); bar.step(); - sLog.outString(""); + sLog.outString(); sLog.outString(">> Loaded 0 auctions. DB table `auctionhouse` is empty."); return; } @@ -357,7 +359,7 @@ void AuctionHouseMgr::LoadAuctions() { barGoLink bar(1); bar.step(); - sLog.outString(""); + sLog.outString(); sLog.outString(">> Loaded 0 auctions. DB table `auctionhouse` is empty."); return; } @@ -468,17 +470,17 @@ AuctionHouseEntry const* AuctionHouseMgr::GetAuctionHouseEntry(uint32 factionTem // but no easy way convert creature faction to player race faction for specific city switch(factionTemplateId) { - case 12: houseid = 1; break; // human - case 29: houseid = 6; break; // orc, and generic for horde - case 55: houseid = 2; break; // dwarf, and generic for alliance - case 68: houseid = 4; break; // undead - case 80: houseid = 3; break; // n-elf - case 104: houseid = 5; break; // trolls - case 120: houseid = 7; break; // booty bay, neutral - case 474: houseid = 7; break; // gadgetzan, neutral - case 855: houseid = 7; break; // everlook, neutral + case 12: houseid = 1; break; // human + case 29: houseid = 6; break; // orc, and generic for horde + case 55: houseid = 2; break; // dwarf, and generic for alliance + case 68: houseid = 4; break; // undead + case 80: houseid = 3; break; // n-elf + case 104: houseid = 5; break; // trolls + case 120: houseid = 7; break; // booty bay, neutral + case 474: houseid = 7; break; // gadgetzan, neutral + case 855: houseid = 7; break; // everlook, neutral case 1604: houseid = 6; break; // b-elfs, - default: // for unknown case + default: // for unknown case { FactionTemplateEntry const* u_entry = sFactionTemplateStore.LookupEntry(factionTemplateId); if(!u_entry) @@ -576,22 +578,22 @@ void AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player ItemPrototype const *proto = item->GetProto(); - if (itemClass != (0xffffffff) && proto->Class != itemClass) + if (itemClass != 0xffffffff && proto->Class != itemClass) continue; - if (itemSubClass != (0xffffffff) && proto->SubClass != itemSubClass) + if (itemSubClass != 0xffffffff && proto->SubClass != itemSubClass) continue; - if (inventoryType != (0xffffffff) && proto->InventoryType != inventoryType) + if (inventoryType != 0xffffffff && proto->InventoryType != inventoryType) continue; - if (quality != (0xffffffff) && proto->Quality != quality) + if (quality != 0xffffffff && proto->Quality != quality) continue; - if( levelmin != (0x00) && (proto->RequiredLevel < levelmin || levelmax != (0x00) && proto->RequiredLevel > levelmax ) ) + if (levelmin != 0x00 && (proto->RequiredLevel < levelmin || (levelmax != 0x00 && proto->RequiredLevel > levelmax))) continue; - if( usable != (0x00) && player->CanUseItem( item ) != EQUIP_ERR_OK ) + if (usable != 0x00 && player->CanUseItem( item ) != EQUIP_ERR_OK) continue; std::string name = proto->Name1; @@ -609,10 +611,10 @@ void AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player } } - if( !wsearchedname.empty() && !Utf8FitTo(name, wsearchedname) ) + if (!wsearchedname.empty() && !Utf8FitTo(name, wsearchedname) ) continue; - if ((count < 50) && (totalcount >= listfrom)) + if (count < 50 && totalcount >= listfrom) { ++count; Aentry->BuildAuctionInfo(data); @@ -630,29 +632,29 @@ bool AuctionEntry::BuildAuctionInfo(WorldPacket & data) const sLog.outError("auction to item, that doesn't exist !!!!"); return false; } - data << (uint32) Id; - data << (uint32) pItem->GetEntry(); + data << uint32(Id); + data << uint32(pItem->GetEntry()); for (uint8 i = 0; i < MAX_INSPECTED_ENCHANTMENT_SLOT; i++) { - data << (uint32) pItem->GetEnchantmentId(EnchantmentSlot(i)); - data << (uint32) pItem->GetEnchantmentDuration(EnchantmentSlot(i)); - data << (uint32) pItem->GetEnchantmentCharges(EnchantmentSlot(i)); + data << uint32(pItem->GetEnchantmentId(EnchantmentSlot(i))); + data << uint32(pItem->GetEnchantmentDuration(EnchantmentSlot(i))); + data << uint32(pItem->GetEnchantmentCharges(EnchantmentSlot(i))); } - data << (uint32) pItem->GetItemRandomPropertyId(); //random item property id - data << (uint32) pItem->GetItemSuffixFactor(); //SuffixFactor - data << (uint32) pItem->GetCount(); //item->count - data << (uint32) pItem->GetSpellCharges(); //item->charge FFFFFFF - data << (uint32) 0; //Unknown - data << (uint64) owner; //Auction->owner - data << (uint32) startbid; //Auction->startbid (not sure if useful) - data << (uint32) (bid ? GetAuctionOutBid() : 0); + data << uint32(pItem->GetItemRandomPropertyId()); //random item property id + data << uint32(pItem->GetItemSuffixFactor()); //SuffixFactor + data << uint32(pItem->GetCount()); //item->count + data << uint32(pItem->GetSpellCharges()); //item->charge FFFFFFF + data << uint32(0); //Unknown + data << uint64(owner); //Auction->owner + data << uint32(startbid); //Auction->startbid (not sure if useful) + data << uint32(bid ? GetAuctionOutBid() : 0); //minimal outbid - data << (uint32) buyout; //auction->buyout - data << (uint32) (expire_time - time(NULL))* 1000; //time left - data << (uint64) bidder; //auction->bidder current - data << (uint32) bid; //current bid + data << uint32(buyout); //auction->buyout + data << uint32((expire_time-time(NULL))*IN_MILISECONDS);//time left + data << uint64(bidder) ; //auction->bidder current + data << uint32(bid); //current bid return true; } diff --git a/src/game/Bag.cpp b/src/game/Bag.cpp index 331b12acde5..06c2f6ade6b 100644 --- a/src/game/Bag.cpp +++ b/src/game/Bag.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,9 +23,7 @@ #include "ObjectMgr.h" #include "Database/DatabaseEnv.h" #include "Log.h" -#include "WorldPacket.h" #include "UpdateData.h" -#include "WorldSession.h" Bag::Bag( ): Item() { @@ -34,7 +32,7 @@ Bag::Bag( ): Item() m_valuesCount = CONTAINER_END; - memset(m_bagslot, 0, sizeof(Item *) * MAX_BAG_SIZE); // Maximum 20 Slots + memset(m_bagslot, 0, sizeof(Item *) * MAX_BAG_SIZE); } Bag::~Bag() diff --git a/src/game/Bag.h b/src/game/Bag.h index d930809cf4a..1c341b5611d 100644 --- a/src/game/Bag.h +++ b/src/game/Bag.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,10 +24,7 @@ // Maximum 36 Slots ( (CONTAINER_END - CONTAINER_FIELD_SLOT_1)/2 #define MAX_BAG_SIZE 36 // 2.0.12 -#include "Object.h" #include "ItemPrototype.h" -#include "Unit.h" -#include "Creature.h" #include "Item.h" class Bag : public Item diff --git a/src/game/BattleGround.cpp b/src/game/BattleGround.cpp index 2538bd3434e..fffedb56ad8 100644 --- a/src/game/BattleGround.cpp +++ b/src/game/BattleGround.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,23 +21,119 @@ #include "Object.h" #include "Player.h" #include "BattleGround.h" +#include "BattleGroundMgr.h" #include "Creature.h" #include "MapManager.h" #include "Language.h" -#include "Chat.h" #include "SpellAuras.h" #include "ArenaTeam.h" #include "World.h" +#include "Group.h" +#include "ObjectMgr.h" +#include "WorldPacket.h" #include "Util.h" +#include "Formulas.h" +#include "GridNotifiersImpl.h" + +namespace MaNGOS +{ + class BattleGroundChatBuilder + { + public: + BattleGroundChatBuilder(ChatMsg msgtype, int32 textId, Player const* source, va_list* args = NULL) + : i_msgtype(msgtype), i_textId(textId), i_source(source), i_args(args) {} + void operator()(WorldPacket& data, int32 loc_idx) + { + char const* text = objmgr.GetMangosString(i_textId,loc_idx); + + if (i_args) + { + // we need copy va_list before use or original va_list will corrupted + va_list ap; + va_copy(ap,*i_args); + + char str [2048]; + vsnprintf(str,2048,text, ap ); + va_end(ap); + + do_helper(data,&str[0]); + } + else + do_helper(data,text); + } + private: + void do_helper(WorldPacket& data, char const* text) + { + uint64 target_guid = i_source ? i_source ->GetGUID() : 0; + + data << uint8(i_msgtype); + data << uint32(LANG_UNIVERSAL); + data << uint64(target_guid); // there 0 for BG messages + data << uint32(0); // can be chat msg group or something + data << uint64(target_guid); + data << uint32(strlen(text)+1); + data << text; + data << uint8(i_source ? i_source->chatTag() : uint8(0)); + } + + ChatMsg i_msgtype; + int32 i_textId; + Player const* i_source; + va_list* i_args; + }; + + class BattleGround2ChatBuilder + { + public: + BattleGround2ChatBuilder(ChatMsg msgtype, int32 textId, Player const* source, int32 arg1, int32 arg2) + : i_msgtype(msgtype), i_textId(textId), i_source(source), i_arg1(arg1), i_arg2(arg2) {} + void operator()(WorldPacket& data, int32 loc_idx) + { + char const* text = objmgr.GetMangosString(i_textId,loc_idx); + char const* arg1str = i_arg1 ? objmgr.GetMangosString(i_arg1,loc_idx) : ""; + char const* arg2str = i_arg2 ? objmgr.GetMangosString(i_arg2,loc_idx) : ""; + + char str [2048]; + snprintf(str,2048,text, arg1str, arg2str ); + + uint64 target_guid = i_source ? i_source ->GetGUID() : 0; + + data << uint8(i_msgtype); + data << uint32(LANG_UNIVERSAL); + data << uint64(target_guid); // there 0 for BG messages + data << uint32(0); // can be chat msg group or something + data << uint64(target_guid); + data << uint32(strlen(str)+1); + data << str; + data << uint8(i_source ? i_source->chatTag() : uint8(0)); + } + private: + + ChatMsg i_msgtype; + int32 i_textId; + Player const* i_source; + int32 i_arg1; + int32 i_arg2; + }; +} // namespace MaNGOS + +template<class Do> +void BattleGround::BroadcastWorker(Do& _do) +{ + for(BattleGroundPlayerMap::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + if (Player *plr = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER))) + _do(plr); +} BattleGround::BattleGround() { - m_TypeID = 0; + m_TypeID = BattleGroundTypeId(0); m_InstanceID = 0; - m_Status = 0; + m_Status = STATUS_NONE; + m_ClientInstanceID = 0; m_EndTime = 0; m_LastResurrectTime = 0; - m_Queue_type = MAX_BATTLEGROUND_QUEUES; + m_QueueId = QUEUE_ID_MAX_LEVEL_19; m_InvitedAlliance = 0; m_InvitedHorde = 0; m_ArenaType = 0; @@ -88,6 +184,16 @@ BattleGround::BattleGround() m_PrematureCountDown = 0; m_HonorMode = BG_NORMAL; + + m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_2M; + m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_1M; + m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_30S; + m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE; + //we must set to some default existing values + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_WS_START_TWO_MINUTES; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_WS_START_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_WS_START_HALF_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_WS_HAS_BEGUN; } BattleGround::~BattleGround() @@ -105,84 +211,55 @@ BattleGround::~BattleGround() DelObject(i); } - // delete creature and go respawn times - WorldDatabase.PExecute("DELETE FROM creature_respawn WHERE instance = '%u'",GetInstanceID()); - WorldDatabase.PExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'",GetInstanceID()); - // delete instance from db - CharacterDatabase.PExecute("DELETE FROM instance WHERE id = '%u'",GetInstanceID()); - // remove from battlegrounds - sBattleGroundMgr.RemoveBattleGround(GetInstanceID()); + if(GetInstanceID()) // not spam by useless queries in case BG templates + { + // delete creature and go respawn times + WorldDatabase.PExecute("DELETE FROM creature_respawn WHERE instance = '%u'",GetInstanceID()); + WorldDatabase.PExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'",GetInstanceID()); + // delete instance from db + CharacterDatabase.PExecute("DELETE FROM instance WHERE id = '%u'",GetInstanceID()); + // remove from battlegrounds + } + + sBattleGroundMgr.RemoveBattleGround(GetInstanceID(), GetTypeID()); // unload map - if(Map * map = MapManager::Instance().FindMap(GetMapId(), GetInstanceID())) - if(map->IsBattleGroundOrArena()) + if (Map * map = MapManager::Instance().FindMap(GetMapId(), GetInstanceID())) + if (map->IsBattleGroundOrArena()) ((BattleGroundMap*)map)->SetUnload(); // remove from bg free slot queue this->RemoveFromBGFreeSlotQueue(); } -void BattleGround::Update(time_t diff) +void BattleGround::Update(uint32 diff) { - if(!GetPlayersSize() && !GetRemovedPlayersSize() && !GetReviveQueueSize()) + if (!GetPlayersSize() && !GetReviveQueueSize()) //BG is empty return; - WorldPacket data; - - if(GetRemovedPlayersSize()) + // remove offline players from bg after 5 minutes + if (!m_OfflineQueue.empty()) { - for(std::map<uint64, uint8>::iterator itr = m_RemovedPlayers.begin(); itr != m_RemovedPlayers.end(); ++itr) + BattleGroundPlayerMap::iterator itr = m_Players.find(*(m_OfflineQueue.begin())); + if (itr != m_Players.end()) { - Player *plr = objmgr.GetPlayer(itr->first); - switch(itr->second) + if (itr->second.OfflineRemoveTime <= sWorld.GetGameTime()) { - //following code is handled by event: - /*case 0: - sBattleGroundMgr.m_BattleGroundQueues[GetTypeID()].RemovePlayer(itr->first); - //RemovePlayerFromQueue(itr->first); - if(plr) - { - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetTeam(), plr->GetBattleGroundQueueIndex(m_TypeID), STATUS_NONE, 0, 0); - plr->GetSession()->SendPacket(&data); - } - break;*/ - case 1: // currently in bg and was removed from bg - if(plr) - RemovePlayerAtLeave(itr->first, true, true); - else - RemovePlayerAtLeave(itr->first, false, false); - break; - case 2: // revive queue - RemovePlayerFromResurrectQueue(itr->first); - break; - default: - sLog.outError("BattleGround: Unknown remove player case!"); + RemovePlayerAtLeave(itr->first, true, true);// remove player from BG + m_OfflineQueue.pop_front(); // remove from offline queue + //do not use itr for anything, because it is erased in RemovePlayerAtLeave() } } - m_RemovedPlayers.clear(); } - // this code isn't efficient and its idea isn't implemented yet - /* offline players are removed from battleground in worldsession::LogoutPlayer() - // remove offline players from bg after ~5 minutes - if(GetPlayersSize()) - { - for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - Player *plr = objmgr.GetPlayer(itr->first); - itr->second.LastOnlineTime += diff; - - if(plr) - itr->second.LastOnlineTime = 0; // update last online time - else - if(itr->second.LastOnlineTime >= MAX_OFFLINE_TIME) // 5 minutes - m_RemovedPlayers[itr->first] = 1; // add to remove list (BG) - } - }*/ + /*********************************************************/ + /*** BATTLEGROUND RESSURECTION SYSTEM ***/ + /*********************************************************/ + //this should be handled by spell system m_LastResurrectTime += diff; if (m_LastResurrectTime >= RESURRECTION_INTERVAL) { - if(GetReviveQueueSize()) + if (GetReviveQueueSize()) { for(std::map<uint64, std::vector<uint64> >::iterator itr = m_ReviveQueue.begin(); itr != m_ReviveQueue.end(); ++itr) { @@ -190,7 +267,7 @@ void BattleGround::Update(time_t diff) for(std::vector<uint64>::iterator itr2 = (itr->second).begin(); itr2 != (itr->second).end(); ++itr2) { Player *plr = objmgr.GetPlayer(*itr2); - if(!plr) + if (!plr) continue; if (!sh) @@ -219,7 +296,7 @@ void BattleGround::Update(time_t diff) for(std::vector<uint64>::iterator itr = m_ResurrectQueue.begin(); itr != m_ResurrectQueue.end(); ++itr) { Player *plr = objmgr.GetPlayer(*itr); - if(!plr) + if (!plr) continue; plr->ResurrectPlayer(1.0f); plr->CastSpell(plr, SPELL_SPIRIT_HEAL_MANA, true); @@ -228,46 +305,151 @@ void BattleGround::Update(time_t diff) m_ResurrectQueue.clear(); } + /*********************************************************/ + /*** BATTLEGROUND BALLANCE SYSTEM ***/ + /*********************************************************/ + // if less then minimum players are in on one side, then start premature finish timer - if(GetStatus() == STATUS_IN_PROGRESS && !isArena() && sBattleGroundMgr.GetPrematureFinishTime() && (GetPlayersCountByTeam(ALLIANCE) < GetMinPlayersPerTeam() || GetPlayersCountByTeam(HORDE) < GetMinPlayersPerTeam())) + if (GetStatus() == STATUS_IN_PROGRESS && !isArena() && sBattleGroundMgr.GetPrematureFinishTime() && (GetPlayersCountByTeam(ALLIANCE) < GetMinPlayersPerTeam() || GetPlayersCountByTeam(HORDE) < GetMinPlayersPerTeam())) { - if(!m_PrematureCountDown) + if (!m_PrematureCountDown) { m_PrematureCountDown = true; m_PrematureCountDownTimer = sBattleGroundMgr.GetPrematureFinishTime(); - SendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING); } - else if(m_PrematureCountDownTimer < diff) + else if (m_PrematureCountDownTimer < diff) { // time's up! - EndBattleGround(0); // noone wins + uint32 winner = 0; + if (GetPlayersCountByTeam(ALLIANCE) >= GetMinPlayersPerTeam()) + winner = ALLIANCE; + else if (GetPlayersCountByTeam(HORDE) >= GetMinPlayersPerTeam()) + winner = HORDE; + + EndBattleGround(winner); m_PrematureCountDown = false; } else { uint32 newtime = m_PrematureCountDownTimer - diff; // announce every minute - if(m_PrematureCountDownTimer != sBattleGroundMgr.GetPrematureFinishTime() && newtime / 60000 != m_PrematureCountDownTimer / 60000) - SendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING); + if (newtime > (MINUTE * IN_MILISECONDS)) + { + if (newtime / (MINUTE * IN_MILISECONDS) != m_PrematureCountDownTimer / (MINUTE * IN_MILISECONDS)) + PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING, CHAT_MSG_SYSTEM, NULL, (uint32)(m_PrematureCountDownTimer / (MINUTE * IN_MILISECONDS))); + } + else + { + //announce every 15 seconds + if (newtime / (15 * IN_MILISECONDS) != m_PrematureCountDownTimer / (15 * IN_MILISECONDS)) + PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING_SECS, CHAT_MSG_SYSTEM, NULL, (uint32)(m_PrematureCountDownTimer / IN_MILISECONDS)); + } m_PrematureCountDownTimer = newtime; } } else if (m_PrematureCountDown) m_PrematureCountDown = false; - if(GetStatus() == STATUS_WAIT_LEAVE) + /*********************************************************/ + /*** BATTLEGROUND STARTING SYSTEM ***/ + /*********************************************************/ + + if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize()) + { + ModifyStartDelayTime(diff); + + if (!(m_Events & BG_STARTING_EVENT_1)) + { + m_Events |= BG_STARTING_EVENT_1; + + // setup here, only when at least one player has ported to the map + if (!SetupBattleGround()) + { + EndNow(); + return; + } + + StartingEventCloseDoors(); + SetStartDelayTime(m_StartDelayTimes[BG_STARTING_EVENT_FIRST]); + //first start warning - 2 or 1 minute + SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_FIRST], CHAT_MSG_BG_SYSTEM_NEUTRAL); + } + // After 1 minute or 30 seconds, warning is signalled + else if (GetStartDelayTime() <= m_StartDelayTimes[BG_STARTING_EVENT_SECOND] && !(m_Events & BG_STARTING_EVENT_2)) + { + m_Events |= BG_STARTING_EVENT_2; + SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_SECOND], CHAT_MSG_BG_SYSTEM_NEUTRAL); + } + // After 30 or 15 seconds, warning is signalled + else if (GetStartDelayTime() <= m_StartDelayTimes[BG_STARTING_EVENT_THIRD] && !(m_Events & BG_STARTING_EVENT_3)) + { + m_Events |= BG_STARTING_EVENT_3; + SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_THIRD], CHAT_MSG_BG_SYSTEM_NEUTRAL); + } + // delay expired (atfer 2 or 1 minute) + else if (GetStartDelayTime() <= 0 && !(m_Events & BG_STARTING_EVENT_4)) + { + m_Events |= BG_STARTING_EVENT_4; + + StartingEventOpenDoors(); + + SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_FOURTH], CHAT_MSG_BG_SYSTEM_NEUTRAL); + SetStatus(STATUS_IN_PROGRESS); + SetStartDelayTime(m_StartDelayTimes[BG_STARTING_EVENT_FOURTH]); + + //remove preparation + if (isArena()) + { + //TODO : add arena sound PlaySoundToAll(SOUND_ARENA_START); + + for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) + if (Player *plr = objmgr.GetPlayer(itr->first)) + plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION); + + CheckArenaWinConditions(); + } + else + { + + PlaySoundToAll(SOUND_BG_START); + + for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) + if (Player* plr = objmgr.GetPlayer(itr->first)) + plr->RemoveAurasDueToSpell(SPELL_PREPARATION); + //Announce BG starting + if (sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE)) + { + sWorld.SendWorldText(LANG_BG_STARTED_ANNOUNCE_WORLD, GetName(), GetMinLevel(), GetMaxLevel()); + } + } + } + } + + /*********************************************************/ + /*** BATTLEGROUND ENDING SYSTEM ***/ + /*********************************************************/ + + if (GetStatus() == STATUS_WAIT_LEAVE) { // remove all players from battleground after 2 minutes - m_EndTime += diff; - if(m_EndTime >= TIME_TO_AUTOREMOVE) // 2 minutes + m_EndTime -= diff; + if (m_EndTime <= 0) { - for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + m_EndTime = 0; + BattleGroundPlayerMap::iterator itr, next; + for(itr = m_Players.begin(); itr != m_Players.end(); itr = next) { - m_RemovedPlayers[itr->first] = 1; // add to remove list (BG) + next = itr; + ++next; + //itr is erased here! + RemovePlayerAtLeave(itr->first, true, true);// remove player from BG + // do not change any battleground's private variables } - // do not change any battleground's private variables } } + + //update start time + m_StartTime += diff; } void BattleGround::SetTeamStartLoc(uint32 TeamID, float X, float Y, float Z, float O) @@ -281,10 +463,10 @@ void BattleGround::SetTeamStartLoc(uint32 TeamID, float X, float Y, float Z, flo void BattleGround::SendPacketToAll(WorldPacket *packet) { - for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + for(BattleGroundPlayerMap::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) { Player *plr = objmgr.GetPlayer(itr->first); - if(plr) + if (plr) plr->GetSession()->SendPacket(packet); else sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first); @@ -293,23 +475,23 @@ void BattleGround::SendPacketToAll(WorldPacket *packet) void BattleGround::SendPacketToTeam(uint32 TeamID, WorldPacket *packet, Player *sender, bool self) { - for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + for(BattleGroundPlayerMap::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) { Player *plr = objmgr.GetPlayer(itr->first); - if(!plr) + if (!plr) { sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first); continue; } - if(!self && sender == plr) + if (!self && sender == plr) continue; - uint32 team = itr->second.Team;//GetPlayerTeam(plr->GetGUID()); + uint32 team = itr->second.Team; if(!team) team = plr->GetTeam(); - if(team == TeamID) + if (team == TeamID) plr->GetSession()->SendPacket(packet); } } @@ -325,20 +507,20 @@ void BattleGround::PlaySoundToTeam(uint32 SoundID, uint32 TeamID) { WorldPacket data; - for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + for(BattleGroundPlayerMap::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) { Player *plr = objmgr.GetPlayer(itr->first); - if(!plr) + if (!plr) { sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first); continue; } - uint32 team = itr->second.Team;//GetPlayerTeam(plr->GetGUID()); + uint32 team = itr->second.Team; if(!team) team = plr->GetTeam(); - if(team == TeamID) + if (team == TeamID) { sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID); plr->GetSession()->SendPacket(&data); @@ -348,20 +530,20 @@ void BattleGround::PlaySoundToTeam(uint32 SoundID, uint32 TeamID) void BattleGround::CastSpellOnTeam(uint32 SpellID, uint32 TeamID) { - for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + for(BattleGroundPlayerMap::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) { Player *plr = objmgr.GetPlayer(itr->first); - if(!plr) + if (!plr) { sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first); continue; } - uint32 team = itr->second.Team;//GetPlayerTeam(plr->GetGUID()); + uint32 team = itr->second.Team; if(!team) team = plr->GetTeam(); - if(team == TeamID) + if (team == TeamID) plr->CastSpell(plr, SpellID, true); } } @@ -385,20 +567,20 @@ void BattleGround::YellToAll(Creature* creature, const char* text, uint32 langua void BattleGround::RewardHonorToTeam(uint32 Honor, uint32 TeamID) { - for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + for(BattleGroundPlayerMap::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) { Player *plr = objmgr.GetPlayer(itr->first); - if(!plr) + if (!plr) { sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first); continue; } - uint32 team = itr->second.Team;//GetPlayerTeam(plr->GetGUID()); + uint32 team = itr->second.Team; if(!team) team = plr->GetTeam(); - if(team == TeamID) + if (team == TeamID) UpdatePlayerScore(plr, SCORE_BONUS_HONOR, Honor); } } @@ -407,24 +589,24 @@ void BattleGround::RewardReputationToTeam(uint32 faction_id, uint32 Reputation, { FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id); - if(!factionEntry) + if (!factionEntry) return; - for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + for(BattleGroundPlayerMap::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) { Player *plr = objmgr.GetPlayer(itr->first); - if(!plr) + if (!plr) { sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first); continue; } - uint32 team = itr->second.Team;//GetPlayerTeam(plr->GetGUID()); + uint32 team = itr->second.Team; if(!team) team = plr->GetTeam(); - if(team == TeamID) - plr->ModifyFactionReputation(factionEntry, Reputation); + if (team == TeamID) + plr->GetReputationMgr().ModifyReputation(factionEntry, Reputation); } } @@ -445,32 +627,25 @@ void BattleGround::UpdateWorldStateForPlayer(uint32 Field, uint32 Value, Player void BattleGround::EndBattleGround(uint32 winner) { this->RemoveFromBGFreeSlotQueue(); - uint32 almost_winning_team = HORDE; + ArenaTeam * winner_arena_team = NULL; ArenaTeam * loser_arena_team = NULL; uint32 loser_rating = 0; uint32 winner_rating = 0; WorldPacket data; - Player *Source = NULL; - const char *winmsg = ""; + int32 winmsg_id = 0; - if(winner == ALLIANCE) + if (winner == ALLIANCE) { - if(isBattleGround()) - winmsg = GetTrinityString(LANG_BG_A_WINS); - else - winmsg = GetTrinityString(LANG_ARENA_GOLD_WINS); + winmsg_id = isBattleGround() ? LANG_BG_A_WINS : LANG_ARENA_GOLD_WINS; PlaySoundToAll(SOUND_ALLIANCE_WINS); // alliance wins sound SetWinner(WINNER_ALLIANCE); } - else if(winner == HORDE) + else if (winner == HORDE) { - if(isBattleGround()) - winmsg = GetTrinityString(LANG_BG_H_WINS); - else - winmsg = GetTrinityString(LANG_ARENA_GREEN_WINS); + winmsg_id = isBattleGround() ? LANG_BG_H_WINS : LANG_ARENA_GREEN_WINS; PlaySoundToAll(SOUND_HORDE_WINS); // horde wins sound @@ -482,38 +657,23 @@ void BattleGround::EndBattleGround(uint32 winner) } SetStatus(STATUS_WAIT_LEAVE); - m_EndTime = 0; + //we must set it this way, because end time is sent in packet! + m_EndTime = TIME_TO_AUTOREMOVE; // arena rating calculation - if(isArena() && isRated()) + if (isArena() && isRated()) { - if(winner == ALLIANCE) - { - winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(ALLIANCE)); - loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(HORDE)); - } - else if(winner == HORDE) - { - winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(HORDE)); - loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(ALLIANCE)); - } - if(winner_arena_team && loser_arena_team) + winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(winner)); + loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(winner))); + if (winner_arena_team && loser_arena_team) { loser_rating = loser_arena_team->GetStats().rating; winner_rating = winner_arena_team->GetStats().rating; int32 winner_change = winner_arena_team->WonAgainst(loser_rating); int32 loser_change = loser_arena_team->LostAgainst(winner_rating); sLog.outDebug("--- Winner rating: %u, Loser rating: %u, Winner change: %u, Losser change: %u ---", winner_rating, loser_rating, winner_change, loser_change); - if(winner == ALLIANCE) - { - SetArenaTeamRatingChangeForTeam(ALLIANCE, winner_change); - SetArenaTeamRatingChangeForTeam(HORDE, loser_change); - } - else - { - SetArenaTeamRatingChangeForTeam(HORDE, winner_change); - SetArenaTeamRatingChangeForTeam(ALLIANCE, loser_change); - } + SetArenaTeamRatingChangeForTeam(winner, winner_change); + SetArenaTeamRatingChangeForTeam(GetOtherTeam(winner), loser_change); } else { @@ -522,69 +682,56 @@ void BattleGround::EndBattleGround(uint32 winner) } } - if(!isArena()){ - - if(m_score[GetTeamIndexByTeamId(ALLIANCE)] == m_score[GetTeamIndexByTeamId(HORDE)]) - almost_winning_team = 0; //no real winner - if(m_score[GetTeamIndexByTeamId(ALLIANCE)] > m_score[GetTeamIndexByTeamId(HORDE)]) - almost_winning_team = ALLIANCE; - - } - - for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + for(BattleGroundPlayerMap::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) { Player *plr = objmgr.GetPlayer(itr->first); - if(!plr) + uint32 team = itr->second.Team; + + if (!plr) { + //if rated arena match - make member lost! + if (isArena() && isRated() && winner_arena_team && loser_arena_team) + { + if (team == winner) + winner_arena_team->OfflineMemberLost(itr->first, loser_rating); + else + loser_arena_team->OfflineMemberLost(itr->first, winner_rating); + } sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first); continue; } // should remove spirit of redemption if(plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION)) - plr->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT); + plr->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); - if(!plr->isAlive()) + if (!plr->isAlive()) { plr->ResurrectPlayer(1.0f); plr->SpawnCorpseBones(); } - uint32 team = itr->second.Team; - if(!team) team = plr->GetTeam(); + //this line is obsolete - team is set ALWAYS + //if(!team) team = plr->GetTeam(); // per player calculation - if(isArena() && isRated() && winner_arena_team && loser_arena_team) + if (isArena() && isRated() && winner_arena_team && loser_arena_team) { - if(team == winner) + if (team == winner) winner_arena_team->MemberWon(plr,loser_rating); else loser_arena_team->MemberLost(plr,winner_rating); } - if(team == winner) + if (team == winner) { - if(!Source) - Source = plr; RewardMark(plr,ITEM_WINNER_COUNT); - UpdatePlayerScore(plr, SCORE_BONUS_HONOR, 20); RewardQuest(plr); } else if(winner !=0) { - RewardMark(plr,ITEM_LOSER_COUNT); + RewardMark(plr,ITEM_LOSER_COUNT); } - else if(winner == 0) - { - if(sWorld.getConfig(CONFIG_PREMATURE_BG_REWARD)) // We're feeling generous, giving rewards to people who not earned them ;) - { //nested ifs for the win! its boring writing that, forgive me my unfunniness - - if(almost_winning_team == team) //player's team had more points - RewardMark(plr,ITEM_WINNER_COUNT); - else - RewardMark(plr,ITEM_LOSER_COUNT); // if scores were the same, each team gets 1 mark. - } - } plr->CombatStopWithPets(true); @@ -593,12 +740,13 @@ void BattleGround::EndBattleGround(uint32 winner) sBattleGroundMgr.BuildPvpLogDataPacket(&data, this); plr->GetSession()->SendPacket(&data); - uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(GetTypeID(), GetArenaType()); - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetTeam(), plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime()); + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType()); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime(), GetArenaType()); plr->GetSession()->SendPacket(&data); + plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND, 1); } - if(isArena() && isRated() && winner_arena_team && loser_arena_team) + if (isArena() && isRated() && winner_arena_team && loser_arena_team) { // update arena points only after increasing the player's match count! //obsolete: winner_arena_team->UpdateArenaPointsHelper(); @@ -610,17 +758,16 @@ void BattleGround::EndBattleGround(uint32 winner) // this way all arena team members will get notified, not only the ones who participated in this match winner_arena_team->NotifyStatsChanged(); loser_arena_team->NotifyStatsChanged(); - sLog.outDebug("Rated arena match between %s and %s finished, winner: %s", loser_arena_team->GetName().c_str(),winner_arena_team->GetName().c_str(),winner_arena_team->GetName().c_str()); } - // inform invited players about the removal - sBattleGroundMgr.m_BattleGroundQueues[sBattleGroundMgr.BGQueueTypeId(GetTypeID(), GetArenaType())].BGEndedRemoveInvites(this); + if (winmsg_id) + SendMessageToAll(winmsg_id, CHAT_MSG_BG_SYSTEM_NEUTRAL); +} - if(Source) - { - ChatHandler(Source).FillMessageData(&data, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, Source->GetGUID(), winmsg); - SendPacketToAll(&data); - } +uint32 BattleGround::GetBonusHonorFromKill(uint32 kills) const +{ + //variable kills means how many honorable kills you scored (so we need kills * honor_for_one_kill) + return MaNGOS::Honor::hk_honor_at_level(GetMaxLevel(), kills); } uint32 BattleGround::GetBattlemasterEntry() const @@ -639,13 +786,13 @@ uint32 BattleGround::GetBattlemasterEntry() const void BattleGround::RewardMark(Player *plr,uint32 count) { // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens - if(plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE)) + if (plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE)) return; if(!plr || !count) return; - BattleGroundMarks mark; + BattleGroundMarks mark; switch(GetTypeID()) { case BATTLEGROUND_AV: @@ -657,7 +804,7 @@ void BattleGround::RewardMark(Player *plr,uint32 count) case BATTLEGROUND_AB: mark = ITEM_AB_MARK_OF_HONOR; break; - case BATTLEGROUND_EY: + case BATTLEGROUND_EY: mark = ITEM_EY_MARK_OF_HONOR; break; default: @@ -676,7 +823,7 @@ void BattleGround::RewardMark(Player *plr,uint32 count) if(Item* item = plr->StoreNewItem( dest, mark, true, 0)) plr->SendNewItem(item,count,false,true); - if(no_space_count > 0) + if (no_space_count > 0) SendRewardMarkByMail(plr,mark,no_space_count); } } @@ -684,14 +831,14 @@ void BattleGround::RewardMark(Player *plr,uint32 count) void BattleGround::SendRewardMarkByMail(Player *plr,uint32 mark, uint32 count) { uint32 bmEntry = GetBattlemasterEntry(); - if(!bmEntry) + if (!bmEntry) return; ItemPrototype const* markProto = objmgr.GetItemPrototype(mark); - if(!markProto) + if (!markProto) return; - if(Item* markItem = Item::CreateItem(mark,count,plr)) + if (Item* markItem = Item::CreateItem(mark,count,plr)) { // save new item before send markItem->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted @@ -703,8 +850,8 @@ void BattleGround::SendRewardMarkByMail(Player *plr,uint32 mark, uint32 count) // subject: item name std::string subject = markProto->Name1; int loc_idx = plr->GetSession()->GetSessionDbLocaleIndex(); - if ( loc_idx >= 0 ) - if(ItemLocale const *il = objmgr.GetItemLocale(markProto->ItemId)) + if (loc_idx >= 0 ) + if (ItemLocale const *il = objmgr.GetItemLocale(markProto->ItemId)) if (il->Name.size() > size_t(loc_idx) && !il->Name[loc_idx].empty()) subject = il->Name[loc_idx]; @@ -721,7 +868,7 @@ void BattleGround::SendRewardMarkByMail(Player *plr,uint32 mark, uint32 count) void BattleGround::RewardQuest(Player *plr) { // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens - if(plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE)) + if (plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE)) return; uint32 quest; @@ -756,17 +903,17 @@ void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPac uint32 team = GetPlayerTeam(guid); bool participant = false; // Remove from lists/maps - std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.find(guid); - if(itr != m_Players.end()) + BattleGroundPlayerMap::iterator itr = m_Players.find(guid); + if (itr != m_Players.end()) { - UpdatePlayersCountByTeam(team, true); // -1 player + UpdatePlayersCountByTeam(team, true); // -1 player m_Players.erase(itr); // check if the player was a participant of the match, or only entered through gm command (goname) participant = true; } std::map<uint64, BattleGroundScore*>::iterator itr2 = m_PlayerScores.find(guid); - if(itr2 != m_PlayerScores.end()) + if (itr2 != m_PlayerScores.end()) { delete itr2->second; // delete player's score m_PlayerScores.erase(itr2); @@ -778,7 +925,7 @@ void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPac // should remove spirit of redemption if(plr && plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION)) - plr->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT); + plr->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); if(plr && !plr->isAlive()) // resurrect on exit { @@ -788,100 +935,91 @@ void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPac RemovePlayer(plr, guid); // BG subclass specific code - if(plr) + if(participant) // if the player was a match participant, remove auras, calc rating, update queue { - plr->ClearAfkReports(); - - if(participant) // if the player was a match participant, remove auras, calc rating, update queue + BattleGroundTypeId bgTypeId = GetTypeID(); + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType()); + if (plr) { + plr->ClearAfkReports(); + if(!team) team = plr->GetTeam(); - uint32 bgTypeId = GetTypeID(); - uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(GetTypeID(), GetArenaType()); // if arena, remove the specific arena auras - if(isArena()) + if (isArena()) { - plr->RemoveArenaAuras(true); // removes debuffs / dots etc., we don't want the player to die after porting out - bgTypeId=BATTLEGROUND_AA; // set the bg type to all arenas (it will be used for queue refreshing) + plr->RemoveArenaAuras(true); // removes debuffs / dots etc., we don't want the player to die after porting out + bgTypeId=BATTLEGROUND_AA; // set the bg type to all arenas (it will be used for queue refreshing) - // summon old pet if there was one and there isn't a current pet - if(!plr->GetPet() && plr->GetTemporaryUnsummonedPetNumber()) - { - Pet* NewPet = new Pet; - if(!NewPet->LoadPetFromDB(plr, 0, (plr)->GetTemporaryUnsummonedPetNumber(), true)) - delete NewPet; + // unsummon current and summon old pet if there was one and there isn't a current pet + plr->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT); + plr->ResummonPetTemporaryUnSummonedIfAny(); - (plr)->SetTemporaryUnsummonedPetNumber(0); - } - - if(isRated() && GetStatus() == STATUS_IN_PROGRESS) + if (isRated() && GetStatus() == STATUS_IN_PROGRESS) { //left a rated match while the encounter was in progress, consider as loser - ArenaTeam * winner_arena_team = 0; - ArenaTeam * loser_arena_team = 0; - if(team == HORDE) - { - winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(ALLIANCE)); - loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(HORDE)); - } - else - { - winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(HORDE)); - loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(ALLIANCE)); - } - if(winner_arena_team && loser_arena_team) - { + ArenaTeam * winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team))); + ArenaTeam * loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(team)); + if (winner_arena_team && loser_arena_team) loser_arena_team->MemberLost(plr,winner_arena_team->GetRating()); - } } } - - WorldPacket data; - if(SendPacket) + if (SendPacket) { - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, team, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_NONE, 0, 0); + WorldPacket data; + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_NONE, 0, 0, 0); plr->GetSession()->SendPacket(&data); } // this call is important, because player, when joins to battleground, this method is not called, so it must be called when leaving bg plr->RemoveBattleGroundQueueId(bgQueueTypeId); - - DecreaseInvitedCount(team); - //we should update battleground queue, but only if bg isn't ending - if (GetQueueType() < MAX_BATTLEGROUND_QUEUES) - sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, GetQueueType()); - - Group * group = plr->GetGroup(); - // remove from raid group if exist - if(group && group == GetBgRaid(team)) + } + else + // removing offline participant + { + if (isRated() && GetStatus() == STATUS_IN_PROGRESS) { - if(!group->RemoveMember(guid, 0)) // group was disbanded - { - SetBgRaid(team, NULL); - delete group; - } + //left a rated match while the encounter was in progress, consider as loser + ArenaTeam * others_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team))); + ArenaTeam * players_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(team)); + if (others_arena_team && players_arena_team) + players_arena_team->OfflineMemberLost(guid, others_arena_team->GetRating()); } + } - // Let others know - sBattleGroundMgr.BuildPlayerLeftBattleGroundPacket(&data, plr); - SendPacketToTeam(team, &data, plr, false); + // remove from raid group if player is member + if (Group *group = GetBgRaid(team)) + { + if( !group->RemoveMember(guid, 0) ) // group was disbanded + { + SetBgRaid(team, NULL); + delete group; + } } + DecreaseInvitedCount(team); + //we should update battleground queue, but only if bg isn't ending + if (isBattleGround() && GetStatus() < STATUS_WAIT_LEAVE) + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, GetQueueId()); + // Let others know + WorldPacket data; + sBattleGroundMgr.BuildPlayerLeftBattleGroundPacket(&data, guid); + SendPacketToTeam(team, &data, plr, false); + } + if (plr) + { // Do next only if found in battleground - plr->SetBattleGroundId(0); // We're not in BG. + plr->SetBattleGroundId(0, BATTLEGROUND_TYPE_NONE); // We're not in BG. // reset destination bg team plr->SetBGTeam(0); - if(Transport) - { - plr->TeleportTo(plr->GetBattleGroundEntryPointMap(), plr->GetBattleGroundEntryPointX(), plr->GetBattleGroundEntryPointY(), plr->GetBattleGroundEntryPointZ(), plr->GetBattleGroundEntryPointO()); - } + if (Transport) + plr->TeleportTo(plr->GetBattleGroundEntryPoint()); - // Log sLog.outDetail("BATTLEGROUND: Removed player %s from BattleGround.", plr->GetName()); } - if(!GetPlayersSize() && !GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE)) + if (!GetPlayersSize() && !GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE)) { // if no players left AND no invitees left, set this bg to delete in next update // direct deletion could cause crashes @@ -897,7 +1035,7 @@ void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPac // this method is called when no players remains in battleground void BattleGround::Reset() { - SetQueueType(MAX_BATTLEGROUND_QUEUES); + SetQueueId(QUEUE_ID_MAX_LEVEL_19); SetWinner(WINNER_NONE); SetStatus(STATUS_WAIT_QUEUE); SetStartTime(0); @@ -909,7 +1047,7 @@ void BattleGround::Reset() m_Events = 0; if (m_InvitedAlliance > 0 || m_InvitedHorde > 0) - sLog.outError("BattleGround system ERROR: bad counter, m_InvitedAlliance: %d, m_InvitedHorde: %d", m_InvitedAlliance, m_InvitedHorde); + sLog.outError("BattleGround system: bad counter, m_InvitedAlliance: %d, m_InvitedHorde: %d", m_InvitedAlliance, m_InvitedHorde); m_InvitedAlliance = 0; m_InvitedHorde = 0; @@ -917,9 +1055,6 @@ void BattleGround::Reset() m_Players.clear(); m_PlayerScores.clear(); - - // reset BGSubclass - ResetBGSubclass(); } void BattleGround::StartBattleGround() @@ -938,7 +1073,7 @@ void BattleGround::AddPlayer(Player *plr) uint32 team = plr->GetBGTeam(); BattleGroundPlayer bp; - bp.LastOnlineTime = 0; + bp.OfflineRemoveTime = 0; bp.Team = team; // Add to list/maps @@ -951,40 +1086,28 @@ void BattleGround::AddPlayer(Player *plr) SendPacketToTeam(team, &data, plr, false); // add arena specific auras - if(isArena()) + if (isArena()) { plr->RemoveArenaSpellCooldowns(); plr->RemoveArenaAuras(); plr->RemoveAllEnchantments(TEMP_ENCHANTMENT_SLOT); if(team == ALLIANCE) // gold { - if(plr->GetTeam() == HORDE) + if (plr->GetTeam() == HORDE) plr->CastSpell(plr, SPELL_HORDE_GOLD_FLAG,true); else plr->CastSpell(plr, SPELL_ALLIANCE_GOLD_FLAG,true); } else // green { - if(plr->GetTeam() == HORDE) + if (plr->GetTeam() == HORDE) plr->CastSpell(plr, SPELL_HORDE_GREEN_FLAG,true); else plr->CastSpell(plr, SPELL_ALLIANCE_GREEN_FLAG,true); } plr->DestroyConjuredItems(true); - - Pet* pet = plr->GetPet(); - if(pet) - { - if(pet->getPetType() == SUMMON_PET || pet->getPetType() == HUNTER_PET) - { - (plr)->SetTemporaryUnsummonedPetNumber(pet->GetCharmInfo()->GetPetNumber()); - (plr)->SetOldPetSpell(pet->GetUInt32Value(UNIT_CREATED_BY_SPELL)); - } - (plr)->RemovePet(NULL,PET_SAVE_NOT_IN_SLOT); - } - else - (plr)->SetTemporaryUnsummonedPetNumber(0); + plr->UnsummonPetTemporaryIfAny(); if(GetStatus() == STATUS_WAIT_JOIN) // not started yet { @@ -1000,15 +1123,83 @@ void BattleGround::AddPlayer(Player *plr) plr->CastSpell(plr, SPELL_PREPARATION, true); // reduces all mana cost of spells. } + // setup BG group membership + PlayerAddedToBGCheckIfBGIsRunning(plr); + AddOrSetPlayerToCorrectBgGroup(plr, guid, team); + // Log sLog.outDetail("BATTLEGROUND: Player %s joined the battle.", plr->GetName()); } +/* this method adds player to his team's bg group, or sets his correct group if player is already in bg group */ +void BattleGround::AddOrSetPlayerToCorrectBgGroup(Player *plr, uint64 plr_guid, uint32 team) +{ + Group* group = GetBgRaid(team); + if(!group) // first player joined + { + group = new Group; + SetBgRaid(team, group); + group->Create(plr_guid, plr->GetName()); + } + else // raid already exist + { + if (group->IsMember(plr_guid)) + { + uint8 subgroup = group->GetMemberGroup(plr_guid); + plr->SetBattleGroundRaid(group, subgroup); + } + else + { + group->AddMember(plr_guid, plr->GetName()); + if (Group* originalGroup = plr->GetOriginalGroup()) + if (originalGroup->IsLeader(plr_guid)) + group->ChangeLeader(plr_guid); + } + } +} + +// This method should be called when player logs into running battleground +void BattleGround::EventPlayerLoggedIn(Player* player, uint64 plr_guid) +{ + // player is correct pointer + for(std::deque<uint64>::iterator itr = m_OfflineQueue.begin(); itr != m_OfflineQueue.end(); ++itr) + { + if (*itr == plr_guid) + { + m_OfflineQueue.erase(itr); + break; + } + } + m_Players[plr_guid].OfflineRemoveTime = 0; + PlayerAddedToBGCheckIfBGIsRunning(player); + // if battleground is starting, then add preparation aura + // we don't have to do that, because preparation aura isn't removed when player logs out +} + +// This method should be called when player logs out from running battleground +void BattleGround::EventPlayerLoggedOut(Player* player) +{ + // player is correct pointer, it is checked in WorldSession::LogoutPlayer() + m_OfflineQueue.push_back(player->GetGUID()); + m_Players[player->GetGUID()].OfflineRemoveTime = sWorld.GetGameTime() + MAX_OFFLINE_TIME; + if (GetStatus() == STATUS_IN_PROGRESS) + { + if (isBattleGround()) + EventPlayerDroppedFlag(player); + else + { + //1 player is logging out, if it is the last, then end arena! + if (GetAlivePlayersCountByTeam(player->GetTeam()) <= 1 && GetPlayersCountByTeam(GetOtherTeam(player->GetTeam()))) + EndBattleGround(GetOtherTeam(player->GetTeam())); + } + } +} + /* This method should be called only once ... it adds pointer to queue */ void BattleGround::AddToBGFreeSlotQueue() { // make sure to add only once - if(!m_InBGFreeSlotQueue) + if (!m_InBGFreeSlotQueue && isBattleGround()) { sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].push_front(this); m_InBGFreeSlotQueue = true; @@ -1021,7 +1212,7 @@ void BattleGround::RemoveFromBGFreeSlotQueue() // set to be able to re-add if needed m_InBGFreeSlotQueue = false; // uncomment this code when battlegrounds will work like instances - for (std::deque<BattleGround*>::iterator itr = sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].end(); ++itr) + for (BGFreeSlotQueueType::iterator itr = sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].end(); ++itr) { if ((*itr)->GetInstanceID() == m_InstanceID) { @@ -1032,61 +1223,12 @@ void BattleGround::RemoveFromBGFreeSlotQueue() } // get the number of free slots for team -// works in similar way that HasFreeSlotsForTeam did, but this is needed for join as group +// returns the number how many players can join battleground to MaxPlayersPerTeam uint32 BattleGround::GetFreeSlotsForTeam(uint32 Team) const { - //if BG is starting ... invite anyone - if (GetStatus() == STATUS_WAIT_JOIN) + //return free slot count to MaxPlayerPerTeam + if (GetStatus() == STATUS_WAIT_JOIN || GetStatus() == STATUS_IN_PROGRESS) return (GetInvitedCount(Team) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(Team) : 0; - //if BG is already started .. do not allow to join too much players of one faction - uint32 otherTeam; - uint32 otherIn; - if (Team == ALLIANCE) - { - otherTeam = GetInvitedCount(HORDE); - otherIn = GetPlayersCountByTeam(HORDE); - } - else - { - otherTeam = GetInvitedCount(ALLIANCE); - otherIn = GetPlayersCountByTeam(ALLIANCE); - } - if (GetStatus() == STATUS_IN_PROGRESS) - { - // difference based on ppl invited (not necessarily entered battle) - // default: allow 0 - uint32 diff = 0; - // allow join one person if the sides are equal (to fill up bg to minplayersperteam) - if (otherTeam == GetInvitedCount(Team)) - diff = 1; - // allow join more ppl if the other side has more players - else if(otherTeam > GetInvitedCount(Team)) - diff = otherTeam - GetInvitedCount(Team); - - // difference based on max players per team (don't allow inviting more) - uint32 diff2 = (GetInvitedCount(Team) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(Team) : 0; - - // difference based on players who already entered - // default: allow 0 - uint32 diff3 = 0; - // allow join one person if the sides are equal (to fill up bg minplayersperteam) - if (otherIn == GetPlayersCountByTeam(Team)) - diff3 = 1; - // allow join more ppl if the other side has more players - else if (otherIn > GetPlayersCountByTeam(Team)) - diff3 = otherIn - GetPlayersCountByTeam(Team); - // or other side has less than minPlayersPerTeam - else if (GetInvitedCount(Team) <= GetMinPlayersPerTeam()) - diff3 = GetMinPlayersPerTeam() - GetInvitedCount(Team) + 1; - - // return the minimum of the 3 differences - - // min of diff and diff 2 - diff = diff < diff2 ? diff : diff2; - - // min of diff, diff2 and diff3 - return diff < diff3 ? diff : diff3 ; - } return 0; } @@ -1117,10 +1259,10 @@ void BattleGround::UpdatePlayerScore(Player *Source, uint32 type, uint32 value) break; case SCORE_BONUS_HONOR: // Honor bonus // do not add honor in arenas - if(isBattleGround()) + if (isBattleGround()) { // reward honor instantly - if(Source->RewardHonor(NULL, 1, value)) + if (Source->RewardHonor(NULL, 1, value)) itr->second->BonusHonor += value; } break; @@ -1142,14 +1284,13 @@ void BattleGround::AddPlayerToResurrectQueue(uint64 npc_guid, uint64 player_guid m_ReviveQueue[npc_guid].push_back(player_guid); Player *plr = objmgr.GetPlayer(player_guid); - if(!plr) + if (!plr) return; - plr->CastSpell(plr, SPELL_WAITING_FOR_RESURRECT, true); SpellEntry const *spellInfo = sSpellStore.LookupEntry( SPELL_WAITING_FOR_RESURRECT ); - if(spellInfo) + if (spellInfo) { - Aura *Aur = CreateAura(spellInfo, 0, NULL, plr); + Aura *Aur = new Aura(spellInfo, 1, NULL, plr); plr->AddAura(Aur); } } @@ -1160,12 +1301,12 @@ void BattleGround::RemovePlayerFromResurrectQueue(uint64 player_guid) { for(std::vector<uint64>::iterator itr2 =(itr->second).begin(); itr2 != (itr->second).end(); ++itr2) { - if(*itr2 == player_guid) + if (*itr2 == player_guid) { (itr->second).erase(itr2); Player *plr = objmgr.GetPlayer(player_guid); - if(!plr) + if (!plr) return; plr->RemoveAurasDueToSpell(SPELL_WAITING_FOR_RESURRECT); @@ -1179,14 +1320,15 @@ void BattleGround::RemovePlayerFromResurrectQueue(uint64 player_guid) bool BattleGround::AddObject(uint32 type, uint32 entry, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime) { Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceID()); - if(!map) + if (!map) return false; // must be created this way, adding to godatamap would add it to the base map of the instance // and when loading it (in go::LoadFromDB()), a new guid would be assigned to the object, and a new object would be created // so we must create it specific for this instance GameObject * go = new GameObject; - if(!go->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT),entry, map,x,y,z,o,rotation0,rotation1,rotation2,rotation3,100,1)) + if(!go->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT),entry, map, + PHASEMASK_NORMAL, x,y,z,o,rotation0,rotation1,rotation2,rotation3,100,1)) { sLog.outErrorDb("Gameobject template %u not found in database! BattleGround not created!", entry); sLog.outError("Cannot create gameobject template %u! BattleGround not created!", entry); @@ -1226,10 +1368,10 @@ bool BattleGround::AddObject(uint32 type, uint32 entry, float x, float y, float void BattleGround::DoorClose(uint32 type) { GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]); - if(obj) + if (obj) { //if doors are open, close it - if( obj->getLootState() == GO_ACTIVATED && !obj->GetGoState() ) + if (obj->getLootState() == GO_ACTIVATED && !obj->GetGoState()) { //change state to allow door to be closed obj->SetLootState(GO_READY); @@ -1245,7 +1387,7 @@ void BattleGround::DoorClose(uint32 type) void BattleGround::DoorOpen(uint32 type) { GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]); - if(obj) + if (obj) { //change state to be sure they will be opened obj->SetLootState(GO_READY); @@ -1276,15 +1418,15 @@ Creature* BattleGround::GetBGCreature(uint32 type) void BattleGround::SpawnBGObject(uint32 type, uint32 respawntime) { Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceID()); - if(!map) + if (!map) return; - if( respawntime == 0 ) + if (respawntime == 0) { GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]); - if(obj) + if (obj) { //we need to change state from GO_JUST_DEACTIVATED to GO_READY in case battleground is starting again - if( obj->getLootState() == GO_JUST_DEACTIVATED ) + if (obj->getLootState() == GO_JUST_DEACTIVATED) obj->SetLootState(GO_READY); obj->SetRespawnTime(0); map->Add(obj); @@ -1293,7 +1435,7 @@ void BattleGround::SpawnBGObject(uint32 type, uint32 respawntime) else { GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]); - if(obj) + if (obj) { map->Add(obj); obj->SetRespawnTime(respawntime); @@ -1305,11 +1447,11 @@ void BattleGround::SpawnBGObject(uint32 type, uint32 respawntime) Creature* BattleGround::AddCreature(uint32 entry, uint32 type, uint32 teamval, float x, float y, float z, float o, uint32 respawntime) { Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceID()); - if(!map) + if (!map) return NULL; Creature* pCreature = new Creature; - if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, entry, teamval)) + if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, PHASEMASK_NORMAL, entry, teamval)) { sLog.outError("Can't create creature entry: %u",entry); delete pCreature; @@ -1318,14 +1460,14 @@ Creature* BattleGround::AddCreature(uint32 entry, uint32 type, uint32 teamval, f pCreature->Relocate(x, y, z, o); - if(!pCreature->IsPositionValid()) + if (!pCreature->IsPositionValid()) { - sLog.outError("ERROR: Creature (guidlow %d, entry %d) not added to battleground. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY()); + sLog.outError("Creature (guidlow %d, entry %d) not added to battleground. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY()); + delete pCreature; return NULL; } pCreature->SetHomePosition(x, y, z, o); - pCreature->AIM_Initialize(); //pCreature->SetDungeonDifficulty(0); @@ -1338,13 +1480,13 @@ Creature* BattleGround::AddCreature(uint32 entry, uint32 type, uint32 teamval, f void BattleGround::SpawnBGCreature(uint32 type, uint32 respawntime) { Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceId()); - if(!map) + if (!map) return false; - if(respawntime == 0) + if (respawntime == 0) { Creature *obj = HashMapHolder<Creature>::Find(m_BgCreatures[type]); - if(obj) + if (obj) { //obj->Respawn(); // bugged obj->SetRespawnTime(0); @@ -1355,7 +1497,7 @@ void BattleGround::SpawnBGCreature(uint32 type, uint32 respawntime) else { Creature *obj = HashMapHolder<Creature>::Find(m_BgCreatures[type]); - if(obj) + if (obj) { obj->setDeathState(DEAD); obj->SetRespawnTime(respawntime); @@ -1366,8 +1508,11 @@ void BattleGround::SpawnBGCreature(uint32 type, uint32 respawntime) */ bool BattleGround::DelCreature(uint32 type) { + if (!m_BgCreatures[type]) + return true; + Creature *cr = HashMapHolder<Creature>::Find(m_BgCreatures[type]); - if(!cr) + if (!cr) { sLog.outError("Can't find creature guid: %u",GUID_LOPART(m_BgCreatures[type])); return false; @@ -1381,8 +1526,11 @@ bool BattleGround::DelCreature(uint32 type) bool BattleGround::DelObject(uint32 type) { + if (!m_BgObjects[type]) + return true; + GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]); - if(!obj) + if (!obj) { sLog.outError("Can't find gobject guid: %u",GUID_LOPART(m_BgObjects[type])); return false; @@ -1397,13 +1545,13 @@ bool BattleGround::AddSpiritGuide(uint32 type, float x, float y, float z, float { uint32 entry = 0; - if(team == ALLIANCE) + if (team == ALLIANCE) entry = 13116; else entry = 13117; Creature* pCreature = AddCreature(entry,type,team,x,y,z,o); - if(!pCreature) + if (!pCreature) { sLog.outError("Can't create Spirit guide. BattleGround not created!"); EndNow(); @@ -1414,10 +1562,12 @@ bool BattleGround::AddSpiritGuide(uint32 type, float x, float y, float z, float pCreature->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, pCreature->GetGUID()); // aura - pCreature->SetUInt32Value(UNIT_FIELD_AURA, SPELL_SPIRIT_HEAL_CHANNEL); - pCreature->SetUInt32Value(UNIT_FIELD_AURAFLAGS, 0x00000009); - pCreature->SetUInt32Value(UNIT_FIELD_AURALEVELS, 0x0000003C); - pCreature->SetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS, 0x000000FF); + //TODO: Fix display here + //pCreature->SetVisibleAura(0, SPELL_SPIRIT_HEAL_CHANNEL); + + //pCreature->SetUInt32Value(UNIT_FIELD_AURAFLAGS, 0x00000009); + //pCreature->SetUInt32Value(UNIT_FIELD_AURALEVELS, 0x0000003C); + //pCreature->SetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS, 0x000000FF); // casting visual effect pCreature->SetUInt32Value(UNIT_CHANNEL_SPELL, SPELL_SPIRIT_HEAL_CHANNEL); // correct cast speed @@ -1428,31 +1578,40 @@ bool BattleGround::AddSpiritGuide(uint32 type, float x, float y, float z, float return true; } -void BattleGround::SendMessageToAll(char const* text) +void BattleGround::SendMessageToAll(int32 entry, ChatMsg type, Player const* source) { - WorldPacket data; - ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, text, NULL); - SendPacketToAll(&data); + MaNGOS::BattleGroundChatBuilder bg_builder(type, entry, source); + MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundChatBuilder> bg_do(bg_builder); + BroadcastWorker(bg_do); } -void BattleGround::SendMessageToAll(int32 entry) +void BattleGround::PSendMessageToAll(int32 entry, ChatMsg type, Player const* source, ...) { - char const* text = GetTrinityString(entry); - WorldPacket data; - ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, text, NULL); - SendPacketToAll(&data); + va_list ap; + va_start(ap, source); + + MaNGOS::BattleGroundChatBuilder bg_builder(type, entry, source, &ap); + MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundChatBuilder> bg_do(bg_builder); + BroadcastWorker(bg_do); + + va_end(ap); +} + +void BattleGround::SendMessage2ToAll(int32 entry, ChatMsg type, Player const* source, int32 arg1, int32 arg2) +{ + MaNGOS::BattleGround2ChatBuilder bg_builder(type, entry, source, arg1, arg2); + MaNGOS::LocalizedPacketDo<MaNGOS::BattleGround2ChatBuilder> bg_do(bg_builder); + BroadcastWorker(bg_do); } void BattleGround::EndNow() { RemoveFromBGFreeSlotQueue(); SetStatus(STATUS_WAIT_LEAVE); - SetEndTime(TIME_TO_AUTOREMOVE); - // inform invited players about the removal - sBattleGroundMgr.m_BattleGroundQueues[sBattleGroundMgr.BGQueueTypeId(GetTypeID(), GetArenaType())].BGEndedRemoveInvites(this); + SetEndTime(0); } -// Battleground messages are localized using the dbc lang, they are not client language dependent +//to be removed const char *BattleGround::GetTrinityString(int32 entry) { // FIXME: now we have different DBC locales and need localized message for each target client @@ -1467,7 +1626,7 @@ buffs are in their positions when battleground starts void BattleGround::HandleTriggerBuff(uint64 const& go_guid) { GameObject *obj = HashMapHolder<GameObject>::Find(go_guid); - if(!obj || obj->GetGoType() != GAMEOBJECT_TYPE_TRAP || !obj->isSpawned()) + if (!obj || obj->GetGoType() != GAMEOBJECT_TYPE_TRAP || !obj->isSpawned()) return; //change buff type, when buff is used: @@ -1483,13 +1642,13 @@ void BattleGround::HandleTriggerBuff(uint64 const& go_guid) //randomly select new buff uint8 buff = urand(0, 2); uint32 entry = obj->GetEntry(); - if( m_BuffChange && entry != Buff_Entries[buff] ) + if (m_BuffChange && entry != Buff_Entries[buff]) { //despawn current buff SpawnBGObject(index, RESPAWN_ONE_DAY); //set index for new one for (uint8 currBuffTypeIndex = 0; currBuffTypeIndex < 3; ++currBuffTypeIndex) - if( entry == Buff_Entries[currBuffTypeIndex] ) + if (entry == Buff_Entries[currBuffTypeIndex]) { index -= currBuffTypeIndex; index += buff; @@ -1507,46 +1666,77 @@ void BattleGround::HandleKillPlayer( Player *player, Player *killer ) UpdatePlayerScore(player, SCORE_DEATHS, 1); // add +1 kills to group and +1 killing_blows to killer - if( killer ) + if (killer) { UpdatePlayerScore(killer, SCORE_HONORABLE_KILLS, 1); UpdatePlayerScore(killer, SCORE_KILLING_BLOWS, 1); - for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + for(BattleGroundPlayerMap::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) { Player *plr = objmgr.GetPlayer(itr->first); - if(!plr || plr == killer) + if (!plr || plr == killer) continue; - if( plr->GetTeam() == killer->GetTeam() && plr->IsAtGroupRewardDistance(player) ) + if (plr->GetTeam() == killer->GetTeam() && plr->IsAtGroupRewardDistance(player)) UpdatePlayerScore(plr, SCORE_HONORABLE_KILLS, 1); } } - // to be able to remove insignia - player->SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE ); + // to be able to remove insignia -- ONLY IN BattleGrounds + if (!isArena()) + player->SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE ); } // return the player's team based on battlegroundplayer info // used in same faction arena matches mainly uint32 BattleGround::GetPlayerTeam(uint64 guid) { - std::map<uint64, BattleGroundPlayer>::const_iterator itr = m_Players.find(guid); - if(itr!=m_Players.end()) + BattleGroundPlayerMap::const_iterator itr = m_Players.find(guid); + if (itr!=m_Players.end()) return itr->second.Team; return 0; } +uint32 BattleGround::GetOtherTeam(uint32 teamId) +{ + return (teamId) ? ((teamId == ALLIANCE) ? HORDE : ALLIANCE) : 0; +} + +bool BattleGround::IsPlayerInBattleGround(uint64 guid) +{ + BattleGroundPlayerMap::const_iterator itr = m_Players.find(guid); + if (itr != m_Players.end()) + return true; + return false; +} + +void BattleGround::PlayerAddedToBGCheckIfBGIsRunning(Player* plr) +{ + if (GetStatus() != STATUS_WAIT_LEAVE) + return; + + WorldPacket data; + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType()); + + BlockMovement(plr); + + sBattleGroundMgr.BuildPvpLogDataPacket(&data, this); + plr->GetSession()->SendPacket(&data); + + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, GetEndTime(), GetStartTime(), GetArenaType()); + plr->GetSession()->SendPacket(&data); +} + uint32 BattleGround::GetAlivePlayersCountByTeam(uint32 Team) const { int count = 0; - for(std::map<uint64, BattleGroundPlayer>::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) { - if(itr->second.Team == Team) + if (itr->second.Team == Team) { Player * pl = objmgr.GetPlayer(itr->first); - if(pl && pl->isAlive()) + if (pl && pl->isAlive()) ++count; } } @@ -1574,3 +1764,23 @@ void BattleGround::HandleKillUnit(Creature *creature, Player *killer) { } +void BattleGround::CheckArenaWinConditions() +{ + if (!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) + EndBattleGround(HORDE); + else if (GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE)) + EndBattleGround(ALLIANCE); +} + +void BattleGround::SetBgRaid( uint32 TeamID, Group *bg_raid ) +{ + Group* &old_raid = TeamID == ALLIANCE ? m_BgRaids[BG_TEAM_ALLIANCE] : m_BgRaids[BG_TEAM_HORDE]; + if(old_raid) old_raid->SetBattlegroundGroup(NULL); + if(bg_raid) bg_raid->SetBattlegroundGroup(this); + old_raid = bg_raid; +} + +WorldSafeLocsEntry const* BattleGround::GetClosestGraveYard( Player* player ) +{ + return objmgr.GetClosestGraveYard( player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetMapId(), player->GetTeam() ); +} diff --git a/src/game/BattleGround.h b/src/game/BattleGround.h index c9592cebe63..9788f264502 100644 --- a/src/game/BattleGround.h +++ b/src/game/BattleGround.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,13 +22,16 @@ #define __BATTLEGROUND_H #include "Common.h" -#include "WorldPacket.h" -#include "WorldSession.h" -#include "Opcodes.h" -#include "ObjectMgr.h" -#include "BattleGroundMgr.h" #include "SharedDefines.h" +class Creature; +class GameObject; +class Group; +class Player; +class WorldPacket; + +struct WorldSafeLocsEntry; + enum BattleGroundSounds { SOUND_HORDE_WINS = 8454, @@ -83,17 +86,22 @@ enum BattleGroundTimeIntervals { RESURRECTION_INTERVAL = 30000, // ms REMIND_INTERVAL = 30000, // ms + INVITATION_REMIND_TIME = 60000, // ms INVITE_ACCEPT_WAIT_TIME = 80000, // ms TIME_TO_AUTOREMOVE = 120000, // ms - MAX_OFFLINE_TIME = 300000, // ms - START_DELAY0 = 120000, // ms - START_DELAY1 = 60000, // ms - START_DELAY2 = 30000, // ms - START_DELAY3 = 15000, // ms used only in arena + MAX_OFFLINE_TIME = 300, // secs RESPAWN_ONE_DAY = 86400, // secs RESPAWN_IMMEDIATELY = 0, // secs BUFF_RESPAWN_TIME = 180, // secs - BG_HONOR_SCORE_TICKS = 330 // points +}; + +enum BattleGroundStartTimeIntervals +{ + BG_START_DELAY_2M = 120000, // ms (2 minutes) + BG_START_DELAY_1M = 60000, // ms (1 minute) + BG_START_DELAY_30S = 30000, // ms (30 seconds) + BG_START_DELAY_15S = 15000, // ms (15 seconds) Used only in arena + BG_START_DELAY_NONE = 0, // ms }; enum BattleGroundBuffObjects @@ -107,16 +115,16 @@ const uint32 Buff_Entries[3] = { BG_OBJECTID_SPEEDBUFF_ENTRY, BG_OBJECTID_REGENB enum BattleGroundStatus { - STATUS_NONE = 0, - STATUS_WAIT_QUEUE = 1, - STATUS_WAIT_JOIN = 2, - STATUS_IN_PROGRESS = 3, - STATUS_WAIT_LEAVE = 4 // custom + STATUS_NONE = 0, // first status, should mean bg is not instance + STATUS_WAIT_QUEUE = 1, // means bg is empty and waiting for queue + STATUS_WAIT_JOIN = 2, // this means, that BG has already started and it is waiting for more players + STATUS_IN_PROGRESS = 3, // means bg is running + STATUS_WAIT_LEAVE = 4 // means some faction has won BG and it is ending }; struct BattleGroundPlayer { - uint32 LastOnlineTime; // for tracking and removing offline players from queue after 5 minutes + time_t OfflineRemoveTime; // for tracking and removing offline players from queue after 5 minutes uint32 Team; // Player's team }; @@ -129,31 +137,33 @@ struct BattleGroundObjectInfo uint32 spellid; }; -#define MAX_QUEUED_PLAYERS_MAP 7 - -enum BattleGroundTypeId +// handle the queue types and bg types separately to enable joining queue for different sized arenas at the same time +enum BattleGroundQueueTypeId { - BATTLEGROUND_AV = 1, - BATTLEGROUND_WS = 2, - BATTLEGROUND_AB = 3, - BATTLEGROUND_NA = 4, - BATTLEGROUND_BE = 5, - BATTLEGROUND_AA = 6, - BATTLEGROUND_EY = 7, - BATTLEGROUND_RL = 8 + BATTLEGROUND_QUEUE_NONE = 0, + BATTLEGROUND_QUEUE_AV = 1, + BATTLEGROUND_QUEUE_WS = 2, + BATTLEGROUND_QUEUE_AB = 3, + BATTLEGROUND_QUEUE_EY = 4, + BATTLEGROUND_QUEUE_SA = 5, + BATTLEGROUND_QUEUE_2v2 = 6, + BATTLEGROUND_QUEUE_3v3 = 7, + BATTLEGROUND_QUEUE_5v5 = 8 }; +#define MAX_BATTLEGROUND_QUEUE_TYPES 9 -// handle the queue types and bg types separately to enable joining queue for different sized arenas at the same time -enum BattleGroundQueueTypeId +enum BGQueueIdBasedOnLevel // queue_id for level ranges { - BATTLEGROUND_QUEUE_AV = 1, - BATTLEGROUND_QUEUE_WS = 2, - BATTLEGROUND_QUEUE_AB = 3, - BATTLEGROUND_QUEUE_EY = 4, - BATTLEGROUND_QUEUE_2v2 = 5, - BATTLEGROUND_QUEUE_3v3 = 6, - BATTLEGROUND_QUEUE_5v5 = 7, + QUEUE_ID_MAX_LEVEL_19 = 0, + QUEUE_ID_MAX_LEVEL_29 = 1, + QUEUE_ID_MAX_LEVEL_39 = 2, + QUEUE_ID_MAX_LEVEL_49 = 3, + QUEUE_ID_MAX_LEVEL_59 = 4, + QUEUE_ID_MAX_LEVEL_69 = 5, + QUEUE_ID_MAX_LEVEL_79 = 6, + QUEUE_ID_MAX_LEVEL_80 = 7 }; +#define MAX_BATTLEGROUND_QUEUES 8 enum ScoreType { @@ -206,6 +216,25 @@ enum BattleGroundTeamId BG_TEAM_ALLIANCE = 0, BG_TEAM_HORDE = 1 }; +#define BG_TEAMS_COUNT 2 + +enum BattleGroundStartingEvents +{ + BG_STARTING_EVENT_NONE = 0x00, + BG_STARTING_EVENT_1 = 0x01, + BG_STARTING_EVENT_2 = 0x02, + BG_STARTING_EVENT_3 = 0x04, + BG_STARTING_EVENT_4 = 0x08 +}; + +enum BattleGroundStartingEventsIds +{ + BG_STARTING_EVENT_FIRST = 0, + BG_STARTING_EVENT_SECOND = 1, + BG_STARTING_EVENT_THIRD = 2, + BG_STARTING_EVENT_FOURTH = 3 +}; +#define BG_STARTING_EVENT_COUNT 4 enum BattleGroundJoinError { @@ -224,10 +253,11 @@ enum BattleGroundJoinError class BattleGroundScore { public: - BattleGroundScore() : KillingBlows(0), HonorableKills(0), Deaths(0), DamageDone(0), HealingDone(0), BonusHonor(0) {}; - virtual ~BattleGroundScore() //virtual destructor is used when deleting score from scores map - { - }; + BattleGroundScore() : KillingBlows(0), Deaths(0), HonorableKills(0), + BonusHonor(0), DamageDone(0), HealingDone(0) + {} + virtual ~BattleGroundScore() {} //virtual destructor is used when deleting score from scores map + uint32 KillingBlows; uint32 Deaths; uint32 HonorableKills; @@ -259,23 +289,23 @@ class BattleGround BattleGround(); /*BattleGround(const BattleGround& bg);*/ virtual ~BattleGround(); - virtual void Update(time_t diff); // must be implemented in BG subclass of BG specific update code, but must in begginning call parent version + virtual void Update(uint32 diff); // must be implemented in BG subclass of BG specific update code, but must in begginning call parent version virtual bool SetupBattleGround() // must be implemented in BG subclass { return true; } - void Reset(); // resets all common properties for battlegrounds - virtual void ResetBGSubclass() // must be implemented in BG subclass - { - } + virtual void Reset(); // resets all common properties for battlegrounds, must be implemented and called in BG subclass + virtual void StartingEventCloseDoors() {} + virtual void StartingEventOpenDoors() {} /* Battleground */ // Get methods: char const* GetName() const { return m_Name; } - uint32 GetTypeID() const { return m_TypeID; } - uint32 GetQueueType() const { return m_Queue_type; } + BattleGroundTypeId GetTypeID() const { return m_TypeID; } + BGQueueIdBasedOnLevel GetQueueId() const { return m_QueueId; } uint32 GetInstanceID() const { return m_InstanceID; } - uint32 GetStatus() const { return m_Status; } + BattleGroundStatus GetStatus() const { return m_Status; } + uint32 GetClientInstanceID() const { return m_ClientInstanceID; } uint32 GetStartTime() const { return m_StartTime; } uint32 GetEndTime() const { return m_EndTime; } uint32 GetLastResurrectTime() const { return m_LastResurrectTime; } @@ -288,17 +318,25 @@ class BattleGround uint32 GetMaxPlayersPerTeam() const { return m_MaxPlayersPerTeam; } uint32 GetMinPlayersPerTeam() const { return m_MinPlayersPerTeam; } - int GetStartDelayTime() const { return m_StartDelayTime; } + int32 GetStartDelayTime() const { return m_StartDelayTime; } uint8 GetArenaType() const { return m_ArenaType; } uint8 GetWinner() const { return m_Winner; } uint32 GetBattlemasterEntry() const; + uint32 GetBonusHonorFromKill(uint32 kills) const; // Set methods: void SetName(char const* Name) { m_Name = Name; } - void SetTypeID(uint32 TypeID) { m_TypeID = TypeID; } - void SetQueueType(uint32 ID) { m_Queue_type = ID; } + void SetTypeID(BattleGroundTypeId TypeID) { m_TypeID = TypeID; } + //here we can count minlevel and maxlevel for players + void SetQueueId(BGQueueIdBasedOnLevel ID) + { + m_QueueId = ID; + uint8 diff = (m_TypeID == BATTLEGROUND_AV) ? 1 : 0; + this->SetLevelRange((ID + 1) * 10 + diff, (ID + 2) * 10 - ((diff + 1) % 2)); + } void SetInstanceID(uint32 InstanceID) { m_InstanceID = InstanceID; } - void SetStatus(uint32 Status) { m_Status = Status; } + void SetStatus(BattleGroundStatus Status) { m_Status = Status; } + void SetClientInstanceID(uint32 InstanceID) { m_ClientInstanceID = InstanceID; } void SetStartTime(uint32 Time) { m_StartTime = Time; } void SetEndTime(uint32 Time) { m_EndTime = Time; } void SetLastResurrectTime(uint32 Time) { m_LastResurrectTime = Time; } @@ -323,12 +361,11 @@ class BattleGround void IncreaseInvitedCount(uint32 team) { (team == ALLIANCE) ? ++m_InvitedAlliance : ++m_InvitedHorde; } uint32 GetInvitedCount(uint32 team) const { - if( team == ALLIANCE ) + if (team == ALLIANCE) return m_InvitedAlliance; else return m_InvitedHorde; } - bool HasFreeSlotsForTeam(uint32 Team) const; bool HasFreeSlots() const; uint32 GetFreeSlotsForTeam(uint32 Team) const; @@ -339,7 +376,6 @@ class BattleGround typedef std::map<uint64, BattleGroundPlayer> BattleGroundPlayerMap; BattleGroundPlayerMap const& GetPlayers() const { return m_Players; } uint32 GetPlayersSize() const { return m_Players.size(); } - uint32 GetRemovedPlayersSize() const { return m_RemovedPlayers.size(); } std::map<uint64, BattleGroundScore*>::const_iterator GetPlayerScoresBegin() const { return m_PlayerScores.begin(); } std::map<uint64, BattleGroundScore*>::const_iterator GetPlayerScoresEnd() const { return m_PlayerScores.end(); } @@ -374,6 +410,10 @@ class BattleGround void SendPacketToTeam(uint32 TeamID, WorldPacket *packet, Player *sender = NULL, bool self = true); void SendPacketToAll(WorldPacket *packet); void YellToAll(Creature* creature, const char* text, uint32 language); + + template<class Do> + void BroadcastWorker(Do& _do); + void PlaySoundToTeam(uint32 SoundID, uint32 TeamID); void PlaySoundToAll(uint32 SoundID); void CastSpellOnTeam(uint32 SpellID, uint32 TeamID); @@ -387,18 +427,15 @@ class BattleGround void EndBattleGround(uint32 winner); void BlockMovement(Player *plr); - void SendMessageToAll(char const* text); - void SendMessageToAll(int32 entry); + void SendMessageToAll(int32 entry, ChatMsg type, Player const* source = NULL); + void PSendMessageToAll(int32 entry, ChatMsg type, Player const* source, ... ); + + // specialized version with 2 string id args + void SendMessage2ToAll(int32 entry, ChatMsg type, Player const* source, int32 strId1 = 0, int32 strId2 = 0); /* Raid Group */ Group *GetBgRaid(uint32 TeamID) const { return TeamID == ALLIANCE ? m_BgRaids[BG_TEAM_ALLIANCE] : m_BgRaids[BG_TEAM_HORDE]; } - void SetBgRaid(uint32 TeamID, Group *bg_raid) - { - Group* &old_raid = TeamID == ALLIANCE ? m_BgRaids[BG_TEAM_ALLIANCE] : m_BgRaids[BG_TEAM_HORDE]; - if(old_raid) old_raid->SetBattlegroundGroup(NULL); - if(bg_raid) bg_raid->SetBattlegroundGroup(this); - old_raid = bg_raid; - } + void SetBgRaid(uint32 TeamID, Group *bg_raid); virtual void UpdatePlayerScore(Player *Source, uint32 type, uint32 value); @@ -407,7 +444,7 @@ class BattleGround uint32 GetAlivePlayersCountByTeam(uint32 Team) const; // used in arenas to correctly handle death in spirit of redemption / last stand etc. (killer = killed) cases void UpdatePlayersCountByTeam(uint32 Team, bool remove) { - if(remove) + if (remove) --m_PlayersCount[GetTeamIndexByTeamId(Team)]; else ++m_PlayersCount[GetTeamIndexByTeamId(Team)]; @@ -415,9 +452,10 @@ class BattleGround // used for rated arena battles void SetArenaTeamIdForTeam(uint32 Team, uint32 ArenaTeamId) { m_ArenaTeamIds[GetTeamIndexByTeamId(Team)] = ArenaTeamId; } - uint32 GetArenaTeamIdForTeam(uint32 Team) const { return m_ArenaTeamIds[GetTeamIndexByTeamId(Team)]; } + uint32 GetArenaTeamIdForTeam(uint32 Team) const { return m_ArenaTeamIds[GetTeamIndexByTeamId(Team)]; } void SetArenaTeamRatingChangeForTeam(uint32 Team, int32 RatingChange) { m_ArenaTeamRatingChanges[GetTeamIndexByTeamId(Team)] = RatingChange; } - int32 GetArenaTeamRatingChangeForTeam(uint32 Team) const { return m_ArenaTeamRatingChanges[GetTeamIndexByTeamId(Team)]; } + int32 GetArenaTeamRatingChangeForTeam(uint32 Team) const { return m_ArenaTeamRatingChanges[GetTeamIndexByTeamId(Team)]; } + void CheckArenaWinConditions(); /* Triggers handle */ // must be implemented in BG subclass @@ -427,16 +465,19 @@ class BattleGround virtual void HandleKillUnit(Creature* /*unit*/, Player* /*killer*/); /* Battleground events */ - /* these functions will return true event is possible, but false if player is bugger */ virtual void EventPlayerDroppedFlag(Player* /*player*/) {} virtual void EventPlayerClickedOnFlag(Player* /*player*/, GameObject* /*target_obj*/) {} virtual void EventPlayerCapturedFlag(Player* /*player*/) {} + void EventPlayerLoggedIn(Player* player, uint64 plr_guid); + void EventPlayerLoggedOut(Player* player); /* Death related */ - virtual WorldSafeLocsEntry const* GetClosestGraveYard(float /*x*/, float /*y*/, float /*z*/, uint32 /*team*/) { return NULL; } + virtual WorldSafeLocsEntry const* GetClosestGraveYard(Player* player); virtual void AddPlayer(Player *plr); // must be implemented in BG subclass + void AddOrSetPlayerToCorrectBgGroup(Player *plr, uint64 plr_guid, uint32 team); + virtual void RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPacket); // can be extended in in BG subclass @@ -459,18 +500,22 @@ class BattleGround void DoorOpen(uint32 type); void DoorClose(uint32 type); + //to be removed const char *GetTrinityString(int32 entry); - virtual bool HandlePlayerUnderMap(Player * plr) {return false;} + virtual bool HandlePlayerUnderMap(Player * /*plr*/) { return false; } // since arenas can be AvA or Hvh, we have to get the "temporary" team of a player uint32 GetPlayerTeam(uint64 guid); + uint32 GetOtherTeam(uint32 teamId); + bool IsPlayerInBattleGround(uint64 guid); void SetDeleteThis() {m_SetDeleteThis = true;} protected: //this method is called, when BG cannot spawn its own spirit guide, or something is wrong, It correctly ends BattleGround void EndNow(); + void PlayerAddedToBGCheckIfBGIsRunning(Player* plr); /* Scorekeeping */ // Player scores @@ -484,29 +529,29 @@ class BattleGround std::map<uint64, std::vector<uint64> > m_ReviveQueue; /* - this is important variable used for invitation messages + these are important variables used for starting messages */ uint8 m_Events; + BattleGroundStartTimeIntervals m_StartDelayTimes[BG_STARTING_EVENT_COUNT]; + //this must be filled in constructors! + uint32 m_StartMessageIds[BG_STARTING_EVENT_COUNT]; bool m_BuffChange; - uint32 m_score[2]; //array that keeps general team scores, used to determine who gets most marks when bg ends prematurely BGHonorMode m_HonorMode; private: /* Battleground */ - uint32 m_TypeID; //Battleground type, defined in enum BattleGroundTypeId + BattleGroundTypeId m_TypeID; uint32 m_InstanceID; //BattleGround Instance's GUID! - uint32 m_Status; + BattleGroundStatus m_Status; + uint32 m_ClientInstanceID; //the instance-id which is sent to the client and without any other internal use uint32 m_StartTime; - uint32 m_EndTime; + int32 m_EndTime; // it is set to 120000 when bg is ending and it decreases itself uint32 m_LastResurrectTime; - uint32 m_Queue_type; + BGQueueIdBasedOnLevel m_QueueId; uint8 m_ArenaType; // 2=2v2, 3=3v3, 5=5v5 bool m_InBGFreeSlotQueue; // used to make sure that BG is only once inserted into the BattleGroundMgr.BGFreeSlotQueue[bgTypeId] deque bool m_SetDeleteThis; // used for safe deletion of the bg after end / all players leave - // this variable is not used .... it can be found in many other ways... but to store it in BG object instance is useless - //uint8 m_BattleGroundType; // 3=BG, 4=arena - //instead of uint8 (in previous line) is bool used bool m_IsArena; uint8 m_Winner; // 0=alliance, 1=horde, 2=none int32 m_StartDelayTime; @@ -514,11 +559,10 @@ class BattleGround bool m_PrematureCountDown; uint32 m_PrematureCountDownTimer; char const *m_Name; - /* Player lists */ std::vector<uint64> m_ResurrectQueue; // Player GUID - std::map<uint64, uint8> m_RemovedPlayers; // uint8 is remove type (0 - bgqueue, 1 - bg, 2 - resurrect queue) + std::deque<uint64> m_OfflineQueue; // Player GUID /* Invited counters are useful for player invitation to BG - do not allow, if BG is started to one faction to have 2 more players than another faction */ /* Invited counters will be changed only when removing already invited player from queue, removing player from battleground and inviting player to BG */ @@ -527,15 +571,15 @@ class BattleGround uint32 m_InvitedHorde; /* Raid Group */ - Group *m_BgRaids[2]; // 0 - alliance, 1 - horde + Group *m_BgRaids[BG_TEAMS_COUNT]; // 0 - alliance, 1 - horde /* Players count by team */ - uint32 m_PlayersCount[2]; + uint32 m_PlayersCount[BG_TEAMS_COUNT]; /* Arena team ids by team */ - uint32 m_ArenaTeamIds[2]; + uint32 m_ArenaTeamIds[BG_TEAMS_COUNT]; - int32 m_ArenaTeamRatingChanges[2]; + int32 m_ArenaTeamRatingChanges[BG_TEAMS_COUNT]; /* Limits */ uint32 m_LevelMin; @@ -545,12 +589,12 @@ class BattleGround uint32 m_MinPlayersPerTeam; uint32 m_MinPlayers; - /* Location */ + /* Start location */ uint32 m_MapId; - float m_TeamStartLocX[2]; - float m_TeamStartLocY[2]; - float m_TeamStartLocZ[2]; - float m_TeamStartLocO[2]; + float m_TeamStartLocX[BG_TEAMS_COUNT]; + float m_TeamStartLocY[BG_TEAMS_COUNT]; + float m_TeamStartLocZ[BG_TEAMS_COUNT]; + float m_TeamStartLocO[BG_TEAMS_COUNT]; }; #endif diff --git a/src/game/BattleGroundAA.cpp b/src/game/BattleGroundAA.cpp index ad9720ad5fe..2aac2dde3eb 100644 --- a/src/game/BattleGroundAA.cpp +++ b/src/game/BattleGroundAA.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,10 +21,20 @@ #include "Player.h" #include "BattleGround.h" #include "BattleGroundAA.h" +#include "Language.h" BattleGroundAA::BattleGroundAA() { + m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_1M; + m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_30S; + m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_15S; + m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE; + //we must set messageIds + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_ARENA_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_ARENA_THIRTY_SECONDS; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_ARENA_FIFTEEN_SECONDS; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_ARENA_HAS_BEGUN; } BattleGroundAA::~BattleGroundAA() @@ -32,11 +42,19 @@ BattleGroundAA::~BattleGroundAA() } -void BattleGroundAA::Update(time_t diff) +void BattleGroundAA::Update(uint32 diff) { BattleGround::Update(diff); } +void BattleGroundAA::StartingEventCloseDoors() +{ +} + +void BattleGroundAA::StartingEventOpenDoors() +{ +} + void BattleGroundAA::AddPlayer(Player *plr) { BattleGround::AddPlayer(plr); diff --git a/src/game/BattleGroundAA.h b/src/game/BattleGroundAA.h index 8a07831eb38..06793ea38e0 100644 --- a/src/game/BattleGroundAA.h +++ b/src/game/BattleGroundAA.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,10 +37,13 @@ class BattleGroundAA : public BattleGround public: BattleGroundAA(); ~BattleGroundAA(); - void Update(time_t diff); + void Update(uint32 diff); /* inherited from BattlegroundClass */ virtual void AddPlayer(Player *plr); + virtual void StartingEventCloseDoors(); + virtual void StartingEventOpenDoors(); + void RemovePlayer(Player *plr, uint64 guid); void HandleAreaTrigger(Player *Source, uint32 Trigger); bool SetupBattleGround(); diff --git a/src/game/BattleGroundAB.cpp b/src/game/BattleGroundAB.cpp index 1cd5cfad420..40f60960377 100644 --- a/src/game/BattleGroundAB.cpp +++ b/src/game/BattleGroundAB.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,12 +23,11 @@ #include "BattleGround.h" #include "BattleGroundAB.h" #include "Creature.h" -#include "Chat.h" #include "ObjectMgr.h" -#include "MapManager.h" #include "Language.h" #include "World.h" #include "Util.h" +#include "WorldPacket.h" // these variables aren't used outside of this file, so declare them only here uint32 BG_AB_HonorScoreTicks[BG_HONOR_MODE_NUM] = { @@ -46,102 +45,31 @@ BattleGroundAB::BattleGroundAB() m_BuffChange = true; m_BgObjects.resize(BG_AB_OBJECT_MAX); m_BgCreatures.resize(BG_AB_ALL_NODES_COUNT); + + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_AB_START_TWO_MINUTES; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_AB_START_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_AB_START_HALF_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_AB_HAS_BEGUN; } BattleGroundAB::~BattleGroundAB() { } -void BattleGroundAB::Update(time_t diff) +void BattleGroundAB::Update(uint32 diff) { BattleGround::Update(diff); - if( GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize() ) + if (GetStatus() == STATUS_IN_PROGRESS) { - ModifyStartDelayTime(diff); - - if( !(m_Events & 0x01) ) - { - m_Events |= 0x01; - - // setup here, only when at least one player has ported to the map - if(!SetupBattleGround()) - { - EndNow(); - return; - } - - sLog.outDebug("Arathi Basin: entering state STATUS_WAIT_JOIN ..."); - - // despawn banners, auras and buffs - for (int obj = BG_AB_OBJECT_BANNER_NEUTRAL; obj < BG_AB_DYNAMIC_NODES_COUNT * 8; ++obj) - SpawnBGObject(obj, RESPAWN_ONE_DAY); - for (int i = 0; i < BG_AB_DYNAMIC_NODES_COUNT * 3; ++i) - SpawnBGObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + i, RESPAWN_ONE_DAY); - - // Starting doors - SpawnBGObject(BG_AB_OBJECT_GATE_A, RESPAWN_IMMEDIATELY); - SpawnBGObject(BG_AB_OBJECT_GATE_H, RESPAWN_IMMEDIATELY); - DoorClose(BG_AB_OBJECT_GATE_A); - DoorClose(BG_AB_OBJECT_GATE_H); - - // Starting base spirit guides - _NodeOccupied(BG_AB_SPIRIT_ALIANCE,ALLIANCE); - _NodeOccupied(BG_AB_SPIRIT_HORDE,HORDE); - - SetStartDelayTime(START_DELAY0); - } - // After 1 minute, warning is signalled - else if( GetStartDelayTime() <= START_DELAY1 && !(m_Events & 0x04) ) - { - m_Events |= 0x04; - SendMessageToAll(GetTrinityString(LANG_BG_AB_ONEMINTOSTART)); - } - // After 1,5 minute, warning is signalled - else if( GetStartDelayTime() <= START_DELAY2 && !(m_Events & 0x08) ) - { - m_Events |= 0x08; - SendMessageToAll(GetTrinityString(LANG_BG_AB_HALFMINTOSTART)); - } - // After 2 minutes, gates OPEN ! x) - else if( GetStartDelayTime() < 0 && !(m_Events & 0x10) ) - { - m_Events |= 0x10; - SendMessageToAll(GetTrinityString(LANG_BG_AB_STARTED)); - - // spawn neutral banners - for (int banner = BG_AB_OBJECT_BANNER_NEUTRAL, i = 0; i < 5; banner += 8, ++i) - SpawnBGObject(banner, RESPAWN_IMMEDIATELY); - for (int i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) - { - //randomly select buff to spawn - uint8 buff = urand(0, 2); - SpawnBGObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + buff + i * 3, RESPAWN_IMMEDIATELY); - } - DoorOpen(BG_AB_OBJECT_GATE_A); - DoorOpen(BG_AB_OBJECT_GATE_H); - - PlaySoundToAll(SOUND_BG_START); - if(sWorld.getConfig(CONFIG_BG_START_MUSIC)) - PlaySoundToAll(SOUND_BG_START_L70ETC); //MUSIC - SetStatus(STATUS_IN_PROGRESS); - - for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) - if(Player* plr = objmgr.GetPlayer(itr->first)) - plr->RemoveAurasDueToSpell(SPELL_PREPARATION); - } - - } - else if( GetStatus() == STATUS_IN_PROGRESS ) - { - int team_points[2] = { 0, 0 }; + int team_points[BG_TEAMS_COUNT] = { 0, 0 }; for (int node = 0; node < BG_AB_DYNAMIC_NODES_COUNT; ++node) { // 3 sec delay to spawn new banner instead previous despawned one - if( m_BannerTimers[node].timer ) + if (m_BannerTimers[node].timer) { - if( m_BannerTimers[node].timer > diff ) + if (m_BannerTimers[node].timer > diff) m_BannerTimers[node].timer -= diff; else { @@ -151,9 +79,9 @@ void BattleGroundAB::Update(time_t diff) } // 1-minute to occupy a node from contested state - if( m_NodeTimers[node] ) + if (m_NodeTimers[node]) { - if( m_NodeTimers[node] > diff ) + if (m_NodeTimers[node] > diff) m_NodeTimers[node] -= diff; else { @@ -169,72 +97,111 @@ void BattleGroundAB::Update(time_t diff) _SendNodeUpdate(node); _NodeOccupied(node,(teamIndex == 0) ? ALLIANCE:HORDE); // Message to chatlog - char buf[256]; - uint8 type = (teamIndex == 0) ? CHAT_MSG_BG_SYSTEM_ALLIANCE : CHAT_MSG_BG_SYSTEM_HORDE; - sprintf(buf, GetTrinityString(LANG_BG_AB_NODE_TAKEN), (teamIndex == 0) ? GetTrinityString(LANG_BG_AB_ALLY) : GetTrinityString(LANG_BG_AB_HORDE), _GetNodeName(node)); - WorldPacket data; - ChatHandler::FillMessageData(&data, NULL, type, LANG_UNIVERSAL, NULL, 0, buf, NULL); - SendPacketToAll(&data); - PlaySoundToAll((teamIndex == 0) ? SOUND_NODE_CAPTURED_ALLIANCE : SOUND_NODE_CAPTURED_HORDE); + + if (teamIndex == 0) + { + // FIXME: team and node names not localized + SendMessage2ToAll(LANG_BG_AB_NODE_TAKEN,CHAT_MSG_BG_SYSTEM_ALLIANCE,NULL,LANG_BG_AB_ALLY,_GetNodeNameId(node)); + PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_ALLIANCE); + } + else + { + // FIXME: team and node names not localized + SendMessage2ToAll(LANG_BG_AB_NODE_TAKEN,CHAT_MSG_BG_SYSTEM_HORDE,NULL,LANG_BG_AB_HORDE,_GetNodeNameId(node)); + PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_HORDE); + } } } - for (int team = 0; team < 2; ++team) - if( m_Nodes[node] == team + BG_AB_NODE_TYPE_OCCUPIED ) + for (int team = 0; team < BG_TEAMS_COUNT; ++team) + if (m_Nodes[node] == team + BG_AB_NODE_TYPE_OCCUPIED) ++team_points[team]; } // Accumulate points - for (int team = 0; team < 2; ++team) + for (int team = 0; team < BG_TEAMS_COUNT; ++team) { int points = team_points[team]; - if( !points ) + if (!points) continue; m_lastTick[team] += diff; - if( m_lastTick[team] > BG_AB_TickIntervals[points] ) + if (m_lastTick[team] > BG_AB_TickIntervals[points]) { m_lastTick[team] -= BG_AB_TickIntervals[points]; m_TeamScores[team] += BG_AB_TickPoints[points]; - m_score[team] = m_TeamScores[team]; m_HonorScoreTics[team] += BG_AB_TickPoints[points]; m_ReputationScoreTics[team] += BG_AB_TickPoints[points]; - if( m_ReputationScoreTics[team] >= BG_AB_ReputationScoreTicks[m_HonorMode] ) + if (m_ReputationScoreTics[team] >= m_ReputationTics) { (team == BG_TEAM_ALLIANCE) ? RewardReputationToTeam(509, 10, ALLIANCE) : RewardReputationToTeam(510, 10, HORDE); - m_ReputationScoreTics[team] -= BG_AB_ReputationScoreTicks[m_HonorMode]; + m_ReputationScoreTics[team] -= m_ReputationTics; } - if( m_HonorScoreTics[team] >= BG_AB_HonorScoreTicks[m_HonorMode] ) + if (m_HonorScoreTics[team] >= m_HonorTics) { - (team == BG_TEAM_ALLIANCE) ? RewardHonorToTeam(20, ALLIANCE) : RewardHonorToTeam(20, HORDE); - m_HonorScoreTics[team] -= BG_AB_HonorScoreTicks[m_HonorMode]; + RewardHonorToTeam(GetBonusHonorFromKill(1), (team == BG_TEAM_ALLIANCE) ? ALLIANCE : HORDE); + m_HonorScoreTics[team] -= m_HonorTics; } - if( !m_IsInformedNearVictory && m_TeamScores[team] > 1800 ) + if (!m_IsInformedNearVictory && m_TeamScores[team] > BG_AB_WARNING_NEAR_VICTORY_SCORE) { - if( team == BG_TEAM_ALLIANCE ) - SendMessageToAll(GetTrinityString(LANG_BG_AB_A_NEAR_VICTORY)); + if (team == BG_TEAM_ALLIANCE) + SendMessageToAll(LANG_BG_AB_A_NEAR_VICTORY, CHAT_MSG_BG_SYSTEM_NEUTRAL); else - SendMessageToAll(GetTrinityString(LANG_BG_AB_H_NEAR_VICTORY)); - PlaySoundToAll(SOUND_NEAR_VICTORY); + SendMessageToAll(LANG_BG_AB_H_NEAR_VICTORY, CHAT_MSG_BG_SYSTEM_NEUTRAL); + PlaySoundToAll(BG_AB_SOUND_NEAR_VICTORY); m_IsInformedNearVictory = true; } - if( m_TeamScores[team] > 2000 ) - m_TeamScores[team] = 2000; - if( team == BG_TEAM_ALLIANCE ) + if (m_TeamScores[team] > BG_AB_MAX_TEAM_SCORE) + m_TeamScores[team] = BG_AB_MAX_TEAM_SCORE; + if (team == BG_TEAM_ALLIANCE) UpdateWorldState(BG_AB_OP_RESOURCES_ALLY, m_TeamScores[team]); - if( team == BG_TEAM_HORDE ) + if (team == BG_TEAM_HORDE) UpdateWorldState(BG_AB_OP_RESOURCES_HORDE, m_TeamScores[team]); } } // Test win condition - if( m_TeamScores[BG_TEAM_ALLIANCE] >= 2000 ) + if (m_TeamScores[BG_TEAM_ALLIANCE] >= BG_AB_MAX_TEAM_SCORE) EndBattleGround(ALLIANCE); - if( m_TeamScores[BG_TEAM_HORDE] >= 2000 ) + if (m_TeamScores[BG_TEAM_HORDE] >= BG_AB_MAX_TEAM_SCORE) EndBattleGround(HORDE); } } +void BattleGroundAB::StartingEventCloseDoors() +{ + // despawn banners, auras and buffs + for (int obj = BG_AB_OBJECT_BANNER_NEUTRAL; obj < BG_AB_DYNAMIC_NODES_COUNT * 8; ++obj) + SpawnBGObject(obj, RESPAWN_ONE_DAY); + for (int i = 0; i < BG_AB_DYNAMIC_NODES_COUNT * 3; ++i) + SpawnBGObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + i, RESPAWN_ONE_DAY); + + // Starting doors + DoorClose(BG_AB_OBJECT_GATE_A); + DoorClose(BG_AB_OBJECT_GATE_H); + SpawnBGObject(BG_AB_OBJECT_GATE_A, RESPAWN_IMMEDIATELY); + SpawnBGObject(BG_AB_OBJECT_GATE_H, RESPAWN_IMMEDIATELY); + + // Starting base spirit guides + _NodeOccupied(BG_AB_SPIRIT_ALIANCE,ALLIANCE); + _NodeOccupied(BG_AB_SPIRIT_HORDE,HORDE); +} + +void BattleGroundAB::StartingEventOpenDoors() +{ + // spawn neutral banners + for (int banner = BG_AB_OBJECT_BANNER_NEUTRAL, i = 0; i < 5; banner += 8, ++i) + SpawnBGObject(banner, RESPAWN_IMMEDIATELY); + for (int i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) + { + //randomly select buff to spawn + uint8 buff = urand(0, 2); + SpawnBGObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + buff + i * 3, RESPAWN_IMMEDIATELY); + } + DoorOpen(BG_AB_OBJECT_GATE_A); + DoorOpen(BG_AB_OBJECT_GATE_H); +} + void BattleGroundAB::AddPlayer(Player *plr) { BattleGround::AddPlayer(plr); @@ -251,19 +218,19 @@ void BattleGroundAB::RemovePlayer(Player * /*plr*/, uint64 /*guid*/) void BattleGroundAB::HandleAreaTrigger(Player *Source, uint32 Trigger) { - if( GetStatus() != STATUS_IN_PROGRESS ) + if (GetStatus() != STATUS_IN_PROGRESS) return; switch(Trigger) { case 3948: // Arathi Basin Alliance Exit. - if( Source->GetTeam() != ALLIANCE ) + if (Source->GetTeam() != ALLIANCE) Source->GetSession()->SendAreaTriggerMessage("Only The Alliance can use that portal"); else Source->LeaveBattleground(); break; case 3949: // Arathi Basin Horde Exit. - if( Source->GetTeam() != HORDE ) + if (Source->GetTeam() != HORDE) Source->GetSession()->SendAreaTriggerMessage("Only The Horde can use that portal"); else Source->LeaveBattleground(); @@ -288,7 +255,7 @@ void BattleGroundAB::HandleAreaTrigger(Player *Source, uint32 Trigger) void BattleGroundAB::_CreateBanner(uint8 node, uint8 type, uint8 teamIndex, bool delay) { // Just put it into the queue - if( delay ) + if (delay) { m_BannerTimers[node].timer = 2000; m_BannerTimers[node].type = type; @@ -301,7 +268,7 @@ void BattleGroundAB::_CreateBanner(uint8 node, uint8 type, uint8 teamIndex, bool SpawnBGObject(obj, RESPAWN_IMMEDIATELY); // handle aura with banner - if( !type ) + if (!type) return; obj = node * 8 + ((type == BG_AB_NODE_TYPE_OCCUPIED) ? (5 + teamIndex) : 7); SpawnBGObject(obj, RESPAWN_IMMEDIATELY); @@ -313,30 +280,25 @@ void BattleGroundAB::_DelBanner(uint8 node, uint8 type, uint8 teamIndex) SpawnBGObject(obj, RESPAWN_ONE_DAY); // handle aura with banner - if( !type ) + if (!type) return; obj = node * 8 + ((type == BG_AB_NODE_TYPE_OCCUPIED) ? (5 + teamIndex) : 7); SpawnBGObject(obj, RESPAWN_ONE_DAY); } -const char* BattleGroundAB::_GetNodeName(uint8 node) +int32 BattleGroundAB::_GetNodeNameId(uint8 node) { switch (node) { - case BG_AB_NODE_STABLES: - return GetTrinityString(LANG_BG_AB_NODE_STABLES); - case BG_AB_NODE_BLACKSMITH: - return GetTrinityString(LANG_BG_AB_NODE_BLACKSMITH); - case BG_AB_NODE_FARM: - return GetTrinityString(LANG_BG_AB_NODE_FARM); - case BG_AB_NODE_LUMBER_MILL: - return GetTrinityString(LANG_BG_AB_NODE_LUMBER_MILL); - case BG_AB_NODE_GOLD_MINE: - return GetTrinityString(LANG_BG_AB_NODE_GOLD_MINE); + case BG_AB_NODE_STABLES: return LANG_BG_AB_NODE_STABLES; + case BG_AB_NODE_BLACKSMITH: return LANG_BG_AB_NODE_BLACKSMITH; + case BG_AB_NODE_FARM: return LANG_BG_AB_NODE_FARM; + case BG_AB_NODE_LUMBER_MILL:return LANG_BG_AB_NODE_LUMBER_MILL; + case BG_AB_NODE_GOLD_MINE: return LANG_BG_AB_NODE_GOLD_MINE; default: ASSERT(0); } - return ""; + return 0; } void BattleGroundAB::FillInitialWorldStates(WorldPacket& data) @@ -355,9 +317,9 @@ void BattleGroundAB::FillInitialWorldStates(WorldPacket& data) // How many bases each team owns uint8 ally = 0, horde = 0; for (uint8 node = 0; node < BG_AB_DYNAMIC_NODES_COUNT; ++node) - if( m_Nodes[node] == BG_AB_NODE_STATUS_ALLY_OCCUPIED ) + if (m_Nodes[node] == BG_AB_NODE_STATUS_ALLY_OCCUPIED) ++ally; - else if( m_Nodes[node] == BG_AB_NODE_STATUS_HORDE_OCCUPIED ) + else if (m_Nodes[node] == BG_AB_NODE_STATUS_HORDE_OCCUPIED) ++horde; data << uint32(BG_AB_OP_OCCUPIED_BASES_ALLY) << uint32(ally); @@ -365,7 +327,7 @@ void BattleGroundAB::FillInitialWorldStates(WorldPacket& data) // Team scores data << uint32(BG_AB_OP_RESOURCES_MAX) << uint32(BG_AB_MAX_TEAM_SCORE); - data << uint32(BG_AB_OP_RESOURCES_WARNING) << uint32(BG_AB_WARNING_SCORE); + data << uint32(BG_AB_OP_RESOURCES_WARNING) << uint32(BG_AB_WARNING_NEAR_VICTORY_SCORE); data << uint32(BG_AB_OP_RESOURCES_ALLY) << uint32(m_TeamScores[BG_TEAM_ALLIANCE]); data << uint32(BG_AB_OP_RESOURCES_HORDE) << uint32(m_TeamScores[BG_TEAM_HORDE]); @@ -378,7 +340,7 @@ void BattleGroundAB::_SendNodeUpdate(uint8 node) // Send node owner state update to refresh map icons on client const uint8 plusArray[] = {0, 2, 3, 0, 1}; - if( m_prevNodes[node] ) + if (m_prevNodes[node]) UpdateWorldState(BG_AB_OP_NODESTATES[node] + plusArray[m_prevNodes[node]], 0); else UpdateWorldState(BG_AB_OP_NODEICONS[node], 0); @@ -388,9 +350,9 @@ void BattleGroundAB::_SendNodeUpdate(uint8 node) // How many bases each team owns uint8 ally = 0, horde = 0; for (uint8 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) - if( m_Nodes[i] == BG_AB_NODE_STATUS_ALLY_OCCUPIED ) + if (m_Nodes[i] == BG_AB_NODE_STATUS_ALLY_OCCUPIED) ++ally; - else if( m_Nodes[i] == BG_AB_NODE_STATUS_HORDE_OCCUPIED ) + else if (m_Nodes[i] == BG_AB_NODE_STATUS_HORDE_OCCUPIED) ++horde; UpdateWorldState(BG_AB_OP_OCCUPIED_BASES_ALLY, ally); @@ -399,46 +361,47 @@ void BattleGroundAB::_SendNodeUpdate(uint8 node) void BattleGroundAB::_NodeOccupied(uint8 node,Team team) { - if( !AddSpiritGuide(node, BG_AB_SpiritGuidePos[node][0], BG_AB_SpiritGuidePos[node][1], BG_AB_SpiritGuidePos[node][2], BG_AB_SpiritGuidePos[node][3], team) ) + if (!AddSpiritGuide(node, BG_AB_SpiritGuidePos[node][0], BG_AB_SpiritGuidePos[node][1], BG_AB_SpiritGuidePos[node][2], BG_AB_SpiritGuidePos[node][3], team)) sLog.outError("Failed to spawn spirit guide! point: %u, team: %u,", node, team); // SpawnBGCreature(node,RESPAWN_IMMEDIATELY); uint8 capturedNodes = 0; for (uint8 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) { - if( m_Nodes[node] == GetTeamIndexByTeamId(team) + BG_AB_NODE_TYPE_OCCUPIED && !m_NodeTimers[i]) + if (m_Nodes[node] == GetTeamIndexByTeamId(team) + BG_AB_NODE_TYPE_OCCUPIED && !m_NodeTimers[i]) ++capturedNodes; } - if(capturedNodes >= 5) + if (capturedNodes >= 5) CastSpellOnTeam(SPELL_AB_QUEST_REWARD_5_BASES, team); - if(capturedNodes >= 4) + if (capturedNodes >= 4) CastSpellOnTeam(SPELL_AB_QUEST_REWARD_4_BASES, team); } void BattleGroundAB::_NodeDeOccupied(uint8 node) { - if( node >= BG_AB_DYNAMIC_NODES_COUNT) + if (node >= BG_AB_DYNAMIC_NODES_COUNT) return; // Those who are waiting to resurrect at this node are taken to the closest own node's graveyard std::vector<uint64> ghost_list = m_ReviveQueue[m_BgCreatures[node]]; - if( !ghost_list.empty() ) + if (!ghost_list.empty()) { WorldSafeLocsEntry const *ClosestGrave = NULL; - Player *plr; - for (std::vector<uint64>::iterator itr = ghost_list.begin(); itr != ghost_list.end(); ++itr) + for (std::vector<uint64>::const_iterator itr = ghost_list.begin(); itr != ghost_list.end(); ++itr) { - plr = objmgr.GetPlayer(*ghost_list.begin()); - if( !plr ) + Player* plr = objmgr.GetPlayer(*itr); + if (!plr) continue; - if( !ClosestGrave ) - ClosestGrave = GetClosestGraveYard(plr->GetPositionX(), plr->GetPositionY(), plr->GetPositionZ(), plr->GetTeam()); - plr->TeleportTo(GetMapId(), ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, plr->GetOrientation()); + if (!ClosestGrave) // cache + ClosestGrave = GetClosestGraveYard(plr); + + if (ClosestGrave) + plr->TeleportTo(GetMapId(), ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, plr->GetOrientation()); } } - if( m_BgCreatures[node] ) + if (m_BgCreatures[node]) DelCreature(node); // buff object isn't despawned @@ -447,7 +410,7 @@ void BattleGroundAB::_NodeDeOccupied(uint8 node) /* Invoked if a player used a banner as a gameobject */ void BattleGroundAB::EventPlayerClickedOnFlag(Player *source, GameObject* /*target_obj*/) { - if( GetStatus() != STATUS_IN_PROGRESS ) + if (GetStatus() != STATUS_IN_PROGRESS) return; uint8 node = BG_AB_NODE_STABLES; @@ -458,7 +421,7 @@ void BattleGroundAB::EventPlayerClickedOnFlag(Player *source, GameObject* /*targ obj=HashMapHolder<GameObject>::Find(m_BgObjects[node*8+BG_AB_OBJECT_AURA_CONTESTED]); } - if( node == BG_AB_DYNAMIC_NODES_COUNT) + if (node == BG_AB_DYNAMIC_NODES_COUNT) { // this means our player isn't close to any of banners - maybe cheater ?? return; @@ -466,18 +429,14 @@ void BattleGroundAB::EventPlayerClickedOnFlag(Player *source, GameObject* /*targ uint8 teamIndex = GetTeamIndexByTeamId(source->GetTeam()); - // Message to chatlog - char buf[256]; - uint8 type = (teamIndex == 0) ? CHAT_MSG_BG_SYSTEM_ALLIANCE : CHAT_MSG_BG_SYSTEM_HORDE; - // Check if player really could use this banner, not cheated - if( !(m_Nodes[node] == 0 || teamIndex == m_Nodes[node]%2) ) + if (!(m_Nodes[node] == 0 || teamIndex == m_Nodes[node]%2)) return; source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); uint32 sound = 0; // If node is neutral, change to contested - if( m_Nodes[node] == BG_AB_NODE_TYPE_NEUTRAL ) + if (m_Nodes[node] == BG_AB_NODE_TYPE_NEUTRAL) { UpdatePlayerScore(source, SCORE_BASES_ASSAULTED, 1); m_prevNodes[node] = m_Nodes[node]; @@ -488,14 +447,20 @@ void BattleGroundAB::EventPlayerClickedOnFlag(Player *source, GameObject* /*targ _CreateBanner(node, BG_AB_NODE_TYPE_CONTESTED, teamIndex, true); _SendNodeUpdate(node); m_NodeTimers[node] = BG_AB_FLAG_CAPTURING_TIME; - sprintf(buf, GetTrinityString(LANG_BG_AB_NODE_CLAIMED), _GetNodeName(node), (teamIndex == 0) ? GetTrinityString(LANG_BG_AB_ALLY) : GetTrinityString(LANG_BG_AB_HORDE)); - sound = SOUND_NODE_CLAIMED; + + // FIXME: team and node names not localized + if (teamIndex == 0) + SendMessage2ToAll(LANG_BG_AB_NODE_CLAIMED,CHAT_MSG_BG_SYSTEM_ALLIANCE, source, _GetNodeNameId(node), LANG_BG_AB_ALLY); + else + SendMessage2ToAll(LANG_BG_AB_NODE_CLAIMED,CHAT_MSG_BG_SYSTEM_HORDE, source, _GetNodeNameId(node), LANG_BG_AB_HORDE); + + sound = BG_AB_SOUND_NODE_CLAIMED; } // If node is contested - else if( (m_Nodes[node] == BG_AB_NODE_STATUS_ALLY_CONTESTED) || (m_Nodes[node] == BG_AB_NODE_STATUS_HORDE_CONTESTED) ) + else if ((m_Nodes[node] == BG_AB_NODE_STATUS_ALLY_CONTESTED) || (m_Nodes[node] == BG_AB_NODE_STATUS_HORDE_CONTESTED)) { // If last state is NOT occupied, change node to enemy-contested - if( m_prevNodes[node] < BG_AB_NODE_TYPE_OCCUPIED ) + if (m_prevNodes[node] < BG_AB_NODE_TYPE_OCCUPIED) { UpdatePlayerScore(source, SCORE_BASES_ASSAULTED, 1); m_prevNodes[node] = m_Nodes[node]; @@ -506,7 +471,12 @@ void BattleGroundAB::EventPlayerClickedOnFlag(Player *source, GameObject* /*targ _CreateBanner(node, BG_AB_NODE_TYPE_CONTESTED, teamIndex, true); _SendNodeUpdate(node); m_NodeTimers[node] = BG_AB_FLAG_CAPTURING_TIME; - sprintf(buf, GetTrinityString(LANG_BG_AB_NODE_ASSAULTED), _GetNodeName(node)); + + // FIXME: node names not localized + if (teamIndex == 0) + SendMessage2ToAll(LANG_BG_AB_NODE_ASSAULTED,CHAT_MSG_BG_SYSTEM_ALLIANCE, source, _GetNodeNameId(node)); + else + SendMessage2ToAll(LANG_BG_AB_NODE_ASSAULTED,CHAT_MSG_BG_SYSTEM_HORDE, source, _GetNodeNameId(node)); } // If contested, change back to occupied else @@ -521,9 +491,14 @@ void BattleGroundAB::EventPlayerClickedOnFlag(Player *source, GameObject* /*targ _SendNodeUpdate(node); m_NodeTimers[node] = 0; _NodeOccupied(node,(teamIndex == 0) ? ALLIANCE:HORDE); - sprintf(buf, GetTrinityString(LANG_BG_AB_NODE_DEFENDED), _GetNodeName(node)); + + // FIXME: node names not localized + if (teamIndex == 0) + SendMessage2ToAll(LANG_BG_AB_NODE_DEFENDED,CHAT_MSG_BG_SYSTEM_ALLIANCE, source, _GetNodeNameId(node)); + else + SendMessage2ToAll(LANG_BG_AB_NODE_DEFENDED,CHAT_MSG_BG_SYSTEM_HORDE, source, _GetNodeNameId(node)); } - sound = (teamIndex == 0) ? SOUND_NODE_ASSAULTED_ALLIANCE : SOUND_NODE_ASSAULTED_HORDE; + sound = (teamIndex == 0) ? BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE : BG_AB_SOUND_NODE_ASSAULTED_HORDE; } // If node is occupied, change to enemy-contested else @@ -538,18 +513,24 @@ void BattleGroundAB::EventPlayerClickedOnFlag(Player *source, GameObject* /*targ _SendNodeUpdate(node); _NodeDeOccupied(node); m_NodeTimers[node] = BG_AB_FLAG_CAPTURING_TIME; - sprintf(buf, GetTrinityString(LANG_BG_AB_NODE_ASSAULTED), _GetNodeName(node)); - sound = (teamIndex == 0) ? SOUND_NODE_ASSAULTED_ALLIANCE : SOUND_NODE_ASSAULTED_HORDE; + + // FIXME: node names not localized + if (teamIndex == 0) + SendMessage2ToAll(LANG_BG_AB_NODE_ASSAULTED,CHAT_MSG_BG_SYSTEM_ALLIANCE, source, _GetNodeNameId(node)); + else + SendMessage2ToAll(LANG_BG_AB_NODE_ASSAULTED,CHAT_MSG_BG_SYSTEM_HORDE, source, _GetNodeNameId(node)); + + sound = (teamIndex == 0) ? BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE : BG_AB_SOUND_NODE_ASSAULTED_HORDE; } - WorldPacket data; - ChatHandler::FillMessageData(&data, source->GetSession(), type, LANG_UNIVERSAL, NULL, source->GetGUID(), buf, NULL); - SendPacketToAll(&data); + // If node is occupied again, send "X has taken the Y" msg. - if( m_Nodes[node] >= BG_AB_NODE_TYPE_OCCUPIED ) + if (m_Nodes[node] >= BG_AB_NODE_TYPE_OCCUPIED) { - sprintf(buf, GetTrinityString(LANG_BG_AB_NODE_TAKEN), (teamIndex == 0) ? GetTrinityString(LANG_BG_AB_ALLY) : GetTrinityString(LANG_BG_AB_HORDE), _GetNodeName(node)); - ChatHandler::FillMessageData(&data, NULL, type, LANG_UNIVERSAL, NULL, 0, buf, NULL); - SendPacketToAll(&data); + // FIXME: team and node names not localized + if (teamIndex == 0) + SendMessage2ToAll(LANG_BG_AB_NODE_TAKEN,CHAT_MSG_BG_SYSTEM_ALLIANCE, NULL, LANG_BG_AB_ALLY, _GetNodeNameId(node)); + else + SendMessage2ToAll(LANG_BG_AB_NODE_TAKEN,CHAT_MSG_BG_SYSTEM_HORDE, NULL, LANG_BG_AB_HORDE, _GetNodeNameId(node)); } PlaySoundToAll(sound); } @@ -558,7 +539,7 @@ bool BattleGroundAB::SetupBattleGround() { for (int i = 0 ; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) { - if( !AddObject(BG_AB_OBJECT_BANNER_NEUTRAL + 8*i,BG_AB_OBJECTID_NODE_BANNER_0 + i,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) + if (!AddObject(BG_AB_OBJECT_BANNER_NEUTRAL + 8*i,BG_AB_OBJECTID_NODE_BANNER_0 + i,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) || !AddObject(BG_AB_OBJECT_BANNER_CONT_A + 8*i,BG_AB_OBJECTID_BANNER_CONT_A,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) || !AddObject(BG_AB_OBJECT_BANNER_CONT_H + 8*i,BG_AB_OBJECTID_BANNER_CONT_H,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) || !AddObject(BG_AB_OBJECT_BANNER_ALLY + 8*i,BG_AB_OBJECTID_BANNER_A,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) @@ -572,7 +553,7 @@ bool BattleGroundAB::SetupBattleGround() return false; } } - if( !AddObject(BG_AB_OBJECT_GATE_A,BG_AB_OBJECTID_GATE_A,BG_AB_DoorPositions[0][0],BG_AB_DoorPositions[0][1],BG_AB_DoorPositions[0][2],BG_AB_DoorPositions[0][3],BG_AB_DoorPositions[0][4],BG_AB_DoorPositions[0][5],BG_AB_DoorPositions[0][6],BG_AB_DoorPositions[0][7],RESPAWN_IMMEDIATELY) + if (!AddObject(BG_AB_OBJECT_GATE_A,BG_AB_OBJECTID_GATE_A,BG_AB_DoorPositions[0][0],BG_AB_DoorPositions[0][1],BG_AB_DoorPositions[0][2],BG_AB_DoorPositions[0][3],BG_AB_DoorPositions[0][4],BG_AB_DoorPositions[0][5],BG_AB_DoorPositions[0][6],BG_AB_DoorPositions[0][7],RESPAWN_IMMEDIATELY) || !AddObject(BG_AB_OBJECT_GATE_H,BG_AB_OBJECTID_GATE_H,BG_AB_DoorPositions[1][0],BG_AB_DoorPositions[1][1],BG_AB_DoorPositions[1][2],BG_AB_DoorPositions[1][3],BG_AB_DoorPositions[1][4],BG_AB_DoorPositions[1][5],BG_AB_DoorPositions[1][6],BG_AB_DoorPositions[1][7],RESPAWN_IMMEDIATELY) ) { @@ -582,7 +563,7 @@ bool BattleGroundAB::SetupBattleGround() //buffs for (int i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) { - if( !AddObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + 3 * i, Buff_Entries[0], BG_AB_BuffPositions[i][0], BG_AB_BuffPositions[i][1], BG_AB_BuffPositions[i][2], BG_AB_BuffPositions[i][3], 0, 0, sin(BG_AB_BuffPositions[i][3]/2), cos(BG_AB_BuffPositions[i][3]/2), RESPAWN_ONE_DAY) + if (!AddObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + 3 * i, Buff_Entries[0], BG_AB_BuffPositions[i][0], BG_AB_BuffPositions[i][1], BG_AB_BuffPositions[i][2], BG_AB_BuffPositions[i][3], 0, 0, sin(BG_AB_BuffPositions[i][3]/2), cos(BG_AB_BuffPositions[i][3]/2), RESPAWN_ONE_DAY) || !AddObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + 3 * i + 1, Buff_Entries[1], BG_AB_BuffPositions[i][0], BG_AB_BuffPositions[i][1], BG_AB_BuffPositions[i][2], BG_AB_BuffPositions[i][3], 0, 0, sin(BG_AB_BuffPositions[i][3]/2), cos(BG_AB_BuffPositions[i][3]/2), RESPAWN_ONE_DAY) || !AddObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + 3 * i + 2, Buff_Entries[2], BG_AB_BuffPositions[i][0], BG_AB_BuffPositions[i][1], BG_AB_BuffPositions[i][2], BG_AB_BuffPositions[i][3], 0, 0, sin(BG_AB_BuffPositions[i][3]/2), cos(BG_AB_BuffPositions[i][3]/2), RESPAWN_ONE_DAY) ) @@ -592,8 +573,11 @@ bool BattleGroundAB::SetupBattleGround() return true; } -void BattleGroundAB::ResetBGSubclass() +void BattleGroundAB::Reset() { + //call parent's class reset + BattleGround::Reset(); + m_TeamScores[BG_TEAM_ALLIANCE] = 0; m_TeamScores[BG_TEAM_HORDE] = 0; m_lastTick[BG_TEAM_ALLIANCE] = 0; @@ -603,6 +587,10 @@ void BattleGroundAB::ResetBGSubclass() m_ReputationScoreTics[BG_TEAM_ALLIANCE] = 0; m_ReputationScoreTics[BG_TEAM_HORDE] = 0; m_IsInformedNearVictory = false; + bool isBGWeekend = false; //TODO FIXME - call sBattleGroundMgr.IsBGWeekend(m_TypeID); - you must also implement that call! + m_HonorTics = (isBGWeekend) ? BG_AB_ABBGWeekendHonorTicks : BG_AB_NotABBGWeekendHonorTicks; + m_ReputationTics = (isBGWeekend) ? BG_AB_ABBGWeekendReputationTicks : BG_AB_NotABBGWeekendReputationTicks; + for (uint8 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) { m_Nodes[i] = 0; @@ -612,32 +600,49 @@ void BattleGroundAB::ResetBGSubclass() } for (uint8 i = 0; i < BG_AB_ALL_NODES_COUNT; ++i) - if(m_BgCreatures[i]) + if (m_BgCreatures[i]) DelCreature(i); } -WorldSafeLocsEntry const* BattleGroundAB::GetClosestGraveYard(float x, float y, float /*z*/, uint32 team) +void BattleGroundAB::EndBattleGround(uint32 winner) { - uint8 teamIndex = GetTeamIndexByTeamId(team); + //win reward + if (winner == ALLIANCE) + RewardHonorToTeam(GetBonusHonorFromKill(1), ALLIANCE); + if (winner == HORDE) + RewardHonorToTeam(GetBonusHonorFromKill(1), HORDE); + //complete map_end rewards (even if no team wins) + RewardHonorToTeam(GetBonusHonorFromKill(1), HORDE); + RewardHonorToTeam(GetBonusHonorFromKill(1), ALLIANCE); + + BattleGround::EndBattleGround(winner); +} + +WorldSafeLocsEntry const* BattleGroundAB::GetClosestGraveYard(Player* player) +{ + uint8 teamIndex = GetTeamIndexByTeamId(player->GetTeam()); // Is there any occupied node for this team? std::vector<uint8> nodes; for (uint8 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) - if( m_Nodes[i] == teamIndex + 3 ) + if (m_Nodes[i] == teamIndex + 3) nodes.push_back(i); WorldSafeLocsEntry const* good_entry = NULL; // If so, select the closest node to place ghost on - if( !nodes.empty() ) + if (!nodes.empty()) { + float plr_x = player->GetPositionX(); + float plr_y = player->GetPositionY(); + float mindist = 999999.0f; for (uint8 i = 0; i < nodes.size(); ++i) { WorldSafeLocsEntry const*entry = sWorldSafeLocsStore.LookupEntry( BG_AB_GraveyardIds[nodes[i]] ); - if( !entry ) + if (!entry) continue; - float dist = (entry->x - x)*(entry->x - x)+(entry->y - y)*(entry->y - y); - if( mindist > dist ) + float dist = (entry->x - plr_x)*(entry->x - plr_x)+(entry->y - plr_y)*(entry->y - plr_y); + if (mindist > dist) { mindist = dist; good_entry = entry; @@ -646,7 +651,7 @@ WorldSafeLocsEntry const* BattleGroundAB::GetClosestGraveYard(float x, float y, nodes.clear(); } // If not, place ghost on starting location - if( !good_entry ) + if (!good_entry) good_entry = sWorldSafeLocsStore.LookupEntry( BG_AB_GraveyardIds[teamIndex+5] ); return good_entry; diff --git a/src/game/BattleGroundAB.h b/src/game/BattleGroundAB.h index 3fcea59c8e8..abe81df5ef8 100644 --- a/src/game/BattleGroundAB.h +++ b/src/game/BattleGroundAB.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -77,17 +77,17 @@ enum BG_AB_NodeObjectId enum BG_AB_ObjectType { // for all 5 node points 8*5=40 objects - BG_AB_OBJECT_BANNER_NEUTRAL = 0, - BG_AB_OBJECT_BANNER_CONT_A = 1, - BG_AB_OBJECT_BANNER_CONT_H = 2, - BG_AB_OBJECT_BANNER_ALLY = 3, - BG_AB_OBJECT_BANNER_HORDE = 4, - BG_AB_OBJECT_AURA_ALLY = 5, - BG_AB_OBJECT_AURA_HORDE = 6, - BG_AB_OBJECT_AURA_CONTESTED = 7, + BG_AB_OBJECT_BANNER_NEUTRAL = 0, + BG_AB_OBJECT_BANNER_CONT_A = 1, + BG_AB_OBJECT_BANNER_CONT_H = 2, + BG_AB_OBJECT_BANNER_ALLY = 3, + BG_AB_OBJECT_BANNER_HORDE = 4, + BG_AB_OBJECT_AURA_ALLY = 5, + BG_AB_OBJECT_AURA_HORDE = 6, + BG_AB_OBJECT_AURA_CONTESTED = 7, //gates - BG_AB_OBJECT_GATE_A = 40, - BG_AB_OBJECT_GATE_H = 41, + BG_AB_OBJECT_GATE_A = 40, + BG_AB_OBJECT_GATE_H = 41, //buffs BG_AB_OBJECT_SPEEDBUFF_STABLES = 42, BG_AB_OBJECT_REGENBUFF_STABLES = 43, @@ -130,8 +130,8 @@ enum BG_AB_Timers enum BG_AB_Score { - BG_AB_MAX_TEAM_SCORE = 2000, - BG_AB_WARNING_SCORE = 1800 + BG_AB_WARNING_NEAR_VICTORY_SCORE = 1800, + BG_AB_MAX_TEAM_SCORE = 2000 }; /* do NOT change the order, else wrong behaviour */ @@ -164,14 +164,19 @@ enum BG_AB_NodeStatus enum BG_AB_Sounds { - SOUND_NODE_CLAIMED = 8192, - SOUND_NODE_CAPTURED_ALLIANCE = 8173, - SOUND_NODE_CAPTURED_HORDE = 8213, - SOUND_NODE_ASSAULTED_ALLIANCE = 8174, - SOUND_NODE_ASSAULTED_HORDE = 8212, - SOUND_NEAR_VICTORY = 8456 + BG_AB_SOUND_NODE_CLAIMED = 8192, + BG_AB_SOUND_NODE_CAPTURED_ALLIANCE = 8173, + BG_AB_SOUND_NODE_CAPTURED_HORDE = 8213, + BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE = 8212, + BG_AB_SOUND_NODE_ASSAULTED_HORDE = 8174, + BG_AB_SOUND_NEAR_VICTORY = 8456 }; +#define BG_AB_NotABBGWeekendHonorTicks 330 +#define BG_AB_ABBGWeekendHonorTicks 200 +#define BG_AB_NotABBGWeekendReputationTicks 200 +#define BG_AB_ABBGWeekendReputationTicks 150 + // x, y, z, o const float BG_AB_NodePositions[BG_AB_DYNAMIC_NODES_COUNT][4] = { {1166.785f, 1200.132f, -56.70859f, 0.9075713f}, // stables @@ -238,13 +243,16 @@ class BattleGroundAB : public BattleGround BattleGroundAB(); ~BattleGroundAB(); - void Update(time_t diff); + void Update(uint32 diff); void AddPlayer(Player *plr); + virtual void StartingEventCloseDoors(); + virtual void StartingEventOpenDoors(); void RemovePlayer(Player *plr,uint64 guid); void HandleAreaTrigger(Player *Source, uint32 Trigger); virtual bool SetupBattleGround(); - virtual void ResetBGSubclass(); - virtual WorldSafeLocsEntry const* GetClosestGraveYard(float x, float y, float z, uint32 team); + virtual void Reset(); + void EndBattleGround(uint32 winner); + virtual WorldSafeLocsEntry const* GetClosestGraveYard(Player* player); /* Scorekeeping */ virtual void UpdatePlayerScore(Player *Source, uint32 type, uint32 value); @@ -265,7 +273,7 @@ class BattleGroundAB : public BattleGround void _NodeOccupied(uint8 node,Team team); void _NodeDeOccupied(uint8 node); - const char* _GetNodeName(uint8 node); + int32 _GetNodeNameId(uint8 node); /* Nodes info: 0: neutral @@ -273,15 +281,19 @@ class BattleGroundAB : public BattleGround 2: horde contested 3: ally occupied 4: horde occupied */ - uint8 m_Nodes[BG_AB_DYNAMIC_NODES_COUNT]; - uint8 m_prevNodes[BG_AB_DYNAMIC_NODES_COUNT]; - BG_AB_BannerTimer m_BannerTimers[BG_AB_DYNAMIC_NODES_COUNT]; - int32 m_NodeTimers[BG_AB_DYNAMIC_NODES_COUNT]; - uint32 m_TeamScores[2]; - uint32 m_lastTick[2]; - uint32 m_HonorScoreTics[2]; - uint32 m_ReputationScoreTics[2]; - bool m_IsInformedNearVictory; + uint8 m_Nodes[BG_AB_DYNAMIC_NODES_COUNT]; + uint8 m_prevNodes[BG_AB_DYNAMIC_NODES_COUNT]; + BG_AB_BannerTimer m_BannerTimers[BG_AB_DYNAMIC_NODES_COUNT]; + int32 m_NodeTimers[BG_AB_DYNAMIC_NODES_COUNT]; + uint32 m_TeamScores[BG_TEAMS_COUNT]; + uint32 m_lastTick[BG_TEAMS_COUNT]; + uint32 m_HonorScoreTics[BG_TEAMS_COUNT]; + uint32 m_ReputationScoreTics[BG_TEAMS_COUNT]; + bool m_IsInformedNearVictory; + uint32 m_HonorTics; + uint32 m_ReputationTics; + + }; #endif diff --git a/src/game/BattleGroundAV.cpp b/src/game/BattleGroundAV.cpp index 6086d0be441..bc0b50d21f0 100644 --- a/src/game/BattleGroundAV.cpp +++ b/src/game/BattleGroundAV.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,21 +21,23 @@ #include "Player.h" #include "BattleGround.h" #include "BattleGroundAV.h" -#include "Creature.h" -#include "Chat.h" -#include "Object.h" -#include "ObjectMgr.h" -#include "ObjectAccessor.h" -#include "MapManager.h" #include "Language.h" #include "SpellAuras.h" #include "Formulas.h" +#include "WorldPacket.h" +#include "ObjectMgr.h" +#include "GameObject.h" BattleGroundAV::BattleGroundAV() { - m_BgObjects.resize(BG_AV_OBJECT_MAX); m_BgCreatures.resize(AV_CPLACE_MAX+AV_STATICCPLACE_MAX); + + //TODO FIX ME! + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_EY_START_TWO_MINUTES; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_EY_START_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_EY_START_HALF_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_EY_HAS_BEGUN; } BattleGroundAV::~BattleGroundAV() @@ -229,8 +231,6 @@ void BattleGroundAV::UpdateScore(uint16 team, int16 points ) uint8 teamindex = GetTeamIndexByTeamId(team); //0=ally 1=horde m_Team_Scores[teamindex] += points; - m_score[teamindex] = m_Team_Scores[teamindex]; - UpdateWorldState(((teamindex==BG_TEAM_HORDE)?AV_Horde_Score:AV_Alliance_Score), m_Team_Scores[teamindex]); if( points < 0) { @@ -241,7 +241,7 @@ void BattleGroundAV::UpdateScore(uint16 team, int16 points ) } else if(!m_IsInformedNearVictory[teamindex] && m_Team_Scores[teamindex] < SEND_MSG_NEAR_LOSE) { - SendMessageToAll(GetTrinityString((teamindex==BG_TEAM_HORDE)?LANG_BG_AV_H_NEAR_LOSE:LANG_BG_AV_A_NEAR_LOSE)); + SendMessageToAll(teamindex==BG_TEAM_HORDE?LANG_BG_AV_H_NEAR_LOSE:LANG_BG_AV_A_NEAR_LOSE, teamindex==BG_TEAM_HORDE ? CHAT_MSG_BG_SYSTEM_HORDE : CHAT_MSG_BG_SYSTEM_ALLIANCE); PlaySoundToAll(AV_SOUND_NEAR_VICTORY); m_IsInformedNearVictory[teamindex] = true; } @@ -296,7 +296,7 @@ Creature* BattleGroundAV::AddAVCreature(uint16 cinfoid, uint16 type ) return creature; } -void BattleGroundAV::Update(time_t diff) +void BattleGroundAV::Update(uint32 diff) { BattleGround::Update(diff); if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize()) @@ -368,19 +368,19 @@ void BattleGroundAV::Update(time_t diff) DoorClose(BG_AV_OBJECT_DOOR_A); DoorClose(BG_AV_OBJECT_DOOR_H); - SetStartDelayTime(START_DELAY0); + SetStartDelayTime(BG_START_DELAY_2M); } // After 1 minute, warning is signalled - else if (GetStartDelayTime() <= START_DELAY1 && !(m_Events & 0x04)) + else if (GetStartDelayTime() <= BG_START_DELAY_1M && !(m_Events & 0x04)) { m_Events |= 0x04; - SendMessageToAll(GetTrinityString(LANG_BG_AV_ONEMINTOSTART)); + SendMessageToAll(LANG_BG_AV_ONEMINTOSTART, CHAT_MSG_BG_SYSTEM_NEUTRAL); } // After 1,5 minute, warning is signalled - else if (GetStartDelayTime() <= START_DELAY2 && !(m_Events & 0x08)) + else if (GetStartDelayTime() <= BG_START_DELAY_1M + BG_START_DELAY_30S && !(m_Events & 0x08)) { m_Events |= 0x08; - SendMessageToAll(GetTrinityString(LANG_BG_AV_HALFMINTOSTART)); + SendMessageToAll(LANG_BG_AV_HALFMINTOSTART, CHAT_MSG_BG_SYSTEM_NEUTRAL); } // After 2 minutes, gates OPEN ! x) else if (GetStartDelayTime() <= 0 && !(m_Events & 0x10)) @@ -389,7 +389,7 @@ void BattleGroundAV::Update(time_t diff) UpdateWorldState(AV_SHOW_A_SCORE, 1); m_Events |= 0x10; - SendMessageToAll(GetTrinityString(LANG_BG_AV_STARTED)); + SendMessageToAll(LANG_BG_AV_STARTED, CHAT_MSG_BG_SYSTEM_NEUTRAL); PlaySoundToAll(SOUND_BG_START); SetStatus(STATUS_IN_PROGRESS); @@ -467,6 +467,14 @@ void BattleGroundAV::Update(time_t diff) } } +void BattleGroundAV::StartingEventCloseDoors() +{ +} + +void BattleGroundAV::StartingEventOpenDoors() +{ +} + void BattleGroundAV::AddPlayer(Player *plr) { BattleGround::AddPlayer(plr); @@ -535,7 +543,7 @@ void BattleGroundAV::RemovePlayer(Player* plr,uint64 /*guid*/) void BattleGroundAV::HandleAreaTrigger(Player *Source, uint32 Trigger) { // this is wrong way to implement these things. On official it done by gameobject spell cast. - if(GetStatus() != STATUS_IN_PROGRESS) + if (GetStatus() != STATUS_IN_PROGRESS) return; uint32 SpellId = 0; @@ -568,7 +576,7 @@ void BattleGroundAV::HandleAreaTrigger(Player *Source, uint32 Trigger) break; } - if(SpellId) + if (SpellId) Source->CastSpell(Source, SpellId, true); } diff --git a/src/game/BattleGroundAV.h b/src/game/BattleGroundAV.h index 310635b995f..f9f4f46b0ae 100644 --- a/src/game/BattleGroundAV.h +++ b/src/game/BattleGroundAV.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -1500,10 +1500,12 @@ class BattleGroundAV : public BattleGround public: BattleGroundAV(); ~BattleGroundAV(); - void Update(time_t diff); + void Update(uint32 diff); /* inherited from BattlegroundClass */ virtual void AddPlayer(Player *plr); + virtual void StartingEventCloseDoors(); + virtual void StartingEventOpenDoors(); void RemovePlayer(Player *plr,uint64 guid); void HandleAreaTrigger(Player *Source, uint32 Trigger); diff --git a/src/game/BattleGroundBE.cpp b/src/game/BattleGroundBE.cpp index a010bb166ac..dd45b78d924 100644 --- a/src/game/BattleGroundBE.cpp +++ b/src/game/BattleGroundBE.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,14 +22,23 @@ #include "Player.h" #include "BattleGround.h" #include "BattleGroundBE.h" -#include "Creature.h" #include "ObjectMgr.h" -#include "MapManager.h" +#include "WorldPacket.h" #include "Language.h" BattleGroundBE::BattleGroundBE() { m_BgObjects.resize(BG_BE_OBJECT_MAX); + + m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_1M; + m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_30S; + m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_15S; + m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE; + //we must set messageIds + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_ARENA_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_ARENA_THIRTY_SECONDS; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_ARENA_FIFTEEN_SECONDS; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_ARENA_HAS_BEGUN; } BattleGroundBE::~BattleGroundBE() @@ -37,77 +46,34 @@ BattleGroundBE::~BattleGroundBE() } -void BattleGroundBE::Update(time_t diff) +void BattleGroundBE::Update(uint32 diff) { BattleGround::Update(diff); - // after bg start we get there - if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize()) - { - ModifyStartDelayTime(diff); - - if (!(m_Events & 0x01)) - { - m_Events |= 0x01; - // setup here, only when at least one player has ported to the map - if(!SetupBattleGround()) - { - EndNow(); - return; - } - for(uint32 i = BG_BE_OBJECT_DOOR_1; i <= BG_BE_OBJECT_DOOR_4; i++) - SpawnBGObject(i, RESPAWN_IMMEDIATELY); - - for(uint32 i = BG_BE_OBJECT_BUFF_1; i <= BG_BE_OBJECT_BUFF_2; i++) - SpawnBGObject(i, RESPAWN_ONE_DAY); - - SetStartDelayTime(START_DELAY1); - SendMessageToAll(LANG_ARENA_ONE_MINUTE); - } - // After 30 seconds, warning is signalled - else if (GetStartDelayTime() <= START_DELAY2 && !(m_Events & 0x04)) - { - m_Events |= 0x04; - SendMessageToAll(LANG_ARENA_THIRTY_SECONDS); - } - // After 15 seconds, warning is signalled - else if (GetStartDelayTime() <= START_DELAY3 && !(m_Events & 0x08)) - { - m_Events |= 0x08; - SendMessageToAll(LANG_ARENA_FIFTEEN_SECONDS); - } - // delay expired (1 minute) - else if (GetStartDelayTime() <= 0 && !(m_Events & 0x10)) - { - m_Events |= 0x10; - - for(uint32 i = BG_BE_OBJECT_DOOR_1; i <= BG_BE_OBJECT_DOOR_2; i++) - DoorOpen(i); - - for(uint32 i = BG_BE_OBJECT_BUFF_1; i <= BG_BE_OBJECT_BUFF_2; i++) - SpawnBGObject(i, 60); - - SendMessageToAll(LANG_ARENA_BEGUN); - SetStatus(STATUS_IN_PROGRESS); - SetStartDelayTime(0); - - for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) - if(Player *plr = objmgr.GetPlayer(itr->first)) - plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION); - - if(!GetPlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) - EndBattleGround(HORDE); - else if(GetPlayersCountByTeam(ALLIANCE) && !GetPlayersCountByTeam(HORDE)) - EndBattleGround(ALLIANCE); - } - } - - /*if(GetStatus() == STATUS_IN_PROGRESS) + /*if (GetStatus() == STATUS_IN_PROGRESS) { // update something }*/ } +void BattleGroundBE::StartingEventCloseDoors() +{ + for(uint32 i = BG_BE_OBJECT_DOOR_1; i <= BG_BE_OBJECT_DOOR_4; i++) + SpawnBGObject(i, RESPAWN_IMMEDIATELY); + + for(uint32 i = BG_BE_OBJECT_BUFF_1; i <= BG_BE_OBJECT_BUFF_2; i++) + SpawnBGObject(i, RESPAWN_ONE_DAY); +} + +void BattleGroundBE::StartingEventOpenDoors() +{ + for(uint32 i = BG_BE_OBJECT_DOOR_1; i <= BG_BE_OBJECT_DOOR_2; i++) + DoorOpen(i); + + for(uint32 i = BG_BE_OBJECT_BUFF_1; i <= BG_BE_OBJECT_BUFF_2; i++) + SpawnBGObject(i, 60); +} + void BattleGroundBE::AddPlayer(Player *plr) { BattleGround::AddPlayer(plr); @@ -122,24 +88,21 @@ void BattleGroundBE::AddPlayer(Player *plr) void BattleGroundBE::RemovePlayer(Player* /*plr*/, uint64 /*guid*/) { - if(GetStatus() == STATUS_WAIT_LEAVE) + if (GetStatus() == STATUS_WAIT_LEAVE) return; UpdateWorldState(0x9f1, GetAlivePlayersCountByTeam(ALLIANCE)); UpdateWorldState(0x9f0, GetAlivePlayersCountByTeam(HORDE)); - if(!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) - EndBattleGround(HORDE); - else if(GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE)) - EndBattleGround(ALLIANCE); + CheckArenaWinConditions(); } void BattleGroundBE::HandleKillPlayer(Player *player, Player *killer) { - if(GetStatus() != STATUS_IN_PROGRESS) + if (GetStatus() != STATUS_IN_PROGRESS) return; - if(!killer) + if (!killer) { sLog.outError("Killer player not found"); return; @@ -150,16 +113,7 @@ void BattleGroundBE::HandleKillPlayer(Player *player, Player *killer) UpdateWorldState(0x9f1, GetAlivePlayersCountByTeam(ALLIANCE)); UpdateWorldState(0x9f0, GetAlivePlayersCountByTeam(HORDE)); - if(!GetAlivePlayersCountByTeam(ALLIANCE)) - { - // all opponents killed - EndBattleGround(HORDE); - } - else if(!GetAlivePlayersCountByTeam(HORDE)) - { - // all opponents killed - EndBattleGround(ALLIANCE); - } + CheckArenaWinConditions(); } bool BattleGroundBE::HandlePlayerUnderMap(Player *player) @@ -171,7 +125,7 @@ bool BattleGroundBE::HandlePlayerUnderMap(Player *player) void BattleGroundBE::HandleAreaTrigger(Player *Source, uint32 Trigger) { // this is wrong way to implement these things. On official it done by gameobject spell cast. - if(GetStatus() != STATUS_IN_PROGRESS) + if (GetStatus() != STATUS_IN_PROGRESS) return; //uint32 SpellId = 0; @@ -190,7 +144,7 @@ void BattleGroundBE::HandleAreaTrigger(Player *Source, uint32 Trigger) break; } - //if(buff_guid) + //if (buff_guid) // HandleTriggerBuff(buff_guid,Source); } @@ -201,15 +155,16 @@ void BattleGroundBE::FillInitialWorldStates(WorldPacket &data) data << uint32(0x9f3) << uint32(1); // 9 } -void BattleGroundBE::ResetBGSubclass() +void BattleGroundBE::Reset() { - + //call parent's class reset + BattleGround::Reset(); } bool BattleGroundBE::SetupBattleGround() { // gates - if( !AddObject(BG_BE_OBJECT_DOOR_1, BG_BE_OBJECT_TYPE_DOOR_1, 6287.277f, 282.1877f, 3.810925f, -2.260201f, 0, 0, 0.9044551f, -0.4265689f, RESPAWN_IMMEDIATELY) + if (!AddObject(BG_BE_OBJECT_DOOR_1, BG_BE_OBJECT_TYPE_DOOR_1, 6287.277f, 282.1877f, 3.810925f, -2.260201f, 0, 0, 0.9044551f, -0.4265689f, RESPAWN_IMMEDIATELY) || !AddObject(BG_BE_OBJECT_DOOR_2, BG_BE_OBJECT_TYPE_DOOR_2, 6189.546f, 241.7099f, 3.101481f, 0.8813917f, 0, 0, 0.4265689f, 0.9044551f, RESPAWN_IMMEDIATELY) || !AddObject(BG_BE_OBJECT_DOOR_3, BG_BE_OBJECT_TYPE_DOOR_3, 6299.116f, 296.5494f, 3.308032f, 0.8813917f, 0, 0, 0.4265689f, 0.9044551f, RESPAWN_IMMEDIATELY) || !AddObject(BG_BE_OBJECT_DOOR_4, BG_BE_OBJECT_TYPE_DOOR_4, 6177.708f, 227.3481f, 3.604374f, -2.260201f, 0, 0, 0.9044551f, -0.4265689f, RESPAWN_IMMEDIATELY) diff --git a/src/game/BattleGroundBE.h b/src/game/BattleGroundBE.h index 00f826f8207..e75e332f44f 100644 --- a/src/game/BattleGroundBE.h +++ b/src/game/BattleGroundBE.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -57,15 +57,17 @@ class BattleGroundBE : public BattleGround public: BattleGroundBE(); ~BattleGroundBE(); - void Update(time_t diff); + void Update(uint32 diff); /* inherited from BattlegroundClass */ virtual void AddPlayer(Player *plr); + virtual void StartingEventCloseDoors(); + virtual void StartingEventOpenDoors(); void RemovePlayer(Player *plr, uint64 guid); void HandleAreaTrigger(Player *Source, uint32 Trigger); bool SetupBattleGround(); - void ResetBGSubclass(); + virtual void Reset(); virtual void FillInitialWorldStates(WorldPacket &d); void HandleKillPlayer(Player* player, Player *killer); bool HandlePlayerUnderMap(Player * plr); diff --git a/src/game/BattleGroundDS.cpp b/src/game/BattleGroundDS.cpp new file mode 100644 index 00000000000..78360b9b194 --- /dev/null +++ b/src/game/BattleGroundDS.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Player.h" +#include "BattleGround.h" +#include "BattleGroundDS.h" +#include "Language.h" + +BattleGroundDS::BattleGroundDS() +{ + + m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_1M; + m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_30S; + m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_15S; + m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE; + //we must set messageIds + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_ARENA_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_ARENA_THIRTY_SECONDS; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_ARENA_FIFTEEN_SECONDS; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_ARENA_HAS_BEGUN; +} + +BattleGroundDS::~BattleGroundDS() +{ + +} + +void BattleGroundDS::Update(uint32 diff) +{ + BattleGround::Update(diff); +} + +void BattleGroundDS::StartingEventCloseDoors() +{ +} + +void BattleGroundDS::StartingEventOpenDoors() +{ +} + +void BattleGroundDS::AddPlayer(Player *plr) +{ + BattleGround::AddPlayer(plr); + //create score and add it to map, default values are set in constructor + BattleGroundDSScore* sc = new BattleGroundDSScore; + + m_PlayerScores[plr->GetGUID()] = sc; +} + +void BattleGroundDS::RemovePlayer(Player * /*plr*/, uint64 /*guid*/) +{ +} + +void BattleGroundDS::HandleKillPlayer(Player* player, Player* killer) +{ + BattleGround::HandleKillPlayer(player, killer); +} + +void BattleGroundDS::HandleAreaTrigger(Player * /*Source*/, uint32 /*Trigger*/) +{ +} + +bool BattleGroundDS::SetupBattleGround() +{ + return true; +} diff --git a/src/game/BattleGroundDS.h b/src/game/BattleGroundDS.h new file mode 100644 index 00000000000..44a6cfbd33a --- /dev/null +++ b/src/game/BattleGroundDS.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __BATTLEGROUNDDS_H +#define __BATTLEGROUNDDS_H + +class BattleGround; + +class BattleGroundDSScore : public BattleGroundScore +{ + public: + BattleGroundDSScore() {}; + virtual ~BattleGroundDSScore() {}; + //TODO fix me +}; + +class BattleGroundDS : public BattleGround +{ + friend class BattleGroundMgr; + + public: + BattleGroundDS(); + ~BattleGroundDS(); + void Update(uint32 diff); + + /* inherited from BattlegroundClass */ + virtual void AddPlayer(Player *plr); + virtual void StartingEventCloseDoors(); + virtual void StartingEventOpenDoors(); + + void RemovePlayer(Player *plr, uint64 guid); + void HandleAreaTrigger(Player *Source, uint32 Trigger); + bool SetupBattleGround(); + void HandleKillPlayer(Player* player, Player *killer); +}; +#endif diff --git a/src/game/BattleGroundEY.cpp b/src/game/BattleGroundEY.cpp index ecf454d1cfe..04ab25c30cc 100644 --- a/src/game/BattleGroundEY.cpp +++ b/src/game/BattleGroundEY.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,11 +23,10 @@ #include "BattleGround.h" #include "BattleGroundEY.h" #include "Creature.h" -#include "Chat.h" #include "ObjectMgr.h" -#include "MapManager.h" #include "Language.h" -#include "World.h" +#include "World.h" //music +#include "WorldPacket.h" #include "Util.h" // these variables aren't used outside of this file, so declare them only here @@ -45,85 +44,25 @@ BattleGroundEY::BattleGroundEY() m_Points_Trigger[BLOOD_ELF] = TR_BLOOD_ELF_BUFF; m_Points_Trigger[DRAENEI_RUINS] = TR_DRAENEI_RUINS_BUFF; m_Points_Trigger[MAGE_TOWER] = TR_MAGE_TOWER_BUFF; + + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_EY_START_TWO_MINUTES; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_EY_START_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_EY_START_HALF_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_EY_HAS_BEGUN; } BattleGroundEY::~BattleGroundEY() { } -void BattleGroundEY::Update(time_t diff) +void BattleGroundEY::Update(uint32 diff) { BattleGround::Update(diff); - // after bg start we get there (once) - if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize()) - { - ModifyStartDelayTime(diff); - - if(!(m_Events & 0x01)) - { - m_Events |= 0x01; - - // setup here, only when at least one player has ported to the map - if(!SetupBattleGround()) - { - EndNow(); - return; - } - - SpawnBGObject(BG_EY_OBJECT_DOOR_A, RESPAWN_IMMEDIATELY); - SpawnBGObject(BG_EY_OBJECT_DOOR_H, RESPAWN_IMMEDIATELY); - -// SpawnBGCreature(EY_SPIRIT_MAIN_ALLIANCE, RESPAWN_IMMEDIATELY); -// SpawnBGCreature(EY_SPIRIT_MAIN_HORDE, RESPAWN_IMMEDIATELY); - for(uint32 i = BG_EY_OBJECT_A_BANNER_FEL_REALVER_CENTER; i < BG_EY_OBJECT_MAX; ++i) - SpawnBGObject(i, RESPAWN_ONE_DAY); - - SetStartDelayTime(START_DELAY0); - } - // After 1 minute, warning is signalled - else if(GetStartDelayTime() <= START_DELAY1 && !(m_Events & 0x04)) - { - m_Events |= 0x04; - SendMessageToAll(GetTrinityString(LANG_BG_EY_ONE_MINUTE)); - } - // After 1,5 minute, warning is signalled - else if(GetStartDelayTime() <= START_DELAY2 && !(m_Events & 0x08)) - { - m_Events |= 0x08; - SendMessageToAll(GetTrinityString(LANG_BG_EY_HALF_MINUTE)); - } - // After 2 minutes, gates OPEN ! x) - else if(GetStartDelayTime() < 0 && !(m_Events & 0x10)) - { - m_Events |= 0x10; - SpawnBGObject(BG_EY_OBJECT_DOOR_A, RESPAWN_ONE_DAY); - SpawnBGObject(BG_EY_OBJECT_DOOR_H, RESPAWN_ONE_DAY); - - for(uint32 i = BG_EY_OBJECT_N_BANNER_FEL_REALVER_CENTER; i <= BG_EY_OBJECT_FLAG_NETHERSTORM; ++i) - SpawnBGObject(i, RESPAWN_IMMEDIATELY); - for(uint32 i = 0; i < EY_POINTS_MAX; ++i) - { - //randomly spawn buff - uint8 buff = urand(0, 2); - SpawnBGObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER + buff + i * 3, RESPAWN_IMMEDIATELY); - } - - SendMessageToAll(GetTrinityString(LANG_BG_EY_BEGIN)); - - PlaySoundToAll(SOUND_BG_START); - if(sWorld.getConfig(CONFIG_BG_START_MUSIC)) - PlaySoundToAll(SOUND_BG_START_L70ETC); //MUSIC - SetStatus(STATUS_IN_PROGRESS); - for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) - if(Player *plr = objmgr.GetPlayer(itr->first)) - plr->RemoveAurasDueToSpell(SPELL_PREPARATION); - } - } - else if(GetStatus() == STATUS_IN_PROGRESS) + if (GetStatus() == STATUS_IN_PROGRESS) { m_PointAddingTimer -= diff; - if(m_PointAddingTimer <= 0) + if (m_PointAddingTimer <= 0) { m_PointAddingTimer = BG_EY_FPOINTS_TICK_TIME; if (m_TeamPointsCount[BG_TEAM_ALLIANCE] > 0) @@ -132,11 +71,11 @@ void BattleGroundEY::Update(time_t diff) AddPoints(HORDE, BG_EY_TickPoints[m_TeamPointsCount[BG_TEAM_HORDE] - 1]); } - if(m_FlagState == BG_EY_FLAG_STATE_WAIT_RESPAWN || m_FlagState == BG_EY_FLAG_STATE_ON_GROUND) + if (m_FlagState == BG_EY_FLAG_STATE_WAIT_RESPAWN || m_FlagState == BG_EY_FLAG_STATE_ON_GROUND) { m_FlagsTimer -= diff; - if(m_FlagsTimer < 0) + if (m_FlagsTimer < 0) { m_FlagsTimer = 0; if (m_FlagState == BG_EY_FLAG_STATE_WAIT_RESPAWN) @@ -147,7 +86,7 @@ void BattleGroundEY::Update(time_t diff) } m_TowerCapCheckTimer -= diff; - if(m_TowerCapCheckTimer <= 0) + if (m_TowerCapCheckTimer <= 0) { //check if player joined point /*I used this order of calls, because although we will check if one player is in gameobject's distance 2 times @@ -162,16 +101,39 @@ void BattleGroundEY::Update(time_t diff) } } +void BattleGroundEY::StartingEventCloseDoors() +{ + SpawnBGObject(BG_EY_OBJECT_DOOR_A, RESPAWN_IMMEDIATELY); + SpawnBGObject(BG_EY_OBJECT_DOOR_H, RESPAWN_IMMEDIATELY); + + for(uint32 i = BG_EY_OBJECT_A_BANNER_FEL_REALVER_CENTER; i < BG_EY_OBJECT_MAX; ++i) + SpawnBGObject(i, RESPAWN_ONE_DAY); +} + +void BattleGroundEY::StartingEventOpenDoors() +{ + SpawnBGObject(BG_EY_OBJECT_DOOR_A, RESPAWN_ONE_DAY); + SpawnBGObject(BG_EY_OBJECT_DOOR_H, RESPAWN_ONE_DAY); + + for(uint32 i = BG_EY_OBJECT_N_BANNER_FEL_REALVER_CENTER; i <= BG_EY_OBJECT_FLAG_NETHERSTORM; ++i) + SpawnBGObject(i, RESPAWN_IMMEDIATELY); + for(uint32 i = 0; i < EY_POINTS_MAX; ++i) + { + //randomly spawn buff + uint8 buff = urand(0, 2); + SpawnBGObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER + buff + i * 3, RESPAWN_IMMEDIATELY); + } +} + void BattleGroundEY::AddPoints(uint32 Team, uint32 Points) { uint8 team_index = GetTeamIndexByTeamId(Team); m_TeamScores[team_index] += Points; - m_score[team_index] = m_TeamScores[team_index]; m_HonorScoreTics[team_index] += Points; - if (m_HonorScoreTics[team_index] >= BG_EY_HonorScoreTicks[m_HonorMode]) + if (m_HonorScoreTics[team_index] >= m_HonorTics ) { - RewardHonorToTeam(20, Team); - m_HonorScoreTics[team_index] -= BG_EY_HonorScoreTicks[m_HonorMode]; + RewardHonorToTeam(GetBonusHonorFromKill(1), Team); + m_HonorScoreTics[team_index] -= m_HonorTics; } UpdateTeamScore(Team); } @@ -188,13 +150,13 @@ void BattleGroundEY::CheckSomeoneJoinedPoint() while (j < m_PlayersNearPoint[EY_POINTS_MAX].size()) { Player *plr = objmgr.GetPlayer(m_PlayersNearPoint[EY_POINTS_MAX][j]); - if(!plr) + if (!plr) { sLog.outError("BattleGroundEY: Player " I64FMTD " not found!", m_PlayersNearPoint[EY_POINTS_MAX][j]); ++j; continue; } - if (plr->isAllowUseBattleGroundObject() && plr->IsWithinDistInMap(obj, BG_EY_POINT_RADIUS)) + if (plr->CanCaptureTowerPoint() && plr->IsWithinDistInMap(obj, BG_EY_POINT_RADIUS)) { //player joined point! //show progress bar @@ -222,7 +184,7 @@ void BattleGroundEY::CheckSomeoneLeftPoint() for(uint8 i = 0; i < EY_POINTS_MAX; ++i) { obj = HashMapHolder<GameObject>::Find(m_BgObjects[BG_EY_OBJECT_TOWER_CAP_FEL_REALVER + i]); - if(obj) + if (obj) { uint8 j = 0; while (j < m_PlayersNearPoint[i].size()) @@ -237,7 +199,7 @@ void BattleGroundEY::CheckSomeoneLeftPoint() ++j; continue; } - if (!plr->isAllowUseBattleGroundObject() || !plr->IsWithinDistInMap(obj, BG_EY_POINT_RADIUS)) + if (!plr->CanCaptureTowerPoint() || !plr->IsWithinDistInMap(obj, BG_EY_POINT_RADIUS)) //move player out of point (add him to players that are out of points { m_PlayersNearPoint[EY_POINTS_MAX].push_back(m_PlayersNearPoint[i][j]); @@ -305,21 +267,46 @@ void BattleGroundEY::UpdatePointStatuses() void BattleGroundEY::UpdateTeamScore(uint32 Team) { uint32 score = GetTeamScore(Team); - if(score >= EY_MAX_TEAM_SCORE) + //TODO there should be some sound played when one team is near victory!! - and define variables + /*if (!m_IsInformedNearVictory && score >= BG_EY_WARNING_NEAR_VICTORY_SCORE) + { + if (Team == ALLIANCE) + SendMessageToAll(LANG_BG_EY_A_NEAR_VICTORY, CHAT_MSG_BG_SYSTEM_NEUTRAL); + else + SendMessageToAll(LANG_BG_EY_H_NEAR_VICTORY, CHAT_MSG_BG_SYSTEM_NEUTRAL); + PlaySoundToAll(BG_EY_SOUND_NEAR_VICTORY); + m_IsInformedNearVictory = true; + }*/ + + if (score >= BG_EY_MAX_TEAM_SCORE) { - score = EY_MAX_TEAM_SCORE; + score = BG_EY_MAX_TEAM_SCORE; EndBattleGround(Team); } - if(Team == ALLIANCE) + if (Team == ALLIANCE) UpdateWorldState(EY_ALLIANCE_RESOURCES, score); else UpdateWorldState(EY_HORDE_RESOURCES, score); } +void BattleGroundEY::EndBattleGround(uint32 winner) +{ + //win reward + if (winner == ALLIANCE) + RewardHonorToTeam(GetBonusHonorFromKill(1), ALLIANCE); + if (winner == HORDE) + RewardHonorToTeam(GetBonusHonorFromKill(1), HORDE); + //complete map reward + RewardHonorToTeam(GetBonusHonorFromKill(1), ALLIANCE); + RewardHonorToTeam(GetBonusHonorFromKill(1), HORDE); + + BattleGround::EndBattleGround(winner); +} + void BattleGroundEY::UpdatePointsCount(uint32 Team) { - if(Team == ALLIANCE) + if (Team == ALLIANCE) UpdateWorldState(EY_ALLIANCE_BASE, m_TeamPointsCount[BG_TEAM_ALLIANCE]); else UpdateWorldState(EY_HORDE_BASE, m_TeamPointsCount[BG_TEAM_HORDE]); @@ -331,14 +318,14 @@ void BattleGroundEY::UpdatePointsIcons(uint32 Team, uint32 Point) if (m_PointState[Point] == EY_POINT_UNDER_CONTROL) { UpdateWorldState(m_PointsIconStruct[Point].WorldStateControlIndex, 0); - if(Team == ALLIANCE) + if (Team == ALLIANCE) UpdateWorldState(m_PointsIconStruct[Point].WorldStateAllianceControlledIndex, 1); else UpdateWorldState(m_PointsIconStruct[Point].WorldStateHordeControlledIndex, 1); } else { - if(Team == ALLIANCE) + if (Team == ALLIANCE) UpdateWorldState(m_PointsIconStruct[Point].WorldStateAllianceControlledIndex, 0); else UpdateWorldState(m_PointsIconStruct[Point].WorldStateHordeControlledIndex, 0); @@ -363,15 +350,15 @@ void BattleGroundEY::RemovePlayer(Player *plr, uint64 guid) for (int j = EY_POINTS_MAX; j >= 0; --j) { for(int i = 0; i < m_PlayersNearPoint[j].size(); ++i) - if(m_PlayersNearPoint[j][i] == guid) + if (m_PlayersNearPoint[j][i] == guid) m_PlayersNearPoint[j].erase(m_PlayersNearPoint[j].begin() + i); } - if(IsFlagPickedup()) + if (IsFlagPickedup()) { - if(m_FlagKeeper == guid) + if (m_FlagKeeper == guid) { - if(plr) - this->EventPlayerDroppedFlag(plr); + if (plr) + EventPlayerDroppedFlag(plr); else { SetFlagPicker(0); @@ -383,7 +370,7 @@ void BattleGroundEY::RemovePlayer(Player *plr, uint64 guid) void BattleGroundEY::HandleAreaTrigger(Player *Source, uint32 Trigger) { - if(GetStatus() != STATUS_IN_PROGRESS) + if (GetStatus() != STATUS_IN_PROGRESS) return; if(!Source->isAlive()) //hack code, must be removed later @@ -392,23 +379,23 @@ void BattleGroundEY::HandleAreaTrigger(Player *Source, uint32 Trigger) switch(Trigger) { case TR_BLOOD_ELF_POINT: - if(m_PointState[BLOOD_ELF] == EY_POINT_UNDER_CONTROL && m_PointOwnedByTeam[BLOOD_ELF] == Source->GetTeam()) - if(m_FlagState && GetFlagPickerGUID() == Source->GetGUID()) + if (m_PointState[BLOOD_ELF] == EY_POINT_UNDER_CONTROL && m_PointOwnedByTeam[BLOOD_ELF] == Source->GetTeam()) + if (m_FlagState && GetFlagPickerGUID() == Source->GetGUID()) EventPlayerCapturedFlag(Source, BG_EY_OBJECT_FLAG_BLOOD_ELF); break; case TR_FEL_REALVER_POINT: - if(m_PointState[FEL_REALVER] == EY_POINT_UNDER_CONTROL && m_PointOwnedByTeam[FEL_REALVER] == Source->GetTeam()) - if(m_FlagState && GetFlagPickerGUID() == Source->GetGUID()) + if (m_PointState[FEL_REALVER] == EY_POINT_UNDER_CONTROL && m_PointOwnedByTeam[FEL_REALVER] == Source->GetTeam()) + if (m_FlagState && GetFlagPickerGUID() == Source->GetGUID()) EventPlayerCapturedFlag(Source, BG_EY_OBJECT_FLAG_FEL_REALVER); break; case TR_MAGE_TOWER_POINT: - if(m_PointState[MAGE_TOWER] == EY_POINT_UNDER_CONTROL && m_PointOwnedByTeam[MAGE_TOWER] == Source->GetTeam()) - if(m_FlagState && GetFlagPickerGUID() == Source->GetGUID()) + if (m_PointState[MAGE_TOWER] == EY_POINT_UNDER_CONTROL && m_PointOwnedByTeam[MAGE_TOWER] == Source->GetTeam()) + if (m_FlagState && GetFlagPickerGUID() == Source->GetGUID()) EventPlayerCapturedFlag(Source, BG_EY_OBJECT_FLAG_MAGE_TOWER); break; case TR_DRAENEI_RUINS_POINT: - if(m_PointState[DRAENEI_RUINS] == EY_POINT_UNDER_CONTROL && m_PointOwnedByTeam[DRAENEI_RUINS] == Source->GetTeam()) - if(m_FlagState && GetFlagPickerGUID() == Source->GetGUID()) + if (m_PointState[DRAENEI_RUINS] == EY_POINT_UNDER_CONTROL && m_PointOwnedByTeam[DRAENEI_RUINS] == Source->GetTeam()) + if (m_FlagState && GetFlagPickerGUID() == Source->GetGUID()) EventPlayerCapturedFlag(Source, BG_EY_OBJECT_FLAG_DRAENEI_RUINS); break; case 4512: @@ -432,7 +419,7 @@ void BattleGroundEY::HandleAreaTrigger(Player *Source, uint32 Trigger) bool BattleGroundEY::SetupBattleGround() { // doors - if( !AddObject(BG_EY_OBJECT_DOOR_A, BG_OBJECT_A_DOOR_EY_ENTRY, 2527.6f, 1596.91f, 1262.13f, -3.12414f, -0.173642f, -0.001515f, 0.98477f, -0.008594f, RESPAWN_IMMEDIATELY) + if (!AddObject(BG_EY_OBJECT_DOOR_A, BG_OBJECT_A_DOOR_EY_ENTRY, 2527.6f, 1596.91f, 1262.13f, -3.12414f, -0.173642f, -0.001515f, 0.98477f, -0.008594f, RESPAWN_IMMEDIATELY) || !AddObject(BG_EY_OBJECT_DOOR_H, BG_OBJECT_H_DOOR_EY_ENTRY, 1803.21f, 1539.49f, 1261.09f, 3.14159f, 0.173648f, 0, 0.984808f, 0, RESPAWN_IMMEDIATELY) // banners (alliance) || !AddObject(BG_EY_OBJECT_A_BANNER_FEL_REALVER_CENTER, BG_OBJECT_A_BANNER_EY_ENTRY, 2057.46f, 1735.07f, 1187.91f, -0.925024f, 0, 0, 0.446198f, -0.894934f, RESPAWN_ONE_DAY) @@ -494,12 +481,12 @@ bool BattleGroundEY::SetupBattleGround() for (int i = 0; i < EY_POINTS_MAX; ++i) { AreaTriggerEntry const* at = sAreaTriggerStore.LookupEntry(m_Points_Trigger[i]); - if( !at ) + if (!at) { sLog.outError("BattleGroundEY: Unknown trigger: %u", m_Points_Trigger[i]); continue; } - if ( !AddObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER + i * 3, Buff_Entries[0], at->x, at->y, at->z, 0.907571f, 0, 0, 0.438371f, 0.898794f, RESPAWN_ONE_DAY) + if (!AddObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER + i * 3, Buff_Entries[0], at->x, at->y, at->z, 0.907571f, 0, 0, 0.438371f, 0.898794f, RESPAWN_ONE_DAY) || !AddObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER + i * 3 + 1, Buff_Entries[1], at->x, at->y, at->z, 0.907571f, 0, 0, 0.438371f, 0.898794f, RESPAWN_ONE_DAY) || !AddObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER + i * 3 + 2, Buff_Entries[2], at->x, at->y, at->z, 0.907571f, 0, 0, 0.438371f, 0.898794f, RESPAWN_ONE_DAY) ) @@ -508,14 +495,14 @@ bool BattleGroundEY::SetupBattleGround() WorldSafeLocsEntry const *sg = NULL; sg = sWorldSafeLocsStore.LookupEntry(EY_GRAVEYARD_MAIN_ALLIANCE); - if( !sg || !AddSpiritGuide(EY_SPIRIT_MAIN_ALLIANCE, sg->x, sg->y, sg->z, 3.124139f, ALLIANCE) ) + if (!sg || !AddSpiritGuide(EY_SPIRIT_MAIN_ALLIANCE, sg->x, sg->y, sg->z, 3.124139f, ALLIANCE)) { sLog.outErrorDb("BatteGroundEY: Failed to spawn spirit guide! BattleGround not created!"); return false; } sg = sWorldSafeLocsStore.LookupEntry(EY_GRAVEYARD_MAIN_HORDE); - if( !sg || !AddSpiritGuide(EY_SPIRIT_MAIN_HORDE, sg->x, sg->y, sg->z, 3.193953f, HORDE) ) + if (!sg || !AddSpiritGuide(EY_SPIRIT_MAIN_HORDE, sg->x, sg->y, sg->z, 3.193953f, HORDE)) { sLog.outErrorDb("BatteGroundEY: Failed to spawn spirit guide! BattleGround not created!"); return false; @@ -524,8 +511,11 @@ bool BattleGroundEY::SetupBattleGround() return true; } -void BattleGroundEY::ResetBGSubclass() +void BattleGroundEY::Reset() { + //call parent's class reset + BattleGround::Reset(); + m_TeamScores[BG_TEAM_ALLIANCE] = 0; m_TeamScores[BG_TEAM_HORDE] = 0; m_TeamPointsCount[BG_TEAM_ALLIANCE] = 0; @@ -538,6 +528,8 @@ void BattleGroundEY::ResetBGSubclass() m_DroppedFlagGUID = 0; m_PointAddingTimer = 0; m_TowerCapCheckTimer = 0; + bool isBGWeekend = false; //TODO FIXME - call sBattleGroundMgr.IsBGWeekend(m_TypeID); - you must also implement that call! + m_HonorTics = (isBGWeekend) ? BG_EY_EYWeekendHonorTicks : BG_EY_NotEYWeekendHonorTicks; for(uint8 i = 0; i < EY_POINTS_MAX; ++i) { @@ -560,9 +552,9 @@ void BattleGroundEY::RespawnFlag(bool send_message) m_FlagState = BG_EY_FLAG_STATE_ON_BASE; SpawnBGObject(BG_EY_OBJECT_FLAG_NETHERSTORM, RESPAWN_IMMEDIATELY); - if(send_message) + if (send_message) { - SendMessageToAll(GetTrinityString(LANG_BG_EY_RESETED_FLAG)); + SendMessageToAll(LANG_BG_EY_RESETED_FLAG, CHAT_MSG_BG_SYSTEM_NEUTRAL); PlaySoundToAll(BG_EY_SOUND_FLAG_RESET); // flags respawned sound... } @@ -574,7 +566,7 @@ void BattleGroundEY::RespawnFlagAfterDrop() RespawnFlag(true); GameObject *obj = HashMapHolder<GameObject>::Find(GetDroppedFlagGUID()); - if(obj) + if (obj) obj->Delete(); else sLog.outError("BattleGroundEY: Unknown dropped flag guid: %u",GUID_LOPART(GetDroppedFlagGUID())); @@ -584,7 +576,7 @@ void BattleGroundEY::RespawnFlagAfterDrop() void BattleGroundEY::HandleKillPlayer(Player *player, Player *killer) { - if(GetStatus() != STATUS_IN_PROGRESS) + if (GetStatus() != STATUS_IN_PROGRESS) return; BattleGround::HandleKillPlayer(player, killer); @@ -593,11 +585,11 @@ void BattleGroundEY::HandleKillPlayer(Player *player, Player *killer) void BattleGroundEY::EventPlayerDroppedFlag(Player *Source) { - if(GetStatus() != STATUS_IN_PROGRESS) + if (GetStatus() != STATUS_IN_PROGRESS) { // if not running, do not cast things at the dropper player, neither send unnecessary messages // just take off the aura - if(IsFlagPickedup() && GetFlagPickerGUID() == Source->GetGUID()) + if (IsFlagPickedup() && GetFlagPickerGUID() == Source->GetGUID()) { SetFlagPicker(0); Source->RemoveAurasDueToSpell(BG_EY_NETHERSTORM_FLAG_SPELL); @@ -605,59 +597,41 @@ void BattleGroundEY::EventPlayerDroppedFlag(Player *Source) return; } - if(!IsFlagPickedup()) + if (!IsFlagPickedup()) return; - if(GetFlagPickerGUID() != Source->GetGUID()) + if (GetFlagPickerGUID() != Source->GetGUID()) return; - const char *message = ""; - uint8 type = 0; - SetFlagPicker(0); Source->RemoveAurasDueToSpell(BG_EY_NETHERSTORM_FLAG_SPELL); m_FlagState = BG_EY_FLAG_STATE_ON_GROUND; m_FlagsTimer = BG_EY_FLAG_RESPAWN_TIME; Source->CastSpell(Source, SPELL_RECENTLY_DROPPED_FLAG, true); Source->CastSpell(Source, BG_EY_PLAYER_DROPPED_FLAG_SPELL, true); - if(Source->GetTeam() == ALLIANCE) - { - message = GetTrinityString(LANG_BG_EY_DROPPED_FLAG); - type = CHAT_MSG_BG_SYSTEM_ALLIANCE; - } - else - { - message = GetTrinityString(LANG_BG_EY_DROPPED_FLAG); - type = CHAT_MSG_BG_SYSTEM_HORDE; - } //this does not work correctly :( (it should remove flag carrier name) UpdateWorldState(NETHERSTORM_FLAG_STATE_HORDE, BG_EY_FLAG_STATE_WAIT_RESPAWN); UpdateWorldState(NETHERSTORM_FLAG_STATE_ALLIANCE, BG_EY_FLAG_STATE_WAIT_RESPAWN); - WorldPacket data; - ChatHandler::FillMessageData(&data, Source->GetSession(), type, LANG_UNIVERSAL, NULL, Source->GetGUID(), message, NULL); - SendPacketToAll(&data); + if (Source->GetTeam() == ALLIANCE) + SendMessageToAll(LANG_BG_EY_DROPPED_FLAG, CHAT_MSG_BG_SYSTEM_ALLIANCE, NULL); + else + SendMessageToAll(LANG_BG_EY_DROPPED_FLAG, CHAT_MSG_BG_SYSTEM_HORDE, NULL); } void BattleGroundEY::EventPlayerClickedOnFlag(Player *Source, GameObject* target_obj) { - if(GetStatus() != STATUS_IN_PROGRESS || this->IsFlagPickedup() || !Source->IsWithinDistInMap(target_obj, 10)) + if (GetStatus() != STATUS_IN_PROGRESS || IsFlagPickedup() || !Source->IsWithinDistInMap(target_obj, 10)) return; - const char *message; - uint8 type = 0; - message = GetTrinityString(LANG_BG_EY_HAS_TAKEN_FLAG); - - if(Source->GetTeam() == ALLIANCE) + if (Source->GetTeam() == ALLIANCE) { UpdateWorldState(NETHERSTORM_FLAG_STATE_ALLIANCE, BG_EY_FLAG_STATE_ON_PLAYER); - type = CHAT_MSG_BG_SYSTEM_ALLIANCE; PlaySoundToAll(BG_EY_SOUND_FLAG_PICKED_UP_ALLIANCE); } else { UpdateWorldState(NETHERSTORM_FLAG_STATE_HORDE, BG_EY_FLAG_STATE_ON_PLAYER); - type = CHAT_MSG_BG_SYSTEM_HORDE; PlaySoundToAll(BG_EY_SOUND_FLAG_PICKED_UP_HORDE); } @@ -671,29 +645,26 @@ void BattleGroundEY::EventPlayerClickedOnFlag(Player *Source, GameObject* target Source->CastSpell(Source, BG_EY_NETHERSTORM_FLAG_SPELL, true); Source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); - WorldPacket data; - ChatHandler::FillMessageData(&data, Source->GetSession(), type, LANG_UNIVERSAL, NULL, Source->GetGUID(), message, NULL); - SendPacketToAll(&data); + if (Source->GetTeam() == ALLIANCE) + PSendMessageToAll(LANG_BG_EY_HAS_TAKEN_FLAG, CHAT_MSG_BG_SYSTEM_ALLIANCE, NULL, Source->GetName()); + else + PSendMessageToAll(LANG_BG_EY_HAS_TAKEN_FLAG, CHAT_MSG_BG_SYSTEM_HORDE, NULL, Source->GetName()); } void BattleGroundEY::EventTeamLostPoint(Player *Source, uint32 Point) { - if(GetStatus() != STATUS_IN_PROGRESS) + if (GetStatus() != STATUS_IN_PROGRESS) return; //Natural point - uint8 message_type = 0; - const char *message = ""; uint32 Team = m_PointOwnedByTeam[Point]; - if(!Team) + if (!Team) return; if (Team == ALLIANCE) { m_TeamPointsCount[BG_TEAM_ALLIANCE]--; - message_type = CHAT_MSG_BG_SYSTEM_ALLIANCE; - message = GetTrinityString(m_LoosingPointTypes[Point].MessageIdAlliance); SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeAlliance, RESPAWN_ONE_DAY); SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeAlliance + 1, RESPAWN_ONE_DAY); SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeAlliance + 2, RESPAWN_ONE_DAY); @@ -701,8 +672,6 @@ void BattleGroundEY::EventTeamLostPoint(Player *Source, uint32 Point) else { m_TeamPointsCount[BG_TEAM_HORDE]--; - message_type = CHAT_MSG_BG_SYSTEM_HORDE; - message = GetTrinityString(m_LoosingPointTypes[Point].MessageIdHorde); SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeHorde, RESPAWN_ONE_DAY); SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeHorde + 1, RESPAWN_ONE_DAY); SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeHorde + 2, RESPAWN_ONE_DAY); @@ -717,9 +686,10 @@ void BattleGroundEY::EventTeamLostPoint(Player *Source, uint32 Point) m_PointOwnedByTeam[Point] = EY_POINT_NO_OWNER; m_PointState[Point] = EY_POINT_NO_OWNER; - WorldPacket data; - ChatHandler::FillMessageData(&data, Source->GetSession(), message_type, LANG_UNIVERSAL, NULL, Source->GetGUID(), message, NULL); - SendPacketToAll(&data); + if (Team == ALLIANCE) + SendMessageToAll(m_LoosingPointTypes[Point].MessageIdAlliance,CHAT_MSG_BG_SYSTEM_ALLIANCE, Source); + else + SendMessageToAll(m_LoosingPointTypes[Point].MessageIdHorde,CHAT_MSG_BG_SYSTEM_HORDE, Source); UpdatePointsIcons(Team, Point); UpdatePointsCount(Team); @@ -727,11 +697,9 @@ void BattleGroundEY::EventTeamLostPoint(Player *Source, uint32 Point) void BattleGroundEY::EventTeamCapturedPoint(Player *Source, uint32 Point) { - if(GetStatus() != STATUS_IN_PROGRESS) + if (GetStatus() != STATUS_IN_PROGRESS) return; - uint8 type = 0; - const char *message = ""; uint32 Team = Source->GetTeam(); SpawnBGObject(m_CapturingPointTypes[Point].DespawnNeutralObjectType, RESPAWN_ONE_DAY); @@ -741,8 +709,6 @@ void BattleGroundEY::EventTeamCapturedPoint(Player *Source, uint32 Point) if (Team == ALLIANCE) { m_TeamPointsCount[BG_TEAM_ALLIANCE]++; - type = CHAT_MSG_BG_SYSTEM_ALLIANCE; - message = GetTrinityString(m_CapturingPointTypes[Point].MessageIdAlliance); SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeAlliance, RESPAWN_IMMEDIATELY); SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeAlliance + 1, RESPAWN_IMMEDIATELY); SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeAlliance + 2, RESPAWN_IMMEDIATELY); @@ -750,8 +716,6 @@ void BattleGroundEY::EventTeamCapturedPoint(Player *Source, uint32 Point) else { m_TeamPointsCount[BG_TEAM_HORDE]++; - type = CHAT_MSG_BG_SYSTEM_HORDE; - message = GetTrinityString(m_CapturingPointTypes[Point].MessageIdHorde); SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeHorde, RESPAWN_IMMEDIATELY); SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeHorde + 1, RESPAWN_IMMEDIATELY); SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeHorde + 2, RESPAWN_IMMEDIATELY); @@ -762,16 +726,17 @@ void BattleGroundEY::EventTeamCapturedPoint(Player *Source, uint32 Point) m_PointOwnedByTeam[Point] = Team; m_PointState[Point] = EY_POINT_UNDER_CONTROL; - WorldPacket data; - ChatHandler::FillMessageData(&data, Source->GetSession(), type, LANG_UNIVERSAL, NULL, Source->GetGUID(), message, NULL); - SendPacketToAll(&data); + if (Team == ALLIANCE) + SendMessageToAll(m_CapturingPointTypes[Point].MessageIdAlliance,CHAT_MSG_BG_SYSTEM_ALLIANCE, Source); + else + SendMessageToAll(m_CapturingPointTypes[Point].MessageIdHorde,CHAT_MSG_BG_SYSTEM_HORDE, Source); - if(m_BgCreatures[Point]) + if (m_BgCreatures[Point]) DelCreature(Point); WorldSafeLocsEntry const *sg = NULL; sg = sWorldSafeLocsStore.LookupEntry(m_CapturingPointTypes[Point].GraveYardId); - if(!sg || !AddSpiritGuide(Point, sg->x, sg->y, sg->z, 3.124139f, Team)) + if (!sg || !AddSpiritGuide(Point, sg->x, sg->y, sg->z, 3.124139f, Team)) sLog.outError("BatteGroundEY: Failed to spawn spirit guide! point: %u, team: %u, graveyard_id: %u", Point, Team, m_CapturingPointTypes[Point].GraveYardId); @@ -783,43 +748,38 @@ void BattleGroundEY::EventTeamCapturedPoint(Player *Source, uint32 Point) void BattleGroundEY::EventPlayerCapturedFlag(Player *Source, uint32 BgObjectType) { - if(GetStatus() != STATUS_IN_PROGRESS || this->GetFlagPickerGUID() != Source->GetGUID()) + if (GetStatus() != STATUS_IN_PROGRESS || GetFlagPickerGUID() != Source->GetGUID()) return; - uint8 type = 0; - uint8 team_id = 0; - const char *message = ""; - SetFlagPicker(0); m_FlagState = BG_EY_FLAG_STATE_WAIT_RESPAWN; Source->RemoveAurasDueToSpell(BG_EY_NETHERSTORM_FLAG_SPELL); Source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); - if(Source->GetTeam() == ALLIANCE) - { + + if (Source->GetTeam() == ALLIANCE) PlaySoundToAll(BG_EY_SOUND_FLAG_CAPTURED_ALLIANCE); - team_id = BG_TEAM_ALLIANCE; - message = GetTrinityString(LANG_BG_EY_CAPTURED_FLAG_A); - type = CHAT_MSG_BG_SYSTEM_ALLIANCE; - } else - { PlaySoundToAll(BG_EY_SOUND_FLAG_CAPTURED_HORDE); - team_id = BG_TEAM_HORDE; - message = GetTrinityString(LANG_BG_EY_CAPTURED_FLAG_H); - type = CHAT_MSG_BG_SYSTEM_HORDE; - } SpawnBGObject(BgObjectType, RESPAWN_IMMEDIATELY); m_FlagsTimer = BG_EY_FLAG_RESPAWN_TIME; m_FlagCapturedBgObjectType = BgObjectType; - WorldPacket data; - ChatHandler::FillMessageData(&data, Source->GetSession(), type, LANG_UNIVERSAL, NULL, Source->GetGUID(), message, NULL); - SendPacketToAll(&data); + uint8 team_id = 0; + if (Source->GetTeam() == ALLIANCE) + { + team_id = BG_TEAM_ALLIANCE; + SendMessageToAll(LANG_BG_EY_CAPTURED_FLAG_A, CHAT_MSG_BG_SYSTEM_ALLIANCE, Source); + } + else + { + team_id = BG_TEAM_HORDE; + SendMessageToAll(LANG_BG_EY_CAPTURED_FLAG_H, CHAT_MSG_BG_SYSTEM_HORDE, Source); + } - if(m_TeamPointsCount[team_id] > 0) + if (m_TeamPointsCount[team_id] > 0) AddPoints(Source->GetTeam(), BG_EY_FlagPoints[m_TeamPointsCount[team_id] - 1]); UpdatePlayerScore(Source, SCORE_FLAG_CAPTURES, 1); @@ -893,16 +853,16 @@ void BattleGroundEY::FillInitialWorldStates(WorldPacket& data) data << uint32(0xc0d) << uint32(0x17b); } -WorldSafeLocsEntry const *BattleGroundEY::GetClosestGraveYard(float x, float y, float z, uint32 team) +WorldSafeLocsEntry const *BattleGroundEY::GetClosestGraveYard(Player* player) { uint32 g_id = 0; - if(team == ALLIANCE) - g_id = EY_GRAVEYARD_MAIN_ALLIANCE; - else if(team == HORDE) - g_id = EY_GRAVEYARD_MAIN_HORDE; - else - return NULL; + switch(player->GetTeam()) + { + case ALLIANCE: g_id = EY_GRAVEYARD_MAIN_ALLIANCE; break; + case HORDE: g_id = EY_GRAVEYARD_MAIN_HORDE; break; + default: return NULL; + } float distance, nearestDistance; @@ -911,26 +871,31 @@ WorldSafeLocsEntry const *BattleGroundEY::GetClosestGraveYard(float x, float y, entry = sWorldSafeLocsStore.LookupEntry(g_id); nearestEntry = entry; - if(!entry) + if (!entry) { sLog.outError("BattleGroundEY: Not found the main team graveyard. Graveyard system isn't working!"); return NULL; } - distance = (entry->x - x)*(entry->x - x) + (entry->y - y)*(entry->y - y) + (entry->z - z)*(entry->z - z); + float plr_x = player->GetPositionX(); + float plr_y = player->GetPositionY(); + float plr_z = player->GetPositionZ(); + + + distance = (entry->x - plr_x)*(entry->x - plr_x) + (entry->y - plr_y)*(entry->y - plr_y) + (entry->z - plr_z)*(entry->z - plr_z); nearestDistance = distance; for(uint8 i = 0; i < EY_POINTS_MAX; ++i) { - if(m_PointOwnedByTeam[i]==team && m_PointState[i]==EY_POINT_UNDER_CONTROL) + if (m_PointOwnedByTeam[i]==player->GetTeam() && m_PointState[i]==EY_POINT_UNDER_CONTROL) { entry = sWorldSafeLocsStore.LookupEntry(m_CapturingPointTypes[i].GraveYardId); - if(!entry) + if (!entry) sLog.outError("BattleGroundEY: Not found graveyard: %u",m_CapturingPointTypes[i].GraveYardId); else { - distance = (entry->x - x)*(entry->x - x) + (entry->y - y)*(entry->y - y) + (entry->z - z)*(entry->z - z); - if(distance < nearestDistance) + distance = (entry->x - plr_x)*(entry->x - plr_x) + (entry->y - plr_y)*(entry->y - plr_y) + (entry->z - plr_z)*(entry->z - plr_z); + if (distance < nearestDistance) { nearestDistance = distance; nearestEntry = entry; diff --git a/src/game/BattleGroundEY.h b/src/game/BattleGroundEY.h index 30ff2b3e584..c4ffb57fba3 100644 --- a/src/game/BattleGroundEY.h +++ b/src/game/BattleGroundEY.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,9 +25,8 @@ class BattleGround; -#define EY_MAX_TEAM_SCORE 2000 -#define BG_EY_FLAG_RESPAWN_TIME 10000 //10 seconds -#define BG_EY_FPOINTS_TICK_TIME 2000 //2 seconds +#define BG_EY_FLAG_RESPAWN_TIME (10*IN_MILISECONDS) //10 seconds +#define BG_EY_FPOINTS_TICK_TIME (2*IN_MILISECONDS) //2 seconds enum BG_EY_WorldStates { @@ -73,11 +72,11 @@ enum BG_EY_ProgressBarConsts enum BG_EY_Sounds { //strange ids, but sure about them - BG_EY_SOUND_FLAG_PICKED_UP_ALLIANCE = 8212, - BG_EY_SOUND_FLAG_CAPTURED_HORDE = 8213, - BG_EY_SOUND_FLAG_PICKED_UP_HORDE = 8174, - BG_EY_SOUND_FLAG_CAPTURED_ALLIANCE = 8173, - BG_EY_SOUND_FLAG_RESET = 8192 + BG_EY_SOUND_FLAG_PICKED_UP_ALLIANCE = 8212, + BG_EY_SOUND_FLAG_CAPTURED_HORDE = 8213, + BG_EY_SOUND_FLAG_PICKED_UP_HORDE = 8174, + BG_EY_SOUND_FLAG_CAPTURED_ALLIANCE = 8173, + BG_EY_SOUND_FLAG_RESET = 8192 }; enum BG_EY_Spells @@ -88,18 +87,18 @@ enum BG_EY_Spells enum EYBattleGroundObjectEntry { - BG_OBJECT_A_DOOR_EY_ENTRY = 184719, //Alliance door - BG_OBJECT_H_DOOR_EY_ENTRY = 184720, //Horde door - BG_OBJECT_FLAG1_EY_ENTRY = 184493, //Netherstorm flag (generic) - BG_OBJECT_FLAG2_EY_ENTRY = 184141, //Netherstorm flag (flagstand) - BG_OBJECT_FLAG3_EY_ENTRY = 184142, //Netherstorm flag (flagdrop) - BG_OBJECT_A_BANNER_EY_ENTRY = 184381, //Visual Banner (Alliance) - BG_OBJECT_H_BANNER_EY_ENTRY = 184380, //Visual Banner (Horde) - BG_OBJECT_N_BANNER_EY_ENTRY = 184382, //Visual Banner (Neutral) - BG_OBJECT_BE_TOWER_CAP_EY_ENTRY = 184080, //BE Tower Cap Pt - BG_OBJECT_FR_TOWER_CAP_EY_ENTRY = 184081, //Fel Reaver Cap Pt - BG_OBJECT_HU_TOWER_CAP_EY_ENTRY = 184082, //Human Tower Cap Pt - BG_OBJECT_DR_TOWER_CAP_EY_ENTRY = 184083 //Draenei Tower Cap Pt + BG_OBJECT_A_DOOR_EY_ENTRY = 184719, //Alliance door + BG_OBJECT_H_DOOR_EY_ENTRY = 184720, //Horde door + BG_OBJECT_FLAG1_EY_ENTRY = 184493, //Netherstorm flag (generic) + BG_OBJECT_FLAG2_EY_ENTRY = 184141, //Netherstorm flag (flagstand) + BG_OBJECT_FLAG3_EY_ENTRY = 184142, //Netherstorm flag (flagdrop) + BG_OBJECT_A_BANNER_EY_ENTRY = 184381, //Visual Banner (Alliance) + BG_OBJECT_H_BANNER_EY_ENTRY = 184380, //Visual Banner (Horde) + BG_OBJECT_N_BANNER_EY_ENTRY = 184382, //Visual Banner (Neutral) + BG_OBJECT_BE_TOWER_CAP_EY_ENTRY = 184080, //BE Tower Cap Pt + BG_OBJECT_FR_TOWER_CAP_EY_ENTRY = 184081, //Fel Reaver Cap Pt + BG_OBJECT_HU_TOWER_CAP_EY_ENTRY = 184082, //Human Tower Cap Pt + BG_OBJECT_DR_TOWER_CAP_EY_ENTRY = 184083 //Draenei Tower Cap Pt }; enum EYBattleGroundPointsTrigger @@ -131,7 +130,7 @@ enum EYBattleGroundPoints DRAENEI_RUINS = 2, MAGE_TOWER = 3, - EY_PLAYERS_OUT_OF_POINTS = 4, + EY_PLAYERS_OUT_OF_POINTS = 4, EY_POINTS_MAX = 4 }; @@ -212,6 +211,15 @@ enum EYBattleGroundObjectTypes BG_EY_OBJECT_MAX = 59 }; +#define BG_EY_NotEYWeekendHonorTicks 330 +#define BG_EY_EYWeekendHonorTicks 200 + +enum BG_EY_Score +{ + BG_EY_WARNING_NEAR_VICTORY_SCORE = 1800, + BG_EY_MAX_TEAM_SCORE = 2000 +}; + enum BG_EY_FlagState { BG_EY_FLAG_STATE_ON_BASE = 0, @@ -300,10 +308,12 @@ class BattleGroundEY : public BattleGround public: BattleGroundEY(); ~BattleGroundEY(); - void Update(time_t diff); + void Update(uint32 diff); /* inherited from BattlegroundClass */ virtual void AddPlayer(Player *plr); + virtual void StartingEventCloseDoors(); + virtual void StartingEventOpenDoors(); /* BG Flags */ uint64 GetFlagPickerGUID() const { return m_FlagKeeper; } @@ -317,10 +327,11 @@ class BattleGroundEY : public BattleGround void HandleBuffUse(uint64 const& buff_guid); void HandleAreaTrigger(Player *Source, uint32 Trigger); void HandleKillPlayer(Player *player, Player *killer); - virtual WorldSafeLocsEntry const* GetClosestGraveYard(float x, float y, float z, uint32 team); + virtual WorldSafeLocsEntry const* GetClosestGraveYard(Player* player); virtual bool SetupBattleGround(); - virtual void ResetBGSubclass(); + virtual void Reset(); void UpdateTeamScore(uint32 Team); + void EndBattleGround(uint32 winner); void UpdatePlayerScore(Player *Source, uint32 type, uint32 value); virtual void FillInitialWorldStates(WorldPacket& data); void SetDroppedFlagGUID(uint64 guid) { m_DroppedFlagGUID = guid;} @@ -370,6 +381,7 @@ class BattleGroundEY : public BattleGround uint8 m_CurrentPointPlayersCount[2*EY_POINTS_MAX]; int32 m_PointAddingTimer; + uint32 m_HonorTics; }; #endif diff --git a/src/game/BattleGroundHandler.cpp b/src/game/BattleGroundHandler.cpp index b1522c939f7..6e94a8c8a9f 100644 --- a/src/game/BattleGroundHandler.cpp +++ b/src/game/BattleGroundHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,7 +25,6 @@ #include "Player.h" #include "ObjectMgr.h" #include "WorldSession.h" -#include "MapManager.h" #include "ObjectAccessor.h" #include "Object.h" #include "Chat.h" @@ -44,7 +43,7 @@ void WorldSession::HandleBattleGroundHelloOpcode( WorldPacket & recv_data ) sLog.outDebug( "WORLD: Recvd CMSG_BATTLEMASTER_HELLO Message from: " I64FMT, guid); Creature *unit = ObjectAccessor::GetCreature(*_player, guid); - if(!unit) + if (!unit) return; if(!unit->isBattleMaster()) // it's not battlemaster @@ -53,9 +52,9 @@ void WorldSession::HandleBattleGroundHelloOpcode( WorldPacket & recv_data ) // Stop the npc if moving unit->StopMoving(); - uint32 bgTypeId = objmgr.GetBattleMasterBG(unit->GetEntry()); + BattleGroundTypeId bgTypeId = sBattleGroundMgr.GetBattleMasterBG(unit->GetEntry()); - if(!_player->GetBGAccessByLevel(bgTypeId)) + if (!_player->GetBGAccessByLevel(bgTypeId)) { // temp, must be gossip message... SendNotification(LANG_YOUR_BG_LEVEL_REQ_ERROR); @@ -65,7 +64,7 @@ void WorldSession::HandleBattleGroundHelloOpcode( WorldPacket & recv_data ) SendBattlegGroundList(guid, bgTypeId); } -void WorldSession::SendBattlegGroundList( uint64 guid, uint32 bgTypeId ) +void WorldSession::SendBattlegGroundList( uint64 guid, BattleGroundTypeId bgTypeId ) { WorldPacket data; sBattleGroundMgr.BuildBattleGroundListPacket(&data, guid, _player, bgTypeId); @@ -77,57 +76,60 @@ void WorldSession::HandleBattleGroundJoinOpcode( WorldPacket & recv_data ) CHECK_PACKET_SIZE(recv_data, 8+4+4+1); uint64 guid; - uint32 bgTypeId; + uint32 bgTypeId_; uint32 instanceId; uint8 joinAsGroup; + bool isPremade = false; Group * grp; recv_data >> guid; // battlemaster guid - recv_data >> bgTypeId; // battleground type id (DBC id) + recv_data >> bgTypeId_; // battleground type id (DBC id) recv_data >> instanceId; // instance id, 0 if First Available selected recv_data >> joinAsGroup; // join as group - if(bgTypeId >= MAX_BATTLEGROUND_TYPES) + if (!sBattlemasterListStore.LookupEntry(bgTypeId_)) { - sLog.outError("Battleground: invalid bgtype received. possible cheater? player guid %u",_player->GetGUIDLow()); + sLog.outError("Battleground: invalid bgtype (%u) received. possible cheater? player guid %u",bgTypeId_,_player->GetGUIDLow()); return; } + BattleGroundTypeId bgTypeId = BattleGroundTypeId(bgTypeId_); + sLog.outDebug( "WORLD: Recvd CMSG_BATTLEMASTER_JOIN Message from: " I64FMT, guid); // can do this, since it's battleground, not arena - uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bgTypeId, 0); + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, 0); // ignore if player is already in BG - if(_player->InBattleGround()) + if (_player->InBattleGround()) return; Creature *unit = ObjectAccessor::GetCreature(*_player, guid); - if(!unit) + if (!unit) return; if(!unit->isBattleMaster()) // it's not battlemaster return; // get bg instance or bg template if instance not found - BattleGround * bg = 0; - if(instanceId) - BattleGround *bg = sBattleGroundMgr.GetBattleGround(instanceId); + BattleGround * bg = NULL; + if (instanceId) + BattleGround *bg = sBattleGroundMgr.GetBattleGroundThroughClientInstance(instanceId, bgTypeId, _player->GetBattleGroundQueueIdFromLevel(bgTypeId)); - if(!bg && !(bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId))) + if (!bg && !(bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId))) { sLog.outError("Battleground: no available bg / template found"); return; } // check queueing conditions - if(!joinAsGroup) + if (!joinAsGroup) { // check Deserter debuff - if( !_player->CanJoinToBattleground() ) + if (!_player->CanJoinToBattleground()) { WorldPacket data(SMSG_GROUP_JOINED_BATTLEGROUND, 4); - data << (uint32) 0xFFFFFFFE; + data << uint32(0xFFFFFFFE); _player->GetSession()->SendPacket(&data); return; } @@ -136,16 +138,17 @@ void WorldSession::HandleBattleGroundJoinOpcode( WorldPacket & recv_data ) //player is already in this queue return; // check if has free queue slots - if(!_player->HasFreeBattleGroundQueueId()) + if (!_player->HasFreeBattleGroundQueueId()) return; } else { grp = _player->GetGroup(); // no group found, error - if(!grp) + if (!grp) return; uint32 err = grp->CanJoinBattleGroundQueue(bgTypeId, bgQueueTypeId, 0, bg->GetMaxPlayersPerTeam(), false, 0); + isPremade = (grp->GetMembersCount() >= bg->GetMinPlayersPerTeam()); if (err != BG_JOIN_ERR_OK) { SendBattleGroundOrArenaJoinError(err); @@ -155,10 +158,11 @@ void WorldSession::HandleBattleGroundJoinOpcode( WorldPacket & recv_data ) // if we're here, then the conditions to join a bg are met. We can proceed in joining. // _player->GetGroup() was already checked, grp is already initialized - if(joinAsGroup /* && _player->GetGroup()*/) + GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddGroup(_player, bgTypeId, 0, false, isPremade, 0); + uint32 avgTime = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].GetAverageQueueWaitTime(ginfo, _player->GetBattleGroundQueueIdFromLevel(bgTypeId)); + if (joinAsGroup /* && _player->GetGroup()*/) { sLog.outDebug("Battleground: the following players are joining as group:"); - GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddGroup(_player, bgTypeId, 0, false, 0); for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) { Player *member = itr->getSource(); @@ -171,7 +175,7 @@ void WorldSession::HandleBattleGroundJoinOpcode( WorldPacket & recv_data ) WorldPacket data; // send status packet (in queue) - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, member->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, ginfo->ArenaType); member->GetSession()->SendPacket(&data); sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, bgTypeId); member->GetSession()->SendPacket(&data); @@ -179,7 +183,6 @@ void WorldSession::HandleBattleGroundJoinOpcode( WorldPacket & recv_data ) sLog.outDebug("Battleground: player joined queue for bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,member->GetGUIDLow(), member->GetName()); } sLog.outDebug("Battleground: group end"); - sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel()); } else { @@ -190,14 +193,15 @@ void WorldSession::HandleBattleGroundJoinOpcode( WorldPacket & recv_data ) WorldPacket data; // send status packet (in queue) - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, ginfo->ArenaType); SendPacket(&data); - GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddGroup(_player, bgTypeId, 0, false, 0); sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddPlayer(_player, ginfo); - sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel()); sLog.outDebug("Battleground: player joined queue for bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,_player->GetGUIDLow(), _player->GetName()); } + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel(bgTypeId)); + if (!ginfo->IsInvitedToBGInstanceGUID) + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AnnounceWorld(ginfo, _player->GetGUID(), true); } void WorldSession::HandleBattleGroundPlayerPositionsOpcode( WorldPacket & /*recv_data*/ ) @@ -209,40 +213,62 @@ void WorldSession::HandleBattleGroundPlayerPositionsOpcode( WorldPacket & /*recv if(!bg) // can't be received if player not in battleground return; - if(bg->GetTypeID() == BATTLEGROUND_WS) + switch( bg->GetTypeID() ) { - uint32 count1 = 0; - uint32 count2 = 0; + case BATTLEGROUND_WS: + { + uint32 count1 = 0; //always constant zero? + uint32 count2 = 0; //count of next fields - Player *ap = objmgr.GetPlayer(((BattleGroundWS*)bg)->GetAllianceFlagPickerGUID()); - if(ap) ++count2; + Player *ali_plr = objmgr.GetPlayer(((BattleGroundWS*)bg)->GetAllianceFlagPickerGUID()); + if (ali_plr) + ++count2; - Player *hp = objmgr.GetPlayer(((BattleGroundWS*)bg)->GetHordeFlagPickerGUID()); - if(hp) ++count2; + Player *horde_plr = objmgr.GetPlayer(((BattleGroundWS*)bg)->GetHordeFlagPickerGUID()); + if (horde_plr) + ++count2; - WorldPacket data(MSG_BATTLEGROUND_PLAYER_POSITIONS, (4+4+16*count1+16*count2)); - data << count1; // alliance flag holders count - /*for(uint8 i = 0; i < count1; i++) - { - data << uint64(0); // guid - data << (float)0; // x - data << (float)0; // y - }*/ - data << count2; // horde flag holders count - if(ap) - { - data << (uint64)ap->GetGUID(); - data << (float)ap->GetPositionX(); - data << (float)ap->GetPositionY(); - } - if(hp) - { - data << (uint64)hp->GetGUID(); - data << (float)hp->GetPositionX(); - data << (float)hp->GetPositionY(); - } + WorldPacket data(MSG_BATTLEGROUND_PLAYER_POSITIONS, (4+4+16*count1+16*count2)); + data << count1; // alliance flag holders count - obsolete, now always 0 + /*for(uint8 i = 0; i < count1; i++) + { + data << uint64(0); // guid + data << (float)0; // x + data << (float)0; // y + }*/ + data << count2; // horde flag holders count - obsolete, now count of next fields + if (ali_plr) + { + data << (uint64)ali_plr->GetGUID(); + data << (float)ali_plr->GetPositionX(); + data << (float)ali_plr->GetPositionY(); + } + if (horde_plr) + { + data << (uint64)horde_plr->GetGUID(); + data << (float)horde_plr->GetPositionX(); + data << (float)horde_plr->GetPositionY(); + } - SendPacket(&data); + SendPacket(&data); + } + break; + case BATTLEGROUND_EY: + //TODO : fix me! + break; + case BATTLEGROUND_AB: + case BATTLEGROUND_AV: + { + //for other BG types - send default + WorldPacket data(MSG_BATTLEGROUND_PLAYER_POSITIONS, (4+4)); + data << uint32(0); + data << uint32(0); + SendPacket(&data); + } + break; + default: + //maybe it is sent also in arena - do nothing + break; } } @@ -251,7 +277,7 @@ void WorldSession::HandleBattleGroundPVPlogdataOpcode( WorldPacket & /*recv_data sLog.outDebug( "WORLD: Recvd MSG_PVP_LOG_DATA Message"); BattleGround *bg = _player->GetBattleGround(); - if(!bg) + if (!bg) return; WorldPacket data; @@ -270,19 +296,15 @@ void WorldSession::HandleBattleGroundListOpcode( WorldPacket &recv_data ) uint32 bgTypeId; recv_data >> bgTypeId; // id from DBC - if(bgTypeId >= MAX_BATTLEGROUND_TYPES) + BattlemasterListEntry const* bl = sBattlemasterListStore.LookupEntry(bgTypeId); + if (!bl) { sLog.outError("Battleground: invalid bgtype received."); return; } - BattlemasterListEntry const* bl = sBattlemasterListStore.LookupEntry(bgTypeId); - - if(!bl) - return; - WorldPacket data; - sBattleGroundMgr.BuildBattleGroundListPacket(&data, _player->GetGUID(), _player, bgTypeId); + sBattleGroundMgr.BuildBattleGroundListPacket(&data, _player->GetGUID(), _player, BattleGroundTypeId(bgTypeId)); SendPacket( &data ); } @@ -295,45 +317,40 @@ void WorldSession::HandleBattleGroundPlayerPortOpcode( WorldPacket &recv_data ) uint8 type; // arenatype if arena uint8 unk2; // unk, can be 0x0 (may be if was invited?) and 0x1 uint32 instanceId; - uint32 bgTypeId; // type id from dbc + uint32 bgTypeId_; // type id from dbc uint16 unk; // 0x1F90 constant? uint8 action; // enter battle 0x1, leave queue 0x0 - recv_data >> type >> unk2 >> bgTypeId >> unk >> action; + recv_data >> type >> unk2 >> bgTypeId_ >> unk >> action; - if(bgTypeId >= MAX_BATTLEGROUND_TYPES) + if (!sBattlemasterListStore.LookupEntry(bgTypeId_)) { - sLog.outError("Battleground: invalid bgtype received."); + sLog.outError("Battleground: invalid bgtype (%u) received.", bgTypeId_); // update battleground slots for the player to fix his UI and sent data. // this is a HACK, I don't know why the client starts sending invalid packets in the first place. // it usually happens with extremely high latency (if debugging / stepping in the code for example) - if(_player->InBattleGroundQueue()) + if (_player->InBattleGroundQueue()) { // update all queues, send invitation info if player is invited, queue info if queued for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) { - uint32 queue_id = _player->GetBattleGroundQueueId(i); - if(!queue_id) + BattleGroundQueueTypeId bgQueueTypeId = _player->GetBattleGroundQueueTypeId(i); + if (!bgQueueTypeId) continue; - BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus = sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID()); - // if the player is not in queue, contine - if(itrPlayerStatus == sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end()) - continue; - - // no group information, this should never happen - if(!itrPlayerStatus->second.GroupInfo) + BattleGroundTypeId bgTypeId = BattleGroundMgr::BGTemplateId(bgQueueTypeId); + BattleGroundQueue::QueuedPlayersMap& qpMap = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers; + BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus = qpMap.find(_player->GetGUID()); + // if the player is not in queue, continue or no group information - this should never happen + if (itrPlayerStatus == qpMap.end() || !itrPlayerStatus->second.GroupInfo) continue; BattleGround * bg = NULL; - // get possibly needed data from groupinfo - bgTypeId = itrPlayerStatus->second.GroupInfo->BgTypeId; uint8 arenatype = itrPlayerStatus->second.GroupInfo->ArenaType; uint8 israted = itrPlayerStatus->second.GroupInfo->IsRated; uint8 status = 0; - - if(!itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID) + if (!itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID) { // not invited to bg, get template bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); @@ -342,130 +359,124 @@ void WorldSession::HandleBattleGroundPlayerPortOpcode( WorldPacket &recv_data ) else { // get the bg we're invited to - BattleGround * bg = sBattleGroundMgr.GetBattleGround(itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID); + BattleGround * bg = sBattleGroundMgr.GetBattleGround(itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID, bgTypeId); status = STATUS_WAIT_JOIN; } - // if bg not found, then continue - if(!bg) - continue; - - // don't invite if already in the instance - if(_player->InBattleGround() && _player->GetBattleGround() && _player->GetBattleGround()->GetInstanceID() == bg->GetInstanceID()) + // if bg not found, then continue, don't invite if already in the instance + if (!bg || (_player->InBattleGround() && _player->GetBattleGround() && _player->GetBattleGround()->GetInstanceID() == bg->GetInstanceID())) continue; // re - invite player with proper data WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, itrPlayerStatus->second.GroupInfo->Team?itrPlayerStatus->second.GroupInfo->Team:_player->GetTeam(), i, status, INVITE_ACCEPT_WAIT_TIME, 0, arenatype, israted); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, i, status, INVITE_ACCEPT_WAIT_TIME, 0, arenatype); SendPacket(&data); } } return; } - uint32 bgQueueTypeId = 0; - // get the bg what we were invited to - BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus; - bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bgTypeId,type); - itrPlayerStatus = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID()); - - if(itrPlayerStatus == sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end()) + //get GroupQueueInfo from BattleGroundQueue + BattleGroundTypeId bgTypeId = BattleGroundTypeId(bgTypeId_); + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, type); + BattleGroundQueue::QueuedPlayersMap& qpMap = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers; + BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus = qpMap.find(_player->GetGUID()); + if (itrPlayerStatus == qpMap.end()) { sLog.outError("Battleground: itrplayerstatus not found."); return; } - instanceId = itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID; - // if action == 1, then instanceId is _required_ - if(!instanceId && action == 1) + instanceId = itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID; + // if action == 1, then instanceId is required + if (!instanceId && action == 1) { sLog.outError("Battleground: instance not found."); return; } - BattleGround *bg = sBattleGroundMgr.GetBattleGround(instanceId); + BattleGround *bg = sBattleGroundMgr.GetBattleGround(instanceId, bgTypeId); // bg template might and must be used in case of leaving queue, when instance is not created yet - if(!bg && action == 0) + if (!bg && action == 0) bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); - - if(!bg) + if (!bg) { - sLog.outError("Battleground: bg not found."); + sLog.outError("Battleground: bg_template not found for type id %u.", bgTypeId); return; } - bgTypeId = bg->GetTypeID(); - - if(_player->InBattleGroundQueue()) + if (_player->InBattleGroundQueue()) { - uint32 queueSlot = 0; - uint32 team = 0; - uint32 arenatype = 0; - uint32 israted = 0; - uint32 rating = 0; - uint32 opponentsRating = 0; - // get the team info from the queue - BattleGroundQueue::QueuedPlayersMap::iterator pitr = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID()); - if(pitr !=sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end() - && pitr->second.GroupInfo ) - { - team = pitr->second.GroupInfo->Team; - arenatype = pitr->second.GroupInfo->ArenaType; - israted = pitr->second.GroupInfo->IsRated; - rating = pitr->second.GroupInfo->ArenaTeamRating; - opponentsRating = pitr->second.GroupInfo->OpponentsTeamRating; - } - else + //we must use temporary variables, because GroupQueueInfo pointer can be deleted in BattleGroundQueue::RemovePlayer() function! + uint32 team = itrPlayerStatus->second.GroupInfo->Team; + uint32 arenaType = itrPlayerStatus->second.GroupInfo->ArenaType; + uint32 isRated = itrPlayerStatus->second.GroupInfo->IsRated; + uint32 rating = itrPlayerStatus->second.GroupInfo->ArenaTeamRating; + uint32 opponentsRating = itrPlayerStatus->second.GroupInfo->OpponentsTeamRating; + + //some checks if player isn't cheating - it is not exactly cheating, but we cannot allow it + if (action == 1 && arenaType == 0) { - sLog.outError("Battleground: Invalid player queue info!"); - return; + //if player is trying to enter battleground (not arena!) and he has deserter debuff, we must just remove him from queue + if (!_player->CanJoinToBattleground()) + { + //send bg command result to show nice message + WorldPacket data2(SMSG_GROUP_JOINED_BATTLEGROUND, 4); + data2 << uint32(0xFFFFFFFE); + _player->GetSession()->SendPacket(&data2); + action = 0; + sLog.outDebug("Battleground: player %s (%u) has a deserter debuff, do not port him to battleground!", _player->GetName(), _player->GetGUIDLow()); + } + //if player don't match battleground max level, then do not allow him to enter! (this might happen when player leveled up during his waiting in queue + if (_player->getLevel() > bg->GetMaxLevel()) + { + sLog.outError("Battleground: Player %s (%u) has level higher than maxlevel of battleground! Do not port him to battleground!", _player->GetName(), _player->GetGUIDLow()); + action = 0; + } } + uint32 queueSlot = _player->GetBattleGroundQueueIndex(bgQueueTypeId); WorldPacket data; - switch(action) + switch( action ) { - case 1: // port to battleground - if(!_player->IsInvitedForBattleGroundQueueType(bgQueueTypeId)) - return; // cheating? + case 1: // port to battleground + if (!_player->IsInvitedForBattleGroundQueueType(bgQueueTypeId)) + return; // cheating? // resurrect the player - if(!_player->isAlive()) + if (!_player->isAlive()) { _player->ResurrectPlayer(1.0f); _player->SpawnCorpseBones(); } // stop taxi flight at port - if(_player->isInFlight()) + if (_player->isInFlight()) { _player->GetMotionMaster()->MovementExpired(); _player->m_taxi.ClearTaxiDestinations(); } - _player->RemoveFromGroup(); - queueSlot = _player->GetBattleGroundQueueIndex(bgQueueTypeId); - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime()); + + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime(), bg->GetArenaType()); _player->GetSession()->SendPacket(&data); // remove battleground queue status from BGmgr sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].RemovePlayer(_player->GetGUID(), false); // this is still needed here if battleground "jumping" shouldn't add deserter debuff - // also this required to prevent stuck at old battleground after SetBattleGroundId set to new - if( BattleGround *currentBg = _player->GetBattleGround() ) + // also this is required to prevent stuck at old battleground after SetBattleGroundId set to new + if (BattleGround *currentBg = _player->GetBattleGround()) currentBg->RemovePlayerAtLeave(_player->GetGUID(), false, true); // set the destination instance id - _player->SetBattleGroundId(bg->GetInstanceID()); + _player->SetBattleGroundId(bg->GetInstanceID(), bgTypeId); // set the destination team _player->SetBGTeam(team); // bg->HandleBeforeTeleportToBattleGround(_player); - sBattleGroundMgr.SendToBattleGround(_player, instanceId); + sBattleGroundMgr.SendToBattleGround(_player, instanceId, bgTypeId); // add only in HandleMoveWorldPortAck() // bg->AddPlayer(_player,team); - sLog.outDebug("Battleground: player %s (%u) joined battle for bg %u, bgtype %u, queue type %u.",_player->GetName(),_player->GetGUIDLow(),bg->GetInstanceID(),bg->GetTypeID(),bgQueueTypeId); + sLog.outDebug("Battleground: player %s (%u) joined battle for bg %u, bgtype %u, queue type %u.", _player->GetName(), _player->GetGUIDLow(), bg->GetInstanceID(), bg->GetTypeID(), bgQueueTypeId); break; - case 0: // leave queue - queueSlot = _player->GetBattleGroundQueueIndex(bgQueueTypeId); - /* - if player leaves rated arena match before match start, it is counted as he played but he lost - */ - if (israted) + case 0: // leave queue + // if player leaves rated arena match before match start, it is counted as he played but he lost + if (isRated) { ArenaTeam * at = objmgr.GetArenaTeamById(team); if (at) @@ -475,13 +486,14 @@ void WorldSession::HandleBattleGroundPlayerPortOpcode( WorldPacket &recv_data ) at->SaveToDB(); } } - _player->RemoveBattleGroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to queue->removeplayer, it causes bugs - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_NONE, 0, 0); + _player->RemoveBattleGroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to queue->removeplayer, it causes bugs + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0); sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].RemovePlayer(_player->GetGUID(), true); - // player left queue, we should update it, maybe now his group fits in - sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId,_player->GetBattleGroundQueueIdFromLevel(),arenatype,israted,rating); + // player left queue, we should update it - do not update Arena Queue + if (!arenaType) + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel(bgTypeId), arenaType, isRated, rating); SendPacket(&data); - sLog.outDebug("Battleground: player %s (%u) left queue for bgtype %u, queue type %u.",_player->GetName(),_player->GetGUIDLow(),bg->GetTypeID(),bgQueueTypeId); + sLog.outDebug("Battleground: player %s (%u) left queue for bgtype %u, queue type %u.", _player->GetName(), _player->GetGUIDLow(), bg->GetTypeID(), bgQueueTypeId); break; default: sLog.outError("Battleground port: unknown action %u", action); @@ -506,9 +518,9 @@ void WorldSession::HandleBattleGroundLeaveOpcode( WorldPacket & /*recv_data*/ ) // return; // not allow leave battleground in combat - if(_player->isInCombat()) - if(BattleGround* bg = _player->GetBattleGround()) - if(bg->GetStatus() != STATUS_WAIT_LEAVE) + if (_player->isInCombat()) + if (BattleGround* bg = _player->GetBattleGround()) + if (bg->GetStatus() != STATUS_WAIT_LEAVE) return; _player->LeaveBattleground(); @@ -520,81 +532,56 @@ void WorldSession::HandleBattlefieldStatusOpcode( WorldPacket & /*recv_data*/ ) sLog.outDebug( "WORLD: Battleground status" ); WorldPacket data; - - // TODO: we must put player back to battleground in case disconnect (< 5 minutes offline time) or teleport player on login(!) from battleground map to entry point - if(_player->InBattleGround()) + // we must update all queues here + BattleGround *bg = NULL; + for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) { - BattleGround *bg = _player->GetBattleGround(); - if(bg) + BattleGroundQueueTypeId bgQueueTypeId = _player->GetBattleGroundQueueTypeId(i); + if (!bgQueueTypeId) + continue; + BattleGroundTypeId bgTypeId = BattleGroundMgr::BGTemplateId(bgQueueTypeId); + uint8 arenaType = BattleGroundMgr::BGArenaType(bgQueueTypeId); + if (bgTypeId == _player->GetBattleGroundTypeId()) { - uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); - uint32 queueSlot = _player->GetBattleGroundQueueIndex(bgQueueTypeId); - if((bg->GetStatus() <= STATUS_IN_PROGRESS)) + bg = _player->GetBattleGround(); + //i cannot check any variable from player class because player class doesn't know if player is in 2v2 / 3v3 or 5v5 arena + //so i must use bg pointer to get that information + if (bg && bg->GetArenaType() == arenaType) { - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime()); + // this line is checked, i only don't know if GetStartTime is changing itself after bg end! + // send status in BattleGround + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, i, STATUS_IN_PROGRESS, bg->GetEndTime(), bg->GetStartTime(), arenaType); SendPacket(&data); - } - for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) - { - uint32 queue_id = _player->GetBattleGroundQueueId(i); // battlegroundqueueid stores the type id, not the instance id, so this is definitely wrong - uint8 arenatype = sBattleGroundMgr.BGArenaType(queue_id); - uint8 isRated = 0; - if (i == queueSlot || !queue_id) // we need to get the instance ids - continue; - BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus = sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID()); - if(itrPlayerStatus == sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end()) - continue; - if(itrPlayerStatus->second.GroupInfo) - { - arenatype = itrPlayerStatus->second.GroupInfo->ArenaType; - isRated = itrPlayerStatus->second.GroupInfo->IsRated; - } - BattleGround *bg2 = sBattleGroundMgr.GetBattleGroundTemplate(sBattleGroundMgr.BGTemplateId(queue_id)); // try this - if(bg2) - { - //in this call is small bug, this call should be filled by player's waiting time in queue - //this call nulls all timers for client : - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg2, _player->GetTeam(), i, STATUS_WAIT_QUEUE, 0, 0,arenatype,isRated); - SendPacket(&data); - } + continue; } } - } - else - { - // we should update all queues? .. i'm not sure if this code is correct - for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) + //we are sending update to player about queue - he can be invited there! + //get GroupQueueInfo for queue status + BattleGroundQueue::QueuedPlayersMap& qpMap = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers; + BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus = qpMap.find(_player->GetGUID()); + if (itrPlayerStatus == qpMap.end()) + continue; + if (itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID) { - uint32 queue_id = _player->GetBattleGroundQueueId(i); - if(!queue_id) + bg = sBattleGroundMgr.GetBattleGround(itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID, bgTypeId); + if (!bg) continue; - uint32 bgTypeId = sBattleGroundMgr.BGTemplateId(queue_id); - uint8 arenatype = sBattleGroundMgr.BGArenaType(queue_id); - uint8 isRated = 0; - BattleGround *bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); - BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus = sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID()); - if(itrPlayerStatus == sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end()) - continue; - if(itrPlayerStatus->second.GroupInfo) - { - arenatype = itrPlayerStatus->second.GroupInfo->ArenaType; - isRated = itrPlayerStatus->second.GroupInfo->IsRated; - } - if(bg && queue_id) - { - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), i, STATUS_WAIT_QUEUE, 0, 0, arenatype, isRated); - SendPacket(&data); - } + uint32 remainingTime = getMSTimeDiff(getMSTime(), itrPlayerStatus->second.GroupInfo->RemoveInviteTime); + // send status invited to BattleGround + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, i, STATUS_WAIT_JOIN, remainingTime, 0, arenaType); + SendPacket(&data); } - } -/* else // not sure if it needed... - { - for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) + else { - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, NULL, _player->GetTeam(),i , STATUS_NONE, 0, 0); + BattleGround *bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); + if (!bg) + continue; + uint32 avgTime = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].GetAverageQueueWaitTime(itrPlayerStatus->second.GroupInfo, _player->GetBattleGroundQueueIdFromLevel(bgTypeId)); + // send status in BattleGround Queue + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, i, STATUS_WAIT_QUEUE, avgTime, getMSTimeDiff(itrPlayerStatus->second.GroupInfo->JoinTime, getMSTime()), arenaType); SendPacket(&data); } - }*/ + } } void WorldSession::HandleAreaSpiritHealerQueryOpcode( WorldPacket & recv_data ) @@ -604,14 +591,14 @@ void WorldSession::HandleAreaSpiritHealerQueryOpcode( WorldPacket & recv_data ) CHECK_PACKET_SIZE(recv_data, 8); BattleGround *bg = _player->GetBattleGround(); - if(!bg) + if (!bg) return; uint64 guid; recv_data >> guid; Creature *unit = ObjectAccessor::GetCreature(*_player, guid); - if(!unit) + if (!unit) return; if(!unit->isSpiritService()) // it's not spirit service @@ -627,14 +614,14 @@ void WorldSession::HandleAreaSpiritHealerQueueOpcode( WorldPacket & recv_data ) CHECK_PACKET_SIZE(recv_data, 8); BattleGround *bg = _player->GetBattleGround(); - if(!bg) + if (!bg) return; uint64 guid; recv_data >> guid; Creature *unit = ObjectAccessor::GetCreature(*_player, guid); - if(!unit) + if (!unit) return; if(!unit->isSpiritService()) // it's not spirit service @@ -651,19 +638,19 @@ void WorldSession::HandleBattleGroundArenaJoin( WorldPacket & recv_data ) recv_data.hexlike(); // ignore if we already in BG or BG queue - if(_player->InBattleGround()) + if (_player->InBattleGround()) return; uint64 guid; // arena Battlemaster guid - uint8 type; // 2v2, 3v3 or 5v5 + uint8 arenaslot; // 2v2, 3v3 or 5v5 uint8 asGroup; // asGroup uint8 isRated; // isRated Group * grp; - recv_data >> guid >> type >> asGroup >> isRated; + recv_data >> guid >> arenaslot >> asGroup >> isRated; Creature *unit = ObjectAccessor::GetCreature(*_player, guid); - if(!unit) + if (!unit) return; if(!unit->isBattleMaster()) // it's not battle master @@ -672,7 +659,7 @@ void WorldSession::HandleBattleGroundArenaJoin( WorldPacket & recv_data ) uint8 arenatype = 0; uint32 arenaRating = 0; - switch(type) + switch(arenaslot) { case 0: arenatype = ARENA_TYPE_2v2; @@ -684,39 +671,39 @@ void WorldSession::HandleBattleGroundArenaJoin( WorldPacket & recv_data ) arenatype = ARENA_TYPE_5v5; break; default: - sLog.outError("Unknown arena type %u at HandleBattleGroundArenaJoin()", type); + sLog.outError("Unknown arena slot %u at HandleBattleGroundArenaJoin()", arenaslot); return; } //check existance BattleGround* bg = NULL; - if( !(bg = sBattleGroundMgr.GetBattleGroundTemplate(BATTLEGROUND_AA)) ) + if (!(bg = sBattleGroundMgr.GetBattleGroundTemplate(BATTLEGROUND_AA))) { sLog.outError("Battleground: template bg (all arenas) not found"); return; } - uint8 bgTypeId = bg->GetTypeID(); - uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bgTypeId, arenatype); + BattleGroundTypeId bgTypeId = bg->GetTypeID(); + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, arenatype); // check queueing conditions - if(!asGroup) + if (!asGroup) { // check if already in queue if (_player->GetBattleGroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES) //player is already in this queue return; // check if has free queue slots - if(!_player->HasFreeBattleGroundQueueId()) + if (!_player->HasFreeBattleGroundQueueId()) return; } else { grp = _player->GetGroup(); // no group found, error - if(!grp) + if (!grp) return; - uint32 err = grp->CanJoinBattleGroundQueue(bgTypeId, bgQueueTypeId, arenatype, arenatype, (bool)isRated, type); + uint32 err = grp->CanJoinBattleGroundQueue(bgTypeId, bgQueueTypeId, arenatype, arenatype, (bool)isRated, arenaslot); if (err != BG_JOIN_ERR_OK) { SendBattleGroundOrArenaJoinError(err); @@ -726,12 +713,12 @@ void WorldSession::HandleBattleGroundArenaJoin( WorldPacket & recv_data ) uint32 ateamId = 0; - if(isRated) + if (isRated) { - ateamId = _player->GetArenaTeamId(type); + ateamId = _player->GetArenaTeamId(arenaslot); // check real arenateam existence only here (if it was moved to group->CanJoin .. () then we would ahve to get it twice) ArenaTeam * at = objmgr.GetArenaTeamById(ateamId); - if(!at) + if (!at) { _player->GetSession()->SendNotInArenaTeamPacket(arenatype); return; @@ -746,23 +733,24 @@ void WorldSession::HandleBattleGroundArenaJoin( WorldPacket & recv_data ) Player *member = itr->getSource(); // calc avg personal rating - avg_pers_rating += member->GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (type*6) + 5); + avg_pers_rating += member->GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (arenaslot*6) + 5); } - if( arenatype ) + if (arenatype) avg_pers_rating /= arenatype; // if avg personal rating is more than 150 points below the teams rating, the team will be queued against an opponent matching or similar to the average personal rating - if(avg_pers_rating + 150 < arenaRating) + if (avg_pers_rating + 150 < arenaRating) arenaRating = avg_pers_rating; } - if(asGroup) + GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddGroup(_player, bgTypeId, arenatype, isRated, false, arenaRating, ateamId); + uint32 avgTime = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].GetAverageQueueWaitTime(ginfo, _player->GetBattleGroundQueueIdFromLevel(bgTypeId)); + if (asGroup) { - GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddGroup(_player, bgTypeId, arenatype, isRated, arenaRating, ateamId); sLog.outDebug("Battleground: arena join as group start"); - if(isRated) - sLog.outDebug("Battleground: arena team id %u, leader %s queued with rating %u for type %u",_player->GetArenaTeamId(type),_player->GetName(),arenaRating,arenatype); + if (isRated) + sLog.outDebug("Battleground: arena team id %u, leader %s queued with rating %u for type %u",_player->GetArenaTeamId(arenaslot),_player->GetName(),arenaRating,arenatype); for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) { Player *member = itr->getSource(); @@ -775,7 +763,7 @@ void WorldSession::HandleBattleGroundArenaJoin( WorldPacket & recv_data ) WorldPacket data; // send status packet (in queue) - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, member->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0, arenatype, isRated); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, arenatype); member->GetSession()->SendPacket(&data); sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, bgTypeId); member->GetSession()->SendPacket(&data); @@ -783,7 +771,8 @@ void WorldSession::HandleBattleGroundArenaJoin( WorldPacket & recv_data ) sLog.outDebug("Battleground: player joined queue for arena as group bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,member->GetGUIDLow(), member->GetName()); } sLog.outDebug("Battleground: arena join as group end"); - sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel(), arenatype, isRated, arenaRating); + if (isRated) + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AnnounceWorld(ginfo, _player->GetGUID(), true); } else { @@ -794,13 +783,12 @@ void WorldSession::HandleBattleGroundArenaJoin( WorldPacket & recv_data ) WorldPacket data; // send status packet (in queue) - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0, arenatype, isRated); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, arenatype); SendPacket(&data); - GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddGroup(_player, bgTypeId, arenatype, isRated, arenaRating); sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddPlayer(_player, ginfo); - sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel(), arenatype, isRated, arenaRating); sLog.outDebug("Battleground: player joined queue for arena, skirmish, bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,_player->GetGUIDLow(), _player->GetName()); } + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel(bgTypeId), arenatype, isRated, arenaRating); } void WorldSession::HandleBattleGroundReportAFK( WorldPacket & recv_data ) @@ -811,7 +799,7 @@ void WorldSession::HandleBattleGroundReportAFK( WorldPacket & recv_data ) recv_data >> playerGuid; Player *reportedPlayer = objmgr.GetPlayer(playerGuid); - if(!reportedPlayer) + if (!reportedPlayer) { sLog.outDebug("WorldSession::HandleBattleGroundReportAFK: player not found"); return; diff --git a/src/game/BattleGroundMgr.cpp b/src/game/BattleGroundMgr.cpp index aecb706f73d..6b7b3c99fe0 100644 --- a/src/game/BattleGroundMgr.cpp +++ b/src/game/BattleGroundMgr.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,7 @@ */ #include "Common.h" +#include "SharedDefines.h" #include "Player.h" #include "BattleGroundMgr.h" #include "BattleGroundAV.h" @@ -29,16 +30,21 @@ #include "BattleGroundBE.h" #include "BattleGroundAA.h" #include "BattleGroundRL.h" -#include "SharedDefines.h" -#include "Policies/SingletonImp.h" +#include "BattleGroundSA.h" +#include "BattleGroundDS.h" +#include "BattleGroundRV.h" #include "MapManager.h" #include "Map.h" #include "MapInstanced.h" #include "ObjectMgr.h" #include "ProgressBar.h" -#include "World.h" #include "Chat.h" #include "ArenaTeam.h" +#include "World.h" +#include "WorldPacket.h" +#include "ProgressBar.h" + +#include "Policies/SingletonImp.h" INSTANTIATE_SINGLETON_1( BattleGroundMgr ); @@ -48,97 +54,103 @@ INSTANTIATE_SINGLETON_1( BattleGroundMgr ); BattleGroundQueue::BattleGroundQueue() { - //queues are empty, we don't have to call clear() -/* for (int i = 0; i < MAX_BATTLEGROUND_QUEUES; i++) + for(uint32 i = 0; i < BG_TEAMS_COUNT; i++) { - //m_QueuedPlayers[i].Horde = 0; - //m_QueuedPlayers[i].Alliance = 0; - //m_QueuedPlayers[i].AverageTime = 0; - }*/ -} - -BattleGroundQueue::~BattleGroundQueue() -{ - for (int i = 0; i < MAX_BATTLEGROUND_QUEUES; i++) - { - m_QueuedPlayers[i].clear(); - for(QueuedGroupsList::iterator itr = m_QueuedGroups[i].begin(); itr!= m_QueuedGroups[i].end(); ++itr) + for(uint32 j = 0; j < MAX_BATTLEGROUND_QUEUES; j++) { - delete (*itr); + m_SumOfWaitTimes[i][j] = 0; + m_WaitTimeLastPlayer[i][j] = 0; + for(uint32 k = 0; k < COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME; k++) + m_WaitTimes[i][j][k] = 0; } - m_QueuedGroups[i].clear(); } } -// initialize eligible groups from the given source matching the given specifications -void BattleGroundQueue::EligibleGroups::Init(BattleGroundQueue::QueuedGroupsList *source, uint32 BgTypeId, uint32 side, uint32 MaxPlayers, uint8 ArenaType, bool IsRated, uint32 MinRating, uint32 MaxRating, uint32 DisregardTime, uint32 excludeTeam) +BattleGroundQueue::~BattleGroundQueue() { - // clear from prev initialization - clear(); - BattleGroundQueue::QueuedGroupsList::iterator itr, next; - // iterate through the source - for(itr = source->begin(); itr!= source->end(); itr = next) + m_QueuedPlayers.clear(); + for (int i = 0; i < MAX_BATTLEGROUND_QUEUES; i++) { - next = itr; - ++next; - if( (*itr)->BgTypeId == BgTypeId && // bg type must match - (*itr)->ArenaType == ArenaType && // arena type must match - (*itr)->IsRated == IsRated && // israted must match - (*itr)->IsInvitedToBGInstanceGUID == 0 && // leave out already invited groups - (*itr)->Team == side && // match side - (*itr)->Players.size() <= MaxPlayers && // the group must fit in the bg - ( !excludeTeam || (*itr)->ArenaTeamId != excludeTeam ) && // if excludeTeam is specified, leave out those arena team ids - ( !IsRated || (*itr)->Players.size() == MaxPlayers ) && // if rated, then pass only if the player count is exact NEEDS TESTING! (but now this should never happen) - ( !DisregardTime || (*itr)->JoinTime <= DisregardTime // pass if disregard time is greater than join time - || (*itr)->ArenaTeamRating == 0 // pass if no rating info - || ( (*itr)->ArenaTeamRating >= MinRating // pass if matches the rating range - && (*itr)->ArenaTeamRating <= MaxRating ) ) ) + for(uint32 j = 0; j < BG_QUEUE_GROUP_TYPES_COUNT; j++) { - // the group matches the conditions - // the source is ordered by join time in decreasing order - // so here we push_front for increasing order - push_front((*itr)); + for(GroupsQueueType::iterator itr = m_QueuedGroups[i][j].begin(); itr!= m_QueuedGroups[i][j].end(); ++itr) + delete (*itr); + m_QueuedGroups[i][j].clear(); } } } +/*********************************************************/ +/*** BATTLEGROUND QUEUE SELECTION POOLS ***/ +/*********************************************************/ + // selection pool initialization, used to clean up from prev selection -void BattleGroundQueue::SelectionPool::Init(EligibleGroups * curr) +void BattleGroundQueue::SelectionPool::Init() { - m_CurrEligGroups = curr; SelectedGroups.clear(); PlayerCount = 0; } // remove group info from selection pool -void BattleGroundQueue::SelectionPool::RemoveGroup(GroupQueueInfo *ginfo) +// returns true when we need to try to add new group to selection pool +// returns false when selection pool is ok or when we kicked smaller group than we need to kick +// sometimes it can be called on empty selection pool +bool BattleGroundQueue::SelectionPool::KickGroup(uint32 size) { - // find what to remove - for(std::list<GroupQueueInfo *>::iterator itr = SelectedGroups.begin(); itr != SelectedGroups.end(); ++itr) + //find maxgroup or LAST group with size == size and kick it + bool found = false; + GroupsQueueType::iterator groupToKick = SelectedGroups.begin(); + for (GroupsQueueType::iterator itr = groupToKick; itr != SelectedGroups.end(); ++itr) { - if((*itr)==ginfo) + if (abs((int32)((*itr)->Players.size() - size)) <= 1) { - SelectedGroups.erase(itr); - // decrease selected players count - PlayerCount -= ginfo->Players.size(); - return; + groupToKick = itr; + found = true; } + else if (!found && (*itr)->Players.size() >= (*groupToKick)->Players.size()) + groupToKick = itr; + } + //if pool is empty, do nothing + if (GetPlayerCount()) + { + //update player count + GroupQueueInfo* ginfo = (*groupToKick); + SelectedGroups.erase(groupToKick); + PlayerCount -= ginfo->Players.size(); + //return false if we kicked smaller group or there are enough players in selection pool + if (ginfo->Players.size() <= size + 1) + return false; } + return true; } -// add group to selection +// add group to selection pool // used when building selection pools -void BattleGroundQueue::SelectionPool::AddGroup(GroupQueueInfo * ginfo) +// returns true if we can invite more players, or when we added group to selection pool +// returns false when selection pool is full +bool BattleGroundQueue::SelectionPool::AddGroup(GroupQueueInfo *ginfo, uint32 desiredCount) { - SelectedGroups.push_back(ginfo); - // increase selected players count - PlayerCount+=ginfo->Players.size(); + //if group is larger than desired count - don't allow to add it to pool + if (!ginfo->IsInvitedToBGInstanceGUID && desiredCount >= PlayerCount + ginfo->Players.size()) + { + SelectedGroups.push_back(ginfo); + // increase selected players count + PlayerCount += ginfo->Players.size(); + return true; + } + if (PlayerCount < desiredCount) + return true; + return false; } +/*********************************************************/ +/*** BATTLEGROUND QUEUES ***/ +/*********************************************************/ + // add group to bg queue with the given leader and bg specifications -GroupQueueInfo * BattleGroundQueue::AddGroup(Player *leader, uint32 BgTypeId, uint8 ArenaType, bool isRated, uint32 arenaRating, uint32 arenateamid) +GroupQueueInfo * BattleGroundQueue::AddGroup(Player *leader, BattleGroundTypeId BgTypeId, uint8 ArenaType, bool isRated, bool isPremade, uint32 arenaRating, uint32 arenateamid) { - uint32 queue_id = leader->GetBattleGroundQueueIdFromLevel(); + BGQueueIdBasedOnLevel queue_id = leader->GetBattleGroundQueueIdFromLevel(BgTypeId); // create new ginfo // cannot use the method like in addplayer, because that could modify an in-queue group's stats @@ -148,28 +160,34 @@ GroupQueueInfo * BattleGroundQueue::AddGroup(Player *leader, uint32 BgTypeId, ui ginfo->ArenaType = ArenaType; ginfo->ArenaTeamId = arenateamid; ginfo->IsRated = isRated; - ginfo->IsInvitedToBGInstanceGUID = 0; // maybe this should be modifiable by function arguments to enable selection of running instances? + ginfo->IsInvitedToBGInstanceGUID = 0; ginfo->JoinTime = getMSTime(); + ginfo->RemoveInviteTime = 0; ginfo->Team = leader->GetTeam(); ginfo->ArenaTeamRating = arenaRating; - ginfo->OpponentsTeamRating = 0; //initialize it to 0 + ginfo->OpponentsTeamRating = 0; ginfo->Players.clear(); - m_QueuedGroups[queue_id].push_back(ginfo); + //compute index (if group is premade or joined a rated match) to queues + uint32 index = 0; + if (!isRated && !isPremade) + index += BG_TEAMS_COUNT; + if (ginfo->Team == HORDE) + index++; + sLog.outDebug("Adding Group to BattleGroundQueue bgTypeId : %u, queue_id : %u, index : %u", BgTypeId, queue_id, index); + + m_QueuedGroups[queue_id][index].push_back(ginfo); // return ginfo, because it is needed to add players to this group info return ginfo; } +//add player to playermap void BattleGroundQueue::AddPlayer(Player *plr, GroupQueueInfo *ginfo) { - uint32 queue_id = plr->GetBattleGroundQueueIdFromLevel(); - //if player isn't in queue, he is added, if already is, then values are overwritten, no memory leak - PlayerQueueInfo& info = m_QueuedPlayers[queue_id][plr->GetGUID()]; - info.InviteTime = 0; - info.LastInviteTime = 0; + PlayerQueueInfo& info = m_QueuedPlayers[plr->GetGUID()]; info.LastOnlineTime = getMSTime(); info.GroupInfo = ginfo; @@ -177,113 +195,228 @@ void BattleGroundQueue::AddPlayer(Player *plr, GroupQueueInfo *ginfo) ginfo->Players[plr->GetGUID()] = &info; } -void BattleGroundQueue::RemovePlayer(uint64 guid, bool decreaseInvitedCount) +void BattleGroundQueue::PlayerInvitedToBGUpdateAverageWaitTime(GroupQueueInfo* ginfo, BGQueueIdBasedOnLevel queue_id) { - Player *plr = objmgr.GetPlayer(guid); + uint32 timeInQueue = getMSTimeDiff(ginfo->JoinTime, getMSTime()); + uint8 team_index = BG_TEAM_ALLIANCE; //default set to BG_TEAM_ALLIANCE - or non rated arenas! + if (!ginfo->ArenaType) + { + if (ginfo->Team == HORDE) + team_index = BG_TEAM_HORDE; + } + else + { + if (ginfo->IsRated) + team_index = BG_TEAM_HORDE; //for rated arenas use BG_TEAM_HORDE + } - uint32 queue_id = 0; - QueuedPlayersMap::iterator itr; - GroupQueueInfo * group; - QueuedGroupsList::iterator group_itr; - bool IsSet = false; - if(plr) + //store pointer to arrayindex of player that was added first + uint32* lastPlayerAddedPointer = &(m_WaitTimeLastPlayer[team_index][queue_id]); + //remove his time from sum + m_SumOfWaitTimes[team_index][queue_id] -= m_WaitTimes[team_index][queue_id][(*lastPlayerAddedPointer)]; + //set average time to new + m_WaitTimes[team_index][queue_id][(*lastPlayerAddedPointer)] = timeInQueue; + //add new time to sum + m_SumOfWaitTimes[team_index][queue_id] += timeInQueue; + //set index of last player added to next one + (*lastPlayerAddedPointer)++; + (*lastPlayerAddedPointer) %= COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME; +} + +uint32 BattleGroundQueue::GetAverageQueueWaitTime(GroupQueueInfo* ginfo, BGQueueIdBasedOnLevel queue_id) +{ + uint8 team_index = BG_TEAM_ALLIANCE; //default set to BG_TEAM_ALLIANCE - or non rated arenas! + if (!ginfo->ArenaType) { - queue_id = plr->GetBattleGroundQueueIdFromLevel(); + if (ginfo->Team == HORDE) + team_index = BG_TEAM_HORDE; + } + else + { + if (ginfo->IsRated) + team_index = BG_TEAM_HORDE; //for rated arenas use BG_TEAM_HORDE + } + //check if there is enought values(we always add values > 0) + if (m_WaitTimes[team_index][queue_id][COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME - 1] ) + return (m_SumOfWaitTimes[team_index][queue_id] / COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME); + else + //if there aren't enough values return 0 - not available + return 0; +} - itr = m_QueuedPlayers[queue_id].find(guid); - if(itr != m_QueuedPlayers[queue_id].end()) - IsSet = true; +//remove player from queue and from group info, if group info is empty then remove it too +void BattleGroundQueue::RemovePlayer(const uint64& guid, bool decreaseInvitedCount) +{ + //Player *plr = objmgr.GetPlayer(guid); + + int32 queue_id = -1; // signed for proper for-loop finish + QueuedPlayersMap::iterator itr; + + //remove player from map, if he's there + itr = m_QueuedPlayers.find(guid); + if (itr == m_QueuedPlayers.end()) + { + sLog.outError("BattleGroundQueue: couldn't find player to remove GUID: %u", GUID_LOPART(guid)); + return; } - if(!IsSet) + GroupQueueInfo* group = itr->second.GroupInfo; + GroupsQueueType::iterator group_itr, group_itr_tmp; + // mostly people with the highest levels are in battlegrounds, thats why + // we count from MAX_BATTLEGROUND_QUEUES - 1 to 0 + // variable index removes useless searching in other team's queue + uint32 index = (group->Team == HORDE) ? BG_TEAM_HORDE : BG_TEAM_ALLIANCE; + + for (int32 queue_id_tmp = MAX_BATTLEGROUND_QUEUES - 1; queue_id_tmp >= 0 && queue_id == -1; --queue_id_tmp) { - // either player is offline, or he levelled up to another queue category - // sLog.outError("Battleground: removing offline player from BG queue - this might not happen, but it should not cause crash"); - for (uint32 i = 0; i < MAX_BATTLEGROUND_QUEUES; i++) + //we must check premade and normal team's queue - because when players from premade are joining bg, + //they leave groupinfo so we can't use its players size to find out index + for (uint32 j = index; j < BG_QUEUE_GROUP_TYPES_COUNT; j += BG_QUEUE_NORMAL_ALLIANCE) { - itr = m_QueuedPlayers[i].find(guid); - if(itr != m_QueuedPlayers[i].end()) + for(group_itr_tmp = m_QueuedGroups[queue_id_tmp][j].begin(); group_itr_tmp != m_QueuedGroups[queue_id_tmp][j].end(); ++group_itr_tmp) { - queue_id = i; - IsSet = true; - break; + if ((*group_itr_tmp) == group) + { + queue_id = queue_id_tmp; + group_itr = group_itr_tmp; + //we must store index to be able to erase iterator + index = j; + break; + } } } } - - // couldn't find the player in bg queue, return - if(!IsSet) + //player can't be in queue without group, but just in case + if (queue_id == -1) { - sLog.outError("Battleground: couldn't find player to remove."); + sLog.outError("BattleGroundQueue: ERROR Cannot find groupinfo for player GUID: %u", GUID_LOPART(guid)); return; } + sLog.outDebug("BattleGroundQueue: Removing player GUID %u, from queue_id %u", GUID_LOPART(guid), (uint32)queue_id); + + // ALL variables are correctly set + // We can ignore leveling up in queue - it should not cause crash + // remove player from group + // if only one player there, remove group - group = itr->second.GroupInfo; + // remove player queue info from group queue info + std::map<uint64, PlayerQueueInfo*>::iterator pitr = group->Players.find(guid); + if (pitr != group->Players.end()) + group->Players.erase(pitr); - for(group_itr=m_QueuedGroups[queue_id].begin(); group_itr != m_QueuedGroups[queue_id].end(); ++group_itr) + // if invited to bg, and should decrease invited count, then do it + if (decreaseInvitedCount && group->IsInvitedToBGInstanceGUID) { - if(group == (GroupQueueInfo*)(*group_itr)) - break; + BattleGround* bg = sBattleGroundMgr.GetBattleGround(group->IsInvitedToBGInstanceGUID, group->BgTypeId); + if (bg) + bg->DecreaseInvitedCount(group->Team); } - // variables are set (what about leveling up when in queue????) - // remove player from group - // if only player there, remove group + // remove player queue info + m_QueuedPlayers.erase(itr); - // remove player queue info from group queue info - std::map<uint64, PlayerQueueInfo*>::iterator pitr = group->Players.find(guid); + //if we left BG queue(not porting) OR if arena team left queue for rated match + if ((decreaseInvitedCount && !group->ArenaType) || (group->ArenaType && group->IsRated && group->Players.empty())) + AnnounceWorld(group, guid, false); - if(pitr != group->Players.end()) - group->Players.erase(pitr); + //if player leaves queue and he is invited to rated arena match, then he have to loose + if (group->IsInvitedToBGInstanceGUID && group->IsRated && decreaseInvitedCount) + { + ArenaTeam * at = objmgr.GetArenaTeamById(group->ArenaTeamId); + if (at) + { + sLog.outDebug("UPDATING memberLost's personal arena rating for %u by opponents rating: %u", GUID_LOPART(guid), group->OpponentsTeamRating); + Player *plr = objmgr.GetPlayer(guid); + if (plr) + at->MemberLost(plr, group->OpponentsTeamRating); + else + at->OfflineMemberLost(guid, group->OpponentsTeamRating); + at->SaveToDB(); + } + } - // check for iterator correctness - if (group_itr != m_QueuedGroups[queue_id].end() && itr != m_QueuedPlayers[queue_id].end()) + // remove group queue info if needed + if (group->Players.empty()) + { + m_QueuedGroups[queue_id][index].erase(group_itr); + delete group; + } + // if group wasn't empty, so it wasn't deleted, and player have left a rated + // queue -> everyone from the group should leave too + // don't remove recursively if already invited to bg! + else if (!group->IsInvitedToBGInstanceGUID && group->IsRated) { - // used when player left the queue, NOT used when porting to bg - if (decreaseInvitedCount) + // remove next player, this is recursive + // first send removal information + if (Player *plr2 = objmgr.GetPlayer(group->Players.begin()->first)) { - // if invited to bg, and should decrease invited count, then do it - if(group->IsInvitedToBGInstanceGUID) - { - BattleGround* bg = sBattleGroundMgr.GetBattleGround(group->IsInvitedToBGInstanceGUID); - if (bg) - bg->DecreaseInvitedCount(group->Team); - if (bg && !bg->GetPlayersSize() && !bg->GetInvitedCount(ALLIANCE) && !bg->GetInvitedCount(HORDE)) - { - // no more players on battleground, set delete it - bg->SetDeleteThis(); - } - } - // update the join queue, maybe now the player's group fits in a queue! - // not yet implemented (should store bgTypeId in group queue info?) + BattleGround * bg = sBattleGroundMgr.GetBattleGroundTemplate(group->BgTypeId); + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(group->BgTypeId, group->ArenaType); + uint32 queueSlot = plr2->GetBattleGroundQueueIndex(bgQueueTypeId); + plr2->RemoveBattleGroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to + // queue->removeplayer, it causes bugs + WorldPacket data; + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0); + plr2->GetSession()->SendPacket(&data); } - // remove player queue info - m_QueuedPlayers[queue_id].erase(itr); - // remove group queue info if needed - if(group->Players.empty()) + // then actually delete, this may delete the group as well! + RemovePlayer(group->Players.begin()->first, decreaseInvitedCount); + } +} + +//Announce world message +void BattleGroundQueue::AnnounceWorld(GroupQueueInfo *ginfo, const uint64& playerGUID, bool isAddedToQueue) +{ + if(ginfo->ArenaType) //if Arena + { + if (sWorld.getConfig(CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE) && ginfo->IsRated) { - m_QueuedGroups[queue_id].erase(group_itr); - delete group; + BattleGround* bg = sBattleGroundMgr.GetBattleGroundTemplate(ginfo->BgTypeId); + if (!bg) + return; + + char const* bgName = bg->GetName(); + if (isAddedToQueue) + sWorld.SendWorldText(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_JOIN, bgName, ginfo->ArenaType, ginfo->ArenaType, ginfo->ArenaTeamRating); + else + sWorld.SendWorldText(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_EXIT, bgName, ginfo->ArenaType, ginfo->ArenaType, ginfo->ArenaTeamRating); } - // NEEDS TESTING! - // group wasn't empty, so it wasn't deleted, and player have left a rated queue -> everyone from the group should leave too - // don't remove recursively if already invited to bg! - else if(!group->IsInvitedToBGInstanceGUID && decreaseInvitedCount && group->IsRated) + } + else //if BG + { + if (sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE)) { - // remove next player, this is recursive - // first send removal information - if(Player *plr2 = objmgr.GetPlayer(group->Players.begin()->first)) + Player *plr = objmgr.GetPlayer(playerGUID); + BattleGround* bg = sBattleGroundMgr.GetBattleGroundTemplate(ginfo->BgTypeId); + if (!bg || !plr) + return; + + BGQueueIdBasedOnLevel queue_id = plr->GetBattleGroundQueueIdFromLevel(bg->GetTypeID()); + char const* bgName = bg->GetName(); + uint32 MinPlayers = bg->GetMinPlayersPerTeam(); + uint32 qHorde = 0; + uint32 qAlliance = 0; + uint32 q_min_level = (queue_id + 1) * 10; + GroupsQueueType::const_iterator itr; + for(itr = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE].begin(); itr != m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE].end(); ++itr) + if (!(*itr)->IsInvitedToBGInstanceGUID) + qAlliance += (*itr)->Players.size(); + for(itr = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_HORDE].begin(); itr != m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_HORDE].end(); ++itr) + if (!(*itr)->IsInvitedToBGInstanceGUID) + qHorde += (*itr)->Players.size(); + + // Show queue status to player only (when joining queue) + if (sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY)) + { + ChatHandler(plr).PSendSysMessage(LANG_BG_QUEUE_ANNOUNCE_SELF, + bgName, q_min_level, q_min_level + 10, qAlliance, MinPlayers, qHorde, MinPlayers); + } + // System message + else { - BattleGround * bg = sBattleGroundMgr.GetBattleGroundTemplate(group->BgTypeId); - uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(group->BgTypeId,group->ArenaType); - uint32 queueSlot = plr2->GetBattleGroundQueueIndex(bgQueueTypeId); - plr2->RemoveBattleGroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to queue->removeplayer, it causes bugs - WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, plr2->GetTeam(), queueSlot, STATUS_NONE, 0, 0); - plr2->GetSession()->SendPacket(&data); + sWorld.SendWorldText(LANG_BG_QUEUE_ANNOUNCE_WORLD, + bgName, q_min_level, q_min_level + 10, qAlliance, MinPlayers, qHorde, MinPlayers); } - // then actually delete, this may delete the group as well! - RemovePlayer(group->Players.begin()->first,decreaseInvitedCount); } } } @@ -291,30 +424,48 @@ void BattleGroundQueue::RemovePlayer(uint64 guid, bool decreaseInvitedCount) bool BattleGroundQueue::InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * bg, uint32 side) { // set side if needed - if(side) + if (side) ginfo->Team = side; - if(!ginfo->IsInvitedToBGInstanceGUID) + if (!ginfo->IsInvitedToBGInstanceGUID) { // not yet invited // set invitation ginfo->IsInvitedToBGInstanceGUID = bg->GetInstanceID(); - uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); + BattleGroundTypeId bgTypeId = bg->GetTypeID(); + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, bg->GetArenaType()); + BGQueueIdBasedOnLevel queue_id = bg->GetQueueId(); + + // set ArenaTeamId for rated matches + if (bg->isArena() && bg->isRated()) + bg->SetArenaTeamIdForTeam(ginfo->Team, ginfo->ArenaTeamId); + + ginfo->RemoveInviteTime = getMSTime() + INVITE_ACCEPT_WAIT_TIME; + // loop through the players for(std::map<uint64,PlayerQueueInfo*>::iterator itr = ginfo->Players.begin(); itr != ginfo->Players.end(); ++itr) { - // set status - itr->second->InviteTime = getMSTime(); - itr->second->LastInviteTime = getMSTime(); - // get the player Player* plr = objmgr.GetPlayer(itr->first); - // if offline, skip him - if(!plr) + // if offline, skip him, this should not happen - player is removed from queue when he logs out + if (!plr) continue; // invite the player - sBattleGroundMgr.InvitePlayer(plr, bg->GetInstanceID(),ginfo->Team); + PlayerInvitedToBGUpdateAverageWaitTime(ginfo, queue_id); + //sBattleGroundMgr.InvitePlayer(plr, bg, ginfo->Team); + + // set invited player counters + bg->IncreaseInvitedCount(ginfo->Team); + + plr->SetInviteForBattleGroundQueueType(bgQueueTypeId, ginfo->IsInvitedToBGInstanceGUID); + + // create remind invite events + BGQueueInviteEvent* inviteEvent = new BGQueueInviteEvent(plr->GetGUID(), ginfo->IsInvitedToBGInstanceGUID, bgTypeId, ginfo->RemoveInviteTime); + plr->m_Events.AddEvent(inviteEvent, plr->m_Events.CalculateTime(INVITATION_REMIND_TIME)); + // create automatic remove events + BGQueueRemoveEvent* removeEvent = new BGQueueRemoveEvent(plr->GetGUID(), ginfo->IsInvitedToBGInstanceGUID, bgTypeId, bgQueueTypeId, ginfo->RemoveInviteTime); + plr->m_Events.AddEvent(removeEvent, plr->m_Events.CalculateTime(INVITE_ACCEPT_WAIT_TIME)); WorldPacket data; @@ -323,7 +474,7 @@ bool BattleGroundQueue::InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * b sLog.outDebug("Battleground: invited plr %s (%u) to BG instance %u queueindex %u bgtype %u, I can't help it if they don't press the enter battle button.",plr->GetName(),plr->GetGUIDLow(),bg->GetInstanceID(),queueSlot,bg->GetTypeID()); // send status packet - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, side?side:plr->GetTeam(), queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME, 0); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME, 0, ginfo->ArenaType); plr->GetSession()->SendPacket(&data); } return true; @@ -332,190 +483,294 @@ bool BattleGroundQueue::InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * b return false; } -// used to recursively select groups from eligible groups -bool BattleGroundQueue::SelectionPool::Build(uint32 MinPlayers, uint32 MaxPlayers, EligibleGroups::iterator startitr) +/* +This function is inviting players to already running battlegrounds +Invitation type is based on config file +large groups are disadvantageous, because they will be kicked first if invitation type = 1 +*/ +void BattleGroundQueue::FillPlayersToBG(BattleGround* bg, BGQueueIdBasedOnLevel queue_id) { - // start from the specified start iterator - for(EligibleGroups::iterator itr1 = startitr; itr1 != m_CurrEligGroups->end(); ++itr1) + uint32 hordeFree = bg->GetFreeSlotsForTeam(HORDE); + uint32 aliFree = bg->GetFreeSlotsForTeam(ALLIANCE); + + //iterator for iterating through bg queue + GroupsQueueType::const_iterator Ali_itr = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE].begin(); + //count of groups in queue - used to stop cycles + uint32 aliCount = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE].size(); + //index to queue which group is current + uint32 aliIndex = 0; + for (; aliIndex < aliCount && m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*Ali_itr), aliFree); aliIndex++) + ++Ali_itr; + //the same thing for horde + GroupsQueueType::const_iterator Horde_itr = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_HORDE].begin(); + uint32 hordeCount = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_HORDE].size(); + uint32 hordeIndex = 0; + for (; hordeIndex < hordeCount && m_SelectionPools[BG_TEAM_HORDE].AddGroup((*Horde_itr), hordeFree); hordeIndex++) + ++Horde_itr; + + //if ofc like BG queue invitation is set in config, then we are happy + if (sWorld.getConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) == 0) + return; + + /* + if we reached this code, then we have to solve NP - complete problem called Subset sum problem + So one solution is to check all possible invitation subgroups, or we can use these conditions: + 1. Last time when BattleGroundQueue::Update was executed we invited all possible players - so there is only small possibility + that we will invite now whole queue, because only 1 change has been made to queues from the last BattleGroundQueue::Update call + 2. Other thing we should consider is group order in queue + */ + + // At first we need to compare free space in bg and our selection pool + int32 diffAli = aliFree - m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount(); + int32 diffHorde = hordeFree - m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount(); + while( abs(diffAli - diffHorde) > 1 && (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() > 0 || m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() > 0) ) { - // if it fits in, select it - if(GetPlayerCount() + (*itr1)->Players.size() <= MaxPlayers) + //each cycle execution we need to kick at least 1 group + if (diffAli < diffHorde) { - EligibleGroups::iterator next = itr1; - ++next; - AddGroup((*itr1)); - if(GetPlayerCount() >= MinPlayers) + //kick alliance group, add to pool new group if needed + if (m_SelectionPools[BG_TEAM_ALLIANCE].KickGroup(diffHorde - diffAli)) { - // enough players are selected - return true; + for (; aliIndex < aliCount && m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*Ali_itr), (aliFree >= diffHorde) ? aliFree - diffHorde : 0); aliIndex++) + ++Ali_itr; + } + //if ali selection is already empty, then kick horde group, but if there are less horde than ali in bg - break; + if (!m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()) + { + if (aliFree <= diffHorde + 1) + break; + m_SelectionPools[BG_TEAM_HORDE].KickGroup(diffHorde - diffAli); } - // try building from the rest of the elig. groups - // if that succeeds, return true - if(Build(MinPlayers,MaxPlayers,next)) - return true; - // the rest didn't succeed, so this group cannot be included - RemoveGroup((*itr1)); } + else + { + //kick horde group, add to pool new group if needed + if (m_SelectionPools[BG_TEAM_HORDE].KickGroup(diffAli - diffHorde)) + { + for (; hordeIndex < hordeCount && m_SelectionPools[BG_TEAM_HORDE].AddGroup((*Horde_itr), (hordeFree >= diffAli) ? hordeFree - diffAli : 0); hordeIndex++) + ++Horde_itr; + } + if (!m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()) + { + if (hordeFree <= diffAli + 1) + break; + m_SelectionPools[BG_TEAM_ALLIANCE].KickGroup(diffAli - diffHorde); + } + } + //count diffs after small update + diffAli = aliFree - m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount(); + diffHorde = hordeFree - m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount(); } - // build didn't succeed - return false; } -// this function is responsible for the selection of queued groups when trying to create new battlegrounds -bool BattleGroundQueue::BuildSelectionPool(uint32 bgTypeId, uint32 queue_id, uint32 MinPlayers, uint32 MaxPlayers, SelectionPoolBuildMode mode, uint8 ArenaType, bool isRated, uint32 MinRating, uint32 MaxRating, uint32 DisregardTime, uint32 excludeTeam) +// this method checks if premade versus premade battleground is possible +// then after 30 mins (default) in queue it moves premade group to normal queue +// it tries to invite as much players as it can - to MaxPlayersPerTeam, because premade groups have more than MinPlayersPerTeam players +bool BattleGroundQueue::CheckPremadeMatch(BGQueueIdBasedOnLevel queue_id, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam) { - uint32 side; - switch(mode) - { - case NORMAL_ALLIANCE: - case ONESIDE_ALLIANCE_TEAM1: - case ONESIDE_ALLIANCE_TEAM2: - side = ALLIANCE; - break; - case NORMAL_HORDE: - case ONESIDE_HORDE_TEAM1: - case ONESIDE_HORDE_TEAM2: - side = HORDE; - break; - default: - //unknown mode, return false - sLog.outDebug("Battleground: unknown selection pool build mode, returning..."); - return false; - break; - } - - // initiate the groups eligible to create the bg - m_EligibleGroups.Init(&(m_QueuedGroups[queue_id]), bgTypeId, side, MaxPlayers, ArenaType, isRated, MinRating, MaxRating, DisregardTime, excludeTeam); - // init the selected groups (clear) - // and set m_CurrEligGroups pointer - // we set it this way to only have one EligibleGroups object to save some memory - m_SelectionPools[mode].Init(&m_EligibleGroups); - // build succeeded - if(m_SelectionPools[mode].Build(MinPlayers,MaxPlayers,m_EligibleGroups.begin())) - { - // the selection pool is set, return - sLog.outDebug("Battleground-debug: pool build succeeded, return true"); - sLog.outDebug("Battleground-debug: Player size for mode %u is %u", mode, m_SelectionPools[mode].GetPlayerCount()); - for(std::list<GroupQueueInfo* >::iterator itr = m_SelectionPools[mode].SelectedGroups.begin(); itr != m_SelectionPools[mode].SelectedGroups.end(); ++itr) + //check match + if (!m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].empty() && !m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].empty()) + { + //start premade match + //if groups aren't invited + GroupsQueueType::const_iterator ali_group, horde_group; + for( ali_group = m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].begin(); ali_group != m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].end(); ++ali_group) + if (!(*ali_group)->IsInvitedToBGInstanceGUID) + break; + for( horde_group = m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].begin(); horde_group != m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].end(); ++horde_group) + if (!(*horde_group)->IsInvitedToBGInstanceGUID) + break; + + if (ali_group != m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].end() && horde_group != m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].end()) { - sLog.outDebug("Battleground-debug: queued group in selection with %u players",(*itr)->Players.size()); - for(std::map<uint64, PlayerQueueInfo * >::iterator itr2 = (*itr)->Players.begin(); itr2 != (*itr)->Players.end(); ++itr2) - sLog.outDebug("Battleground-debug: player in above group GUID %u", (uint32)(itr2->first)); + m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*ali_group), MaxPlayersPerTeam); + m_SelectionPools[BG_TEAM_HORDE].AddGroup((*horde_group), MaxPlayersPerTeam); + //add groups/players from normal queue to size of bigger group + uint32 maxPlayers = std::max(m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount(), m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()); + GroupsQueueType::const_iterator itr; + for(uint32 i = 0; i < BG_TEAMS_COUNT; i++) + { + for(itr = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + i].begin(); itr != m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + i].end(); ++itr) + { + //if itr can join BG and player count is less that maxPlayers, then add group to selectionpool + if (!(*itr)->IsInvitedToBGInstanceGUID && !m_SelectionPools[i].AddGroup((*itr), maxPlayers)) + break; + } + } + //premade selection pools are set + return true; } - return true; } - // failed to build a selection pool matching the given values + // now check if we can move group from Premade queue to normal queue (timer has expired) or group size lowered!! + // this could be 2 cycles but i'm checking only first team in queue - it can cause problem - + // if first is invited to BG and seconds timer expired, but we can ignore it, because players have only 80 seconds to click to enter bg + // and when they click or after 80 seconds the queue info is removed from queue + uint32 time_before = getMSTime() - sWorld.getConfig(CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH); + for(uint32 i = 0; i < BG_TEAMS_COUNT; i++) + { + if (!m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE + i].empty()) + { + GroupsQueueType::iterator itr = m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE + i].begin(); + if (!(*itr)->IsInvitedToBGInstanceGUID && ((*itr)->JoinTime < time_before || (*itr)->Players.size() < MinPlayersPerTeam)) + { + //we must insert group to normal queue and erase pointer from premade queue + m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + i].push_front((*itr)); + m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE + i].erase(itr); + } + } + } + //selection pools are not set return false; } -// used to remove the Enter Battle window if the battle has already, but someone still has it -// (this can happen in arenas mainly, since the preparation is shorter than the timer for the bgqueueremove event -void BattleGroundQueue::BGEndedRemoveInvites(BattleGround *bg) +// this method tries to create battleground or arena with MinPlayersPerTeam against MinPlayersPerTeam +bool BattleGroundQueue::CheckNormalMatch(BattleGround* bg_template, BGQueueIdBasedOnLevel queue_id, uint32 minPlayers, uint32 maxPlayers) { - uint32 queue_id = bg->GetQueueType(); - uint32 bgInstanceId = bg->GetInstanceID(); - uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); - QueuedGroupsList::iterator itr, next; - for(itr = m_QueuedGroups[queue_id].begin(); itr != m_QueuedGroups[queue_id].end(); itr = next) - { - // must do this way, because the groupinfo will be deleted when all playerinfos are removed - GroupQueueInfo * ginfo = (*itr); - next = itr; - ++next; - // if group was invited to this bg instance, then remove all references - if(ginfo->IsInvitedToBGInstanceGUID == bgInstanceId) + GroupsQueueType::const_iterator itr_team[BG_TEAMS_COUNT]; + for(uint32 i = 0; i < BG_TEAMS_COUNT; i++) + { + itr_team[i] = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + i].begin(); + for(; itr_team[i] != m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + i].end(); ++(itr_team[i])) { - // after removing this much playerinfos, the ginfo will be deleted, so we'll use a for loop - uint32 to_remove = ginfo->Players.size(); - uint32 team = ginfo->Team; - for(int i = 0; i < to_remove; ++i) + if (!(*(itr_team[i]))->IsInvitedToBGInstanceGUID) { - // always remove the first one in the group - std::map<uint64, PlayerQueueInfo * >::iterator itr2 = ginfo->Players.begin(); - if(itr2 == ginfo->Players.end()) - { - sLog.outError("Empty Players in ginfo, this should never happen!"); - return; - } + m_SelectionPools[i].AddGroup(*(itr_team[i]), maxPlayers); + if (m_SelectionPools[i].GetPlayerCount() >= minPlayers) + break; + } + } + } + //try to invite same number of players - this cycle may cause longer wait time even if there are enough players in queue, but we want ballanced bg + uint32 j = BG_TEAM_ALLIANCE; + if (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() < m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()) + j = BG_TEAM_HORDE; + if( sWorld.getConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) != 0 + && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() >= minPlayers && m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() >= minPlayers ) + { + //we will try to invite more groups to team with less players indexed by j + ++(itr_team[j]); //this will not cause a crash, because for cycle above reached break; + for(; itr_team[j] != m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + j].end(); ++(itr_team[j])) + { + if (!(*(itr_team[j]))->IsInvitedToBGInstanceGUID) + if (!m_SelectionPools[j].AddGroup(*(itr_team[j]), m_SelectionPools[(j + 1) % BG_TEAMS_COUNT].GetPlayerCount())) + break; + } + // do not allow to start bg with more than 2 players more on 1 faction + if (abs((int32)(m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() - m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount())) > 2) + return false; + } + //allow 1v0 if debug bg + if (sBattleGroundMgr.isTesting() && bg_template->isBattleGround() && (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() || m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount())) + return true; + //return true if there are enough players in selection pools - enable to work .debug bg command correctly + return m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() >= minPlayers && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() >= minPlayers; +} - // get the player - Player * plr = objmgr.GetPlayer(itr2->first); - if(!plr) - { - sLog.outError("Player offline when trying to remove from GroupQueueInfo, this should never happen."); - continue; - } +// this method will check if we can invite players to same faction skirmish match +bool BattleGroundQueue::CheckSkirmishForSameFaction(BGQueueIdBasedOnLevel queue_id, uint32 minPlayersPerTeam) +{ + if (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() < minPlayersPerTeam && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() < minPlayersPerTeam) + return false; + uint32 teamIndex = BG_TEAM_ALLIANCE; + uint32 otherTeam = BG_TEAM_HORDE; + uint32 otherTeamId = HORDE; + if (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() == minPlayersPerTeam ) + { + teamIndex = BG_TEAM_HORDE; + otherTeam = BG_TEAM_ALLIANCE; + otherTeamId = ALLIANCE; + } + //clear other team's selection + m_SelectionPools[otherTeam].Init(); + //store last ginfo pointer + GroupQueueInfo* ginfo = m_SelectionPools[teamIndex].SelectedGroups.back(); + //set itr_team to group that was added to selection pool latest + GroupsQueueType::iterator itr_team = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].begin(); + for(; itr_team != m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].end(); ++itr_team) + if (ginfo == *itr_team) + break; + if (itr_team == m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].end()) + return false; + GroupsQueueType::iterator itr_team2 = itr_team; + ++itr_team2; + //invite players to other selection pool + for(; itr_team2 != m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].end(); ++itr_team2) + { + //if selection pool is full then break; + if (!(*itr_team2)->IsInvitedToBGInstanceGUID && !m_SelectionPools[otherTeam].AddGroup(*itr_team2, minPlayersPerTeam)) + break; + } + if (m_SelectionPools[otherTeam].GetPlayerCount() != minPlayersPerTeam) + return false; - // get the queueslot - uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); - if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue - { - plr->RemoveBattleGroundQueueId(bgQueueTypeId); - // remove player from queue, this might delete the ginfo as well! don't use that pointer after this! - RemovePlayer(itr2->first, true); - // this is probably unneeded, since this player was already invited -> does not fit when initing eligible groups - // but updating the queue can't hurt - Update(bgQueueTypeId, bg->GetQueueType()); - // send info to client - WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, team, queueSlot, STATUS_NONE, 0, 0); - plr->GetSession()->SendPacket(&data); - } + //here we have correct 2 selections and we need to change one teams team and move selection pool teams to other team's queue + for(GroupsQueueType::iterator itr = m_SelectionPools[otherTeam].SelectedGroups.begin(); itr != m_SelectionPools[otherTeam].SelectedGroups.end(); ++itr) + { + //set correct team + (*itr)->Team = otherTeamId; + //add team to other queue + m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + otherTeam].push_front(*itr); + //remove team from old queue + GroupsQueueType::iterator itr2 = itr_team; + ++itr2; + for(; itr2 != m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].end(); ++itr2) + { + if (*itr2 == *itr) + { + m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].erase(itr2); + break; } } } + return true; } /* this method is called when group is inserted, or player / group is removed from BG Queue - there is only one player's status changed, so we don't use while(true) cycles to invite whole queue it must be called after fully adding the members of a group to ensure group joining -should be called after removeplayer functions in some cases +should be called from BattleGround::RemovePlayer function in some cases */ -void BattleGroundQueue::Update(uint32 bgTypeId, uint32 queue_id, uint8 arenatype, bool isRated, uint32 arenaRating) +void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLevel queue_id, uint8 arenaType, bool isRated, uint32 arenaRating) { - if (queue_id >= MAX_BATTLEGROUND_QUEUES) - { - //this is error, that caused crashes (not in , but now it shouldn't) - sLog.outError("BattleGroundQueue::Update() called for non existing queue type - this can cause crash, pls report problem, if this is the last line of error log before crash"); - return; - } - - //if no players in queue ... do nothing - if (m_QueuedGroups[queue_id].empty()) + //if no players in queue - do nothing + if( m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].empty() && + m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].empty() && + m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE].empty() && + m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_HORDE].empty() ) return; - uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bgTypeId, arenatype); + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, arenaType); - //battleground with free slot for player should be always the last in this queue + //battleground with free slot for player should be always in the beggining of the queue + // maybe it would be better to create bgfreeslotqueue for each queue_id_based_on_level BGFreeSlotQueueType::iterator itr, next; for (itr = sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].end(); itr = next) { next = itr; ++next; - // battleground is running, so if: - // DO NOT allow queue manager to invite new player to running arena - if ((*itr)->isBattleGround() && (*itr)->GetTypeID() == bgTypeId && (*itr)->GetQueueType() == queue_id && (*itr)->GetStatus() > STATUS_WAIT_QUEUE && (*itr)->GetStatus() < STATUS_WAIT_LEAVE) + // DO NOT allow queue manager to invite new player to arena + if( (*itr)->isBattleGround() && (*itr)->GetTypeID() == bgTypeId && (*itr)->GetQueueId() == queue_id && + (*itr)->GetStatus() > STATUS_WAIT_QUEUE && (*itr)->GetStatus() < STATUS_WAIT_LEAVE ) { - //we must check both teams BattleGround* bg = *itr; //we have to store battleground pointer here, because when battleground is full, it is removed from free queue (not yet implemented!!) // and iterator is invalid - for(QueuedGroupsList::iterator itr = m_QueuedGroups[queue_id].begin(); itr != m_QueuedGroups[queue_id].end(); ++itr) - { - // did the group join for this bg type? - if((*itr)->BgTypeId != bgTypeId) - continue; - // if so, check if fits in - if(bg->GetFreeSlotsForTeam((*itr)->Team) >= (*itr)->Players.size()) - { - // if group fits in, invite it - InviteGroupToBG((*itr),bg,(*itr)->Team); - } - } + // clear selection pools + m_SelectionPools[BG_TEAM_ALLIANCE].Init(); + m_SelectionPools[BG_TEAM_HORDE].Init(); + + // call a function that does the job for us + FillPlayersToBG(bg, queue_id); + + // now everything is set, invite players + for(GroupsQueueType::const_iterator itr = m_SelectionPools[BG_TEAM_ALLIANCE].SelectedGroups.begin(); itr != m_SelectionPools[BG_TEAM_ALLIANCE].SelectedGroups.end(); ++itr) + InviteGroupToBG((*itr), bg, (*itr)->Team); + for(GroupsQueueType::const_iterator itr = m_SelectionPools[BG_TEAM_HORDE].SelectedGroups.begin(); itr != m_SelectionPools[BG_TEAM_HORDE].SelectedGroups.end(); ++itr) + InviteGroupToBG((*itr), bg, (*itr)->Team); if (!bg->HasFreeSlots()) { - //remove BG from BGFreeSlotQueue + // remove BG from BGFreeSlotQueue bg->RemoveFromBGFreeSlotQueue(); } } @@ -524,25 +779,29 @@ void BattleGroundQueue::Update(uint32 bgTypeId, uint32 queue_id, uint8 arenatype // finished iterating through the bgs with free slots, maybe we need to create a new bg BattleGround * bg_template = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); - if(!bg_template) + if (!bg_template) { sLog.outError("Battleground: Update: bg template not found for %u", bgTypeId); return; } - // get the min. players per team, properly for larger arenas as well. (must have full teams for arena matches!) uint32 MinPlayersPerTeam = bg_template->GetMinPlayersPerTeam(); uint32 MaxPlayersPerTeam = bg_template->GetMaxPlayersPerTeam(); - if(bg_template->isArena()) + if (sBattleGroundMgr.isTesting()) + MinPlayersPerTeam = 1; + if (bg_template->isArena()) { - if(sBattleGroundMgr.isArenaTesting()) + if (sBattleGroundMgr.isArenaTesting()) { MaxPlayersPerTeam = 1; MinPlayersPerTeam = 1; } else { - switch(arenatype) + //this switch can be much shorter + MaxPlayersPerTeam = arenaType; + MinPlayersPerTeam = arenaType; + /*switch(arenaType) { case ARENA_TYPE_2v2: MaxPlayersPerTeam = 2; @@ -556,293 +815,192 @@ void BattleGroundQueue::Update(uint32 bgTypeId, uint32 queue_id, uint8 arenatype MaxPlayersPerTeam = 5; MinPlayersPerTeam = 5; break; - } + }*/ } } - // found out the minimum and maximum ratings the newly added team should battle against - // arenaRating is the rating of the latest joined team - uint32 arenaMinRating = (arenaRating <= sBattleGroundMgr.GetMaxRatingDifference()) ? 0 : arenaRating - sBattleGroundMgr.GetMaxRatingDifference(); - // if no rating is specified, set maxrating to 0 - uint32 arenaMaxRating = (arenaRating == 0)? 0 : arenaRating + sBattleGroundMgr.GetMaxRatingDifference(); - uint32 discardTime = 0; - // if max rating difference is set and the time past since server startup is greater than the rating discard time - // (after what time the ratings aren't taken into account when making teams) then - // the discard time is current_time - time_to_discard, teams that joined after that, will have their ratings taken into account - // else leave the discard time on 0, this way all ratings will be discarded - if(sBattleGroundMgr.GetMaxRatingDifference() && getMSTime() >= sBattleGroundMgr.GetRatingDiscardTimer()) - discardTime = getMSTime() - sBattleGroundMgr.GetRatingDiscardTimer(); - - // try to build the selection pools - bool bAllyOK = BuildSelectionPool(bgTypeId, queue_id, MinPlayersPerTeam, MaxPlayersPerTeam, NORMAL_ALLIANCE, arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime); - if(bAllyOK) - sLog.outDebug("Battleground: ally pool successfully built"); - else - sLog.outDebug("Battleground: ally pool wasn't created"); - bool bHordeOK = BuildSelectionPool(bgTypeId, queue_id, MinPlayersPerTeam, MaxPlayersPerTeam, NORMAL_HORDE, arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime); - if(bHordeOK) - sLog.outDebug("Battleground: horde pool successfully built"); - else - sLog.outDebug("Battleground: horde pool wasn't created"); + m_SelectionPools[BG_TEAM_ALLIANCE].Init(); + m_SelectionPools[BG_TEAM_HORDE].Init(); - // if selection pools are ready, create the new bg - if (bAllyOK && bHordeOK) + if (bg_template->isBattleGround()) { - BattleGround * bg2 = 0; - // special handling for arenas - if(bg_template->isArena()) + //check if there is premade against premade match + if (CheckPremadeMatch(queue_id, MinPlayersPerTeam, MaxPlayersPerTeam)) { - // Find a random arena, that can be created - uint8 arenas[] = {BATTLEGROUND_NA, BATTLEGROUND_BE, BATTLEGROUND_RL}; - uint32 arena_num = urand(0,2); - if( !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[arena_num%3])) && - !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[(arena_num+1)%3])) && - !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[(arena_num+2)%3])) ) + //create new battleground + BattleGround * bg2 = NULL; + if (!(bg2 = sBattleGroundMgr.CreateNewBattleGround(bgTypeId, queue_id, 0, false))) { - sLog.outError("Battleground: couldn't create any arena instance!"); + sLog.outError("BattleGroundQueue::Update - Cannot create battleground: %u", bgTypeId); return; } - - // set the MaxPlayersPerTeam values based on arenatype - // setting the min player values isn't needed, since we won't be using that value later on. - if(sBattleGroundMgr.isArenaTesting()) - { - bg2->SetMaxPlayersPerTeam(1); - bg2->SetMaxPlayers(2); - } - else - { - switch(arenatype) - { - case ARENA_TYPE_2v2: - bg2->SetMaxPlayersPerTeam(2); - bg2->SetMaxPlayers(4); - break; - case ARENA_TYPE_3v3: - bg2->SetMaxPlayersPerTeam(3); - bg2->SetMaxPlayers(6); - break; - case ARENA_TYPE_5v5: - bg2->SetMaxPlayersPerTeam(5); - bg2->SetMaxPlayers(10); - break; - default: - break; - } - } - } - else - { - // create new battleground - bg2 = sBattleGroundMgr.CreateNewBattleGround(bgTypeId); - } - - if(!bg2) - { - sLog.outError("Battleground: couldn't create bg %u",bgTypeId); - return; - } - - // start the joining of the bg - bg2->SetStatus(STATUS_WAIT_JOIN); - bg2->SetQueueType(queue_id); - // initialize arena / rating info - bg2->SetArenaType(arenatype); - // set rating - bg2->SetRated(isRated); - - std::list<GroupQueueInfo* >::iterator itr; - - //Send corrent ammount of invites to both faction at start of BG, not all in current queue, makes starting teams even - unsigned int QUEUED_HORDE = m_SelectionPools[NORMAL_HORDE].SelectedGroups.size(); - unsigned int QUEUED_ALLIANCE= m_SelectionPools[NORMAL_ALLIANCE].SelectedGroups.size(); - unsigned int maxbginvites = 0; - - if(QUEUED_ALLIANCE <= QUEUED_HORDE) - maxbginvites = QUEUED_ALLIANCE; - else - maxbginvites = QUEUED_HORDE; - - // invite groups from horde selection pool - int invitecounter = 0; - for(itr = m_SelectionPools[NORMAL_HORDE].SelectedGroups.begin(); itr != m_SelectionPools[NORMAL_HORDE].SelectedGroups.end(); ++itr) - { - if (invitecounter >= maxbginvites) - return; - InviteGroupToBG((*itr),bg2,HORDE); - ++invitecounter; - } - - // invite groups from ally selection pool - invitecounter = 0; - for(itr = m_SelectionPools[NORMAL_ALLIANCE].SelectedGroups.begin(); itr != m_SelectionPools[NORMAL_ALLIANCE].SelectedGroups.end(); ++itr) - { - if (invitecounter >= maxbginvites) - return; - InviteGroupToBG((*itr),bg2,ALLIANCE); - ++invitecounter; - } - - if (isRated) - { - std::list<GroupQueueInfo* >::iterator itr_alliance = m_SelectionPools[NORMAL_ALLIANCE].SelectedGroups.begin(); - std::list<GroupQueueInfo* >::iterator itr_horde = m_SelectionPools[NORMAL_HORDE].SelectedGroups.begin(); - (*itr_alliance)->OpponentsTeamRating = (*itr_horde)->ArenaTeamRating; - sLog.outDebug("setting opposite team rating for team %u to %u", (*itr_alliance)->ArenaTeamId, (*itr_alliance)->OpponentsTeamRating); - (*itr_horde)->OpponentsTeamRating = (*itr_alliance)->ArenaTeamRating; - sLog.outDebug("setting opposite team rating for team %u to %u", (*itr_horde)->ArenaTeamId, (*itr_horde)->OpponentsTeamRating); + //invite those selection pools + for(uint32 i = 0; i < BG_TEAMS_COUNT; i++) + for(GroupsQueueType::const_iterator itr = m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.begin(); itr != m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.end(); ++itr) + InviteGroupToBG((*itr), bg2, (*itr)->Team); + //start bg + bg2->StartBattleGround(); + //clear structures + m_SelectionPools[BG_TEAM_ALLIANCE].Init(); + m_SelectionPools[BG_TEAM_HORDE].Init(); } - - // start the battleground - bg2->StartBattleGround(); } - // there weren't enough players for a "normal" match - // if arena, enable horde versus horde or alliance versus alliance teams here - - else if(bg_template->isArena()) + // now check if there are in queues enough players to start new game of (normal battleground, or non-rated arena) + if (!isRated) { - bool bOneSideHordeTeam1 = false, bOneSideHordeTeam2 = false; - bool bOneSideAllyTeam1 = false, bOneSideAllyTeam2 = false; - bOneSideHordeTeam1 = BuildSelectionPool(bgTypeId, queue_id,MaxPlayersPerTeam,MaxPlayersPerTeam,ONESIDE_HORDE_TEAM1,arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime); - if(bOneSideHordeTeam1) + // if there are enough players in pools, start new battleground or non rated arena + if (CheckNormalMatch(bg_template, queue_id, MinPlayersPerTeam, MaxPlayersPerTeam) + || (bg_template->isArena() && CheckSkirmishForSameFaction(queue_id, MinPlayersPerTeam)) ) { - // one team has been selected, find out if other can be selected too - std::list<GroupQueueInfo* >::iterator itr; - // temporarily change the team side to enable building the next pool excluding the already selected groups - for(itr = m_SelectionPools[ONESIDE_HORDE_TEAM1].SelectedGroups.begin(); itr != m_SelectionPools[ONESIDE_HORDE_TEAM1].SelectedGroups.end(); ++itr) - (*itr)->Team=ALLIANCE; - - bOneSideHordeTeam2 = BuildSelectionPool(bgTypeId, queue_id,MaxPlayersPerTeam,MaxPlayersPerTeam,ONESIDE_HORDE_TEAM2,arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime, (*(m_SelectionPools[ONESIDE_HORDE_TEAM1].SelectedGroups.begin()))->ArenaTeamId); - - // change back the team to horde - for(itr = m_SelectionPools[ONESIDE_HORDE_TEAM1].SelectedGroups.begin(); itr != m_SelectionPools[ONESIDE_HORDE_TEAM1].SelectedGroups.end(); ++itr) - (*itr)->Team=HORDE; - - if(!bOneSideHordeTeam2) - bOneSideHordeTeam1 = false; - } - if(!bOneSideHordeTeam1) - { - // check for one sided ally - bOneSideAllyTeam1 = BuildSelectionPool(bgTypeId, queue_id,MaxPlayersPerTeam,MaxPlayersPerTeam,ONESIDE_ALLIANCE_TEAM1,arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime); - if(bOneSideAllyTeam1) + // we successfully created a pool + BattleGround * bg2 = NULL; + if (!(bg2 = sBattleGroundMgr.CreateNewBattleGround(bgTypeId, queue_id, arenaType, false))) { - // one team has been selected, find out if other can be selected too - std::list<GroupQueueInfo* >::iterator itr; - // temporarily change the team side to enable building the next pool excluding the already selected groups - for(itr = m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.begin(); itr != m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.end(); ++itr) - (*itr)->Team=HORDE; - - bOneSideAllyTeam2 = BuildSelectionPool(bgTypeId, queue_id,MaxPlayersPerTeam,MaxPlayersPerTeam,ONESIDE_ALLIANCE_TEAM2,arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime,(*(m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.begin()))->ArenaTeamId); - - // change back the team to ally - for(itr = m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.begin(); itr != m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.end(); ++itr) - (*itr)->Team=ALLIANCE; + sLog.outError("BattleGroundQueue::Update - Cannot create battleground: %u", bgTypeId); + return; } - if(!bOneSideAllyTeam2) - bOneSideAllyTeam1 = false; + // invite those selection pools + for(uint32 i = 0; i < BG_TEAMS_COUNT; i++) + for(GroupsQueueType::const_iterator itr = m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.begin(); itr != m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.end(); ++itr) + InviteGroupToBG((*itr), bg2, (*itr)->Team); + // start bg + bg2->StartBattleGround(); } - // 1-sided BuildSelectionPool() will work, because the MinPlayersPerTeam == MaxPlayersPerTeam in every arena!!!! - if( (bOneSideHordeTeam1 && bOneSideHordeTeam2) || - (bOneSideAllyTeam1 && bOneSideAllyTeam2) ) + } + else if (bg_template->isArena()) + { + // found out the minimum and maximum ratings the newly added team should battle against + // arenaRating is the rating of the latest joined team, or 0 + // 0 is on (automatic update call) and we must set it to team's with longest wait time + if (!arenaRating ) { - // which side has enough players? - uint32 side = 0; - SelectionPoolBuildMode mode1, mode2; - // find out what pools are we using - if(bOneSideAllyTeam1 && bOneSideAllyTeam2) + GroupQueueInfo* front1 = NULL; + GroupQueueInfo* front2 = NULL; + if (!m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].empty()) { - side = ALLIANCE; - mode1 = ONESIDE_ALLIANCE_TEAM1; - mode2 = ONESIDE_ALLIANCE_TEAM2; + front1 = m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].front(); + arenaRating = front1->ArenaTeamRating; } - else + if (!m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].empty()) { - side = HORDE; - mode1 = ONESIDE_HORDE_TEAM1; - mode2 = ONESIDE_HORDE_TEAM2; + front2 = m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].front(); + arenaRating = front2->ArenaTeamRating; } - - // create random arena - uint8 arenas[] = {BATTLEGROUND_NA, BATTLEGROUND_BE, BATTLEGROUND_RL}; - uint32 arena_num = urand(0,2); - BattleGround* bg2 = NULL; - if( !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[arena_num%3])) && - !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[(arena_num+1)%3])) && - !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[(arena_num+2)%3])) ) + if (front1 && front2) { - sLog.outError("Could not create arena."); - return; + if (front1->JoinTime < front2->JoinTime) + arenaRating = front1->ArenaTeamRating; } + else if (!front1 && !front2) + return; //queues are empty + } + + //set rating range + uint32 arenaMinRating = (arenaRating <= sBattleGroundMgr.GetMaxRatingDifference()) ? 0 : arenaRating - sBattleGroundMgr.GetMaxRatingDifference(); + uint32 arenaMaxRating = arenaRating + sBattleGroundMgr.GetMaxRatingDifference(); + // if max rating difference is set and the time past since server startup is greater than the rating discard time + // (after what time the ratings aren't taken into account when making teams) then + // the discard time is current_time - time_to_discard, teams that joined after that, will have their ratings taken into account + // else leave the discard time on 0, this way all ratings will be discarded + uint32 discardTime = getMSTime() - sBattleGroundMgr.GetRatingDiscardTimer(); - sLog.outDebug("Battleground: One-faction arena created."); - // init stats - if(sBattleGroundMgr.isArenaTesting()) + // we need to find 2 teams which will play next game + + GroupsQueueType::iterator itr_team[BG_TEAMS_COUNT]; + + //optimalization : --- we dont need to use selection_pools - each update we select max 2 groups + + for(uint32 i = BG_QUEUE_PREMADE_ALLIANCE; i < BG_QUEUE_NORMAL_ALLIANCE; i++) + { + // take the group that joined first + itr_team[i] = m_QueuedGroups[queue_id][i].begin(); + for(; itr_team[i] != m_QueuedGroups[queue_id][i].end(); ++(itr_team[i])) { - bg2->SetMaxPlayersPerTeam(1); - bg2->SetMaxPlayers(2); + // if group match conditions, then add it to pool + if( !(*itr_team[i])->IsInvitedToBGInstanceGUID + && (((*itr_team[i])->ArenaTeamRating >= arenaMinRating && (*itr_team[i])->ArenaTeamRating <= arenaMaxRating) + || (*itr_team[i])->JoinTime < discardTime) ) + { + m_SelectionPools[i].AddGroup((*itr_team[i]), MaxPlayersPerTeam); + // break for cycle to be able to start selecting another group from same faction queue + break; + } } - else + } + // now we are done if we have 2 groups - ali vs horde! + // if we don't have, we must try to continue search in same queue + // tmp variables are correctly set + // this code isn't much userfriendly - but it is supposed to continue search for mathing group in HORDE queue + if (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() == 0 && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()) + { + itr_team[BG_TEAM_ALLIANCE] = itr_team[BG_TEAM_HORDE]; + ++itr_team[BG_TEAM_ALLIANCE]; + for(; itr_team[BG_TEAM_ALLIANCE] != m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].end(); ++(itr_team[BG_TEAM_ALLIANCE])) { - switch(arenatype) + if( !(*itr_team[BG_TEAM_ALLIANCE])->IsInvitedToBGInstanceGUID + && (((*itr_team[BG_TEAM_ALLIANCE])->ArenaTeamRating >= arenaMinRating && (*itr_team[BG_TEAM_ALLIANCE])->ArenaTeamRating <= arenaMaxRating) + || (*itr_team[BG_TEAM_ALLIANCE])->JoinTime < discardTime) ) { - case ARENA_TYPE_2v2: - bg2->SetMaxPlayersPerTeam(2); - bg2->SetMaxPlayers(4); + m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*itr_team[BG_TEAM_ALLIANCE]), MaxPlayersPerTeam); break; - case ARENA_TYPE_3v3: - bg2->SetMaxPlayersPerTeam(3); - bg2->SetMaxPlayers(6); - break; - case ARENA_TYPE_5v5: - bg2->SetMaxPlayersPerTeam(5); - bg2->SetMaxPlayers(10); - break; - default: + } + } + } + // this code isn't much userfriendly - but it is supposed to continue search for mathing group in ALLIANCE queue + if (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() == 0 && m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()) + { + itr_team[BG_TEAM_HORDE] = itr_team[BG_TEAM_ALLIANCE]; + ++itr_team[BG_TEAM_HORDE]; + for(; itr_team[BG_TEAM_HORDE] != m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].end(); ++(itr_team[BG_TEAM_HORDE])) + { + if( !(*itr_team[BG_TEAM_HORDE])->IsInvitedToBGInstanceGUID + && (((*itr_team[BG_TEAM_HORDE])->ArenaTeamRating >= arenaMinRating && (*itr_team[BG_TEAM_HORDE])->ArenaTeamRating <= arenaMaxRating) + || (*itr_team[BG_TEAM_HORDE])->JoinTime < discardTime) ) + { + m_SelectionPools[BG_TEAM_HORDE].AddGroup((*itr_team[BG_TEAM_HORDE]), MaxPlayersPerTeam); break; } } + } - bg2->SetRated(isRated); - - // assigned team of the other group - uint32 other_side; - if(side == ALLIANCE) - other_side = HORDE; - else - other_side = ALLIANCE; - - // start the joining of the bg - bg2->SetStatus(STATUS_WAIT_JOIN); - bg2->SetQueueType(queue_id); - // initialize arena / rating info - bg2->SetArenaType(arenatype); - - std::list<GroupQueueInfo* >::iterator itr; - - // invite players from the first group as horde players (actually green team) - for(itr = m_SelectionPools[mode1].SelectedGroups.begin(); itr != m_SelectionPools[mode1].SelectedGroups.end(); ++itr) + //if we have 2 teams, then start new arena and invite players! + if (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()) + { + BattleGround* arena = NULL; + if (!(arena = sBattleGroundMgr.CreateNewBattleGround(bgTypeId, queue_id, arenaType, true))) { - InviteGroupToBG((*itr),bg2,HORDE); + sLog.outError("BattlegroundQueue::Update couldn't create arena instance for rated arena match!"); + return; } - // invite players from the second group as ally players (actually gold team) - for(itr = m_SelectionPools[mode2].SelectedGroups.begin(); itr != m_SelectionPools[mode2].SelectedGroups.end(); ++itr) + (*(itr_team[BG_TEAM_ALLIANCE]))->OpponentsTeamRating = (*(itr_team[BG_TEAM_HORDE]))->ArenaTeamRating; + sLog.outDebug("setting oposite teamrating for team %u to %u", (*(itr_team[BG_TEAM_ALLIANCE]))->ArenaTeamId, (*(itr_team[BG_TEAM_ALLIANCE]))->OpponentsTeamRating); + (*(itr_team[BG_TEAM_HORDE]))->OpponentsTeamRating = (*(itr_team[BG_TEAM_ALLIANCE]))->ArenaTeamRating; + sLog.outDebug("setting oposite teamrating for team %u to %u", (*(itr_team[BG_TEAM_HORDE]))->ArenaTeamId, (*(itr_team[BG_TEAM_HORDE]))->OpponentsTeamRating); + // now we must move team if we changed its faction to another faction queue, because then we will spam log by errors in Queue::RemovePlayer + if ((*(itr_team[BG_TEAM_ALLIANCE]))->Team != ALLIANCE) { - InviteGroupToBG((*itr),bg2,ALLIANCE); + // add to alliance queue + m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].push_front(*(itr_team[BG_TEAM_ALLIANCE])); + // erase from horde queue + m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].erase(itr_team[BG_TEAM_ALLIANCE]); + itr_team[BG_TEAM_ALLIANCE] = m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].begin(); } - - if (isRated) + if ((*(itr_team[BG_TEAM_HORDE]))->Team != HORDE) { - std::list<GroupQueueInfo* >::iterator itr_alliance = m_SelectionPools[mode1].SelectedGroups.begin(); - std::list<GroupQueueInfo* >::iterator itr_horde = m_SelectionPools[mode2].SelectedGroups.begin(); - (*itr_alliance)->OpponentsTeamRating = (*itr_horde)->ArenaTeamRating; - (*itr_horde)->OpponentsTeamRating = (*itr_alliance)->ArenaTeamRating; + m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].push_front(*(itr_team[BG_TEAM_HORDE])); + m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].erase(itr_team[BG_TEAM_HORDE]); + itr_team[BG_TEAM_HORDE] = m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].begin(); } - bg2->StartBattleGround(); + InviteGroupToBG(*(itr_team[BG_TEAM_ALLIANCE]), arena, ALLIANCE); + InviteGroupToBG(*(itr_team[BG_TEAM_HORDE]), arena, HORDE); + + sLog.outDebug("Starting rated arena match!"); + + arena->StartBattleGround(); } } } @@ -854,36 +1012,29 @@ void BattleGroundQueue::Update(uint32 bgTypeId, uint32 queue_id, uint8 arenatype bool BGQueueInviteEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) { Player* plr = objmgr.GetPlayer( m_PlayerGuid ); - // player logged off (we should do nothing, he is correctly removed from queue in another procedure) if (!plr) return true; - // Player can be in another BG queue and must be removed in normal way in any case - // // player is already in battleground ... do nothing (battleground queue status is deleted when player is teleported to BG) - // if (plr->GetBattleGroundId() > 0) - // return true; - - BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID); + BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID, m_BgTypeId); + //if battleground ended and its instance deleted - do nothing if (!bg) return true; - uint32 queueSlot = plr->GetBattleGroundQueueIndex(bg->GetTypeID()); - if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); + uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); + if( queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES ) // player is in queue or in battleground { - uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); - uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); - if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue + // check if player is invited to this bg + BattleGroundQueue::QueuedPlayersMap const& qpMap = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers; + BattleGroundQueue::QueuedPlayersMap::const_iterator qItr = qpMap.find(m_PlayerGuid); + if( qItr != qpMap.end() && qItr->second.GroupInfo->IsInvitedToBGInstanceGUID == m_BgInstanceGUID + && qItr->second.GroupInfo->RemoveInviteTime == m_RemoveTime ) { - // check if player is invited to this bg ... this check must be here, because when player leaves queue and joins another, it would cause a problems - BattleGroundQueue::QueuedPlayersMap const& qpMap = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()]; - BattleGroundQueue::QueuedPlayersMap::const_iterator qItr = qpMap.find(m_PlayerGuid); - if (qItr != qpMap.end() && qItr->second.GroupInfo->IsInvitedToBGInstanceGUID == m_BgInstanceGUID) - { - WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, qItr->second.GroupInfo->Team, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME/2, 0); - plr->GetSession()->SendPacket(&data); - } + WorldPacket data; + //we must send remaining time in queue + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME - INVITATION_REMIND_TIME, 0, qItr->second.GroupInfo->ArenaType); + plr->GetSession()->SendPacket(&data); } } return true; //event will be deleted @@ -891,10 +1042,18 @@ bool BGQueueInviteEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) void BGQueueInviteEvent::Abort(uint64 /*e_time*/) { - //this should not be called - sLog.outError("Battleground invite event ABORTED!"); + //do nothing } +/* + this event has many possibilities when it is executed: + 1. player is in battleground ( he clicked enter on invitation window ) + 2. player left battleground queue and he isn't there any more + 3. player left battleground queue and he joined it again and IsInvitedToBGInstanceGUID = 0 + 4. player left queue and he joined again and he has been invited to same battleground again -> we should not remove him from queue yet + 5. player is invited to bg and he didn't choose what to do and timer expired - only in this condition we should call queue::RemovePlayer + we must remove player in the 5. case even if battleground object doesn't exist! +*/ bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) { Player* plr = objmgr.GetPlayer( m_PlayerGuid ); @@ -902,40 +1061,33 @@ bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) // player logged off (we should do nothing, he is correctly removed from queue in another procedure) return true; - BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID); - if (!bg) - return true; - - sLog.outDebug("Battleground: removing player %u from bg queue for instance %u because of not pressing enter battle in time.",plr->GetGUIDLow(),m_BgInstanceGUID); + BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID, m_BgTypeId); + //battleground can be deleted already when we are removing queue info + //bg pointer can be NULL! so use it carefully! - uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); - uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); - if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue + uint32 queueSlot = plr->GetBattleGroundQueueIndex(m_BgQueueTypeId); + if( queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES ) // player is in queue, or in Battleground { - // check if player is invited to this bg ... this check must be here, because when player leaves queue and joins another, it would cause a problems - BattleGroundQueue::QueuedPlayersMap::iterator qMapItr = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()].find(m_PlayerGuid); - if (qMapItr != sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()].end() && qMapItr->second.GroupInfo && qMapItr->second.GroupInfo->IsInvitedToBGInstanceGUID == m_BgInstanceGUID) + // check if player is in queue for this BG and if we are removing his invite event + BattleGroundQueue::QueuedPlayersMap& qpMap = sBattleGroundMgr.m_BattleGroundQueues[m_BgQueueTypeId].m_QueuedPlayers; + BattleGroundQueue::QueuedPlayersMap::iterator qMapItr = qpMap.find(m_PlayerGuid); + if( qMapItr != qpMap.end() && qMapItr->second.GroupInfo + && qMapItr->second.GroupInfo->IsInvitedToBGInstanceGUID == m_BgInstanceGUID + && qMapItr->second.GroupInfo->RemoveInviteTime == m_RemoveTime ) { - if (qMapItr->second.GroupInfo->IsRated) - { - ArenaTeam * at = objmgr.GetArenaTeamById(qMapItr->second.GroupInfo->ArenaTeamId); - if (at) - { - sLog.outDebug("UPDATING memberLost's personal arena rating for %u by opponents rating: %u", GUID_LOPART(plr->GetGUID()), qMapItr->second.GroupInfo->OpponentsTeamRating); - at->MemberLost(plr, qMapItr->second.GroupInfo->OpponentsTeamRating); - at->SaveToDB(); - } - } - plr->RemoveBattleGroundQueueId(bgQueueTypeId); - sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].RemovePlayer(m_PlayerGuid, true); - sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgQueueTypeId, bg->GetQueueType()); + sLog.outDebug("Battleground: removing player %u from bg queue for instance %u because of not pressing enter battle in time.",plr->GetGUIDLow(),m_BgInstanceGUID); + + plr->RemoveBattleGroundQueueId(m_BgQueueTypeId); + sBattleGroundMgr.m_BattleGroundQueues[m_BgQueueTypeId].RemovePlayer(m_PlayerGuid, true); + //update queues if battleground isn't ended + if (bg) + sBattleGroundMgr.m_BattleGroundQueues[m_BgQueueTypeId].Update(m_BgTypeId, bg->GetQueueId()); + WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, m_PlayersTeam, queueSlot, STATUS_NONE, 0, 0); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0); plr->GetSession()->SendPacket(&data); } } - else - sLog.outDebug("Battleground: Player was already removed from queue"); //event will be deleted return true; @@ -943,76 +1095,98 @@ bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) void BGQueueRemoveEvent::Abort(uint64 /*e_time*/) { - //this should not be called - sLog.outError("Battleground remove event ABORTED!"); + //do nothing } /*********************************************************/ /*** BATTLEGROUND MANAGER ***/ /*********************************************************/ -BattleGroundMgr::BattleGroundMgr() +BattleGroundMgr::BattleGroundMgr() : m_AutoDistributionTimeChecker(0), m_ArenaTesting(false) { - m_BattleGrounds.clear(); - m_AutoDistributePoints = (bool)sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS); - m_MaxRatingDifference = sWorld.getConfig(CONFIG_ARENA_MAX_RATING_DIFFERENCE); - m_RatingDiscardTimer = sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER); - m_PrematureFinishTimer = sWorld.getConfig(CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER); - m_NextRatingDiscardUpdate = m_RatingDiscardTimer; - m_AutoDistributionTimeChecker = 0; - m_ArenaTesting = false; + for(uint32 i = BATTLEGROUND_TYPE_NONE; i < MAX_BATTLEGROUND_TYPE_ID; i++) + m_BattleGrounds[i].clear(); + m_NextRatingDiscardUpdate = sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER); + m_Testing=false; } BattleGroundMgr::~BattleGroundMgr() { - BattleGroundSet::iterator itr, next; - for(itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); itr = next) + DeleteAllBattleGrounds(); +} + +void BattleGroundMgr::DeleteAllBattleGrounds() +{ + for(uint32 i = BATTLEGROUND_TYPE_NONE; i < MAX_BATTLEGROUND_TYPE_ID; i++) { - next = itr; - ++next; - BattleGround * bg = itr->second; - m_BattleGrounds.erase(itr); - delete bg; + for(BattleGroundSet::iterator itr = m_BattleGrounds[i].begin(); itr != m_BattleGrounds[i].end();) + { + BattleGround * bg = itr->second; + m_BattleGrounds[i].erase(itr++); + if (!m_ClientBattleGroundIds[i][bg->GetQueueId()].empty()) + m_ClientBattleGroundIds[i][bg->GetQueueId()].erase(bg->GetClientInstanceID()); + delete bg; + } + } + + // destroy template battlegrounds that listed only in queues (other already terminated) + for(uint32 bgTypeId = 0; bgTypeId < MAX_BATTLEGROUND_TYPE_ID; ++bgTypeId) + { + // ~BattleGround call unregistring BG from queue + while(!BGFreeSlotQueue[bgTypeId].empty()) + delete BGFreeSlotQueue[bgTypeId].front(); } - m_BattleGrounds.clear(); } // used to update running battlegrounds, and delete finished ones -void BattleGroundMgr::Update(time_t diff) +void BattleGroundMgr::Update(uint32 diff) { BattleGroundSet::iterator itr, next; - for(itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); itr = next) + for(uint32 i = BATTLEGROUND_TYPE_NONE; i < MAX_BATTLEGROUND_TYPE_ID; i++) { - next = itr; - ++next; - itr->second->Update(diff); - // use the SetDeleteThis variable - // direct deletion caused crashes - if(itr->second->m_SetDeleteThis) + itr = m_BattleGrounds[i].begin(); + // skip updating battleground template + if (itr != m_BattleGrounds[i].end()) + ++itr; + for(; itr != m_BattleGrounds[i].end(); itr = next) { - BattleGround * bg = itr->second; - m_BattleGrounds.erase(itr); - delete bg; + next = itr; + ++next; + itr->second->Update(diff); + // use the SetDeleteThis variable + // direct deletion caused crashes + if (itr->second->m_SetDeleteThis) + { + BattleGround * bg = itr->second; + m_BattleGrounds[i].erase(itr); + if (!m_ClientBattleGroundIds[i][bg->GetQueueId()].empty()) + m_ClientBattleGroundIds[i][bg->GetQueueId()].erase(bg->GetClientInstanceID()); + delete bg; + } } } // if rating difference counts, maybe force-update queues - if(m_MaxRatingDifference) + if (sWorld.getConfig(CONFIG_ARENA_MAX_RATING_DIFFERENCE) && sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER)) { // it's time to force update - if(m_NextRatingDiscardUpdate < diff) + if (m_NextRatingDiscardUpdate < diff) { // forced update for level 70 rated arenas - m_BattleGroundQueues[BATTLEGROUND_QUEUE_2v2].Update(BATTLEGROUND_AA,6,ARENA_TYPE_2v2,true,0); - m_BattleGroundQueues[BATTLEGROUND_QUEUE_3v3].Update(BATTLEGROUND_AA,6,ARENA_TYPE_3v3,true,0); - m_BattleGroundQueues[BATTLEGROUND_QUEUE_5v5].Update(BATTLEGROUND_AA,6,ARENA_TYPE_5v5,true,0); - m_NextRatingDiscardUpdate = m_RatingDiscardTimer; + sLog.outDebug("BattleGroundMgr: UPDATING ARENA QUEUES"); + m_BattleGroundQueues[BATTLEGROUND_QUEUE_2v2].Update(BATTLEGROUND_AA, QUEUE_ID_MAX_LEVEL_79, ARENA_TYPE_2v2, true, 0); + m_BattleGroundQueues[BATTLEGROUND_QUEUE_2v2].Update(BATTLEGROUND_AA, QUEUE_ID_MAX_LEVEL_80, ARENA_TYPE_2v2, true, 0); + m_BattleGroundQueues[BATTLEGROUND_QUEUE_3v3].Update(BATTLEGROUND_AA, QUEUE_ID_MAX_LEVEL_79, ARENA_TYPE_3v3, true, 0); + m_BattleGroundQueues[BATTLEGROUND_QUEUE_3v3].Update(BATTLEGROUND_AA, QUEUE_ID_MAX_LEVEL_80, ARENA_TYPE_3v3, true, 0); + m_BattleGroundQueues[BATTLEGROUND_QUEUE_5v5].Update(BATTLEGROUND_AA, QUEUE_ID_MAX_LEVEL_79, ARENA_TYPE_5v5, true, 0); + m_BattleGroundQueues[BATTLEGROUND_QUEUE_5v5].Update(BATTLEGROUND_AA, QUEUE_ID_MAX_LEVEL_80, ARENA_TYPE_5v5, true, 0); + m_NextRatingDiscardUpdate = sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER); } else m_NextRatingDiscardUpdate -= diff; } - if(m_AutoDistributePoints) + if (sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS)) { - if(m_AutoDistributionTimeChecker < diff) + if (m_AutoDistributionTimeChecker < diff) { if(time(NULL) > m_NextAutoDistributionTime) { @@ -1027,10 +1201,11 @@ void BattleGroundMgr::Update(time_t diff) } } -void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint32 team, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint32 arenatype, uint8 israted) +void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint8 arenatype) { // we can be in 3 queues in same time... - if(StatusID == 0) + + if (StatusID == 0 || !bg) { data->Initialize(SMSG_BATTLEFIELD_STATUS, 4*3); *data << uint32(QueueSlot); // queue id (0...2) @@ -1041,10 +1216,11 @@ void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGro data->Initialize(SMSG_BATTLEFIELD_STATUS, (4+1+1+4+2+4+1+4+4+4)); *data << uint32(QueueSlot); // queue id (0...2) - player can be in 3 queues in time // uint64 in client - *data << uint64( uint64(arenatype ? arenatype : bg->GetArenaType()) | (uint64(0x0D) << 8) | (uint64(bg->GetTypeID()) << 16) | (uint64(0x1F90) << 48) ); - *data << uint32(0); // unknown + *data << uint64( uint64(arenatype) | (uint64(0x0D) << 8) | (uint64(bg->GetTypeID()) << 16) | (uint64(0x1F90) << 48) ); + *data << uint32(bg->GetClientInstanceID()); // alliance/horde for BG and skirmish/rated for Arenas - *data << uint8(bg->isArena() ? ( israted ? israted : bg->isRated() ) : bg->GetTeamIndexByTeamId(team)); + // following displays the minimap-icon 0 = faction icon 1 = arenaicon + *data << uint8(bg->isRated()); /* *data << uint8(arenatype ? arenatype : bg->GetArenaType()); // team type (0=BG, 2=2x2, 3=3x3, 5=5x5), for arenas // NOT PROPER VALUE IF ARENA ISN'T RUNNING YET!!!! switch(bg->GetTypeID()) // value depends on bg id { @@ -1072,12 +1248,21 @@ void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGro case BATTLEGROUND_RL: *data << uint8(8); break; + case BATTLEGROUND_SA: + *data << uint8(9); + break; + case BATTLEGROUND_DS: + *data << uint8(10); + break; + case BATTLEGROUND_RV: + *data << uint8(11); + break; default: // unknown *data << uint8(0); break; } - if(bg->isArena() && (StatusID == STATUS_WAIT_QUEUE)) + if (bg->isArena() && (StatusID == STATUS_WAIT_QUEUE)) *data << uint32(BATTLEGROUND_AA); // all arenas I don't think so. else *data << uint32(bg->GetTypeID()); // BG id from DBC @@ -1085,17 +1270,14 @@ void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGro *data << uint16(0x1F90); // unk value 8080 *data << uint32(bg->GetInstanceID()); // instance id - if(bg->isBattleGround()) - *data << uint8(bg->GetTeamIndexByTeamId(team)); // team - else - *data << uint8(israted?israted:bg->isRated()); // is rated battle + *data << uint8(bg->isArena()); // minimap-icon 0=faction 1=arena */ *data << uint32(StatusID); // status switch(StatusID) { case STATUS_WAIT_QUEUE: // status_in_queue *data << uint32(Time1); // average wait time, milliseconds - *data << uint32(Time2); // time in queue, updated every minute? + *data << uint32(Time2); // time in queue, updated every minute!, milliseconds break; case STATUS_WAIT_JOIN: // status_invite *data << uint32(bg->GetMapId()); // map id @@ -1103,7 +1285,7 @@ void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGro break; case STATUS_IN_PROGRESS: // status_in_progress *data << uint32(bg->GetMapId()); // map id - *data << uint32(Time1); // 0 at bg start, 120000 after bg end, time to bg auto leave, milliseconds + *data << uint32(Time1); // time to bg auto leave, 0 at bg start, 120000 after bg end, milliseconds *data << uint32(Time2); // time from bg start, milliseconds *data << uint8(0x1); // unk sometimes 0x0! break; @@ -1116,16 +1298,16 @@ 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! sLog.outDebug("rating change: %d", bg->m_ArenaTeamRatingChanges[i]); } @@ -1133,16 +1315,16 @@ void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg) { uint32 at_id = bg->m_ArenaTeamIds[i]; ArenaTeam * at = objmgr.GetArenaTeamById(at_id); - if(at) + if (at) *data << at->GetName(); - else//*/ + else *data << (uint8)0; } } - if(bg->GetWinner() == 2) + if (bg->GetStatus() != STATUS_WAIT_LEAVE) { - *data << uint8(0); // bg in progress + *data << uint8(0); // bg not ended } else { @@ -1156,10 +1338,7 @@ 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) + if (type == 0) { *data << (int32)itr->second->HonorableKills; *data << (int32)itr->second->Deaths; @@ -1167,18 +1346,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); } @@ -1205,13 +1378,16 @@ void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg) *data << (uint32)((BattleGroundABScore*)itr->second)->BasesDefended; // bases defended break; case BATTLEGROUND_EY: - *data << (uint32)0x00000001; // count of next fields + *data << (uint32)0x00000001; // count of next fields *data << (uint32)((BattleGroundEYScore*)itr->second)->FlagCaptures; // flag captures break; case BATTLEGROUND_NA: case BATTLEGROUND_BE: case BATTLEGROUND_AA: case BATTLEGROUND_RL: + case BATTLEGROUND_SA: // wotlk + case BATTLEGROUND_DS: // wotlk + case BATTLEGROUND_RV: // wotlk *data << (int32)0; // 0 break; default: @@ -1222,7 +1398,7 @@ void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg) } } -void BattleGroundMgr::BuildGroupJoinedBattlegroundPacket(WorldPacket *data, uint32 bgTypeId) +void BattleGroundMgr::BuildGroupJoinedBattlegroundPacket(WorldPacket *data, BattleGroundTypeId bgTypeId) { /*bgTypeId is: 0 - Your group has joined a battleground queue, but you are not eligible @@ -1250,10 +1426,10 @@ void BattleGroundMgr::BuildPlaySoundPacket(WorldPacket *data, uint32 soundid) *data << uint32(soundid); } -void BattleGroundMgr::BuildPlayerLeftBattleGroundPacket(WorldPacket *data, Player *plr) +void BattleGroundMgr::BuildPlayerLeftBattleGroundPacket(WorldPacket *data, const uint64& guid) { data->Initialize(SMSG_BATTLEGROUND_PLAYER_LEFT, 8); - *data << uint64(plr->GetGUID()); + *data << uint64(guid); } void BattleGroundMgr::BuildPlayerJoinedBattleGroundPacket(WorldPacket *data, Player *plr) @@ -1262,62 +1438,97 @@ void BattleGroundMgr::BuildPlayerJoinedBattleGroundPacket(WorldPacket *data, Pla *data << uint64(plr->GetGUID()); } -void BattleGroundMgr::InvitePlayer(Player* plr, uint32 bgInstanceGUID, uint32 team) +BattleGround * BattleGroundMgr::GetBattleGroundThroughClientInstance(uint32 instanceId, BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLevel queue_id) { - // set invited player counters: - BattleGround* bg = GetBattleGround(bgInstanceGUID); - if(!bg) - return; - bg->IncreaseInvitedCount(team); + //cause at HandleBattleGroundJoinOpcode the clients sends the instanceid he gets from + //SMSG_BATTLEFIELD_LIST we need to find the battleground with this clientinstance-id + BattleGround* bg = GetBattleGroundTemplate(bgTypeId); + if (!bg) + return NULL; + + if (bg->isArena()) + return GetBattleGround(instanceId, bgTypeId); - plr->SetInviteForBattleGroundQueueType(BGQueueTypeId(bg->GetTypeID(),bg->GetArenaType()), bgInstanceGUID); + for(BattleGroundSet::iterator itr = m_BattleGrounds[bgTypeId].begin(); itr != m_BattleGrounds[bgTypeId].end(); ++itr) + { + if (itr->second->GetClientInstanceID() == instanceId) + return itr->second; + } + return NULL; +} - // set the arena teams for rated matches - if(bg->isArena() && bg->isRated()) +BattleGround * BattleGroundMgr::GetBattleGround(uint32 InstanceID, BattleGroundTypeId bgTypeId) +{ + //search if needed + BattleGroundSet::iterator itr; + if (bgTypeId == BATTLEGROUND_TYPE_NONE) { - switch(bg->GetArenaType()) + for(uint32 i = BATTLEGROUND_AV; i < MAX_BATTLEGROUND_TYPE_ID; i++) { - case ARENA_TYPE_2v2: - bg->SetArenaTeamIdForTeam(team, plr->GetArenaTeamId(0)); - break; - case ARENA_TYPE_3v3: - bg->SetArenaTeamIdForTeam(team, plr->GetArenaTeamId(1)); - break; - case ARENA_TYPE_5v5: - bg->SetArenaTeamIdForTeam(team, plr->GetArenaTeamId(2)); - break; - default: - break; + itr = m_BattleGrounds[i].find(InstanceID); + if (itr != m_BattleGrounds[i].end()) + return itr->second; } + return NULL; } + itr = m_BattleGrounds[bgTypeId].find(InstanceID); + return ( (itr != m_BattleGrounds[bgTypeId].end()) ? itr->second : NULL ); +} - // create invite events: - //add events to player's counters ---- this is not good way - there should be something like global event processor, where we should add those events - BGQueueInviteEvent* inviteEvent = new BGQueueInviteEvent(plr->GetGUID(), bgInstanceGUID); - plr->m_Events.AddEvent(inviteEvent, plr->m_Events.CalculateTime(INVITE_ACCEPT_WAIT_TIME/2)); - BGQueueRemoveEvent* removeEvent = new BGQueueRemoveEvent(plr->GetGUID(), bgInstanceGUID, team); - plr->m_Events.AddEvent(removeEvent, plr->m_Events.CalculateTime(INVITE_ACCEPT_WAIT_TIME)); +BattleGround * BattleGroundMgr::GetBattleGroundTemplate(BattleGroundTypeId bgTypeId) +{ + //map is sorted and we can be sure that lowest instance id has only BG template + return m_BattleGrounds[bgTypeId].empty() ? NULL : m_BattleGrounds[bgTypeId].begin()->second; } -BattleGround * BattleGroundMgr::GetBattleGroundTemplate(uint32 bgTypeId) +uint32 BattleGroundMgr::CreateClientVisibleInstanceId(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLevel queue_id) { - return BGFreeSlotQueue[bgTypeId].empty() ? NULL : BGFreeSlotQueue[bgTypeId].back(); + if (IsArenaType(bgTypeId)) + return 0; //arenas don't have client-instanceids + + // we create here an instanceid, which is just for + // displaying this to the client and without any other use.. + // the client-instanceIds are unique for each battleground-type + // the instance-id just needs to be as low as possible, beginning with 1 + // the following works, because std::set is default ordered with "<" + // the optimalization would be to use as bitmask std::vector<uint32> - but that would only make code unreadable + uint32 lastId = 0; + for(std::set<uint32>::iterator itr = m_ClientBattleGroundIds[bgTypeId][queue_id].begin(); itr != m_ClientBattleGroundIds[bgTypeId][queue_id].end();) + { + if( (++lastId) != *itr) //if there is a gap between the ids, we will break.. + break; + lastId = *itr; + } + m_ClientBattleGroundIds[bgTypeId][queue_id].insert(lastId + 1); + return lastId + 1; } // create a new battleground that will really be used to play -BattleGround * BattleGroundMgr::CreateNewBattleGround(uint32 bgTypeId) +BattleGround * BattleGroundMgr::CreateNewBattleGround(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLevel queue_id, uint8 arenaType, bool isRated) { - BattleGround *bg = NULL; - // get the template BG BattleGround *bg_template = GetBattleGroundTemplate(bgTypeId); - - if(!bg_template) + if (!bg_template) { sLog.outError("BattleGround: CreateNewBattleGround - bg template not found for %u", bgTypeId); - return 0; + return NULL; } + //for arenas there is random map used + if (bg_template->isArena()) + { + BattleGroundTypeId arenas[] = {BATTLEGROUND_NA, BATTLEGROUND_BE, BATTLEGROUND_RL}; + uint32 arena_num = urand(0,2); + bgTypeId = arenas[arena_num]; + bg_template = GetBattleGroundTemplate(bgTypeId); + if (!bg_template) + { + sLog.outError("BattleGround: CreateNewBattleGround - bg template not found for %u", bgTypeId); + return NULL; + } + } + + BattleGround *bg = NULL; // create a copy of the BG template switch(bgTypeId) { @@ -1345,42 +1556,47 @@ BattleGround * BattleGroundMgr::CreateNewBattleGround(uint32 bgTypeId) case BATTLEGROUND_RL: bg = new BattleGroundRL(*(BattleGroundRL*)bg_template); break; + case BATTLEGROUND_SA: + bg = new BattleGroundSA(*(BattleGroundSA*)bg_template); + break; + case BATTLEGROUND_DS: + bg = new BattleGroundDS(*(BattleGroundDS*)bg_template); + break; + case BATTLEGROUND_RV: + bg = new BattleGroundRV(*(BattleGroundRV*)bg_template); + break; default: - //bg = new BattleGround; + //error, but it is handled few lines above return 0; - break; // placeholder for non implemented BG } // generate a new instance id bg->SetInstanceID(MapManager::Instance().GenerateInstanceId()); // set instance id + bg->SetClientInstanceID(CreateClientVisibleInstanceId(bgTypeId, queue_id)); // reset the new bg (set status to status_wait_queue from status_none) bg->Reset(); - /* will be setup in BG::Update() when the first player is ported in - if(!(bg->SetupBattleGround())) - { - sLog.outError("BattleGround: CreateNewBattleGround: SetupBattleGround failed for bg %u", bgTypeId); - delete bg; - return 0; - } - */ + // start the joining of the bg + bg->SetStatus(STATUS_WAIT_JOIN); + bg->SetQueueId(queue_id); + bg->SetArenaType(arenaType); + bg->SetRated(isRated); // add BG to free slot queue bg->AddToBGFreeSlotQueue(); // add bg to update list - AddBattleGround(bg->GetInstanceID(), bg); + AddBattleGround(bg->GetInstanceID(), bg->GetTypeID(), bg); return bg; } // used to create the BG templates -uint32 BattleGroundMgr::CreateBattleGround(uint32 bgTypeId, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam, uint32 LevelMin, uint32 LevelMax, char* BattleGroundName, uint32 MapID, float Team1StartLocX, float Team1StartLocY, float Team1StartLocZ, float Team1StartLocO, float Team2StartLocX, float Team2StartLocY, float Team2StartLocZ, float Team2StartLocO) +uint32 BattleGroundMgr::CreateBattleGround(BattleGroundTypeId bgTypeId, bool IsArena, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam, uint32 LevelMin, uint32 LevelMax, char* BattleGroundName, uint32 MapID, float Team1StartLocX, float Team1StartLocY, float Team1StartLocZ, float Team1StartLocO, float Team2StartLocX, float Team2StartLocY, float Team2StartLocZ, float Team2StartLocO) { // Create the BG BattleGround *bg = NULL; - switch(bgTypeId) { case BATTLEGROUND_AV: bg = new BattleGroundAV; break; @@ -1391,35 +1607,27 @@ uint32 BattleGroundMgr::CreateBattleGround(uint32 bgTypeId, uint32 MinPlayersPer case BATTLEGROUND_AA: bg = new BattleGroundAA; break; case BATTLEGROUND_EY: bg = new BattleGroundEY; break; case BATTLEGROUND_RL: bg = new BattleGroundRL; break; + case BATTLEGROUND_SA: bg = new BattleGroundSA; break; + case BATTLEGROUND_DS: bg = new BattleGroundDS; break; + case BATTLEGROUND_RV: bg = new BattleGroundRV; break; default:bg = new BattleGround; break; // placeholder for non implemented BG } bg->SetMapId(MapID); - - bg->Reset(); - - BattlemasterListEntry const *bl = sBattlemasterListStore.LookupEntry(bgTypeId); - //in previous method is checked if exists entry in sBattlemasterListStore, so no check needed - if (bl) - { - bg->SetArenaorBGType(bl->type == TYPE_ARENA); - } - bg->SetTypeID(bgTypeId); - bg->SetInstanceID(0); // template bg, instance id is 0 + bg->SetInstanceID(0); + bg->SetArenaorBGType(IsArena); bg->SetMinPlayersPerTeam(MinPlayersPerTeam); bg->SetMaxPlayersPerTeam(MaxPlayersPerTeam); - bg->SetMinPlayers(MinPlayersPerTeam*2); - bg->SetMaxPlayers(MaxPlayersPerTeam*2); + bg->SetMinPlayers(MinPlayersPerTeam * 2); + bg->SetMaxPlayers(MaxPlayersPerTeam * 2); bg->SetName(BattleGroundName); bg->SetTeamStartLoc(ALLIANCE, Team1StartLocX, Team1StartLocY, Team1StartLocZ, Team1StartLocO); bg->SetTeamStartLoc(HORDE, Team2StartLocX, Team2StartLocY, Team2StartLocZ, Team2StartLocO); bg->SetLevelRange(LevelMin, LevelMax); - //add BattleGround instance to FreeSlotQueue (.back() will return the template!) - bg->AddToBGFreeSlotQueue(); - - // do NOT add to update list, since this is a template battleground! + // add bg to update list + AddBattleGround(bg->GetInstanceID(), bg->GetTypeID(), bg); // return some not-null value, bgTypeId is good enough for me return bgTypeId; @@ -1432,13 +1640,14 @@ void BattleGroundMgr::CreateInitialBattleGrounds() uint32 MaxPlayersPerTeam, MinPlayersPerTeam, MinLvl, MaxLvl, start1, start2; BattlemasterListEntry const *bl; WorldSafeLocsEntry const *start; + bool IsArena; uint32 count = 0; // 0 1 2 3 4 5 6 7 8 QueryResult *result = WorldDatabase.Query("SELECT id, MinPlayersPerTeam,MaxPlayersPerTeam,MinLvl,MaxLvl,AllianceStartLoc,AllianceStartO,HordeStartLoc,HordeStartO FROM battleground_template"); - if(!result) + if (!result) { barGoLink bar(1); @@ -1456,44 +1665,46 @@ void BattleGroundMgr::CreateInitialBattleGrounds() Field *fields = result->Fetch(); bar.step(); - uint32 bgTypeID = fields[0].GetUInt32(); + uint32 bgTypeID_ = fields[0].GetUInt32(); - // can be overwrited by values from DB - bl = sBattlemasterListStore.LookupEntry(bgTypeID); - if(!bl) + // can be overwrite by values from DB + bl = sBattlemasterListStore.LookupEntry(bgTypeID_); + if (!bl) { - sLog.outError("Battleground ID %u not found in BattlemasterList.dbc. Battleground not created.",bgTypeID); + sLog.outError("Battleground ID %u not found in BattlemasterList.dbc. Battleground not created.", bgTypeID_); continue; } - MaxPlayersPerTeam = bl->maxplayersperteam; - MinPlayersPerTeam = bl->maxplayersperteam/2; - MinLvl = bl->minlvl; - MaxLvl = bl->maxlvl; - - if(fields[1].GetUInt32()) - MinPlayersPerTeam = fields[1].GetUInt32(); + BattleGroundTypeId bgTypeID = BattleGroundTypeId(bgTypeID_); - if(fields[2].GetUInt32()) - MaxPlayersPerTeam = fields[2].GetUInt32(); - - if(fields[3].GetUInt32()) - MinLvl = fields[3].GetUInt32(); - - if(fields[4].GetUInt32()) - MaxLvl = fields[4].GetUInt32(); + IsArena = (bl->type == TYPE_ARENA); + MinPlayersPerTeam = fields[1].GetUInt32(); + MaxPlayersPerTeam = fields[2].GetUInt32(); + MinLvl = fields[3].GetUInt32(); + MaxLvl = fields[4].GetUInt32(); + //check values from DB + if (MaxPlayersPerTeam == 0 || MinPlayersPerTeam == 0 || MinPlayersPerTeam > MaxPlayersPerTeam) + { + MaxPlayersPerTeam = bl->maxplayersperteam; + MinPlayersPerTeam = bl->maxplayersperteam / 2; + } + if (MinLvl == 0 || MaxLvl == 0 || MinLvl > MaxLvl) + { + MinLvl = bl->minlvl; + MaxLvl = bl->maxlvl; + } start1 = fields[5].GetUInt32(); start = sWorldSafeLocsStore.LookupEntry(start1); - if(start) + if (start) { AStartLoc[0] = start->x; AStartLoc[1] = start->y; AStartLoc[2] = start->z; AStartLoc[3] = fields[6].GetFloat(); } - else if(bgTypeID == BATTLEGROUND_AA) + else if (bgTypeID == BATTLEGROUND_AA) { AStartLoc[0] = 0; AStartLoc[1] = 0; @@ -1502,21 +1713,21 @@ void BattleGroundMgr::CreateInitialBattleGrounds() } else { - sLog.outErrorDb("Table `battleground_template` for id %u have non-existed WorldSafeLocs.dbc id %u in field `AllianceStartLoc`. BG not created.",bgTypeID,start1); + sLog.outErrorDb("Table `battleground_template` for id %u have non-existed WorldSafeLocs.dbc id %u in field `AllianceStartLoc`. BG not created.", bgTypeID, start1); continue; } start2 = fields[7].GetUInt32(); start = sWorldSafeLocsStore.LookupEntry(start2); - if(start) + if (start) { HStartLoc[0] = start->x; HStartLoc[1] = start->y; HStartLoc[2] = start->z; HStartLoc[3] = fields[8].GetFloat(); } - else if(bgTypeID == BATTLEGROUND_AA) + else if (bgTypeID == BATTLEGROUND_AA) { HStartLoc[0] = 0; HStartLoc[1] = 0; @@ -1525,12 +1736,12 @@ void BattleGroundMgr::CreateInitialBattleGrounds() } else { - sLog.outErrorDb("Table `battleground_template` for id %u have non-existed WorldSafeLocs.dbc id %u in field `HordeStartLoc`. BG not created.",bgTypeID,start2); + sLog.outErrorDb("Table `battleground_template` for id %u have non-existed WorldSafeLocs.dbc id %u in field `HordeStartLoc`. BG not created.", bgTypeID, start2); continue; } //sLog.outDetail("Creating battleground %s, %u-%u", bl->name[sWorld.GetDBClang()], MinLvl, MaxLvl); - if(!CreateBattleGround(bgTypeID, MinPlayersPerTeam, MaxPlayersPerTeam, MinLvl, MaxLvl, bl->name[sWorld.GetDefaultDbcLocale()], bl->mapid[0], AStartLoc[0], AStartLoc[1], AStartLoc[2], AStartLoc[3], HStartLoc[0], HStartLoc[1], HStartLoc[2], HStartLoc[3])) + if (!CreateBattleGround(bgTypeID, IsArena, MinPlayersPerTeam, MaxPlayersPerTeam, MinLvl, MaxLvl, bl->name[sWorld.GetDefaultDbcLocale()], bl->mapid[0], AStartLoc[0], AStartLoc[1], AStartLoc[2], AStartLoc[3], HStartLoc[0], HStartLoc[1], HStartLoc[2], HStartLoc[3])) continue; ++count; @@ -1544,11 +1755,11 @@ void BattleGroundMgr::CreateInitialBattleGrounds() void BattleGroundMgr::InitAutomaticArenaPointDistribution() { - if(m_AutoDistributePoints) + if (sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS)) { sLog.outDebug("Initializing Automatic Arena Point Distribution"); QueryResult * result = CharacterDatabase.Query("SELECT NextArenaPointDistributionTime FROM saved_variables"); - if(!result) + if (!result) { sLog.outDebug("Battleground: Next arena point distribution time not found in SavedVariables, reseting it now."); m_NextAutoDistributionTime = time(NULL) + BATTLEGROUND_ARENA_POINT_DISTRIBUTION_DAY * sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS); @@ -1566,9 +1777,9 @@ void BattleGroundMgr::InitAutomaticArenaPointDistribution() void BattleGroundMgr::DistributeArenaPoints() { // used to distribute arena points based on last week's stats - sWorld.SendGlobalText("Flushing Arena points based on team ratings, this may take a few minutes. Please stand by...", NULL); + sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_START); - sWorld.SendGlobalText("Distributing arena points to players...", NULL); + sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_ONLINE_START); //temporary structure for storing maximum points to add values for all players std::map<uint32, uint32> PlayerPoints; @@ -1576,7 +1787,7 @@ void BattleGroundMgr::DistributeArenaPoints() //at first update all points for all team members for(ObjectMgr::ArenaTeamMap::iterator team_itr = objmgr.GetArenaTeamMapBegin(); team_itr != objmgr.GetArenaTeamMapEnd(); ++team_itr) { - if(ArenaTeam * at = team_itr->second) + if (ArenaTeam * at = team_itr->second) { at->UpdateArenaPointsHelper(PlayerPoints); } @@ -1586,7 +1797,7 @@ void BattleGroundMgr::DistributeArenaPoints() for (std::map<uint32, uint32>::iterator plr_itr = PlayerPoints.begin(); plr_itr != PlayerPoints.end(); ++plr_itr) { //update to database - CharacterDatabase.PExecute("UPDATE characters SET arena_pending_points = '%u' WHERE `guid` = '%u'", plr_itr->second, plr_itr->first); + CharacterDatabase.PExecute("UPDATE characters SET arena_pending_points = '%u' WHERE guid = '%u'", plr_itr->second, plr_itr->first); //add points if player is online Player* pl = objmgr.GetPlayer(plr_itr->first); if (pl) @@ -1595,12 +1806,12 @@ void BattleGroundMgr::DistributeArenaPoints() PlayerPoints.clear(); - sWorld.SendGlobalText("Finished setting arena points for online players.", NULL); + sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_ONLINE_END); - sWorld.SendGlobalText("Modifying played count, arena points etc. for loaded arena teams, sending updated stats to online players...", NULL); + sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_TEAM_START); for(ObjectMgr::ArenaTeamMap::iterator titr = objmgr.GetArenaTeamMapBegin(); titr != objmgr.GetArenaTeamMapEnd(); ++titr) { - if(ArenaTeam * at = titr->second) + if (ArenaTeam * at = titr->second) { at->FinishWeek(); // set played this week etc values to 0 in memory, too at->SaveToDB(); // save changes @@ -1608,16 +1819,16 @@ void BattleGroundMgr::DistributeArenaPoints() } } - sWorld.SendGlobalText("Modification done.", NULL); + sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_TEAM_END); - sWorld.SendGlobalText("Done flushing Arena points.", NULL); + sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_END); } -void BattleGroundMgr::BuildBattleGroundListPacket(WorldPacket *data, uint64 guid, Player* plr, uint32 bgTypeId) +void BattleGroundMgr::BuildBattleGroundListPacket(WorldPacket *data, const uint64& guid, Player* plr, BattleGroundTypeId bgTypeId) { uint32 PlayerLevel = 10; - if(plr) + if (plr) PlayerLevel = plr->getLevel(); data->Initialize(SMSG_BATTLEFIELD_LIST); @@ -1636,27 +1847,25 @@ void BattleGroundMgr::BuildBattleGroundListPacket(WorldPacket *data, uint64 guid uint32 count = 0; *data << uint32(0x00); // number of bg instances - for(std::map<uint32, BattleGround*>::iterator itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); ++itr) + uint32 queue_id = plr->GetBattleGroundQueueIdFromLevel(bgTypeId); + for(std::set<uint32>::iterator itr = m_ClientBattleGroundIds[bgTypeId][queue_id].begin(); itr != m_ClientBattleGroundIds[bgTypeId][queue_id].end();++itr) { - if(itr->second->GetTypeID() == bgTypeId && (PlayerLevel >= itr->second->GetMinLevel()) && (PlayerLevel <= itr->second->GetMaxLevel())) - { - *data << uint32(itr->second->GetInstanceID()); - ++count; - } + *data << uint32(*itr); + ++count; } data->put<uint32>( count_pos , count); } } -void BattleGroundMgr::SendToBattleGround(Player *pl, uint32 instanceId) +void BattleGroundMgr::SendToBattleGround(Player *pl, uint32 instanceId, BattleGroundTypeId bgTypeId) { - BattleGround *bg = GetBattleGround(instanceId); - if(bg) + BattleGround *bg = GetBattleGround(instanceId, bgTypeId); + if (bg) { uint32 mapid = bg->GetMapId(); float x, y, z, O; uint32 team = pl->GetBGTeam(); - if(team==0) + if (team==0) team = pl->GetTeam(); bg->GetTeamStartLoc(team, x, y, z, O); @@ -1669,24 +1878,17 @@ void BattleGroundMgr::SendToBattleGround(Player *pl, uint32 instanceId) } } -void BattleGroundMgr::SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *bg, uint64 guid) +void BattleGroundMgr::SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *bg, const uint64& guid) { WorldPacket data(SMSG_AREA_SPIRIT_HEALER_TIME, 12); uint32 time_ = 30000 - bg->GetLastResurrectTime(); // resurrect every 30 seconds - if(time_ == uint32(-1)) + if (time_ == uint32(-1)) time_ = 0; data << guid << time_; pl->GetSession()->SendPacket(&data); } -void BattleGroundMgr::RemoveBattleGround(uint32 instanceID) -{ - BattleGroundSet::iterator itr = m_BattleGrounds.find(instanceID); - if(itr!=m_BattleGrounds.end()) - m_BattleGrounds.erase(itr); -} - -bool BattleGroundMgr::IsArenaType(uint32 bgTypeId) const +bool BattleGroundMgr::IsArenaType(BattleGroundTypeId bgTypeId) { return ( bgTypeId == BATTLEGROUND_AA || bgTypeId == BATTLEGROUND_BE || @@ -1694,96 +1896,169 @@ bool BattleGroundMgr::IsArenaType(uint32 bgTypeId) const bgTypeId == BATTLEGROUND_RL ); } -bool BattleGroundMgr::IsBattleGroundType(uint32 bgTypeId) const -{ - return !IsArenaType(bgTypeId); -} - -uint32 BattleGroundMgr::BGQueueTypeId(uint32 bgTypeId, uint8 arenaType) const +BattleGroundQueueTypeId BattleGroundMgr::BGQueueTypeId(BattleGroundTypeId bgTypeId, uint8 arenaType) { switch(bgTypeId) { - case BATTLEGROUND_WS: - return BATTLEGROUND_QUEUE_WS; - case BATTLEGROUND_AB: - return BATTLEGROUND_QUEUE_AB; - case BATTLEGROUND_AV: - return BATTLEGROUND_QUEUE_AV; - case BATTLEGROUND_EY: - return BATTLEGROUND_QUEUE_EY; - case BATTLEGROUND_AA: - case BATTLEGROUND_NA: - case BATTLEGROUND_RL: - case BATTLEGROUND_BE: - switch(arenaType) - { - case ARENA_TYPE_2v2: - return BATTLEGROUND_QUEUE_2v2; - case ARENA_TYPE_3v3: - return BATTLEGROUND_QUEUE_3v3; - case ARENA_TYPE_5v5: - return BATTLEGROUND_QUEUE_5v5; + case BATTLEGROUND_WS: + return BATTLEGROUND_QUEUE_WS; + case BATTLEGROUND_AB: + return BATTLEGROUND_QUEUE_AB; + case BATTLEGROUND_AV: + return BATTLEGROUND_QUEUE_AV; + case BATTLEGROUND_EY: + return BATTLEGROUND_QUEUE_EY; + case BATTLEGROUND_SA: + return BATTLEGROUND_QUEUE_SA; + case BATTLEGROUND_AA: + case BATTLEGROUND_NA: + case BATTLEGROUND_RL: + case BATTLEGROUND_BE: + case BATTLEGROUND_DS: + case BATTLEGROUND_RV: + switch(arenaType) + { + case ARENA_TYPE_2v2: + return BATTLEGROUND_QUEUE_2v2; + case ARENA_TYPE_3v3: + return BATTLEGROUND_QUEUE_3v3; + case ARENA_TYPE_5v5: + return BATTLEGROUND_QUEUE_5v5; + default: + return BATTLEGROUND_QUEUE_NONE; + } default: - return 0; - } - default: - return 0; + return BATTLEGROUND_QUEUE_NONE; } } -uint32 BattleGroundMgr::BGTemplateId(uint32 bgQueueTypeId) const +BattleGroundTypeId BattleGroundMgr::BGTemplateId(BattleGroundQueueTypeId bgQueueTypeId) { switch(bgQueueTypeId) { - case BATTLEGROUND_QUEUE_WS: - return BATTLEGROUND_WS; - case BATTLEGROUND_QUEUE_AB: - return BATTLEGROUND_AB; - case BATTLEGROUND_QUEUE_AV: - return BATTLEGROUND_AV; - case BATTLEGROUND_QUEUE_EY: - return BATTLEGROUND_EY; - case BATTLEGROUND_QUEUE_2v2: - case BATTLEGROUND_QUEUE_3v3: - case BATTLEGROUND_QUEUE_5v5: - return BATTLEGROUND_AA; - default: - return 0; + case BATTLEGROUND_QUEUE_WS: + return BATTLEGROUND_WS; + case BATTLEGROUND_QUEUE_AB: + return BATTLEGROUND_AB; + case BATTLEGROUND_QUEUE_AV: + return BATTLEGROUND_AV; + case BATTLEGROUND_QUEUE_EY: + return BATTLEGROUND_EY; + case BATTLEGROUND_QUEUE_SA: + return BATTLEGROUND_SA; + case BATTLEGROUND_QUEUE_2v2: + case BATTLEGROUND_QUEUE_3v3: + case BATTLEGROUND_QUEUE_5v5: + return BATTLEGROUND_AA; + default: + return BattleGroundTypeId(0); // used for unknown template (it existed and do nothing) } } -uint8 BattleGroundMgr::BGArenaType(uint32 bgQueueTypeId) const +uint8 BattleGroundMgr::BGArenaType(BattleGroundQueueTypeId bgQueueTypeId) { switch(bgQueueTypeId) { - case BATTLEGROUND_QUEUE_2v2: - return ARENA_TYPE_2v2; - case BATTLEGROUND_QUEUE_3v3: - return ARENA_TYPE_3v3; - case BATTLEGROUND_QUEUE_5v5: - return ARENA_TYPE_5v5; - default: - return 0; + case BATTLEGROUND_QUEUE_2v2: + return ARENA_TYPE_2v2; + case BATTLEGROUND_QUEUE_3v3: + return ARENA_TYPE_3v3; + case BATTLEGROUND_QUEUE_5v5: + return ARENA_TYPE_5v5; + default: + return 0; } } +void BattleGroundMgr::ToggleTesting() +{ + m_Testing = !m_Testing; + if (m_Testing) + sWorld.SendWorldText(LANG_DEBUG_BG_ON); + else + sWorld.SendWorldText(LANG_DEBUG_BG_OFF); +} + void BattleGroundMgr::ToggleArenaTesting() { m_ArenaTesting = !m_ArenaTesting; - if(m_ArenaTesting) - sWorld.SendGlobalText("Arenas are set to 1v1 for debugging. So, don't join as group.", NULL); + if (m_ArenaTesting) + sWorld.SendWorldText(LANG_DEBUG_ARENA_ON); else - sWorld.SendGlobalText("Arenas are set to normal playercount.", NULL); + sWorld.SendWorldText(LANG_DEBUG_ARENA_OFF); } void BattleGroundMgr::SetHolidayWeekends(uint32 mask) { - for(uint32 bgtype = 1; bgtype <= 8; ++bgtype) + for(uint32 bgtype = 1; bgtype < MAX_BATTLEGROUND_TYPE_ID; ++bgtype) { - if(BattleGround * bg = GetBattleGroundTemplate(bgtype)) + if(BattleGround * bg = GetBattleGroundTemplate(BattleGroundTypeId(bgtype))) { bg->SetHoliday(mask & (1 << bgtype)); } } } +uint32 BattleGroundMgr::GetMaxRatingDifference() const +{ + // this is for stupid people who can't use brain and set max rating difference to 0 + uint32 diff = sWorld.getConfig(CONFIG_ARENA_MAX_RATING_DIFFERENCE); + if (diff == 0) + diff = 5000; + return diff; +} + +uint32 BattleGroundMgr::GetRatingDiscardTimer() const +{ + return sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER); +} + +uint32 BattleGroundMgr::GetPrematureFinishTime() const +{ + return sWorld.getConfig(CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER); +} + +void BattleGroundMgr::LoadBattleMastersEntry() +{ + mBattleMastersMap.clear(); // need for reload case + + QueryResult *result = WorldDatabase.Query( "SELECT entry,bg_template FROM battlemaster_entry" ); + + uint32 count = 0; + + if (!result) + { + barGoLink bar( 1 ); + bar.step(); + + sLog.outString(); + sLog.outString( ">> Loaded 0 battlemaster entries - table is empty!" ); + return; + } + + barGoLink bar( result->GetRowCount() ); + + do + { + ++count; + bar.step(); + + Field *fields = result->Fetch(); + + uint32 entry = fields[0].GetUInt32(); + uint32 bgTypeId = fields[1].GetUInt32(); + if (!sBattlemasterListStore.LookupEntry(bgTypeId)) + { + sLog.outErrorDb("Table `battlemaster_entry` contain entry %u for not existed battleground type %u, ignored.",entry,bgTypeId); + continue; + } + + mBattleMastersMap[entry] = BattleGroundTypeId(bgTypeId); + + } while( result->NextRow() ); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u battlemaster entries", count ); +} diff --git a/src/game/BattleGroundMgr.h b/src/game/BattleGroundMgr.h index 5edcc1457d3..889b8b6f4fe 100644 --- a/src/game/BattleGroundMgr.h +++ b/src/game/BattleGroundMgr.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,30 +21,23 @@ #ifndef __BATTLEGROUNDMGR_H #define __BATTLEGROUNDMGR_H -#include "BattleGround.h" +#include "Common.h" #include "Policies/Singleton.h" +#include "BattleGround.h" -class BattleGround; - -//TODO it is not possible to have this structure, because we should have BattlegroundSet for each queue -//so i propose to change this type to array 1..MAX_BATTLEGROUND_TYPES of sets or maps.. typedef std::map<uint32, BattleGround*> BattleGroundSet; -//typedef std::map<uint32, BattleGroundQueue*> BattleGroundQueueSet; -typedef std::deque<BattleGround*> BGFreeSlotQueueType; -#define MAX_BATTLEGROUND_QUEUES 7 // for level ranges 10-19, 20-29, 30-39, 40-49, 50-59, 60-69, 70+ +//this container can't be deque, because deque doesn't like removing the last element - if you remove it, it invalidates next iterator and crash appears +typedef std::list<BattleGround*> BGFreeSlotQueueType; -#define MAX_BATTLEGROUND_TYPES 9 // each BG type will be in array +typedef UNORDERED_MAP<uint32, BattleGroundTypeId> BattleMastersMap; -#define MAX_BATTLEGROUND_QUEUE_TYPES 8 - -#define BATTLEGROUND_ARENA_POINT_DISTRIBUTION_DAY 86400 // seconds in a day +#define BATTLEGROUND_ARENA_POINT_DISTRIBUTION_DAY 86400 // seconds in a day +#define COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME 10 struct GroupQueueInfo; // type predefinition struct PlayerQueueInfo // stores information for players in queue { - uint32 InviteTime; // first invite time - uint32 LastInviteTime; // last invite time uint32 LastOnlineTime; // for tracking and removing offline players from queue after 5 minutes GroupQueueInfo * GroupInfo; // pointer to the associated groupqueueinfo }; @@ -53,16 +46,26 @@ struct GroupQueueInfo // stores informatio { std::map<uint64, PlayerQueueInfo*> Players; // player queue info map uint32 Team; // Player team (ALLIANCE/HORDE) - uint32 BgTypeId; // battleground type id + BattleGroundTypeId BgTypeId; // battleground type id bool IsRated; // rated uint8 ArenaType; // 2v2, 3v3, 5v5 or 0 when BG uint32 ArenaTeamId; // team id if rated match uint32 JoinTime; // time when group was added + uint32 RemoveInviteTime; // time when we will remove invite for players in group uint32 IsInvitedToBGInstanceGUID; // was invited to certain BG uint32 ArenaTeamRating; // if rated match, inited to the rating of the team uint32 OpponentsTeamRating; // for rated arena matches }; +enum BattleGroundQueueGroupTypes +{ + BG_QUEUE_PREMADE_ALLIANCE = 0, + BG_QUEUE_PREMADE_HORDE = 1, + BG_QUEUE_NORMAL_ALLIANCE = 2, + BG_QUEUE_NORMAL_HORDE = 3 +}; +#define BG_QUEUE_GROUP_TYPES_COUNT 4 + class BattleGround; class BattleGroundQueue { @@ -70,64 +73,61 @@ class BattleGroundQueue BattleGroundQueue(); ~BattleGroundQueue(); - void Update(uint32 bgTypeId, uint32 queue_id, uint8 arenatype = 0, bool isRated = false, uint32 minRating = 0); + void Update(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLevel queue_id, uint8 arenaType = 0, bool isRated = false, uint32 minRating = 0); - GroupQueueInfo * AddGroup(Player * leader, uint32 BgTypeId, uint8 ArenaType, bool isRated, uint32 ArenaRating, uint32 ArenaTeamId = 0); + void FillPlayersToBG(BattleGround* bg, BGQueueIdBasedOnLevel queue_id); + bool CheckPremadeMatch(BGQueueIdBasedOnLevel queue_id, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam); + bool CheckNormalMatch(BattleGround* bg_template, BGQueueIdBasedOnLevel queue_id, uint32 minPlayers, uint32 maxPlayers); + bool CheckSkirmishForSameFaction(BGQueueIdBasedOnLevel queue_id, uint32 minPlayersPerTeam); + GroupQueueInfo * AddGroup(Player * leader, BattleGroundTypeId bgTypeId, uint8 ArenaType, bool isRated, bool isPremade, uint32 ArenaRating, uint32 ArenaTeamId = 0); void AddPlayer(Player *plr, GroupQueueInfo *ginfo); - void RemovePlayer(uint64 guid, bool decreaseInvitedCount); + void RemovePlayer(const uint64& guid, bool decreaseInvitedCount); + void PlayerInvitedToBGUpdateAverageWaitTime(GroupQueueInfo* ginfo, BGQueueIdBasedOnLevel queue_id); + uint32 GetAverageQueueWaitTime(GroupQueueInfo* ginfo, BGQueueIdBasedOnLevel queue_id); + void DecreaseGroupLength(uint32 queueId, uint32 AsGroup); - void BGEndedRemoveInvites(BattleGround * bg); + void AnnounceWorld(GroupQueueInfo *ginfo, const uint64& playerGUID, bool isAddedToQueue); typedef std::map<uint64, PlayerQueueInfo> QueuedPlayersMap; - QueuedPlayersMap m_QueuedPlayers[MAX_BATTLEGROUND_QUEUES]; - - typedef std::list<GroupQueueInfo*> QueuedGroupsList; - QueuedGroupsList m_QueuedGroups[MAX_BATTLEGROUND_QUEUES]; - - // class to hold pointers to the groups eligible for a specific selection pool building mode - class EligibleGroups : public std::list<GroupQueueInfo *> - { - public: - void Init(QueuedGroupsList * source, uint32 BgTypeId, uint32 side, uint32 MaxPlayers, uint8 ArenaType = 0, bool IsRated = false, uint32 MinRating = 0, uint32 MaxRating = 0, uint32 DisregardTime = 0, uint32 excludeTeam = 0); - }; - - EligibleGroups m_EligibleGroups; + QueuedPlayersMap m_QueuedPlayers; + + //we need constant add to begin and constant remove / add from the end, therefore deque suits our problem well + typedef std::list<GroupQueueInfo*> GroupsQueueType; + + /* + This two dimensional array is used to store All queued groups + First dimension specifies the bgTypeId + Second dimension specifies the player's group types - + BG_QUEUE_PREMADE_ALLIANCE is used for premade alliance groups and alliance rated arena teams + BG_QUEUE_PREMADE_HORDE is used for premade horde groups and horde rated arena teams + BG_QUEUE_NORMAL_ALLIANCE is used for normal (or small) alliance groups or non-rated arena matches + BG_QUEUE_NORMAL_HORDE is used for normal (or small) horde groups or non-rated arena matches + */ + GroupsQueueType m_QueuedGroups[MAX_BATTLEGROUND_QUEUES][BG_QUEUE_GROUP_TYPES_COUNT]; // class to select and invite groups to bg class SelectionPool { public: - void Init(EligibleGroups * curr); - void AddGroup(GroupQueueInfo * group); - void RemoveGroup(GroupQueueInfo * group); + void Init(); + bool AddGroup(GroupQueueInfo *ginfo, uint32 desiredCount); + bool KickGroup(uint32 size); uint32 GetPlayerCount() const {return PlayerCount;} - bool Build(uint32 MinPlayers, uint32 MaxPlayers, EligibleGroups::iterator startitr); public: - std::list<GroupQueueInfo *> SelectedGroups; + GroupsQueueType SelectedGroups; private: uint32 PlayerCount; - EligibleGroups * m_CurrEligGroups; }; - enum SelectionPoolBuildMode - { - NORMAL_ALLIANCE, - NORMAL_HORDE, - ONESIDE_ALLIANCE_TEAM1, - ONESIDE_ALLIANCE_TEAM2, - ONESIDE_HORDE_TEAM1, - ONESIDE_HORDE_TEAM2, - - NUM_SELECTION_POOL_TYPES - }; - - SelectionPool m_SelectionPools[NUM_SELECTION_POOL_TYPES]; - - bool BuildSelectionPool(uint32 bgTypeId, uint32 queue_id, uint32 MinPlayers, uint32 MaxPlayers, SelectionPoolBuildMode mode, uint8 ArenaType = 0, bool isRated = false, uint32 MinRating = 0, uint32 MaxRating = 0, uint32 DisregardTime = 0, uint32 excludeTeam = 0); + //one selection pool for horde, other one for alliance + SelectionPool m_SelectionPools[BG_TEAMS_COUNT]; private: bool InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * bg, uint32 side); + uint32 m_WaitTimes[BG_TEAMS_COUNT][MAX_BATTLEGROUND_QUEUES][COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME]; + uint32 m_WaitTimeLastPlayer[BG_TEAMS_COUNT][MAX_BATTLEGROUND_QUEUES]; + uint32 m_SumOfWaitTimes[BG_TEAMS_COUNT][MAX_BATTLEGROUND_QUEUES]; }; /* @@ -137,7 +137,10 @@ class BattleGroundQueue class BGQueueInviteEvent : public BasicEvent { public: - BGQueueInviteEvent(uint64 pl_guid, uint32 BgInstanceGUID) : m_PlayerGuid(pl_guid), m_BgInstanceGUID(BgInstanceGUID) {}; + BGQueueInviteEvent(const uint64& pl_guid, uint32 BgInstanceGUID, BattleGroundTypeId BgTypeId, uint32 removeTime) : + m_PlayerGuid(pl_guid), m_BgInstanceGUID(BgInstanceGUID), m_BgTypeId(BgTypeId), m_RemoveTime(removeTime) + { + }; virtual ~BGQueueInviteEvent() {}; virtual bool Execute(uint64 e_time, uint32 p_time); @@ -145,15 +148,22 @@ class BGQueueInviteEvent : public BasicEvent private: uint64 m_PlayerGuid; uint32 m_BgInstanceGUID; + uint32 m_RemoveTime; + BattleGroundTypeId m_BgTypeId; }; /* - This class is used to remove player from BG queue after 2 minutes from first invitation + This class is used to remove player from BG queue after 1 minute 20 seconds from first invitation + We must store removeInvite time in case player left queue and joined and is invited again + We must store bgQueueTypeId, because battleground can be deleted already, when player entered it */ class BGQueueRemoveEvent : public BasicEvent { public: - BGQueueRemoveEvent(uint64 pl_guid, uint32 bgInstanceGUID, uint32 playersTeam) : m_PlayerGuid(pl_guid), m_BgInstanceGUID(bgInstanceGUID), m_PlayersTeam(playersTeam) {}; + BGQueueRemoveEvent(const uint64& pl_guid, uint32 bgInstanceGUID, BattleGroundTypeId BgTypeId, BattleGroundQueueTypeId bgQueueTypeId, uint32 removeTime) : + m_PlayerGuid(pl_guid), m_BgInstanceGUID(bgInstanceGUID), m_BgTypeId(BgTypeId), m_BgQueueTypeId(bgQueueTypeId), m_RemoveTime(removeTime) + { + }; virtual ~BGQueueRemoveEvent() {}; virtual bool Execute(uint64 e_time, uint32 p_time); @@ -161,7 +171,9 @@ class BGQueueRemoveEvent : public BasicEvent private: uint64 m_PlayerGuid; uint32 m_BgInstanceGUID; - uint32 m_PlayersTeam; + uint32 m_RemoveTime; + BattleGroundTypeId m_BgTypeId; + BattleGroundQueueTypeId m_BgQueueTypeId; }; class BattleGroundMgr @@ -170,83 +182,81 @@ class BattleGroundMgr /* Construction */ BattleGroundMgr(); ~BattleGroundMgr(); - void Update(time_t diff); + void Update(uint32 diff); /* Packet Building */ void BuildPlayerJoinedBattleGroundPacket(WorldPacket *data, Player *plr); - void BuildPlayerLeftBattleGroundPacket(WorldPacket *data, Player *plr); - void BuildBattleGroundListPacket(WorldPacket *data, uint64 guid, Player *plr, uint32 bgTypeId); - void BuildGroupJoinedBattlegroundPacket(WorldPacket *data, uint32 bgTypeId); + void BuildPlayerLeftBattleGroundPacket(WorldPacket *data, const uint64& guid); + void BuildBattleGroundListPacket(WorldPacket *data, const uint64& guid, Player *plr, BattleGroundTypeId bgTypeId); + void BuildGroupJoinedBattlegroundPacket(WorldPacket *data, BattleGroundTypeId bgTypeId); void BuildUpdateWorldStatePacket(WorldPacket *data, uint32 field, uint32 value); void BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg); - void BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint32 team, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint32 arenatype = 0, uint8 israted = 0); + void BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint8 arenatype); void BuildPlaySoundPacket(WorldPacket *data, uint32 soundid); - - /* Player invitation */ - // called from Queue update, or from Addplayer to queue - void InvitePlayer(Player* plr, uint32 bgInstanceGUID, uint32 team); + void SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *bg, const uint64& guid); /* Battlegrounds */ - BattleGroundSet::iterator GetBattleGroundsBegin() { return m_BattleGrounds.begin(); }; - BattleGroundSet::iterator GetBattleGroundsEnd() { return m_BattleGrounds.end(); }; + BattleGround* GetBattleGroundThroughClientInstance(uint32 instanceId, BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLevel queue_id); + BattleGround* GetBattleGround(uint32 InstanceID, BattleGroundTypeId bgTypeId); //there must be uint32 because MAX_BATTLEGROUND_TYPE_ID means unknown - BattleGround* GetBattleGround(uint32 ID) - { - BattleGroundSet::iterator i = m_BattleGrounds.find(ID); - if(i != m_BattleGrounds.end()) - return i->second; - else - return NULL; - }; - - BattleGround * GetBattleGroundTemplate(uint32 bgTypeId); - BattleGround * CreateNewBattleGround(uint32 bgTypeId); + BattleGround* GetBattleGroundTemplate(BattleGroundTypeId bgTypeId); + BattleGround* CreateNewBattleGround(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLevel queue_id, uint8 arenaType, bool isRated); - uint32 CreateBattleGround(uint32 bgTypeId, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam, uint32 LevelMin, uint32 LevelMax, char* BattleGroundName, uint32 MapID, float Team1StartLocX, float Team1StartLocY, float Team1StartLocZ, float Team1StartLocO, float Team2StartLocX, float Team2StartLocY, float Team2StartLocZ, float Team2StartLocO); + uint32 CreateBattleGround(BattleGroundTypeId bgTypeId, bool IsArena, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam, uint32 LevelMin, uint32 LevelMax, char* BattleGroundName, uint32 MapID, float Team1StartLocX, float Team1StartLocY, float Team1StartLocZ, float Team1StartLocO, float Team2StartLocX, float Team2StartLocY, float Team2StartLocZ, float Team2StartLocO); - inline void AddBattleGround(uint32 ID, BattleGround* BG) { m_BattleGrounds[ID] = BG; }; - void RemoveBattleGround(uint32 instanceID); + void AddBattleGround(uint32 InstanceID, BattleGroundTypeId bgTypeId, BattleGround* BG) { m_BattleGrounds[bgTypeId][InstanceID] = BG; }; + void RemoveBattleGround(uint32 instanceID, BattleGroundTypeId bgTypeId) { m_BattleGrounds[bgTypeId].erase(instanceID); } + uint32 CreateClientVisibleInstanceId(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLevel queue_id); void CreateInitialBattleGrounds(); + void DeleteAllBattleGrounds(); - void SendToBattleGround(Player *pl, uint32 bgTypeId); + void SendToBattleGround(Player *pl, uint32 InstanceID, BattleGroundTypeId bgTypeId); /* Battleground queues */ //these queues are instantiated when creating BattlegroundMrg BattleGroundQueue m_BattleGroundQueues[MAX_BATTLEGROUND_QUEUE_TYPES]; // public, because we need to access them in BG handler code - BGFreeSlotQueueType BGFreeSlotQueue[MAX_BATTLEGROUND_TYPES]; + BGFreeSlotQueueType BGFreeSlotQueue[MAX_BATTLEGROUND_TYPE_ID]; - void SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *bg, uint64 guid); - - bool IsArenaType(uint32 bgTypeId) const; - bool IsBattleGroundType(uint32 bgTypeId) const; - uint32 BGQueueTypeId(uint32 bgTypeId, uint8 arenaType) const; - uint32 BGTemplateId(uint32 bgQueueTypeId) const; - uint8 BGArenaType(uint32 bgQueueTypeId) const; - - uint32 GetMaxRatingDifference() const {return m_MaxRatingDifference;} - uint32 GetRatingDiscardTimer() const {return m_RatingDiscardTimer;} + uint32 GetMaxRatingDifference() const; + uint32 GetRatingDiscardTimer() const; + uint32 GetPrematureFinishTime() const; void InitAutomaticArenaPointDistribution(); void DistributeArenaPoints(); - uint32 GetPrematureFinishTime() const {return m_PrematureFinishTimer;} void ToggleArenaTesting(); - const bool isArenaTesting() const { return m_ArenaTesting; } + void ToggleTesting(); void SetHolidayWeekends(uint32 mask); + void LoadBattleMastersEntry(); + BattleGroundTypeId GetBattleMasterBG(uint32 entry) const + { + BattleMastersMap::const_iterator itr = mBattleMastersMap.find(entry); + if (itr != mBattleMastersMap.end()) + return itr->second; + return BATTLEGROUND_WS; + } + + bool isArenaTesting() const { return m_ArenaTesting; } + bool isTesting() const { return m_Testing; } + + static bool IsArenaType(BattleGroundTypeId bgTypeId); + static bool IsBattleGroundType(BattleGroundTypeId bgTypeId) { return !BattleGroundMgr::IsArenaType(bgTypeId); } + static BattleGroundQueueTypeId BGQueueTypeId(BattleGroundTypeId bgTypeId, uint8 arenaType); + static BattleGroundTypeId BGTemplateId(BattleGroundQueueTypeId bgQueueTypeId); + static uint8 BGArenaType(BattleGroundQueueTypeId bgQueueTypeId); private: + BattleMastersMap mBattleMastersMap; /* Battlegrounds */ - BattleGroundSet m_BattleGrounds; - uint32 m_MaxRatingDifference; - uint32 m_RatingDiscardTimer; + BattleGroundSet m_BattleGrounds[MAX_BATTLEGROUND_TYPE_ID]; + std::set<uint32> m_ClientBattleGroundIds[MAX_BATTLEGROUND_TYPE_ID][MAX_BATTLEGROUND_QUEUES]; //the instanceids just visible for the client uint32 m_NextRatingDiscardUpdate; - bool m_AutoDistributePoints; uint64 m_NextAutoDistributionTime; uint32 m_AutoDistributionTimeChecker; - uint32 m_PrematureFinishTimer; bool m_ArenaTesting; + bool m_Testing; }; #define sBattleGroundMgr Trinity::Singleton<BattleGroundMgr>::Instance() diff --git a/src/game/BattleGroundNA.cpp b/src/game/BattleGroundNA.cpp index 5debaa3c6a5..364ba972bec 100644 --- a/src/game/BattleGroundNA.cpp +++ b/src/game/BattleGroundNA.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,14 +22,23 @@ #include "Player.h" #include "BattleGround.h" #include "BattleGroundNA.h" -#include "Creature.h" #include "ObjectMgr.h" -#include "MapManager.h" +#include "WorldPacket.h" #include "Language.h" BattleGroundNA::BattleGroundNA() { m_BgObjects.resize(BG_NA_OBJECT_MAX); + + m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_1M; + m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_30S; + m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_15S; + m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE; + //we must set messageIds + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_ARENA_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_ARENA_THIRTY_SECONDS; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_ARENA_FIFTEEN_SECONDS; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_ARENA_HAS_BEGUN; } BattleGroundNA::~BattleGroundNA() @@ -37,74 +46,31 @@ BattleGroundNA::~BattleGroundNA() } -void BattleGroundNA::Update(time_t diff) +void BattleGroundNA::Update(uint32 diff) { BattleGround::Update(diff); - // after bg start we get there - if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize()) - { - ModifyStartDelayTime(diff); - - if (!(m_Events & 0x01)) - { - m_Events |= 0x01; - // setup here, only when at least one player has ported to the map - if(!SetupBattleGround()) - { - EndNow(); - return; - } - for(uint32 i = BG_NA_OBJECT_DOOR_1; i <= BG_NA_OBJECT_DOOR_4; i++) - SpawnBGObject(i, RESPAWN_IMMEDIATELY); - - SetStartDelayTime(START_DELAY1); - SendMessageToAll(LANG_ARENA_ONE_MINUTE); - } - // After 30 seconds, warning is signalled - else if (GetStartDelayTime() <= START_DELAY2 && !(m_Events & 0x04)) - { - m_Events |= 0x04; - SendMessageToAll(LANG_ARENA_THIRTY_SECONDS); - } - // After 15 seconds, warning is signalled - else if (GetStartDelayTime() <= START_DELAY3 && !(m_Events & 0x08)) - { - m_Events |= 0x08; - SendMessageToAll(LANG_ARENA_FIFTEEN_SECONDS); - } - // delay expired (1 minute) - else if (GetStartDelayTime() <= 0 && !(m_Events & 0x10)) - { - m_Events |= 0x10; - - for(uint32 i = BG_NA_OBJECT_DOOR_1; i <= BG_NA_OBJECT_DOOR_2; i++) - DoorOpen(i); - - for(uint32 i = BG_NA_OBJECT_BUFF_1; i <= BG_NA_OBJECT_BUFF_2; i++) - SpawnBGObject(i, 60); - - SendMessageToAll(LANG_ARENA_BEGUN); - SetStatus(STATUS_IN_PROGRESS); - SetStartDelayTime(0); - - for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) - if(Player *plr = objmgr.GetPlayer(itr->first)) - plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION); - - if(!GetPlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) - EndBattleGround(HORDE); - else if(GetPlayersCountByTeam(ALLIANCE) && !GetPlayersCountByTeam(HORDE)) - EndBattleGround(ALLIANCE); - } - } - - /*if(GetStatus() == STATUS_IN_PROGRESS) + /*if (GetStatus() == STATUS_IN_PROGRESS) { // update something }*/ } +void BattleGroundNA::StartingEventCloseDoors() +{ + for(uint32 i = BG_NA_OBJECT_DOOR_1; i <= BG_NA_OBJECT_DOOR_4; i++) + SpawnBGObject(i, RESPAWN_IMMEDIATELY); +} + +void BattleGroundNA::StartingEventOpenDoors() +{ + for(uint32 i = BG_NA_OBJECT_DOOR_1; i <= BG_NA_OBJECT_DOOR_2; i++) + DoorOpen(i); + + for(uint32 i = BG_NA_OBJECT_BUFF_1; i <= BG_NA_OBJECT_BUFF_2; i++) + SpawnBGObject(i, 60); +} + void BattleGroundNA::AddPlayer(Player *plr) { BattleGround::AddPlayer(plr); @@ -119,24 +85,21 @@ void BattleGroundNA::AddPlayer(Player *plr) void BattleGroundNA::RemovePlayer(Player* /*plr*/, uint64 /*guid*/) { - if(GetStatus() == STATUS_WAIT_LEAVE) + if (GetStatus() == STATUS_WAIT_LEAVE) return; UpdateWorldState(0xa0f, GetAlivePlayersCountByTeam(ALLIANCE)); UpdateWorldState(0xa10, GetAlivePlayersCountByTeam(HORDE)); - if(!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) - EndBattleGround(HORDE); - else if(GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE)) - EndBattleGround(ALLIANCE); + CheckArenaWinConditions(); } void BattleGroundNA::HandleKillPlayer(Player *player, Player *killer) { - if(GetStatus() != STATUS_IN_PROGRESS) + if (GetStatus() != STATUS_IN_PROGRESS) return; - if(!killer) + if (!killer) { sLog.outError("BattleGroundNA: Killer player not found"); return; @@ -147,16 +110,7 @@ void BattleGroundNA::HandleKillPlayer(Player *player, Player *killer) UpdateWorldState(0xa0f, GetAlivePlayersCountByTeam(ALLIANCE)); UpdateWorldState(0xa10, GetAlivePlayersCountByTeam(HORDE)); - if(!GetAlivePlayersCountByTeam(ALLIANCE)) - { - // all opponents killed - EndBattleGround(HORDE); - } - else if(!GetAlivePlayersCountByTeam(HORDE)) - { - // all opponents killed - EndBattleGround(ALLIANCE); - } + CheckArenaWinConditions(); } bool BattleGroundNA::HandlePlayerUnderMap(Player *player) @@ -167,7 +121,7 @@ bool BattleGroundNA::HandlePlayerUnderMap(Player *player) void BattleGroundNA::HandleAreaTrigger(Player *Source, uint32 Trigger) { - if(GetStatus() != STATUS_IN_PROGRESS) + if (GetStatus() != STATUS_IN_PROGRESS) return; //uint32 SpellId = 0; @@ -183,7 +137,7 @@ void BattleGroundNA::HandleAreaTrigger(Player *Source, uint32 Trigger) break; } - //if(buff_guid) + //if (buff_guid) // HandleTriggerBuff(buff_guid,Source); } @@ -194,15 +148,16 @@ void BattleGroundNA::FillInitialWorldStates(WorldPacket &data) data << uint32(0xa11) << uint32(1); // 9 } -void BattleGroundNA::ResetBGSubclass() +void BattleGroundNA::Reset() { - + //call parent's class reset + BattleGround::Reset(); } bool BattleGroundNA::SetupBattleGround() { // gates - if( !AddObject(BG_NA_OBJECT_DOOR_1, BG_NA_OBJECT_TYPE_DOOR_1, 4031.854, 2966.833, 12.6462, -2.648788, 0, 0, 0.9697962, -0.2439165, RESPAWN_IMMEDIATELY) + if (!AddObject(BG_NA_OBJECT_DOOR_1, BG_NA_OBJECT_TYPE_DOOR_1, 4031.854, 2966.833, 12.6462, -2.648788, 0, 0, 0.9697962, -0.2439165, RESPAWN_IMMEDIATELY) || !AddObject(BG_NA_OBJECT_DOOR_2, BG_NA_OBJECT_TYPE_DOOR_2, 4081.179, 2874.97, 12.39171, 0.4928045, 0, 0, 0.2439165, 0.9697962, RESPAWN_IMMEDIATELY) || !AddObject(BG_NA_OBJECT_DOOR_3, BG_NA_OBJECT_TYPE_DOOR_3, 4023.709, 2981.777, 10.70117, -2.648788, 0, 0, 0.9697962, -0.2439165, RESPAWN_IMMEDIATELY) || !AddObject(BG_NA_OBJECT_DOOR_4, BG_NA_OBJECT_TYPE_DOOR_4, 4090.064, 2858.438, 10.23631, 0.4928045, 0, 0, 0.2439165, 0.9697962, RESPAWN_IMMEDIATELY) diff --git a/src/game/BattleGroundNA.h b/src/game/BattleGroundNA.h index ae419b9fc44..56e2cf373c4 100644 --- a/src/game/BattleGroundNA.h +++ b/src/game/BattleGroundNA.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -58,15 +58,17 @@ class BattleGroundNA : public BattleGround public: BattleGroundNA(); ~BattleGroundNA(); - void Update(time_t diff); + void Update(uint32 diff); /* inherited from BattlegroundClass */ virtual void AddPlayer(Player *plr); + virtual void StartingEventCloseDoors(); + virtual void StartingEventOpenDoors(); void RemovePlayer(Player *plr, uint64 guid); void HandleAreaTrigger(Player *Source, uint32 Trigger); bool SetupBattleGround(); - virtual void ResetBGSubclass(); + virtual void Reset(); virtual void FillInitialWorldStates(WorldPacket &d); void HandleKillPlayer(Player* player, Player *killer); bool HandlePlayerUnderMap(Player * plr); diff --git a/src/game/BattleGroundRL.cpp b/src/game/BattleGroundRL.cpp index 6d68403fe4c..033733aa61a 100644 --- a/src/game/BattleGroundRL.cpp +++ b/src/game/BattleGroundRL.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,14 +22,23 @@ #include "Player.h" #include "BattleGround.h" #include "BattleGroundRL.h" -#include "Creature.h" #include "ObjectMgr.h" -#include "MapManager.h" #include "Language.h" +#include "WorldPacket.h" BattleGroundRL::BattleGroundRL() { m_BgObjects.resize(BG_RL_OBJECT_MAX); + + m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_1M; + m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_30S; + m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_15S; + m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE; + //we must set messageIds + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_ARENA_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_ARENA_THIRTY_SECONDS; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_ARENA_FIFTEEN_SECONDS; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_ARENA_HAS_BEGUN; } BattleGroundRL::~BattleGroundRL() @@ -37,75 +46,31 @@ BattleGroundRL::~BattleGroundRL() } -void BattleGroundRL::Update(time_t diff) +void BattleGroundRL::Update(uint32 diff) { BattleGround::Update(diff); - if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize()) - { - ModifyStartDelayTime(diff); - - if (!(m_Events & 0x01)) - { - m_Events |= 0x01; - - // setup here, only when at least one player has ported to the map - if(!SetupBattleGround()) - { - EndNow(); - return; - } - - for(uint32 i = BG_RL_OBJECT_DOOR_1; i <= BG_RL_OBJECT_DOOR_2; i++) - SpawnBGObject(i, RESPAWN_IMMEDIATELY); - - SetStartDelayTime(START_DELAY1); - SendMessageToAll(LANG_ARENA_ONE_MINUTE); - } - // After 30 seconds, warning is signalled - else if (GetStartDelayTime() <= START_DELAY2 && !(m_Events & 0x04)) - { - m_Events |= 0x04; - SendMessageToAll(LANG_ARENA_THIRTY_SECONDS); - } - // After 15 seconds, warning is signalled - else if (GetStartDelayTime() <= START_DELAY3 && !(m_Events & 0x08)) - { - m_Events |= 0x08; - SendMessageToAll(LANG_ARENA_FIFTEEN_SECONDS); - } - // delay expired (1 minute) - else if (GetStartDelayTime() <= 0 && !(m_Events & 0x10)) - { - m_Events |= 0x10; - - for(uint32 i = BG_RL_OBJECT_DOOR_1; i <= BG_RL_OBJECT_DOOR_2; i++) - DoorOpen(i); - - for(uint32 i = BG_RL_OBJECT_BUFF_1; i <= BG_RL_OBJECT_BUFF_2; i++) - SpawnBGObject(i, 60); - - SendMessageToAll(LANG_ARENA_BEGUN); - SetStatus(STATUS_IN_PROGRESS); - SetStartDelayTime(0); - - for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) - if(Player *plr = objmgr.GetPlayer(itr->first)) - plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION); - - if(!GetPlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) - EndBattleGround(HORDE); - else if(GetPlayersCountByTeam(ALLIANCE) && !GetPlayersCountByTeam(HORDE)) - EndBattleGround(ALLIANCE); - } - } - - /*if(GetStatus() == STATUS_IN_PROGRESS) + /*if (GetStatus() == STATUS_IN_PROGRESS) { // update something }*/ } +void BattleGroundRL::StartingEventCloseDoors() +{ + for(uint32 i = BG_RL_OBJECT_DOOR_1; i <= BG_RL_OBJECT_DOOR_2; i++) + SpawnBGObject(i, RESPAWN_IMMEDIATELY); +} + +void BattleGroundRL::StartingEventOpenDoors() +{ + for(uint32 i = BG_RL_OBJECT_DOOR_1; i <= BG_RL_OBJECT_DOOR_2; i++) + DoorOpen(i); + + for(uint32 i = BG_RL_OBJECT_BUFF_1; i <= BG_RL_OBJECT_BUFF_2; i++) + SpawnBGObject(i, 60); +} + void BattleGroundRL::AddPlayer(Player *plr) { BattleGround::AddPlayer(plr); @@ -120,24 +85,21 @@ void BattleGroundRL::AddPlayer(Player *plr) void BattleGroundRL::RemovePlayer(Player* /*plr*/, uint64 /*guid*/) { - if(GetStatus() == STATUS_WAIT_LEAVE) + if (GetStatus() == STATUS_WAIT_LEAVE) return; UpdateWorldState(0xbb8, GetAlivePlayersCountByTeam(ALLIANCE)); UpdateWorldState(0xbb9, GetAlivePlayersCountByTeam(HORDE)); - if(!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) - EndBattleGround(HORDE); - else if(GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE)) - EndBattleGround(ALLIANCE); + CheckArenaWinConditions(); } void BattleGroundRL::HandleKillPlayer(Player *player, Player *killer) { - if(GetStatus() != STATUS_IN_PROGRESS) + if (GetStatus() != STATUS_IN_PROGRESS) return; - if(!killer) + if (!killer) { sLog.outError("Killer player not found"); return; @@ -148,16 +110,7 @@ void BattleGroundRL::HandleKillPlayer(Player *player, Player *killer) UpdateWorldState(0xbb8, GetAlivePlayersCountByTeam(ALLIANCE)); UpdateWorldState(0xbb9, GetAlivePlayersCountByTeam(HORDE)); - if(!GetAlivePlayersCountByTeam(ALLIANCE)) - { - // all opponents killed - EndBattleGround(HORDE); - } - else if(!GetAlivePlayersCountByTeam(HORDE)) - { - // all opponents killed - EndBattleGround(ALLIANCE); - } + CheckArenaWinConditions(); } bool BattleGroundRL::HandlePlayerUnderMap(Player *player) @@ -169,7 +122,7 @@ bool BattleGroundRL::HandlePlayerUnderMap(Player *player) void BattleGroundRL::HandleAreaTrigger(Player *Source, uint32 Trigger) { // this is wrong way to implement these things. On official it done by gameobject spell cast. - if(GetStatus() != STATUS_IN_PROGRESS) + if (GetStatus() != STATUS_IN_PROGRESS) return; //uint32 SpellId = 0; @@ -185,7 +138,7 @@ void BattleGroundRL::HandleAreaTrigger(Player *Source, uint32 Trigger) break; } - //if(buff_guid) + //if (buff_guid) // HandleTriggerBuff(buff_guid,Source); } @@ -196,15 +149,16 @@ void BattleGroundRL::FillInitialWorldStates(WorldPacket &data) data << uint32(0xbba) << uint32(1); // 9 } -void BattleGroundRL::ResetBGSubclass() +void BattleGroundRL::Reset() { - + //call parent's reset + BattleGround::Reset(); } bool BattleGroundRL::SetupBattleGround() { // gates - if( !AddObject(BG_RL_OBJECT_DOOR_1, BG_RL_OBJECT_TYPE_DOOR_1, 1293.561, 1601.938, 31.60557, -1.457349, 0, 0, -0.6658813, 0.7460576, RESPAWN_IMMEDIATELY) + if (!AddObject(BG_RL_OBJECT_DOOR_1, BG_RL_OBJECT_TYPE_DOOR_1, 1293.561, 1601.938, 31.60557, -1.457349, 0, 0, -0.6658813, 0.7460576, RESPAWN_IMMEDIATELY) || !AddObject(BG_RL_OBJECT_DOOR_2, BG_RL_OBJECT_TYPE_DOOR_2, 1278.648, 1730.557, 31.60557, 1.684245, 0, 0, 0.7460582, 0.6658807, RESPAWN_IMMEDIATELY) // buffs || !AddObject(BG_RL_OBJECT_BUFF_1, BG_RL_OBJECT_TYPE_BUFF_1, 1328.719971, 1632.719971, 36.730400, -1.448624, 0, 0, 0.6626201, -0.7489557, 120) diff --git a/src/game/BattleGroundRL.h b/src/game/BattleGroundRL.h index 599cb53dc63..772c9dd0879 100644 --- a/src/game/BattleGroundRL.h +++ b/src/game/BattleGroundRL.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -54,16 +54,18 @@ class BattleGroundRL : public BattleGround public: BattleGroundRL(); ~BattleGroundRL(); - void Update(time_t diff); + void Update(uint32 diff); /* inherited from BattlegroundClass */ virtual void AddPlayer(Player *plr); + virtual void Reset(); + virtual void FillInitialWorldStates(WorldPacket &d); + virtual void StartingEventCloseDoors(); + virtual void StartingEventOpenDoors(); void RemovePlayer(Player *plr, uint64 guid); void HandleAreaTrigger(Player *Source, uint32 Trigger); bool SetupBattleGround(); - virtual void ResetBGSubclass(); - virtual void FillInitialWorldStates(WorldPacket &d); void HandleKillPlayer(Player* player, Player *killer); bool HandlePlayerUnderMap(Player * plr); }; diff --git a/src/game/BattleGroundRV.cpp b/src/game/BattleGroundRV.cpp new file mode 100644 index 00000000000..8c252c35c97 --- /dev/null +++ b/src/game/BattleGroundRV.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Player.h" +#include "BattleGround.h" +#include "BattleGroundRV.h" +#include "Language.h" + +BattleGroundRV::BattleGroundRV() +{ + + m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_1M; + m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_30S; + m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_15S; + m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE; + //we must set messageIds + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_ARENA_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_ARENA_THIRTY_SECONDS; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_ARENA_FIFTEEN_SECONDS; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_ARENA_HAS_BEGUN; +} + +BattleGroundRV::~BattleGroundRV() +{ + +} + +void BattleGroundRV::Update(uint32 diff) +{ + BattleGround::Update(diff); +} + +void BattleGroundRV::StartingEventCloseDoors() +{ +} + +void BattleGroundRV::StartingEventOpenDoors() +{ +} + +void BattleGroundRV::AddPlayer(Player *plr) +{ + BattleGround::AddPlayer(plr); + //create score and add it to map, default values are set in constructor + BattleGroundRVScore* sc = new BattleGroundRVScore; + + m_PlayerScores[plr->GetGUID()] = sc; +} + +void BattleGroundRV::RemovePlayer(Player * /*plr*/, uint64 /*guid*/) +{ +} + +void BattleGroundRV::HandleKillPlayer(Player* player, Player* killer) +{ + BattleGround::HandleKillPlayer(player, killer); +} + +void BattleGroundRV::HandleAreaTrigger(Player * /*Source*/, uint32 /*Trigger*/) +{ +} + +bool BattleGroundRV::SetupBattleGround() +{ + return true; +} diff --git a/src/game/BattleGroundRV.h b/src/game/BattleGroundRV.h new file mode 100644 index 00000000000..e3e94baf101 --- /dev/null +++ b/src/game/BattleGroundRV.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __BATTLEGROUNDRV_H +#define __BATTLEGROUNDRV_H + +class BattleGround; + +class BattleGroundRVScore : public BattleGroundScore +{ + public: + BattleGroundRVScore() {}; + virtual ~BattleGroundRVScore() {}; + //TODO fix me +}; + +class BattleGroundRV : public BattleGround +{ + friend class BattleGroundMgr; + + public: + BattleGroundRV(); + ~BattleGroundRV(); + void Update(uint32 diff); + + /* inherited from BattlegroundClass */ + virtual void AddPlayer(Player *plr); + virtual void StartingEventCloseDoors(); + virtual void StartingEventOpenDoors(); + + void RemovePlayer(Player *plr, uint64 guid); + void HandleAreaTrigger(Player *Source, uint32 Trigger); + bool SetupBattleGround(); + void HandleKillPlayer(Player* player, Player *killer); +}; +#endif diff --git a/src/game/BattleGroundSA.cpp b/src/game/BattleGroundSA.cpp new file mode 100644 index 00000000000..5e6b02edfce --- /dev/null +++ b/src/game/BattleGroundSA.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Player.h" +#include "BattleGround.h" +#include "BattleGroundSA.h" +#include "Language.h" + +BattleGroundSA::BattleGroundSA() +{ + //TODO FIX ME! + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_WS_START_TWO_MINUTES; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_WS_START_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_WS_START_HALF_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_WS_HAS_BEGUN; +} + +BattleGroundSA::~BattleGroundSA() +{ + +} + +void BattleGroundSA::Update(uint32 diff) +{ + BattleGround::Update(diff); +} + +void BattleGroundSA::StartingEventCloseDoors() +{ +} + +void BattleGroundSA::StartingEventOpenDoors() +{ +} + +void BattleGroundSA::AddPlayer(Player *plr) +{ + BattleGround::AddPlayer(plr); + //create score and add it to map, default values are set in constructor + BattleGroundSAScore* sc = new BattleGroundSAScore; + + m_PlayerScores[plr->GetGUID()] = sc; +} + +void BattleGroundSA::RemovePlayer(Player* /*plr*/,uint64 /*guid*/) +{ + +} + +void BattleGroundSA::HandleAreaTrigger(Player *Source, uint32 Trigger) +{ + // this is wrong way to implement these things. On official it done by gameobject spell cast. + if (GetStatus() != STATUS_IN_PROGRESS) + return; +} + +void BattleGroundSA::UpdatePlayerScore(Player* Source, uint32 type, uint32 value) +{ + + std::map<uint64, BattleGroundScore*>::iterator itr = m_PlayerScores.find(Source->GetGUID()); + + if(itr == m_PlayerScores.end()) // player not found... + return; + + BattleGround::UpdatePlayerScore(Source,type,value); +} diff --git a/src/game/BattleGroundSA.h b/src/game/BattleGroundSA.h new file mode 100644 index 00000000000..3ba23d02656 --- /dev/null +++ b/src/game/BattleGroundSA.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __BATTLEGROUNDSA_H +#define __BATTLEGROUNDSA_H + +class BattleGround; + +class BattleGroundSAScore : public BattleGroundScore +{ + public: + BattleGroundSAScore() {}; + virtual ~BattleGroundSAScore() {}; +}; + +class BattleGroundSA : public BattleGround +{ + friend class BattleGroundMgr; + + public: + BattleGroundSA(); + ~BattleGroundSA(); + void Update(uint32 diff); + + /* inherited from BattlegroundClass */ + virtual void AddPlayer(Player *plr); + virtual void StartingEventCloseDoors(); + virtual void StartingEventOpenDoors(); + + void RemovePlayer(Player *plr,uint64 guid); + void HandleAreaTrigger(Player *Source, uint32 Trigger); + //bool SetupBattleGround(); + + /* Scorekeeping */ + void UpdatePlayerScore(Player *Source, uint32 type, uint32 value); + + private: +}; +#endif diff --git a/src/game/BattleGroundWS.cpp b/src/game/BattleGroundWS.cpp index 3a8c5668a7a..fac59229e63 100644 --- a/src/game/BattleGroundWS.cpp +++ b/src/game/BattleGroundWS.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,8 +24,8 @@ #include "BattleGroundWS.h" #include "Creature.h" #include "GameObject.h" -#include "Chat.h" -#include "MapManager.h" +#include "ObjectMgr.h" +#include "WorldPacket.h" #include "Language.h" #include "World.h" @@ -52,123 +52,58 @@ BattleGroundWS::BattleGroundWS() { m_BgObjects.resize(BG_WS_OBJECT_MAX); m_BgCreatures.resize(BG_CREATURES_MAX_WS); + + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_WS_START_TWO_MINUTES; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_WS_START_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_WS_START_HALF_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_WS_HAS_BEGUN; } BattleGroundWS::~BattleGroundWS() { } -void BattleGroundWS::Update(time_t diff) +void BattleGroundWS::Update(uint32 diff) { BattleGround::Update(diff); - // after bg start we get there (once) - if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize()) - { - ModifyStartDelayTime(diff); - - if(!(m_Events & 0x01)) - { - m_Events |= 0x01; - - // setup here, only when at least one player has ported to the map - if(!SetupBattleGround()) - { - EndNow(); - return; - } - -// for(uint32 i = WS_SPIRIT_MAIN_ALLIANCE; i <= WS_SPIRIT_MAIN_HORDE; i++) -// SpawnBGCreature(i, RESPAWN_IMMEDIATELY); - - for(uint32 i = BG_WS_OBJECT_DOOR_A_1; i <= BG_WS_OBJECT_DOOR_H_4; i++) - { - SpawnBGObject(i, RESPAWN_IMMEDIATELY); - DoorClose(i); - } - for(uint32 i = BG_WS_OBJECT_A_FLAG; i <= BG_WS_OBJECT_BERSERKBUFF_2; i++) - SpawnBGObject(i, RESPAWN_ONE_DAY); - - SetStartDelayTime(START_DELAY0); - } - // After 1 minute, warning is signalled - else if(GetStartDelayTime() <= START_DELAY1 && !(m_Events & 0x04)) - { - m_Events |= 0x04; - SendMessageToAll(GetTrinityString(LANG_BG_WS_ONE_MINUTE)); - } - // After 1,5 minute, warning is signalled - else if(GetStartDelayTime() <= START_DELAY2 && !(m_Events & 0x08)) - { - m_Events |= 0x08; - SendMessageToAll(GetTrinityString(LANG_BG_WS_HALF_MINUTE)); - } - // After 2 minutes, gates OPEN ! x) - else if(GetStartDelayTime() < 0 && !(m_Events & 0x10)) - { - m_Events |= 0x10; - for(uint32 i = BG_WS_OBJECT_DOOR_A_1; i <= BG_WS_OBJECT_DOOR_A_4; i++) - DoorOpen(i); - for(uint32 i = BG_WS_OBJECT_DOOR_H_1; i <= BG_WS_OBJECT_DOOR_H_2; i++) - DoorOpen(i); - - SpawnBGObject(BG_WS_OBJECT_DOOR_A_5, RESPAWN_ONE_DAY); - SpawnBGObject(BG_WS_OBJECT_DOOR_A_6, RESPAWN_ONE_DAY); - SpawnBGObject(BG_WS_OBJECT_DOOR_H_3, RESPAWN_ONE_DAY); - SpawnBGObject(BG_WS_OBJECT_DOOR_H_4, RESPAWN_ONE_DAY); - - for(uint32 i = BG_WS_OBJECT_A_FLAG; i <= BG_WS_OBJECT_BERSERKBUFF_2; i++) - SpawnBGObject(i, RESPAWN_IMMEDIATELY); - - SendMessageToAll(GetTrinityString(LANG_BG_WS_BEGIN)); - - PlaySoundToAll(SOUND_BG_START); - if(sWorld.getConfig(CONFIG_BG_START_MUSIC)) - PlaySoundToAll(SOUND_BG_START_L70ETC); //MUSIC - Custom config - SetStatus(STATUS_IN_PROGRESS); - - for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) - if(Player* plr = objmgr.GetPlayer(itr->first)) - plr->RemoveAurasDueToSpell(SPELL_PREPARATION); - } - } - else if(GetStatus() == STATUS_IN_PROGRESS) + if (GetStatus() == STATUS_IN_PROGRESS) { - if(m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_WAIT_RESPAWN) + if (m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_WAIT_RESPAWN) { m_FlagsTimer[BG_TEAM_ALLIANCE] -= diff; - if(m_FlagsTimer[BG_TEAM_ALLIANCE] < 0) + if (m_FlagsTimer[BG_TEAM_ALLIANCE] < 0) { m_FlagsTimer[BG_TEAM_ALLIANCE] = 0; RespawnFlag(ALLIANCE, true); } } - if(m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_ON_GROUND) + if (m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_ON_GROUND) { m_FlagsDropTimer[BG_TEAM_ALLIANCE] -= diff; - if(m_FlagsDropTimer[BG_TEAM_ALLIANCE] < 0) + if (m_FlagsDropTimer[BG_TEAM_ALLIANCE] < 0) { m_FlagsDropTimer[BG_TEAM_ALLIANCE] = 0; RespawnFlagAfterDrop(ALLIANCE); } } - if(m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_WAIT_RESPAWN) + if (m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_WAIT_RESPAWN) { m_FlagsTimer[BG_TEAM_HORDE] -= diff; - if(m_FlagsTimer[BG_TEAM_HORDE] < 0) + if (m_FlagsTimer[BG_TEAM_HORDE] < 0) { m_FlagsTimer[BG_TEAM_HORDE] = 0; RespawnFlag(HORDE, true); } } - if(m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_ON_GROUND) + if (m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_ON_GROUND) { m_FlagsDropTimer[BG_TEAM_HORDE] -= diff; - if(m_FlagsDropTimer[BG_TEAM_HORDE] < 0) + if (m_FlagsDropTimer[BG_TEAM_HORDE] < 0) { m_FlagsDropTimer[BG_TEAM_HORDE] = 0; RespawnFlagAfterDrop(HORDE); @@ -177,6 +112,33 @@ void BattleGroundWS::Update(time_t diff) } } +void BattleGroundWS::StartingEventCloseDoors() +{ + for(uint32 i = BG_WS_OBJECT_DOOR_A_1; i <= BG_WS_OBJECT_DOOR_H_4; i++) + { + DoorClose(i); + SpawnBGObject(i, RESPAWN_IMMEDIATELY); + } + for(uint32 i = BG_WS_OBJECT_A_FLAG; i <= BG_WS_OBJECT_BERSERKBUFF_2; i++) + SpawnBGObject(i, RESPAWN_ONE_DAY); +} + +void BattleGroundWS::StartingEventOpenDoors() +{ + for(uint32 i = BG_WS_OBJECT_DOOR_A_1; i <= BG_WS_OBJECT_DOOR_A_4; i++) + DoorOpen(i); + for(uint32 i = BG_WS_OBJECT_DOOR_H_1; i <= BG_WS_OBJECT_DOOR_H_2; i++) + DoorOpen(i); + + SpawnBGObject(BG_WS_OBJECT_DOOR_A_5, RESPAWN_ONE_DAY); + SpawnBGObject(BG_WS_OBJECT_DOOR_A_6, RESPAWN_ONE_DAY); + SpawnBGObject(BG_WS_OBJECT_DOOR_H_3, RESPAWN_ONE_DAY); + SpawnBGObject(BG_WS_OBJECT_DOOR_H_4, RESPAWN_ONE_DAY); + + for(uint32 i = BG_WS_OBJECT_A_FLAG; i <= BG_WS_OBJECT_BERSERKBUFF_2; i++) + SpawnBGObject(i, RESPAWN_IMMEDIATELY); +} + void BattleGroundWS::AddPlayer(Player *plr) { BattleGround::AddPlayer(plr); @@ -188,7 +150,7 @@ void BattleGroundWS::AddPlayer(Player *plr) void BattleGroundWS::RespawnFlag(uint32 Team, bool captured) { - if(Team == ALLIANCE) + if (Team == ALLIANCE) { sLog.outDebug("Respawn Alliance flag"); m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_BASE; @@ -199,37 +161,37 @@ void BattleGroundWS::RespawnFlag(uint32 Team, bool captured) m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_BASE; } - if(captured) + if (captured) { //when map_update will be allowed for battlegrounds this code will be useless SpawnBGObject(BG_WS_OBJECT_H_FLAG, RESPAWN_IMMEDIATELY); SpawnBGObject(BG_WS_OBJECT_A_FLAG, RESPAWN_IMMEDIATELY); - SendMessageToAll(GetTrinityString(LANG_BG_WS_F_PLACED)); + SendMessageToAll(LANG_BG_WS_F_PLACED, CHAT_MSG_BG_SYSTEM_NEUTRAL); PlaySoundToAll(BG_WS_SOUND_FLAGS_RESPAWNED); // flag respawned sound... } } void BattleGroundWS::RespawnFlagAfterDrop(uint32 team) { - if(GetStatus() != STATUS_IN_PROGRESS) + if (GetStatus() != STATUS_IN_PROGRESS) return; RespawnFlag(team,false); - if(team == ALLIANCE) + if (team == ALLIANCE) { SpawnBGObject(BG_WS_OBJECT_A_FLAG, RESPAWN_IMMEDIATELY); - SendMessageToAll(GetTrinityString(LANG_BG_WS_ALLIANCE_FLAG_RESPAWNED)); + SendMessageToAll(LANG_BG_WS_ALLIANCE_FLAG_RESPAWNED, CHAT_MSG_BG_SYSTEM_NEUTRAL); } else { SpawnBGObject(BG_WS_OBJECT_H_FLAG, RESPAWN_IMMEDIATELY); - SendMessageToAll(GetTrinityString(LANG_BG_WS_HORDE_FLAG_RESPAWNED)); + SendMessageToAll(LANG_BG_WS_HORDE_FLAG_RESPAWNED, CHAT_MSG_BG_SYSTEM_NEUTRAL); } PlaySoundToAll(BG_WS_SOUND_FLAGS_RESPAWNED); GameObject *obj = HashMapHolder<GameObject>::Find(GetDroppedFlagGUID(team)); - if(obj) + if (obj) obj->Delete(); else sLog.outError("unknown droped flag bg, guid: %u",GUID_LOPART(GetDroppedFlagGUID(team))); @@ -239,17 +201,13 @@ void BattleGroundWS::RespawnFlagAfterDrop(uint32 team) void BattleGroundWS::EventPlayerCapturedFlag(Player *Source) { - if(GetStatus() != STATUS_IN_PROGRESS) + if (GetStatus() != STATUS_IN_PROGRESS) return; - uint8 type = 0; uint32 winner = 0; - const char *message = ""; - - //TODO FIX reputation and honor gains for low level players! Source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); - if(Source->GetTeam() == ALLIANCE) + if (Source->GetTeam() == ALLIANCE) { if (!this->IsHordeFlagPickedup()) return; @@ -258,13 +216,10 @@ void BattleGroundWS::EventPlayerCapturedFlag(Player *Source) m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_WAIT_RESPAWN; // Drop Horde Flag from Player Source->RemoveAurasDueToSpell(BG_WS_SPELL_WARSONG_FLAG); - message = GetTrinityString(LANG_BG_WS_CAPTURED_HF); - type = CHAT_MSG_BG_SYSTEM_ALLIANCE; - if(GetTeamScore(ALLIANCE) < BG_WS_MAX_TEAM_SCORE) + if (GetTeamScore(ALLIANCE) < BG_WS_MAX_TEAM_SCORE) AddPoint(ALLIANCE, 1); PlaySoundToAll(BG_WS_SOUND_FLAG_CAPTURED_ALLIANCE); - RewardReputationToTeam(890, BG_WSG_Reputation[m_HonorMode][BG_WSG_FLAG_CAP], ALLIANCE); // +35 reputation - RewardHonorToTeam(BG_WSG_Honor[m_HonorMode][BG_WSG_FLAG_CAP], ALLIANCE); // +40 bonushonor + RewardReputationToTeam(890, m_ReputationCapture, ALLIANCE); } else { @@ -275,34 +230,34 @@ void BattleGroundWS::EventPlayerCapturedFlag(Player *Source) m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_WAIT_RESPAWN; // Drop Alliance Flag from Player Source->RemoveAurasDueToSpell(BG_WS_SPELL_SILVERWING_FLAG); - message = GetTrinityString(LANG_BG_WS_CAPTURED_AF); - type = CHAT_MSG_BG_SYSTEM_HORDE; - if(GetTeamScore(HORDE) < BG_WS_MAX_TEAM_SCORE) + if (GetTeamScore(HORDE) < BG_WS_MAX_TEAM_SCORE) AddPoint(HORDE, 1); PlaySoundToAll(BG_WS_SOUND_FLAG_CAPTURED_HORDE); - RewardReputationToTeam(889, BG_WSG_Reputation[m_HonorMode][BG_WSG_FLAG_CAP], HORDE); // +35 reputation - RewardHonorToTeam(BG_WSG_Honor[m_HonorMode][BG_WSG_FLAG_CAP], HORDE); // +40 bonushonor + RewardReputationToTeam(889, m_ReputationCapture, HORDE); } + //for flag capture is reward 2 honorable kills + RewardHonorToTeam(GetBonusHonorFromKill(2), Source->GetTeam()); SpawnBGObject(BG_WS_OBJECT_H_FLAG, BG_WS_FLAG_RESPAWN_TIME); SpawnBGObject(BG_WS_OBJECT_A_FLAG, BG_WS_FLAG_RESPAWN_TIME); - WorldPacket data; - ChatHandler::FillMessageData(&data, Source->GetSession(), type, LANG_UNIVERSAL, NULL, Source->GetGUID(), message, NULL); - SendPacketToAll(&data); + if (Source->GetTeam() == ALLIANCE) + SendMessageToAll(LANG_BG_WS_CAPTURED_HF, CHAT_MSG_BG_SYSTEM_ALLIANCE, Source); + else + SendMessageToAll(LANG_BG_WS_CAPTURED_AF, CHAT_MSG_BG_SYSTEM_HORDE, Source); UpdateFlagState(Source->GetTeam(), 1); // flag state none UpdateTeamScore(Source->GetTeam()); // only flag capture should be updated - UpdatePlayerScore(Source, SCORE_FLAG_CAPTURES, 1); // +1 flag captures... + UpdatePlayerScore(Source, SCORE_FLAG_CAPTURES, 1); // +1 flag captures - if(GetTeamScore(ALLIANCE) == BG_WS_MAX_TEAM_SCORE) + if (GetTeamScore(ALLIANCE) == BG_WS_MAX_TEAM_SCORE) winner = ALLIANCE; - if(GetTeamScore(HORDE) == BG_WS_MAX_TEAM_SCORE) + if (GetTeamScore(HORDE) == BG_WS_MAX_TEAM_SCORE) winner = HORDE; - if(winner) + if (winner) { UpdateWorldState(BG_WS_FLAG_UNK_ALLIANCE, 0); UpdateWorldState(BG_WS_FLAG_UNK_HORDE, 0); @@ -320,15 +275,15 @@ void BattleGroundWS::EventPlayerCapturedFlag(Player *Source) void BattleGroundWS::EventPlayerDroppedFlag(Player *Source) { - if(GetStatus() != STATUS_IN_PROGRESS) + if (GetStatus() != STATUS_IN_PROGRESS) { // if not running, do not cast things at the dropper player (prevent spawning the "dropped" flag), neither send unnecessary messages // just take off the aura - if(Source->GetTeam() == ALLIANCE) + if (Source->GetTeam() == ALLIANCE) { - if(!this->IsHordeFlagPickedup()) + if (!this->IsHordeFlagPickedup()) return; - if(GetHordeFlagPickerGUID() == Source->GetGUID()) + if (GetHordeFlagPickerGUID() == Source->GetGUID()) { SetHordeFlagPicker(0); Source->RemoveAurasDueToSpell(BG_WS_SPELL_WARSONG_FLAG); @@ -336,9 +291,9 @@ void BattleGroundWS::EventPlayerDroppedFlag(Player *Source) } else { - if(!this->IsAllianceFlagPickedup()) + if (!this->IsAllianceFlagPickedup()) return; - if(GetAllianceFlagPickerGUID() == Source->GetGUID()) + if (GetAllianceFlagPickerGUID() == Source->GetGUID()) { SetAllianceFlagPicker(0); Source->RemoveAurasDueToSpell(BG_WS_SPELL_SILVERWING_FLAG); @@ -347,36 +302,30 @@ void BattleGroundWS::EventPlayerDroppedFlag(Player *Source) return; } - const char *message = ""; - uint8 type = 0; bool set = false; - if(Source->GetTeam() == ALLIANCE) + if (Source->GetTeam() == ALLIANCE) { - if(!this->IsHordeFlagPickedup()) + if (!IsHordeFlagPickedup()) return; - if(GetHordeFlagPickerGUID() == Source->GetGUID()) + if (GetHordeFlagPickerGUID() == Source->GetGUID()) { SetHordeFlagPicker(0); Source->RemoveAurasDueToSpell(BG_WS_SPELL_WARSONG_FLAG); m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_GROUND; - message = GetTrinityString(LANG_BG_WS_DROPPED_HF); - type = CHAT_MSG_BG_SYSTEM_HORDE; Source->CastSpell(Source, BG_WS_SPELL_WARSONG_FLAG_DROPPED, true); set = true; } } else { - if(!this->IsAllianceFlagPickedup()) + if (!IsAllianceFlagPickedup()) return; - if(GetAllianceFlagPickerGUID() == Source->GetGUID()) + if (GetAllianceFlagPickerGUID() == Source->GetGUID()) { SetAllianceFlagPicker(0); Source->RemoveAurasDueToSpell(BG_WS_SPELL_SILVERWING_FLAG); m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_GROUND; - message = GetTrinityString(LANG_BG_WS_DROPPED_AF); - type = CHAT_MSG_BG_SYSTEM_ALLIANCE; Source->CastSpell(Source, BG_WS_SPELL_SILVERWING_FLAG_DROPPED, true); set = true; } @@ -387,14 +336,16 @@ void BattleGroundWS::EventPlayerDroppedFlag(Player *Source) Source->CastSpell(Source, SPELL_RECENTLY_DROPPED_FLAG, true); UpdateFlagState(Source->GetTeam(), 1); - WorldPacket data; - ChatHandler::FillMessageData(&data, Source->GetSession(), type, LANG_UNIVERSAL, NULL, Source->GetGUID(), message, NULL); - SendPacketToAll(&data); - - if(Source->GetTeam() == ALLIANCE) + if (Source->GetTeam() == ALLIANCE) + { + SendMessageToAll(LANG_BG_WS_DROPPED_HF, CHAT_MSG_BG_SYSTEM_HORDE, Source); UpdateWorldState(BG_WS_FLAG_UNK_HORDE, uint32(-1)); + } else + { + SendMessageToAll(LANG_BG_WS_DROPPED_AF, CHAT_MSG_BG_SYSTEM_ALLIANCE, Source); UpdateWorldState(BG_WS_FLAG_UNK_ALLIANCE, uint32(-1)); + } m_FlagsDropTimer[GetTeamIndexByTeamId(Source->GetTeam()) ? 0 : 1] = BG_WS_FLAG_DROP_TIME; } @@ -402,17 +353,17 @@ void BattleGroundWS::EventPlayerDroppedFlag(Player *Source) void BattleGroundWS::EventPlayerClickedOnFlag(Player *Source, GameObject* target_obj) { - if(GetStatus() != STATUS_IN_PROGRESS) + if (GetStatus() != STATUS_IN_PROGRESS) return; - const char *message; - uint8 type = 0; + int32 message_id = 0; + ChatMsg type; //alliance flag picked up from base if(Source->GetTeam() == HORDE && this->GetFlagState(ALLIANCE) == BG_WS_FLAG_STATE_ON_BASE && this->m_BgObjects[BG_WS_OBJECT_A_FLAG] == target_obj->GetGUID()) { - message = GetTrinityString(LANG_BG_WS_PICKEDUP_AF); + message_id = LANG_BG_WS_PICKEDUP_AF; type = CHAT_MSG_BG_SYSTEM_HORDE; PlaySoundToAll(BG_WS_SOUND_ALLIANCE_FLAG_PICKED_UP); SpawnBGObject(BG_WS_OBJECT_A_FLAG, RESPAWN_ONE_DAY); @@ -428,7 +379,7 @@ void BattleGroundWS::EventPlayerClickedOnFlag(Player *Source, GameObject* target if (Source->GetTeam() == ALLIANCE && this->GetFlagState(HORDE) == BG_WS_FLAG_STATE_ON_BASE && this->m_BgObjects[BG_WS_OBJECT_H_FLAG] == target_obj->GetGUID()) { - message = GetTrinityString(LANG_BG_WS_PICKEDUP_HF); + message_id = LANG_BG_WS_PICKEDUP_HF; type = CHAT_MSG_BG_SYSTEM_ALLIANCE; PlaySoundToAll(BG_WS_SOUND_HORDE_FLAG_PICKED_UP); SpawnBGObject(BG_WS_OBJECT_H_FLAG, RESPAWN_ONE_DAY); @@ -441,11 +392,11 @@ void BattleGroundWS::EventPlayerClickedOnFlag(Player *Source, GameObject* target } //Alliance flag on ground(not in base) (returned or picked up again from ground!) - if(this->GetFlagState(ALLIANCE) == BG_WS_FLAG_STATE_ON_GROUND && Source->IsWithinDistInMap(target_obj, 10) && target_obj->GetGOInfo()->id == BG_OBJECT_A_FLAG_GROUND_WS_ENTRY) + if (GetFlagState(ALLIANCE) == BG_WS_FLAG_STATE_ON_GROUND && Source->IsWithinDistInMap(target_obj, 10)) { - if(Source->GetTeam() == ALLIANCE) + if (Source->GetTeam() == ALLIANCE) { - message = GetTrinityString(LANG_BG_WS_RETURNED_AF); + message_id = LANG_BG_WS_RETURNED_AF; type = CHAT_MSG_BG_SYSTEM_ALLIANCE; UpdateFlagState(HORDE, BG_WS_FLAG_STATE_WAIT_RESPAWN); RespawnFlag(ALLIANCE, false); @@ -455,7 +406,7 @@ void BattleGroundWS::EventPlayerClickedOnFlag(Player *Source, GameObject* target } else { - message = GetTrinityString(LANG_BG_WS_PICKEDUP_AF); + message_id = LANG_BG_WS_PICKEDUP_AF; type = CHAT_MSG_BG_SYSTEM_HORDE; PlaySoundToAll(BG_WS_SOUND_ALLIANCE_FLAG_PICKED_UP); SpawnBGObject(BG_WS_OBJECT_A_FLAG, RESPAWN_ONE_DAY); @@ -470,11 +421,11 @@ void BattleGroundWS::EventPlayerClickedOnFlag(Player *Source, GameObject* target } //Horde flag on ground(not in base) (returned or picked up again) - if(this->GetFlagState(HORDE) == BG_WS_FLAG_STATE_ON_GROUND && Source->IsWithinDistInMap(target_obj, 10) && target_obj->GetGOInfo()->id == BG_OBJECT_H_FLAG_GROUND_WS_ENTRY) + if (GetFlagState(HORDE) == BG_WS_FLAG_STATE_ON_GROUND && Source->IsWithinDistInMap(target_obj, 10)) { - if(Source->GetTeam() == HORDE) + if (Source->GetTeam() == HORDE) { - message = GetTrinityString(LANG_BG_WS_RETURNED_HF); + message_id = LANG_BG_WS_RETURNED_HF; type = CHAT_MSG_BG_SYSTEM_HORDE; UpdateFlagState(ALLIANCE, BG_WS_FLAG_STATE_WAIT_RESPAWN); RespawnFlag(HORDE, false); @@ -484,7 +435,7 @@ void BattleGroundWS::EventPlayerClickedOnFlag(Player *Source, GameObject* target } else { - message = GetTrinityString(LANG_BG_WS_PICKEDUP_HF); + message_id = LANG_BG_WS_PICKEDUP_HF; type = CHAT_MSG_BG_SYSTEM_ALLIANCE; PlaySoundToAll(BG_WS_SOUND_HORDE_FLAG_PICKED_UP); SpawnBGObject(BG_WS_OBJECT_H_FLAG, RESPAWN_ONE_DAY); @@ -498,21 +449,19 @@ void BattleGroundWS::EventPlayerClickedOnFlag(Player *Source, GameObject* target //target_obj->Delete(); } - if (!type) + if (!message_id) return; - WorldPacket data; - ChatHandler::FillMessageData(&data, Source->GetSession(), type, LANG_UNIVERSAL, NULL, Source->GetGUID(), message, NULL); - SendPacketToAll(&data); + SendMessageToAll(message_id, type, Source); Source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); } void BattleGroundWS::RemovePlayer(Player *plr, uint64 guid) { // sometimes flag aura not removed :( - if(IsAllianceFlagPickedup() && m_FlagKeepers[BG_TEAM_ALLIANCE] == guid) + if (IsAllianceFlagPickedup() && m_FlagKeepers[BG_TEAM_ALLIANCE] == guid) { - if(!plr) + if (!plr) { sLog.outError("BattleGroundWS: Removing offline player who has the FLAG!!"); this->SetAllianceFlagPicker(0); @@ -521,9 +470,9 @@ void BattleGroundWS::RemovePlayer(Player *plr, uint64 guid) else this->EventPlayerDroppedFlag(plr); } - if(IsHordeFlagPickedup() && m_FlagKeepers[BG_TEAM_HORDE] == guid) + if (IsHordeFlagPickedup() && m_FlagKeepers[BG_TEAM_HORDE] == guid) { - if(!plr) + if (!plr) { sLog.outError("BattleGroundWS: Removing offline player who has the FLAG!!"); this->SetHordeFlagPicker(0); @@ -536,7 +485,7 @@ void BattleGroundWS::RemovePlayer(Player *plr, uint64 guid) void BattleGroundWS::UpdateFlagState(uint32 team, uint32 value) { - if(team == ALLIANCE) + if (team == ALLIANCE) UpdateWorldState(BG_WS_FLAG_STATE_ALLIANCE, value); else UpdateWorldState(BG_WS_FLAG_STATE_HORDE, value); @@ -544,7 +493,7 @@ void BattleGroundWS::UpdateFlagState(uint32 team, uint32 value) void BattleGroundWS::UpdateTeamScore(uint32 team) { - if(team == ALLIANCE) + if (team == ALLIANCE) UpdateWorldState(BG_WS_FLAG_CAPTURES_ALLIANCE, GetTeamScore(team)); else UpdateWorldState(BG_WS_FLAG_CAPTURES_HORDE, GetTeamScore(team)); @@ -553,7 +502,7 @@ void BattleGroundWS::UpdateTeamScore(uint32 team) void BattleGroundWS::HandleAreaTrigger(Player *Source, uint32 Trigger) { // this is wrong way to implement these things. On official it done by gameobject spell cast. - if(GetStatus() != STATUS_IN_PROGRESS) + if (GetStatus() != STATUS_IN_PROGRESS) return; //uint32 SpellId = 0; @@ -579,13 +528,13 @@ void BattleGroundWS::HandleAreaTrigger(Player *Source, uint32 Trigger) //buff_guid = m_BgObjects[BG_WS_OBJECT_BERSERKBUFF_2]; break; case 3646: // Alliance Flag spawn - if(m_FlagState[BG_TEAM_HORDE] && !m_FlagState[BG_TEAM_ALLIANCE]) - if(GetHordeFlagPickerGUID() == Source->GetGUID()) + if (m_FlagState[BG_TEAM_HORDE] && !m_FlagState[BG_TEAM_ALLIANCE]) + if (GetHordeFlagPickerGUID() == Source->GetGUID()) EventPlayerCapturedFlag(Source); break; case 3647: // Horde Flag spawn - if(m_FlagState[BG_TEAM_ALLIANCE] && !m_FlagState[BG_TEAM_HORDE]) - if(GetAllianceFlagPickerGUID() == Source->GetGUID()) + if (m_FlagState[BG_TEAM_ALLIANCE] && !m_FlagState[BG_TEAM_HORDE]) + if (GetAllianceFlagPickerGUID() == Source->GetGUID()) EventPlayerCapturedFlag(Source); break; case 3649: // unk1 @@ -599,14 +548,14 @@ void BattleGroundWS::HandleAreaTrigger(Player *Source, uint32 Trigger) break; } - //if(buff_guid) + //if (buff_guid) // HandleTriggerBuff(buff_guid,Source); } bool BattleGroundWS::SetupBattleGround() { // flags - if( !AddObject(BG_WS_OBJECT_A_FLAG, BG_OBJECT_A_FLAG_WS_ENTRY, 1540.423f, 1481.325f, 351.8284f, 3.089233f, 0, 0, 0.9996573f, 0.02617699f, BG_WS_FLAG_RESPAWN_TIME/1000) + if (!AddObject(BG_WS_OBJECT_A_FLAG, BG_OBJECT_A_FLAG_WS_ENTRY, 1540.423f, 1481.325f, 351.8284f, 3.089233f, 0, 0, 0.9996573f, 0.02617699f, BG_WS_FLAG_RESPAWN_TIME/1000) || !AddObject(BG_WS_OBJECT_H_FLAG, BG_OBJECT_H_FLAG_WS_ENTRY, 916.0226f, 1434.405f, 345.413f, 0.01745329f, 0, 0, 0.008726535f, 0.9999619f, BG_WS_FLAG_RESPAWN_TIME/1000) // buffs || !AddObject(BG_WS_OBJECT_SPEEDBUFF_1, BG_OBJECTID_SPEEDBUFF_ENTRY, 1449.93f, 1470.71f, 342.6346f, -1.64061f, 0, 0, 0.7313537f, -0.6819983f, BUFF_RESPAWN_TIME) @@ -634,14 +583,14 @@ bool BattleGroundWS::SetupBattleGround() } WorldSafeLocsEntry const *sg = sWorldSafeLocsStore.LookupEntry(WS_GRAVEYARD_MAIN_ALLIANCE); - if(!sg || !AddSpiritGuide(WS_SPIRIT_MAIN_ALLIANCE, sg->x, sg->y, sg->z, 3.124139f, ALLIANCE)) + if (!sg || !AddSpiritGuide(WS_SPIRIT_MAIN_ALLIANCE, sg->x, sg->y, sg->z, 3.124139f, ALLIANCE)) { sLog.outErrorDb("BatteGroundWS: Failed to spawn Alliance spirit guide! BattleGround not created!"); return false; } sg = sWorldSafeLocsStore.LookupEntry(WS_GRAVEYARD_MAIN_HORDE); - if(!sg || !AddSpiritGuide(WS_SPIRIT_MAIN_HORDE, sg->x, sg->y, sg->z, 3.193953f, HORDE)) + if (!sg || !AddSpiritGuide(WS_SPIRIT_MAIN_HORDE, sg->x, sg->y, sg->z, 3.193953f, HORDE)) { sLog.outErrorDb("BatteGroundWS: Failed to spawn Horde spirit guide! BattleGround not created!"); return false; @@ -652,8 +601,11 @@ bool BattleGroundWS::SetupBattleGround() return true; } -void BattleGroundWS::ResetBGSubclass() +void BattleGroundWS::Reset() { + //call parent's class reset + BattleGround::Reset(); + m_FlagKeepers[BG_TEAM_ALLIANCE] = 0; m_FlagKeepers[BG_TEAM_HORDE] = 0; m_DroppedFlagGUID[BG_TEAM_ALLIANCE] = 0; @@ -662,19 +614,36 @@ void BattleGroundWS::ResetBGSubclass() m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_BASE; m_TeamScores[BG_TEAM_ALLIANCE] = 0; m_TeamScores[BG_TEAM_HORDE] = 0; + bool isBGWeekend = false; //TODO FIXME - call sBattleGroundMgr.IsBGWeekend(m_TypeID); - you must also implement that call! + m_ReputationCapture = (isBGWeekend) ? 45 : 35; + m_HonorWinKills = (isBGWeekend) ? 3 : 1; + m_HonorEndKills = (isBGWeekend) ? 4 : 2; /* Spirit nodes is static at this BG and then not required deleting at BG reset. - if(m_BgCreatures[WS_SPIRIT_MAIN_ALLIANCE]) + if (m_BgCreatures[WS_SPIRIT_MAIN_ALLIANCE]) DelCreature(WS_SPIRIT_MAIN_ALLIANCE); - - if(m_BgCreatures[WS_SPIRIT_MAIN_HORDE]) + if (m_BgCreatures[WS_SPIRIT_MAIN_HORDE]) DelCreature(WS_SPIRIT_MAIN_HORDE); */ } +void BattleGroundWS::EndBattleGround(uint32 winner) +{ + //win reward + if (winner == ALLIANCE) + RewardHonorToTeam(GetBonusHonorFromKill(m_HonorWinKills), ALLIANCE); + if (winner == HORDE) + RewardHonorToTeam(GetBonusHonorFromKill(m_HonorWinKills), HORDE); + //complete map_end rewards (even if no team wins) + RewardHonorToTeam(GetBonusHonorFromKill(m_HonorEndKills), ALLIANCE); + RewardHonorToTeam(GetBonusHonorFromKill(m_HonorEndKills), HORDE); + + BattleGround::EndBattleGround(winner); +} + void BattleGroundWS::HandleKillPlayer(Player *player, Player *killer) { - if(GetStatus() != STATUS_IN_PROGRESS) + if (GetStatus() != STATUS_IN_PROGRESS) return; EventPlayerDroppedFlag(player); @@ -704,19 +673,42 @@ void BattleGroundWS::UpdatePlayerScore(Player *Source, uint32 type, uint32 value } } +WorldSafeLocsEntry const* BattleGroundWS::GetClosestGraveYard(Player* player) +{ + //if status in progress, it returns main graveyards with spiritguides + //else it will return the graveyard in the flagroom - this is especially good + //if a player dies in preparation phase - then the player can't cheat + //and teleport to the graveyard outside the flagroom + //and start running around, while the doors are still closed + if (player->GetTeam() == ALLIANCE) + { + if (GetStatus() == STATUS_IN_PROGRESS) + return sWorldSafeLocsStore.LookupEntry(WS_GRAVEYARD_MAIN_ALLIANCE); + else + return sWorldSafeLocsStore.LookupEntry(WS_GRAVEYARD_FLAGROOM_ALLIANCE); + } + else + { + if (GetStatus() == STATUS_IN_PROGRESS) + return sWorldSafeLocsStore.LookupEntry(WS_GRAVEYARD_MAIN_HORDE); + else + return sWorldSafeLocsStore.LookupEntry(WS_GRAVEYARD_FLAGROOM_HORDE); + } +} + void BattleGroundWS::FillInitialWorldStates(WorldPacket& data) { data << uint32(BG_WS_FLAG_CAPTURES_ALLIANCE) << uint32(GetTeamScore(ALLIANCE)); data << uint32(BG_WS_FLAG_CAPTURES_HORDE) << uint32(GetTeamScore(HORDE)); - if(m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_ON_GROUND) + if (m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_ON_GROUND) data << uint32(BG_WS_FLAG_UNK_ALLIANCE) << uint32(-1); else if (m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_ON_PLAYER) data << uint32(BG_WS_FLAG_UNK_ALLIANCE) << uint32(1); else data << uint32(BG_WS_FLAG_UNK_ALLIANCE) << uint32(0); - if(m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_ON_GROUND) + if (m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_ON_GROUND) data << uint32(BG_WS_FLAG_UNK_HORDE) << uint32(-1); else if (m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_ON_PLAYER) data << uint32(BG_WS_FLAG_UNK_HORDE) << uint32(1); diff --git a/src/game/BattleGroundWS.h b/src/game/BattleGroundWS.h index 4e57826cd30..906bccfe6ab 100644 --- a/src/game/BattleGroundWS.h +++ b/src/game/BattleGroundWS.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,7 +29,7 @@ enum BG_WS_TimerOrScore BG_WS_FLAG_RESPAWN_TIME = 23000, BG_WS_FLAG_DROP_TIME = 10000, BG_WS_SPELL_FORCE_TIME = 600000, - BG_WS_SPELL_BRUTAL_TIME = 900000 + BG_WS_SPELL_BRUTAL_TIME = 900000 }; enum BG_WS_Sound @@ -116,8 +116,10 @@ enum BG_WS_FlagState enum BG_WS_Graveyards { - WS_GRAVEYARD_MAIN_ALLIANCE = 771, - WS_GRAVEYARD_MAIN_HORDE = 772 + WS_GRAVEYARD_FLAGROOM_ALLIANCE = 769, + WS_GRAVEYARD_FLAGROOM_HORDE = 770, + WS_GRAVEYARD_MAIN_ALLIANCE = 771, + WS_GRAVEYARD_MAIN_HORDE = 772 }; enum BG_WS_CreatureTypes @@ -145,10 +147,12 @@ class BattleGroundWS : public BattleGround /* Construction */ BattleGroundWS(); ~BattleGroundWS(); - void Update(time_t diff); + void Update(uint32 diff); /* inherited from BattlegroundClass */ virtual void AddPlayer(Player *plr); + virtual void StartingEventCloseDoors(); + virtual void StartingEventOpenDoors(); /* BG Flags */ uint64 GetAllianceFlagPickerGUID() const { return m_FlagKeepers[BG_TEAM_ALLIANCE]; } @@ -174,7 +178,9 @@ class BattleGroundWS : public BattleGround void HandleAreaTrigger(Player *Source, uint32 Trigger); void HandleKillPlayer(Player *player, Player *killer); bool SetupBattleGround(); - virtual void ResetBGSubclass(); + virtual void Reset(); + void EndBattleGround(uint32 winner); + virtual WorldSafeLocsEntry const* GetClosestGraveYard(Player* player); void UpdateFlagState(uint32 team, uint32 value); void UpdateTeamScore(uint32 team); @@ -185,9 +191,9 @@ class BattleGroundWS : public BattleGround /* Scorekeeping */ uint32 GetTeamScore(uint32 TeamID) const { return m_TeamScores[GetTeamIndexByTeamId(TeamID)]; } - void AddPoint(uint32 TeamID, uint32 Points = 1) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] += Points; m_score[GetTeamIndexByTeamId(TeamID)] = m_TeamScores[GetTeamIndexByTeamId(TeamID)];} - void SetTeamPoint(uint32 TeamID, uint32 Points = 0) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] = Points; m_score[GetTeamIndexByTeamId(TeamID)] = m_TeamScores[GetTeamIndexByTeamId(TeamID)];} - void RemovePoint(uint32 TeamID, uint32 Points = 1) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] -= Points; m_score[GetTeamIndexByTeamId(TeamID)] = m_TeamScores[GetTeamIndexByTeamId(TeamID)]; } + void AddPoint(uint32 TeamID, uint32 Points = 1) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] += Points; } + void SetTeamPoint(uint32 TeamID, uint32 Points = 0) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] = Points; } + void RemovePoint(uint32 TeamID, uint32 Points = 1) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] -= Points; } private: uint64 m_FlagKeepers[2]; // 0 - alliance, 1 - horde @@ -197,8 +203,9 @@ class BattleGroundWS : public BattleGround int32 m_FlagsTimer[2]; int32 m_FlagsDropTimer[2]; - int32 m_FlagSpellForceTimer; - int32 m_FlagSpellBrutalTimer; + uint32 m_ReputationCapture; + uint32 m_HonorWinKills; + uint32 m_HonorEndKills; }; #endif diff --git a/src/game/CMakeLists.txt b/src/game/CMakeLists.txt index 316eb8a659c..081d9669696 100644 --- a/src/game/CMakeLists.txt +++ b/src/game/CMakeLists.txt @@ -4,6 +4,8 @@ SET(game_STAT_SRCS AccountMgr.cpp AccountMgr.h + AchievementMgr.h + AchievementMgr.cpp AddonHandler.cpp AddonHandler.h AggressorAI.cpp @@ -12,9 +14,9 @@ SET(game_STAT_SRCS ArenaTeam.cpp ArenaTeam.h ArenaTeamHandler.cpp + AuctionHouseHandler.cpp AuctionHouseBot.cpp AuctionHouseBot.h - AuctionHouseHandler.cpp AuctionHouseMgr.cpp AuctionHouseMgr.h Bag.cpp @@ -24,22 +26,31 @@ SET(game_STAT_SRCS BattleGroundAB.cpp BattleGroundAV.cpp BattleGroundBE.cpp + BattleGroundDS.cpp BattleGroundEY.cpp BattleGroundNA.cpp BattleGroundRL.cpp + BattleGroundRV.cpp + BattleGroundSA.cpp BattleGroundWS.cpp BattleGround.h BattleGroundAA.h BattleGroundAB.h BattleGroundAV.h BattleGroundBE.h + BattleGroundDS.h BattleGroundEY.h BattleGroundNA.h BattleGroundRL.h + BattleGroundRV.h + BattleGroundSA.h BattleGroundWS.h BattleGroundHandler.cpp BattleGroundMgr.cpp BattleGroundMgr.h + Calendar.cpp + Calendar.h + CalendarHandler.cpp Cell.h CellImpl.h Channel.cpp @@ -62,10 +73,17 @@ SET(game_STAT_SRCS CreatureAIRegistry.h CreatureAISelector.cpp CreatureAISelector.h + CreatureEventAI.cpp + CreatureEventAIMgr.cpp Creature.cpp Creature.h CreatureGroups.cpp CreatureGroups.h + DBCEnums.h + DBCStores.cpp + DBCStores.h + DBCStructure.h + DBCfmt.h Debugcmds.cpp DestinationHolder.cpp DestinationHolder.h @@ -76,8 +94,8 @@ SET(game_STAT_SRCS FleeingMovementGenerator.cpp FleeingMovementGenerator.h Formulas.h - GameEvent.cpp - GameEvent.h + GameEventMgr.cpp + GameEventMgr.h GameObject.cpp GameObject.h GlobalEvents.cpp @@ -131,6 +149,8 @@ SET(game_STAT_SRCS MapInstanced.h MapManager.cpp MapManager.h + MapReference.h + MapRefManager.h MiscHandler.cpp MotionMaster.cpp MotionMaster.h @@ -151,6 +171,8 @@ SET(game_STAT_SRCS Object.h ObjectMgr.cpp ObjectMgr.h + ObjectPosSelector.cpp + ObjectPosSelector.h Opcodes.cpp Opcodes.h OutdoorPvP.cpp @@ -184,6 +206,8 @@ SET(game_STAT_SRCS PlayerDump.h PointMovementGenerator.cpp PointMovementGenerator.h + PoolHandler.cpp + PoolHandler.h PossessedAI.cpp PossessedAI.h QueryHandler.cpp @@ -194,6 +218,8 @@ SET(game_STAT_SRCS RandomMovementGenerator.h ReactorAI.cpp ReactorAI.h + ReputationMgr.cpp + ReputationMgr.h ScriptCalls.cpp ScriptCalls.h SharedDefines.h @@ -221,7 +247,7 @@ SET(game_STAT_SRCS TemporarySummon.h TicketHandler.cpp TicketMgr.cpp - TicketMgr.h + TicketMgr.h Tools.cpp Tools.h TotemAI.cpp @@ -241,6 +267,8 @@ SET(game_STAT_SRCS UpdateData.h UpdateFields.h UpdateMask.h + Vehicle.cpp + Vehicle.h VoiceChatHandler.cpp WaypointManager.cpp WaypointManager.h @@ -267,4 +295,4 @@ SET(game_STAT_SRCS ) add_library(game STATIC ${game_STAT_SRCS}) -ADD_DEPENDENCIES(game revision.h)
\ No newline at end of file +ADD_DEPENDENCIES(game revision.h) diff --git a/src/game/Calendar.cpp b/src/game/Calendar.cpp new file mode 100644 index 00000000000..0c1efb20f87 --- /dev/null +++ b/src/game/Calendar.cpp @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ diff --git a/src/game/Calendar.h b/src/game/Calendar.h new file mode 100644 index 00000000000..7a86afa7db7 --- /dev/null +++ b/src/game/Calendar.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_CALENDAR_H +#define MANGOS_CALENDAR_H + +class Calendar +{ + +}; +#endif diff --git a/src/game/CalendarHandler.cpp b/src/game/CalendarHandler.cpp new file mode 100644 index 00000000000..e9946e1a05e --- /dev/null +++ b/src/game/CalendarHandler.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Log.h" +#include "Player.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "Opcodes.h" +#include "InstanceSaveMgr.h" + +void WorldSession::HandleCalendarGetCalendar(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_GET_CALENDAR"); + recv_data.hexlike(); + + time_t cur_time = time(NULL); + + WorldPacket data(SMSG_CALENDAR_SEND_CALENDAR,4+4*0+4+4*0+4+4); + + // TODO: calendar invite event output + data << (uint32) 0; //invite node count + // TODO: calendar event output + data << (uint32) 0; //event count + + data << (uint32) 0; //wtf?? + data << (uint32) secsToTimeBitFields(cur_time); // current time + + uint32 counter = 0; + size_t p_counter = data.wpos(); + data << uint32(counter); // instance save count + + for(int i = 0; i < TOTAL_DIFFICULTIES; ++i) + { + for (Player::BoundInstancesMap::iterator itr = _player->m_boundInstances[i].begin(); itr != _player->m_boundInstances[i].end(); ++itr) + { + if(itr->second.perm) + { + InstanceSave *save = itr->second.save; + data << uint32(save->GetMapId()); + data << uint32(save->GetDifficulty()); + data << uint32(save->GetResetTime() - cur_time); + data << uint64(save->GetInstanceId()); // instance save id as unique instance copy id + ++counter; + } + } + } + data.put<uint32>(p_counter,counter); + + data << (uint32) 1135753200; //wtf?? (28.12.2005 12:00) + data << (uint32) 0; // unk counter 4 + data << (uint32) 0; // unk counter 5 + //sLog.outDebug("Sending calendar"); + //data.hexlike(); + SendPacket(&data); +} + +void WorldSession::HandleCalendarGetEvent(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_GET_EVENT"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarGuildFilter(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_GUILD_FILTER"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarArenaTeam(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_ARENA_TEAM"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarAddEvent(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_ADD_EVENT"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarUpdateEvent(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_UPDATE_EVENT"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarRemoveEvent(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_REMOVE_EVENT"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarCopyEvent(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_COPY_EVENT"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarEventInvite(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_INVITE"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarEventRsvp(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_RSVP"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarEventRemoveInvite(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_REMOVE_INVITE"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarEventStatus(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_STATUS"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarEventModeratorStatus(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_MODERATOR_STATUS"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarComplain(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_COMPLAIN"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarGetNumPending(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_GET_NUM_PENDING"); + recv_data.hexlike(); + + WorldPacket data(SMSG_CALENDAR_SEND_NUM_PENDING, 4); + data << uint32(0); // 0 - no pending invites, 1 - some pending invites + SendPacket(&data); +} diff --git a/src/game/Cell.h b/src/game/Cell.h index 160e037bcfb..7e66d5d9f73 100644 --- a/src/game/Cell.h +++ b/src/game/Cell.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -93,13 +93,13 @@ struct TRINITY_DLL_DECL Cell y = data.Part.grid_y*MAX_NUMBER_OF_CELLS + data.Part.cell_y; } - inline bool DiffCell(const Cell &cell) const + bool DiffCell(const Cell &cell) const { return( data.Part.cell_x != cell.data.Part.cell_x || data.Part.cell_y != cell.data.Part.cell_y ); } - inline bool DiffGrid(const Cell &cell) const + bool DiffGrid(const Cell &cell) const { return( data.Part.grid_x != cell.data.Part.grid_x || data.Part.grid_y != cell.data.Part.grid_y ); diff --git a/src/game/CellImpl.h b/src/game/CellImpl.h index 463b051f7ac..e74e0013a19 100644 --- a/src/game/CellImpl.h +++ b/src/game/CellImpl.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/Channel.cpp b/src/game/Channel.cpp index bf787d7d25d..8d61dd733ea 100644 --- a/src/game/Channel.cpp +++ b/src/game/Channel.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,7 +24,7 @@ #include "SocialMgr.h" Channel::Channel(const std::string& name, uint32 channel_id) -: m_name(name), m_announce(true), m_moderate(false), m_channelId(channel_id), m_ownerGUID(0), m_password(""), m_flags(0) +: m_announce(true), m_moderate(false), m_name(name), m_flags(0), m_channelId(channel_id), m_ownerGUID(0) { // set special flags if built-in channel ChatChannelsEntry const* ch = GetChannelEntryFor(channel_id); @@ -455,7 +455,7 @@ void Channel::List(Player* player) // PLAYER can't see MODERATOR, GAME MASTER, ADMINISTRATOR characters // MODERATOR, GAME MASTER, ADMINISTRATOR can see all - if( plr && ( plr->GetSession()->GetSecurity() == SEC_PLAYER || gmInWhoList && plr->IsVisibleGloballyFor(player) ) ) + if (plr && ( plr->GetSession()->GetSecurity() == SEC_PLAYER || (gmInWhoList && plr->IsVisibleGloballyFor(player)))) { data << uint64(i->first); data << uint8(i->second.flags); // flags seems to be changed... @@ -693,12 +693,12 @@ void Channel::SendToOne(WorldPacket *data, uint64 who) plr->GetSession()->SendPacket(data); } -void Channel::Voice(uint64 guid1, uint64 guid2) +void Channel::Voice(uint64 /*guid1*/, uint64 /*guid2*/) { } -void Channel::DeVoice(uint64 guid1, uint64 guid2) +void Channel::DeVoice(uint64 /*guid1*/, uint64 /*guid2*/) { } diff --git a/src/game/Channel.h b/src/game/Channel.h index 82b62df12c4..924046e4739 100644 --- a/src/game/Channel.h +++ b/src/game/Channel.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,7 +23,6 @@ #include "Common.h" #include "WorldPacket.h" -#include "WorldSession.h" #include "Opcodes.h" #include "Player.h" @@ -31,95 +30,95 @@ #include <map> #include <string> -class Channel +enum ChatNotify { - enum ChatNotify - { - CHAT_JOINED_NOTICE = 0x00, //+ "%s joined channel."; - CHAT_LEFT_NOTICE = 0x01, //+ "%s left channel."; - //CHAT_SUSPENDED_NOTICE = 0x01, // "%s left channel."; - CHAT_YOU_JOINED_NOTICE = 0x02, //+ "Joined Channel: [%s]"; -- You joined - //CHAT_YOU_CHANGED_NOTICE = 0x02, // "Changed Channel: [%s]"; - CHAT_YOU_LEFT_NOTICE = 0x03, //+ "Left Channel: [%s]"; -- You left - CHAT_WRONG_PASSWORD_NOTICE = 0x04, //+ "Wrong password for %s."; - CHAT_NOT_MEMBER_NOTICE = 0x05, //+ "Not on channel %s."; - CHAT_NOT_MODERATOR_NOTICE = 0x06, //+ "Not a moderator of %s."; - CHAT_PASSWORD_CHANGED_NOTICE = 0x07, //+ "[%s] Password changed by %s."; - CHAT_OWNER_CHANGED_NOTICE = 0x08, //+ "[%s] Owner changed to %s."; - CHAT_PLAYER_NOT_FOUND_NOTICE = 0x09, //+ "[%s] Player %s was not found."; - CHAT_NOT_OWNER_NOTICE = 0x0A, //+ "[%s] You are not the channel owner."; - CHAT_CHANNEL_OWNER_NOTICE = 0x0B, //+ "[%s] Channel owner is %s."; - CHAT_MODE_CHANGE_NOTICE = 0x0C, //? - CHAT_ANNOUNCEMENTS_ON_NOTICE = 0x0D, //+ "[%s] Channel announcements enabled by %s."; - CHAT_ANNOUNCEMENTS_OFF_NOTICE = 0x0E, //+ "[%s] Channel announcements disabled by %s."; - CHAT_MODERATION_ON_NOTICE = 0x0F, //+ "[%s] Channel moderation enabled by %s."; - CHAT_MODERATION_OFF_NOTICE = 0x10, //+ "[%s] Channel moderation disabled by %s."; - CHAT_MUTED_NOTICE = 0x11, //+ "[%s] You do not have permission to speak."; - CHAT_PLAYER_KICKED_NOTICE = 0x12, //? "[%s] Player %s kicked by %s."; - CHAT_BANNED_NOTICE = 0x13, //+ "[%s] You are banned from that channel."; - CHAT_PLAYER_BANNED_NOTICE = 0x14, //? "[%s] Player %s banned by %s."; - CHAT_PLAYER_UNBANNED_NOTICE = 0x15, //? "[%s] Player %s unbanned by %s."; - CHAT_PLAYER_NOT_BANNED_NOTICE = 0x16, //+ "[%s] Player %s is not banned."; - CHAT_PLAYER_ALREADY_MEMBER_NOTICE = 0x17, //+ "[%s] Player %s is already on the channel."; - CHAT_INVITE_NOTICE = 0x18, //+ "%2$s has invited you to join the channel '%1$s'."; - CHAT_INVITE_WRONG_FACTION_NOTICE = 0x19, //+ "Target is in the wrong alliance for %s."; - CHAT_WRONG_FACTION_NOTICE = 0x1A, //+ "Wrong alliance for %s."; - CHAT_INVALID_NAME_NOTICE = 0x1B, //+ "Invalid channel name"; - CHAT_NOT_MODERATED_NOTICE = 0x1C, //+ "%s is not moderated"; - CHAT_PLAYER_INVITED_NOTICE = 0x1D, //+ "[%s] You invited %s to join the channel"; - CHAT_PLAYER_INVITE_BANNED_NOTICE = 0x1E, //+ "[%s] %s has been banned."; - CHAT_THROTTLED_NOTICE = 0x1F, //+ "[%s] The number of messages that can be sent to this channel is limited, please wait to send another message."; - CHAT_NOT_IN_AREA_NOTICE = 0x20, //+ "[%s] You are not in the correct area for this channel."; -- The user is trying to send a chat to a zone specific channel, and they're not physically in that zone. - CHAT_NOT_IN_LFG_NOTICE = 0x21, //+ "[%s] You must be queued in looking for group before joining this channel."; -- The user must be in the looking for group system to join LFG chat channels. - CHAT_VOICE_ON_NOTICE = 0x22, //+ "[%s] Channel voice enabled by %s."; - CHAT_VOICE_OFF_NOTICE = 0x23, //+ "[%s] Channel voice disabled by %s."; - }; + CHAT_JOINED_NOTICE = 0x00, //+ "%s joined channel."; + CHAT_LEFT_NOTICE = 0x01, //+ "%s left channel."; + //CHAT_SUSPENDED_NOTICE = 0x01, // "%s left channel."; + CHAT_YOU_JOINED_NOTICE = 0x02, //+ "Joined Channel: [%s]"; -- You joined + //CHAT_YOU_CHANGED_NOTICE = 0x02, // "Changed Channel: [%s]"; + CHAT_YOU_LEFT_NOTICE = 0x03, //+ "Left Channel: [%s]"; -- You left + CHAT_WRONG_PASSWORD_NOTICE = 0x04, //+ "Wrong password for %s."; + CHAT_NOT_MEMBER_NOTICE = 0x05, //+ "Not on channel %s."; + CHAT_NOT_MODERATOR_NOTICE = 0x06, //+ "Not a moderator of %s."; + CHAT_PASSWORD_CHANGED_NOTICE = 0x07, //+ "[%s] Password changed by %s."; + CHAT_OWNER_CHANGED_NOTICE = 0x08, //+ "[%s] Owner changed to %s."; + CHAT_PLAYER_NOT_FOUND_NOTICE = 0x09, //+ "[%s] Player %s was not found."; + CHAT_NOT_OWNER_NOTICE = 0x0A, //+ "[%s] You are not the channel owner."; + CHAT_CHANNEL_OWNER_NOTICE = 0x0B, //+ "[%s] Channel owner is %s."; + CHAT_MODE_CHANGE_NOTICE = 0x0C, //? + CHAT_ANNOUNCEMENTS_ON_NOTICE = 0x0D, //+ "[%s] Channel announcements enabled by %s."; + CHAT_ANNOUNCEMENTS_OFF_NOTICE = 0x0E, //+ "[%s] Channel announcements disabled by %s."; + CHAT_MODERATION_ON_NOTICE = 0x0F, //+ "[%s] Channel moderation enabled by %s."; + CHAT_MODERATION_OFF_NOTICE = 0x10, //+ "[%s] Channel moderation disabled by %s."; + CHAT_MUTED_NOTICE = 0x11, //+ "[%s] You do not have permission to speak."; + CHAT_PLAYER_KICKED_NOTICE = 0x12, //? "[%s] Player %s kicked by %s."; + CHAT_BANNED_NOTICE = 0x13, //+ "[%s] You are banned from that channel."; + CHAT_PLAYER_BANNED_NOTICE = 0x14, //? "[%s] Player %s banned by %s."; + CHAT_PLAYER_UNBANNED_NOTICE = 0x15, //? "[%s] Player %s unbanned by %s."; + CHAT_PLAYER_NOT_BANNED_NOTICE = 0x16, //+ "[%s] Player %s is not banned."; + CHAT_PLAYER_ALREADY_MEMBER_NOTICE = 0x17, //+ "[%s] Player %s is already on the channel."; + CHAT_INVITE_NOTICE = 0x18, //+ "%2$s has invited you to join the channel '%1$s'."; + CHAT_INVITE_WRONG_FACTION_NOTICE = 0x19, //+ "Target is in the wrong alliance for %s."; + CHAT_WRONG_FACTION_NOTICE = 0x1A, //+ "Wrong alliance for %s."; + CHAT_INVALID_NAME_NOTICE = 0x1B, //+ "Invalid channel name"; + CHAT_NOT_MODERATED_NOTICE = 0x1C, //+ "%s is not moderated"; + CHAT_PLAYER_INVITED_NOTICE = 0x1D, //+ "[%s] You invited %s to join the channel"; + CHAT_PLAYER_INVITE_BANNED_NOTICE = 0x1E, //+ "[%s] %s has been banned."; + CHAT_THROTTLED_NOTICE = 0x1F, //+ "[%s] The number of messages that can be sent to this channel is limited, please wait to send another message."; + CHAT_NOT_IN_AREA_NOTICE = 0x20, //+ "[%s] You are not in the correct area for this channel."; -- The user is trying to send a chat to a zone specific channel, and they're not physically in that zone. + CHAT_NOT_IN_LFG_NOTICE = 0x21, //+ "[%s] You must be queued in looking for group before joining this channel."; -- The user must be in the looking for group system to join LFG chat channels. + CHAT_VOICE_ON_NOTICE = 0x22, //+ "[%s] Channel voice enabled by %s."; + CHAT_VOICE_OFF_NOTICE = 0x23, //+ "[%s] Channel voice disabled by %s."; +}; - enum ChannelFlags - { - CHANNEL_FLAG_NONE = 0x00, - CHANNEL_FLAG_CUSTOM = 0x01, - // 0x02 - CHANNEL_FLAG_TRADE = 0x04, - CHANNEL_FLAG_NOT_LFG = 0x08, - CHANNEL_FLAG_GENERAL = 0x10, - CHANNEL_FLAG_CITY = 0x20, - CHANNEL_FLAG_LFG = 0x40, - CHANNEL_FLAG_VOICE = 0x80 - // General 0x18 = 0x10 | 0x08 - // Trade 0x3C = 0x20 | 0x10 | 0x08 | 0x04 - // LocalDefence 0x18 = 0x10 | 0x08 - // GuildRecruitment 0x38 = 0x20 | 0x10 | 0x08 - // LookingForGroup 0x50 = 0x40 | 0x10 - }; +enum ChannelFlags +{ + CHANNEL_FLAG_NONE = 0x00, + CHANNEL_FLAG_CUSTOM = 0x01, + // 0x02 + CHANNEL_FLAG_TRADE = 0x04, + CHANNEL_FLAG_NOT_LFG = 0x08, + CHANNEL_FLAG_GENERAL = 0x10, + CHANNEL_FLAG_CITY = 0x20, + CHANNEL_FLAG_LFG = 0x40, + CHANNEL_FLAG_VOICE = 0x80 + // General 0x18 = 0x10 | 0x08 + // Trade 0x3C = 0x20 | 0x10 | 0x08 | 0x04 + // LocalDefence 0x18 = 0x10 | 0x08 + // GuildRecruitment 0x38 = 0x20 | 0x10 | 0x08 + // LookingForGroup 0x50 = 0x40 | 0x10 +}; - enum ChannelDBCFlags - { - CHANNEL_DBC_FLAG_NONE = 0x00000, - CHANNEL_DBC_FLAG_INITIAL = 0x00001, // General, Trade, LocalDefense, LFG - CHANNEL_DBC_FLAG_ZONE_DEP = 0x00002, // General, Trade, LocalDefense, GuildRecruitment - CHANNEL_DBC_FLAG_GLOBAL = 0x00004, // WorldDefense - CHANNEL_DBC_FLAG_TRADE = 0x00008, // Trade - CHANNEL_DBC_FLAG_CITY_ONLY = 0x00010, // Trade, GuildRecruitment - CHANNEL_DBC_FLAG_CITY_ONLY2 = 0x00020, // Trade, GuildRecruitment - CHANNEL_DBC_FLAG_DEFENSE = 0x10000, // LocalDefense, WorldDefense - CHANNEL_DBC_FLAG_GUILD_REQ = 0x20000, // GuildRecruitment - CHANNEL_DBC_FLAG_LFG = 0x40000 // LookingForGroup - }; +enum ChannelDBCFlags +{ + CHANNEL_DBC_FLAG_NONE = 0x00000, + CHANNEL_DBC_FLAG_INITIAL = 0x00001, // General, Trade, LocalDefense, LFG + CHANNEL_DBC_FLAG_ZONE_DEP = 0x00002, // General, Trade, LocalDefense, GuildRecruitment + CHANNEL_DBC_FLAG_GLOBAL = 0x00004, // WorldDefense + CHANNEL_DBC_FLAG_TRADE = 0x00008, // Trade + CHANNEL_DBC_FLAG_CITY_ONLY = 0x00010, // Trade, GuildRecruitment + CHANNEL_DBC_FLAG_CITY_ONLY2 = 0x00020, // Trade, GuildRecruitment + CHANNEL_DBC_FLAG_DEFENSE = 0x10000, // LocalDefense, WorldDefense + CHANNEL_DBC_FLAG_GUILD_REQ = 0x20000, // GuildRecruitment + CHANNEL_DBC_FLAG_LFG = 0x40000 // LookingForGroup +}; - enum ChannelMemberFlags - { - MEMBER_FLAG_NONE = 0x00, - MEMBER_FLAG_OWNER = 0x01, - MEMBER_FLAG_MODERATOR = 0x02, - MEMBER_FLAG_VOICED = 0x04, - MEMBER_FLAG_MUTED = 0x08, - MEMBER_FLAG_CUSTOM = 0x10, - MEMBER_FLAG_MIC_MUTED = 0x20, - // 0x40 - // 0x80 - }; +enum ChannelMemberFlags +{ + MEMBER_FLAG_NONE = 0x00, + MEMBER_FLAG_OWNER = 0x01, + MEMBER_FLAG_MODERATOR = 0x02, + MEMBER_FLAG_VOICED = 0x04, + MEMBER_FLAG_MUTED = 0x08, + MEMBER_FLAG_CUSTOM = 0x10, + MEMBER_FLAG_MIC_MUTED = 0x20, + // 0x40 + // 0x80 +}; +class Channel +{ struct PlayerInfo { uint64 player; @@ -204,11 +203,8 @@ class Channel void SendToAllButOne(WorldPacket *data, uint64 who); void SendToOne(WorldPacket *data, uint64 who); - bool IsOn(uint64 who) const { return players.count(who) != 0; } - - bool IsBanned(const uint64 guid) const {return banned.count(guid) != 0; } - - bool IsFirst() const { return !(players.size() > 1); } + bool IsOn(uint64 who) const { return players.find(who) != players.end(); } + bool IsBanned(uint64 guid) const { return banned.find(guid) != banned.end(); } uint8 GetPlayerFlags(uint64 p) const { diff --git a/src/game/ChannelHandler.cpp b/src/game/ChannelHandler.cpp index 71e7ea365c2..9b06693c589 100644 --- a/src/game/ChannelHandler.cpp +++ b/src/game/ChannelHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/ChannelMgr.h b/src/game/ChannelMgr.h index 453ee144d86..66f692ef69c 100644 --- a/src/game/ChannelMgr.h +++ b/src/game/ChannelMgr.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,7 +40,7 @@ class ChannelMgr } Channel *GetJoinChannel(const std::string& name, uint32 channel_id) { - if(channels.count(name) == 0) + if (channels.find(name) == channels.end()) { Channel *nchan = new Channel(name,channel_id); channels[name] = nchan; diff --git a/src/game/CharacterHandler.cpp b/src/game/CharacterHandler.cpp index 4aef21df530..a2e189603f9 100644 --- a/src/game/CharacterHandler.cpp +++ b/src/game/CharacterHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,7 +31,6 @@ #include "Guild.h" #include "UpdateMask.h" #include "Auth/md5.h" -#include "MapManager.h" #include "ObjectAccessor.h" #include "Group.h" #include "Database/DatabaseImpl.h" @@ -64,11 +63,11 @@ bool LoginQueryHolder::Initialize() // NOTE: all fields in `characters` must be read to prevent lost character data at next save in case wrong DB structure. // !!! NOTE: including unused `zone`,`online` - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADFROM, "SELECT guid, account, data, name, race, class, position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty, arena_pending_points FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADFROM, "SELECT guid, account, data, name, race, class, position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty, arena_pending_points,bgid,bgteam,bgmap,bgx,bgy,bgz,bgo FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGROUP, "SELECT leaderGuid FROM group_member WHERE memberGuid ='%u'", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES, "SELECT id, permanent, map, difficulty, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = '%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADAURAS, "SELECT caster_guid,spell,effect_index,stackcount,amount,maxduration,remaintime,remaincharges FROM character_aura WHERE guid = '%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSPELLS, "SELECT spell,slot,active,disabled FROM character_spell WHERE guid = '%u'", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADAURAS, "SELECT caster_guid,spell,effect_mask,stackcount,amount0, amount1, amount2 ,maxduration,remaintime,remaincharges FROM character_aura WHERE guid = '%u'", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSPELLS, "SELECT spell,active,disabled FROM character_spell WHERE guid = '%u'", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS, "SELECT quest,status,rewarded,explored,timer,mobcount1,mobcount2,mobcount3,mobcount4,itemcount1,itemcount2,itemcount3,itemcount4 FROM character_queststatus WHERE guid = '%u'", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS,"SELECT quest,time FROM character_queststatus_daily WHERE guid = '%u'", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADTUTORIALS, "SELECT tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7 FROM character_tutorial WHERE account = '%u' AND realmid = '%u'", GetAccountId(), realmID); @@ -85,6 +84,8 @@ bool LoginQueryHolder::Initialize() // in other case still be dummy query res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGUILD, "SELECT guildid,rank FROM guild_member WHERE guid = '%u'", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADARENAINFO, "SELECT arenateamid, played_week, played_season, personal_rating FROM arena_team_member WHERE guid='%u'", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADACHIEVEMENTS, "SELECT achievement, date FROM character_achievement WHERE guid = '%u'", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADCRITERIAPROGRESS,"SELECT criteria, counter, date FROM character_achievement_progress WHERE guid = '%u'", GUID_LOPART(m_guid)); return res; } @@ -164,7 +165,7 @@ void WorldSession::HandleCharEnumOpcode( WorldPacket & /*recv_data*/ ) "SELECT characters.guid, characters.data, characters.name, characters.position_x, characters.position_y, characters.position_z, characters.map, characters.totaltime, characters.leveltime, " // 9 10 11 12 13 "characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, guild_member.guildid " - "FROM characters LEFT JOIN character_pet ON characters.guid=character_pet.owner AND character_pet.slot='0' " + "FROM characters LEFT JOIN character_pet ON characters.guid=character_pet.owner AND character_pet.slot='%u' " "LEFT JOIN guild_member ON characters.guid = guild_member.guid " "WHERE characters.account = '%u' ORDER BY characters.guid" : @@ -173,11 +174,11 @@ void WorldSession::HandleCharEnumOpcode( WorldPacket & /*recv_data*/ ) "SELECT characters.guid, characters.data, characters.name, characters.position_x, characters.position_y, characters.position_z, characters.map, characters.totaltime, characters.leveltime, " // 9 10 11 12 13 14 "characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, guild_member.guildid, genitive " - "FROM characters LEFT JOIN character_pet ON characters.guid = character_pet.owner AND character_pet.slot='0' " + "FROM characters LEFT JOIN character_pet ON characters.guid = character_pet.owner AND character_pet.slot='%u' " "LEFT JOIN character_declinedname ON characters.guid = character_declinedname.guid " "LEFT JOIN guild_member ON characters.guid = guild_member.guid " "WHERE characters.account = '%u' ORDER BY characters.guid", - GetAccountId()); + PET_SAVE_AS_CURRENT,GetAccountId()); } void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) @@ -234,17 +235,16 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) if (raceEntry->addon > Expansion()) { data << (uint8)CHAR_CREATE_EXPANSION; - sLog.outError("Not Expansion 1 account:[%d] but tried to Create character with expansion 1 race (%u)",GetAccountId(),race_); + sLog.outError("Expansion %u account:[%d] tried to Create character with expansion %u race (%u)",Expansion(),GetAccountId(),raceEntry->addon,race_); SendPacket( &data ); return; } // prevent character creating Expansion class without Expansion account - // TODO: use possible addon field in ChrClassesEntry in next dbc version - if (Expansion() < 2 && class_ == CLASS_DEATH_KNIGHT) + if (classEntry->addon > Expansion()) { - data << (uint8)CHAR_CREATE_EXPANSION; - sLog.outError("Not Expansion 2 account:[%d] but tried to Create character with expansion 2 class (%u)",GetAccountId(),class_); + data << (uint8)CHAR_CREATE_EXPANSION_CLASS; + sLog.outError("Expansion %u account:[%d] tried to Create character with expansion %u class (%u)",Expansion(),GetAccountId(),classEntry->addon,class_); SendPacket( &data ); return; } @@ -311,29 +311,77 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) } } + // speedup check for heroic class disabled case + uint32 heroic_free_slots = sWorld.getConfig(CONFIG_HEROIC_CHARACTERS_PER_REALM); + if(heroic_free_slots==0 && GetSecurity()==SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT) + { + data << (uint8)CHAR_CREATE_UNIQUE_CLASS_LIMIT; + SendPacket( &data ); + return; + } + + // speedup check for heroic class disabled case + uint32 req_level_for_heroic = sWorld.getConfig(CONFIG_MIN_LEVEL_FOR_HEROIC_CHARACTER_CREATING); + if(GetSecurity()==SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT && req_level_for_heroic > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)) + { + data << (uint8)CHAR_CREATE_LEVEL_REQUIREMENT; + SendPacket( &data ); + return; + } + bool AllowTwoSideAccounts = !sWorld.IsPvPRealm() || sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS) || GetSecurity() > SEC_PLAYER; uint32 skipCinematics = sWorld.getConfig(CONFIG_SKIP_CINEMATICS); bool have_same_race = false; - if(!AllowTwoSideAccounts || skipCinematics == 1) + + // if 0 then allowed creating without any characters + bool have_req_level_for_heroic = (req_level_for_heroic==0); + + if(!AllowTwoSideAccounts || skipCinematics == 1 || class_ == CLASS_DEATH_KNIGHT) { - QueryResult *result2 = CharacterDatabase.PQuery("SELECT DISTINCT race FROM characters WHERE account = '%u' %s", GetAccountId(),skipCinematics == 1 ? "" : "LIMIT 1"); + QueryResult *result2 = CharacterDatabase.PQuery("SELECT guid,race,class FROM characters WHERE account = '%u' %s", + GetAccountId(), (skipCinematics == 1 || class_ == CLASS_DEATH_KNIGHT) ? "" : "LIMIT 1"); if(result2) { uint32 team_= Player::TeamForRace(race_); Field* field = result2->Fetch(); - uint8 race = field[0].GetUInt32(); + uint8 acc_race = field[1].GetUInt32(); + + if(GetSecurity()==SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT) + { + uint8 acc_class = field[2].GetUInt32(); + if(acc_class == CLASS_DEATH_KNIGHT) + { + if(heroic_free_slots > 0) + --heroic_free_slots; + + if(heroic_free_slots==0) + { + data << (uint8)CHAR_CREATE_UNIQUE_CLASS_LIMIT; + SendPacket( &data ); + return; + } + } + + if(!have_req_level_for_heroic) + { + uint32 acc_guid = field[0].GetUInt32(); + uint32 acc_level = Player::GetUInt32ValueFromDB(UNIT_FIELD_LEVEL,acc_guid); + if(acc_level >= req_level_for_heroic) + have_req_level_for_heroic = true; + } + } // need to check team only for first character // TODO: what to if account already has characters of both races? if (!AllowTwoSideAccounts) { - uint32 team=0; - if(race > 0) - team = Player::TeamForRace(race); + uint32 acc_team=0; + if(acc_race > 0) + acc_team = Player::TeamForRace(acc_race); - if(team != team_) + if(acc_team != team_) { data << (uint8)CHAR_CREATE_PVP_TEAMS_VIOLATION; SendPacket( &data ); @@ -342,20 +390,55 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) } } - if (skipCinematics == 1) + // search same race for cinematic or same class if need + // TODO: check if cinematic already shown? (already logged in?; cinematic field) + while ((skipCinematics == 1 && !have_same_race) || class_ == CLASS_DEATH_KNIGHT) { - // TODO: check if cinematic already shown? (already logged in?; cinematic field) - while (race_ != race && result2->NextRow()) + if(!result2->NextRow()) + break; + + field = result2->Fetch(); + acc_race = field[1].GetUInt32(); + + if(!have_same_race) + have_same_race = race_ == acc_race; + + if(GetSecurity()==SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT) { - field = result2->Fetch(); - race = field[0].GetUInt32(); + uint8 acc_class = field[2].GetUInt32(); + if(acc_class == CLASS_DEATH_KNIGHT) + { + if(heroic_free_slots > 0) + --heroic_free_slots; + + if(heroic_free_slots==0) + { + data << (uint8)CHAR_CREATE_UNIQUE_CLASS_LIMIT; + SendPacket( &data ); + return; + } + } + + if(!have_req_level_for_heroic) + { + uint32 acc_guid = field[0].GetUInt32(); + uint32 acc_level = Player::GetUInt32ValueFromDB(UNIT_FIELD_LEVEL,acc_guid); + if(acc_level >= req_level_for_heroic) + have_req_level_for_heroic = true; + } } - have_same_race = race_ == race; } delete result2; } } + if(GetSecurity()==SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT && !have_req_level_for_heroic) + { + data << (uint8)CHAR_CREATE_LEVEL_REQUIREMENT; + SendPacket( &data ); + return; + } + // extract other data required for player creating uint8 gender, skin, face, hairStyle, hairColor, facialHair, outfitId; recv_data >> gender >> skin >> face; @@ -373,7 +456,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) return; } - if(have_same_race && skipCinematics == 1 || skipCinematics == 2) + if ((have_same_race && skipCinematics == 1) || skipCinematics == 2) pNewChar->setCinematic(1); // not show intro // Player created, save it now @@ -439,7 +522,7 @@ void WorldSession::HandleCharDeleteOpcode( WorldPacket & recv_data ) return; std::string IP_str = GetRemoteAddress(); - sLog.outDetail("Account: %d (IP: %s) Delete Character:[%s] (guid:%u)",GetAccountId(),IP_str.c_str(),name.c_str(),GUID_LOPART(guid)); + sLog.outDetail("Account: %d (IP: %s) Delete Character:[%s] (guid: %u)",GetAccountId(),IP_str.c_str(),name.c_str(),GUID_LOPART(guid)); sLog.outChar("Account: %d (IP: %s) Delete Character:[%s] (guid: %u)",GetAccountId(),IP_str.c_str(),name.c_str(),GUID_LOPART(guid)); if(sLog.IsOutCharDump()) // optimize GetPlayerDump call @@ -514,9 +597,11 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder) data << pCurrChar->GetOrientation(); SendPacket(&data); - data.Initialize( SMSG_ACCOUNT_DATA_TIMES, 128 ); - for(int i = 0; i < 32; i++) - data << uint32(0); + data.Initialize( SMSG_ACCOUNT_DATA_TIMES, 4+1+8*4 ); // changed in WotLK + data << uint32(time(NULL)); // unix time of something + data << uint8(1); + for(int i = 0; i < NUM_ACCOUNT_DATA_TYPES; i++) + data << uint32(GetAccountData(i)->Time); // also unix time SendPacket(&data); data.Initialize(SMSG_FEATURE_SYSTEM_STATUS, 2); // added in 2.2.0 @@ -562,6 +647,11 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder) DEBUG_LOG( "WORLD: Sent server info" ); } + data.Initialize(SMSG_LEARNED_DANCE_MOVES, 4+4); + data << uint32(0); + data << uint32(0); + SendPacket(&data); + //QueryResult *result = CharacterDatabase.PQuery("SELECT guildid,rank FROM guild_member WHERE guid = '%u'",pCurrChar->GetGUIDLow()); QueryResult *resultGuild = holder->GetResult(PLAYER_LOGIN_QUERY_LOADGUILD); @@ -619,12 +709,12 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder) { pCurrChar->setCinematic(1); - ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(pCurrChar->getRace()); - if(rEntry) + if(ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(pCurrChar->getClass())) { - data.Initialize( SMSG_TRIGGER_CINEMATIC,4 ); - data << uint32(rEntry->startmovie); - SendPacket( &data ); + if (cEntry->CinematicSequence) + pCurrChar->SendCinematicStart(cEntry->CinematicSequence); + else if (ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(pCurrChar->getRace())) + pCurrChar->SendCinematicStart(rEntry->CinematicSequence); // send new char string if not empty if (!sWorld.GetNewCharString().empty()) @@ -672,28 +762,11 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder) pCurrChar->CastSpell(pCurrChar, 20584, true, 0);// auras SPELL_AURA_INCREASE_SPEED(+speed in wisp form), SPELL_AURA_INCREASE_SWIM_SPEED(+swim speed in wisp form), SPELL_AURA_TRANSFORM (to wisp form) pCurrChar->CastSpell(pCurrChar, 8326, true, 0); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?) - //pCurrChar->SetUInt32Value(UNIT_FIELD_AURA+41, 8326); - //pCurrChar->SetUInt32Value(UNIT_FIELD_AURA+42, 20584); - //pCurrChar->SetUInt32Value(UNIT_FIELD_AURAFLAGS+6, 238); - //pCurrChar->SetUInt32Value(UNIT_FIELD_AURALEVELS+11, 514); - //pCurrChar->SetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS+11, 65535); - //pCurrChar->SetUInt32Value(UNIT_FIELD_DISPLAYID, 1825); - //if (pCurrChar->getRace() == RACE_NIGHTELF) - //{ - // pCurrChar->SetSpeed(MOVE_RUN, 1.5f*1.2f, true); - // pCurrChar->SetSpeed(MOVE_SWIM, 1.5f*1.2f, true); - //} - //else - //{ - // pCurrChar->SetSpeed(MOVE_RUN, 1.5f, true); - // pCurrChar->SetSpeed(MOVE_SWIM, 1.5f, true); - //} pCurrChar->SetMovement(MOVE_WATER_WALK); } if(uint32 sourceNode = pCurrChar->m_taxi.GetTaxiSource()) { - sLog.outDebug( "WORLD: Restart character %u taxi flight", pCurrChar->GetGUIDLow() ); uint32 MountId = objmgr.GetTaxiMount(sourceNode, pCurrChar->GetTeam()); @@ -741,13 +814,12 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder) SendDoFlight( MountId, path, startNode ); } - // Load pet if any and player is alive and not in taxi flight - if(pCurrChar->isAlive() && pCurrChar->m_taxi.GetTaxiSource()==0) - pCurrChar->LoadPet(); + // Load pet if any (if player not alive and in taxi flight or another then pet will remember as temporary unsummoned) + pCurrChar->LoadPet(); // Set FFA PvP for non GM in non-rest mode if(sWorld.IsFFAPvPRealm() && !pCurrChar->isGameMaster() && !pCurrChar->HasFlag(PLAYER_FLAGS,PLAYER_FLAGS_RESTING) ) - pCurrChar->SetFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP); + pCurrChar->SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); if(pCurrChar->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP)) pCurrChar->SetContestedPvP(); @@ -795,15 +867,7 @@ void WorldSession::HandleSetFactionAtWar( WorldPacket & recv_data ) recv_data >> repListID; recv_data >> flag; - FactionStateList::iterator itr = GetPlayer()->m_factions.find(repListID); - if (itr == GetPlayer()->m_factions.end()) - return; - - // always invisible or hidden faction can't change war state - if(itr->second.Flags & (FACTION_FLAG_INVISIBLE_FORCED|FACTION_FLAG_HIDDEN) ) - return; - - GetPlayer()->SetFactionAtWar(&itr->second,flag); + GetPlayer()->GetReputationMgr().SetAtWar(repListID,flag); } //I think this function is never used :/ I dunno, but i guess this opcode not exists @@ -811,7 +875,7 @@ void WorldSession::HandleSetFactionCheat( WorldPacket & /*recv_data*/ ) { //CHECK_PACKET_SIZE(recv_data,4+4); - //sLog.outDebug("WORLD SESSION: HandleSetFactionCheat"); + sLog.outError("WORLD SESSION: HandleSetFactionCheat, not expected call, please report."); /* uint32 FactionID; uint32 Standing; @@ -831,7 +895,7 @@ void WorldSession::HandleSetFactionCheat( WorldPacket & /*recv_data*/ ) } } */ - GetPlayer()->UpdateReputation(); + GetPlayer()->GetReputationMgr().SendStates(); } void WorldSession::HandleMeetingStoneInfo( WorldPacket & /*recv_data*/ ) @@ -896,11 +960,7 @@ void WorldSession::HandleSetWatchedFactionInactiveOpcode(WorldPacket & recv_data uint8 inactive; recv_data >> replistid >> inactive; - FactionStateList::iterator itr = _player->m_factions.find(replistid); - if (itr == _player->m_factions.end()) - return; - - _player->SetFactionInactive(&itr->second, inactive); + _player->GetReputationMgr().SetInactive(replistid, inactive); } void WorldSession::HandleToggleHelmOpcode( WorldPacket & /*recv_data*/ ) @@ -917,11 +977,11 @@ void WorldSession::HandleToggleCloakOpcode( WorldPacket & /*recv_data*/ ) void WorldSession::HandleChangePlayerNameOpcode(WorldPacket& recv_data) { + CHECK_PACKET_SIZE(recv_data, 8+1); + uint64 guid; std::string newname; - CHECK_PACKET_SIZE(recv_data, 8+1); - recv_data >> guid; recv_data >> newname; @@ -929,15 +989,15 @@ void WorldSession::HandleChangePlayerNameOpcode(WorldPacket& recv_data) if(!normalizePlayerName(newname)) { WorldPacket data(SMSG_CHAR_RENAME, 1); - data << (uint8)CHAR_NAME_NO_NAME; + data << uint8(CHAR_NAME_NO_NAME); SendPacket( &data ); return; } - if(!ObjectMgr::IsValidName(newname,true)) + if(!ObjectMgr::IsValidName(newname, true)) { WorldPacket data(SMSG_CHAR_RENAME, 1); - data << (uint8)CHAR_NAME_INVALID_CHARACTER; + data << uint8(CHAR_NAME_INVALID_CHARACTER); SendPacket( &data ); return; } @@ -946,7 +1006,7 @@ void WorldSession::HandleChangePlayerNameOpcode(WorldPacket& recv_data) if(GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(newname)) { WorldPacket data(SMSG_CHAR_RENAME, 1); - data << (uint8)CHAR_NAME_RESERVED; + data << uint8(CHAR_NAME_RESERVED); SendPacket( &data ); return; } @@ -975,7 +1035,7 @@ void WorldSession::HandleChangePlayerNameOpcodeCallBack(QueryResult *result, uin if (!result) { WorldPacket data(SMSG_CHAR_RENAME, 1); - data << (uint8)CHAR_CREATE_ERROR; + data << uint8(CHAR_CREATE_ERROR); session->SendPacket( &data ); return; } @@ -989,11 +1049,11 @@ void WorldSession::HandleChangePlayerNameOpcodeCallBack(QueryResult *result, uin CharacterDatabase.PExecute("UPDATE characters set name = '%s', at_login = at_login & ~ %u WHERE guid ='%u'", newname.c_str(), uint32(AT_LOGIN_RENAME), guidLow); CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid ='%u'", guidLow); - sLog.outChar("Account: %d (IP: %s) Character:[%s] (guid:%u) Changed name to: %s",session->GetAccountId(), session->GetRemoteAddress().c_str(), oldname.c_str(), guidLow, newname.c_str()); + sLog.outChar("Account: %d (IP: %s) Character:[%s] (guid:%u) Changed name to: %s", session->GetAccountId(), session->GetRemoteAddress().c_str(), oldname.c_str(), guidLow, newname.c_str()); - WorldPacket data(SMSG_CHAR_RENAME,1+8+(newname.size()+1)); - data << (uint8)RESPONSE_SUCCESS; - data << guid; + WorldPacket data(SMSG_CHAR_RENAME, 1+8+(newname.size()+1)); + data << uint8(RESPONSE_SUCCESS); + data << uint64(guid); data << newname; session->SendPacket(&data); } @@ -1088,3 +1148,170 @@ void WorldSession::HandleDeclinedPlayerNameOpcode(WorldPacket& recv_data) SendPacket(&data); } +void WorldSession::HandleAlterAppearance( WorldPacket & recv_data ) +{ + sLog.outDebug("CMSG_ALTER_APPEARANCE"); + + CHECK_PACKET_SIZE(recv_data, 4+4+4); + + uint32 Hair, Color, FacialHair; + recv_data >> Hair >> Color >> FacialHair; + + BarberShopStyleEntry const* bs_hair = sBarberShopStyleStore.LookupEntry(Hair); + + if(!bs_hair || bs_hair->type != 0 || bs_hair->race != _player->getRace() || bs_hair->gender != _player->getGender()) + return; + + BarberShopStyleEntry const* bs_facialHair = sBarberShopStyleStore.LookupEntry(FacialHair); + + if(!bs_facialHair || bs_facialHair->type != 2 || bs_facialHair->race != _player->getRace() || bs_facialHair->gender != _player->getGender()) + return; + + uint32 Cost = _player->GetBarberShopCost(bs_hair->hair_id, Color, bs_facialHair->hair_id); + + // 0 - ok + // 1,3 - not enough money + // 2 - you have to seat on barber chair + if(_player->GetMoney() < Cost) + { + WorldPacket data(SMSG_BARBER_SHOP_RESULT, 4); + data << uint32(1); // no money + SendPacket(&data); + return; + } + else + { + WorldPacket data(SMSG_BARBER_SHOP_RESULT, 4); + data << uint32(0); // ok + SendPacket(&data); + } + + _player->ModifyMoney(-int32(Cost)); // it isn't free + _player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER, Cost); + + _player->SetByteValue(PLAYER_BYTES, 2, uint8(bs_hair->hair_id)); + _player->SetByteValue(PLAYER_BYTES, 3, uint8(Color)); + _player->SetByteValue(PLAYER_BYTES_2, 0, uint8(bs_facialHair->hair_id)); + + _player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP, 1); + + _player->SetStandState(0); // stand up +} + +void WorldSession::HandleRemoveGlyph( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data, 4); + + uint32 slot; + recv_data >> slot; + + if(slot >= MAX_GLYPH_SLOT_INDEX) + { + sLog.outDebug("Client sent wrong glyph slot number in opcode CMSG_REMOVE_GLYPH %u", slot); + return; + } + + if(uint32 glyph = _player->GetGlyph(slot)) + { + if(GlyphPropertiesEntry const *gp = sGlyphPropertiesStore.LookupEntry(glyph)) + { + _player->RemoveAurasDueToSpell(gp->SpellId); + _player->SetGlyph(slot, 0); + } + } +} + +void WorldSession::HandleCharCustomize(WorldPacket& recv_data) +{ + CHECK_PACKET_SIZE(recv_data, 8+1); + + uint64 guid; + std::string newname; + + recv_data >> guid; + recv_data >> newname; + + CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+1+1+1+1+1+1); + + uint8 gender, skin, face, hairStyle, hairColor, facialHair; + recv_data >> gender >> skin >> hairColor >> hairStyle >> facialHair >> face; + + QueryResult *result = CharacterDatabase.PQuery("SELECT at_login FROM characters WHERE guid ='%u'", GUID_LOPART(guid)); + if (!result) + { + WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); + data << uint8(CHAR_CREATE_ERROR); + SendPacket( &data ); + return; + } + + Field *fields = result->Fetch(); + uint32 at_loginFlags = fields[0].GetUInt32(); + delete result; + + if (!(at_loginFlags & AT_LOGIN_CUSTOMIZE)) + { + WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); + data << uint8(CHAR_CREATE_ERROR); + SendPacket( &data ); + return; + } + + // prevent character rename to invalid name + if(!normalizePlayerName(newname)) + { + WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); + data << uint8(CHAR_NAME_NO_NAME); + SendPacket( &data ); + return; + } + + if(!ObjectMgr::IsValidName(newname,true)) + { + WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); + data << uint8(CHAR_NAME_INVALID_CHARACTER); + SendPacket( &data ); + return; + } + + // check name limitations + if(GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(newname)) + { + WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); + data << uint8(CHAR_NAME_RESERVED); + SendPacket( &data ); + return; + } + + // character with this name already exist + if(uint64 newguid = objmgr.GetPlayerGUIDByName(newname)) + { + if(newguid != guid) + { + WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); + data << uint8(CHAR_CREATE_NAME_IN_USE); + SendPacket( &data ); + return; + } + } + + CharacterDatabase.escape_string(newname); + Player::Customize(guid, gender, skin, face, hairStyle, hairColor, facialHair); + CharacterDatabase.PExecute("UPDATE characters set name = '%s', at_login = at_login & ~ %u WHERE guid ='%u'", newname.c_str(), uint32(AT_LOGIN_CUSTOMIZE), GUID_LOPART(guid)); + CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid ='%u'", GUID_LOPART(guid)); + + std::string IP_str = GetRemoteAddress(); + sLog.outChar("Account: %d (IP: %s), Character guid: %u Customized to: %s", GetAccountId(), IP_str.c_str(), GUID_LOPART(guid), newname.c_str()); + + WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1+8+(newname.size()+1)+6); + data << uint8(RESPONSE_SUCCESS); + data << uint64(guid); + data << newname; + data << uint8(gender); + data << uint8(skin); + data << uint8(face); + data << uint8(hairStyle); + data << uint8(hairColor); + data << uint8(facialHair); + SendPacket(&data); +} diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp index 27e54bd7bde..402fdd048c7 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,11 +30,28 @@ #include "Player.h" #include "UpdateMask.h" #include "Chat.h" -#include "MapManager.h" #include "GridNotifiersImpl.h" #include "CellImpl.h" +#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|Hplayer:name|h[name]|h|r - client, in some messages, at click copy only name instead link +// |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() @@ -57,163 +74,168 @@ ChatCommand * ChatHandler::getCommandTable() { NULL, 0, false, NULL, "", NULL } }; - static ChatCommand serverSetCommandTable[] = + static ChatCommand banCommandTable[] = { - { "loglevel", SEC_CONSOLE, true, &ChatHandler::HandleServerSetLogLevelCommand, "", NULL }, - { "difftime", SEC_CONSOLE, true, &ChatHandler::HandleServerSetDiffTimeCommand, "", NULL }, - { "motd", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerSetMotdCommand, "", NULL }, + { "account", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanAccountCommand, "", NULL }, + { "character", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanCharacterCommand, "", NULL }, + { "ip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanIPCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; - static ChatCommand serverIdleRestartCommandTable[] = + static ChatCommand baninfoCommandTable[] = { - { "cancel", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCancelCommand,"", NULL }, - { "" , SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerIdleRestartCommand, "", NULL }, + { "account", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanInfoAccountCommand, "", NULL }, + { "character", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanInfoCharacterCommand, "", NULL }, + { "ip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanInfoIPCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; - static ChatCommand serverIdleShutdownCommandTable[] = + static ChatCommand banlistCommandTable[] = { - { "cancel", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCancelCommand,"", NULL }, - { "" , SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerIdleShutDownCommand, "", NULL }, + { "account", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanListAccountCommand, "", NULL }, + { "character", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanListCharacterCommand, "", NULL }, + { "ip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanListIPCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; - static ChatCommand serverRestartCommandTable[] = + static ChatCommand castCommandTable[] = { - { "cancel", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCancelCommand,"", NULL }, - { "" , SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerRestartCommand, "", NULL }, + { "back", SEC_ADMINISTRATOR, false, &ChatHandler::HandleCastBackCommand, "", NULL }, + { "dist", SEC_ADMINISTRATOR, false, &ChatHandler::HandleCastDistCommand, "", NULL }, + { "self", SEC_ADMINISTRATOR, false, &ChatHandler::HandleCastSelfCommand, "", NULL }, + { "target", SEC_ADMINISTRATOR, false, &ChatHandler::HandleCastTargetCommand, "", NULL }, + { "", SEC_ADMINISTRATOR, false, &ChatHandler::HandleCastCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; - static ChatCommand serverShutdownCommandTable[] = + static ChatCommand debugPlayCommandTable[] = { - { "cancel", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCancelCommand,"", NULL }, - { "" , SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } + { "cinematic", SEC_MODERATOR, false, &ChatHandler::HandleDebugPlayCinematicCommand, "", NULL }, + { "movie", SEC_MODERATOR, false, &ChatHandler::HandleDebugPlayMovieCommand, "", NULL }, + { "sound", SEC_MODERATOR, false, &ChatHandler::HandleDebugPlaySoundCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } }; - static ChatCommand serverCommandTable[] = + static ChatCommand debugSendCommandTable[] = { - { "corpses", SEC_GAMEMASTER, true, &ChatHandler::HandleServerCorpsesCommand, "", NULL }, - { "exit", SEC_CONSOLE, true, &ChatHandler::HandleServerExitCommand, "", NULL }, - { "idlerestart", SEC_ADMINISTRATOR, true, NULL, "", serverIdleRestartCommandTable }, - { "idleshutdown", SEC_ADMINISTRATOR, true, NULL, "", serverShutdownCommandTable }, - { "info", SEC_PLAYER, true, &ChatHandler::HandleServerInfoCommand, "", NULL }, - { "motd", SEC_PLAYER, true, &ChatHandler::HandleServerMotdCommand, "", NULL }, - { "restart", SEC_ADMINISTRATOR, true, NULL, "", serverRestartCommandTable }, - { "shutdown", SEC_ADMINISTRATOR, true, NULL, "", serverShutdownCommandTable }, - { "set", SEC_ADMINISTRATOR, true, NULL, "", serverSetCommandTable }, - { NULL, 0, false, NULL, "", NULL } + { "buyerror", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendBuyErrorCommand, "", NULL }, + { "channelnotify", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendChannelNotifyCommand, "", NULL }, + { "chatmmessage", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendChatMsgCommand, "", NULL }, + { "equiperror", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendEquipErrorCommand, "", NULL }, + { "largepacket", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendLargePacketCommand, "", NULL }, + { "opcode", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendOpcodeCommand, "", NULL }, + { "poi", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendPoiCommand, "", NULL }, + { "qpartymsg", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendQuestPartyMsgCommand, "", NULL }, + { "qinvalidmsg", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendQuestInvalidMsgCommand, "", NULL }, + { "sellerror", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendSellErrorCommand, "", NULL }, + { "setphaseshift", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendSetPhaseShiftCommand, "", NULL }, + { "spellfail", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendSpellFailCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } }; - static ChatCommand modifyCommandTable[] = + static ChatCommand debugCommandTable[] = { - { "hp", SEC_MODERATOR, false, &ChatHandler::HandleModifyHPCommand, "", NULL }, - { "mana", SEC_MODERATOR, false, &ChatHandler::HandleModifyManaCommand, "", NULL }, - { "rage", SEC_MODERATOR, false, &ChatHandler::HandleModifyRageCommand, "", NULL }, - { "energy", SEC_MODERATOR, false, &ChatHandler::HandleModifyEnergyCommand, "", NULL }, - { "money", SEC_MODERATOR, false, &ChatHandler::HandleModifyMoneyCommand, "", NULL }, - { "speed", SEC_MODERATOR, false, &ChatHandler::HandleModifySpeedCommand, "", NULL }, - { "swim", SEC_MODERATOR, false, &ChatHandler::HandleModifySwimCommand, "", NULL }, - { "scale", SEC_MODERATOR, false, &ChatHandler::HandleModifyScaleCommand, "", NULL }, - { "bit", SEC_MODERATOR, false, &ChatHandler::HandleModifyBitCommand, "", NULL }, - { "bwalk", SEC_MODERATOR, false, &ChatHandler::HandleModifyBWalkCommand, "", NULL }, - { "fly", SEC_MODERATOR, false, &ChatHandler::HandleModifyFlyCommand, "", NULL }, - { "aspeed", SEC_MODERATOR, false, &ChatHandler::HandleModifyASpeedCommand, "", NULL }, - { "faction", SEC_MODERATOR, false, &ChatHandler::HandleModifyFactionCommand, "", NULL }, - { "spell", SEC_MODERATOR, false, &ChatHandler::HandleModifySpellCommand, "", NULL }, - { "tp", SEC_MODERATOR, false, &ChatHandler::HandleModifyTalentCommand, "", NULL }, - { "titles", SEC_MODERATOR, false, &ChatHandler::HandleModifyKnownTitlesCommand, "", NULL }, - { "mount", SEC_MODERATOR, false, &ChatHandler::HandleModifyMountCommand, "", NULL }, - { "honor", SEC_MODERATOR, false, &ChatHandler::HandleModifyHonorCommand, "", NULL }, - { "rep", SEC_MODERATOR, false, &ChatHandler::HandleModifyRepCommand, "", NULL }, - { "arena", SEC_MODERATOR, false, &ChatHandler::HandleModifyArenaCommand, "", NULL }, - { "drunk", SEC_MODERATOR, false, &ChatHandler::HandleDrunkCommand, "", NULL }, - { "standstate", SEC_GAMEMASTER, false, &ChatHandler::HandleStandStateCommand, "", NULL }, - { "morph", SEC_GAMEMASTER, false, &ChatHandler::HandleMorphCommand, "", NULL }, - { "gender", SEC_ADMINISTRATOR, false, &ChatHandler::HandleModifyGenderCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } + { "setbit", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSet32Bit, "", NULL }, + { "threat", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugThreatList, "", NULL }, + { "hostil", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugHostilRefList, "", NULL }, + { "anim", SEC_GAMEMASTER, false, &ChatHandler::HandleDebugAnimCommand, "", NULL }, + { "arena", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugArenaCommand, "", NULL }, + { "bg", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugBattlegroundCommand, "", NULL }, + { "getitemstate", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugGetItemState, "", NULL }, + { "lootrecipient", SEC_GAMEMASTER, false, &ChatHandler::HandleDebugGetLootRecipient, "", NULL }, + { "getvalue", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugGetValue, "", NULL }, + { "Mod32Value", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugMod32Value, "", NULL }, + { "play", SEC_MODERATOR, false, NULL, "", debugPlayCommandTable }, + { "send", SEC_ADMINISTRATOR, false, NULL, "", debugSendCommandTable }, + { "setitemflag", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSetItemFlagCommand, "", NULL }, + { "setvalue", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSetValue, "", NULL }, + { "spawnvehicle", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSpawnVehicle, "", NULL }, + { "uws", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugUpdateWorldStateCommand, "", NULL }, + { "update", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugUpdate, "", NULL }, + { NULL, 0, false, NULL, "", NULL } }; - static ChatCommand wpCommandTable[] = + static ChatCommand eventCommandTable[] = { - { "show", SEC_GAMEMASTER, false, &ChatHandler::HandleWpShowCommand, "", NULL }, - { "addwp", SEC_GAMEMASTER, false, &ChatHandler::HandleWpAddCommand, "", NULL }, - { "load", SEC_GAMEMASTER, false, &ChatHandler::HandleWpLoadPathCommand, "", NULL }, - { "modify", SEC_GAMEMASTER, false, &ChatHandler::HandleWpModifyCommand, "", NULL }, - { "event", SEC_GAMEMASTER, false, &ChatHandler::HandleWpEventCommand, "", NULL }, - { "unload", SEC_GAMEMASTER, false, &ChatHandler::HandleWpUnLoadPathCommand, "", NULL }, - + { "activelist", SEC_GAMEMASTER, true, &ChatHandler::HandleEventActiveListCommand, "", NULL }, + { "start", SEC_GAMEMASTER, true, &ChatHandler::HandleEventStartCommand, "", NULL }, + { "stop", SEC_GAMEMASTER, true, &ChatHandler::HandleEventStopCommand, "", NULL }, + { "", SEC_GAMEMASTER, true, &ChatHandler::HandleEventInfoCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; - - static ChatCommand banCommandTable[] = + static ChatCommand gmCommandTable[] = { - { "account", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanAccountCommand, "", NULL }, - { "character", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanCharacterCommand, "", NULL }, - { "ip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanIPCommand, "", NULL }, + { "chat", SEC_MODERATOR, false, &ChatHandler::HandleGMChatCommand, "", NULL }, + { "fly", SEC_ADMINISTRATOR, false, &ChatHandler::HandleGMFlyCommand, "", NULL }, + { "ingame", SEC_PLAYER, true, &ChatHandler::HandleGMListIngameCommand, "", NULL }, + { "list", SEC_ADMINISTRATOR, true, &ChatHandler::HandleGMListFullCommand, "", NULL }, + { "visible", SEC_MODERATOR, false, &ChatHandler::HandleGMVisibleCommand, "", NULL }, + { "", SEC_MODERATOR, false, &ChatHandler::HandleGMCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; - static ChatCommand baninfoCommandTable[] = + static ChatCommand goCommandTable[] = { - { "account", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanInfoAccountCommand, "", NULL }, - { "character", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanInfoCharacterCommand, "", NULL }, - { "ip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanInfoIPCommand, "", NULL }, + { "grid", SEC_MODERATOR, false, &ChatHandler::HandleGoGridCommand, "", NULL }, + { "creature", SEC_MODERATOR, false, &ChatHandler::HandleGoCreatureCommand, "", NULL }, + { "object", SEC_MODERATOR, false, &ChatHandler::HandleGoObjectCommand, "", NULL }, + { "trigger", SEC_MODERATOR, false, &ChatHandler::HandleGoTriggerCommand, "", NULL }, + { "ticket", SEC_MODERATOR, false, &ChatHandler::HandleGoTicketCommand, "", NULL }, + { "graveyard", SEC_MODERATOR, false, &ChatHandler::HandleGoGraveyardCommand, "", NULL }, + { "zonexy", SEC_MODERATOR, false, &ChatHandler::HandleGoZoneXYCommand, "", NULL }, + { "xy", SEC_MODERATOR, false, &ChatHandler::HandleGoXYCommand, "", NULL }, + { "xyz", SEC_MODERATOR, false, &ChatHandler::HandleGoXYZCommand, "", NULL }, + { "", SEC_MODERATOR, false, &ChatHandler::HandleGoXYZCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; - static ChatCommand banlistCommandTable[] = + static ChatCommand gobjectCommandTable[] = { - { "account", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanListAccountCommand, "", NULL }, - { "character", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanListCharacterCommand, "", NULL }, - { "ip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanListIPCommand, "", NULL }, + { "activate", SEC_GAMEMASTER, false, &ChatHandler::HandleActivateObjectCommand, "", NULL }, + { "add", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectAddCommand, "", NULL }, + { "delete", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectDeleteCommand, "", NULL }, + { "move", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectMoveCommand, "", NULL }, + { "near", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectNearCommand, "", NULL }, + { "setphase", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectPhaseCommand, "", NULL }, + { "target", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectTargetCommand, "", NULL }, + { "tempadd", SEC_GAMEMASTER, false, &ChatHandler::HandleTempGameObjectCommand, "", NULL }, + { "turn", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectTurnCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; - static ChatCommand unbanCommandTable[] = + static ChatCommand groupCommandTable[] = { - { "account", SEC_ADMINISTRATOR, true, &ChatHandler::HandleUnBanAccountCommand, "", NULL }, - { "character", SEC_ADMINISTRATOR, true, &ChatHandler::HandleUnBanCharacterCommand, "", NULL }, - { "ip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleUnBanIPCommand, "", NULL }, + { "leader", SEC_ADMINISTRATOR, false, &ChatHandler::HandleGroupLeaderCommand, "", NULL }, + { "disband", SEC_ADMINISTRATOR, false, &ChatHandler::HandleGroupDisbandCommand, "", NULL }, + { "remove", SEC_ADMINISTRATOR, false, &ChatHandler::HandleGroupRemoveCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand guildCommandTable[] = + { + { "create", SEC_GAMEMASTER, true, &ChatHandler::HandleGuildCreateCommand, "", NULL }, + { "delete", SEC_GAMEMASTER, true, &ChatHandler::HandleGuildDeleteCommand, "", NULL }, + { "invite", SEC_GAMEMASTER, true, &ChatHandler::HandleGuildInviteCommand, "", NULL }, + { "uninvite", SEC_GAMEMASTER, true, &ChatHandler::HandleGuildUninviteCommand, "", NULL }, + { "rank", SEC_GAMEMASTER, true, &ChatHandler::HandleGuildRankCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; - static ChatCommand debugCommandTable[] = + static ChatCommand honorCommandTable[] = { - { "inarc", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugInArcCommand, "", NULL }, - { "spellfail", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSpellFailCommand, "", NULL }, - { "setpoi", SEC_ADMINISTRATOR, false, &ChatHandler::HandleSetPoiCommand, "", NULL }, - { "qpartymsg", SEC_ADMINISTRATOR, false, &ChatHandler::HandleSendQuestPartyMsgCommand, "", NULL }, - { "qinvalidmsg", SEC_ADMINISTRATOR, false, &ChatHandler::HandleSendQuestInvalidMsgCommand, "", NULL }, - { "equiperr", SEC_ADMINISTRATOR, false, &ChatHandler::HandleEquipErrorCommand, "", NULL }, - { "sellerr", SEC_ADMINISTRATOR, false, &ChatHandler::HandleSellErrorCommand, "", NULL }, - { "buyerr", SEC_ADMINISTRATOR, false, &ChatHandler::HandleBuyErrorCommand, "", NULL }, - { "sendopcode", SEC_ADMINISTRATOR, false, &ChatHandler::HandleSendOpcodeCommand, "", NULL }, - { "uws", SEC_ADMINISTRATOR, false, &ChatHandler::HandleUpdateWorldStateCommand, "", NULL }, - { "ps", SEC_ADMINISTRATOR, false, &ChatHandler::HandlePlaySound2Command, "", NULL }, - { "scn", SEC_ADMINISTRATOR, false, &ChatHandler::HandleSendChannelNotifyCommand, "", NULL }, - { "scm", SEC_ADMINISTRATOR, false, &ChatHandler::HandleSendChatMsgCommand, "", NULL }, - { "getitemstate", SEC_ADMINISTRATOR, false, &ChatHandler::HandleGetItemState, "", NULL }, - { "playsound", SEC_MODERATOR, false, &ChatHandler::HandlePlaySoundCommand, "", NULL }, - { "update", SEC_ADMINISTRATOR, false, &ChatHandler::HandleUpdate, "", NULL }, - { "setvalue", SEC_ADMINISTRATOR, false, &ChatHandler::HandleSetValue, "", NULL }, - { "getvalue", SEC_ADMINISTRATOR, false, &ChatHandler::HandleGetValue, "", NULL }, - { "Mod32Value", SEC_ADMINISTRATOR, false, &ChatHandler::HandleMod32Value, "", NULL }, - { "anim", SEC_GAMEMASTER, false, &ChatHandler::HandleAnimCommand, "", NULL }, - { "lootrecipient", SEC_GAMEMASTER, false, &ChatHandler::HandleGetLootRecipient, "", NULL }, - { "arena", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugArenaCommand, "", NULL }, + { "add", SEC_GAMEMASTER, false, &ChatHandler::HandleHonorAddCommand, "", NULL }, + { "addkill", SEC_GAMEMASTER, false, &ChatHandler::HandleHonorAddKillCommand, "", NULL }, + { "update", SEC_GAMEMASTER, false, &ChatHandler::HandleHonorUpdateCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; - static ChatCommand eventCommandTable[] = + static ChatCommand instanceCommandTable[] = { - { "activelist", SEC_GAMEMASTER, true, &ChatHandler::HandleEventActiveListCommand, "", NULL }, - { "start", SEC_GAMEMASTER, true, &ChatHandler::HandleEventStartCommand, "", NULL }, - { "stop", SEC_GAMEMASTER, true, &ChatHandler::HandleEventStopCommand, "", NULL }, - { "", SEC_GAMEMASTER, true, &ChatHandler::HandleEventInfoCommand, "", NULL }, + { "listbinds", SEC_ADMINISTRATOR, false, &ChatHandler::HandleInstanceListBindsCommand, "", NULL }, + { "unbind", SEC_ADMINISTRATOR, false, &ChatHandler::HandleInstanceUnbindCommand, "", NULL }, + { "stats", SEC_ADMINISTRATOR, true, &ChatHandler::HandleInstanceStatsCommand, "", NULL }, + { "savedata", SEC_ADMINISTRATOR, false, &ChatHandler::HandleInstanceSaveDataCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; @@ -225,6 +247,7 @@ ChatCommand * ChatHandler::getCommandTable() { "all_default", SEC_MODERATOR, false, &ChatHandler::HandleLearnAllDefaultCommand, "", NULL }, { "all_lang", SEC_MODERATOR, false, &ChatHandler::HandleLearnAllLangCommand, "", NULL }, { "all_myclass", SEC_ADMINISTRATOR, false, &ChatHandler::HandleLearnAllMyClassCommand, "", NULL }, + { "all_mypettalents",SEC_ADMINISTRATOR, false, &ChatHandler::HandleLearnAllMyPetTalentsCommand,"", NULL }, { "all_myspells", SEC_ADMINISTRATOR, false, &ChatHandler::HandleLearnAllMySpellsCommand, "", NULL }, { "all_mytalents", SEC_ADMINISTRATOR, false, &ChatHandler::HandleLearnAllMyTalentsCommand, "", NULL }, { "all_recipes", SEC_GAMEMASTER, false, &ChatHandler::HandleLearnAllRecipesCommand, "", NULL }, @@ -232,9 +255,140 @@ ChatCommand * ChatHandler::getCommandTable() { NULL, 0, false, NULL, "", NULL } }; + static ChatCommand listCommandTable[] = + { + { "creature", SEC_ADMINISTRATOR, true, &ChatHandler::HandleListCreatureCommand, "", NULL }, + { "item", SEC_ADMINISTRATOR, true, &ChatHandler::HandleListItemCommand, "", NULL }, + { "object", SEC_ADMINISTRATOR, true, &ChatHandler::HandleListObjectCommand, "", NULL }, + { "auras", SEC_ADMINISTRATOR, false, &ChatHandler::HandleListAurasCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand lookupPlayerCommandTable[] = + { + { "ip", SEC_GAMEMASTER, true, &ChatHandler::HandleLookupPlayerIpCommand, "", NULL }, + { "account", SEC_GAMEMASTER, true, &ChatHandler::HandleLookupPlayerAccountCommand, "", NULL }, + { "email", SEC_GAMEMASTER, true, &ChatHandler::HandleLookupPlayerEmailCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand lookupCommandTable[] = + { + { "area", SEC_MODERATOR, true, &ChatHandler::HandleLookupAreaCommand, "", NULL }, + { "creature", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupCreatureCommand, "", NULL }, + { "event", SEC_GAMEMASTER, true, &ChatHandler::HandleLookupEventCommand, "", NULL }, + { "faction", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupFactionCommand, "", NULL }, + { "item", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupItemCommand, "", NULL }, + { "itemset", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupItemSetCommand, "", NULL }, + { "object", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupObjectCommand, "", NULL }, + { "quest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupQuestCommand, "", NULL }, + { "player", SEC_GAMEMASTER, true, NULL, "", lookupPlayerCommandTable }, + { "skill", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupSkillCommand, "", NULL }, + { "spell", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupSpellCommand, "", NULL }, + { "tele", SEC_MODERATOR, true, &ChatHandler::HandleLookupTeleCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand modifyCommandTable[] = + { + { "hp", SEC_MODERATOR, false, &ChatHandler::HandleModifyHPCommand, "", NULL }, + { "mana", SEC_MODERATOR, false, &ChatHandler::HandleModifyManaCommand, "", NULL }, + { "rage", SEC_MODERATOR, false, &ChatHandler::HandleModifyRageCommand, "", NULL }, + { "runicpower", SEC_MODERATOR, false, &ChatHandler::HandleModifyRunicPowerCommand, "", NULL }, + { "energy", SEC_MODERATOR, false, &ChatHandler::HandleModifyEnergyCommand, "", NULL }, + { "money", SEC_MODERATOR, false, &ChatHandler::HandleModifyMoneyCommand, "", NULL }, + { "speed", SEC_MODERATOR, false, &ChatHandler::HandleModifySpeedCommand, "", NULL }, + { "swim", SEC_MODERATOR, false, &ChatHandler::HandleModifySwimCommand, "", NULL }, + { "scale", SEC_MODERATOR, false, &ChatHandler::HandleModifyScaleCommand, "", NULL }, + { "bit", SEC_MODERATOR, false, &ChatHandler::HandleModifyBitCommand, "", NULL }, + { "bwalk", SEC_MODERATOR, false, &ChatHandler::HandleModifyBWalkCommand, "", NULL }, + { "fly", SEC_MODERATOR, false, &ChatHandler::HandleModifyFlyCommand, "", NULL }, + { "aspeed", SEC_MODERATOR, false, &ChatHandler::HandleModifyASpeedCommand, "", NULL }, + { "faction", SEC_MODERATOR, false, &ChatHandler::HandleModifyFactionCommand, "", NULL }, + { "spell", SEC_MODERATOR, false, &ChatHandler::HandleModifySpellCommand, "", NULL }, + { "tp", SEC_MODERATOR, false, &ChatHandler::HandleModifyTalentCommand, "", NULL }, + { "titles", SEC_MODERATOR, false, &ChatHandler::HandleModifyKnownTitlesCommand, "", NULL }, + { "mount", SEC_MODERATOR, false, &ChatHandler::HandleModifyMountCommand, "", NULL }, + { "honor", SEC_MODERATOR, false, &ChatHandler::HandleModifyHonorCommand, "", NULL }, + { "rep", SEC_GAMEMASTER, false, &ChatHandler::HandleModifyRepCommand, "", NULL }, + { "arena", SEC_MODERATOR, false, &ChatHandler::HandleModifyArenaCommand, "", NULL }, + { "drunk", SEC_MODERATOR, false, &ChatHandler::HandleModifyDrunkCommand, "", NULL }, + { "standstate", SEC_GAMEMASTER, false, &ChatHandler::HandleModifyStandStateCommand, "", NULL }, + { "morph", SEC_GAMEMASTER, false, &ChatHandler::HandleModifyMorphCommand, "", NULL }, + { "phase", SEC_ADMINISTRATOR, false, &ChatHandler::HandleModifyPhaseCommand, "", NULL }, + { "gender", SEC_GAMEMASTER, false, &ChatHandler::HandleModifyGenderCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand npcCommandTable[] = + { + { "add", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcAddCommand, "", NULL }, + { "additem", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcAddVendorItemCommand, "", NULL }, + { "addmove", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcAddMoveCommand, "", NULL }, + { "allowmove", SEC_ADMINISTRATOR, false, &ChatHandler::HandleNpcAllowMovementCommand, "", NULL }, + { "changeentry", SEC_ADMINISTRATOR, false, &ChatHandler::HandleNpcChangeEntryCommand, "", NULL }, + { "changelevel", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcChangeLevelCommand, "", NULL }, + { "delete", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcDeleteCommand, "", NULL }, + { "delitem", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcDelVendorItemCommand, "", NULL }, + { "factionid", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcFactionIdCommand, "", NULL }, + { "flag", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcFlagCommand, "", NULL }, + { "follow", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcFollowCommand, "", NULL }, + { "info", SEC_ADMINISTRATOR, false, &ChatHandler::HandleNpcInfoCommand, "", NULL }, + { "move", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcMoveCommand, "", NULL }, + { "playemote", SEC_ADMINISTRATOR, false, &ChatHandler::HandleNpcPlayEmoteCommand, "", NULL }, + { "setmodel", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSetModelCommand, "", NULL }, + { "setmovetype", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSetMoveTypeCommand, "", NULL }, + { "setphase", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSetPhaseCommand, "", NULL }, + { "spawndist", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSpawnDistCommand, "", NULL }, + { "spawntime", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSpawnTimeCommand, "", NULL }, + { "say", SEC_MODERATOR, false, &ChatHandler::HandleNpcSayCommand, "", NULL }, + { "textemote", SEC_MODERATOR, false, &ChatHandler::HandleNpcTextEmoteCommand, "", NULL }, + { "unfollow", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcUnFollowCommand, "", NULL }, + { "whisper", SEC_MODERATOR, false, &ChatHandler::HandleNpcWhisperCommand, "", NULL }, + { "yell", SEC_MODERATOR, false, &ChatHandler::HandleNpcYellCommand, "", NULL }, + { "tempadd", SEC_GAMEMASTER, false, &ChatHandler::HandleTempAddSpwCommand, "", NULL }, + { "tame", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcTameCommand, "", NULL }, + { "setdeathstate", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSetDeathStateCommand, "", NULL }, + { "addformation", SEC_MODERATOR, false, &ChatHandler::HandleNpcAddFormationCommand, "", NULL }, + + //{ TODO: fix or remove this commands + { "addweapon", SEC_ADMINISTRATOR, false, &ChatHandler::HandleNpcAddWeaponCommand, "", NULL }, + { "name", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcNameCommand, "", NULL }, + { "subname", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSubNameCommand, "", NULL }, + //} + + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand petCommandTable[] = + { + { "create", SEC_GAMEMASTER, false, &ChatHandler::HandleCreatePetCommand, "", NULL }, + { "learn", SEC_GAMEMASTER, false, &ChatHandler::HandlePetLearnCommand, "", NULL }, + { "unlearn", SEC_GAMEMASTER, false, &ChatHandler::HandlePetUnlearnCommand, "", NULL }, + { "tp", SEC_GAMEMASTER, false, &ChatHandler::HandlePetTpCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand pdumpCommandTable[] = + { + { "load", SEC_ADMINISTRATOR, true, &ChatHandler::HandlePDumpLoadCommand, "", NULL }, + { "write", SEC_ADMINISTRATOR, true, &ChatHandler::HandlePDumpWriteCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand questCommandTable[] = + { + { "add", SEC_ADMINISTRATOR, false, &ChatHandler::HandleQuestAdd, "", NULL }, + { "complete", SEC_ADMINISTRATOR, false, &ChatHandler::HandleQuestComplete, "", NULL }, + { "remove", SEC_ADMINISTRATOR, false, &ChatHandler::HandleQuestRemove, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + static ChatCommand reloadCommandTable[] = { { "all", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllCommand, "", NULL }, + { "all_achievement",SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllAchievementCommand,"", NULL }, + { "all_area", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllAreaCommand, "", NULL }, { "all_loot", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllLootCommand, "", NULL }, { "all_npc", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllNpcCommand, "", NULL }, { "all_quest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllQuestCommand, "", NULL }, @@ -245,6 +399,8 @@ ChatCommand * ChatHandler::getCommandTable() { "config", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadConfigCommand, "", NULL }, + { "achievement_criteria_data", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAchievementCriteriaDataCommand, "", NULL }, + { "achievement_reward", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAchievementRewardCommand, "", NULL }, { "areatrigger_tavern", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAreaTriggerTavernCommand, "", NULL }, { "areatrigger_teleport", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAreaTriggerTeleportCommand, "", NULL }, { "access_requirement", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAccessRequirementCommand, "", NULL }, @@ -265,13 +421,23 @@ ChatCommand * ChatHandler::getCommandTable() { "gameobject_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadGameObjectScriptsCommand, "", NULL }, { "item_enchantment_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadItemEnchantementsCommand, "", NULL }, { "item_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesItemCommand, "", NULL }, - { "trinity_string", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadTrinityStringCommand, "", NULL }, + { "locales_achievement_reward", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesAchievementRewardCommand,"", NULL }, + { "locales_creature", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesCreatureCommand, "", NULL }, + { "locales_gameobject", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesGameobjectCommand, "", NULL }, + { "locales_item", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesItemCommand, "", NULL }, + { "locales_npc_text", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesNpcTextCommand, "", NULL }, + { "locales_page_text", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesPageTextCommand, "", NULL }, + { "locales_points_of_interest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesPointsOfInterestCommand, "", NULL }, + { "locales_quest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesQuestCommand, "", NULL }, +// { "auctions", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAuctionsCommand, "", NULL }, + { "milling_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesMillingCommand, "", NULL }, { "npc_gossip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadNpcGossipCommand, "", NULL }, { "npc_option", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadNpcOptionCommand, "", NULL }, { "npc_trainer", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadNpcTrainerCommand, "", NULL }, { "npc_vendor", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadNpcVendorCommand, "", NULL }, { "page_text", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadPageTextsCommand, "", NULL }, { "pickpocketing_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesPickpocketingCommand,"",NULL}, + { "points_of_interest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadPointsOfInterestCommand, "",NULL}, { "prospecting_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesProspectingCommand,"", NULL }, { "quest_mail_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesQuestMailCommand, "", NULL }, { "quest_end_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadQuestEndScriptsCommand, "", NULL }, @@ -285,93 +451,30 @@ ChatCommand * ChatHandler::getCommandTable() { "skinning_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesSkinningCommand, "", NULL }, { "spell_affect", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellAffectCommand, "", NULL }, { "spell_required", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellRequiredCommand, "", NULL }, + { "spell_area", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellAreaCommand, "", NULL }, { "spell_elixir", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellElixirCommand, "", NULL }, { "spell_learn_spell", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellLearnSpellCommand, "", NULL }, + { "spell_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesSpellCommand, "", NULL }, { "spell_linked_spell", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellLinkedSpellCommand, "", NULL }, { "spell_pet_auras", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellPetAurasCommand, "", NULL }, { "spell_proc_event", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellProcEventCommand, "", NULL }, + { "spell_bonus_data", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellBonusesCommand, "", NULL }, { "spell_script_target", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellScriptTargetCommand, "", NULL }, { "spell_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellScriptsCommand, "", NULL }, { "spell_target_position", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellTargetPositionCommand, "", NULL }, { "spell_threats", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellThreatsCommand, "", NULL }, { "spell_disabled", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellDisabledCommand, "", NULL }, - { "locales_creature", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesCreatureCommand, "", NULL }, - { "locales_gameobject", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesGameobjectCommand, "", NULL }, - { "locales_item", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesItemCommand, "", NULL }, - { "locales_npc_text", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesNpcTextCommand, "", NULL }, - { "locales_page_text", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesPageTextCommand, "", NULL }, - { "locales_quest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesQuestCommand, "", NULL }, + { "trinity_string", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadTrinityStringCommand, "", NULL }, { "auctions", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAuctionsCommand, "", NULL }, { "waypoint_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadWpScriptsCommand, "", NULL }, { "gm_tickets", SEC_ADMINISTRATOR, true, &ChatHandler::HandleGMTicketReloadCommand, "", NULL }, - { "", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; - static ChatCommand honorCommandTable[] = - { - { "add", SEC_GAMEMASTER, false, &ChatHandler::HandleAddHonorCommand, "", NULL }, - { "addkill", SEC_GAMEMASTER, false, &ChatHandler::HandleHonorAddKillCommand, "", NULL }, - { "update", SEC_GAMEMASTER, false, &ChatHandler::HandleUpdateHonorFieldsCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand guildCommandTable[] = - { - { "create", SEC_GAMEMASTER, true, &ChatHandler::HandleGuildCreateCommand, "", NULL }, - { "delete", SEC_GAMEMASTER, true, &ChatHandler::HandleGuildDeleteCommand, "", NULL }, - { "invite", SEC_GAMEMASTER, true, &ChatHandler::HandleGuildInviteCommand, "", NULL }, - { "uninvite", SEC_GAMEMASTER, true, &ChatHandler::HandleGuildUninviteCommand, "", NULL }, - { "rank", SEC_GAMEMASTER, true, &ChatHandler::HandleGuildRankCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand petCommandTable[] = - { - { "create", SEC_GAMEMASTER, false, &ChatHandler::HandleCreatePetCommand, "", NULL }, - { "learn", SEC_GAMEMASTER, false, &ChatHandler::HandlePetLearnCommand, "", NULL }, - { "unlearn", SEC_GAMEMASTER, false, &ChatHandler::HandlePetUnlearnCommand, "", NULL }, - { "tp", SEC_GAMEMASTER, false, &ChatHandler::HandlePetTpCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - - static ChatCommand groupCommandTable[] = - { - { "leader", SEC_ADMINISTRATOR, false, &ChatHandler::HandleGroupLeaderCommand, "", NULL }, - { "disband", SEC_ADMINISTRATOR, false, &ChatHandler::HandleGroupDisbandCommand, "", NULL }, - { "remove", SEC_ADMINISTRATOR, false, &ChatHandler::HandleGroupRemoveCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand lookupPlayerCommandTable[] = - { - { "ip", SEC_GAMEMASTER, true, &ChatHandler::HandleLookupPlayerIpCommand, "", NULL }, - { "account", SEC_GAMEMASTER, true, &ChatHandler::HandleLookupPlayerAccountCommand, "", NULL }, - { "email", SEC_GAMEMASTER, true, &ChatHandler::HandleLookupPlayerEmailCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand lookupCommandTable[] = - { - { "area", SEC_MODERATOR, true, &ChatHandler::HandleLookupAreaCommand, "", NULL }, - { "creature", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupCreatureCommand, "", NULL }, - { "event", SEC_GAMEMASTER, true, &ChatHandler::HandleLookupEventCommand, "", NULL }, - { "faction", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupFactionCommand, "", NULL }, - { "item", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupItemCommand, "", NULL }, - { "itemset", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupItemSetCommand, "", NULL }, - { "object", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupObjectCommand, "", NULL }, - { "quest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupQuestCommand, "", NULL }, - { "player", SEC_GAMEMASTER, true, NULL, "", lookupPlayerCommandTable }, - { "skill", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupSkillCommand, "", NULL }, - { "spell", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupSpellCommand, "", NULL }, - { "tele", SEC_MODERATOR, true, &ChatHandler::HandleLookupTeleCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - static ChatCommand resetCommandTable[] = { + { "achievements", SEC_ADMINISTRATOR, false, &ChatHandler::HandleResetAchievementsCommand, "", NULL }, { "honor", SEC_ADMINISTRATOR, false, &ChatHandler::HandleResetHonorCommand, "", NULL }, { "level", SEC_ADMINISTRATOR, false, &ChatHandler::HandleResetLevelCommand, "", NULL }, { "spells", SEC_ADMINISTRATOR, false, &ChatHandler::HandleResetSpellsCommand, "", NULL }, @@ -381,130 +484,93 @@ ChatCommand * ChatHandler::getCommandTable() { NULL, 0, false, NULL, "", NULL } }; - static ChatCommand castCommandTable[] = + static ChatCommand sendCommandTable[] = { - { "back", SEC_ADMINISTRATOR, false, &ChatHandler::HandleCastBackCommand, "", NULL }, - { "dist", SEC_ADMINISTRATOR, false, &ChatHandler::HandleCastDistCommand, "", NULL }, - { "self", SEC_ADMINISTRATOR, false, &ChatHandler::HandleCastSelfCommand, "", NULL }, - { "target", SEC_ADMINISTRATOR, false, &ChatHandler::HandleCastTargetCommand, "", NULL }, - { "", SEC_ADMINISTRATOR, false, &ChatHandler::HandleCastCommand, "", NULL }, + { "items", SEC_ADMINISTRATOR, true, &ChatHandler::HandleSendItemsCommand, "", NULL }, + { "mail", SEC_MODERATOR, true, &ChatHandler::HandleSendMailCommand, "", NULL }, + { "message", SEC_ADMINISTRATOR, true, &ChatHandler::HandleSendMessageCommand, "", NULL }, + { "money", SEC_ADMINISTRATOR, true, &ChatHandler::HandleSendMoneyCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; - static ChatCommand pdumpCommandTable[] = + static ChatCommand serverIdleRestartCommandTable[] = { - { "load", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLoadPDumpCommand, "", NULL }, - { "write", SEC_ADMINISTRATOR, true, &ChatHandler::HandleWritePDumpCommand, "", NULL }, + { "cancel", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCancelCommand,"", NULL }, + { "" , SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerIdleRestartCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; - static ChatCommand listCommandTable[] = + static ChatCommand serverIdleShutdownCommandTable[] = { - { "creature", SEC_ADMINISTRATOR, true, &ChatHandler::HandleListCreatureCommand, "", NULL }, - { "item", SEC_ADMINISTRATOR, true, &ChatHandler::HandleListItemCommand, "", NULL }, - { "object", SEC_ADMINISTRATOR, true, &ChatHandler::HandleListObjectCommand, "", NULL }, - { "auras", SEC_ADMINISTRATOR, false, &ChatHandler::HandleListAurasCommand, "", NULL }, + { "cancel", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCancelCommand,"", NULL }, + { "" , SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerIdleShutDownCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; - static ChatCommand teleCommandTable[] = + static ChatCommand serverRestartCommandTable[] = { - { "add", SEC_ADMINISTRATOR, false, &ChatHandler::HandleAddTeleCommand, "", NULL }, - { "del", SEC_ADMINISTRATOR, true, &ChatHandler::HandleDelTeleCommand, "", NULL }, - { "name", SEC_MODERATOR, true, &ChatHandler::HandleNameTeleCommand, "", NULL }, - { "group", SEC_MODERATOR, false, &ChatHandler::HandleGroupTeleCommand, "", NULL }, - { "", SEC_MODERATOR, false, &ChatHandler::HandleTeleCommand, "", NULL }, + { "cancel", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCancelCommand,"", NULL }, + { "" , SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerRestartCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; - static ChatCommand npcCommandTable[] = + static ChatCommand serverShutdownCommandTable[] = { - { "say", SEC_MODERATOR, false, &ChatHandler::HandleNpcSayCommand, "", NULL }, - { "textemote", SEC_MODERATOR, false, &ChatHandler::HandleNpcTextEmoteCommand, "", NULL }, - { "add", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcAddCommand, "", NULL }, - { "delete", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcDeleteCommand, "", NULL }, - { "spawndist", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSpawnDistCommand, "", NULL }, - { "spawntime", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSpawnTimeCommand, "", NULL }, - { "factionid", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcFactionIdCommand, "", NULL }, - { "addmove", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcAddMoveCommand, "", NULL }, - { "setmovetype", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSetMoveTypeCommand, "", NULL }, - { "move", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcMoveCommand, "", NULL }, - { "changelevel", SEC_GAMEMASTER, false, &ChatHandler::HandleChangeLevelCommand, "", NULL }, - { "setmodel", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSetModelCommand, "", NULL }, - { "additem", SEC_GAMEMASTER, false, &ChatHandler::HandleAddVendorItemCommand, "", NULL }, - { "delitem", SEC_GAMEMASTER, false, &ChatHandler::HandleDelVendorItemCommand, "", NULL }, - { "flag", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcFlagCommand, "", NULL }, - { "changeentry", SEC_ADMINISTRATOR, false, &ChatHandler::HandleNpcChangeEntryCommand, "", NULL }, - { "info", SEC_ADMINISTRATOR, false, &ChatHandler::HandleNpcInfoCommand, "", NULL }, - { "playemote", SEC_ADMINISTRATOR, false, &ChatHandler::HandleNpcPlayEmoteCommand, "", NULL }, - { "follow", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcFollowCommand, "", NULL }, - { "unfollow", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcUnFollowCommand, "", NULL }, - { "whisper", SEC_MODERATOR, false, &ChatHandler::HandleNpcWhisperCommand, "", NULL }, - { "yell", SEC_MODERATOR, false, &ChatHandler::HandleNpcYellCommand, "", NULL }, - { "addtemp", SEC_GAMEMASTER, false, &ChatHandler::HandleTempAddSpwCommand, "", NULL }, - { "addformation", SEC_MODERATOR, false, &ChatHandler::HandleNpcAddFormationCommand, "", NULL }, - - //{ TODO: fix or remove this commands - { "name", SEC_GAMEMASTER, false, &ChatHandler::HandleNameCommand, "", NULL }, - { "subname", SEC_GAMEMASTER, false, &ChatHandler::HandleSubNameCommand, "", NULL }, - { "addweapon", SEC_ADMINISTRATOR, false, &ChatHandler::HandleAddWeaponCommand, "", NULL }, - //} - + { "cancel", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCancelCommand,"", NULL }, + { "" , SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; - static ChatCommand goCommandTable[] = + static ChatCommand serverSetCommandTable[] = { - { "grid", SEC_MODERATOR, false, &ChatHandler::HandleGoGridCommand, "", NULL }, - { "creature", SEC_GAMEMASTER, false, &ChatHandler::HandleGoCreatureCommand, "", NULL }, - { "object", SEC_GAMEMASTER, false, &ChatHandler::HandleGoObjectCommand, "", NULL }, - { "trigger", SEC_GAMEMASTER, false, &ChatHandler::HandleGoTriggerCommand, "", NULL }, - { "graveyard", SEC_GAMEMASTER, false, &ChatHandler::HandleGoGraveyardCommand, "", NULL }, - { "zonexy", SEC_MODERATOR, false, &ChatHandler::HandleGoZoneXYCommand, "", NULL }, - { "xy", SEC_MODERATOR, false, &ChatHandler::HandleGoXYCommand, "", NULL }, - { "xyz", SEC_MODERATOR, false, &ChatHandler::HandleGoXYZCommand, "", NULL }, - { "", SEC_MODERATOR, false, &ChatHandler::HandleGoXYZCommand, "", NULL }, + { "difftime", SEC_CONSOLE, true, &ChatHandler::HandleServerSetDiffTimeCommand, "", NULL }, + { "loglevel", SEC_CONSOLE, true, &ChatHandler::HandleServerSetLogLevelCommand, "", NULL }, + { "logfilelevel", SEC_CONSOLE, true, &ChatHandler::HandleServerSetLogFileLevelCommand, "", NULL }, + { "motd", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerSetMotdCommand, "", NULL }, + { "closed", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerSetClosedCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; - static ChatCommand gobjectCommandTable[] = + static ChatCommand serverCommandTable[] = { - { "add", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectCommand, "", NULL }, - { "delete", SEC_GAMEMASTER, false, &ChatHandler::HandleDelObjectCommand, "", NULL }, - { "target", SEC_GAMEMASTER, false, &ChatHandler::HandleTargetObjectCommand, "", NULL }, - { "turn", SEC_GAMEMASTER, false, &ChatHandler::HandleTurnObjectCommand, "", NULL }, - { "move", SEC_GAMEMASTER, false, &ChatHandler::HandleMoveObjectCommand, "", NULL }, - { "near", SEC_ADMINISTRATOR, false, &ChatHandler::HandleNearObjectCommand, "", NULL }, - { "activate", SEC_GAMEMASTER, false, &ChatHandler::HandleActivateObjectCommand, "", NULL }, - { "addtemp", SEC_GAMEMASTER, false, &ChatHandler::HandleTempGameObjectCommand, "", NULL }, + { "corpses", SEC_GAMEMASTER, true, &ChatHandler::HandleServerCorpsesCommand, "", NULL }, + { "exit", SEC_CONSOLE, true, &ChatHandler::HandleServerExitCommand, "", NULL }, + { "idlerestart", SEC_ADMINISTRATOR, true, NULL, "", serverIdleRestartCommandTable }, + { "idleshutdown", SEC_ADMINISTRATOR, true, NULL, "", serverShutdownCommandTable }, + { "info", SEC_PLAYER, true, &ChatHandler::HandleServerInfoCommand, "", NULL }, + { "motd", SEC_PLAYER, true, &ChatHandler::HandleServerMotdCommand, "", NULL }, + { "restart", SEC_ADMINISTRATOR, true, NULL, "", serverRestartCommandTable }, + { "shutdown", SEC_ADMINISTRATOR, true, NULL, "", serverShutdownCommandTable }, + { "set", SEC_ADMINISTRATOR, true, NULL, "", serverSetCommandTable }, { NULL, 0, false, NULL, "", NULL } }; - static ChatCommand questCommandTable[] = + static ChatCommand teleCommandTable[] = { - { "add", SEC_ADMINISTRATOR, false, &ChatHandler::HandleAddQuest, "", NULL }, - { "complete", SEC_ADMINISTRATOR, false, &ChatHandler::HandleCompleteQuest, "", NULL }, - { "remove", SEC_ADMINISTRATOR, false, &ChatHandler::HandleRemoveQuest, "", NULL }, + { "add", SEC_ADMINISTRATOR, false, &ChatHandler::HandleTeleAddCommand, "", NULL }, + { "del", SEC_ADMINISTRATOR, true, &ChatHandler::HandleTeleDelCommand, "", NULL }, + { "name", SEC_MODERATOR, true, &ChatHandler::HandleTeleNameCommand, "", NULL }, + { "group", SEC_MODERATOR, false, &ChatHandler::HandleTeleGroupCommand, "", NULL }, + { "", SEC_MODERATOR, false, &ChatHandler::HandleTeleCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; - static ChatCommand gmCommandTable[] = + static ChatCommand unbanCommandTable[] = { - { "chat", SEC_MODERATOR, false, &ChatHandler::HandleGMChatCommand, "", NULL }, - { "ingame", SEC_PLAYER, true, &ChatHandler::HandleGMListIngameCommand, "", NULL }, - { "list", SEC_ADMINISTRATOR, true, &ChatHandler::HandleGMListFullCommand, "", NULL }, - { "visible", SEC_MODERATOR, false, &ChatHandler::HandleVisibleCommand, "", NULL }, - { "fly", SEC_ADMINISTRATOR, false, &ChatHandler::HandleFlyModeCommand, "", NULL }, - { "", SEC_MODERATOR, false, &ChatHandler::HandleGMmodeCommand, "", NULL }, + { "account", SEC_ADMINISTRATOR, true, &ChatHandler::HandleUnBanAccountCommand, "", NULL }, + { "character", SEC_ADMINISTRATOR, true, &ChatHandler::HandleUnBanCharacterCommand, "", NULL }, + { "ip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleUnBanIPCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; - static ChatCommand instanceCommandTable[] = + static ChatCommand wpCommandTable[] = { - { "listbinds", SEC_MODERATOR, false, &ChatHandler::HandleInstanceListBindsCommand, "", NULL }, - { "unbind", SEC_MODERATOR, false, &ChatHandler::HandleInstanceUnbindCommand, "", NULL }, - { "stats", SEC_MODERATOR, true, &ChatHandler::HandleInstanceStatsCommand, "", NULL }, - { "savedata", SEC_MODERATOR, false, &ChatHandler::HandleInstanceSaveDataCommand, "", NULL }, + { "show", SEC_GAMEMASTER, false, &ChatHandler::HandleWpShowCommand, "", NULL }, + { "addwp", SEC_GAMEMASTER, false, &ChatHandler::HandleWpAddCommand, "", NULL }, + { "load", SEC_GAMEMASTER, false, &ChatHandler::HandleWpLoadPathCommand, "", NULL }, + { "modify", SEC_GAMEMASTER, false, &ChatHandler::HandleWpModifyCommand, "", NULL }, + { "event", SEC_GAMEMASTER, false, &ChatHandler::HandleWpEventCommand, "", NULL }, + { "unload", SEC_GAMEMASTER, false, &ChatHandler::HandleWpUnLoadPathCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; @@ -539,8 +605,10 @@ ChatCommand * ChatHandler::getCommandTable() { "honor", SEC_GAMEMASTER, false, NULL, "", honorCommandTable }, //wp commands - { "path", SEC_GAMEMASTER, false, NULL, "", wpCommandTable }, + { "wp", SEC_GAMEMASTER, false, NULL, "", wpCommandTable }, { "loadpath", SEC_ADMINISTRATOR, false, &ChatHandler::HandleReloadAllPaths, "", NULL }, + // AH bot + { "ahbotoptions", SEC_GAMEMASTER, false, &ChatHandler::HandleAHBotOptionsCommand, "", NULL }, { "quest", SEC_ADMINISTRATOR, false, NULL, "", questCommandTable }, { "reload", SEC_ADMINISTRATOR, true, NULL, "", reloadCommandTable }, @@ -588,7 +656,6 @@ ChatCommand * ChatHandler::getCommandTable() { "plimit", SEC_ADMINISTRATOR, true, &ChatHandler::HandlePLimitCommand, "", NULL }, { "start", SEC_PLAYER, false, &ChatHandler::HandleStartCommand, "", NULL }, { "taxicheat", SEC_MODERATOR, false, &ChatHandler::HandleTaxiCheatCommand, "", NULL }, - { "allowmove", SEC_ADMINISTRATOR, false, &ChatHandler::HandleAllowMovementCommand, "", NULL }, { "linkgrave", SEC_ADMINISTRATOR, false, &ChatHandler::HandleLinkGraveCommand, "", NULL }, { "neargrave", SEC_ADMINISTRATOR, false, &ChatHandler::HandleNearGraveCommand, "", NULL }, { "explorecheat", SEC_ADMINISTRATOR, false, &ChatHandler::HandleExploreCheatCommand, "", NULL }, @@ -608,21 +675,18 @@ ChatCommand * ChatHandler::getCommandTable() { "password", SEC_PLAYER, false, &ChatHandler::HandlePasswordCommand, "", NULL }, { "lockaccount", SEC_PLAYER, false, &ChatHandler::HandleLockAccountCommand, "", NULL }, { "respawn", SEC_ADMINISTRATOR, false, &ChatHandler::HandleRespawnCommand, "", NULL }, - { "senditems", SEC_ADMINISTRATOR, true, &ChatHandler::HandleSendItemsCommand, "", NULL }, - { "sendmail", SEC_MODERATOR, true, &ChatHandler::HandleSendMailCommand, "", NULL }, - { "sendmoney", SEC_ADMINISTRATOR, true, &ChatHandler::HandleSendMoneyCommand, "", NULL }, + { "send", SEC_MODERATOR, true, NULL, "", sendCommandTable }, { "rename", SEC_GAMEMASTER, true, &ChatHandler::HandleRenameCommand, "", NULL }, + { "customize", SEC_GAMEMASTER, true, &ChatHandler::HandleCustomizeCommand, "", NULL }, { "loadscripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLoadScriptsCommand, "", NULL }, - { "mute", SEC_GAMEMASTER, true, &ChatHandler::HandleMuteCommand, "", NULL }, - { "unmute", SEC_GAMEMASTER, true, &ChatHandler::HandleUnmuteCommand, "", NULL }, + { "mute", SEC_MODERATOR, true, &ChatHandler::HandleMuteCommand, "", NULL }, + { "unmute", SEC_MODERATOR, true, &ChatHandler::HandleUnmuteCommand, "", NULL }, { "movegens", SEC_ADMINISTRATOR, false, &ChatHandler::HandleMovegensCommand, "", NULL }, { "cometome", SEC_ADMINISTRATOR, false, &ChatHandler::HandleComeToMeCommand, "", NULL }, { "damage", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDamageCommand, "", NULL }, { "combatstop", SEC_GAMEMASTER, false, &ChatHandler::HandleCombatStopCommand, "", NULL }, - { "ahbotoptions", SEC_ADMINISTRATOR, true, &ChatHandler::HandleAHBotOptionsCommand, "", NULL }, - { "flusharenapoints", SEC_ADMINISTRATOR, false, &ChatHandler::HandleFlushArenaPointsCommand, "", NULL }, + { "flusharenapoints",SEC_ADMINISTRATOR, false, &ChatHandler::HandleFlushArenaPointsCommand, "", NULL }, { "chardelete", SEC_CONSOLE, true, &ChatHandler::HandleCharacterDeleteCommand, "", NULL }, - { "sendmessage", SEC_ADMINISTRATOR, true, &ChatHandler::HandleSendMessageCommand, "", NULL }, { "playall", SEC_ADMINISTRATOR, false, &ChatHandler::HandlePlayAllCommand, "", NULL }, { "repairitems", SEC_GAMEMASTER, false, &ChatHandler::HandleRepairitemsCommand, "", NULL }, { "freeze", SEC_ADMINISTRATOR, false, &ChatHandler::HandleFreezeCommand, "", NULL }, @@ -648,28 +712,9 @@ ChatCommand * ChatHandler::getCommandTable() { Field *fields = result->Fetch(); std::string name = fields[0].GetCppString(); - for(uint32 i = 0; commandTable[i].Name != NULL; i++) - { - if (name == commandTable[i].Name) - { - commandTable[i].SecurityLevel = (uint16)fields[1].GetUInt16(); - commandTable[i].Help = fields[2].GetCppString(); - } - if(commandTable[i].ChildCommands != NULL) - { - ChatCommand *ptable = commandTable[i].ChildCommands; - for(uint32 j = 0; ptable[j].Name != NULL; j++) - { - // first case for "" named subcommand - if (ptable[j].Name[0]=='\0' && name == commandTable[i].Name || - name == fmtstring("%s %s", commandTable[i].Name, ptable[j].Name) ) - { - ptable[j].SecurityLevel = (uint16)fields[1].GetUInt16(); - ptable[j].Help = fields[2].GetCppString(); - } - } - } - } + + SetDataForCommandInTable(commandTable, name.c_str(), fields[1].GetUInt16(), fields[2].GetCppString(), name); + } while(result->NextRow()); delete result; } @@ -689,6 +734,55 @@ bool ChatHandler::isAvailable(ChatCommand const& cmd) const return m_session->GetSecurity() >= cmd.SecurityLevel; } +bool ChatHandler::HasLowerSecurity(Player* target, uint64 guid, bool strong) +{ + WorldSession* target_session = NULL; + uint32 target_account = 0; + + if (target) + target_session = target->GetSession(); + else if (guid) + target_account = objmgr.GetPlayerAccountIdByGUID(guid); + + if(!target_session && !target_account) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return true; + } + + return HasLowerSecurityAccount(target_session,target_account,strong); +} + +bool ChatHandler::HasLowerSecurityAccount(WorldSession* target, uint32 target_account, bool strong) +{ + uint32 target_sec; + + // allow everything from console and RA console + if (!m_session) + return false; + + // ignore only for non-players for non strong checks (when allow apply command at least to same sec level) + if (m_session->GetSecurity() > SEC_PLAYER && !strong && !sWorld.getConfig(CONFIG_GM_LOWER_SECURITY)) + return false; + + if (target) + target_sec = target->GetSecurity(); + else if (target_account) + target_sec = accmgr.GetSecurity(target_account); + else + return true; // caller must report error for (target==NULL && target_account==0) + + if (m_session->GetSecurity() < target_sec || strong && m_session->GetSecurity() <= target_sec) + { + SendSysMessage(LANG_YOURS_SECURITY_IS_LOW); + SetSentErrorMessage(true); + return true; + } + + return false; +} + bool ChatHandler::hasStringAbbr(const char* name, const char* part) { // non "" command @@ -774,9 +868,9 @@ void ChatHandler::PSendSysMessage(int32 entry, ...) { const char *format = GetTrinityString(entry); va_list ap; - char str [1024]; + char str [2048]; va_start(ap, entry); - vsnprintf(str,1024,format, ap ); + vsnprintf(str,2048,format, ap ); va_end(ap); SendSysMessage(str); } @@ -784,9 +878,9 @@ void ChatHandler::PSendSysMessage(int32 entry, ...) void ChatHandler::PSendSysMessage(const char *format, ...) { va_list ap; - char str [1024]; + char str [2048]; va_start(ap, format); - vsnprintf(str,1024,format, ap ); + vsnprintf(str,2048,format, ap ); va_end(ap); SendSysMessage(str); } @@ -861,6 +955,61 @@ bool ChatHandler::ExecuteCommandInTable(ChatCommand *table, const char* text, co return false; } +bool ChatHandler::SetDataForCommandInTable(ChatCommand *table, const char* text, uint32 security, std::string const& help, std::string const& fullcommand ) +{ + std::string cmd = ""; + + while (*text != ' ' && *text != '\0') + { + cmd += *text; + ++text; + } + + while (*text == ' ') ++text; + + for(uint32 i = 0; table[i].Name != NULL; i++) + { + // for data fill use full explicit command names + if( table[i].Name != cmd ) + continue; + + // select subcommand from child commands list (including "") + if(table[i].ChildCommands != NULL) + { + if(SetDataForCommandInTable(table[i].ChildCommands, text, security, help, fullcommand)) + return true; + else if(*text) + return false; + + // fail with "" subcommands, then use normal level up command instead + } + // expected subcommand by full name DB content + else if(*text) + { + sLog.outErrorDb("Table `command` have unexpected subcommand '%s' in command '%s', skip.",text,fullcommand.c_str()); + return false; + } + + if(table[i].SecurityLevel != security) + sLog.outDetail("Table `command` overwrite for command '%s' default security (%u) by %u",fullcommand.c_str(),table[i].SecurityLevel,security); + + table[i].SecurityLevel = security; + table[i].Help = help; + return true; + } + + // in case "" command let process by caller + if(!cmd.empty()) + { + if(table==getCommandTable()) + sLog.outErrorDb("Table `command` have not existed command '%s', skip.",cmd.c_str()); + else + sLog.outErrorDb("Table `command` have not existed subcommand '%s' in command '%s', skip.",cmd.c_str(),fullcommand.c_str()); + } + + return false; +} + int ChatHandler::ParseCommands(const char* text) { ASSERT(text); @@ -892,11 +1041,11 @@ int ChatHandler::ParseCommands(const char* text) ++text; if(!ExecuteCommandInTable(getCommandTable(), text, fullcmd)) - { - if(m_session && m_session->GetSecurity() == SEC_PLAYER) - return 0; - SendSysMessage(LANG_NO_CMD); - } + { + if(m_session && m_session->GetSecurity() == SEC_PLAYER) + return 0; + SendSysMessage(LANG_NO_CMD); + } return 1; } @@ -1105,7 +1254,7 @@ Creature* ChatHandler::getSelectedCreature() if(!m_session) return NULL; - return ObjectAccessor::GetCreatureOrPet(*m_session->GetPlayer(),m_session->GetPlayer()->GetSelection()); + return ObjectAccessor::GetCreatureOrPetOrVehicle(*m_session->GetPlayer(),m_session->GetPlayer()->GetSelection()); } char* ChatHandler::extractKeyFromLink(char* text, char const* linkType, char** something1) @@ -1176,12 +1325,23 @@ char* ChatHandler::extractKeyFromLink(char* text, char const* const* linkTypes, // [name] Shift-click form |color|linkType:key|h[name]|h|r // or // [name] Shift-click form |color|linkType:key:something1:...:somethingN|h[name]|h|r + // or + // [name] Shift-click form |linkType:key|h[name]|h|r - char* check = strtok(text, "|"); // skip color - if(!check) - return NULL; // end of data + char* tail; - char* cLinkType = strtok(NULL, ":"); // linktype + if(text[1]=='c') + { + char* check = strtok(text, "|"); // skip color + if(!check) + return NULL; // end of data + + tail = strtok(NULL, ""); // tail + } + else + tail = text+1; // skip first | + + char* cLinkType = strtok(tail, ":"); // linktype if(!cLinkType) return NULL; // end of data @@ -1257,20 +1417,29 @@ GameObject* ChatHandler::GetObjectGlobalyWithGuidOrNearWithDbGuid(uint32 lowguid Cell cell(p); cell.data.Part.reserved = ALL_DISTRICT; - Trinity::GameObjectWithDbGUIDCheck go_check(*pl,lowguid); - Trinity::GameObjectSearcher<Trinity::GameObjectWithDbGUIDCheck> checker(obj,go_check); + MaNGOS::GameObjectWithDbGUIDCheck go_check(*pl,lowguid); + MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(pl,obj,go_check); TypeContainerVisitor<Trinity::GameObjectSearcher<Trinity::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker); CellLock<GridReadGuard> cell_lock(cell, p); - cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(pl->GetMapId(), pl)); + cell_lock->Visit(cell_lock, object_checker, *pl->GetMap()); } 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 }; @@ -1278,31 +1447,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 >= MAX_TALENT_RANK) + 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) @@ -1320,9 +1499,84 @@ GameTele const* ChatHandler::extractGameTeleFromLink(char* text) return objmgr.GetGameTele(cId); } -const char *ChatHandler::GetName() const +enum GuidLinkType { - return m_session->GetPlayer()->GetName(); + SPELL_LINK_PLAYER = 0, // must be first for selection in not link case + SPELL_LINK_CREATURE = 1, + SPELL_LINK_GAMEOBJECT = 2 +}; + +static char const* const guidKeys[] = +{ + "Hplayer", + "Hcreature", + "Hgameobject", + 0 +}; + +uint64 ChatHandler::extractGuidFromLink(char* text) +{ + int type = 0; + + // |color|Hcreature:creature_guid|h[name]|h|r + // |color|Hgameobject:go_guid|h[name]|h|r + // |color|Hplayer:name|h[name]|h|r + char* idS = extractKeyFromLink(text,guidKeys,&type); + if(!idS) + return 0; + + switch(type) + { + case SPELL_LINK_PLAYER: + { + std::string name = idS; + if(!normalizePlayerName(name)) + return 0; + + if(Player* player = objmgr.GetPlayer(name.c_str())) + return player->GetGUID(); + + if(uint64 guid = objmgr.GetPlayerGUIDByName(name)) + return guid; + + return 0; + } + case SPELL_LINK_CREATURE: + { + uint32 lowguid = (uint32)atol(idS); + + if(CreatureData const* data = objmgr.GetCreatureData(lowguid) ) + return MAKE_NEW_GUID(lowguid,data->id,HIGHGUID_UNIT); + else + return 0; + } + case SPELL_LINK_GAMEOBJECT: + { + uint32 lowguid = (uint32)atol(idS); + + if(GameObjectData const* data = objmgr.GetGOData(lowguid) ) + return MAKE_NEW_GUID(lowguid,data->id,HIGHGUID_GAMEOBJECT); + else + return 0; + } + } + + // unknown type? + return 0; +} + +std::string ChatHandler::extractPlayerNameFromLink(char* text) +{ + // |color|Hplayer:name|h[name]|h|r + char* name_str = extractKeyFromLink(text,"Hplayer"); + if(!name_str) + return ""; + + std::string name = name_str; + if(!normalizePlayerName(name)) + return ""; + + return name; } bool ChatHandler::needReportToTarget(Player* chr) const @@ -1348,7 +1602,7 @@ void CliHandler::SendSysMessage(const char *str) m_print("\r\n"); } -const char *CliHandler::GetName() const +std::string CliHandler::GetNameLink() const { return GetTrinityString(LANG_CONSOLE_COMMAND); } diff --git a/src/game/Chat.h b/src/game/Chat.h index 8a1d11b4886..d516d44c6a1 100644 --- a/src/game/Chat.h +++ b/src/game/Chat.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -72,8 +72,6 @@ class ChatHandler int ParseCommands(const char* text); - virtual char const* GetName() const; - protected: explicit ChatHandler() : m_session(NULL) {} // for CLI subclass @@ -81,10 +79,13 @@ class ChatHandler virtual bool isAvailable(ChatCommand const& cmd) const; virtual bool needReportToTarget(Player* chr) const; + bool HasLowerSecurity(Player* target, uint64 guid, bool strong = false); + bool HasLowerSecurityAccount(WorldSession* target, uint32 account, bool strong = false); void SendGlobalSysMessage(const char *str); void SendGlobalGMSysMessage(const char *str); + bool SetDataForCommandInTable(ChatCommand *table, const char* text, uint32 security, std::string const& help, std::string const& fullcommand ); bool ExecuteCommandInTable(ChatCommand *table, const char* text, const std::string& fullcommand); bool ShowHelpForCommand(ChatCommand *table, const char* cmd); bool ShowHelpForSubCommands(ChatCommand *table, char const* cmd, char const* subcmd); @@ -99,43 +100,102 @@ class ChatHandler bool HandleAccountSetGmLevelCommand(const char* args); bool HandleAccountSetPasswordCommand(const char* args); - bool HandleHelpCommand(const char* args); bool HandleAHBotOptionsCommand(const char * args); - bool HandleCommandsCommand(const char* args); - bool HandleStartCommand(const char* args); - bool HandleDismountCommand(const char* args); - bool HandleSaveCommand(const char* args); - bool HandleGMListIngameCommand(const char* args); - bool HandleGMListFullCommand(const char* args); - - bool HandleNamegoCommand(const char* args); - bool HandleGonameCommand(const char* args); - bool HandleGroupgoCommand(const char* args); - bool HandleRecallCommand(const char* args); bool HandleNameAnnounceCommand(const char* args); bool HandleGMNameAnnounceCommand(const char* args); - bool HandleAnnounceCommand(const char* args); bool HandleGMAnnounceCommand(const char* args); - bool HandleNotifyCommand(const char* args); bool HandleGMNotifyCommand(const char* args); - bool HandleGMmodeCommand(const char* args); - bool HandleGMChatCommand(const char* args); - bool HandleVisibleCommand(const char* args); - bool HandleGPSCommand(const char* args); - bool HandleTaxiCheatCommand(const char* args); - bool HandleWhispersCommand(const char* args); - bool HandleNameTeleCommand(const char* args); - bool HandleGroupTeleCommand(const char* args); - bool HandleDrunkCommand(const char* args); - bool HandleSendItemsCommand(const char* args); - bool HandleSendMailCommand(const char* args); - bool HandleSendMoneyCommand(const char* args); + + bool HandleBanAccountCommand(const char* args); + bool HandleBanCharacterCommand(const char* args); + bool HandleBanIPCommand(const char* args); + bool HandleBanInfoAccountCommand(const char* args); + bool HandleBanInfoCharacterCommand(const char* args); + bool HandleBanInfoIPCommand(const char* args); + bool HandleBanListAccountCommand(const char* args); + bool HandleBanListCharacterCommand(const char* args); + bool HandleBanListIPCommand(const char* args); + + bool HandleCastCommand(const char *args); + bool HandleCastBackCommand(const char *args); + bool HandleCastDistCommand(const char *args); + bool HandleCastSelfCommand(const char *args); + bool HandleCastTargetCommand(const char *args); + + bool HandleDebugAnimCommand(const char* args); + bool HandleDebugArenaCommand(const char * args); + bool HandleDebugBattlegroundCommand(const char * args); + bool HandleDebugGetItemState(const char * args); + bool HandleDebugGetLootRecipient(const char * args); + bool HandleDebugGetValue(const char* args); + bool HandleDebugMod32Value(const char* args); + bool HandleDebugSetValue(const char* args); + bool HandleDebugSetItemFlagCommand(const char * args); + bool HandleDebugSpawnVehicle(const char * args); + bool HandleDebugUpdate(const char* args); + bool HandleDebugUpdateWorldStateCommand(const char* args); + + bool HandleDebugSet32Bit(const char* args); + bool HandleDebugThreatList(const char * args); + bool HandleDebugHostilRefList(const char * args); + bool HandlePossessCommand(const char* args); + bool HandleUnPossessCommand(const char* args); + bool HandleBindSightCommand(const char* args); + bool HandleUnbindSightCommand(const char* args); + + bool HandleDebugPlayCinematicCommand(const char* args); + bool HandleDebugPlayMovieCommand(const char* args); + bool HandleDebugPlaySoundCommand(const char* args); + + bool HandleDebugSendBuyErrorCommand(const char* args); + bool HandleDebugSendChannelNotifyCommand(const char* args); + bool HandleDebugSendChatMsgCommand(const char* args); + bool HandleDebugSendEquipErrorCommand(const char* args); + bool HandleDebugSendLargePacketCommand(const char * args); + bool HandleDebugSendOpcodeCommand(const char* args); + bool HandleDebugSendPoiCommand(const char* args); + bool HandleDebugSendQuestPartyMsgCommand(const char* args); + bool HandleDebugSendQuestInvalidMsgCommand(const char* args); + bool HandleDebugSendSellErrorCommand(const char* args); + bool HandleDebugSendSetPhaseShiftCommand(const char * args); + bool HandleDebugSendSpellFailCommand(const char* args); bool HandleEventActiveListCommand(const char* args); bool HandleEventStartCommand(const char* args); bool HandleEventStopCommand(const char* args); bool HandleEventInfoCommand(const char* args); + bool HandleGameObjectAddCommand(const char* args); + bool HandleGameObjectDeleteCommand(const char* args); + bool HandleGameObjectMoveCommand(const char* args); + bool HandleGameObjectNearCommand(const char* args); + bool HandleGameObjectPhaseCommand(const char* args); + bool HandleGameObjectStateCommand(const char* args); + bool HandleGameObjectTargetCommand(const char* args); + bool HandleGameObjectTurnCommand(const char* args); + + bool HandleGMCommand(const char* args); + bool HandleGMChatCommand(const char* args); + bool HandleGMFlyCommand(const char* args); + bool HandleGMListFullCommand(const char* args); + bool HandleGMListIngameCommand(const char* args); + bool HandleGMVisibleCommand(const char* args); + + bool HandleGuildCreateCommand(const char* args); + bool HandleGuildInviteCommand(const char* args); + bool HandleGuildUninviteCommand(const char* args); + bool HandleGuildRankCommand(const char* args); + bool HandleGuildDeleteCommand(const char* args); + + bool HandleHonorAddCommand(const char* args); + bool HandleHonorAddKillCommand(const char* args); + bool HandleHonorUpdateCommand(const char* args); + + bool HandleInstanceListBindsCommand(const char* args); + bool HandleInstanceUnbindCommand(const char* args); + bool HandleInstanceStatsCommand(const char* args); + bool HandleInstanceSaveDataCommand(const char * args); + bool HandleLearnCommand(const char* args); bool HandleLearnAllCommand(const char* args); bool HandleLearnAllGMCommand(const char* args); @@ -144,9 +204,15 @@ class ChatHandler bool HandleLearnAllDefaultCommand(const char* args); bool HandleLearnAllLangCommand(const char* args); bool HandleLearnAllMyClassCommand(const char* args); + bool HandleLearnAllMyPetTalentsCommand(const char* args); bool HandleLearnAllMySpellsCommand(const char* args); bool HandleLearnAllMyTalentsCommand(const char* args); + bool HandleListAurasCommand(const char * args); + bool HandleListCreatureCommand(const char* args); + bool HandleListItemCommand(const char* args); + bool HandleListObjectCommand(const char* args); + bool HandleLookupAreaCommand(const char* args); bool HandleLookupCreatureCommand(const char* args); bool HandleLookupEventCommand(const char* args); @@ -166,6 +232,7 @@ class ChatHandler bool HandleModifyHPCommand(const char* args); bool HandleModifyManaCommand(const char* args); bool HandleModifyRageCommand(const char* args); + bool HandleModifyRunicPowerCommand(const char* args); bool HandleModifyEnergyCommand(const char* args); bool HandleModifyMoneyCommand(const char* args); bool HandleModifyASpeedCommand(const char* args); @@ -182,12 +249,17 @@ class ChatHandler bool HandleModifyHonorCommand (const char* args); bool HandleModifyRepCommand(const char* args); bool HandleModifyArenaCommand(const char* args); + bool HandleModifyPhaseCommand(const char* args); bool HandleModifyGenderCommand(const char* args); + //-----------------------Npc Commands----------------------- bool HandleNpcAddCommand(const char* args); bool HandleNpcAddMoveCommand(const char* args); + bool HandleNpcAddVendorItemCommand(const char* args); bool HandleNpcChangeEntryCommand(const char *args); + bool HandleNpcChangeLevelCommand(const char* args); bool HandleNpcDeleteCommand(const char* args); + bool HandleNpcDelVendorItemCommand(const char* args); bool HandleNpcFactionIdCommand(const char* args); bool HandleNpcFlagCommand(const char* args); bool HandleNpcFollowCommand(const char* args); @@ -195,8 +267,10 @@ class ChatHandler bool HandleNpcMoveCommand(const char* args); bool HandleNpcPlayEmoteCommand(const char* args); bool HandleNpcSayCommand(const char* args); + bool HandleNpcSetDeathStateCommand(const char* args); bool HandleNpcSetModelCommand(const char* args); bool HandleNpcSetMoveTypeCommand(const char* args); + bool HandleNpcSetPhaseCommand(const char* args); bool HandleNpcSpawnDistCommand(const char* args); bool HandleNpcSpawnTimeCommand(const char* args); bool HandleNpcTameCommand(const char* args); @@ -206,8 +280,18 @@ class ChatHandler bool HandleNpcYellCommand(const char* args); bool HandleNpcAddFormationCommand(const char* args); - bool HandleReloadCommand(const char* args); + //TODO: NpcCommands that needs to be fixed : + bool HandleNpcAddWeaponCommand(const char* args); + bool HandleNpcNameCommand(const char* args); + bool HandleNpcSubNameCommand(const char* args); + //---------------------------------------------------------- + + bool HandleQuestAdd(const char * args); + bool HandleQuestRemove(const char * args); + bool HandleQuestComplete(const char * args); + bool HandleReloadAllCommand(const char* args); + bool HandleReloadAllAchievementCommand(const char* args); bool HandleReloadAllAreaCommand(const char* args); bool HandleReloadAllItemCommand(const char* args); bool HandleReloadAllLootCommand(const char* args); @@ -219,7 +303,8 @@ class ChatHandler bool HandleReloadConfigCommand(const char* args); - bool HandleReloadWpScriptsCommand(const char* args); + bool HandleReloadAchievementCriteriaDataCommand(const char* args); + bool HandleReloadAchievementRewardCommand(const char* args); bool HandleReloadAreaTriggerTavernCommand(const char* args); bool HandleReloadAreaTriggerTeleportCommand(const char* args); bool HandleReloadAccessRequirementCommand(const char* args); @@ -233,21 +318,35 @@ class ChatHandler bool HandleReloadGameTeleCommand(const char* args); bool HandleReloadGOQuestRelationsCommand(const char* args); bool HandleReloadGOQuestInvRelationsCommand(const char* args); + bool HandleReloadItemEnchantementsCommand(const char* args); + bool HandleReloadLocalesAchievementRewardCommand(const char* args); + bool HandleReloadLocalesCreatureCommand(const char* args); + bool HandleReloadLocalesGameobjectCommand(const char* args); + bool HandleReloadLocalesItemCommand(const char* args); + bool HandleReloadLocalesNpcTextCommand(const char* args); + bool HandleReloadLocalesPageTextCommand(const char* args); + bool HandleReloadLocalesPointsOfInterestCommand(const char* args); + bool HandleReloadLocalesQuestCommand(const char* args); +// bool HandleReloadAuctionsCommand(const char* args); bool HandleReloadLootTemplatesCreatureCommand(const char* args); bool HandleReloadLootTemplatesDisenchantCommand(const char* args); bool HandleReloadLootTemplatesFishingCommand(const char* args); bool HandleReloadLootTemplatesGameobjectCommand(const char* args); bool HandleReloadLootTemplatesItemCommand(const char* args); + bool HandleReloadLootTemplatesMillingCommand(const char* args); bool HandleReloadLootTemplatesPickpocketingCommand(const char* args); bool HandleReloadLootTemplatesProspectingCommand(const char* args); bool HandleReloadLootTemplatesReferenceCommand(const char* args); bool HandleReloadLootTemplatesQuestMailCommand(const char* args); bool HandleReloadLootTemplatesSkinningCommand(const char* args); + bool HandleReloadLootTemplatesSpellCommand(const char* args); bool HandleReloadTrinityStringCommand(const char* args); bool HandleReloadNpcGossipCommand(const char* args); bool HandleReloadNpcOptionCommand(const char* args); bool HandleReloadNpcTrainerCommand(const char* args); bool HandleReloadNpcVendorCommand(const char* args); + bool HandleReloadPageTextsCommand(const char* args); + bool HandleReloadPointsOfInterestCommand(const char* args); bool HandleReloadQuestAreaTriggersCommand(const char* args); bool HandleReloadQuestEndScriptsCommand(const char* args); bool HandleReloadQuestStartScriptsCommand(const char* args); @@ -258,30 +357,33 @@ class ChatHandler bool HandleReloadSkillFishingBaseLevelCommand(const char* args); bool HandleReloadSpellAffectCommand(const char* args); bool HandleReloadSpellRequiredCommand(const char* args); + bool HandleReloadSpellAreaCommand(const char* args); bool HandleReloadSpellElixirCommand(const char* args); bool HandleReloadSpellLearnSpellCommand(const char* args); bool HandleReloadSpellLinkedSpellCommand(const char* args); bool HandleReloadSpellProcEventCommand(const char* args); + bool HandleReloadSpellBonusesCommand(const char* args); bool HandleReloadSpellScriptTargetCommand(const char* args); bool HandleReloadSpellScriptsCommand(const char* args); bool HandleReloadSpellTargetPositionCommand(const char* args); bool HandleReloadSpellThreatsCommand(const char* args); bool HandleReloadSpellPetAurasCommand(const char* args); bool HandleReloadSpellDisabledCommand(const char* args); - bool HandleReloadPageTextsCommand(const char* args); - bool HandleReloadItemEnchantementsCommand(const char* args); - bool HandleReloadLocalesCreatureCommand(const char* args); - bool HandleReloadLocalesGameobjectCommand(const char* args); - bool HandleReloadLocalesItemCommand(const char* args); - bool HandleReloadLocalesNpcTextCommand(const char* args); - bool HandleReloadLocalesPageTextCommand(const char* args); - bool HandleReloadLocalesQuestCommand(const char* args); bool HandleReloadAuctionsCommand(const char* args); + bool HandleReloadWpScriptsCommand(const char* args); - bool HandleInstanceListBindsCommand(const char* args); - bool HandleInstanceUnbindCommand(const char* args); - bool HandleInstanceStatsCommand(const char* args); - bool HandleInstanceSaveDataCommand(const char * args); + bool HandleResetAchievementsCommand(const char * args); + bool HandleResetAllCommand(const char * args); + bool HandleResetHonorCommand(const char * args); + bool HandleResetLevelCommand(const char * args); + bool HandleResetSpellsCommand(const char * args); + bool HandleResetStatsCommand(const char * args); + bool HandleResetTalentsCommand(const char * args); + + bool HandleSendItemsCommand(const char* args); + bool HandleSendMailCommand(const char* args); + bool HandleSendMessageCommand(const char * args); + bool HandleSendMoneyCommand(const char* args); bool HandleServerCorpsesCommand(const char* args); bool HandleServerExitCommand(const char* args); @@ -292,40 +394,58 @@ class ChatHandler bool HandleServerRestartCommand(const char* args); bool HandleServerSetMotdCommand(const char* args); bool HandleServerSetLogLevelCommand(const char* args); + bool HandleServerSetLogFileLevelCommand(const char* args); bool HandleServerSetDiffTimeCommand(const char* args); bool HandleServerShutDownCommand(const char* args); bool HandleServerShutDownCancelCommand(const char* args); + bool HandleServerSetClosedCommand(const char* args); - bool HandleAddHonorCommand(const char* args); - bool HandleHonorAddKillCommand(const char* args); - bool HandleUpdateHonorFieldsCommand(const char* args); + bool HandleTeleCommand(const char * args); + bool HandleTeleAddCommand(const char * args); + bool HandleTeleDelCommand(const char * args); + bool HandleTeleGroupCommand(const char* args); + bool HandleTeleNameCommand(const char* args); - bool HandleLoadScriptsCommand(const char* args); - bool HandleSendQuestPartyMsgCommand(const char* args); - bool HandleSendQuestInvalidMsgCommand(const char* args); + bool HandleUnBanAccountCommand(const char* args); + bool HandleUnBanCharacterCommand(const char* args); + bool HandleUnBanIPCommand(const char* args); - bool HandleDebugInArcCommand(const char* args); - bool HandleDebugSpellFailCommand(const char* args); + bool HandleWpAddCommand(const char* args); + bool HandleWpLoadPathCommand(const char* args); + bool HandleWpUnLoadPathCommand(const char* args); + bool HandleWpModifyCommand(const char* args); + bool HandleWpEventCommand(const char* args); + bool HandleWpShowCommand(const char* args); + bool HandleReloadAllPaths(const char *args); + + bool HandleHelpCommand(const char* args); + bool HandleCommandsCommand(const char* args); + bool HandleStartCommand(const char* args); + bool HandleDismountCommand(const char* args); + bool HandleSaveCommand(const char* args); + + bool HandleNamegoCommand(const char* args); + bool HandleGonameCommand(const char* args); + bool HandleGroupgoCommand(const char* args); + bool HandleRecallCommand(const char* args); + bool HandleAnnounceCommand(const char* args); + bool HandleNotifyCommand(const char* args); + bool HandleGPSCommand(const char* args); + bool HandleTaxiCheatCommand(const char* args); + bool HandleWhispersCommand(const char* args); + bool HandleModifyDrunkCommand(const char* args); + + bool HandleLoadScriptsCommand(const char* args); bool HandleGUIDCommand(const char* args); - bool HandleNameCommand(const char* args); - bool HandleSubNameCommand(const char* args); bool HandleItemMoveCommand(const char* args); bool HandleDeMorphCommand(const char* args); - bool HandleAddVendorItemCommand(const char* args); - bool HandleDelVendorItemCommand(const char* args); - bool HandleChangeLevelCommand(const char* args); - bool HandleSetPoiCommand(const char* args); - bool HandleEquipErrorCommand(const char* args); bool HandleGoCreatureCommand(const char* args); bool HandleGoObjectCommand(const char* args); + bool HandleGoTicketCommand(const char* args); bool HandleGoTriggerCommand(const char* args); bool HandleGoGraveyardCommand(const char* args); - bool HandleTargetObjectCommand(const char* args); - bool HandleDelObjectCommand(const char* args); - bool HandleMoveObjectCommand(const char* args); - bool HandleTurnObjectCommand(const char* args); - bool HandleObjectStateCommand(const char* args); + bool HandlePInfoCommand(const char* args); bool HandlePLimitCommand(const char* args); bool HandleMuteCommand(const char* args); @@ -336,37 +456,21 @@ class ChatHandler bool HandleListFreezeCommand(const char* args); bool HandleCharacterDeleteCommand(const char* args); - bool HandleBanAccountCommand(const char* args); - bool HandleBanCharacterCommand(const char* args); - bool HandleBanIPCommand(const char* args); - bool HandleUnBanAccountCommand(const char* args); - bool HandleUnBanCharacterCommand(const char* args); - bool HandleUnBanIPCommand(const char* args); - bool HandleBanInfoAccountCommand(const char* args); - bool HandleBanInfoCharacterCommand(const char* args); - bool HandleBanInfoIPCommand(const char* args); - bool HandleBanListAccountCommand(const char* args); - bool HandleBanListCharacterCommand(const char* args); - bool HandleBanListIPCommand(const char* args); bool HandleGoXYCommand(const char* args); bool HandleGoXYZCommand(const char* args); bool HandleGoZoneXYCommand(const char* args); bool HandleGoGridCommand(const char* args); - bool HandleAddWeaponCommand(const char* args); - bool HandleAllowMovementCommand(const char* args); + bool HandleNpcAllowMovementCommand(const char* args); bool HandleGoCommand(const char* args); bool HandleCooldownCommand(const char* args); bool HandleUnLearnCommand(const char* args); bool HandleGetDistanceCommand(const char* args); - bool HandleGameObjectCommand(const char* args); - bool HandleAnimCommand(const char* args); - bool HandlePlaySoundCommand(const char* args); - bool HandleStandStateCommand(const char* args); + bool HandleModifyStandStateCommand(const char* args); bool HandleDieCommand(const char* args); bool HandleDamageCommand(const char *args); bool HandleReviveCommand(const char* args); - bool HandleMorphCommand(const char* args); + bool HandleModifyMorphCommand(const char* args); bool HandleAuraCommand(const char* args); bool HandleUnAuraCommand(const char* args); bool HandleLinkGraveCommand(const char* args); @@ -390,29 +494,9 @@ class ChatHandler bool HandleGroupDisbandCommand(const char* args); bool HandleGroupRemoveCommand(const char* args); - bool HandleGuildCreateCommand(const char* args); - bool HandleGuildInviteCommand(const char* args); - bool HandleGuildUninviteCommand(const char* args); - bool HandleGuildRankCommand(const char* args); - bool HandleGuildDeleteCommand(const char* args); - bool HandleUpdate(const char* args); bool HandleBankCommand(const char* args); bool HandleChangeWeather(const char* args); bool HandleKickPlayerCommand(const char * args); - bool HandleTeleCommand(const char * args); - bool HandleAddTeleCommand(const char * args); - bool HandleDelTeleCommand(const char * args); - bool HandleListAurasCommand(const char * args); - - bool HandleResetHonorCommand(const char * args); - bool HandleResetLevelCommand(const char * args); - bool HandleResetSpellsCommand(const char * args); - - bool HandleResetStatsCommand(const char * args); - bool HandleResetTalentsCommand(const char * args); - - bool HandleResetAllCommand(const char * args); - // GM ticket command handlers bool HandleGMTicketListCommand(const char* args); @@ -429,43 +513,20 @@ class ChatHandler bool HandleMaxSkillCommand(const char* args); bool HandleSetSkillCommand(const char* args); - bool HandleListCreatureCommand(const char* args); - bool HandleListItemCommand(const char* args); - bool HandleListObjectCommand(const char* args); - bool HandleNearObjectCommand(const char* args); bool HandlePasswordCommand(const char* args); bool HandleLockAccountCommand(const char* args); bool HandleRespawnCommand(const char* args); - //New Wp Commands - bool HandleWpAddCommand(const char* args); - bool HandleWpLoadPathCommand(const char* args); - bool HandleWpUnLoadPathCommand(const char* args); - bool HandleWpModifyCommand(const char* args); - bool HandleWpEventCommand(const char* args); - bool HandleWpShowCommand(const char* args); - bool HandleReloadAllPaths(const char *args); - - bool HandleFlyModeCommand(const char* args); - bool HandleSendOpcodeCommand(const char* args); - bool HandleSellErrorCommand(const char* args); - bool HandleBuyErrorCommand(const char* args); - bool HandleUpdateWorldStateCommand(const char* args); - bool HandlePlaySound2Command(const char* args); - bool HandleSendChannelNotifyCommand(const char* args); - bool HandleSendChatMsgCommand(const char* args); bool HandleRenameCommand(const char * args); - bool HandleLoadPDumpCommand(const char *args); - bool HandleWritePDumpCommand(const char *args); - bool HandleCastCommand(const char *args); - bool HandleCastBackCommand(const char *args); - bool HandleCastDistCommand(const char *args); - bool HandleCastSelfCommand(const char *args); - bool HandleCastTargetCommand(const char *args); + bool HandleCustomizeCommand(const char * args); + bool HandlePDumpLoadCommand(const char *args); + bool HandlePDumpWriteCommand(const char *args); bool HandleComeToMeCommand(const char *args); bool HandleCombatStopCommand(const char *args); - bool HandleCharDeleteCommand(const char *args); - bool HandleSendMessageCommand(const char * args); + + /*bool HandleCharDeleteCommand(const char *args); + bool HandleSendMessageCommand(const char * args);*/ + bool HandleFlushArenaPointsCommand(const char *args); bool HandlePlayAllCommand(const char* args); bool HandleRepairitemsCommand(const char* args); @@ -474,32 +535,30 @@ class ChatHandler bool HandleTempAddSpwCommand(const char* args); //! Development Commands - bool HandleSetValue(const char* args); - bool HandleGetValue(const char* args); + + /*bool HandleQuestAdd(const char * args); + bool HandleQuestRemove(const char * args); + bool HandleQuestComplete(const char * args);*/ + bool HandleSet32Bit(const char* args); - bool HandleMod32Value(const char* args); - bool HandleAddQuest(const char * args); - bool HandleRemoveQuest(const char * args); - bool HandleCompleteQuest(const char * args); bool HandleSaveAllCommand(const char* args); - bool HandleGetItemState(const char * args); - bool HandleGetLootRecipient(const char * args); - bool HandleDebugArenaCommand(const char * args); - bool HandleDebugThreatList(const char * args); - bool HandleDebugHostilRefList(const char * args); - bool HandlePossessCommand(const char* args); - bool HandleUnPossessCommand(const char* args); - bool HandleBindSightCommand(const char* args); - bool HandleUnbindSightCommand(const char* args); 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); + uint64 extractGuidFromLink(char* text); GameTele const* extractGameTeleFromLink(char* text); bool GetPlayerGroupAndGUIDByName(const char* cname, Player* &plr, Group* &group, uint64 &guid, bool offline = false); + std::string extractPlayerNameFromLink(char* text); + + std::string playerLink(std::string const& name) const { return m_session ? "|cffffffff|Hplayer:"+name+"|h["+name+"]|h|r" : name; } + virtual std::string GetNameLink() const { return GetNameLink(m_session->GetPlayer()); } + std::string GetNameLink(Player* chr) const { return playerLink(chr->GetName()); } GameObject* GetObjectGlobalyWithGuidOrNearWithDbGuid(uint32 lowguid,uint32 entry); @@ -529,7 +588,7 @@ class CliHandler : public ChatHandler const char *GetTrinityString(int32 entry) const; bool isAvailable(ChatCommand const& cmd) const; void SendSysMessage(const char *str); - char const* GetName() const; + std::string GetNameLink() const; bool needReportToTarget(Player* chr) const; private: diff --git a/src/game/ChatHandler.cpp b/src/game/ChatHandler.cpp index 22b58cccdf3..ae282b05c1e 100644 --- a/src/game/ChatHandler.cpp +++ b/src/game/ChatHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,13 +30,14 @@ #include "ChannelMgr.h" #include "Group.h" #include "Guild.h" -#include "MapManager.h" #include "ObjectAccessor.h" #include "ScriptCalls.h" #include "Player.h" #include "SpellAuras.h" #include "Language.h" #include "Util.h" +#include "GridNotifiersImpl.h" +#include "CellImpl.h" void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) { @@ -66,11 +67,11 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if(langDesc->skill_id != 0 && !_player->HasSkill(langDesc->skill_id)) { // also check SPELL_AURA_COMPREHEND_LANGUAGE (client offers option to speak in that language) - Unit::AuraList const& langAuras = _player->GetAurasByType(SPELL_AURA_COMPREHEND_LANGUAGE); + Unit::AuraEffectList const& langAuras = _player->GetAurasByType(SPELL_AURA_COMPREHEND_LANGUAGE); bool foundAura = false; - for(Unit::AuraList::const_iterator i = langAuras.begin();i != langAuras.end(); ++i) + for(Unit::AuraEffectList::const_iterator i = langAuras.begin();i != langAuras.end(); ++i) { - if((*i)->GetModifier()->m_miscvalue == lang) + if((*i)->GetMiscValue() == lang) { foundAura = true; break; @@ -85,6 +86,21 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if(lang == LANG_ADDON) { + if(sWorld.getConfig(CONFIG_CHATLOG_ADDON)) + { + std::string msg = ""; + recv_data >> msg; + + if(msg.empty()) + { + sLog.outDebug("Player %s send empty addon msg", GetPlayer()->GetName()); + return; + } + + sLog.outChat("[ADDON] Player %s sends: %s", + GetPlayer()->GetName(), msg.c_str()); + } + // Disabled addon channel? if(!sWorld.getConfig(CONFIG_ADDON_CHANNEL)) return; @@ -122,9 +138,9 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) } // but overwrite it by SPELL_AURA_MOD_LANGUAGE auras (only single case used) - Unit::AuraList const& ModLangAuras = _player->GetAurasByType(SPELL_AURA_MOD_LANGUAGE); + Unit::AuraEffectList const& ModLangAuras = _player->GetAurasByType(SPELL_AURA_MOD_LANGUAGE); if(!ModLangAuras.empty()) - lang = ModLangAuras.front()->GetModifier()->m_miscvalue; + lang = ModLangAuras.front()->GetMiscValue(); } if (!_player->CanSpeak()) @@ -192,7 +208,7 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) Player *player = objmgr.GetPlayer(to.c_str()); uint32 tSecurity = GetSecurity(); - uint32 pSecurity = player ? player->GetSession()->GetSecurity() : 0; + uint32 pSecurity = player ? player->GetSession()->GetSecurity() : SEC_PLAYER; if(!player || tSecurity == SEC_PLAYER && pSecurity > SEC_PLAYER && !player->isAcceptWhispers()) { WorldPacket data(SMSG_CHAT_PLAYER_NOT_FOUND, (to.size()+1)); @@ -235,13 +251,19 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if(msg.empty()) break; - Group *group = GetPlayer()->GetGroup(); - if(!group) + // if player is in battleground, he cannot say to battleground members by /p + Group *group = GetPlayer()->GetOriginalGroup(); + // so if player hasn't OriginalGroup and his player->GetGroup() is BG raid, then return + if( !group && (!(group = GetPlayer()->GetGroup()) || group->isBGGroup()) ) return; WorldPacket data; ChatHandler::FillMessageData(&data, this, CHAT_MSG_PARTY, lang, NULL, 0, msg.c_str(),NULL); - group->BroadcastPacket(&data, group->GetMemberGroup(GetPlayer()->GetGUID())); + group->BroadcastPacket(&data, false, group->GetMemberGroup(GetPlayer()->GetGUID())); + + if(sWorld.getConfig(CONFIG_CHATLOG_PARTY)) + sLog.outChat("[PARTY] Player %s tells group with leader %s: %s", + GetPlayer()->GetName(), group->GetLeaderName(), msg.c_str()); } break; case CHAT_MSG_GUILD: @@ -267,6 +289,17 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) Guild *guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()); if (guild) guild->BroadcastToGuild(this, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL); + + if(lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHATLOG_GUILD)) + { + sLog.outChat("[GUILD] Player %s tells guild %s: %s", + GetPlayer()->GetName(), guild->GetName().c_str(), msg.c_str()); + } + else if (lang == LANG_ADDON && sWorld.getConfig(CONFIG_CHATLOG_ADDON)) + { + sLog.outChat("[ADDON] Player %s sends to guild %s: %s", + GetPlayer()->GetName(), guild->GetName().c_str(), msg.c_str()); + } } break; @@ -294,6 +327,10 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) Guild *guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()); if (guild) guild->BroadcastToOfficers(this, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL); + + if(sWorld.getConfig(CONFIG_CHATLOG_GUILD)) + sLog.outChat("[OFFICER] Player %s tells guild %s officers: %s", + GetPlayer()->GetName(), guild->GetName().c_str(), msg.c_str()); } break; } @@ -315,13 +352,19 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if(msg.empty()) break; - Group *group = GetPlayer()->GetGroup(); - if(!group || !group->isRaidGroup() || group->isBGGroup()) + // if player is in battleground, he cannot say to battleground members by /ra + Group *group = GetPlayer()->GetOriginalGroup(); + // so if player hasn't OriginalGroup and his player->GetGroup() is BG raid or his group isn't raid, then return + if( !group && !(group = GetPlayer()->GetGroup()) || group->isBGGroup() || !group->isRaidGroup() ) return; WorldPacket data; ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID, lang, "", 0, msg.c_str(),NULL); - group->BroadcastPacket(&data); + group->BroadcastPacket(&data, false); + + if(sWorld.getConfig(CONFIG_CHATLOG_RAID)) + sLog.outChat("[RAID] Player %s tells raid with leader %s: %s", + GetPlayer()->GetName(), group->GetLeaderName(), msg.c_str()); } break; case CHAT_MSG_RAID_LEADER: { @@ -341,13 +384,18 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if(msg.empty()) break; - Group *group = GetPlayer()->GetGroup(); - if(!group || !group->isRaidGroup() || !group->IsLeader(GetPlayer()->GetGUID()) || group->isBGGroup()) + // if player is in battleground, he cannot say to battleground members by /ra + Group *group = GetPlayer()->GetOriginalGroup(); + if( !group && !(group = GetPlayer()->GetGroup()) || group->isBGGroup() || !group->isRaidGroup() || !group->IsLeader(GetPlayer()->GetGUID())) return; WorldPacket data; ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID_LEADER, lang, "", 0, msg.c_str(),NULL); - group->BroadcastPacket(&data); + group->BroadcastPacket(&data, false); + + if(sWorld.getConfig(CONFIG_CHATLOG_RAID)) + sLog.outChat("[RAID] Leader player %s tells raid: %s", + GetPlayer()->GetName(), msg.c_str()); } break; case CHAT_MSG_RAID_WARNING: { @@ -366,8 +414,13 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) return; WorldPacket data; + //in battleground, raid warning is sent only to players in battleground - code is ok ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID_WARNING, lang, "", 0, msg.c_str(),NULL); - group->BroadcastPacket(&data); + group->BroadcastPacket(&data, false); + + if(sWorld.getConfig(CONFIG_CHATLOG_RAID)) + sLog.outChat("[RAID] Leader player %s warns raid with: %s", + GetPlayer()->GetName(), msg.c_str()); } break; case CHAT_MSG_BATTLEGROUND: @@ -382,13 +435,18 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if(msg.empty()) break; + //battleground raid is always in Player->GetGroup(), never in GetOriginalGroup() Group *group = GetPlayer()->GetGroup(); - if(!group || !group->isRaidGroup() || !group->isBGGroup()) + if(!group || !group->isBGGroup()) return; WorldPacket data; ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND, lang, "", 0, msg.c_str(),NULL); - group->BroadcastPacket(&data); + group->BroadcastPacket(&data, false); + + if(sWorld.getConfig(CONFIG_CHATLOG_BGROUND)) + sLog.outChat("[BATTLEGROUND] Player %s tells battleground with leader %s: %s", + GetPlayer()->GetName(), group->GetLeaderName(), msg.c_str()); } break; case CHAT_MSG_BATTLEGROUND_LEADER: @@ -403,13 +461,18 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if(msg.empty()) break; + //battleground raid is always in Player->GetGroup(), never in GetOriginalGroup() Group *group = GetPlayer()->GetGroup(); - if(!group || !group->isRaidGroup() || !group->IsLeader(GetPlayer()->GetGUID()) || !group->isBGGroup()) + if(!group || !group->isBGGroup() || !group->IsLeader(GetPlayer()->GetGUID())) return; WorldPacket data; ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND_LEADER, lang, "", 0, msg.c_str(),NULL); - group->BroadcastPacket(&data); + group->BroadcastPacket(&data, false); + + if(sWorld.getConfig(CONFIG_CHATLOG_BGROUND)) + sLog.outChat("[RAID] Leader player %s tells battleground: %s", + GetPlayer()->GetName(), msg.c_str()); } break; case CHAT_MSG_CHANNEL: @@ -431,8 +494,22 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if(ChannelMgr* cMgr = channelMgr(_player->GetTeam())) { - if(Channel *chn = cMgr->GetChannel(channel,_player)) + Channel *chn = cMgr->GetChannel(channel,_player); + if(chn) + { chn->Say(_player->GetGUID(),msg.c_str(),lang); + + if((chn->HasFlag(CHANNEL_FLAG_TRADE) || + chn->HasFlag(CHANNEL_FLAG_GENERAL) || + chn->HasFlag(CHANNEL_FLAG_CITY) || + chn->HasFlag(CHANNEL_FLAG_LFG)) && + sWorld.getConfig(CONFIG_CHATLOG_SYSCHAN)) + sLog.outChat("[SYSCHAN] Player %s tells channel %s: %s", + GetPlayer()->GetName(), chn->GetName().c_str(), msg.c_str()); + else if(sWorld.getConfig(CONFIG_CHATLOG_CHANNEL)) + sLog.outChat("[CHANNEL] Player %s tells channel %s: %s", + GetPlayer()->GetName(), chn->GetName().c_str(), msg.c_str()); + } } } break; @@ -491,6 +568,38 @@ void WorldSession::HandleEmoteOpcode( WorldPacket & recv_data ) GetPlayer()->HandleEmoteCommand(emote); } +namespace MaNGOS +{ + class EmoteChatBuilder + { + public: + EmoteChatBuilder(Player const& pl, uint32 text_emote, uint32 emote_num, Unit const* target) + : i_player(pl), i_text_emote(text_emote), i_emote_num(emote_num), i_target(target) {} + + void operator()(WorldPacket& data, int32 loc_idx) + { + char const* nam = i_target ? i_target->GetNameForLocaleIdx(loc_idx) : NULL; + uint32 namlen = (nam ? strlen(nam) : 0) + 1; + + data.Initialize(SMSG_TEXT_EMOTE, (20+namlen)); + data << i_player.GetGUID(); + data << (uint32)i_text_emote; + data << i_emote_num; + data << (uint32)namlen; + if( namlen > 1 ) + data.append(nam, namlen); + else + data << (uint8)0x00; + } + + private: + Player const& i_player; + uint32 i_text_emote; + uint32 i_emote_num; + Unit const* i_target; + }; +} // namespace MaNGOS + void WorldSession::HandleTextEmoteOpcode( WorldPacket & recv_data ) { if(!GetPlayer()->isAlive()) @@ -512,56 +621,44 @@ void WorldSession::HandleTextEmoteOpcode( WorldPacket & recv_data ) recv_data >> emoteNum; recv_data >> guid; - const char *nam = 0; - uint32 namlen = 1; + EmotesTextEntry const *em = sEmotesTextStore.LookupEntry(text_emote); + if (!em) + return; - Unit* unit = ObjectAccessor::GetUnit(*_player, guid); - Creature *pCreature = dynamic_cast<Creature *>(unit); - if(unit) + uint32 emote_anim = em->textid; + + switch(emote_anim) { - nam = unit->GetName(); - namlen = (nam ? strlen(nam) : 0) + 1; + case EMOTE_STATE_SLEEP: + case EMOTE_STATE_SIT: + case EMOTE_STATE_KNEEL: + case EMOTE_ONESHOT_NONE: + break; + default: + GetPlayer()->HandleEmoteCommand(emote_anim); + break; } - EmotesTextEntry const *em = sEmotesTextStore.LookupEntry(text_emote); - if (em) - { - uint32 emote_anim = em->textid; + Unit* unit = ObjectAccessor::GetUnit(*_player, guid); - WorldPacket data; + CellPair p = MaNGOS::ComputeCellPair(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY()); - switch(emote_anim) - { - case EMOTE_STATE_SLEEP: - case EMOTE_STATE_SIT: - case EMOTE_STATE_KNEEL: - case EMOTE_ONESHOT_NONE: - break; - default: - GetPlayer()->HandleEmoteCommand(emote_anim); - break; - } + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); - data.Initialize(SMSG_TEXT_EMOTE, (20+namlen)); - data << GetPlayer()->GetGUID(); - data << (uint32)text_emote; - data << emoteNum; - data << (uint32)namlen; - if( namlen > 1 ) - { - data.append(nam, namlen); - } - else - { - data << (uint8)0x00; - } + MaNGOS::EmoteChatBuilder emote_builder(*GetPlayer(), text_emote, emoteNum, unit); + MaNGOS::LocalizedPacketDo<MaNGOS::EmoteChatBuilder > emote_do(emote_builder); + MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::EmoteChatBuilder > > emote_worker(GetPlayer(),sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE),emote_do); + TypeContainerVisitor<MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::EmoteChatBuilder > >, WorldTypeMapContainer > message(emote_worker); + CellLock<GridReadGuard> cell_lock(cell, p); + cell_lock->Visit(cell_lock, message, *GetPlayer()->GetMap()); - GetPlayer()->SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE),true); + GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE, text_emote, 0, unit); - //Send scripted event call - if (pCreature && Script) - Script->ReceiveEmote(GetPlayer(),pCreature,text_emote); - } + //Send scripted event call + if (unit && unit->GetTypeId()==TYPEID_UNIT && Script) + Script->ReceiveEmote(GetPlayer(),(Creature*)unit,text_emote); } void WorldSession::HandleChatIgnoredOpcode(WorldPacket& recv_data ) diff --git a/src/game/CombatHandler.cpp b/src/game/CombatHandler.cpp index fb212870822..878d8e35648 100644 --- a/src/game/CombatHandler.cpp +++ b/src/game/CombatHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,7 +22,6 @@ #include "Log.h" #include "WorldPacket.h" #include "WorldSession.h" -#include "World.h" #include "ObjectAccessor.h" #include "CreatureAI.h" #include "ObjectDefines.h" diff --git a/src/game/ConfusedMovementGenerator.cpp b/src/game/ConfusedMovementGenerator.cpp index bb7b56fdca7..06871ecdfa4 100644 --- a/src/game/ConfusedMovementGenerator.cpp +++ b/src/game/ConfusedMovementGenerator.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/ConfusedMovementGenerator.h b/src/game/ConfusedMovementGenerator.h index 4940c4bcaea..ad47b13cb39 100644 --- a/src/game/ConfusedMovementGenerator.h +++ b/src/game/ConfusedMovementGenerator.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/Corpse.cpp b/src/game/Corpse.cpp index fe4794dea41..d427d1e5943 100644 --- a/src/game/Corpse.cpp +++ b/src/game/Corpse.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,12 +22,9 @@ #include "Corpse.h" #include "Player.h" #include "UpdateMask.h" -#include "MapManager.h" #include "ObjectAccessor.h" #include "Database/DatabaseEnv.h" #include "Opcodes.h" -#include "WorldSession.h" -#include "WorldPacket.h" #include "GossipDef.h" #include "World.h" @@ -36,7 +33,7 @@ Corpse::Corpse(CorpseType type) : WorldObject() m_objectType |= TYPEMASK_CORPSE; m_objectTypeId = TYPEID_CORPSE; // 2.3.2 - 0x58 - m_updateFlag = (UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID | UPDATEFLAG_HASPOSITION); + m_updateFlag = (UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID | UPDATEFLAG_HAS_POSITION); m_valuesCount = CORPSE_END; @@ -71,26 +68,26 @@ bool Corpse::Create( uint32 guidlow ) return true; } -bool Corpse::Create( uint32 guidlow, Player *owner, uint32 mapid, float x, float y, float z, float ang ) +bool Corpse::Create( uint32 guidlow, Player *owner) { SetInstanceId(owner->GetInstanceId()); - WorldObject::_Create(guidlow, HIGHGUID_CORPSE, mapid); + WorldObject::_Create(guidlow, HIGHGUID_CORPSE, owner->GetMapId(), owner->GetPhaseMask()); - Relocate(x,y,z,ang); + Relocate(owner->GetPositionX(), owner->GetPositionY(), owner->GetPositionZ(), owner->GetOrientation()); if(!IsPositionValid()) { - sLog.outError("ERROR: Corpse (guidlow %d, owner %s) not created. Suggested coordinates isn't valid (X: %f Y: %f)", - guidlow,owner->GetName(),x,y); + sLog.outError("Corpse (guidlow %d, owner %s) not created. Suggested coordinates isn't valid (X: %f Y: %f)", + guidlow,owner->GetName(),owner->GetPositionX(), owner->GetPositionY()); return false; } SetFloatValue( OBJECT_FIELD_SCALE_X, 1 ); - SetFloatValue( CORPSE_FIELD_POS_X, x ); - SetFloatValue( CORPSE_FIELD_POS_Y, y ); - SetFloatValue( CORPSE_FIELD_POS_Z, z ); - SetFloatValue( CORPSE_FIELD_FACING, ang ); + SetFloatValue( CORPSE_FIELD_POS_X, GetPositionX() ); + SetFloatValue( CORPSE_FIELD_POS_Y, GetPositionY() ); + SetFloatValue( CORPSE_FIELD_POS_Z, GetPositionZ() ); + SetFloatValue( CORPSE_FIELD_FACING, GetOrientation() ); SetUInt64Value( CORPSE_FIELD_OWNER, owner->GetGUID() ); m_grid = Trinity::ComputeGridPair(GetPositionX(), GetPositionY()); @@ -100,17 +97,27 @@ bool Corpse::Create( uint32 guidlow, Player *owner, uint32 mapid, float x, float void Corpse::SaveToDB() { - // prevent DB data inconsistance problems and duplicates + // prevent DB data inconsistence problems and duplicates CharacterDatabase.BeginTransaction(); DeleteFromDB(); std::ostringstream ss; - ss << "INSERT INTO corpse (guid,player,position_x,position_y,position_z,orientation,zone,map,data,time,corpse_type,instance) VALUES (" - << GetGUIDLow() << ", " << GUID_LOPART(GetOwnerGUID()) << ", " << GetPositionX() << ", " << GetPositionY() << ", " << GetPositionZ() << ", " - << GetOrientation() << ", " << GetZoneId() << ", " << GetMapId() << ", '"; + ss << "INSERT INTO corpse (guid,player,position_x,position_y,position_z,orientation,zone,map,data,time,corpse_type,instance,phaseMask) VALUES (" + << GetGUIDLow() << ", " + << GUID_LOPART(GetOwnerGUID()) << ", " + << GetPositionX() << ", " + << GetPositionY() << ", " + << GetPositionZ() << ", " + << GetOrientation() << ", " + << GetZoneId() << ", " + << GetMapId() << ", '"; for(uint16 i = 0; i < m_valuesCount; i++ ) ss << GetUInt32Value(i) << " "; - ss << "'," << uint64(m_time) <<", " << uint32(GetType()) << ", " << int(GetInstanceId()) << ")"; + ss << "'," + << uint64(m_time) <<", " + << uint32(GetType()) << ", " + << int(GetInstanceId()) << ", " + << uint16(GetPhaseMask()) << ")"; // prevent out of range error CharacterDatabase.Execute( ss.str().c_str() ); CharacterDatabase.CommitTransaction(); } @@ -143,12 +150,12 @@ bool Corpse::LoadFromDB(uint32 guid, QueryResult *result, uint32 InstanceId) { bool external = (result != NULL); if (!external) - // 0 1 2 3 4 5 6 7 8 - result = CharacterDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map,data,time,corpse_type,instance FROM corpse WHERE guid = '%u'",guid); + // 0 1 2 3 4 5 6 7 8 9 + result = CharacterDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map,data,time,corpse_type,instance,phaseMask FROM corpse WHERE guid = '%u'",guid); if( ! result ) { - sLog.outError("ERROR: Corpse (GUID: %u) not found in table `corpse`, can't load. ",guid); + sLog.outError("Corpse (GUID: %u) not found in table `corpse`, can't load. ",guid); return false; } @@ -166,8 +173,8 @@ bool Corpse::LoadFromDB(uint32 guid, QueryResult *result, uint32 InstanceId) bool Corpse::LoadFromDB(uint32 guid, Field *fields) { - // 0 1 2 3 4 5 6 7 8 - //result = CharacterDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map,data,time,corpse_type,instance FROM corpse WHERE guid = '%u'",guid); + // 0 1 2 3 4 5 6 7 8 9 + //result = CharacterDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map,data,time,corpse_type,instance,phaseMask FROM corpse WHERE guid = '%u'",guid); float positionX = fields[0].GetFloat(); float positionY = fields[1].GetFloat(); float positionZ = fields[2].GetFloat(); @@ -176,7 +183,7 @@ bool Corpse::LoadFromDB(uint32 guid, Field *fields) if(!LoadValues( fields[5].GetString() )) { - sLog.outError("ERROR: Corpse #%d have broken data in `data` field. Can't be loaded.",guid); + sLog.outError("Corpse #%d have broken data in `data` field. Can't be loaded.",guid); return false; } @@ -184,10 +191,11 @@ bool Corpse::LoadFromDB(uint32 guid, Field *fields) m_type = CorpseType(fields[7].GetUInt32()); if(m_type >= MAX_CORPSE_TYPE) { - sLog.outError("ERROR: Corpse (guidlow %d, owner %d) have wrong corpse type, not load.",GetGUIDLow(),GUID_LOPART(GetOwnerGUID())); + sLog.outError("Corpse (guidlow %d, owner %d) have wrong corpse type, not load.",GetGUIDLow(),GUID_LOPART(GetOwnerGUID())); return false; } uint32 instanceid = fields[8].GetUInt32(); + uint32 phaseMask = fields[9].GetUInt32(); // overwrite possible wrong/corrupted guid SetUInt64Value(OBJECT_FIELD_GUID, MAKE_NEW_GUID(guid, 0, HIGHGUID_CORPSE)); @@ -195,11 +203,12 @@ bool Corpse::LoadFromDB(uint32 guid, Field *fields) // place SetInstanceId(instanceid); SetMapId(mapid); + SetPhaseMask(phaseMask,false); Relocate(positionX,positionY,positionZ,ort); if(!IsPositionValid()) { - sLog.outError("ERROR: Corpse (guidlow %d, owner %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)", + sLog.outError("Corpse (guidlow %d, owner %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)", GetGUIDLow(),GUID_LOPART(GetOwnerGUID()),GetPositionX(),GetPositionY()); return false; } @@ -211,6 +220,6 @@ bool Corpse::LoadFromDB(uint32 guid, Field *fields) bool Corpse::isVisibleForInState(Player const* u, bool inVisibleList) const { - return IsInWorld() && u->IsInWorld() && IsWithinDistInMap(u,World::GetMaxVisibleDistanceForObject()+(inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f), false); + return IsInWorld() && u->IsInWorld() && IsWithinDistInMap(u->m_seer,World::GetMaxVisibleDistanceForObject()+(inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f), false); } diff --git a/src/game/Corpse.h b/src/game/Corpse.h index 91131c1cb85..da3511abfbb 100644 --- a/src/game/Corpse.h +++ b/src/game/Corpse.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -58,7 +58,7 @@ class Corpse : public WorldObject void RemoveFromWorld(); bool Create( uint32 guidlow ); - bool Create( uint32 guidlow, Player *owner, uint32 mapid, float x, float y, float z, float ang ); + bool Create( uint32 guidlow, Player *owner ); void SaveToDB(); bool LoadFromDB(uint32 guid, QueryResult *result, uint32 InstanceId); @@ -82,14 +82,11 @@ class Corpse : public WorldObject Player* lootRecipient; bool lootForBody; - void Say(const char* text, uint32 language, uint64 TargetGuid) { MonsterSay(text,language,TargetGuid); } - void Yell(const char* text, uint32 language, uint64 TargetGuid) { MonsterYell(text,language,TargetGuid); } - void TextEmote(const char* text, uint64 TargetGuid) { MonsterTextEmote(text,TargetGuid); } - void Whisper(const char* text, uint64 receiver) { MonsterWhisper(text,receiver); } void Say(int32 textId, uint32 language, uint64 TargetGuid) { MonsterSay(textId,language,TargetGuid); } void Yell(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYell(textId,language,TargetGuid); } void TextEmote(int32 textId, uint64 TargetGuid) { MonsterTextEmote(textId,TargetGuid); } void Whisper(int32 textId,uint64 receiver) { MonsterWhisper(textId,receiver); } + void YellToZone(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYellToZone(textId,language,TargetGuid); } GridReference<Corpse> &GetGridRef() { return m_gridRef; } private: diff --git a/src/game/Creature.cpp b/src/game/Creature.cpp index 0681dcddd04..f03fe50aaff 100644 --- a/src/game/Creature.cpp +++ b/src/game/Creature.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,7 +21,6 @@ #include "Common.h" #include "Database/DatabaseEnv.h" #include "WorldPacket.h" -#include "WorldSession.h" #include "World.h" #include "ObjectMgr.h" #include "SpellMgr.h" @@ -29,6 +28,7 @@ #include "QuestDef.h" #include "GossipDef.h" #include "Player.h" +#include "PoolHandler.h" #include "Opcodes.h" #include "Log.h" #include "LootMgr.h" @@ -36,32 +36,24 @@ #include "CreatureAI.h" #include "CreatureAISelector.h" #include "Formulas.h" -#include "SpellAuras.h" #include "WaypointMovementGenerator.h" #include "InstanceData.h" -#include "BattleGround.h" +#include "BattleGroundMgr.h" #include "Util.h" #include "GridNotifiers.h" #include "GridNotifiersImpl.h" #include "CellImpl.h" #include "OutdoorPvPMgr.h" -#include "GameEvent.h" +#include "GameEventMgr.h" #include "CreatureGroups.h" // apply implementation of the singletons #include "Policies/SingletonImp.h" -void TrainerSpellData::Clear() -{ - for (TrainerSpellList::iterator itr = spellList.begin(); itr != spellList.end(); ++itr) - delete (*itr); - spellList.empty(); -} - TrainerSpell const* TrainerSpellData::Find(uint32 spell_id) const { - for(TrainerSpellList::const_iterator itr = spellList.begin(); itr != spellList.end(); ++itr) - if((*itr)->spell == spell_id) - return *itr; + TrainerSpellMap::const_iterator itr = spellList.find(spell_id); + if (itr != spellList.end()) + return &itr->second; return NULL; } @@ -144,14 +136,15 @@ Unit(), lootForPickPocketed(false), lootForBody(false), m_groupLootTimer(0), lootingGroupLeaderGUID(0), m_lootMoney(0), m_lootRecipient(0), m_deathTimer(0), m_respawnTime(0), m_respawnDelay(25), m_corpseDelay(60), m_respawnradius(0.0f), -m_gossipOptionLoaded(false), m_emoteState(0), m_isPet(false), m_isTotem(false), m_reactState(REACT_AGGRESSIVE), -m_regenTimer(2000), m_defaultMovementType(IDLE_MOTION_TYPE), m_equipmentId(0), -m_AlreadyCallAssistance(false), m_regenHealth(true), m_AI_locked(false), m_isDeadByDefault(false), -m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL),m_creatureInfo(NULL), m_DBTableGuid(0), m_formationID(0) +m_gossipOptionLoaded(false), +m_defaultMovementType(IDLE_MOTION_TYPE), m_DBTableGuid(0), m_equipmentId(0), m_AlreadyCallAssistance(false), +m_regenHealth(true), m_AI_locked(false), m_isDeadByDefault(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), +m_creatureInfo(NULL), m_reactState(REACT_AGGRESSIVE), m_formation(NULL), m_summonMask(SUMMON_MASK_NONE) { + m_regenTimer = 200; m_valuesCount = UNIT_END; - for(int i =0; i<4; ++i) + for(int i =0; i<CREATURE_MAX_SPELLS; ++i) m_spells[i] = 0; m_CreatureSpellCooldowns.clear(); @@ -183,33 +176,34 @@ void Creature::AddToWorld() ObjectAccessor::Instance().AddObject(this); Unit::AddToWorld(); SearchFormation(); + AIM_Initialize(); } } void Creature::RemoveFromWorld() { - ///- Remove the creature from the accessor if(IsInWorld()) { - if(m_formationID) - formation_mgr.DestroyGroup(m_formationID, GetGUID()); - ObjectAccessor::Instance().RemoveObject(this); + // Clear formation info + if(m_formation) + formation_mgr.RemoveCreatureFromGroup(m_formation, this); Unit::RemoveFromWorld(); + ObjectAccessor::Instance().RemoveObject(this); } } void Creature::SearchFormation() { - if(isPet()) + if(isSummon()) return; uint32 lowguid = GetDBTableGUIDLow(); + if(!lowguid) + return; - if(lowguid && CreatureGroupMap.find(lowguid) != CreatureGroupMap.end()) - { - m_formationID = CreatureGroupMap[lowguid]->leaderGUID; - formation_mgr.UpdateCreatureGroup(m_formationID, this); - } + CreatureGroupInfoType::iterator frmdata = CreatureGroupMap.find(lowguid); + if(frmdata != CreatureGroupMap.end()) + formation_mgr.AddCreatureToGroup(frmdata->second->leaderGUID, this); } void Creature::RemoveCorpse() @@ -310,10 +304,8 @@ bool Creature::InitEntry(uint32 Entry, uint32 team, const CreatureData *data ) if(!m_respawnradius && m_defaultMovementType==RANDOM_MOTION_TYPE) m_defaultMovementType = IDLE_MOTION_TYPE; - m_spells[0] = GetCreatureInfo()->spell1; - m_spells[1] = GetCreatureInfo()->spell2; - m_spells[2] = GetCreatureInfo()->spell3; - m_spells[3] = GetCreatureInfo()->spell4; + for(int i=0; i < CREATURE_MAX_SPELLS; ++i) + m_spells[i] = GetCreatureInfo()->spells[i]; return true; } @@ -327,7 +319,6 @@ bool Creature::UpdateEntry(uint32 Entry, uint32 team, const CreatureData *data ) // creatures always have melee weapon ready if any SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE ); - SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_AURAS ); SelectLevel(GetCreatureInfo()); if (team == HORDE) @@ -438,7 +429,11 @@ void Creature::Update(uint32 diff) //Call AI respawn virtual function AI()->JustRespawned(); - GetMap()->Add(this); + uint16 poolid = poolhandler.IsPartOfAPool(GetGUIDLow(), GetTypeId()); + if (poolid) + poolhandler.UpdatePool(poolid, GetGUIDLow(), GetTypeId()); + else + GetMap()->Add(this); } break; } @@ -616,19 +611,37 @@ bool Creature::AIM_Initialize(CreatureAI* ai) return false; } - if(i_AI) delete i_AI; - i_motionMaster.Initialize(); + UnitAI *oldAI = i_AI; + + Motion_Initialize(); + i_AI = ai ? ai : FactorySelector::selectAI(this); + if(oldAI) delete oldAI; IsAIEnabled = true; i_AI->InitializeAI(); return true; } -bool Creature::Create (uint32 guidlow, Map *map, uint32 Entry, uint32 team, const CreatureData *data) +void Creature::Motion_Initialize() +{ + if(!m_formation) + i_motionMaster.Initialize(); + else if(m_formation->getLeader() == this) + { + m_formation->FormationReset(false); + i_motionMaster.Initialize(); + } + else if(m_formation->isFormed()) + i_motionMaster.MoveIdle(MOTION_SLOT_IDLE); //wait the order of leader + else + i_motionMaster.Initialize(); +} + +bool Creature::Create (uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, uint32 team, const CreatureData *data) { SetMapId(map->GetId()); SetInstanceId(map->GetInstanceId()); - //m_DBTableGuid = guidlow; + SetPhaseMask(phaseMask,false); //oX = x; oY = y; dX = x; dY = y; m_moveTime = 0; m_startMove = 0; const bool bResult = CreateFromProto(guidlow, Entry, team, data); @@ -749,7 +762,7 @@ bool Creature::isCanInteractWithBattleMaster(Player* pPlayer, bool msg) const if(!isBattleMaster()) return false; - uint32 bgTypeId = objmgr.GetBattleMasterBG(GetEntry()); + BattleGroundTypeId bgTypeId = sBattleGroundMgr.GetBattleMasterBG(GetEntry()); if(!msg) return pPlayer->GetBGAccessByLevel(bgTypeId); @@ -765,8 +778,11 @@ bool Creature::isCanInteractWithBattleMaster(Player* pPlayer, bool msg) const case BATTLEGROUND_NA: case BATTLEGROUND_BE: case BATTLEGROUND_AA: - case BATTLEGROUND_RL: pPlayer->PlayerTalkClass->SendGossipMenu(10024,GetGUID()); break; - break; + case BATTLEGROUND_RL: + case BATTLEGROUND_SA: + case BATTLEGROUND_DS: + case BATTLEGROUND_RV: pPlayer->PlayerTalkClass->SendGossipMenu(10024,GetGUID()); break; + default: break; } return false; } @@ -802,7 +818,7 @@ void Creature::prepareGossipMenu( Player *pPlayer,uint32 gossipid ) if(gso->Id==1) { uint32 textid=GetNpcTextId(); - GossipText * gossiptext=objmgr.GetGossipText(textid); + GossipText const* gossiptext=objmgr.GetGossipText(textid); if(!gossiptext) cantalking=false; } @@ -913,13 +929,11 @@ void Creature::sendPreparedGossip(Player* player) if(!player) return; - GossipMenu& gossipmenu = player->PlayerTalkClass->GetGossipMenu(); - if(GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_WORLDEVENT) // if world event npc then gameeventmgr.HandleWorldEventGossip(player, this); // update world state with progress - // in case empty gossip menu open quest menu if any - if (gossipmenu.Empty() && GetNpcTextId() == 0) + // in case no gossip flag and quest menu not empty, open quest menu (client expect gossip menu with this flag) + if (!HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_GOSSIP) && !player->PlayerTalkClass->GetQuestMenu().Empty()) { player->SendPreparedQuest(GetGUID()); return; @@ -1020,7 +1034,7 @@ void Creature::OnGossipSelect(Player* player, uint32 option) break; case GOSSIP_OPTION_BATTLEFIELD: { - uint32 bgTypeId = objmgr.GetBattleMasterBG(GetEntry()); + BattleGroundTypeId bgTypeId = sBattleGroundMgr.GetBattleMasterBG(GetEntry()); player->GetSession()->SendBattlegGroundList( GetGUID(), bgTypeId ); break; } @@ -1035,12 +1049,12 @@ void Creature::OnPoiSelect(Player* player, GossipOption const *gossip) { if(gossip->GossipId==GOSSIP_GUARD_SPELLTRAINER || gossip->GossipId==GOSSIP_GUARD_SKILLTRAINER) { - Poi_Icon icon = ICON_POI_0; + Poi_Icon icon = ICON_POI_BLANK; //need add more case. switch(gossip->Action) { case GOSSIP_GUARD_BANK: - icon=ICON_POI_HOUSE; + icon=ICON_POI_SMALL_HOUSE; break; case GOSSIP_GUARD_RIDE: icon=ICON_POI_RWHORSE; @@ -1049,7 +1063,7 @@ void Creature::OnPoiSelect(Player* player, GossipOption const *gossip) icon=ICON_POI_BLUETOWER; break; default: - icon=ICON_POI_TOWER; + icon=ICON_POI_GREYTOWER; break; } uint32 textid = GetGossipTextId( gossip->Action, GetZoneId() ); @@ -1183,10 +1197,10 @@ void Creature::SaveToDB() return; } - SaveToDB(GetMapId(), data->spawnMask); + SaveToDB(GetMapId(), data->spawnMask,GetPhaseMask()); } -void Creature::SaveToDB(uint32 mapid, uint8 spawnMask) +void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) { // update in loaded data if (!m_DBTableGuid) @@ -1206,6 +1220,7 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask) // data->guid = guid don't must be update at save data.id = GetEntry(); data.mapid = mapid; + data.phaseMask = phaseMask; data.displayid = displayId; data.equipmentId = GetEquipmentId(); data.posX = GetPositionX(); @@ -1234,7 +1249,8 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask) << m_DBTableGuid << "," << GetEntry() << "," << mapid <<"," - << (uint32)spawnMask << "," + << uint32(spawnMask) << "," // cast to prevent save as symbol + << uint16(GetPhaseMask()) << "," // prevent out of range error << displayId <<"," << GetEquipmentId() <<"," << GetPositionX() << "," @@ -1368,7 +1384,7 @@ bool Creature::CreateFromProto(uint32 guidlow, uint32 Entry, uint32 team, const CreatureInfo const *cinfo = objmgr.GetCreatureTemplate(Entry); if(!cinfo) { - sLog.outErrorDb("Error: creature entry %u does not exist.", Entry); + sLog.outErrorDb("Creature entry %u does not exist.", Entry); return false; } m_originalEntry = Entry; @@ -1400,18 +1416,22 @@ bool Creature::LoadFromDB(uint32 guid, Map *map) return false; } + if(const CreatureInfo *cInfo = objmgr.GetCreatureTemplate(data->id)) + if(cInfo->VehicleId) + return false; + m_DBTableGuid = guid; if (map->GetInstanceId() != 0) guid = objmgr.GenerateLowGuid(HIGHGUID_UNIT); uint16 team = 0; - if(!Create(guid,map,data->id,team,data)) + if(!Create(guid,map,data->phaseMask,data->id,team,data)) return false; Relocate(data->posX,data->posY,data->posZ,data->orientation); if(!IsPositionValid()) { - sLog.outError("ERROR: Creature (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %f Y: %f)",GetGUIDLow(),GetEntry(),GetPositionX(),GetPositionY()); + sLog.outError("Creature (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %f Y: %f)",GetGUIDLow(),GetEntry(),GetPositionX(),GetPositionY()); return false; } //We should set first home position, because then AI calls home movement @@ -1454,7 +1474,6 @@ bool Creature::LoadFromDB(uint32 guid, Map *map) // checked at creature_template loading m_defaultMovementType = MovementGeneratorType(data->movementType); - AIM_Initialize(); return true; } @@ -1465,11 +1484,7 @@ void Creature::LoadEquipment(uint32 equip_entry, bool force) if (force) { for (uint8 i = 0; i < 3; i++) - { - SetUInt32Value( UNIT_VIRTUAL_ITEM_SLOT_DISPLAY + i, 0); - SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + (i * 2), 0); - SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + (i * 2) + 1, 0); - } + SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + i, 0); m_equipmentId = 0; } return; @@ -1481,11 +1496,7 @@ void Creature::LoadEquipment(uint32 equip_entry, bool force) m_equipmentId = equip_entry; for (uint8 i = 0; i < 3; i++) - { - SetUInt32Value( UNIT_VIRTUAL_ITEM_SLOT_DISPLAY + i, einfo->equipmodel[i]); - SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + (i * 2), einfo->equipinfo[i]); - SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + (i * 2) + 1, einfo->equipslot[i]); - } + SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + i, einfo->equipentry[i]); } bool Creature::hasQuest(uint32 quest_id) const @@ -1543,6 +1554,10 @@ bool Creature::canSeeOrDetect(Unit const* u, bool detect, bool inVisibleList, bo if (u == this) return true; + // phased visibility (both must phased in same way) + if(!InSamePhase(u)) + return false; + // always seen by owner if(GetGUID() == u->GetCharmerOrOwnerGUID()) return true; @@ -1631,7 +1646,7 @@ void Creature::setDeathState(DeathState s) { if((s == JUST_DIED && !m_isDeadByDefault)||(s == JUST_ALIVED && m_isDeadByDefault)) { - m_deathTimer = m_corpseDelay*1000; + m_deathTimer = m_corpseDelay*IN_MILISECONDS; // always save boss respawn time at death to prevent crash cheating if(sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY) || isWorldBoss()) @@ -1657,6 +1672,10 @@ void Creature::setDeathState(DeathState s) return; Unit::setDeathState(CORPSE); + + //Dismiss group if is leader + if(m_formation && m_formation->getLeader() == this) + m_formation->FormationReset(true); } if(s == JUST_ALIVED) { @@ -1670,9 +1689,9 @@ void Creature::setDeathState(DeathState s) AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); SetUInt32Value(UNIT_NPC_FLAGS, cinfo->npcflag); clearUnitState(UNIT_STAT_ALL_STATE); - i_motionMaster.Initialize(); SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool)); LoadCreaturesAddon(true); + Motion_Initialize(); } } @@ -1713,7 +1732,7 @@ void Creature::Respawn() } } -bool Creature::IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges) +bool Creature::IsImmunedToSpell(SpellEntry const* spellInfo) { if (!spellInfo) return false; @@ -1721,15 +1740,15 @@ bool Creature::IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges) if (GetCreatureInfo()->MechanicImmuneMask & (1 << (spellInfo->Mechanic - 1))) return true; - return Unit::IsImmunedToSpell(spellInfo, useCharges); + return Unit::IsImmunedToSpell(spellInfo); } -bool Creature::IsImmunedToSpellEffect(uint32 effect, uint32 mechanic) const +bool Creature::IsImmunedToSpellEffect(SpellEntry const* spellInfo, uint32 index) const { - if (GetCreatureInfo()->MechanicImmuneMask & (1 << (mechanic-1))) + if (GetCreatureInfo()->MechanicImmuneMask & (1 << (spellInfo->EffectMechanic[index] - 1))) return true; - return Unit::IsImmunedToSpellEffect(effect, mechanic); + return Unit::IsImmunedToSpellEffect(spellInfo, index); } SpellEntry const *Creature::reachWithSpellAttack(Unit *pVictim) @@ -1744,7 +1763,7 @@ SpellEntry const *Creature::reachWithSpellAttack(Unit *pVictim) SpellEntry const *spellInfo = sSpellStore.LookupEntry(m_spells[i] ); if(!spellInfo) { - sLog.outError("WORLD: unknown spell id %i\n", m_spells[i]); + sLog.outError("WORLD: unknown spell id %i", m_spells[i]); continue; } @@ -1766,14 +1785,16 @@ SpellEntry const *Creature::reachWithSpellAttack(Unit *pVictim) if(spellInfo->manaCost > GetPower(POWER_MANA)) continue; SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex); - float range = GetSpellMaxRange(srange); - float minrange = GetSpellMinRange(srange); + float range = GetSpellMaxRangeForHostile(srange); + float minrange = GetSpellMinRangeForHostile(srange); float dist = GetDistance(pVictim); //if(!isInFront( pVictim, range ) && spellInfo->AttributesEx ) // continue; if( dist > range || dist < minrange ) continue; - if(HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED)) + if(spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED)) + continue; + if(spellInfo->PreventionType == SPELL_PREVENTION_TYPE_PACIFY && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED)) continue; return spellInfo; } @@ -1792,7 +1813,7 @@ SpellEntry const *Creature::reachWithSpellCure(Unit *pVictim) SpellEntry const *spellInfo = sSpellStore.LookupEntry(m_spells[i] ); if(!spellInfo) { - sLog.outError("WORLD: unknown spell id %i\n", m_spells[i]); + sLog.outError("WORLD: unknown spell id %i", m_spells[i]); continue; } @@ -1810,14 +1831,16 @@ SpellEntry const *Creature::reachWithSpellCure(Unit *pVictim) if(spellInfo->manaCost > GetPower(POWER_MANA)) continue; SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex); - float range = GetSpellMaxRange(srange); - float minrange = GetSpellMinRange(srange); + float range = GetSpellMaxRangeForFriend(srange); + float minrange = GetSpellMinRangeForFriend( srange); float dist = GetDistance(pVictim); //if(!isInFront( pVictim, range ) && spellInfo->AttributesEx ) // continue; if( dist > range || dist < minrange ) continue; - if(HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED)) + if(spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED)) + continue; + if(spellInfo->PreventionType == SPELL_PREVENTION_TYPE_PACIFY && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED)) continue; return spellInfo; } @@ -1865,7 +1888,7 @@ void Creature::DoFleeToGetAssistance(float radius) // Optional parameter Creature* pCreature = NULL; Trinity::NearestAssistCreatureInCreatureRangeCheck u_check(this,getVictim(),radius); - Trinity::CreatureLastSearcher<Trinity::NearestAssistCreatureInCreatureRangeCheck> searcher(pCreature, u_check); + Trinity::CreatureLastSearcher<Trinity::NearestAssistCreatureInCreatureRangeCheck> searcher(this, pCreature, u_check); VisitNearbyGridObject(radius, searcher); if(!pCreature) @@ -1885,7 +1908,7 @@ Unit* Creature::SelectNearestTarget(float dist) const { Trinity::NearestHostileUnitInAttackDistanceCheck u_check(this, dist); - Trinity::UnitLastSearcher<Trinity::NearestHostileUnitInAttackDistanceCheck> searcher(target, u_check); + Trinity::UnitLastSearcher<Trinity::NearestHostileUnitInAttackDistanceCheck> searcher(this, target, u_check); TypeContainerVisitor<Trinity::UnitLastSearcher<Trinity::NearestHostileUnitInAttackDistanceCheck>, WorldTypeMapContainer > world_unit_searcher(searcher); TypeContainerVisitor<Trinity::UnitLastSearcher<Trinity::NearestHostileUnitInAttackDistanceCheck>, GridTypeMapContainer > grid_unit_searcher(searcher); @@ -1915,8 +1938,8 @@ void Creature::CallAssistance() cell.data.Part.reserved = ALL_DISTRICT; cell.SetNoCreate(); - Trinity::AnyAssistCreatureInRangeCheck u_check(this, getVictim(), radius); - Trinity::CreatureListSearcher<Trinity::AnyAssistCreatureInRangeCheck> searcher(assistList, u_check); + MaNGOS::AnyAssistCreatureInRangeCheck u_check(this, getVictim(), radius); + MaNGOS::CreatureListSearcher<MaNGOS::AnyAssistCreatureInRangeCheck> searcher(this, assistList, u_check); TypeContainerVisitor<Trinity::CreatureListSearcher<Trinity::AnyAssistCreatureInRangeCheck>, GridTypeMapContainer > grid_creature_searcher(searcher); @@ -1976,7 +1999,7 @@ void Creature::SaveRespawnTime() if(m_respawnTime > time(NULL)) // dead (no corpse) objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),m_respawnTime); else if(m_deathTimer > 0) // dead (corpse) - objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),time(NULL)+m_respawnDelay+m_deathTimer/1000); + objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),time(NULL)+m_respawnDelay+m_deathTimer/IN_MILISECONDS); } bool Creature::IsOutOfThreatArea(Unit* pVictim) const @@ -2065,9 +2088,8 @@ bool Creature::LoadCreaturesAddon(bool reload) continue; } - Aura* AdditionalAura = CreateAura(AdditionalSpellInfo, cAura->effect_idx, NULL, this, this, 0); - AddAura(AdditionalAura); - sLog.outDebug("Spell: %u with Aura %u added to creature (GUIDLow: %u Entry: %u )", cAura->spell_id, AdditionalSpellInfo->EffectApplyAuraName[0],GetGUIDLow(),GetEntry()); + AddAuraEffect(AdditionalSpellInfo->Id, cAura->effect_idx, this); + sLog.outDebug("Spell: %u with Aura %u added to creature (GUIDLow: %u Entry: %u )", cAura->spell_id, AdditionalSpellInfo->EffectApplyAuraName[cAura->effect_idx],GetGUIDLow(),GetEntry()); } } return true; @@ -2101,7 +2123,7 @@ void Creature::AddCreatureSpellCooldown(uint32 spellid) uint32 cooldown = GetSpellRecoveryTime(spellInfo); if(cooldown) - _AddCreatureSpellCooldown(spellid, time(NULL) + cooldown/1000); + _AddCreatureSpellCooldown(spellid, time(NULL) + cooldown/IN_MILISECONDS); if(spellInfo->Category) _AddCreatureCategoryCooldown(spellInfo->Category, time(NULL)); @@ -2120,7 +2142,7 @@ bool Creature::HasCategoryCooldown(uint32 spell_id) const return true; CreatureSpellCooldowns::const_iterator itr = m_CreatureCategoryCooldowns.find(spellInfo->Category); - return(itr != m_CreatureCategoryCooldowns.end() && time_t(itr->second + (spellInfo->CategoryRecoveryTime / 1000)) > time(NULL)); + return(itr != m_CreatureCategoryCooldowns.end() && time_t(itr->second + (spellInfo->CategoryRecoveryTime / IN_MILISECONDS)) > time(NULL)); } bool Creature::HasSpellCooldown(uint32 spell_id) const @@ -2149,7 +2171,7 @@ time_t Creature::GetRespawnTimeEx() const if(m_respawnTime > now) // dead (no corpse) return m_respawnTime; else if(m_deathTimer > 0) // dead (corpse) - return now+m_respawnDelay+m_deathTimer/1000; + return now+m_respawnDelay+m_deathTimer/IN_MILISECONDS; else return now; } @@ -2191,7 +2213,7 @@ void Creature::AllLootRemovedFromCorpse() // corpse was not skinnable -> apply corpse looted timer if (!cinfo || !cinfo->SkinLootId) - nDeathTimer = (uint32)((m_corpseDelay * 1000) * sWorld.getRate(RATE_CORPSE_DECAY_LOOTED)); + nDeathTimer = (uint32)((m_corpseDelay * IN_MILISECONDS) * sWorld.getRate(RATE_CORPSE_DECAY_LOOTED)); // corpse skinnable, but without skinning flag, and then skinned, corpse will despawn next update else nDeathTimer = 0; @@ -2215,12 +2237,17 @@ uint32 Creature::getLevelForTarget( Unit const* target ) const return level; } -std::string Creature::GetScriptName() +std::string Creature::GetAIName() const +{ + return ObjectMgr::GetCreatureTemplate(GetEntry())->AIName; +} + +std::string Creature::GetScriptName() const { return objmgr.GetScriptName(GetScriptId()); } -uint32 Creature::GetScriptId() +uint32 Creature::GetScriptId() const { return ObjectMgr::GetCreatureTemplate(GetEntry())->ScriptID; } @@ -2322,4 +2349,3 @@ const char* Creature::GetNameForLocaleIdx(int32 loc_idx) const return GetName(); } - diff --git a/src/game/Creature.h b/src/game/Creature.h index 0930fe00701..0f12401bba8 100644 --- a/src/game/Creature.h +++ b/src/game/Creature.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,7 +28,6 @@ #include "LootMgr.h" #include "Database/DatabaseEnv.h" #include "Cell.h" -#include "CreatureGroups.h" #include <list> @@ -38,6 +37,7 @@ class CreatureAI; class Quest; class Player; class WorldSession; +class CreatureGroup; enum Gossip_Option { @@ -137,6 +137,16 @@ enum CreatureFlagsExtra CREATURE_FLAG_EXTRA_NO_CRIT = 0x00020000, // creature can't do critical strikes }; +enum SummonMask +{ + SUMMON_MASK_NONE = 0x00000000, + SUMMON_MASK_SUMMON = 0x00000001, + SUMMON_MASK_GUARDIAN = 0x00000002, + SUMMON_MASK_TOTEM = 0x00000004, + SUMMON_MASK_PET = 0x00000008, + SUMMON_MASK_VEHICLE = 0x00000010, +}; + // GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some platform #if defined( __GNUC__ ) #pragma pack(1) @@ -177,7 +187,7 @@ struct CreatureInfo uint32 rangeattacktime; uint32 unit_flags; // enum UnitFlags mask values uint32 dynamicflags; - uint32 family; // enum CreatureFamily values for type==CREATURE_TYPE_BEAST, or 0 in another cases + uint32 family; // enum CreatureFamily values (optional) uint32 trainer_type; uint32 trainer_spell; uint32 classNum; @@ -196,16 +206,16 @@ struct CreatureInfo int32 resistance4; int32 resistance5; int32 resistance6; - uint32 spell1; - uint32 spell2; - uint32 spell3; - uint32 spell4; + uint32 spells[CREATURE_MAX_SPELLS]; uint32 PetSpellDataId; + uint32 VehicleId; uint32 mingold; uint32 maxgold; char const* AIName; uint32 MovementType; uint32 InhabitType; + float unk16; + float unk17; bool RacialLeader; bool RegenHealth; uint32 equipmentId; @@ -222,6 +232,8 @@ struct CreatureInfo return SKILL_HERBALISM; else if(type_flags & CREATURE_TYPEFLAGS_MININGLOOT) return SKILL_MINING; + else if(type_flags & CREATURE_TYPEFLAGS_ENGINEERLOOT) + return SKILL_ENGINERING; else return SKILL_SKINNING; // normal case } @@ -244,12 +256,15 @@ struct NpcOptionLocale std::vector<std::string> BoxText; }; +struct PointOfInterestLocale +{ + std::vector<std::string> IconName; +}; + struct EquipmentInfo { uint32 entry; - uint32 equipmodel[3]; - uint32 equipinfo[3]; - uint32 equipslot[3]; + uint32 equipentry[3]; }; // from `creature` table @@ -257,6 +272,7 @@ struct CreatureData { uint32 id; // entry in creature_template uint16 mapid; + uint16 phaseMask; uint32 displayid; int32 equipmentId; float posX; @@ -310,6 +326,29 @@ enum InhabitTypeValues INHABIT_ANYWHERE = INHABIT_GROUND | INHABIT_WATER | INHABIT_AIR }; +// Enums used by StringTextData::Type (CreatureEventAI) +enum ChatType +{ + CHAT_TYPE_SAY = 0, + CHAT_TYPE_YELL = 1, + CHAT_TYPE_TEXT_EMOTE = 2, + CHAT_TYPE_BOSS_EMOTE = 3, + CHAT_TYPE_WHISPER = 4, + CHAT_TYPE_BOSS_WHISPER = 5, + CHAT_TYPE_ZONE_YELL = 6 +}; + +//Selection method used by SelectTarget (CreatureEventAI) +enum AttackingTarget +{ + ATTACKING_TARGET_RANDOM = 0, //Just selects a random target + ATTACKING_TARGET_TOPAGGRO, //Selects targes from top aggro to bottom + ATTACKING_TARGET_BOTTOMAGGRO, //Selects targets from bottom aggro to top + ATTACKING_TARGET_RANDOM_PLAYER, //Just selects a random target (player only) + ATTACKING_TARGET_TOPAGGRO_PLAYER, //Selects targes from top aggro to bottom (player only) + ATTACKING_TARGET_BOTTOMAGGRO_PLAYER, //Selects targets from bottom aggro to top (player only) +}; + // GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform #if defined( __GNUC__ ) #pragma pack() @@ -353,6 +392,7 @@ struct VendorItemData { for (VendorItemList::iterator itr = m_items.begin(); itr != m_items.end(); ++itr) delete (*itr); + m_items.clear(); } }; @@ -370,25 +410,34 @@ typedef std::list<VendorItemCount> VendorItemCounts; struct TrainerSpell { + TrainerSpell() : spell(0), spellCost(0), reqSkill(0), reqSkillValue(0), reqLevel(0), learnedSpell(0) {} + + TrainerSpell(uint32 _spell, uint32 _spellCost, uint32 _reqSkill, uint32 _reqSkillValue, uint32 _reqLevel, uint32 _learnedspell) + : spell(_spell), spellCost(_spellCost), reqSkill(_reqSkill), reqSkillValue(_reqSkillValue), reqLevel(_reqLevel), learnedSpell(_learnedspell) + {} + uint32 spell; - uint32 spellcost; - uint32 reqskill; - uint32 reqskillvalue; - uint32 reqlevel; + uint32 spellCost; + uint32 reqSkill; + uint32 reqSkillValue; + uint32 reqLevel; + uint32 learnedSpell; + + // helpers + bool IsCastable() const { return learnedSpell != spell; } }; -typedef std::vector<TrainerSpell*> TrainerSpellList; +typedef UNORDERED_MAP<uint32 /*spellid*/, TrainerSpell> TrainerSpellMap; struct TrainerSpellData { TrainerSpellData() : trainerType(0) {} - TrainerSpellList spellList; + TrainerSpellMap spellList; uint32 trainerType; // trainer type based at trainer spells, can be different from creature_template value. // req. for correct show non-prof. trainers like weaponmaster, allowed values 0 and 2. - - void Clear(); TrainerSpell const* Find(uint32 spell_id) const; + void Clear() { spellList.clear(); } }; typedef std::list<GossipOption> GossipOptionList; @@ -410,7 +459,7 @@ class TRINITY_DLL_SPEC Creature : public Unit void AddToWorld(); void RemoveFromWorld(); - bool Create (uint32 guidlow, Map *map, uint32 Entry, uint32 team, const CreatureData *data = NULL); + bool Create (uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, uint32 team, const CreatureData *data = NULL); bool LoadCreaturesAddon(bool reload = false); void SelectLevel(const CreatureInfo *cinfo); void LoadEquipment(uint32 equip_entry, bool force=false); @@ -422,9 +471,13 @@ class TRINITY_DLL_SPEC Creature : public Unit void GetRespawnCoord(float &x, float &y, float &z, float* ori = NULL, float* dist =NULL) const; uint32 GetEquipmentId() const { return m_equipmentId; } - bool isPet() const { return m_isPet; } + uint32 HasSummonMask(uint32 mask) const { return mask & m_summonMask; } + bool isSummon() const { return m_summonMask & SUMMON_MASK_SUMMON; } + bool isPet() const { return m_summonMask & SUMMON_MASK_PET; } + bool isVehicle() const { return m_summonMask & SUMMON_MASK_VEHICLE; } + bool isWorldCreature() const { return m_summonMask & SUMMON_MASK_PET; } + bool isTotem() const { return m_summonMask & SUMMON_MASK_TOTEM; } void SetCorpseDelay(uint32 delay) { m_corpseDelay = delay; } - bool isTotem() const { return m_isTotem; } bool isRacialLeader() const { return GetCreatureInfo()->RacialLeader; } bool isCivilian() const { return GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_CIVILIAN; } bool isTrigger() const { return GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_TRIGGER; } @@ -439,9 +492,9 @@ class TRINITY_DLL_SPEC Creature : public Unit bool isCanInteractWithBattleMaster(Player* player, bool msg) const; bool isCanTrainingAndResetTalentsOf(Player* pPlayer) const; bool IsOutOfThreatArea(Unit* pVictim) const; - bool IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges = false); + bool IsImmunedToSpell(SpellEntry const* spellInfo); // redefine Unit::IsImmunedToSpell - bool IsImmunedToSpellEffect(uint32 effect, uint32 mechanic) const; + bool IsImmunedToSpellEffect(SpellEntry const* spellInfo, uint32 index) const; // redefine Unit::IsImmunedToSpellEffect bool isElite() const { @@ -465,6 +518,7 @@ class TRINITY_DLL_SPEC Creature : public Unit bool IsInEvadeMode() const; bool AIM_Initialize(CreatureAI* ai = NULL); + void Motion_Initialize(); void AI_SendMoveToPacket(float x, float y, float z, uint32 time, uint32 MovementFlags, uint8 type); CreatureAI* AI() { return (CreatureAI*)i_AI; } @@ -506,8 +560,9 @@ class TRINITY_DLL_SPEC Creature : public Unit CreatureInfo const *GetCreatureInfo() const { return m_creatureInfo; } CreatureDataAddon const* GetCreatureAddon() const; - std::string GetScriptName(); - uint32 GetScriptId(); + std::string GetAIName() const; + std::string GetScriptName() const; + uint32 GetScriptId() const; void prepareGossipMenu( Player *pPlayer, uint32 gossipid = 0 ); void sendPreparedGossip( Player* player ); @@ -521,15 +576,11 @@ class TRINITY_DLL_SPEC Creature : public Unit GossipOption const* GetGossipOption( uint32 id ) const; void addGossipOption(GossipOption const& gso) { m_goptions.push_back(gso); } - void setEmoteState(uint8 emote) { m_emoteState = emote; }; - void Say(const char* text, uint32 language, uint64 TargetGuid) { MonsterSay(text,language,TargetGuid); } - void Yell(const char* text, uint32 language, uint64 TargetGuid) { MonsterYell(text,language,TargetGuid); } - void TextEmote(const char* text, uint64 TargetGuid, bool IsBossEmote = false) { MonsterTextEmote(text,TargetGuid,IsBossEmote); } - void Whisper(const char* text, uint64 receiver, bool IsBossWhisper = false) { MonsterWhisper(text,receiver,IsBossWhisper); } void Say(int32 textId, uint32 language, uint64 TargetGuid) { MonsterSay(textId,language,TargetGuid); } void Yell(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYell(textId,language,TargetGuid); } void TextEmote(int32 textId, uint64 TargetGuid, bool IsBossEmote = false) { MonsterTextEmote(textId,TargetGuid,IsBossEmote); } void Whisper(int32 textId, uint64 receiver, bool IsBossWhisper = false) { MonsterWhisper(textId,receiver,IsBossWhisper); } + void YellToZone(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYellToZone(textId,language,TargetGuid); } // overwrite WorldObject function for proper name localization const char* GetNameForLocaleIdx(int32 locale_idx) const; @@ -540,7 +591,7 @@ class TRINITY_DLL_SPEC Creature : public Unit bool LoadFromDB(uint32 guid, Map *map); void SaveToDB(); // overwrited in Pet - virtual void SaveToDB(uint32 mapid, uint8 spawnMask); + virtual void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask); virtual void DeleteFromDB(); // overwrited in Pet Loot loot; @@ -625,10 +676,11 @@ class TRINITY_DLL_SPEC Creature : public Unit void UpdateWaypointID(uint32 wpID){m_waypointID = wpID;} void SearchFormation(); - bool IsFormationLeader() {return (GetDBTableGUIDLow() && GetDBTableGUIDLow() == m_formationID);} - uint32 GetFormationID(){return m_formationID;} + CreatureGroup *GetFormation() {return m_formation;} + void SetFormation(CreatureGroup *formation) {m_formation = formation;} Unit *SelectVictim(); + void SetDeadByDefault (bool death_state) {m_isDeadByDefault = death_state;} void SetDisableReputationGain(bool disable) { DisableReputationGain = disable; } bool IsReputationGainDisabled() { return DisableReputationGain; } @@ -657,13 +709,10 @@ class TRINITY_DLL_SPEC Creature : public Unit bool m_gossipOptionLoaded; GossipOptionList m_goptions; - uint8 m_emoteState; - bool m_isPet; // set only in Pet::Pet - bool m_isTotem; // set only in Totem::Totem + uint32 m_summonMask; ReactStates m_reactState; // for AI, not charmInfo void RegenerateMana(); void RegenerateHealth(); - uint32 m_regenTimer; MovementGeneratorType m_defaultMovementType; Cell m_currentCell; // store current cell where creature listed uint32 m_DBTableGuid; ///< For new or temporary creatures is 0 for saved it is lowguid @@ -690,7 +739,7 @@ class TRINITY_DLL_SPEC Creature : public Unit uint32 m_path_id; //Formation var - uint32 m_formationID; + CreatureGroup *m_formation; GridReference<Creature> m_gridRef; CreatureInfo const* m_creatureInfo; // in heroic mode can different from ObjMgr::GetCreatureTemplate(GetEntry()) diff --git a/src/game/CreatureAI.cpp b/src/game/CreatureAI.cpp index d21fe43fcaa..59df2e866f2 100644 --- a/src/game/CreatureAI.cpp +++ b/src/game/CreatureAI.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -71,6 +71,38 @@ void CreatureAI::OnCharmed(bool apply) me->IsAIEnabled = false; } +void CreatureAI::DoZoneInCombat(Unit* pUnit) +{ + if (!pUnit) + pUnit = me; + + Map *map = pUnit->GetMap(); + + if (!map->IsDungeon()) //use IsDungeon instead of Instanceable, in case battlegrounds will be instantiated + { + sLog.outError("DoZoneInCombat call for map that isn't an instance (pUnit entry = %d)", pUnit->GetTypeId() == TYPEID_UNIT ? ((Creature*)pUnit)->GetEntry() : 0); + return; + } + + if (!pUnit->CanHaveThreatList() || pUnit->getThreatManager().isThreatListEmpty()) + { + sLog.outError("DoZoneInCombat called for creature that either cannot have threat list or has empty threat list (pUnit entry = %d)", pUnit->GetTypeId() == TYPEID_UNIT ? ((Creature*)pUnit)->GetEntry() : 0); + return; + } + + Map::PlayerList const &PlayerList = map->GetPlayers(); + for(Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) + { + if (Player* i_pl = i->getSource()) + if (i_pl->isAlive()) + { + pUnit->SetInCombatWith(i_pl); + i_pl->SetInCombatWith(pUnit); + pUnit->AddThreat(i_pl, 0.0f); + } + } +} + void CreatureAI::MoveInLineOfSight(Unit *who) { if(me->getVictim()) @@ -112,9 +144,9 @@ void SimpleCharmedAI::UpdateAI(const uint32 /*diff*/) //kill self if charm aura has infinite duration if(charmer->IsInEvadeMode()) { - Unit::AuraList const& auras = me->GetAurasByType(SPELL_AURA_MOD_CHARM); - for(Unit::AuraList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter) - if((*iter)->GetCasterGUID() == charmer->GetGUID() && (*iter)->IsPermanent()) + Unit::AuraEffectList const& auras = me->GetAurasByType(SPELL_AURA_MOD_CHARM); + for(Unit::AuraEffectList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter) + if((*iter)->GetCasterGUID() == charmer->GetGUID() && (*iter)->GetParentAura()->IsPermanent()) { charmer->Kill(me); return; @@ -129,3 +161,8 @@ void SimpleCharmedAI::UpdateAI(const uint32 /*diff*/) AttackStart(charmer->SelectNearestTarget()); } +/*void CreatureAI::AttackedBy( Unit* attacker ) +{ + if(!m_creature->getVictim()) + AttackStart(attacker); +}*/ diff --git a/src/game/CreatureAI.h b/src/game/CreatureAI.h index b7a584fd36e..c968f07532a 100644 --- a/src/game/CreatureAI.h +++ b/src/game/CreatureAI.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -73,9 +73,9 @@ enum SelectAggroTarget class TRINITY_DLL_SPEC UnitAI { protected: - Unit *me; + Unit* const me; public: - UnitAI(Unit *u) : me(u) {} + explicit UnitAI(Unit *u) : me(u) {} virtual void AttackStart(Unit *); virtual void UpdateAI(const uint32 diff) = 0; @@ -96,9 +96,9 @@ class TRINITY_DLL_SPEC UnitAI class TRINITY_DLL_SPEC PlayerAI : public UnitAI { protected: - Player *me; + Player* const me; public: - PlayerAI(Player *p) : UnitAI((Unit*)p), me(p) {} + explicit PlayerAI(Player *p) : UnitAI((Unit*)p), me(p) {} void OnCharmed(bool apply); }; @@ -112,21 +112,26 @@ class TRINITY_DLL_SPEC SimpleCharmedAI : public PlayerAI class TRINITY_DLL_SPEC CreatureAI : public UnitAI { protected: - Creature *me; + Creature* const me; + Creature* const m_creature; bool UpdateVictim(); public: - CreatureAI(Creature *c) : UnitAI((Unit*)c), me(c) {} + explicit CreatureAI(Creature *c) : UnitAI((Unit*)c), me(c), m_creature(c) {} virtual ~CreatureAI() {} - // Called if IsVisible(Unit *who) is true at each *who move + ///== Reactions At ================================= + + // Called if IsVisible(Unit *who) is true at each *who move, reaction at visibility zone enter virtual void MoveInLineOfSight(Unit *); - // Called at stopping attack by any attacker + // Called for reaction at stopping attack at no attackers or targets virtual void EnterEvadeMode(); // Called at any Damage from any attacker (before damage apply) + // Note: it for recalculation damage or special reaction at damage + // for attack reaction use AttackedBy called for not DOT damage in Unit::DealDamage also virtual void DamageTaken(Unit *done_by, uint32 & /*damage*/) {} // Called when the creature is killed @@ -146,8 +151,8 @@ class TRINITY_DLL_SPEC CreatureAI : public UnitAI // Called when spell hits a target virtual void SpellHitTarget(Unit* target, const SpellEntry*) {} - // Called when vitim entered water and creature can not enter water - virtual bool canReachByRangeAttack(Unit*) { return false; } + // Called when the creature is target of hostile action: swing, hostile spell landed, fear/etc) + //virtual void AttackedBy(Unit* attacker); // Called when creature is spawned or respawned (for reseting variables) virtual void JustRespawned() {} @@ -156,6 +161,33 @@ class TRINITY_DLL_SPEC CreatureAI : public UnitAI virtual void MovementInform(uint32 /*MovementType*/, uint32 /*Data*/) {} void OnCharmed(bool apply); + + // Called at reaching home after evade + virtual void JustReachedHome() {} + + void DoZoneInCombat(Unit* pUnit = NULL); + + ///== Triggered Actions Requested ================== + + // Called when creature attack expected (if creature can and no have current victim) + // Note: for reaction at hostile action must be called AttackedBy function. + //virtual void AttackStart(Unit *) {} + + // Called at World update tick + //virtual void UpdateAI(const uint32 diff ) {} + + ///== State checks ================================= + + // Is unit visible for MoveInLineOfSight + //virtual bool IsVisible(Unit *) const { return false; } + + // Called when victim entered water and creature can not enter water + virtual bool canReachByRangeAttack(Unit*) { return false; } + + ///== Fields ======================================= + + // Pointer to controlled by AI creature + //Creature* const m_creature; }; struct SelectableAI : public FactoryHolder<CreatureAI>, public Permissible<Creature> diff --git a/src/game/CreatureAIImpl.h b/src/game/CreatureAIImpl.h index 2f82522f426..a8b8271c5ff 100644 --- a/src/game/CreatureAIImpl.h +++ b/src/game/CreatureAIImpl.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/CreatureAIRegistry.cpp b/src/game/CreatureAIRegistry.cpp index 2ac73da702b..6253c06b8bc 100644 --- a/src/game/CreatureAIRegistry.cpp +++ b/src/game/CreatureAIRegistry.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,7 +18,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "CreatureAIRegistry.h" #include "NullCreatureAI.h" #include "ReactorAI.h" #include "AggressorAI.h" @@ -27,10 +26,10 @@ #include "PossessedAI.h" #include "TotemAI.h" #include "OutdoorPvPObjectiveAI.h" +#include "CreatureEventAI.h" #include "RandomMovementGenerator.h" #include "CreatureAIImpl.h" #include "MovementGeneratorImpl.h" -#include "MapManager.h" #include "CreatureAIRegistry.h" #include "WaypointMovementGenerator.h" @@ -48,6 +47,7 @@ namespace AIRegistry (new CreatureAIFactory<TotemAI>("TotemAI"))->RegisterSelf(); (new CreatureAIFactory<OutdoorPvPObjectiveAI>("OutdoorPvPObjectiveAI"))->RegisterSelf(); (new CreatureAIFactory<PossessedAI>("PossessedAI"))->RegisterSelf(); + (new CreatureAIFactory<CreatureEventAI>("EventAI"))->RegisterSelf(); (new MovementGeneratorFactory<RandomMovementGenerator<Creature> >(RANDOM_MOTION_TYPE))->RegisterSelf(); (new MovementGeneratorFactory<WaypointMovementGenerator<Creature> >(WAYPOINT_MOTION_TYPE))->RegisterSelf(); diff --git a/src/game/CreatureAIRegistry.h b/src/game/CreatureAIRegistry.h index b4155403e9b..2b92a096731 100644 --- a/src/game/CreatureAIRegistry.h +++ b/src/game/CreatureAIRegistry.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/CreatureAISelector.cpp b/src/game/CreatureAISelector.cpp index a2f3d39fcc7..9250a254155 100644 --- a/src/game/CreatureAISelector.cpp +++ b/src/game/CreatureAISelector.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,6 +26,7 @@ #include "MovementGenerator.h" #include "ScriptCalls.h" #include "Pet.h" +#include "TemporarySummon.h" INSTANTIATE_SINGLETON_1(CreatureAIRegistry); INSTANTIATE_SINGLETON_1(MovementGeneratorRegistry); @@ -34,24 +35,21 @@ namespace FactorySelector { CreatureAI* selectAI(Creature *creature) { - //if(creature->isPossessed()) - // creature->InitPossessedAI(); - - // Allow scripting AI for normal creatures and not controlled pets (guardians and mini-pets) - if((!creature->isPet() || !((Pet*)creature)->isControlled()) && !creature->isCharmed()) - if(CreatureAI* scriptedAI = Script->GetAI(creature)) - return scriptedAI; - + const CreatureAICreator *ai_factory = NULL; CreatureAIRegistry &ai_registry(CreatureAIRepository::Instance()); - assert( creature->GetCreatureInfo() != NULL ); - CreatureInfo const *cinfo=creature->GetCreatureInfo(); - const CreatureAICreator *ai_factory = NULL; + //player-controlled guardians with pet bar + if(creature->HasSummonMask(SUMMON_MASK_GUARDIAN) && ((Guardian*)creature)->GetOwner()->GetTypeId() == TYPEID_PLAYER) + ai_factory = ai_registry.GetRegistryItem("PetAI"); - std::string ainame=cinfo->AIName; + //scriptname in db + if(!ai_factory) + if(CreatureAI* scriptedAI = Script->GetAI(creature)) + return scriptedAI; - // select by script name - if( !ainame.empty()) + // AIname in db + std::string ainame=creature->GetAIName(); + if(!ai_factory && !ainame.empty()) ai_factory = ai_registry.GetRegistryItem( ainame.c_str() ); // select by NPC flags @@ -59,7 +57,7 @@ namespace FactorySelector { if( creature->isGuard() ) ai_factory = ai_registry.GetRegistryItem("GuardAI"); - else if(creature->isPet() || (creature->isCharmed() && !creature->isPossessed())) + else if(creature->HasSummonMask(SUMMON_MASK_GUARDIAN)) ai_factory = ai_registry.GetRegistryItem("PetAI"); else if(creature->isTotem()) ai_factory = ai_registry.GetRegistryItem("TotemAI"); diff --git a/src/game/CreatureAISelector.h b/src/game/CreatureAISelector.h index c7b4e541f3c..436233b5b21 100644 --- a/src/game/CreatureAISelector.h +++ b/src/game/CreatureAISelector.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/CreatureEventAI.cpp b/src/game/CreatureEventAI.cpp new file mode 100644 index 00000000000..3c45fbdf9b3 --- /dev/null +++ b/src/game/CreatureEventAI.cpp @@ -0,0 +1,1661 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "CreatureEventAI.h" +#include "CreatureEventAIMgr.h" +#include "ObjectMgr.h" +#include "Spell.h" +#include "World.h" +#include "Cell.h" +#include "CellImpl.h" +#include "GameEventMgr.h" +#include "GridNotifiers.h" +#include "GridNotifiersImpl.h" +#include "WorldPacket.h" +#include "InstanceData.h" + +int CreatureEventAI::Permissible(const Creature *creature) +{ + if( creature->GetAIName() == "EventAI" ) + return PERMIT_BASE_SPECIAL; + return PERMIT_BASE_NO; +} + +CreatureEventAI::CreatureEventAI(Creature *c) : CreatureAI(c), InCombat(false) +{ + CreatureEventAI_Event_Map::iterator CreatureEvents = CreatureEAI_Mgr.GetCreatureEventAIMap().find(m_creature->GetEntry()); + if (CreatureEvents != CreatureEAI_Mgr.GetCreatureEventAIMap().end()) + { + std::vector<CreatureEventAI_Event>::iterator i; + for (i = (*CreatureEvents).second.begin(); i != (*CreatureEvents).second.end(); ++i) + { + + //Debug check + #ifndef _DEBUG + if ((*i).event_flags & EFLAG_DEBUG_ONLY) + continue; + #endif + if(((*i).event_flags & (EFLAG_HEROIC | EFLAG_NORMAL)) && m_creature->GetMap()->IsDungeon() ) + { + if( (m_creature->GetMap()->IsHeroic() && (*i).event_flags & EFLAG_HEROIC) || + (!m_creature->GetMap()->IsHeroic() && (*i).event_flags & EFLAG_NORMAL)) + { + //event flagged for instance mode + CreatureEventAIList.push_back(CreatureEventAIHolder(*i)); + } + continue; + } + CreatureEventAIList.push_back(CreatureEventAIHolder(*i)); + } + //EventMap had events but they were not added because they must be for instance + if (CreatureEventAIList.empty()) + sLog.outError("CreatureEventAI: CreatureId has events but no events added to list because of instance flags.", m_creature->GetEntry()); + } + else + sLog.outError("CreatureEventAI: EventMap for Creature %u is empty but creature is using CreatureEventAI.", m_creature->GetEntry()); + + bEmptyList = CreatureEventAIList.empty(); + Phase = 0; + CombatMovementEnabled = true; + MeleeEnabled = true; + AttackDistance = 0; + AttackAngle = 0.0f; + + //Handle Spawned Events + if (!bEmptyList) + { + for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) + { + if ((*i).Event.event_type == EVENT_T_SPAWNED) + ProcessEvent(*i); + } + } +} + +bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pActionInvoker) +{ + if (!pHolder.Enabled || pHolder.Time) + return false; + + //Check the inverse phase mask (event doesn't trigger if current phase bit is set in mask) + 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; + + union + { + uint32 param1; + int32 param1_s; + }; + + union + { + uint32 param2; + int32 param2_s; + }; + + union + { + uint32 param3; + int32 param3_s; + }; + + union + { + uint32 param4; + int32 param4_s; + }; + + param1 = pHolder.Event.event_param1; + param2 = pHolder.Event.event_param2; + param3 = pHolder.Event.event_param3; + param4 = pHolder.Event.event_param4; + + //Check event conditions based on the event type, also reset events + switch (pHolder.Event.event_type) + { + case EVENT_T_TIMER: + { + if (!InCombat) + return false; + + //Repeat Timers + if (param3 == param4) + { + pHolder.Time = param3; + + }else if (param4 > param3) + pHolder.Time = urand(param3, param4); + else + { + sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + case EVENT_T_TIMER_OOC: + { + if (InCombat) + return false; + + //Repeat Timers + if (param3 == param4) + { + pHolder.Time = param3; + + }else if (param4 > param3) + pHolder.Time = urand(param3, param4); + else + { + + sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + case EVENT_T_HP: + { + if (!InCombat || !m_creature->GetMaxHealth()) + return false; + + uint32 perc = (m_creature->GetHealth()*100) / m_creature->GetMaxHealth(); + + if (perc > param1 || perc < param2) + return false; + + //Repeat Timers + if (param3 == param4) + { + pHolder.Time = param3; + + }else if (param4 > param3) + pHolder.Time = urand(param3, param4); + else + { + + sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + case EVENT_T_MANA: + { + if (!InCombat || !m_creature->GetMaxPower(POWER_MANA)) + return false; + + uint32 perc = (m_creature->GetPower(POWER_MANA)*100) / m_creature->GetMaxPower(POWER_MANA); + + if (perc > param1 || perc < param2) + return false; + + //Repeat Timers + if (param3 == param4) + { + pHolder.Time = param3; + + }else if (param4 > param3) + pHolder.Time = urand(param3, param4); + else + { + + sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + case EVENT_T_AGGRO: + { + } + break; + case EVENT_T_KILL: + { + //Repeat Timers + if (param1 == param2) + { + pHolder.Time = param1; + + }else if (param2 > param1) + pHolder.Time = urand(param1, param2); + else + { + + sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + case EVENT_T_DEATH: + { + } + break; + case EVENT_T_EVADE: + { + } + break; + case EVENT_T_SPELLHIT: + { + //Spell hit is special case, param1 and param2 handled within CreatureEventAI::SpellHit + + //Repeat Timers + if (param3 == param4) + { + pHolder.Time = param3; + + }else if (param4 > param3) + pHolder.Time = urand(param3, param4); + else + { + + sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + case EVENT_T_RANGE: + { + //Repeat Timers + if (param3 == param4) + { + pHolder.Time = param3; + + }else if (param4 > param3) + pHolder.Time = urand(param3, param4); + else + { + + sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + case EVENT_T_OOC_LOS: + { + //Repeat Timers + if (param3 == param4) + { + pHolder.Time = param3; + + }else if (param4 > param3) + pHolder.Time = urand(param3, param4); + else + { + + sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + case EVENT_T_SPAWNED: + { + } + break; + case EVENT_T_TARGET_HP: + { + if (!InCombat || !m_creature->getVictim() || !m_creature->getVictim()->GetMaxHealth()) + return false; + + uint32 perc = (m_creature->getVictim()->GetHealth()*100) / m_creature->getVictim()->GetMaxHealth(); + + if (perc > param1 || perc < param2) + return false; + + //Repeat Timers + if (param3 == param4) + { + pHolder.Time = param3; + + }else if (param4 > param3) + pHolder.Time = urand(param3, param4); + else + { + + sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + case EVENT_T_TARGET_CASTING: + { + if (!InCombat || !m_creature->getVictim() || !m_creature->getVictim()->IsNonMeleeSpellCasted(false, false, true)) + return false; + + //Repeat Timers + if (param1 == param2) + { + pHolder.Time = param1; + + }else if (param2 > param1) + pHolder.Time = urand(param1, param2); + else + { + + sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + case EVENT_T_FRIENDLY_HP: + { + if (!InCombat) + return false; + + Unit* pUnit = DoSelectLowestHpFriendly(param2, param1); + + if (!pUnit) + return false; + + pActionInvoker = pUnit; + + //Repeat Timers + if (param3 == param4) + { + pHolder.Time = param3; + + }else if (param4 > param3) + pHolder.Time = urand(param3, param4); + else + { + + sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + case EVENT_T_FRIENDLY_IS_CC: + { + if (!InCombat) + return false; + + std::list<Creature*> pList; + DoFindFriendlyCC(pList, param2); + + //List is empty + if (pList.empty()) + return false; + + //We don't really care about the whole list, just return first available + pActionInvoker = *(pList.begin()); + + //Repeat Timers + if (param3 == param4) + { + pHolder.Time = param3; + + }else if (param4 > param3) + pHolder.Time = urand(param3, param4); + else + { + sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + case EVENT_T_FRIENDLY_MISSING_BUFF: + { + std::list<Creature*> pList; + DoFindFriendlyMissingBuff(pList, param2, param1); + + //List is empty + if (pList.empty()) + return false; + + //We don't really care about the whole list, just return first available + pActionInvoker = *(pList.begin()); + + //Repeat Timers + if (param3 == param4) + { + pHolder.Time = param3; + + }else if (param4 > param3) + pHolder.Time = urand(param3, param4); + else + { + + sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + case EVENT_T_SUMMONED_UNIT: + { + //Prevent event from occuring on no unit or non creatures + if (!pActionInvoker || pActionInvoker->GetTypeId()!=TYPEID_UNIT) + return false; + + //Creature id doesn't match up + if (param1 && ((Creature*)pActionInvoker)->GetEntry() != param1) + return false; + + //Repeat Timers + if (param2 == param3) + { + pHolder.Time = param2; + + }else if (param3 > param2) + pHolder.Time = urand(param2, param3); + else + { + + sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + case EVENT_T_REACHED_HOME: + { + } + break; + case EVENT_T_RECEIVE_EMOTE: + { + } + break; + default: + + sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u has invalid Event Type(%u), missing from ProcessEvent() Switch.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + break; + } + + //Disable non-repeatable events + if (!(pHolder.Event.event_flags & EFLAG_REPEATABLE)) + pHolder.Enabled = false; + + //Process actions + for (uint32 j = 0; j < MAX_ACTIONS; j++) + ProcessAction(pHolder.Event.action[j].type, pHolder.Event.action[j].param1, pHolder.Event.action[j].param2, pHolder.Event.action[j].param3, rnd, pHolder.Event.event_id, pActionInvoker); + + return true; +} + +void CreatureEventAI::ProcessAction(uint16 type, uint32 param1, uint32 param2, uint32 param3, uint32 rnd, uint32 EventId, Unit* pActionInvoker) +{ + switch (type) + { + case ACTION_T_TEXT: + { + if (!param1) + return; + + uint32 temp = 0; + + if (param2 && param3) + { + switch( rand()%3 ) + { + case 0: temp = param1; break; + case 2: temp = param2; break; + case 3: temp = param3; break; + } + }else if ( param2 && urand(0,1) ) + { + temp = param2; + }else + { + temp = param1; + } + + if (temp) + { + Unit* target = NULL; + Unit* owner = NULL; + + if (pActionInvoker) + { + if (pActionInvoker->GetTypeId() == TYPEID_PLAYER) + target = pActionInvoker; + else if (owner = pActionInvoker->GetOwner()) + { + if (owner->GetTypeId() == TYPEID_PLAYER) + target = owner; + } + } + else if (target = m_creature->getVictim()) + { + if (target->GetTypeId() != TYPEID_PLAYER) + { + if (owner = target->GetOwner()) + { + if (owner->GetTypeId() == TYPEID_PLAYER) + target = owner; + } + } + } + + DoScriptText(temp, m_creature, target); + } + } + break; + case ACTION_T_SET_FACTION: + { + if (param1) + m_creature->setFaction(param1); + else + { + if (CreatureInfo const* ci = GetCreatureTemplateStore(m_creature->GetEntry())) + { + //if no id provided, assume reset and then use default + if (m_creature->getFaction() != ci->faction_A) + m_creature->setFaction(ci->faction_A); + } + } + } + break; + case ACTION_T_MORPH_TO_ENTRY_OR_MODEL: + { + if (param1 || param2) + { + //set model based on entry from creature_template + if (param1) + { + if (CreatureInfo const* ci = GetCreatureTemplateStore(param1)) + { + //use default display + if (ci->Modelid1) + m_creature->SetDisplayId(ci->Modelid1); + } + } + //if no param1, then use value from param2 (modelId) + else + m_creature->SetDisplayId(param2); + } + else + m_creature->DeMorph(); + } + break; + case ACTION_T_SOUND: + m_creature->PlayDirectSound(param1); + break; + case ACTION_T_EMOTE: + m_creature->HandleEmoteCommand(param1); + break; + case ACTION_T_RANDOM_SOUND: + { + uint32 temp = GetRandActionParam(rnd, param1, param2, param3); + + if (temp != uint32(0xffffffff)) + m_creature->PlayDirectSound( temp ); + } + break; + case ACTION_T_RANDOM_EMOTE: + { + uint32 temp = GetRandActionParam(rnd, param1, param2, param3); + + if (temp != uint32(0xffffffff)) + m_creature->HandleEmoteCommand(temp); + } + break; + case ACTION_T_CAST: + { + Unit* target = GetTargetByType(param2, pActionInvoker); + Unit* caster = m_creature; + + if (!target) + return; + + //Cast is always triggered if target is forced to cast on self + if (param3 & CAST_FORCE_TARGET_SELF) + { + param3 |= CAST_TRIGGERED; + caster = target; + } + + //Allowed to cast only if not casting (unless we interrupt ourself) or if spell is triggered + bool canCast = !(caster->IsNonMeleeSpellCasted(false) && (param3 & CAST_TRIGGERED | CAST_INTURRUPT_PREVIOUS)); + + // If cast flag CAST_AURA_NOT_PRESENT is active, check if target already has aura on them + if(param3 & CAST_AURA_NOT_PRESENT) + { + if(target->HasAura(param1)) + return; + } + + if (canCast) + { + const SpellEntry* tSpell = GetSpellStore()->LookupEntry(param1); + + //Verify that spell exists + if (tSpell) + { + //Check if cannot cast spell + if (!(param3 & (CAST_FORCE_TARGET_SELF | CAST_FORCE_CAST)) && + !CanCast(target, tSpell, (param3 & CAST_TRIGGERED))) + { + //Melee current victim if flag not set + if (!(param3 & CAST_NO_MELEE_IF_OOM)) + { + if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE) + { + AttackDistance = 0; + AttackAngle = 0; + + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim(), AttackDistance, AttackAngle); + } + } + + } + else + { + //Interrupt any previous spell + if (caster->IsNonMeleeSpellCasted(false) && param3 & CAST_INTURRUPT_PREVIOUS) + caster->InterruptNonMeleeSpells(false); + + caster->CastSpell(target, param1, (param3 & CAST_TRIGGERED)); + } + + }else + sLog.outErrorDb("CreatureEventAI: event %d creature %d attempt to cast spell that doesn't exist %d", EventId, m_creature->GetEntry(), param1); + } + } + break; + case ACTION_T_SUMMON: + { + Unit* target = GetTargetByType(param2, pActionInvoker); + + Creature* pCreature = NULL; + + if (param3) + pCreature = m_creature->SummonCreature(param1, 0, 0, 0, 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, param3); + else + pCreature = m_creature->SummonCreature(param1, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 0); + + if (!pCreature) + { + + sLog.outErrorDb( "CreatureEventAI: failed to spawn creature %u. Spawn event %d is on creature %d", param1, EventId, m_creature->GetEntry()); + } + else if (param2 != TARGET_T_SELF && target) + pCreature->AI()->AttackStart(target); + } + break; + case ACTION_T_THREAT_SINGLE_PCT: + { + Unit* target = GetTargetByType(param2, pActionInvoker); + + if (target) + m_creature->getThreatManager().modifyThreatPercent(target, param1); + } + break; + case ACTION_T_THREAT_ALL_PCT: + { + Unit* Temp = NULL; + + std::list<HostilReference*>::iterator i = m_creature->getThreatManager().getThreatList().begin(); + for (; i != m_creature->getThreatManager().getThreatList().end(); ++i) + { + Temp = Unit::GetUnit(*m_creature,(*i)->getUnitGuid()); + if (Temp) + m_creature->getThreatManager().modifyThreatPercent(Temp, param1); + } + } + break; + case ACTION_T_QUEST_EVENT: + { + Unit* target = GetTargetByType(param2, pActionInvoker); + + if (target && target->GetTypeId() == TYPEID_PLAYER) + ((Player*)target)->AreaExploredOrEventHappens(param1); + } + break; + case ACTION_T_CASTCREATUREGO: + { + Unit* target = GetTargetByType(param3, pActionInvoker); + + if (target && target->GetTypeId() == TYPEID_PLAYER) + ((Player*)target)->CastedCreatureOrGO(param1, m_creature->GetGUID(), param2); + } + break; + case ACTION_T_SET_UNIT_FIELD: + { + Unit* target = GetTargetByType(param3, pActionInvoker); + + if (param1 < OBJECT_END || param1 >= UNIT_END) + return; + + if (target) + target->SetUInt32Value(param1, param2); + } + break; + case ACTION_T_SET_UNIT_FLAG: + { + Unit* target = GetTargetByType(param2, pActionInvoker); + + if (target) + target->SetFlag(UNIT_FIELD_FLAGS, param1); + } + break; + case ACTION_T_REMOVE_UNIT_FLAG: + { + Unit* target = GetTargetByType(param2, pActionInvoker); + + if (target) + target->RemoveFlag(UNIT_FIELD_FLAGS, param1); + } + break; + case ACTION_T_AUTO_ATTACK: + { + if (param1) + MeleeEnabled = true; + else MeleeEnabled = false; + } + break; + case ACTION_T_COMBAT_MOVEMENT: + { + CombatMovementEnabled = param1; + + //Allow movement (create new targeted movement gen only if idle) + if (CombatMovementEnabled) + { + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim(), AttackDistance, AttackAngle); + } + else + { + m_creature->GetMotionMaster()->MoveIdle(); + } + } + break; + case ACTION_T_SET_PHASE: + { + Phase = param1; + } + break; + case ACTION_T_INC_PHASE: + { + Phase += param1; + + if (Phase > 31) + + sLog.outErrorDb( "CreatureEventAI: Event %d incremented Phase above 31. Phase mask cannot be used with phases past 31. CreatureEntry = %d", EventId, m_creature->GetEntry()); + } + break; + case ACTION_T_EVADE: + { + EnterEvadeMode(); + } + break; + case ACTION_T_FLEE: + { + if(m_creature->HasAuraType(SPELL_AURA_PREVENTS_FLEEING)) + break; + TimetoFleeLeft = 8000; + m_creature->DoFleeToGetAssistance(); + IsFleeing = true; + } + break; + case ACTION_T_QUEST_EVENT_ALL: + { + Unit* Temp = NULL; + if( pActionInvoker && pActionInvoker->GetTypeId() == TYPEID_PLAYER ) + { + Temp = Unit::GetUnit(*m_creature,pActionInvoker->GetGUID()); + if( Temp ) + ((Player*)Temp)->GroupEventHappens(param1,m_creature); + } + } + break; + case ACTION_T_CASTCREATUREGO_ALL: + { + Unit* Temp = NULL; + + std::list<HostilReference*>::iterator i = m_creature->getThreatManager().getThreatList().begin(); + for (; i != m_creature->getThreatManager().getThreatList().end(); ++i) + { + Temp = Unit::GetUnit(*m_creature,(*i)->getUnitGuid()); + if (Temp && Temp->GetTypeId() == TYPEID_PLAYER) + ((Player*)Temp)->CastedCreatureOrGO(param1, m_creature->GetGUID(), param2); + } + } + break; + case ACTION_T_REMOVEAURASFROMSPELL: + { + Unit* target = GetTargetByType(param1, pActionInvoker); + + if (target) + target->RemoveAurasDueToSpell(param2); + } + break; + case ACTION_T_RANGED_MOVEMENT: + { + AttackDistance = param1; + AttackAngle = ((float)param2/180)*M_PI; + + if (CombatMovementEnabled) + { + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim(), AttackDistance, AttackAngle); + } + } + break; + case ACTION_T_RANDOM_PHASE: + { + uint32 temp = GetRandActionParam(rnd, param1, param2, param3); + + Phase = temp; + } + break; + case ACTION_T_RANDOM_PHASE_RANGE: + { + if (param2 > param1) + { + Phase = param1 + (rnd % (param2 - param1)); + } + else + sLog.outErrorDb( "CreatureEventAI: ACTION_T_RANDOM_PHASE_RANGE cannot have Param2 <= Param1. Divide by Zero. Event = %d. CreatureEntry = %d", EventId, m_creature->GetEntry()); + } + break; + case ACTION_T_SUMMON_ID: + { + Unit* target = GetTargetByType(param2, pActionInvoker); + + //Duration + Creature* pCreature = NULL; + + CreatureEventAI_Summon_Map::const_iterator i = CreatureEAI_Mgr.GetCreatureEventAISummonMap().find(param3); + if (i == CreatureEAI_Mgr.GetCreatureEventAISummonMap().end()) + { + + sLog.outErrorDb( "CreatureEventAI: failed to spawn creature %u. Summon map index %u does not exist. EventID %d. CreatureID %d", param1, param3, EventId, m_creature->GetEntry()); + return; + } + + if ((*i).second.SpawnTimeSecs) + pCreature = m_creature->SummonCreature(param1, (*i).second.position_x, (*i).second.position_y, (*i).second.position_z, (*i).second.orientation, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, (*i).second.SpawnTimeSecs); + else pCreature = m_creature->SummonCreature(param1, (*i).second.position_x, (*i).second.position_y, (*i).second.position_z, (*i).second.orientation, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 0); + + if (!pCreature) + { + + sLog.outErrorDb( "CreatureEventAI: failed to spawn creature %u. EventId %d.Creature %d", param1, EventId, m_creature->GetEntry()); + } + else if (param2 != TARGET_T_SELF && target) + pCreature->AI()->AttackStart(target); + } + break; + case ACTION_T_KILLED_MONSTER: + { + //first attempt player who tapped creature + if (Player* pPlayer = m_creature->GetLootRecipient()) + pPlayer->RewardPlayerAndGroupAtEvent(param1, m_creature); + else + { + //if not available, use pActionInvoker + Unit* pTarget = GetTargetByType(param2, pActionInvoker); + + if (Player* pPlayer = pTarget->GetCharmerOrOwnerPlayerOrPlayerItself()) + pPlayer->RewardPlayerAndGroupAtEvent(param1, m_creature); + } + } + break; + case ACTION_T_SET_INST_DATA: + { + InstanceData* pInst = (InstanceData*)m_creature->GetInstanceData(); + if (!pInst) + { + sLog.outErrorDb("CreatureEventAI: Event %d attempt to set instance data without instance script. Creature %d", EventId, m_creature->GetEntry()); + return; + } + + pInst->SetData(param1, param2); + } + break; + case ACTION_T_SET_INST_DATA64: + { + Unit* target = GetTargetByType(param2, pActionInvoker); + if (!target) + { + sLog.outErrorDb("CreatureEventAI: Event %d attempt to set instance data64 but Target == NULL. Creature %d", EventId, m_creature->GetEntry()); + return; + } + + InstanceData* pInst = (InstanceData*)m_creature->GetInstanceData(); + if (!pInst) + { + sLog.outErrorDb("CreatureEventAI: Event %d attempt to set instance data64 without instance script. Creature %d", EventId, m_creature->GetEntry()); + return; + } + + pInst->SetData64(param1, target->GetGUID()); + } + break; + case ACTION_T_UPDATE_TEMPLATE: + { + if (m_creature->GetEntry() == param1) + { + + sLog.outErrorDb("CreatureEventAI: Event %d ACTION_T_UPDATE_TEMPLATE call with param1 == current entry. Creature %d", EventId, m_creature->GetEntry()); + return; + } + + m_creature->UpdateEntry(param1, param2 ? HORDE : ALLIANCE); + } + break; + case ACTION_T_DIE: + { + if (m_creature->isDead()) + { + + sLog.outErrorDb("CreatureEventAI: Event %d ACTION_T_DIE on dead creature. Creature %d", EventId, m_creature->GetEntry()); + return; + } + m_creature->DealDamage(m_creature, m_creature->GetMaxHealth(),NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + break; + case ACTION_T_ZONE_COMBAT_PULSE: + { + if (!m_creature->isInCombat() || !m_creature->GetMap()->IsDungeon()) + { + + sLog.outErrorDb("CreatureEventAI: Event %d ACTION_T_ZONE_COMBAT_PULSE on creature out of combat or in non-dungeon map. Creature %d", EventId, m_creature->GetEntry()); + return; + } + + DoZoneInCombat(m_creature); + } + break; + + // TRINITY ONLY + case ACTION_T_SET_ACTIVE: + me->setActive(param1 ? true : false); + break; + case ACTION_T_SET_AGGRESSIVE: + me->SetReactState(ReactStates(param1)); + break; + case ACTION_T_ATTACK_START_PULSE: + AttackStart(me->SelectNearestTarget((float)param1)); + break; + } +} + +void CreatureEventAI::JustRespawned() +{ + InCombat = false; + IsFleeing = false; + Reset(); + + if (bEmptyList) + return; + + //Handle Spawned Events + for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) + { + if ((*i).Event.event_type == EVENT_T_SPAWNED) + ProcessEvent(*i); + } +} + +void CreatureEventAI::Reset() +{ + EventUpdateTime = EVENT_UPDATE_TIME; + EventDiff = 0; + + TimetoFleeLeft = 0; + IsFleeing = false; + + if (bEmptyList) + return; + + //Reset all events to enabled + for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) + { + switch ((*i).Event.event_type) + { + //Reset all out of combat timers + case EVENT_T_TIMER_OOC: + { + if ((*i).Event.event_param2 == (*i).Event.event_param1) + { + (*i).Time = (*i).Event.event_param1; + (*i).Enabled = true; + } + else if ((*i).Event.event_param2 > (*i).Event.event_param1) + { + (*i).Time = urand((*i).Event.event_param1, (*i).Event.event_param2); + (*i).Enabled = true; + } + else + sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has InitialMax < InitialMin. Event disabled.", m_creature->GetEntry(), (*i).Event.event_id, (*i).Event.event_type); + } + break; + //default: + //TODO: enable below code line / verify this is correct to enable events previously disabled (ex. aggro yell), instead of enable this in void Aggro() + //(*i).Enabled = true; + //(*i).Time = 0; + //break; + } + } +} + +void CreatureEventAI::JustReachedHome() +{ + m_creature->LoadCreaturesAddon(); + + if (!bEmptyList) + { + for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) + { + if ((*i).Event.event_type == EVENT_T_REACHED_HOME) + ProcessEvent(*i); + } + } + + Reset(); +} + +void CreatureEventAI::EnterEvadeMode() +{ + m_creature->InterruptNonMeleeSpells(true); + m_creature->RemoveAllAuras(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(); + + if (m_creature->isAlive()) + m_creature->GetMotionMaster()->MoveTargetedHome(); + + m_creature->SetLootRecipient(NULL); + + InCombat = false; + + if (bEmptyList) + return; + + //Handle Evade events + for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) + { + if ((*i).Event.event_type == EVENT_T_EVADE) + ProcessEvent(*i); + } +} + +void CreatureEventAI::JustDied(Unit* killer) +{ + InCombat = false; + IsFleeing = false; + Reset(); + + if (bEmptyList) + return; + + //Handle Evade events + for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) + { + if ((*i).Event.event_type == EVENT_T_DEATH) + ProcessEvent(*i, killer); + } +} + +void CreatureEventAI::KilledUnit(Unit* victim) +{ + if (bEmptyList || victim->GetTypeId() != TYPEID_PLAYER) + return; + + for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) + { + if ((*i).Event.event_type == EVENT_T_KILL) + ProcessEvent(*i, victim); + } +} + +void CreatureEventAI::JustSummoned(Creature* pUnit) +{ + if (bEmptyList || !pUnit) + return; + + for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) + { + if ((*i).Event.event_type == EVENT_T_SUMMONED_UNIT) + ProcessEvent(*i, pUnit); + } +} + +void CreatureEventAI::Aggro(Unit *who) +{ + //Check for on combat start events + if (!bEmptyList) + { + for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) + { + switch ((*i).Event.event_type) + { + case EVENT_T_AGGRO: + (*i).Enabled = true; + ProcessEvent(*i, who); + break; + //Reset all in combat timers + case EVENT_T_TIMER: + if ((*i).Event.event_param2 == (*i).Event.event_param1) + { + (*i).Time = (*i).Event.event_param1; + (*i).Enabled = true; + } + else if ((*i).Event.event_param2 > (*i).Event.event_param1) + { + (*i).Time = urand((*i).Event.event_param1, (*i).Event.event_param2); + (*i).Enabled = true; + } + else + sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has InitialMax < InitialMin. Event disabled.", m_creature->GetEntry(), (*i).Event.event_id, (*i).Event.event_type); + break; + //All normal events need to be re-enabled and their time set to 0 + default: + (*i).Enabled = true; + (*i).Time = 0; + break; + } + } + } + + EventUpdateTime = EVENT_UPDATE_TIME; + EventDiff = 0; +} + +void CreatureEventAI::AttackStart(Unit *who) +{ + if (!who) + return; + + if (m_creature->Attack(who, MeleeEnabled)) + { + if (!InCombat) + { + InCombat = true; + Aggro(who); + } + + if (CombatMovementEnabled) + { + m_creature->GetMotionMaster()->MoveChase(who, AttackDistance, AttackAngle); + } + else + { + m_creature->GetMotionMaster()->MoveIdle(); + } + } +} + +void CreatureEventAI::MoveInLineOfSight(Unit *who) +{ + if (!who || InCombat) + return; + + //Check for OOC LOS Event + if (!bEmptyList && !m_creature->getVictim()) + { + for (std::list<CreatureEventAIHolder>::iterator itr = CreatureEventAIList.begin(); itr != CreatureEventAIList.end(); ++itr) + { + if ((*itr).Event.event_type == EVENT_T_OOC_LOS) + { + //can trigger if closer than fMaxAllowedRange + float fMaxAllowedRange = (*itr).Event.event_param2; + + //if range is ok and we are actually in LOS + if (m_creature->IsWithinDistInMap(who, fMaxAllowedRange) && m_creature->IsWithinLOSInMap(who)) + { + //if friendly event&&who is not hostile OR hostile event&&who is hostile + if (((*itr).Event.event_param1 && !m_creature->IsHostileTo(who)) || + ((!(*itr).Event.event_param1) && m_creature->IsHostileTo(who))) + ProcessEvent(*itr, who); + } + } + } + } + + //if (m_creature->isCivilian() && m_creature->IsNeutralToAll()) + // return; + + if(me->canStartAttack(who)) + AttackStart(who); +} + +void CreatureEventAI::SpellHit(Unit* pUnit, const SpellEntry* pSpell) +{ + + if (bEmptyList) + return; + + for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) + { + if ((*i).Event.event_type == EVENT_T_SPELLHIT) + { + //If spell id matches (or no spell id) & if spell school matches (or no spell school) + if (!(*i).Event.event_param1 || pSpell->Id == (*i).Event.event_param1) + { + if ((*i).Event.event_param2_s == -1 || pSpell->SchoolMask == (*i).Event.event_param2) + ProcessEvent(*i, pUnit); + } + } + } +} + +void CreatureEventAI::UpdateAI(const uint32 diff) +{ + //Check if we are in combat (also updates calls threat update code) + bool Combat = InCombat ? UpdateVictim() : false; + + //Must return if creature isn't alive. Normally select hostil target and get victim prevent this + if (!m_creature->isAlive()) + return; + + if (IsFleeing) + { + if(TimetoFleeLeft < diff) + { + me->SetControlled(false, UNIT_STAT_FLEEING); + me->SetNoCallAssistance(false); + me->CallAssistance(); + if(me->getVictim()) + me->GetMotionMaster()->MoveChase(me->getVictim()); + IsFleeing = false; + } + else + TimetoFleeLeft -= diff; + + return; + } + + if (!bEmptyList) + { + //Events are only updated once every EVENT_UPDATE_TIME ms to prevent lag with large amount of events + if (EventUpdateTime < diff) + { + EventDiff += diff; + + //Check for time based events + for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) + { + //Decrement Timers + if ((*i).Time) + { + if ((*i).Time > EventDiff) + { + //Do not decrement timers if event cannot trigger in this phase + if (!((*i).Event.event_inverse_phase_mask & (1 << Phase))) + (*i).Time -= EventDiff; + + //Skip processing of events that have time remaining + continue; + } + else (*i).Time = 0; + } + + //Events that are updated every EVENT_UPDATE_TIME + switch ((*i).Event.event_type) + { + case EVENT_T_TIMER_OOC: + ProcessEvent(*i); + break; + case EVENT_T_TIMER: + case EVENT_T_MANA: + case EVENT_T_HP: + case EVENT_T_TARGET_HP: + case EVENT_T_TARGET_CASTING: + case EVENT_T_FRIENDLY_HP: + if (Combat) + ProcessEvent(*i); + break; + case EVENT_T_RANGE: + if (Combat) + { + if (m_creature->IsWithinDistInMap(m_creature->getVictim(),(float)(*i).Event.event_param2)) + { + if (m_creature->GetDistance(m_creature->getVictim()) >= (float)(*i).Event.event_param1) + ProcessEvent(*i); + } + } + break; + } + } + + EventDiff = 0; + EventUpdateTime = EVENT_UPDATE_TIME; + } + else + { + EventDiff += diff; + EventUpdateTime -= diff; + } + } + + //Melee Auto-Attack + if (Combat && MeleeEnabled) + DoMeleeAttackIfReady(); +} + +inline Unit* CreatureEventAI::SelectUnit(AttackingTarget target, uint32 position) +{ + //ThreatList m_threatlist; + std::list<HostilReference*>& m_threatlist = m_creature->getThreatManager().getThreatList(); + std::list<HostilReference*>::iterator i = m_threatlist.begin(); + std::list<HostilReference*>::reverse_iterator r = m_threatlist.rbegin(); + + if (position >= m_threatlist.size() || !m_threatlist.size()) + return NULL; + + switch (target) + { + case ATTACKING_TARGET_RANDOM: + { + advance ( i , position + (rand() % (m_threatlist.size() - position ) )); + return Unit::GetUnit(*m_creature,(*i)->getUnitGuid()); + } + case ATTACKING_TARGET_TOPAGGRO: + { + advance ( i , position); + return Unit::GetUnit(*m_creature,(*i)->getUnitGuid()); + } + case ATTACKING_TARGET_BOTTOMAGGRO: + { + advance ( r , position); + return Unit::GetUnit(*m_creature,(*r)->getUnitGuid()); + } + } + return NULL; +} + +inline uint32 CreatureEventAI::GetRandActionParam(uint32 rnd, uint32 param1, uint32 param2, uint32 param3) +{ + switch (rnd % 3) + { + case 0: + return param1; + break; + case 1: + return param2; + break; + case 2: + return param3; + break; + } + return 0; +} + +inline Unit* CreatureEventAI::GetTargetByType(uint32 Target, Unit* pActionInvoker) +{ + switch (Target) + { + case TARGET_T_SELF: + return m_creature; + break; + case TARGET_T_HOSTILE: + return m_creature->getVictim(); + break; + case TARGET_T_HOSTILE_SECOND_AGGRO: + return SelectUnit(ATTACKING_TARGET_TOPAGGRO,1); + break; + case TARGET_T_HOSTILE_LAST_AGGRO: + return SelectUnit(ATTACKING_TARGET_BOTTOMAGGRO,0); + break; + case TARGET_T_HOSTILE_RANDOM: + return SelectUnit(ATTACKING_TARGET_RANDOM,0); + break; + case TARGET_T_HOSTILE_RANDOM_NOT_TOP: + return SelectUnit(ATTACKING_TARGET_RANDOM,1); + break; + case TARGET_T_ACTION_INVOKER: + return pActionInvoker; + break; + default: + return NULL; + break; + }; +} + +Unit* CreatureEventAI::DoSelectLowestHpFriendly(float range, uint32 MinHPDiff) +{ + CellPair p(MaNGOS::ComputeCellPair(m_creature->GetPositionX(), m_creature->GetPositionY())); + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + Unit* pUnit = NULL; + + MaNGOS::MostHPMissingInRange u_check(m_creature, range, MinHPDiff); + MaNGOS::UnitLastSearcher<MaNGOS::MostHPMissingInRange> searcher(m_creature, pUnit, u_check); + + /* + typedef TYPELIST_4(GameObject, Creature*except pets*, DynamicObject, Corpse*Bones*) AllGridObjectTypes; + This means that if we only search grid then we cannot possibly return pets or players so this is safe + */ + TypeContainerVisitor<MaNGOS::UnitLastSearcher<MaNGOS::MostHPMissingInRange>, GridTypeMapContainer > grid_unit_searcher(searcher); + + CellLock<GridReadGuard> cell_lock(cell, p); + cell_lock->Visit(cell_lock, grid_unit_searcher, *m_creature->GetMap()); + return pUnit; +} + +void CreatureEventAI::DoFindFriendlyCC(std::list<Creature*>& _list, float range) +{ + CellPair p(MaNGOS::ComputeCellPair(m_creature->GetPositionX(), m_creature->GetPositionY())); + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + MaNGOS::FriendlyCCedInRange u_check(m_creature, range); + MaNGOS::CreatureListSearcher<MaNGOS::FriendlyCCedInRange> searcher(m_creature, _list, u_check); + + TypeContainerVisitor<MaNGOS::CreatureListSearcher<MaNGOS::FriendlyCCedInRange>, GridTypeMapContainer > grid_creature_searcher(searcher); + + CellLock<GridReadGuard> cell_lock(cell, p); + cell_lock->Visit(cell_lock, grid_creature_searcher, *m_creature->GetMap()); +} + +void CreatureEventAI::DoFindFriendlyMissingBuff(std::list<Creature*>& _list, float range, uint32 spellid) +{ + CellPair p(MaNGOS::ComputeCellPair(m_creature->GetPositionX(), m_creature->GetPositionY())); + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + MaNGOS::FriendlyMissingBuffInRange u_check(m_creature, range, spellid); + MaNGOS::CreatureListSearcher<MaNGOS::FriendlyMissingBuffInRange> searcher(m_creature, _list, u_check); + + TypeContainerVisitor<MaNGOS::CreatureListSearcher<MaNGOS::FriendlyMissingBuffInRange>, GridTypeMapContainer > grid_creature_searcher(searcher); + + CellLock<GridReadGuard> cell_lock(cell, p); + cell_lock->Visit(cell_lock, grid_creature_searcher, *m_creature->GetMap()); +} + +//********************************* +//*** Functions used globally *** + +void CreatureEventAI::DoScriptText(int32 textEntry, WorldObject* pSource, Unit* target) +{ + if (!pSource) + { + sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i, invalid Source pointer.",textEntry); + return; + } + + if (textEntry >= 0) + { + sLog.outErrorDb("CreatureEventAI: DoScriptText with source entry %u (TypeId=%u, guid=%u) attempts to process text entry %i, but text entry must be negative.",pSource->GetEntry(),pSource->GetTypeId(),pSource->GetGUIDLow(),textEntry); + return; + } + + CreatureEventAI_TextMap::const_iterator i = CreatureEAI_Mgr.GetCreatureEventAITextMap().find(textEntry); + + if (i == CreatureEAI_Mgr.GetCreatureEventAITextMap().end()) + { + sLog.outErrorDb("CreatureEventAI: DoScriptText with source entry %u (TypeId=%u, guid=%u) could not find text entry %i.",pSource->GetEntry(),pSource->GetTypeId(),pSource->GetGUIDLow(),textEntry); + return; + } + + sLog.outDebug("CreatureEventAI: DoScriptText: text entry=%i, Sound=%u, Type=%u, Language=%u, Emote=%u",textEntry,(*i).second.SoundId,(*i).second.Type,(*i).second.Language,(*i).second.Emote); + + if((*i).second.SoundId) + { + if (GetSoundEntriesStore()->LookupEntry((*i).second.SoundId)) + pSource->PlayDirectSound((*i).second.SoundId); + else + sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i tried to process invalid sound id %u.",textEntry,(*i).second.SoundId); + } + + if((*i).second.Emote) + { + if (pSource->GetTypeId() == TYPEID_UNIT || pSource->GetTypeId() == TYPEID_PLAYER) + { + ((Unit*)pSource)->HandleEmoteCommand((*i).second.Emote); + } + else + sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i tried to process emote for invalid TypeId (%u).",textEntry,pSource->GetTypeId()); + } + + switch((*i).second.Type) + { + case CHAT_TYPE_SAY: + pSource->MonsterSay(textEntry, (*i).second.Language, target ? target->GetGUID() : 0); + break; + case CHAT_TYPE_YELL: + pSource->MonsterYell(textEntry, (*i).second.Language, target ? target->GetGUID() : 0); + break; + case CHAT_TYPE_TEXT_EMOTE: + pSource->MonsterTextEmote(textEntry, target ? target->GetGUID() : 0); + break; + case CHAT_TYPE_BOSS_EMOTE: + pSource->MonsterTextEmote(textEntry, target ? target->GetGUID() : 0, true); + break; + case CHAT_TYPE_WHISPER: + { + if (target && target->GetTypeId() == TYPEID_PLAYER) + pSource->MonsterWhisper(textEntry, target->GetGUID()); + else sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i cannot whisper without target unit (TYPEID_PLAYER).", textEntry); + }break; + case CHAT_TYPE_BOSS_WHISPER: + { + if (target && target->GetTypeId() == TYPEID_PLAYER) + pSource->MonsterWhisper(textEntry, target->GetGUID(), true); + else sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i cannot whisper without target unit (TYPEID_PLAYER).", textEntry); + }break; + case CHAT_TYPE_ZONE_YELL: + pSource->MonsterYellToZone(textEntry, (*i).second.Language, target ? target->GetGUID() : 0); + break; + } +} + +bool CreatureEventAI::CanCast(Unit* Target, SpellEntry const *Spell, bool Triggered) +{ + //No target so we can't cast + if (!Target || !Spell) + return false; + + //Silenced so we can't cast + if (!Triggered && me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED)) + return false; + + //Check for power + if (!Triggered && me->GetPower((Powers)Spell->powerType) < Spell->manaCost) + return false; + + SpellRangeEntry const *TempRange = NULL; + + TempRange = GetSpellRangeStore()->LookupEntry(Spell->rangeIndex); + + //Spell has invalid range store so we can't use it + if (!TempRange) + return false; + + //Unit is out of range of this spell + if (me->GetDistance(Target) > me->GetSpellMaxRangeForTarget(Target, TempRange) + || me->GetDistance(Target) < me->GetSpellMinRangeForTarget(Target, TempRange)) + return false; + + return true; +} + +bool CreatureEventAI::ReceiveEmote(Player* pPlayer, Creature* pCreature, uint32 uiEmote) +{ + if(pCreature->isCharmed()) + return true; + + CreatureEventAI* pTmpCreature = (CreatureEventAI*)(pCreature->AI()); + + if (pTmpCreature->bEmptyList) + return true; + + for (std::list<CreatureEventAIHolder>::iterator itr = pTmpCreature->CreatureEventAIList.begin(); itr != pTmpCreature->CreatureEventAIList.end(); ++itr) + { + if ((*itr).Event.event_type == EVENT_T_RECEIVE_EMOTE) + { + if ((*itr).Event.event_param1 != uiEmote) + return true; + + bool bProcess = false; + + switch((*itr).Event.event_param2) + { + //enum ConditionType + case CONDITION_NONE: // 0 0 + bProcess = true; + break; + case CONDITION_AURA: // spell_id effindex + if (pPlayer->HasAura((*itr).Event.event_param3,(*itr).Event.event_param4)) + bProcess = true; + break; + case CONDITION_ITEM: // item_id count + if (pPlayer->HasItemCount((*itr).Event.event_param3,(*itr).Event.event_param4)) + bProcess = true; + break; + case CONDITION_ITEM_EQUIPPED: // item_id count + if (pPlayer->HasItemOrGemWithIdEquipped((*itr).Event.event_param3,(*itr).Event.event_param4)) + bProcess = true; + break; + case CONDITION_ZONEID: // zone_id 0 + if (pPlayer->GetZoneId() == (*itr).Event.event_param3) + bProcess = true; + break; + case CONDITION_REPUTATION_RANK: // faction_id min_rank + if (pPlayer->GetReputationRank((*itr).Event.event_param3) >= (*itr).Event.event_param4) + bProcess = true; + break; + case CONDITION_TEAM: // player_team 0, (469 - Alliance 67 - Horde) + if (pPlayer->GetTeam() == (*itr).Event.event_param3) + bProcess = true; + break; + case CONDITION_SKILL: // skill_id min skill_value + if (pPlayer->HasSkill((*itr).Event.event_param3) && pPlayer->GetSkillValue((*itr).Event.event_param3) >= (*itr).Event.event_param4) + bProcess = true; + break; + case CONDITION_QUESTREWARDED: // quest_id 0 + if (pPlayer->GetQuestRewardStatus((*itr).Event.event_param3)) + bProcess = true; + break; + case CONDITION_QUESTTAKEN: // quest_id 0, for condition true while quest active. + if (pPlayer->GetQuestStatus((*itr).Event.event_param3) == QUEST_STATUS_INCOMPLETE) + bProcess = true; + break; + case CONDITION_ACTIVE_EVENT: // event_id 0 + if (IsHolidayActive(HolidayIds((*itr).Event.event_param3))) + bProcess = true; + break; + } + + if (bProcess) + { + sLog.outDebug("CreatureEventAI: ReceiveEmote CreatureEventAI: Condition ok, processing"); + pTmpCreature->ProcessEvent(*itr, pPlayer); + } + } + } + + return true; +} diff --git a/src/game/CreatureEventAI.h b/src/game/CreatureEventAI.h new file mode 100644 index 00000000000..83e925e1fd4 --- /dev/null +++ b/src/game/CreatureEventAI.h @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_CREATURE_EAI_H +#define MANGOS_CREATURE_EAI_H + +#include "Common.h" +#include "Creature.h" +#include "CreatureAI.h" +#include "Unit.h" + +class Player; +class WorldObject; + +#define EVENT_UPDATE_TIME 500 +#define SPELL_RUN_AWAY 8225 +#define MAX_ACTIONS 3 +#define TEXT_SOURCE_RANGE -1000000 //the amount of entries each text source has available + +enum Event_Types +{ + EVENT_T_TIMER = 0, //InitialMin, InitialMax, RepeatMin, RepeatMax + EVENT_T_TIMER_OOC = 1, //InitialMin, InitialMax, RepeatMin, RepeatMax + EVENT_T_HP = 2, //HPMax%, HPMin%, RepeatMin, RepeatMax + EVENT_T_MANA = 3, //ManaMax%,ManaMin% RepeatMin, RepeatMax + EVENT_T_AGGRO = 4, //NONE + EVENT_T_KILL = 5, //RepeatMin, RepeatMax + EVENT_T_DEATH = 6, //NONE + EVENT_T_EVADE = 7, //NONE + EVENT_T_SPELLHIT = 8, //SpellID, School, RepeatMin, RepeatMax + EVENT_T_RANGE = 9, //MinDist, MaxDist, RepeatMin, RepeatMax + EVENT_T_OOC_LOS = 10, //NoHostile, NoFriendly, RepeatMin, RepeatMax + EVENT_T_SPAWNED = 11, //NONE + EVENT_T_TARGET_HP = 12, //HPMax%, HPMin%, RepeatMin, RepeatMax + EVENT_T_TARGET_CASTING = 13, //RepeatMin, RepeatMax + EVENT_T_FRIENDLY_HP = 14, //HPDeficit, Radius, RepeatMin, RepeatMax + EVENT_T_FRIENDLY_IS_CC = 15, //DispelType, Radius, RepeatMin, RepeatMax + EVENT_T_FRIENDLY_MISSING_BUFF = 16, //SpellId, Radius, RepeatMin, RepeatMax + EVENT_T_SUMMONED_UNIT = 17, //CreatureId, RepeatMin, RepeatMax + EVENT_T_TARGET_MANA = 18, //ManaMax%, ManaMin%, RepeatMin, RepeatMax + EVENT_T_QUEST_ACCEPT = 19, //QuestID + EVENT_T_QUEST_COMPLETE = 20, // + EVENT_T_REACHED_HOME = 21, //NONE + EVENT_T_RECEIVE_EMOTE = 22, //EmoteId, Condition, CondValue1, CondValue2 + + EVENT_T_END, +}; + +enum Action_Types +{ + ACTION_T_NONE = 0, //No action + ACTION_T_TEXT = 1, //-TextId1, optionally -TextId2, optionally -TextId3(if -TextId2 exist). If more than just -TextId1 is defined, randomize. Negative values. + ACTION_T_SET_FACTION = 2, //FactionId (or 0 for default) + ACTION_T_MORPH_TO_ENTRY_OR_MODEL = 3, //Creature_template entry(param1) OR ModelId (param2) (or 0 for both to demorph) + ACTION_T_SOUND = 4, //SoundId + ACTION_T_EMOTE = 5, //EmoteId + ACTION_T_RANDOM_SAY = 6, //UNUSED + ACTION_T_RANDOM_YELL = 7, //UNUSED + ACTION_T_RANDOM_TEXTEMOTE = 8, //UNUSED + ACTION_T_RANDOM_SOUND = 9, //SoundId1, SoundId2, SoundId3 (-1 in any field means no output if randomed that field) + ACTION_T_RANDOM_EMOTE = 10, //EmoteId1, EmoteId2, EmoteId3 (-1 in any field means no output if randomed that field) + ACTION_T_CAST = 11, //SpellId, Target, CastFlags + ACTION_T_SUMMON = 12, //CreatureID, Target, Duration in ms + ACTION_T_THREAT_SINGLE_PCT = 13, //Threat%, Target + ACTION_T_THREAT_ALL_PCT = 14, //Threat% + ACTION_T_QUEST_EVENT = 15, //QuestID, Target + ACTION_T_CASTCREATUREGO = 16, //QuestID, SpellId, Target + ACTION_T_SET_UNIT_FIELD = 17, //Field_Number, Value, Target + ACTION_T_SET_UNIT_FLAG = 18, //Flags (may be more than one field OR'd together), Target + ACTION_T_REMOVE_UNIT_FLAG = 19, //Flags (may be more than one field OR'd together), Target + ACTION_T_AUTO_ATTACK = 20, //AllowAttackState (0 = stop attack, anything else means continue attacking) + ACTION_T_COMBAT_MOVEMENT = 21, //AllowCombatMovement (0 = stop combat based movement, anything else continue attacking) + ACTION_T_SET_PHASE = 22, //Phase + ACTION_T_INC_PHASE = 23, //Value (may be negative to decrement phase, should not be 0) + ACTION_T_EVADE = 24, //No Params + ACTION_T_FLEE = 25, //No Params + ACTION_T_QUEST_EVENT_ALL = 26, //QuestID + ACTION_T_CASTCREATUREGO_ALL = 27, //QuestId, SpellId + ACTION_T_REMOVEAURASFROMSPELL = 28, //Target, Spellid + ACTION_T_RANGED_MOVEMENT = 29, //Distance, Angle + ACTION_T_RANDOM_PHASE = 30, //PhaseId1, PhaseId2, PhaseId3 + ACTION_T_RANDOM_PHASE_RANGE = 31, //PhaseMin, PhaseMax + ACTION_T_SUMMON_ID = 32, //CreatureId, Target, SpawnId + ACTION_T_KILLED_MONSTER = 33, //CreatureId, Target + ACTION_T_SET_INST_DATA = 34, //Field, Data + ACTION_T_SET_INST_DATA64 = 35, //Field, Target + ACTION_T_UPDATE_TEMPLATE = 36, //Entry, Team + ACTION_T_DIE = 37, //No Params + ACTION_T_ZONE_COMBAT_PULSE = 38, //No Params + + ACTION_T_SET_ACTIVE = 101, //Apply + ACTION_T_SET_AGGRESSIVE = 102, //Apply + ACTION_T_ATTACK_START_PULSE = 103, //Distance + + ACTION_T_END, +}; + +enum Target +{ + //Self (m_creature) + TARGET_T_SELF = 0, //Self cast + + //Hostile targets (if pet then returns pet owner) + TARGET_T_HOSTILE, //Our current target (ie: highest aggro) + TARGET_T_HOSTILE_SECOND_AGGRO, //Second highest aggro (generaly used for cleaves and some special attacks) + TARGET_T_HOSTILE_LAST_AGGRO, //Dead last on aggro (no idea what this could be used for) + TARGET_T_HOSTILE_RANDOM, //Just any random target on our threat list + TARGET_T_HOSTILE_RANDOM_NOT_TOP, //Any random target except top threat + + //Invoker targets (if pet then returns pet owner) + TARGET_T_ACTION_INVOKER, //Unit who caused this Event to occur (only works for EVENT_T_AGGRO, EVENT_T_KILL, EVENT_T_DEATH, EVENT_T_SPELLHIT, EVENT_T_OOC_LOS, EVENT_T_FRIENDLY_HP, EVENT_T_FRIENDLY_IS_CC, EVENT_T_FRIENDLY_MISSING_BUFF) + + //Hostile targets (including pets) + TARGET_T_HOSTILE_WPET, //Current target (can be a pet) + TARGET_T_HOSTILE_WPET_SECOND_AGGRO, //Second highest aggro (generaly used for cleaves and some special attacks) + TARGET_T_HOSTILE_WPET_LAST_AGGRO, //Dead last on aggro (no idea what this could be used for) + TARGET_T_HOSTILE_WPET_RANDOM, //Just any random target on our threat list + TARGET_T_HOSTILE_WPET_RANDOM_NOT_TOP, //Any random target except top threat + + TARGET_T_ACTION_INVOKER_WPET, + + TARGET_T_END +}; + +enum CastFlags +{ + CAST_INTURRUPT_PREVIOUS = 0x01, //Interrupt any spell casting + CAST_TRIGGERED = 0x02, //Triggered (this makes spell cost zero mana and have no cast time) + CAST_FORCE_CAST = 0x04, //Forces cast even if creature is out of mana or out of range + CAST_NO_MELEE_IF_OOM = 0x08, //Prevents creature from entering melee if out of mana or out of range + CAST_FORCE_TARGET_SELF = 0x10, //Forces the target to cast this spell on itself + CAST_AURA_NOT_PRESENT = 0x20, //Only casts the spell if the target does not have an aura from the spell +}; + +enum EventFlags +{ + EFLAG_REPEATABLE = 0x01, //Event repeats + EFLAG_NORMAL = 0x02, //Event only occurs in Normal instance difficulty + EFLAG_HEROIC = 0x04, //Event only occurs in Heroic instance difficulty + EFLAG_RESERVED_3 = 0x08, + EFLAG_RESERVED_4 = 0x10, + EFLAG_RESERVED_5 = 0x20, + EFLAG_RESERVED_6 = 0x40, + EFLAG_DEBUG_ONLY = 0x80, //Event only occurs in debug build of SD2 only +}; + +// String text additional data, used in (CreatureEventAI) +struct StringTextData +{ + uint32 SoundId; + uint8 Type; + uint32 Language; + uint32 Emote; +}; +// Text Maps +typedef UNORDERED_MAP<int32, StringTextData> CreatureEventAI_TextMap; + +struct CreatureEventAI_Event +{ + uint32 event_id; + + uint32 creature_id; + + uint16 event_type; + uint32 event_inverse_phase_mask; + uint8 event_chance; + uint8 event_flags; + union + { + uint32 event_param1; + int32 event_param1_s; + }; + union + { + uint32 event_param2; + int32 event_param2_s; + }; + union + { + uint32 event_param3; + int32 event_param3_s; + }; + union + { + uint32 event_param4; + int32 event_param4_s; + }; + + struct _action + { + uint16 type; + union + { + uint32 param1; + int32 param1_s; + }; + union + { + uint32 param2; + int32 param2_s; + }; + union + { + uint32 param3; + int32 param3_s; + }; + }action[MAX_ACTIONS]; +}; +//Event_Map +typedef UNORDERED_MAP<uint32, std::vector<CreatureEventAI_Event> > CreatureEventAI_Event_Map; + +struct CreatureEventAI_Summon +{ + uint32 id; + + float position_x; + float position_y; + float position_z; + float orientation; + uint32 SpawnTimeSecs; +}; + +//EventSummon_Map +typedef UNORDERED_MAP<uint32, CreatureEventAI_Summon> CreatureEventAI_Summon_Map; + +struct CreatureEventAIHolder +{ + CreatureEventAIHolder(CreatureEventAI_Event p) : Event(p), Time(0), Enabled(true){} + + CreatureEventAI_Event Event; + uint32 Time; + bool Enabled; +}; + +class TRINITY_DLL_SPEC CreatureEventAI : public CreatureAI +{ + + public: + explicit CreatureEventAI(Creature *c); + ~CreatureEventAI() + { + CreatureEventAIList.clear(); + } + void JustRespawned(); + void Reset(); + void JustReachedHome(); + void EnterEvadeMode(); + void JustDied(Unit* killer); + void KilledUnit(Unit* victim); + void JustSummoned(Creature* pUnit); + void Aggro(Unit *who); + void AttackStart(Unit *who); + void MoveInLineOfSight(Unit *who); + void SpellHit(Unit* pUnit, const SpellEntry* pSpell); + void UpdateAI(const uint32 diff); + static int Permissible(const Creature *); + + bool ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pActionInvoker = NULL); + void ProcessAction(uint16 type, uint32 param1, uint32 param2, uint32 param3, uint32 rnd, uint32 EventId, Unit* pActionInvoker); + inline uint32 GetRandActionParam(uint32 rnd, uint32 param1, uint32 param2, uint32 param3); + inline Unit* GetTargetByType(uint32 Target, Unit* pActionInvoker); + inline Unit* SelectUnit(AttackingTarget target, uint32 position); + + void DoScriptText(int32 textEntry, WorldObject* pSource, Unit* target); + bool CanCast(Unit* Target, SpellEntry const *Spell, bool Triggered); + bool ReceiveEmote(Player* pPlayer, Creature* pCreature, uint32 uiEmote); + + Unit* DoSelectLowestHpFriendly(float range, uint32 MinHPDiff); + void DoFindFriendlyMissingBuff(std::list<Creature*>& _list, float range, uint32 spellid); + void DoFindFriendlyCC(std::list<Creature*>& _list, float range); + + //Bool for if we are in combat or not + bool InCombat; + //Holder for events (stores enabled, time, and eventid) + std::list<CreatureEventAIHolder> CreatureEventAIList; + uint32 EventUpdateTime; //Time between event updates + uint32 EventDiff; //Time between the last event call + bool bEmptyList; + + //Variables used by Events themselves + uint8 Phase; //Current phase, max 32 phases + bool CombatMovementEnabled; //If we allow targeted movment gen (movement twoards top threat) + bool MeleeEnabled; //If we allow melee auto attack + uint32 AttackDistance; //Distance to attack from + float AttackAngle; //Angle of attack + + uint32 TimetoFleeLeft; + bool IsFleeing; +}; +#endif diff --git a/src/game/CreatureEventAIMgr.cpp b/src/game/CreatureEventAIMgr.cpp new file mode 100644 index 00000000000..5ac08400804 --- /dev/null +++ b/src/game/CreatureEventAIMgr.cpp @@ -0,0 +1,563 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Database/DatabaseEnv.h" +#include "Database/SQLStorage.h" +#include "CreatureEventAI.h" +#include "CreatureEventAIMgr.h" +#include "ObjectMgr.h" +#include "ProgressBar.h" +#include "Policies/SingletonImp.h" +#include "ObjectDefines.h" + +INSTANTIATE_SINGLETON_1(CreatureEventAIMgr); + +// ------------------- +void CreatureEventAIMgr::LoadCreatureEventAI_Texts() +{ + // Drop Existing Text Map, only done once and we are ready to add data from multiple sources. + m_CreatureEventAI_TextMap.clear(); + + // Load EventAI Text + LoadTrinityStrings(WorldDatabase,"creature_ai_texts",-1,1+(TEXT_SOURCE_RANGE)); + + // Gather Additional data from EventAI Texts + QueryResult *result = WorldDatabase.PQuery("SELECT entry, sound, type, language, emote FROM creature_ai_texts"); + + sLog.outString("Loading EventAI Texts additional data..."); + if (result) + { + barGoLink bar(result->GetRowCount()); + uint32 count = 0; + + do + { + bar.step(); + Field* fields = result->Fetch(); + StringTextData temp; + + int32 i = fields[0].GetInt32(); + temp.SoundId = fields[1].GetInt32(); + temp.Type = fields[2].GetInt32(); + temp.Language = fields[3].GetInt32(); + temp.Emote = fields[4].GetInt32(); + + if (i >= 0) + { + sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` is not a negative value.",i); + continue; + } + + if (i <= TEXT_SOURCE_RANGE) + { + sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` is out of accepted entry range for table.",i); + continue; + } + + if (temp.SoundId) + { + if (!GetSoundEntriesStore()->LookupEntry(temp.SoundId)) + sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` has soundId %u but sound does not exist.",i,temp.SoundId); + } + + if (!GetLanguageDescByID(temp.Language)) + sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` using Language %u but Language does not exist.",i,temp.Language); + + if (temp.Type > CHAT_TYPE_BOSS_WHISPER) + sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` has Type %u but this Chat Type does not exist.",i,temp.Type); + + m_CreatureEventAI_TextMap[i] = temp; + ++count; + } while (result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString(">> Loaded %u additional CreatureEventAI Texts data.", count); + }else + { + barGoLink bar(1); + bar.step(); + sLog.outString(); + sLog.outString(">> Loaded 0 additional CreatureEventAI Texts data. DB table `creature_ai_texts` is empty."); + } + +} + +// ------------------- +void CreatureEventAIMgr::LoadCreatureEventAI_Summons() +{ + + //Drop Existing EventSummon Map + m_CreatureEventAI_Summon_Map.clear(); + + //Gather additional data for EventAI + QueryResult *result = WorldDatabase.PQuery("SELECT id, position_x, position_y, position_z, orientation, spawntimesecs FROM creature_ai_summons"); + if (result) + { + barGoLink bar(result->GetRowCount()); + uint32 Count = 0; + + do + { + bar.step(); + Field *fields = result->Fetch(); + + CreatureEventAI_Summon temp; + + uint32 i = fields[0].GetUInt32(); + temp.position_x = fields[1].GetFloat(); + temp.position_y = fields[2].GetFloat(); + temp.position_z = fields[3].GetFloat(); + temp.orientation = fields[4].GetFloat(); + temp.SpawnTimeSecs = fields[5].GetUInt32(); + + //Add to map + m_CreatureEventAI_Summon_Map[i] = temp; + ++Count; + }while (result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString(">> Loaded %u CreatureEventAI summon definitions", Count); + }else + { + barGoLink bar(1); + bar.step(); + sLog.outString(); + sLog.outString(">> Loaded 0 CreatureEventAI Summon definitions. DB table `creature_ai_summons` is empty."); + } + +} + +// ------------------- +void CreatureEventAIMgr::LoadCreatureEventAI_Scripts() +{ + //Drop Existing EventAI List + m_CreatureEventAI_Event_Map.clear(); + + //Gather event data + QueryResult *result = WorldDatabase.PQuery("SELECT id, creature_id, event_type, event_inverse_phase_mask, event_chance, event_flags, " + "event_param1, event_param2, event_param3, event_param4, " + "action1_type, action1_param1, action1_param2, action1_param3, " + "action2_type, action2_param1, action2_param2, action2_param3, " + "action3_type, action3_param1, action3_param2, action3_param3 " + "FROM creature_ai_scripts"); + if (result) + { + barGoLink bar(result->GetRowCount()); + uint32 Count = 0; + + do + { + bar.step(); + Field *fields = result->Fetch(); + + CreatureEventAI_Event temp; + + temp.event_id = fields[0].GetUInt32(); + uint32 i = temp.event_id; + temp.creature_id = fields[1].GetUInt32(); + uint32 creature_id = temp.creature_id; + temp.event_type = fields[2].GetUInt16(); + temp.event_inverse_phase_mask = fields[3].GetUInt32(); + temp.event_chance = fields[4].GetUInt8(); + temp.event_flags = fields[5].GetUInt8(); + temp.event_param1 = fields[6].GetUInt32(); + temp.event_param2 = fields[7].GetUInt32(); + temp.event_param3 = fields[8].GetUInt32(); + temp.event_param4 = fields[9].GetUInt32(); + + CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(temp.creature_id); + //Creature does not exist in database + if (!cInfo) + { + sLog.outErrorDb("CreatureEventAI: Event %u has script for non-existing creature.", i); + continue; + } + + //Report any errors in event + if (temp.event_type >= EVENT_T_END) + sLog.outErrorDb("CreatureEventAI: Event %u has incorrect event type. Maybe DB requires updated version of SD2.", i); + + //No chance of this event occuring + if (temp.event_chance == 0) + sLog.outErrorDb("CreatureEventAI: Event %u has 0 percent chance. Event will never trigger!", i); + + //Chance above 100, force it to be 100 + if (temp.event_chance > 100) + { + sLog.outErrorDb("CreatureEventAI: Creature %u are using event %u with more than 100 percent chance. Adjusting to 100 percent.", temp.creature_id, i); + temp.event_chance = 100; + } + + //Individual event checks + switch (temp.event_type) + { + case EVENT_T_HP: + case EVENT_T_MANA: + case EVENT_T_TARGET_HP: + { + if (temp.event_param2 > 100) + sLog.outErrorDb("CreatureEventAI: Creature %u are using percentage event(%u) with param2 (MinPercent) > 100. Event will never trigger! ", temp.creature_id, i); + + if (temp.event_param1 <= temp.event_param2) + sLog.outErrorDb("CreatureEventAI: Creature %u are using percentage event(%u) with param1 <= param2 (MaxPercent <= MinPercent). Event will never trigger! ", temp.creature_id, i); + + if (temp.event_flags & EFLAG_REPEATABLE && !temp.event_param3 && !temp.event_param4) + { + sLog.outErrorDb("CreatureEventAI: Creature %u has param3 and param4=0 (RepeatMin/RepeatMax) but cannot be repeatable without timers. Removing EFLAG_REPEATABLE for event %u.", temp.creature_id, i); + temp.event_flags &= ~EFLAG_REPEATABLE; + } + } + break; + + case EVENT_T_SPELLHIT: + { + if (temp.event_param1) + { + SpellEntry const* pSpell = GetSpellStore()->LookupEntry(temp.event_param1); + if (!pSpell) + { + sLog.outErrorDb("CreatureEventAI: Creature %u has non-existant SpellID(%u) defined in event %u.", temp.creature_id, temp.event_param1, i); + continue; + } + + if (temp.event_param2_s != -1 && temp.event_param2 != pSpell->SchoolMask) + sLog.outErrorDb("CreatureEventAI: Creature %u has param1(spellId %u) but param2 is not -1 and not equal to spell's school mask. Event %u can never trigger.", temp.creature_id, temp.event_param1, i); + } + + //TODO: fix this system with SPELL_SCHOOL_MASK. Current complicate things, using int32(-1) instead of just 0 + //SPELL_SCHOOL_MASK_NONE = 0 and does not exist, thus it can not ever trigger or be used in SpellHit() + if (temp.event_param2_s != -1 && temp.event_param2_s > SPELL_SCHOOL_MASK_ALL) + sLog.outErrorDb("CreatureEventAI: Creature %u is using invalid SpellSchoolMask(%u) defined in event %u.", temp.creature_id, temp.event_param2, i); + + if (temp.event_param4 < temp.event_param3) + sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); + } + break; + + case EVENT_T_RANGE: + case EVENT_T_OOC_LOS: + case EVENT_T_FRIENDLY_HP: + case EVENT_T_FRIENDLY_IS_CC: + case EVENT_T_FRIENDLY_MISSING_BUFF: + { + if (temp.event_param4 < temp.event_param3) + sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); + } + break; + + case EVENT_T_TIMER: + case EVENT_T_TIMER_OOC: + { + if (temp.event_param2 < temp.event_param1) + sLog.outErrorDb("CreatureEventAI: Creature %u are using timed event(%u) with param2 < param1 (InitialMax < InitialMin). Event will never repeat.", temp.creature_id, i); + + if (temp.event_param4 < temp.event_param3) + sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); + } + break; + + case EVENT_T_KILL: + case EVENT_T_TARGET_CASTING: + { + if (temp.event_param2 < temp.event_param1) + sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); + } + break; + + case EVENT_T_AGGRO: + case EVENT_T_DEATH: + case EVENT_T_EVADE: + case EVENT_T_SPAWNED: + case EVENT_T_REACHED_HOME: + { + if (temp.event_flags & EFLAG_REPEATABLE) + { + sLog.outErrorDb("CreatureEventAI: Creature %u has EFLAG_REPEATABLE set. Event can never be repeatable. Removing flag for event %u.", temp.creature_id, i); + temp.event_flags &= ~EFLAG_REPEATABLE; + } + } + break; + } + + for (uint32 j = 0; j < MAX_ACTIONS; j++) + { + temp.action[j].type = fields[10+(j*4)].GetUInt16(); + temp.action[j].param1 = fields[11+(j*4)].GetUInt32(); + temp.action[j].param2 = fields[12+(j*4)].GetUInt32(); + temp.action[j].param3 = fields[13+(j*4)].GetUInt32(); + + //Report any errors in actions + switch (temp.action[j].type) + { + case ACTION_T_TEXT: + { + if (temp.action[j].param1_s < 0) + { + if (m_CreatureEventAI_TextMap.find(temp.action[j].param1_s) == m_CreatureEventAI_TextMap.end()) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 refrences non-existing entry in texts table.", i, j+1); + } + if (temp.action[j].param2_s < 0) + { + if (m_CreatureEventAI_TextMap.find(temp.action[j].param2_s) == m_CreatureEventAI_TextMap.end()) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u param2 refrences non-existing entry in texts table.", i, j+1); + + if (!temp.action[j].param1_s) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u has param2, but param1 is not set. Required for randomized text.", i, j+1); + } + if (temp.action[j].param3_s < 0) + { + if (m_CreatureEventAI_TextMap.find(temp.action[j].param3_s) == m_CreatureEventAI_TextMap.end()) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u param3 refrences non-existing entry in texts table.", i, j+1); + + if (!temp.action[j].param1_s || !temp.action[j].param2_s) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u has param3, but param1 and/or param2 is not set. Required for randomized text.", i, j+1); + } + } + break; + case ACTION_T_SET_FACTION: + if (temp.action[j].param1 !=0 && !GetFactionStore()->LookupEntry(temp.action[j].param1)) + { + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant FactionId %u.", i, j+1, temp.action[j].param1); + temp.action[j].param1 = 0; + } + break; + case ACTION_T_MORPH_TO_ENTRY_OR_MODEL: + if (temp.action[j].param1 !=0 || temp.action[j].param2 !=0) + { + if (temp.action[j].param1 && !GetCreatureTemplateStore(temp.action[j].param1)) + { + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant Creature entry %u.", i, j+1, temp.action[j].param1); + temp.action[j].param1 = 0; + } + + if (temp.action[j].param2 && !GetCreatureDisplayStore()->LookupEntry(temp.action[j].param2)) + { + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant ModelId %u.", i, j+1, temp.action[j].param2); + temp.action[j].param2 = 0; + } + } + break; + case ACTION_T_SOUND: + if (!GetSoundEntriesStore()->LookupEntry(temp.action[j].param1)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant SoundID %u.", i, j+1, temp.action[j].param1); + break; +/* + case ACTION_T_RANDOM_SOUND: + { + if(!GetSoundEntriesStore()->LookupEntry(temp.action[j].param1)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 uses non-existant SoundID %u.", i, j+1, temp.action[j].param1); + if(!GetSoundEntriesStore()->LookupEntry(temp.action[j].param2)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u param2 uses non-existant SoundID %u.", i, j+1, temp.action[j].param2); + if(!GetSoundEntriesStore()->LookupEntry(temp.action[j].param3)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u param3 uses non-existant SoundID %u.", i, j+1, temp.action[j].param3); + } + break; + */ + case ACTION_T_CAST: + { + const SpellEntry *spell = GetSpellStore()->LookupEntry(temp.action[j].param1); + if (!spell) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant SpellID %u.", i, j+1, temp.action[j].param1); + else + { + if (spell->RecoveryTime > 0 && temp.event_flags & EFLAG_REPEATABLE) + { + //output as debug for now, also because there's no general rule all spells have RecoveryTime + if (temp.event_param3 < spell->RecoveryTime) + debug_log("CreatureEventAI: Event %u Action %u uses SpellID %u but cooldown is longer(%u) than minumum defined in event param3(%u).", i, j+1,temp.action[j].param1, spell->RecoveryTime, temp.event_param3); + } + } + + if (temp.action[j].param2 >= TARGET_T_END) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); + } + break; + case ACTION_T_REMOVEAURASFROMSPELL: + { + if (!GetSpellStore()->LookupEntry(temp.action[j].param2)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant SpellID %u.", i, j+1, temp.action[j].param2); + + if (temp.action[j].param1 >= TARGET_T_END) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); + } + break; + case ACTION_T_QUEST_EVENT: + { + if (Quest const* qid = GetQuestTemplateStore(temp.action[j].param1)) + { + if (!qid->HasFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u. SpecialFlags for quest entry %u does not include |2, Action will not have any effect.", i, j+1, temp.action[j].param1); + } + else + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant Quest entry %u.", i, j+1, temp.action[j].param1); + + if (temp.action[j].param2 >= TARGET_T_END) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); + } + break; + case ACTION_T_QUEST_EVENT_ALL: + { + if (Quest const* qid = GetQuestTemplateStore(temp.action[j].param1)) + { + if (!qid->HasFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u. SpecialFlags for quest entry %u does not include |2, Action will not have any effect.", i, j+1, temp.action[j].param1); + } + else + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant Quest entry %u.", i, j+1, temp.action[j].param1); + } + break; + case ACTION_T_CASTCREATUREGO: + { + if (!GetCreatureTemplateStore(temp.action[j].param1)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, temp.action[j].param1); + + if (!GetSpellStore()->LookupEntry(temp.action[j].param2)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant SpellID %u.", i, j+1, temp.action[j].param2); + + if (temp.action[j].param3 >= TARGET_T_END) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); + } + break; + case ACTION_T_CASTCREATUREGO_ALL: + { + if (!GetQuestTemplateStore(temp.action[j].param1)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant Quest entry %u.", i, j+1, temp.action[j].param1); + + if (!GetSpellStore()->LookupEntry(temp.action[j].param2)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant SpellID %u.", i, j+1, temp.action[j].param2); + } + break; + + //2nd param target + case ACTION_T_SUMMON_ID: + { + if (!GetCreatureTemplateStore(temp.action[j].param1)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, temp.action[j].param1); + + if (m_CreatureEventAI_Summon_Map.find(temp.action[j].param3) == m_CreatureEventAI_Summon_Map.end()) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u summons missing CreatureEventAI_Summon %u", i, j+1, temp.action[j].param3); + + if (temp.action[j].param2 >= TARGET_T_END) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); + } + break; + case ACTION_T_KILLED_MONSTER: + { + if (!GetCreatureTemplateStore(temp.action[j].param1)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, temp.action[j].param1); + + if (temp.action[j].param2 >= TARGET_T_END) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); + } + break; + case ACTION_T_SUMMON: + { + if (!GetCreatureTemplateStore(temp.action[j].param1)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, temp.action[j].param1); + + if (temp.action[j].param2 >= TARGET_T_END) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); + } + break; + case ACTION_T_THREAT_SINGLE_PCT: + case ACTION_T_SET_UNIT_FLAG: + case ACTION_T_REMOVE_UNIT_FLAG: + if (temp.action[j].param2 >= TARGET_T_END) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); + break; + + //3rd param target + case ACTION_T_SET_UNIT_FIELD: + if (temp.action[j].param1 < OBJECT_END || temp.action[j].param1 >= UNIT_END) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 (UNIT_FIELD*). Index out of range for intended use.", i, j+1); + if (temp.action[j].param3 >= TARGET_T_END) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); + break; + + case ACTION_T_SET_PHASE: + if (temp.action[j].param1 > 31) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase > 31. Phase mask cannot be used past phase 31.", i, j+1); + break; + + case ACTION_T_INC_PHASE: + if (!temp.action[j].param1) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u is incrementing phase by 0. Was this intended?", i, j+1); + break; + + case ACTION_T_SET_INST_DATA: + { + if (!(temp.event_flags & EFLAG_NORMAL) && !(temp.event_flags & EFLAG_HEROIC)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u. Cannot set instance data without event flags (normal/heroic).", i, j+1); + + if (temp.action[j].param2 > 4/*SPECIAL*/) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set instance data above encounter state 4. Custom case?", i, j+1); + } + break; + case ACTION_T_SET_INST_DATA64: + { + if (!(temp.event_flags & EFLAG_NORMAL) && !(temp.event_flags & EFLAG_HEROIC)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u. Cannot set instance data without event flags (normal/heroic).", i, j+1); + + if (temp.action[j].param2 >= TARGET_T_END) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); + } + break; + case ACTION_T_UPDATE_TEMPLATE: + { + if (!GetCreatureTemplateStore(temp.action[j].param1)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, temp.action[j].param1); + } + break; + case ACTION_T_RANDOM_SAY: + case ACTION_T_RANDOM_YELL: + case ACTION_T_RANDOM_TEXTEMOTE: + sLog.outErrorDb("CreatureEventAI: Event %u Action %u currently unused ACTION type. Did you forget to update database?", i, j+1); + break; + + default: + if (temp.action[j].type >= ACTION_T_END) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u has incorrect action type. Maybe DB requires updated version of SD2.", i, j+1); + break; + } + } + + //Add to list + m_CreatureEventAI_Event_Map[creature_id].push_back(temp); + ++Count; + + if(cInfo->AIName == "") + const_cast<CreatureInfo*>(cInfo)->AIName = "EventAI"; + } while (result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString(">> Loaded %u CreatureEventAI scripts", Count); + }else + { + barGoLink bar(1); + bar.step(); + sLog.outString(); + sLog.outString(">> Loaded 0 CreatureEventAI scripts. DB table `creature_ai_scripts` is empty."); + } +} diff --git a/src/game/CreatureEventAIMgr.h b/src/game/CreatureEventAIMgr.h new file mode 100644 index 00000000000..ea5989a74ed --- /dev/null +++ b/src/game/CreatureEventAIMgr.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_CREATURE_EAI_MGR_H +#define MANGOS_CREATURE_EAI_MGR_H + +#include "Common.h" +#include "CreatureEventAI.h" + +class CreatureEventAIMgr +{ + public: + CreatureEventAIMgr(){}; + ~CreatureEventAIMgr(){}; + + void LoadCreatureEventAI_Texts(); + void LoadCreatureEventAI_Summons(); + void LoadCreatureEventAI_Scripts(); + + CreatureEventAI_Event_Map& GetCreatureEventAIMap() { return m_CreatureEventAI_Event_Map; } + CreatureEventAI_Summon_Map& GetCreatureEventAISummonMap() { return m_CreatureEventAI_Summon_Map; } + CreatureEventAI_TextMap& GetCreatureEventAITextMap() { return m_CreatureEventAI_TextMap; } + + private: + CreatureEventAI_Event_Map m_CreatureEventAI_Event_Map; + CreatureEventAI_Summon_Map m_CreatureEventAI_Summon_Map; + CreatureEventAI_TextMap m_CreatureEventAI_TextMap; +}; + +#define CreatureEAI_Mgr MaNGOS::Singleton<CreatureEventAIMgr>::Instance() +#endif diff --git a/src/game/CreatureGroups.cpp b/src/game/CreatureGroups.cpp index 55b56e0b94e..3b7a27cdab5 100644 --- a/src/game/CreatureGroups.cpp +++ b/src/game/CreatureGroups.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,43 +23,52 @@ #include "ObjectMgr.h" #include "ProgressBar.h" #include "Policies/SingletonImp.h" +#include "CreatureAI.h" #define MAX_DESYNC 5.0f -//TODO: Create group manager for each map + INSTANTIATE_SINGLETON_1(CreatureGroupManager); -UNORDERED_MAP<uint32, CreatureGroup*> CreatureGroupHolder; -UNORDERED_MAP<uint32, FormationMember*> CreatureGroupMap; +CreatureGroupInfoType CreatureGroupMap; -void CreatureGroupManager::UpdateCreatureGroup(uint32 group_id, Creature *member) +void CreatureGroupManager::AddCreatureToGroup(uint32 groupId, Creature *member) { - if(CreatureGroupHolder.find(group_id) != CreatureGroupHolder.end()) + Map *map = member->FindMap(); + if(!map) + return; + + CreatureGroupHolderType::iterator itr = map->CreatureGroupHolder.find(groupId); + + //Add member to an existing group + if(itr != map->CreatureGroupHolder.end()) { - sLog.outDebug("Group found: %u, inserting creature GUID: %u", group_id, member->GetGUIDLow()); - CreatureGroupHolder[group_id]->AddMember(member); + sLog.outDebug("Group found: %u, inserting creature GUID: %u, Group InstanceID %u", groupId, member->GetGUIDLow(), member->GetInstanceId()); + itr->second->AddMember(member); } + //Create new group else { - sLog.outDebug("Group not found: %u. Creating new group.", group_id); - CreatureGroup* formation = new CreatureGroup; - CreatureGroupHolder[group_id] = formation; - formation->AddMember(member); + sLog.outDebug("Group not found: %u. Creating new group.", groupId); + CreatureGroup* group = new CreatureGroup(groupId); + map->CreatureGroupHolder[groupId] = group; + group->AddMember(member); } } -void CreatureGroupManager::DestroyGroup(uint32 group_id, uint64 guid) +void CreatureGroupManager::RemoveCreatureFromGroup(CreatureGroup *group, Creature *member) { - if(CreatureGroupHolder.find(group_id) != CreatureGroupHolder.end()) + sLog.outDebug("Deleting member pointer to GUID: %u from group %u", group->GetId(), member->GetDBTableGUIDLow()); + group->RemoveMember(member); + + if(group->isEmpty()) { - sLog.outDebug("Deleting member pointer to GUID: %u from group %u", group_id, guid); - CreatureGroupHolder[group_id]->RemoveMember(guid); + Map *map = member->FindMap(); + if(!map) + return; - if(CreatureGroupHolder[group_id]->isEmpty()) - { - sLog.outDebug("Deleting group %u", group_id); - delete CreatureGroupHolder[group_id]; - CreatureGroupHolder.erase(group_id); - } + sLog.outDebug("Deleting group with InstanceID %u", member->GetInstanceId()); + map->CreatureGroupHolder.erase(group->GetId()); + delete group; } } @@ -73,7 +82,7 @@ void CreatureGroupManager::LoadCreatureFormations() if(!result) { - sLog.outErrorDb(" an error occured while loading the table `creature_formations` ( maybe it doesn't exist ?)\n"); + sLog.outErrorDb(" ...an error occured while loading the table `creature_formations` ( maybe it doesn't exist ?)\n"); return; } delete result; @@ -87,11 +96,11 @@ void CreatureGroupManager::LoadCreatureFormations() return; } - uint32 total_records = result->GetRowCount(), lastLeaderGUID = 0; + uint32 total_records = result->GetRowCount(); barGoLink bar( total_records); Field *fields; - FormationMember *group_member; + FormationInfo *group_member; //Loading data... do { @@ -99,22 +108,36 @@ void CreatureGroupManager::LoadCreatureFormations() bar.step(); //Load group member data - group_member = new FormationMember; + group_member = new FormationInfo; group_member->leaderGUID = fields[0].GetUInt32(); - group_member->memberGUID = fields[1].GetUInt32(); - group_member->groupAI = fields[4].GetUInt8(); + uint32 memberGUID = fields[1].GetUInt32(); + group_member->groupAI = fields[4].GetUInt8(); //If creature is group leader we may skip loading of dist/angle - if(group_member->leaderGUID != group_member->memberGUID) + if(group_member->leaderGUID != memberGUID) { - group_member->follow_dist = fields[2].GetUInt32(); - group_member->follow_angle = fields[3].GetUInt32(); + group_member->follow_dist = fields[2].GetUInt32(); + group_member->follow_angle = fields[3].GetUInt32(); } - CreatureGroupMap[group_member->memberGUID] = group_member; + // check data correctness + { + QueryResult* result = WorldDatabase.PQuery("SELECT guid FROM creature WHERE guid = %u", group_member->leaderGUID); + if(!result) + { + sLog.outErrorDb("creature_formations table leader guid %u incorrect (not exist)", group_member->leaderGUID); + continue; + } - if(lastLeaderGUID != group_member->leaderGUID) - lastLeaderGUID = group_member->leaderGUID; - } + result = WorldDatabase.PQuery("SELECT guid FROM creature WHERE guid = %u", memberGUID); + if(!result) + { + sLog.outErrorDb("creature_formations table member guid %u incorrect (not exist)", memberGUID); + continue; + } + } + + CreatureGroupMap[memberGUID] = group_member; + } while(result->NextRow()) ; sLog.outString(); @@ -126,90 +149,104 @@ void CreatureGroupManager::LoadCreatureFormations() void CreatureGroup::AddMember(Creature *member) { - sLog.outDebug("Adding unit GUID: %u.", member->GetGUIDLow()); + sLog.outDebug("CreatureGroup::AddMember: Adding unit GUIDLow: %u.", member->GetGUIDLow()); - uint64 guid = member->GetGUID(); - //We first search creature member if exist - if(CreatureGroupMembers.find(guid) != CreatureGroupMembers.end()) - return; - //Check is that shit is a leader - if(member->IsFormationLeader()) + //Check if it is a leader + if(member->GetDBTableGUIDLow() == m_groupID) { sLog.outDebug("Unit GUID: %u is formation leader. Adding group.", member->GetGUIDLow()); m_leader = member; } - //Add to the club - CreatureGroupMembers[guid] = member; - sLog.outDebug("Unit GUID: %u added.", member->GetGUID()); + + m_members[member] = CreatureGroupMap.find(member->GetDBTableGUIDLow())->second; + member->SetFormation(this); } -void CreatureGroup::RemoveMember(uint64 guid) +void CreatureGroup::RemoveMember(Creature *member) { - if(CreatureGroupMembers.find(guid) != CreatureGroupMembers.end()) - CreatureGroupMembers.erase(guid); + if(m_leader == member) + m_leader = NULL; + + m_members.erase(member); + member->SetFormation(NULL); } -void CreatureGroup::MemberHasAttacked(Creature *member) +void CreatureGroup::MemberAttackStart(Creature *member, Unit *target) { - uint8 groupAI = CreatureGroupMap[member->GetDBTableGUIDLow()]->groupAI;; + uint8 groupAI = CreatureGroupMap[member->GetDBTableGUIDLow()]->groupAI; + if(!groupAI) + return; - for(UNORDERED_MAP<uint64, Creature*>::iterator itr = CreatureGroupMembers.begin(); itr != CreatureGroupMembers.end(); itr++) + if(groupAI == 1 && member != m_leader) + return; + + for(CreatureGroupMemberType::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) { - sLog.outDebug("AI:%u:Group member found: %u, attacked by %s.", groupAI, itr->second->GetGUIDLow(), member->getVictim()->GetName()); - if(itr->second) - { - switch(groupAI) - { - case 0: - return; - case 1: - if(!member->IsFormationLeader()) - return; - } - //Attack if everything is OK - itr->second->AI()->AttackStart(member->getVictim()); - } + sLog.outDebug("GROUP ATTACK: group instance id %u calls member instid %u", m_leader->GetInstanceId(), member->GetInstanceId()); + //sLog.outDebug("AI:%u:Group member found: %u, attacked by %s.", groupAI, itr->second->GetGUIDLow(), member->getVictim()->GetName()); + + //Skip one check + if(itr->first == member) + continue; + + if(!itr->first->isAlive()) + continue; + + if(itr->first->getVictim()) + continue; + + if(itr->first->canAttack(target)) + itr->first->AI()->AttackStart(target); } } -void CreatureGroup::LeaderMovedInEvade() +void CreatureGroup::FormationReset(bool dismiss) { - for(UNORDERED_MAP<uint64, Creature*>::iterator itr = CreatureGroupMembers.begin(); itr != CreatureGroupMembers.end(); itr++) + for(CreatureGroupMemberType::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) { - if(itr->second && itr->second->isAlive() && !itr->second->isInCombat() && !itr->second->getVictim()) - SetMemberDestination(itr->second); + if(itr->first != m_leader && itr->first->isAlive()) + { + if(dismiss) + itr->first->GetMotionMaster()->Initialize(); + else + itr->first->GetMotionMaster()->MoveIdle(MOTION_SLOT_IDLE); + sLog.outDebug("Set %s movement for member GUID: %u", dismiss ? "default" : "idle", itr->first->GetGUIDLow()); + } } + m_Formed = !dismiss; } -void CreatureGroup::SetMemberDestination(Creature *member) +void CreatureGroup::LeaderMoveTo(float x, float y, float z) { - if(!member || !m_leader || member->IsFormationLeader()) + if(!m_leader) return; - float x,y,z; - uint32 lowguid = member->GetDBTableGUIDLow(); - - UNORDERED_MAP<uint32, FormationMember*>::iterator itr2 = CreatureGroupMap.find(lowguid); + float pathangle = atan2(m_leader->GetPositionY() - y, m_leader->GetPositionX() - x); - if(m_leader->GetMotionMaster()->GetDestination(x,y,z) && itr2 != CreatureGroupMap.end()) + for(CreatureGroupMemberType::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) { - float dist, angle, pathangle; + Creature *member = itr->first; + if(member == m_leader || !member->isAlive() || member->getVictim()) + continue; - angle = CreatureGroupMap[lowguid]->follow_angle; - dist = CreatureGroupMap[lowguid]->follow_dist; - pathangle = atan2(m_leader->GetPositionY()-y, m_leader->GetPositionX()-x); + float angle = itr->second->follow_angle; + float dist = itr->second->follow_dist; - x = x+cos(angle+pathangle)*dist; - y = y+sin(angle+pathangle)*dist; + float dx = x + cos(angle + pathangle) * dist; + float dy = y + sin(angle + pathangle) * dist; + float dz = z; - member->GetMotionMaster()->Clear(); + Trinity::NormalizeMapCoord(dx); + Trinity::NormalizeMapCoord(dy); - if(member->GetDistance(m_leader) < dist+MAX_DESYNC) + member->UpdateGroundPositionZ(dx, dy, dz); + + if(member->GetDistance(m_leader) < dist + MAX_DESYNC) member->SetUnitMovementFlags(m_leader->GetUnitMovementFlags()); else member->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); - member->GetMotionMaster()->MovePoint(0, x, y, z); + member->GetMotionMaster()->MovePoint(0, dx, dy, dz); + member->SetHomePosition(dx, dy, dz, pathangle); } } - diff --git a/src/game/CreatureGroups.h b/src/game/CreatureGroups.h index dbf8dcec645..8c345aadd40 100644 --- a/src/game/CreatureGroups.h +++ b/src/game/CreatureGroups.h @@ -1,7 +1,7 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,49 +21,58 @@ #ifndef _FORMATIONS_H #define _FORMATIONS_H -#include <map> -#include "Creature.h" -#include "Policies/Singleton.h" +#include "Common.h" -struct FormationMember +class CreatureGroup; + +struct FormationInfo { - float follow_dist; - float follow_angle; - uint32 memberGUID; uint32 leaderGUID; + float follow_dist; + float follow_angle; uint8 groupAI; }; class CreatureGroupManager { public: - void UpdateCreatureGroup(uint32 group_id, Creature *member); - void DestroyGroup(uint32 group_id, uint64 guid); + void AddCreatureToGroup(uint32 group_id, Creature *creature); + void RemoveCreatureFromGroup(CreatureGroup *group, Creature *creature); void LoadCreatureFormations(); }; +typedef UNORDERED_MAP<uint32/*memberDBGUID*/, FormationInfo*> CreatureGroupInfoType; + +extern CreatureGroupInfoType CreatureGroupMap; + class CreatureGroup { - UNORDERED_MAP<uint64, Creature*>CreatureGroupMembers; - Creature *m_leader; //Important do not forget sometimes to work with pointers instead synonims :D:D + private: + Creature *m_leader; //Important do not forget sometimes to work with pointers instead synonims :D:D + typedef std::map<Creature*, FormationInfo*> CreatureGroupMemberType; + CreatureGroupMemberType m_members; + uint32 m_groupID; + bool m_Formed; + public: - CreatureGroup() : m_leader(NULL) {} - ~CreatureGroup(){sLog.outDebug("Destroying group");} - void AddMember(Creature *); - void RemoveMember(uint64 guid); - void LeaderMovedInEvade(); - void MemberHasAttacked(Creature *); - void SetMemberDestination(Creature *); - bool isEmpty() {return CreatureGroupMembers.empty();} -}; + //Group cannot be created empty + explicit CreatureGroup(uint32 id) : m_groupID(id), m_leader(NULL), m_Formed(false) {} + ~CreatureGroup() { sLog.outDebug("Destroying group"); } + + Creature* getLeader() const { return m_leader; } + uint32 GetId() const { return m_groupID; } + bool isEmpty() const { return m_members.empty(); } + bool isFormed() const { return m_Formed; } -typedef UNORDERED_MAP<uint32, CreatureGroup*> CreatureGroupHolderType; + void AddMember(Creature *member); + void RemoveMember(Creature *member); + void FormationReset(bool dismiss); -extern CreatureGroupHolderType CreatureGroupHolder; -extern UNORDERED_MAP<uint32, FormationMember*> CreatureGroupMap; + void LeaderMoveTo(float x, float y, float z); + void MemberAttackStart(Creature* member, Unit *target); +}; #define formation_mgr Trinity::Singleton<CreatureGroupManager>::Instance() #endif - diff --git a/src/game/DBCEnums.h b/src/game/DBCEnums.h new file mode 100644 index 00000000000..078804c19f4 --- /dev/null +++ b/src/game/DBCEnums.h @@ -0,0 +1,331 @@ +/* +* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef DBCENUMS_H +#define DBCENUMS_H + +// client supported max level for player/pets/etc. Avoid overflow or client stability affected. +// also see GT_MAX_LEVEL define +#define MAX_LEVEL 100 + +// Server side limitation. Base at used code requirements. +// also see MAX_LEVEL and GT_MAX_LEVEL define +#define STRONG_MAX_LEVEL 255 + +enum AreaTeams +{ + AREATEAM_NONE = 0, + AREATEAM_ALLY = 2, + AREATEAM_HORDE = 4 +}; + +enum AchievementFactionFlags +{ + ACHIEVEMENT_FACTION_FLAG_HORDE = 0x00000000, + ACHIEVEMENT_FACTION_FLAG_ALLIANCE = 0x00000001, +}; + +enum AchievementFlags +{ + ACHIEVEMENT_FLAG_COUNTER = 0x00000001, // Just count statistic (never stop and complete) + ACHIEVEMENT_FLAG_UNK2 = 0x00000002, // not used + ACHIEVEMENT_FLAG_STORE_MAX_VALUE = 0x00000004, // Store only max value? used only in "Reach level xx" + ACHIEVEMENT_FLAG_SUMM = 0x00000008, // Use summ criteria value from all reqirements (and calculate max value) + ACHIEVEMENT_FLAG_MAX_USED = 0x00000010, // Show max criteria (and calculate max value ??) + ACHIEVEMENT_FLAG_REQ_COUNT = 0x00000020, // Use not zero req count (and calculate max value) + ACHIEVEMENT_FLAG_AVERANGE = 0x00000040, // Show as averange value (value / time_in_days) depend from other flag (by def use last criteria value) + ACHIEVEMENT_FLAG_BAR = 0x00000080, // Show as progress bar (value / max vale) depend from other flag (by def use last criteria value) + ACHIEVEMENT_FLAG_REALM_FIRST_REACH = 0x00000100, // + ACHIEVEMENT_FLAG_REALM_FIRST_KILL = 0x00000200, // +}; + +enum AchievementCriteriaCondition +{ + ACHIEVEMENT_CRITERIA_CONDITION_NONE = 0, + ACHIEVEMENT_CRITERIA_CONDITION_NO_DEATH = 1, + ACHIEVEMENT_CRITERIA_CONDITION_UNK1 = 2, // only used in "Complete a daily quest every day for five consecutive days" + ACHIEVEMENT_CRITERIA_CONDITION_MAP = 3, // requires you to be on specific map + ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE = 4, // only used in "Win 10 arenas without losing" + ACHIEVEMENT_CRITERIA_CONDITION_UNK2 = 9, // unk + ACHIEVEMENT_CRITERIA_CONDITION_UNK3 = 13, // unk +}; + +enum AchievementCriteriaCompletionFlags +{ + ACHIEVEMENT_CRITERIA_FLAG_SHOW_PROGRESS_BAR = 0x00000001, // Show progress as bar + ACHIEVEMENT_CRITERIA_FLAG_HIDE_CRITERIA = 0x00000002, // Not show criteria in client + ACHIEVEMENT_CRITERIA_FLAG_UNK3 = 0x00000004, // BG related?? + ACHIEVEMENT_CRITERIA_FLAG_UNK4 = 0x00000008, // + ACHIEVEMENT_CRITERIA_FLAG_UNK5 = 0x00000010, // not used + ACHIEVEMENT_CRITERIA_FLAG_MONEY_COUNTER = 0x00000020, // Displays counter as money +}; + +enum AchievementCriteriaGroupFlags +{ + // you mustn't be in a group while fulfilling this achievement + ACHIEVEMENT_CRITERIA_GROUP_NOT_IN_GROUP = 2, +}; + +enum AchievementCriteriaTypes +{ + ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE = 0, + ACHIEVEMENT_CRITERIA_TYPE_WIN_BG = 1, + ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL = 5, + ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL = 7, + ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT = 8, + ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT = 9, + // you have to complete a daily quest x times in a row + ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY = 10, + ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE = 11, + ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE = 13, + ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST = 14, + ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND= 15, + ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP= 16, + ACHIEVEMENT_CRITERIA_TYPE_DEATH= 17, + ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON = 18, + ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID = 19, + ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE = 20, + ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER = 23, + ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING = 24, + ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM = 26, + ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST = 27, + ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET = 28, + ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL= 29, + ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE = 30, + ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA = 31, + ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA = 32, + ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA = 33, + ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL = 34, + // TODO: this criteria has additional conditions which can not be found in the dbcs + ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL = 35, + ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM = 36, + // TODO: the archievements 1162 and 1163 requires a special rating which can't be found in the dbc + ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA = 37, + ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING = 38, + ACHIEVEMENT_CRITERIA_TYPE_REACH_TEAM_RATING = 39, + ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL = 40, + ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM = 41, + ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM= 42, + ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA = 43, + ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK= 44, + ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT= 45, + ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION= 46, + ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION= 47, + // noted: rewarded as soon as the player payed, not at taking place at the seat + ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP= 48, + ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM = 49, + // TODO: itemlevel is mentioned in text but not present in dbc + ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT = 50, + ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT= 51, + ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS = 52, + ACHIEVEMENT_CRITERIA_TYPE_HK_RACE = 53, + ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE = 54, + ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE = 55, + // TODO: in some cases map not present, and in some cases need do without die + ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS = 56, + ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM = 57, + ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS = 59, + ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS = 60, + ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS = 61, + ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD = 62, + ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING = 63, + ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER = 65, + ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL = 66, + ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY = 67, + ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT = 68, + ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2= 69, + ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL= 70, + ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT = 72, + // TODO: title id is not mentioned in dbc + ACHIEVEMENT_CRITERIA_TYPE_EARNED_PVP_TITLE = 74, + ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS= 75, + ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL = 76, + ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL = 77, + // TODO: creature type (demon, undead etc.) is not stored in dbc + ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE = 78, + ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS= 80, + ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION= 82, + ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID= 83, + ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS= 84, + ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD = 85, + ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED = 86, + ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION = 87, + ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION = 88, + ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS = 89, + ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM = 90, + ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM = 91, + ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED = 93, + ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED = 94, + ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH = 95, + ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_POWER = 96, + ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_STAT = 97, + ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER = 98, + ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR = 99, + ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_RATING = 100, + ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_DEALT = 101, + ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_RECEIVED = 102, + ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED = 103, + ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CASTED = 104, + ACHIEVEMENT_CRITERIA_TYPE_TOTAL_HEALING_RECEIVED = 105, + ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED = 106, + ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED = 107, + ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN = 108, + ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE = 109, + // TODO: target entry is missing + ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2 = 110, + ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE= 112, + ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL = 113, + ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS = 114, + // 0..114 => 115 criteria types total + ACHIEVEMENT_CRITERIA_TYPE_TOTAL = 115, +}; + +enum AreaFlags +{ + AREA_FLAG_SNOW = 0x00000001, // snow (only Dun Morogh, Naxxramas, Razorfen Downs and Winterspring) + AREA_FLAG_UNK1 = 0x00000002, // may be necropolis? + AREA_FLAG_UNK2 = 0x00000004, // Only used for areas on map 571 (development before) + AREA_FLAG_SLAVE_CAPITAL = 0x00000008, // city and city subsones + AREA_FLAG_UNK3 = 0x00000010, // can't find common meaning + AREA_FLAG_SLAVE_CAPITAL2 = 0x00000020, // slave capital city flag? + AREA_FLAG_UNK4 = 0x00000040, // many zones have this flag + AREA_FLAG_ARENA = 0x00000080, // arena, both instanced and world arenas + AREA_FLAG_CAPITAL = 0x00000100, // main capital city flag + AREA_FLAG_CITY = 0x00000200, // only for one zone named "City" (where it located?) + AREA_FLAG_OUTLAND = 0x00000400, // expansion zones? (only Eye of the Storm not have this flag, but have 0x00004000 flag) + AREA_FLAG_SANCTUARY = 0x00000800, // sanctuary area (PvP disabled) + AREA_FLAG_NEED_FLY = 0x00001000, // only Netherwing Ledge, Socrethar's Seat, Tempest Keep, The Arcatraz, The Botanica, The Mechanar, Sorrow Wing Point, Dragonspine Ridge, Netherwing Mines, Dragonmaw Base Camp, Dragonmaw Skyway + AREA_FLAG_UNUSED1 = 0x00002000, // not used now (no area/zones with this flag set in 3.0.3) + AREA_FLAG_OUTLAND2 = 0x00004000, // expansion zones? (only Circle of Blood Arena not have this flag, but have 0x00000400 flag) + AREA_FLAG_PVP = 0x00008000, // pvp objective area? (Death's Door also has this flag although it's no pvp object area) + AREA_FLAG_ARENA_INSTANCE = 0x00010000, // used by instanced arenas only + AREA_FLAG_UNUSED2 = 0x00020000, // not used now (no area/zones with this flag set in 3.0.3) + AREA_FLAG_UNK5 = 0x00040000, // only used for Amani Pass, Hatchet Hills + AREA_FLAG_UNK6 = 0x00080000, // Valgarde and Acherus: The Ebon Hold + AREA_FLAG_LOWLEVEL = 0x00100000, // used for some starting areas with area_level <=15 + AREA_FLAG_TOWN = 0x00200000, // small towns with Inn + AREA_FLAG_UNK7 = 0x00400000, // Warsong Hold, Acherus: The Ebon Hold, New Agamand Inn, Vengeance Landing Inn + AREA_FLAG_UNK8 = 0x00800000, // Westguard Inn, Acherus: The Ebon Hold, Valgarde + AREA_FLAG_OUTDOOR_PVP = 0x01000000, // Wintergrasp and it's subzones + AREA_FLAG_UNK9 = 0x02000000, // unknown + AREA_FLAG_UNK10 = 0x04000000, // unknown + AREA_FLAG_OUTDOOR_PVP2 = 0x08000000 // Wintergrasp and it's subzones +}; + +enum FactionTemplateFlags +{ + FACTION_TEMPLATE_FLAG_CONTESTED_GUARD = 0x00001000, // faction will attack players that were involved in PvP combats +}; + +enum FactionMasks +{ + FACTION_MASK_PLAYER = 1, // any player + FACTION_MASK_ALLIANCE = 2, // player or creature from alliance team + FACTION_MASK_HORDE = 4, // player or creature from horde team + FACTION_MASK_MONSTER = 8 // aggressive creature from monster team + // if none flags set then non-aggressive creature +}; + +enum MapTypes +{ + MAP_COMMON = 0, + MAP_INSTANCE = 1, + MAP_RAID = 2, + MAP_BATTLEGROUND = 3, + MAP_ARENA = 4 +}; + +enum AbilytyLearnType +{ + ABILITY_LEARNED_ON_GET_PROFESSION_SKILL = 1, + ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL = 2 +}; + +enum ItemEnchantmentType +{ + ITEM_ENCHANTMENT_TYPE_NONE = 0, + ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL = 1, + ITEM_ENCHANTMENT_TYPE_DAMAGE = 2, + ITEM_ENCHANTMENT_TYPE_EQUIP_SPELL = 3, + ITEM_ENCHANTMENT_TYPE_RESISTANCE = 4, + ITEM_ENCHANTMENT_TYPE_STAT = 5, + ITEM_ENCHANTMENT_TYPE_TOTEM = 6, + ITEM_ENCHANTMENT_TYPE_USE_SPELL = 7, + ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET = 8 +}; + +enum TotemCategoryType +{ + TOTEM_CATEGORY_TYPE_KNIFE = 1, + TOTEM_CATEGORY_TYPE_TOTEM = 2, + TOTEM_CATEGORY_TYPE_ROD = 3, + TOTEM_CATEGORY_TYPE_PICK = 21, + TOTEM_CATEGORY_TYPE_STONE = 22, + TOTEM_CATEGORY_TYPE_HAMMER = 23, + TOTEM_CATEGORY_TYPE_SPANNER = 24 +}; + +// SummonProperties.dbc, col 1 +/*enum SummonGroup +{ + SUMMON_GROUP_UNKNOWN1 = 0, // 1160 spells in 3.0.3 + SUMMON_GROUP_UNKNOWN2 = 1, // 861 spells in 3.0.3 + SUMMON_GROUP_PETS = 2, // 52 spells in 3.0.3, pets mostly + SUMMON_GROUP_CONTROLLABLE = 3, // 13 spells in 3.0.3, mostly controllable + SUMMON_GROUP_UNKNOWN3 = 4 // 86 spells in 3.0.3, taxi/mounts +}; + +// SummonProperties.dbc, col 3 +enum SummonType +{ + SUMMON_TYPE_UNKNOWN = 0, // different summons, 1330 spells in 3.0.3 + SUMMON_TYPE_SUMMON = 1, // generic summons, 49 spells in 3.0.3 + SUMMON_TYPE_GUARDIAN = 2, // summon guardian, 393 spells in 3.0.3 + SUMMON_TYPE_ARMY = 3, // summon army, 5 spells in 3.0.3 + SUMMON_TYPE_TOTEM = 4, // summon totem, 169 spells in 3.0.3 + SUMMON_TYPE_CRITTER = 5, // critter/minipet, 195 spells in 3.0.3 + SUMMON_TYPE_DK = 6, // summon DRW/Ghoul, 2 spells in 3.0.3 + SUMMON_TYPE_BOMB = 7, // summon bot/bomb, 4 spells in 3.0.3 + SUMMON_TYPE_PHASING = 8, // something todo with DK prequest line, 2 spells in 3.0.3 + SUMMON_TYPE_SIEGE_VEH = 9, // summon different vehicles, 14 spells in 3.0.3 + SUMMON_TYPE_DRAKE_VEH = 10, // summon drake (vehicle), 3 spells + SUMMON_TYPE_LIGHTWELL = 11 // summon lightwell, 6 spells in 3.0.3 +}; + +// SummonProperties.dbc, col 5 +enum SummonFlags +{ + SUMMON_FLAG_NONE = 0x0000, // 1342 spells in 3.0.3 + SUMMON_FLAG_UNK1 = 0x0001, // 75 spells in 3.0.3, something unfriendly + SUMMON_FLAG_UNK2 = 0x0002, // 616 spells in 3.0.3, something friendly + SUMMON_FLAG_UNK3 = 0x0004, // 22 spells in 3.0.3, no idea... + SUMMON_FLAG_UNK4 = 0x0008, // 49 spells in 3.0.3, some mounts + SUMMON_FLAG_UNK5 = 0x0010, // 25 spells in 3.0.3, quest related? + SUMMON_FLAG_UNK6 = 0x0020, // 0 spells in 3.0.3, unused + SUMMON_FLAG_UNK7 = 0x0040, // 12 spells in 3.0.3, no idea + SUMMON_FLAG_UNK8 = 0x0080, // 4 spells in 3.0.3, no idea + SUMMON_FLAG_UNK9 = 0x0100, // 51 spells in 3.0.3, no idea, many quest related + SUMMON_FLAG_UNK10 = 0x0200, // 51 spells in 3.0.3, something defensive + SUMMON_FLAG_UNK11 = 0x0400, // 3 spells, requires something near? + SUMMON_FLAG_UNK12 = 0x0800, // 30 spells in 3.0.3, no idea + SUMMON_FLAG_UNK13 = 0x1000, // 8 spells in 3.0.3, siege vehicle + SUMMON_FLAG_UNK14 = 0x2000, // 2 spells in 3.0.3, escort? +}; +*/ +#endif + diff --git a/src/game/DBCStores.cpp b/src/game/DBCStores.cpp new file mode 100644 index 00000000000..4d1ba2a5cff --- /dev/null +++ b/src/game/DBCStores.cpp @@ -0,0 +1,736 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "DBCStores.h" +#include "Policies/SingletonImp.h" +#include "Log.h" +#include "ProgressBar.h" +#include "SharedDefines.h" + +#include "DBCfmt.h" + +#include <map> + +typedef std::map<uint16,uint32> AreaFlagByAreaID; +typedef std::map<uint32,uint32> AreaFlagByMapID; + +DBCStorage <AreaTableEntry> sAreaStore(AreaTableEntryfmt); +DBCStorage <AreaGroupEntry> sAreaGroupStore(AreaGroupEntryfmt); +static AreaFlagByAreaID sAreaFlagByAreaID; +static AreaFlagByMapID sAreaFlagByMapID; // for instances without generated *.map files + +DBCStorage <AchievementEntry> sAchievementStore(Achievementfmt); +DBCStorage <AchievementCriteriaEntry> sAchievementCriteriaStore(AchievementCriteriafmt); +DBCStorage <AreaTriggerEntry> sAreaTriggerStore(AreaTriggerEntryfmt); +DBCStorage <AuctionHouseEntry> sAuctionHouseStore(AuctionHouseEntryfmt); +DBCStorage <BankBagSlotPricesEntry> sBankBagSlotPricesStore(BankBagSlotPricesEntryfmt); +DBCStorage <BattlemasterListEntry> sBattlemasterListStore(BattlemasterListEntryfmt); +DBCStorage <BarberShopStyleEntry> sBarberShopStyleStore(BarberShopStyleEntryfmt); +DBCStorage <CharStartOutfitEntry> sCharStartOutfitStore(CharStartOutfitEntryfmt); +DBCStorage <CharTitlesEntry> sCharTitlesStore(CharTitlesEntryfmt); +DBCStorage <ChatChannelsEntry> sChatChannelsStore(ChatChannelsEntryfmt); +DBCStorage <ChrClassesEntry> sChrClassesStore(ChrClassesEntryfmt); +DBCStorage <ChrRacesEntry> sChrRacesStore(ChrRacesEntryfmt); +DBCStorage <CinematicSequencesEntry> sCinematicSequencesStore(CinematicSequencesEntryfmt); +DBCStorage <CreatureDisplayInfoEntry> sCreatureDisplayInfoStore(CreatureDisplayInfofmt); +DBCStorage <CreatureFamilyEntry> sCreatureFamilyStore(CreatureFamilyfmt); +DBCStorage <CreatureSpellDataEntry> sCreatureSpellDataStore(CreatureSpellDatafmt); +DBCStorage <CreatureTypeEntry> sCreatureTypeStore(CreatureTypefmt); +DBCStorage <CurrencyTypesEntry> sCurrencyTypesStore(CurrencyTypesfmt); + +DBCStorage <DurabilityQualityEntry> sDurabilityQualityStore(DurabilityQualityfmt); +DBCStorage <DurabilityCostsEntry> sDurabilityCostsStore(DurabilityCostsfmt); + +DBCStorage <EmotesTextEntry> sEmotesTextStore(EmoteEntryfmt); + +typedef std::map<uint32,SimpleFactionsList> FactionTeamMap; +static FactionTeamMap sFactionTeamMap; +DBCStorage <FactionEntry> sFactionStore(FactionEntryfmt); +DBCStorage <FactionTemplateEntry> sFactionTemplateStore(FactionTemplateEntryfmt); + +DBCStorage <GemPropertiesEntry> sGemPropertiesStore(GemPropertiesEntryfmt); +DBCStorage <GlyphPropertiesEntry> sGlyphPropertiesStore(GlyphPropertiesfmt); +DBCStorage <GlyphSlotEntry> sGlyphSlotStore(GlyphSlotfmt); + +DBCStorage <GtBarberShopCostBaseEntry> sGtBarberShopCostBaseStore(GtBarberShopCostBasefmt); +DBCStorage <GtCombatRatingsEntry> sGtCombatRatingsStore(GtCombatRatingsfmt); +DBCStorage <GtChanceToMeleeCritBaseEntry> sGtChanceToMeleeCritBaseStore(GtChanceToMeleeCritBasefmt); +DBCStorage <GtChanceToMeleeCritEntry> sGtChanceToMeleeCritStore(GtChanceToMeleeCritfmt); +DBCStorage <GtChanceToSpellCritBaseEntry> sGtChanceToSpellCritBaseStore(GtChanceToSpellCritBasefmt); +DBCStorage <GtChanceToSpellCritEntry> sGtChanceToSpellCritStore(GtChanceToSpellCritfmt); +DBCStorage <GtOCTRegenHPEntry> sGtOCTRegenHPStore(GtOCTRegenHPfmt); +//DBCStorage <GtOCTRegenMPEntry> sGtOCTRegenMPStore(GtOCTRegenMPfmt); -- not used currently +DBCStorage <GtRegenHPPerSptEntry> sGtRegenHPPerSptStore(GtRegenHPPerSptfmt); +DBCStorage <GtRegenMPPerSptEntry> sGtRegenMPPerSptStore(GtRegenMPPerSptfmt); + +DBCStorage <HolidaysEntry> sHolidaysStore(Holidaysfmt); + +DBCStorage <ItemEntry> sItemStore(Itemfmt); +DBCStorage <ItemBagFamilyEntry> sItemBagFamilyStore(ItemBagFamilyfmt); +//DBCStorage <ItemCondExtCostsEntry> sItemCondExtCostsStore(ItemCondExtCostsEntryfmt); +//DBCStorage <ItemDisplayInfoEntry> sItemDisplayInfoStore(ItemDisplayTemplateEntryfmt); -- not used currently +DBCStorage <ItemExtendedCostEntry> sItemExtendedCostStore(ItemExtendedCostEntryfmt); +DBCStorage <ItemLimitCategoryEntry> sItemLimitCategoryStore(ItemLimitCategoryEntryfmt); +DBCStorage <ItemRandomPropertiesEntry> sItemRandomPropertiesStore(ItemRandomPropertiesfmt); +DBCStorage <ItemRandomSuffixEntry> sItemRandomSuffixStore(ItemRandomSuffixfmt); +DBCStorage <ItemSetEntry> sItemSetStore(ItemSetEntryfmt); + +DBCStorage <LockEntry> sLockStore(LockEntryfmt); + +DBCStorage <MailTemplateEntry> sMailTemplateStore(MailTemplateEntryfmt); +DBCStorage <MapEntry> sMapStore(MapEntryfmt); +DBCStorage <MovieEntry> sMovieStore(MovieEntryfmt); + +DBCStorage <QuestSortEntry> sQuestSortStore(QuestSortEntryfmt); + +DBCStorage <RandomPropertiesPointsEntry> sRandomPropertiesPointsStore(RandomPropertiesPointsfmt); +DBCStorage <ScalingStatDistributionEntry> sScalingStatDistributionStore(ScalingStatDistributionfmt); +DBCStorage <ScalingStatValuesEntry> sScalingStatValuesStore(ScalingStatValuesfmt); + +DBCStorage <SkillLineEntry> sSkillLineStore(SkillLinefmt); +DBCStorage <SkillLineAbilityEntry> sSkillLineAbilityStore(SkillLineAbilityfmt); + +DBCStorage <SoundEntriesEntry> sSoundEntriesStore(SoundEntriesfmt); + +DBCStorage <SpellItemEnchantmentEntry> sSpellItemEnchantmentStore(SpellItemEnchantmentfmt); +DBCStorage <SpellItemEnchantmentConditionEntry> sSpellItemEnchantmentConditionStore(SpellItemEnchantmentConditionfmt); +DBCStorage <SpellEntry> sSpellStore(SpellEntryfmt); +SpellCategoryStore sSpellCategoryStore; +PetFamilySpellsStore sPetFamilySpellsStore; + +DBCStorage <SpellCastTimesEntry> sSpellCastTimesStore(SpellCastTimefmt); +DBCStorage <SpellDurationEntry> sSpellDurationStore(SpellDurationfmt); +DBCStorage <SpellFocusObjectEntry> sSpellFocusObjectStore(SpellFocusObjectfmt); +DBCStorage <SpellRadiusEntry> sSpellRadiusStore(SpellRadiusfmt); +DBCStorage <SpellRangeEntry> sSpellRangeStore(SpellRangefmt); +DBCStorage <SpellRuneCostEntry> sSpellRuneCostStore(SpellRuneCostfmt); +DBCStorage <SpellShapeshiftEntry> sSpellShapeshiftStore(SpellShapeshiftfmt); +DBCStorage <StableSlotPricesEntry> sStableSlotPricesStore(StableSlotPricesfmt); +DBCStorage <SummonPropertiesEntry> sSummonPropertiesStore(SummonPropertiesfmt); +DBCStorage <TalentEntry> sTalentStore(TalentEntryfmt); +TalentSpellPosMap sTalentSpellPosMap; +DBCStorage <TalentTabEntry> sTalentTabStore(TalentTabEntryfmt); + +// store absolute bit position for first rank for talent inspect +typedef std::map<uint32,uint32> TalentInspectMap; +static TalentInspectMap sTalentPosInInspect; +static TalentInspectMap sTalentTabSizeInInspect; +static uint32 sTalentTabPages[12/*MAX_CLASSES*/][3]; + +DBCStorage <TaxiNodesEntry> sTaxiNodesStore(TaxiNodesEntryfmt); +TaxiMask sTaxiNodesMask; +TaxiMask sOldContinentsNodesMask; + +// DBC used only for initialization sTaxiPathSetBySource at startup. +TaxiPathSetBySource sTaxiPathSetBySource; +DBCStorage <TaxiPathEntry> sTaxiPathStore(TaxiPathEntryfmt); + +// DBC used only for initialization sTaxiPathSetBySource at startup. +TaxiPathNodesByPath sTaxiPathNodesByPath; + +static DBCStorage <TaxiPathNodeEntry> sTaxiPathNodeStore(TaxiPathNodeEntryfmt); +DBCStorage <TotemCategoryEntry> sTotemCategoryStore(TotemCategoryEntryfmt); +DBCStorage <VehicleEntry> sVehicleStore(VehicleEntryfmt); +DBCStorage <VehicleSeatEntry> sVehicleSeatStore(VehicleSeatEntryfmt); +DBCStorage <WorldMapAreaEntry> sWorldMapAreaStore(WorldMapAreaEntryfmt); +DBCStorage <WorldSafeLocsEntry> sWorldSafeLocsStore(WorldSafeLocsEntryfmt); +DBCStorage <WorldMapOverlayEntry> sWorldMapOverlayStore(WorldMapOverlayEntryfmt); + +typedef std::list<std::string> StoreProblemList; + +static bool LoadDBC_assert_print(uint32 fsize,uint32 rsize, const std::string& filename) +{ + sLog.outError("ERROR: Size of '%s' setted by format string (%u) not equal size of C++ structure (%u).",filename.c_str(),fsize,rsize); + + // assert must fail after function call + return false; +} + +template<class T> +inline void LoadDBC(uint32& availableDbcLocales,barGoLink& bar, StoreProblemList& errlist, DBCStorage<T>& storage, const std::string& dbc_path, const std::string& filename) +{ + // compatibility format and C++ structure sizes + assert(DBCFileLoader::GetFormatRecordSize(storage.GetFormat()) == sizeof(T) || LoadDBC_assert_print(DBCFileLoader::GetFormatRecordSize(storage.GetFormat()),sizeof(T),filename)); + + std::string dbc_filename = dbc_path + filename; + if(storage.Load(dbc_filename.c_str())) + { + bar.step(); + for(uint8 i = 0; i < MAX_LOCALE; ++i) + { + if(!(availableDbcLocales & (1 << i))) + continue; + + std::string dbc_filename_loc = dbc_path + localeNames[i] + "/" + filename; + if(!storage.LoadStringsFrom(dbc_filename_loc.c_str())) + availableDbcLocales &= ~(1<<i); // mark as not available for speedup next checks + } + } + else + { + // sort problematic dbc to (1) non compatible and (2) non-existed + FILE * f=fopen(dbc_filename.c_str(),"rb"); + if(f) + { + char buf[100]; + snprintf(buf,100," (exist, but have %d fields instead %d) Wrong client version DBC file?",storage.GetFieldCount(),strlen(storage.GetFormat())); + errlist.push_back(dbc_filename + buf); + fclose(f); + } + else + errlist.push_back(dbc_filename); + } +} + +void LoadDBCStores(const std::string& dataPath) +{ + std::string dbcPath = dataPath+"dbc/"; + + const uint32 DBCFilesCount = 77; + + barGoLink bar( DBCFilesCount ); + + StoreProblemList bad_dbc_files; + uint32 availableDbcLocales = 0xFFFFFFFF; + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sAreaStore, dbcPath,"AreaTable.dbc"); + + // must be after sAreaStore loading + for(uint32 i = 0; i < sAreaStore.GetNumRows(); ++i) // areaflag numbered from 0 + { + if(AreaTableEntry const* area = sAreaStore.LookupEntry(i)) + { + // fill AreaId->DBC records + sAreaFlagByAreaID.insert(AreaFlagByAreaID::value_type(uint16(area->ID),area->exploreFlag)); + + // fill MapId->DBC records ( skip sub zones and continents ) + if(area->zone==0 && area->mapid != 0 && area->mapid != 1 && area->mapid != 530 && area->mapid != 571 ) + sAreaFlagByMapID.insert(AreaFlagByMapID::value_type(area->mapid,area->exploreFlag)); + } + } + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sAchievementStore, dbcPath,"Achievement.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sAchievementCriteriaStore, dbcPath,"Achievement_Criteria.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sAreaTriggerStore, dbcPath,"AreaTrigger.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sAreaGroupStore, dbcPath,"AreaGroup.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sAuctionHouseStore, dbcPath,"AuctionHouse.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sBankBagSlotPricesStore, dbcPath,"BankBagSlotPrices.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sBattlemasterListStore, dbcPath,"BattlemasterList.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sBarberShopStyleStore, dbcPath,"BarberShopStyle.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCharStartOutfitStore, dbcPath,"CharStartOutfit.dbc"); + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCharTitlesStore, dbcPath,"CharTitles.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sChatChannelsStore, dbcPath,"ChatChannels.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sChrClassesStore, dbcPath,"ChrClasses.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sChrRacesStore, dbcPath,"ChrRaces.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCinematicSequencesStore, dbcPath,"CinematicSequences.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCreatureDisplayInfoStore, dbcPath,"CreatureDisplayInfo.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCreatureFamilyStore, dbcPath,"CreatureFamily.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCreatureSpellDataStore, dbcPath,"CreatureSpellData.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCreatureTypeStore, dbcPath,"CreatureType.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCurrencyTypesStore, dbcPath,"CurrencyTypes.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sDurabilityCostsStore, dbcPath,"DurabilityCosts.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sDurabilityQualityStore, dbcPath,"DurabilityQuality.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sEmotesTextStore, dbcPath,"EmotesText.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sFactionStore, dbcPath,"Faction.dbc"); + for (uint32 i=0;i<sFactionStore.GetNumRows(); ++i) + { + FactionEntry const * faction = sFactionStore.LookupEntry(i); + if (faction && faction->team) + { + SimpleFactionsList &flist = sFactionTeamMap[faction->team]; + flist.push_back(i); + } + } + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sFactionTemplateStore, dbcPath,"FactionTemplate.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGemPropertiesStore, dbcPath,"GemProperties.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGlyphPropertiesStore, dbcPath,"GlyphProperties.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGlyphSlotStore, dbcPath,"GlyphSlot.dbc"); + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtBarberShopCostBaseStore,dbcPath,"gtBarberShopCostBase.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtCombatRatingsStore, dbcPath,"gtCombatRatings.dbc"); + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtChanceToMeleeCritBaseStore, dbcPath,"gtChanceToMeleeCritBase.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtChanceToMeleeCritStore, dbcPath,"gtChanceToMeleeCrit.dbc"); + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtChanceToSpellCritBaseStore, dbcPath,"gtChanceToSpellCritBase.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtChanceToSpellCritStore, dbcPath,"gtChanceToSpellCrit.dbc"); + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtOCTRegenHPStore, dbcPath,"gtOCTRegenHP.dbc"); + //LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtOCTRegenMPStore, dbcPath,"gtOCTRegenMP.dbc"); -- not used currently + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtRegenHPPerSptStore, dbcPath,"gtRegenHPPerSpt.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtRegenMPPerSptStore, dbcPath,"gtRegenMPPerSpt.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sHolidaysStore, dbcPath,"Holidays.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemStore, dbcPath,"Item.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemBagFamilyStore, dbcPath,"ItemBagFamily.dbc"); + //LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemDisplayInfoStore, dbcPath,"ItemDisplayInfo.dbc"); -- not used currently + //LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemCondExtCostsStore, dbcPath,"ItemCondExtCosts.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemExtendedCostStore, dbcPath,"ItemExtendedCost.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemLimitCategoryStore, dbcPath,"ItemLimitCategory.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemRandomPropertiesStore,dbcPath,"ItemRandomProperties.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemRandomSuffixStore, dbcPath,"ItemRandomSuffix.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemSetStore, dbcPath,"ItemSet.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sLockStore, dbcPath,"Lock.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sMailTemplateStore, dbcPath,"MailTemplate.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sMapStore, dbcPath,"Map.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sMovieStore, dbcPath,"Movie.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sQuestSortStore, dbcPath,"QuestSort.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sRandomPropertiesPointsStore, dbcPath,"RandPropPoints.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sScalingStatDistributionStore, dbcPath,"ScalingStatDistribution.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sScalingStatValuesStore, dbcPath,"ScalingStatValues.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSkillLineStore, dbcPath,"SkillLine.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSkillLineAbilityStore, dbcPath,"SkillLineAbility.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSoundEntriesStore, dbcPath,"SoundEntries.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellStore, dbcPath,"Spell.dbc"); + for(uint32 i = 1; i < sSpellStore.GetNumRows(); ++i) + { + SpellEntry const * spell = sSpellStore.LookupEntry(i); + if(spell && spell->Category) + sSpellCategoryStore[spell->Category].insert(i); + + /*// DBC not support uint64 fields but SpellEntry have SpellFamilyFlags mapped at 2 uint32 fields + // uint32 field already converted to bigendian if need, but must be swapped for correct uint64 bigendian view + #if TRINITY_ENDIAN == TRINITY_BIGENDIAN + std::swap(*((uint32*)(&spell->SpellFamilyFlags)),*(((uint32*)(&spell->SpellFamilyFlags))+1)); + #endif*/ + } + + for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j) + { + SkillLineAbilityEntry const *skillLine = sSkillLineAbilityStore.LookupEntry(j); + + if(!skillLine) + continue; + + SpellEntry const* spellInfo = sSpellStore.LookupEntry(skillLine->spellId); + + if(spellInfo && (spellInfo->Attributes & 0x1D0) == 0x1D0) + { + for (unsigned int i = 1; i < sCreatureFamilyStore.GetNumRows(); ++i) + { + CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(i); + if(!cFamily) + continue; + + if(skillLine->skillId != cFamily->skillLine[0] && skillLine->skillId != cFamily->skillLine[1]) + continue; + + sPetFamilySpellsStore[i].insert(spellInfo->Id); + } + } + } + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellCastTimesStore, dbcPath,"SpellCastTimes.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellDurationStore, dbcPath,"SpellDuration.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellFocusObjectStore, dbcPath,"SpellFocusObject.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellItemEnchantmentStore,dbcPath,"SpellItemEnchantment.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellItemEnchantmentConditionStore,dbcPath,"SpellItemEnchantmentCondition.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellRadiusStore, dbcPath,"SpellRadius.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellRangeStore, dbcPath,"SpellRange.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellRuneCostStore, dbcPath,"SpellRuneCost.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellShapeshiftStore, dbcPath,"SpellShapeshiftForm.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sStableSlotPricesStore, dbcPath,"StableSlotPrices.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSummonPropertiesStore, dbcPath,"SummonProperties.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTalentStore, dbcPath,"Talent.dbc"); + + // create talent spells set + for (unsigned int i = 0; i < sTalentStore.GetNumRows(); ++i) + { + TalentEntry const *talentInfo = sTalentStore.LookupEntry(i); + if (!talentInfo) continue; + for (int j = 0; j < MAX_TALENT_RANK; j++) + if(talentInfo->RankID[j]) + sTalentSpellPosMap[talentInfo->RankID[j]] = TalentSpellPos(i,j); + } + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTalentTabStore, dbcPath,"TalentTab.dbc"); + + // prepare fast data access to bit pos of talent ranks for use at inspecting + { + // fill table by amount of talent ranks and fill sTalentTabBitSizeInInspect + // store in with (row,col,talent)->size key for correct sorting by (row,col) + typedef std::map<uint32,uint32> TalentBitSize; + TalentBitSize sTalentBitSize; + for(uint32 i = 1; i < sTalentStore.GetNumRows(); ++i) + { + TalentEntry const *talentInfo = sTalentStore.LookupEntry(i); + if (!talentInfo) continue; + + TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab ); + if(!talentTabInfo) + continue; + + // find talent rank + uint32 curtalent_maxrank = 0; + for(uint32 k = MAX_TALENT_RANK; k > 0; --k) + { + if(talentInfo->RankID[k-1]) + { + curtalent_maxrank = k; + break; + } + } + + sTalentBitSize[(talentInfo->Row<<24) + (talentInfo->Col<<16)+talentInfo->TalentID] = curtalent_maxrank; + sTalentTabSizeInInspect[talentInfo->TalentTab] += curtalent_maxrank; + } + + // now have all max ranks (and then bit amount used for store talent ranks in inspect) + for(uint32 talentTabId = 1; talentTabId < sTalentTabStore.GetNumRows(); ++talentTabId) + { + TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentTabId ); + if(!talentTabInfo) + continue; + + // prevent memory corruption; otherwise cls will become 12 below + if ((talentTabInfo->ClassMask & CLASSMASK_ALL_PLAYABLE)==0) + continue; + + // store class talent tab pages + uint32 cls = 1; + for(uint32 m=1;!(m & talentTabInfo->ClassMask) && cls < MAX_CLASSES;m <<=1, ++cls) {} + + sTalentTabPages[cls][talentTabInfo->tabpage]=talentTabId; + + // add total amount bits for first rank starting from talent tab first talent rank pos. + uint32 pos = 0; + for(TalentBitSize::iterator itr = sTalentBitSize.begin(); itr != sTalentBitSize.end(); ++itr) + { + uint32 talentId = itr->first & 0xFFFF; + TalentEntry const *talentInfo = sTalentStore.LookupEntry( talentId ); + if(!talentInfo) + continue; + + if(talentInfo->TalentTab != talentTabId) + continue; + + sTalentPosInInspect[talentId] = pos; + pos+= itr->second; + } + } + } + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTaxiNodesStore, dbcPath,"TaxiNodes.dbc"); + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTaxiPathStore, dbcPath,"TaxiPath.dbc"); + for(uint32 i = 1; i < sTaxiPathStore.GetNumRows(); ++i) + if(TaxiPathEntry const* entry = sTaxiPathStore.LookupEntry(i)) + sTaxiPathSetBySource[entry->from][entry->to] = TaxiPathBySourceAndDestination(entry->ID,entry->price); + uint32 pathCount = sTaxiPathStore.GetNumRows(); + + //## TaxiPathNode.dbc ## Loaded only for initialization different structures + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTaxiPathNodeStore, dbcPath,"TaxiPathNode.dbc"); + // Calculate path nodes count + std::vector<uint32> pathLength; + pathLength.resize(pathCount); // 0 and some other indexes not used + for(uint32 i = 1; i < sTaxiPathNodeStore.GetNumRows(); ++i) + if(TaxiPathNodeEntry const* entry = sTaxiPathNodeStore.LookupEntry(i)) + { + if (pathLength[entry->path] < entry->index + 1) + pathLength[entry->path] = entry->index + 1; + } + // Set path length + sTaxiPathNodesByPath.resize(pathCount); // 0 and some other indexes not used + for(uint32 i = 1; i < sTaxiPathNodesByPath.size(); ++i) + sTaxiPathNodesByPath[i].resize(pathLength[i]); + // fill data + for(uint32 i = 1; i < sTaxiPathNodeStore.GetNumRows(); ++i) + if(TaxiPathNodeEntry const* entry = sTaxiPathNodeStore.LookupEntry(i)) + sTaxiPathNodesByPath[entry->path][entry->index] = TaxiPathNode(entry->mapid,entry->x,entry->y,entry->z,entry->actionFlag,entry->delay); + sTaxiPathNodeStore.Clear(); + + // Initialize global taxinodes mask + // include existed nodes that have at least single not spell base (scripted) path + { + std::set<uint32> spellPaths; + for(uint32 i = 1; i < sSpellStore.GetNumRows (); ++i) + if(SpellEntry const* sInfo = sSpellStore.LookupEntry (i)) + for(int j=0; j < 3; ++j) + if(sInfo->Effect[j]==123 /*SPELL_EFFECT_SEND_TAXI*/) + spellPaths.insert(sInfo->EffectMiscValue[j]); + + memset(sTaxiNodesMask,0,sizeof(sTaxiNodesMask)); + memset(sOldContinentsNodesMask,0,sizeof(sTaxiNodesMask)); + for(uint32 i = 1; i < sTaxiNodesStore.GetNumRows(); ++i) + { + TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(i); + if(!node) + continue; + + TaxiPathSetBySource::const_iterator src_i = sTaxiPathSetBySource.find(i); + if(src_i!=sTaxiPathSetBySource.end() && !src_i->second.empty()) + { + bool ok = false; + for(TaxiPathSetForSource::const_iterator dest_i = src_i->second.begin();dest_i != src_i->second.end(); ++dest_i) + { + // not spell path + if(spellPaths.find(dest_i->second.ID)==spellPaths.end()) + { + ok = true; + break; + } + } + + if(!ok) + continue; + } + + // valid taxi network node + uint8 field = (uint8)((i - 1) / 32); + uint32 submask = 1<<((i-1)%32); + sTaxiNodesMask[field] |= submask; + + // old continent node (+ nodes virtually at old continents, check explicitly to avoid loading map files for zone info) + if (node->map_id < 2 || i == 82 || i == 83 || i == 93 || i == 94) + sOldContinentsNodesMask[field] |= submask; + } + } + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTotemCategoryStore, dbcPath,"TotemCategory.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sVehicleStore, dbcPath,"Vehicle.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sVehicleSeatStore, dbcPath,"VehicleSeat.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sWorldMapAreaStore, dbcPath,"WorldMapArea.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sWorldSafeLocsStore, dbcPath,"WorldSafeLocs.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sWorldMapOverlayStore, dbcPath,"WorldMapOverlay.dbc"); + + // error checks + if(bad_dbc_files.size() >= DBCFilesCount ) + { + sLog.outError("\nIncorrect DataDir value in Trinityd.conf or ALL required *.dbc files (%d) not found by path: %sdbc",DBCFilesCount,dataPath.c_str()); + exit(1); + } + else if(!bad_dbc_files.empty() ) + { + std::string str; + for(std::list<std::string>::iterator i = bad_dbc_files.begin(); i != bad_dbc_files.end(); ++i) + str += *i + "\n"; + + sLog.outError("\nSome required *.dbc files (%u from %d) not found or not compatible:\n%s",(uint32)bad_dbc_files.size(),DBCFilesCount,str.c_str()); + exit(1); + } + + // Check loaded DBC files proper version + if( !sSpellStore.LookupEntry(62735) || // last added spell in 3.0.9 + !sMapStore.LookupEntry(624) || // last map added in 3.0.8a/3.0.9 + !sGemPropertiesStore.LookupEntry(1557) || // last gem property added in 3.0.8a/3.0.9 + !sItemExtendedCostStore.LookupEntry(2589) || // last item extended cost added in 3.0.8a/3.0.9 + !sCharTitlesStore.LookupEntry(144) || // last char title added in 3.0.8a/3.0.9 + !sAreaStore.LookupEntry(2769) || // last area (areaflag) added in 3.0.8a/3.0.9 + !sItemStore.LookupEntry(45037) ) // last client known item added in 3.0.9 + { + sLog.outError("\nYou have _outdated_ DBC files. Please extract correct versions from current using client."); + exit(1); + } + + sLog.outString(); + sLog.outString( ">> Initialized %d data stores", DBCFilesCount ); +} + +SimpleFactionsList const* GetFactionTeamList(uint32 faction) +{ + FactionTeamMap::const_iterator itr = sFactionTeamMap.find(faction); + if(itr==sFactionTeamMap.end()) + return NULL; + return &itr->second; +} + +char* GetPetName(uint32 petfamily, uint32 dbclang) +{ + if(!petfamily) + return NULL; + CreatureFamilyEntry const *pet_family = sCreatureFamilyStore.LookupEntry(petfamily); + if(!pet_family) + return NULL; + return pet_family->Name[dbclang]?pet_family->Name[dbclang]:NULL; +} + +TalentSpellPos const* GetTalentSpellPos(uint32 spellId) +{ + TalentSpellPosMap::const_iterator itr = sTalentSpellPosMap.find(spellId); + if(itr==sTalentSpellPosMap.end()) + return NULL; + + return &itr->second; +} + +uint32 GetTalentSpellCost(uint32 spellId) +{ + if(TalentSpellPos const* pos = GetTalentSpellPos(spellId)) + return pos->rank+1; + + return 0; +} + +int32 GetAreaFlagByAreaID(uint32 area_id) +{ + AreaFlagByAreaID::iterator i = sAreaFlagByAreaID.find(area_id); + if(i == sAreaFlagByAreaID.end()) + return -1; + + return i->second; +} + +AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id) +{ + int32 areaflag = GetAreaFlagByAreaID(area_id); + if(areaflag < 0) + return NULL; + + return sAreaStore.LookupEntry(areaflag ); +} + +AreaTableEntry const* GetAreaEntryByAreaFlagAndMap(uint32 area_flag,uint32 map_id) +{ + if(area_flag) + return sAreaStore.LookupEntry(area_flag); + + if(MapEntry const* mapEntry = sMapStore.LookupEntry(map_id)) + return GetAreaEntryByAreaID(mapEntry->linked_zone); + + return NULL; +} + +uint32 GetAreaFlagByMapId(uint32 mapid) +{ + AreaFlagByMapID::iterator i = sAreaFlagByMapID.find(mapid); + if(i == sAreaFlagByMapID.end()) + return 0; + else + return i->second; +} + +uint32 GetVirtualMapForMapAndZone(uint32 mapid, uint32 zoneId) +{ + if(mapid != 530 && mapid != 571) // speed for most cases + return mapid; + + if(WorldMapAreaEntry const* wma = sWorldMapAreaStore.LookupEntry(zoneId)) + return wma->virtual_map_id >= 0 ? wma->virtual_map_id : wma->map_id; + + return mapid; +} + +ContentLevels GetContentLevelsForMapAndZone(uint32 mapid, uint32 zoneId) +{ + mapid = GetVirtualMapForMapAndZone(mapid,zoneId); + if(mapid < 2) + return CONTENT_1_60; + + MapEntry const* mapEntry = sMapStore.LookupEntry(mapid); + if(!mapEntry) + return CONTENT_1_60; + + switch(mapEntry->Expansion()) + { + default: return CONTENT_1_60; + case 1: return CONTENT_61_70; + case 2: return CONTENT_71_80; + } +} + +ChatChannelsEntry const* GetChannelEntryFor(uint32 channel_id) +{ + // not sorted, numbering index from 0 + for(uint32 i = 0; i < sChatChannelsStore.GetNumRows(); ++i) + { + ChatChannelsEntry const* ch = sChatChannelsStore.LookupEntry(i); + if(ch && ch->ChannelID == channel_id) + return ch; + } + return NULL; +} + +bool IsTotemCategoryCompatiableWith(uint32 itemTotemCategoryId, uint32 requiredTotemCategoryId) +{ + if(requiredTotemCategoryId==0) + return true; + if(itemTotemCategoryId==0) + return false; + + TotemCategoryEntry const* itemEntry = sTotemCategoryStore.LookupEntry(itemTotemCategoryId); + if(!itemEntry) + return false; + TotemCategoryEntry const* reqEntry = sTotemCategoryStore.LookupEntry(requiredTotemCategoryId); + if(!reqEntry) + return false; + + if(itemEntry->categoryType!=reqEntry->categoryType) + return false; + + return (itemEntry->categoryMask & reqEntry->categoryMask)==reqEntry->categoryMask; +} + +void Zone2MapCoordinates(float& x,float& y,uint32 zone) +{ + WorldMapAreaEntry const* maEntry = sWorldMapAreaStore.LookupEntry(zone); + + // if not listed then map coordinates (instance) + if(!maEntry) + return; + + std::swap(x,y); // at client map coords swapped + x = x*((maEntry->x2-maEntry->x1)/100)+maEntry->x1; + y = y*((maEntry->y2-maEntry->y1)/100)+maEntry->y1; // client y coord from top to down +} + +void Map2ZoneCoordinates(float& x,float& y,uint32 zone) +{ + WorldMapAreaEntry const* maEntry = sWorldMapAreaStore.LookupEntry(zone); + + // if not listed then map coordinates (instance) + if(!maEntry) + return; + + x = (x-maEntry->x1)/((maEntry->x2-maEntry->x1)/100); + y = (y-maEntry->y1)/((maEntry->y2-maEntry->y1)/100); // client y coord from top to down + std::swap(x,y); // client have map coords swapped +} + +uint32 GetTalentInspectBitPosInTab(uint32 talentId) +{ + TalentInspectMap::const_iterator itr = sTalentPosInInspect.find(talentId); + if(itr == sTalentPosInInspect.end()) + return 0; + + return itr->second; +} + +uint32 GetTalentTabInspectBitSize(uint32 talentTabId) +{ + TalentInspectMap::const_iterator itr = sTalentTabSizeInInspect.find(talentTabId); + if(itr == sTalentTabSizeInInspect.end()) + return 0; + + return itr->second; +} + +uint32 const* GetTalentTabPages(uint32 cls) +{ + return sTalentTabPages[cls]; +} + +// script support functions +TRINITY_DLL_SPEC DBCStorage <SoundEntriesEntry> const* GetSoundEntriesStore() { return &sSoundEntriesStore; } +TRINITY_DLL_SPEC DBCStorage <SpellEntry> const* GetSpellStore() { return &sSpellStore; } +TRINITY_DLL_SPEC DBCStorage <SpellRangeEntry> const* GetSpellRangeStore() { return &sSpellRangeStore; } +TRINITY_DLL_SPEC DBCStorage <FactionEntry> const* GetFactionStore() { return &sFactionStore; } +TRINITY_DLL_SPEC DBCStorage <ItemEntry> const* GetItemDisplayStore() { return &sItemStore; } +TRINITY_DLL_SPEC DBCStorage <CreatureDisplayInfoEntry> const* GetCreatureDisplayStore() { return &sCreatureDisplayInfoStore; } diff --git a/src/game/DBCStores.h b/src/game/DBCStores.h new file mode 100644 index 00000000000..c7c7b54608e --- /dev/null +++ b/src/game/DBCStores.h @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_DBCSTORES_H +#define MANGOS_DBCSTORES_H + +#include "Common.h" +#include "Database/DBCStore.h" +#include "DBCStructure.h" + +#include <list> + +typedef std::list<uint32> SimpleFactionsList; + +SimpleFactionsList const* GetFactionTeamList(uint32 faction); +char* GetPetName(uint32 petfamily, uint32 dbclang); +uint32 GetTalentSpellCost(uint32 spellId); +TalentSpellPos const* GetTalentSpellPos(uint32 spellId); + +int32 GetAreaFlagByAreaID(uint32 area_id); // -1 if not found +AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id); +AreaTableEntry const* GetAreaEntryByAreaFlagAndMap(uint32 area_flag,uint32 map_id); +uint32 GetAreaFlagByMapId(uint32 mapid); + +uint32 GetVirtualMapForMapAndZone(uint32 mapid, uint32 zoneId); + +enum ContentLevels +{ + CONTENT_1_60 = 0, + CONTENT_61_70, + CONTENT_71_80 +}; +ContentLevels GetContentLevelsForMapAndZone(uint32 mapid, uint32 zoneId); + +ChatChannelsEntry const* GetChannelEntryFor(uint32 channel_id); + +bool IsTotemCategoryCompatiableWith(uint32 itemTotemCategoryId, uint32 requiredTotemCategoryId); + +void Zone2MapCoordinates(float& x,float& y,uint32 zone); +void Map2ZoneCoordinates(float& x,float& y,uint32 zone); + +uint32 GetTalentInspectBitPosInTab(uint32 talentId); +uint32 GetTalentTabInspectBitSize(uint32 talentTabId); +uint32 const* /*[3]*/ GetTalentTabPages(uint32 cls); + +extern DBCStorage <AchievementEntry> sAchievementStore; +extern DBCStorage <AchievementCriteriaEntry> sAchievementCriteriaStore; +extern DBCStorage <AreaTableEntry> sAreaStore;// recommend access using functions +extern DBCStorage <AreaGroupEntry> sAreaGroupStore; +extern DBCStorage <AreaTriggerEntry> sAreaTriggerStore; +extern DBCStorage <AuctionHouseEntry> sAuctionHouseStore; +extern DBCStorage <BankBagSlotPricesEntry> sBankBagSlotPricesStore; +extern DBCStorage <BarberShopStyleEntry> sBarberShopStyleStore; +extern DBCStorage <BattlemasterListEntry> sBattlemasterListStore; +//extern DBCStorage <ChatChannelsEntry> sChatChannelsStore; -- accessed using function, no usable index +extern DBCStorage <CharStartOutfitEntry> sCharStartOutfitStore; +extern DBCStorage <CharTitlesEntry> sCharTitlesStore; +extern DBCStorage <ChrClassesEntry> sChrClassesStore; +extern DBCStorage <ChrRacesEntry> sChrRacesStore; +extern DBCStorage <CinematicSequencesEntry> sCinematicSequencesStore; +extern DBCStorage <CreatureDisplayInfoEntry> sCreatureDisplayInfoStore; +extern DBCStorage <CreatureFamilyEntry> sCreatureFamilyStore; +extern DBCStorage <CreatureSpellDataEntry> sCreatureSpellDataStore; +extern DBCStorage <CreatureTypeEntry> sCreatureTypeStore; +extern DBCStorage <CurrencyTypesEntry> sCurrencyTypesStore; +extern DBCStorage <DurabilityCostsEntry> sDurabilityCostsStore; +extern DBCStorage <DurabilityQualityEntry> sDurabilityQualityStore; +extern DBCStorage <EmotesTextEntry> sEmotesTextStore; +extern DBCStorage <FactionEntry> sFactionStore; +extern DBCStorage <FactionTemplateEntry> sFactionTemplateStore; +extern DBCStorage <GemPropertiesEntry> sGemPropertiesStore; +extern DBCStorage <GlyphPropertiesEntry> sGlyphPropertiesStore; +extern DBCStorage <GlyphSlotEntry> sGlyphSlotStore; + +extern DBCStorage <GtBarberShopCostBaseEntry> sGtBarberShopCostBaseStore; +extern DBCStorage <GtCombatRatingsEntry> sGtCombatRatingsStore; +extern DBCStorage <GtChanceToMeleeCritBaseEntry> sGtChanceToMeleeCritBaseStore; +extern DBCStorage <GtChanceToMeleeCritEntry> sGtChanceToMeleeCritStore; +extern DBCStorage <GtChanceToSpellCritBaseEntry> sGtChanceToSpellCritBaseStore; +extern DBCStorage <GtChanceToSpellCritEntry> sGtChanceToSpellCritStore; +extern DBCStorage <GtOCTRegenHPEntry> sGtOCTRegenHPStore; +//extern DBCStorage <GtOCTRegenMPEntry> sGtOCTRegenMPStore; -- not used currently +extern DBCStorage <GtRegenHPPerSptEntry> sGtRegenHPPerSptStore; +extern DBCStorage <GtRegenMPPerSptEntry> sGtRegenMPPerSptStore; +extern DBCStorage <HolidaysEntry> sHolidaysStore; +extern DBCStorage <ItemEntry> sItemStore; +extern DBCStorage <ItemBagFamilyEntry> sItemBagFamilyStore; +//extern DBCStorage <ItemDisplayInfoEntry> sItemDisplayInfoStore; -- not used currently +extern DBCStorage <ItemExtendedCostEntry> sItemExtendedCostStore; +extern DBCStorage <ItemLimitCategoryEntry> sItemLimitCategoryStore; +extern DBCStorage <ItemRandomPropertiesEntry> sItemRandomPropertiesStore; +extern DBCStorage <ItemRandomSuffixEntry> sItemRandomSuffixStore; +extern DBCStorage <ItemSetEntry> sItemSetStore; +extern DBCStorage <LockEntry> sLockStore; +extern DBCStorage <MailTemplateEntry> sMailTemplateStore; +extern DBCStorage <MapEntry> sMapStore; +extern DBCStorage <MovieEntry> sMovieStore; +extern DBCStorage <QuestSortEntry> sQuestSortStore; +extern DBCStorage <RandomPropertiesPointsEntry> sRandomPropertiesPointsStore; +extern DBCStorage <ScalingStatDistributionEntry> sScalingStatDistributionStore; +extern DBCStorage <ScalingStatValuesEntry> sScalingStatValuesStore; +extern DBCStorage <SkillLineEntry> sSkillLineStore; +extern DBCStorage <SkillLineAbilityEntry> sSkillLineAbilityStore; +extern DBCStorage <SoundEntriesEntry> sSoundEntriesStore; +extern DBCStorage <SpellCastTimesEntry> sSpellCastTimesStore; +extern DBCStorage <SpellDurationEntry> sSpellDurationStore; +extern DBCStorage <SpellFocusObjectEntry> sSpellFocusObjectStore; +extern DBCStorage <SpellItemEnchantmentEntry> sSpellItemEnchantmentStore; +extern DBCStorage <SpellItemEnchantmentConditionEntry> sSpellItemEnchantmentConditionStore; +extern SpellCategoryStore sSpellCategoryStore; +extern PetFamilySpellsStore sPetFamilySpellsStore; +extern DBCStorage <SpellRadiusEntry> sSpellRadiusStore; +extern DBCStorage <SpellRangeEntry> sSpellRangeStore; +extern DBCStorage <SpellRuneCostEntry> sSpellRuneCostStore; +extern DBCStorage <SpellShapeshiftEntry> sSpellShapeshiftStore; +extern DBCStorage <SpellEntry> sSpellStore; +extern DBCStorage <StableSlotPricesEntry> sStableSlotPricesStore; +extern DBCStorage <SummonPropertiesEntry> sSummonPropertiesStore; +extern DBCStorage <TalentEntry> sTalentStore; +extern DBCStorage <TalentTabEntry> sTalentTabStore; +extern DBCStorage <TaxiNodesEntry> sTaxiNodesStore; +extern DBCStorage <TaxiPathEntry> sTaxiPathStore; +extern TaxiMask sTaxiNodesMask; +extern TaxiMask sOldContinentsNodesMask; +extern TaxiPathSetBySource sTaxiPathSetBySource; +extern TaxiPathNodesByPath sTaxiPathNodesByPath; +extern DBCStorage <TotemCategoryEntry> sTotemCategoryStore; +extern DBCStorage <VehicleEntry> sVehicleStore; +extern DBCStorage <VehicleSeatEntry> sVehicleSeatStore; +//extern DBCStorage <WorldMapAreaEntry> sWorldMapAreaStore; -- use Zone2MapCoordinates and Map2ZoneCoordinates +extern DBCStorage <WorldSafeLocsEntry> sWorldSafeLocsStore; +extern DBCStorage <WorldMapOverlayEntry> sWorldMapOverlayStore; + +void LoadDBCStores(const std::string& dataPath); + +// script support functions +TRINITY_DLL_SPEC DBCStorage <SoundEntriesEntry> const* GetSoundEntriesStore(); +TRINITY_DLL_SPEC DBCStorage <SpellEntry> const* GetSpellStore(); +TRINITY_DLL_SPEC DBCStorage <SpellRangeEntry> const* GetSpellRangeStore(); +TRINITY_DLL_SPEC DBCStorage <FactionEntry> const* GetFactionStore(); +TRINITY_DLL_SPEC DBCStorage <ItemEntry> const* GetItemDisplayStore(); +TRINITY_DLL_SPEC DBCStorage <CreatureDisplayInfoEntry> const* GetCreatureDisplayStore(); +#endif diff --git a/src/game/DBCStructure.h b/src/game/DBCStructure.h new file mode 100644 index 00000000000..e804cba5066 --- /dev/null +++ b/src/game/DBCStructure.h @@ -0,0 +1,1709 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_DBCSTRUCTURE_H +#define MANGOS_DBCSTRUCTURE_H + +#include "DBCEnums.h" +#include "Platform/Define.h" +#include "Util.h" + +#include <map> +#include <set> +#include <vector> + +// Structures using to access raw DBC data and required packing to portability + +// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some platform +#if defined( __GNUC__ ) +#pragma pack(1) +#else +#pragma pack(push,1) +#endif + +struct AchievementEntry +{ + uint32 ID; // 0 + uint32 factionFlag; // 1 -1=all, 0=horde, 1=alliance + uint32 mapID; // 2 -1=none + //uint32 parentAchievement; // 3 its Achievement parent (can`t start while parent uncomplete, use its Criteria if don`t have own, use its progress on begin) + //char *name[16]; // 4-19 + //uint32 name_flags; // 20 + //char *description[16]; // 21-36 + //uint32 desc_flags; // 37 + uint32 categoryId; // 38 + uint32 points; // 39 reward points + //uint32 OrderInCategory; // 40 + uint32 flags; // 41 + //uint32 icon; // 42 icon (from SpellIcon.dbc) + //char *titleReward[16]; // 43-58 + //uint32 titleReward_flags; // 59 + uint32 count; // 60 - need this count of completed criterias (own or referenced achievement criterias) + uint32 refAchievement; // 61 - referenced achievement (counting of all completed criterias) +}; + +struct AchievementCategoryEntry +{ + uint32 ID; // 0 + uint32 parentCategory; // 1 -1 for main category + //char *name[16]; // 2-17 + //uint32 name_flags; // 18 + //uint32 sortOrder; // 19 +}; + +struct AchievementCriteriaEntry +{ + uint32 ID; // 0 + uint32 referredAchievement; // 1 + uint32 requiredType; // 2 + union + { + // ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE = 0 + // TODO: also used for player deaths.. + struct + { + uint32 creatureID; // 3 + uint32 creatureCount; // 4 + } kill_creature; + + // ACHIEVEMENT_CRITERIA_TYPE_WIN_BG = 1 + // TODO: there are further criterias instead just winning + struct + { + uint32 bgMapID; // 3 + uint32 winCount; // 4 + } win_bg; + + // ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL = 5 + struct + { + uint32 unused; // 3 + uint32 level; // 4 + } reach_level; + + // ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL = 7 + struct + { + uint32 skillID; // 3 + uint32 skillLevel; // 4 + } reach_skill_level; + + // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT = 8 + struct + { + uint32 linkedAchievement; // 3 + } complete_achievement; + + // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT = 9 + struct + { + uint32 unused; // 3 + uint32 totalQuestCount; // 4 + } complete_quest_count; + + // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY = 10 + struct + { + uint32 unused; // 3 + uint32 numberOfDays; // 4 + } complete_daily_quest_daily; + + // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE = 11 + struct + { + uint32 zoneID; // 3 + uint32 questCount; // 4 + } complete_quests_in_zone; + + // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST = 14 + struct + { + uint32 unused; // 3 + uint32 questCount; // 4 + } complete_daily_quest; + + // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND = 15 + struct + { + uint32 mapID; // 3 + } complete_battleground; + + // ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP = 16 + struct + { + uint32 mapID; // 3 + } death_at_map; + + // ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON = 18 + struct + { + uint32 manLimit; // 3 + } death_in_dungeon; + + // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID = 19 + struct + { + uint32 groupSize; // 3 can be 5, 10 or 25 + } complete_raid; + + // ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE = 20 + struct + { + uint32 creatureEntry; // 3 + } killed_by_creature; + + // ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING = 24 + struct + { + uint32 unused; // 3 + uint32 fallHeight; // 4 + } fall_without_dying; + + // ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM = 26 + struct + { + uint32 type; // 3, see enum EnviromentalDamage + } death_from; + + // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST = 27 + struct + { + uint32 questID; // 3 + uint32 questCount; // 4 + } complete_quest; + + // ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET = 28 + // ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2 = 69 + struct + { + uint32 spellID; // 3 + uint32 spellCount; // 4 + } be_spell_target; + + // ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL = 29 + // ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2 = 110 + struct + { + uint32 spellID; // 3 + uint32 castCount; // 4 + } cast_spell; + + // ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA = 31 + struct + { + uint32 areaID; // 3 Reference to AreaTable.dbc + uint32 killCount; // 4 + } honorable_kill_at_area; + + // ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA = 32 + struct + { + uint32 mapID; // 3 Reference to Map.dbc + } win_arena; + + // ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA = 33 + struct + { + uint32 mapID; // 3 Reference to Map.dbc + } play_arena; + + // ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL = 34 + struct + { + uint32 spellID; // 3 Reference to Map.dbc + } learn_spell; + + // ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM = 36 + struct + { + uint32 itemID; // 3 + uint32 itemCount; // 4 + } own_item; + + // ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA = 37 + struct + { + uint32 unused; // 3 + uint32 count; // 4 + uint32 flag; // 5 4=in a row + } win_rated_arena; + + // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING = 38 + struct + { + uint32 teamtype; // 3 {2,3,5} + } highest_team_rating; + + // ACHIEVEMENT_CRITERIA_TYPE_REACH_TEAM_RATING = 39 + struct + { + uint32 teamtype; // 3 {2,3,5} + uint32 teamrating; // 4 + } reach_team_rating; + + // ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL = 40 + struct + { + uint32 skillID; // 3 + uint32 skillLevel; // 4 apprentice=1, journeyman=2, expert=3, artisan=4, master=5, grand master=6 + } learn_skill_level; + + // ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM = 41 + struct + { + uint32 itemID; // 3 + uint32 itemCount; // 4 + } use_item; + + // ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM = 42 + struct + { + uint32 itemID; // 3 + uint32 itemCount; // 4 + } loot_item; + + // ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA = 43 + struct + { + // TODO: This rank is _NOT_ the index from AreaTable.dbc + uint32 areaReference; // 3 + } explore_area; + + // ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK = 44 + struct + { + // TODO: This rank is _NOT_ the index from CharTitles.dbc + uint32 rank; // 3 + } own_rank; + + // ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT = 45 + struct + { + uint32 unused; // 3 + uint32 numberOfSlots; // 4 + } buy_bank_slot; + + // ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION = 46 + struct + { + uint32 factionID; // 3 + uint32 reputationAmount; // 4 Total reputation amount, so 42000 = exalted + } gain_reputation; + + // ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION= 47 + struct + { + uint32 unused; // 3 + uint32 numberOfExaltedFactions; // 4 + } gain_exalted_reputation; + + // ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP = 48 + struct + { + uint32 unused; // 3 + uint32 numberOfVisits; // 4 + } visit_barber; + + // ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM = 49 + // TODO: where is the required itemlevel stored? + struct + { + uint32 itemSlot; // 3 + } equip_epic_item; + + // ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT = 50 + struct + { + uint32 rollValue; // 3 + uint32 count; // 4 + } roll_need_on_loot; + // ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT = 51 + struct + { + uint32 rollValue; // 3 + uint32 count; // 4 + } roll_greed_on_loot; + + // ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS = 52 + struct + { + uint32 classID; // 3 + uint32 count; // 4 + } hk_class; + + // ACHIEVEMENT_CRITERIA_TYPE_HK_RACE = 53 + struct + { + uint32 raceID; // 3 + uint32 count; // 4 + } hk_race; + + // ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE = 54 + // TODO: where is the information about the target stored? + struct + { + uint32 emoteID; // 3 enum TextEmotes + uint32 count; // 4 count of emotes, always required special target or requirements + } do_emote; + // ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE = 13 + // ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE = 55 + // ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS = 56 + struct + { + uint32 unused; // 3 + uint32 count; // 4 + uint32 flag; // 5 =3 for battleground healing + uint32 mapid; // 6 + } healing_done; + + // ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM = 57 + struct + { + uint32 itemID; // 3 + uint32 count; // 4 + } equip_item; + + // ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD= 62 + struct + { + uint32 unused; // 3 + uint32 goldInCopper; // 4 + } quest_reward_money; + + + // ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY = 67 + struct + { + uint32 unused; // 3 + uint32 goldInCopper; // 4 + } loot_money; + + // ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT = 68 + struct + { + uint32 goEntry; // 3 + uint32 useCount; // 4 + } use_gameobject; + + // ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL = 70 + // TODO: are those special criteria stored in the dbc or do we have to add another sql table? + struct + { + uint32 unused; // 3 + uint32 killCount; // 4 + } special_pvp_kill; + + // ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT = 72 + struct + { + uint32 goEntry; // 3 + uint32 lootCount; // 4 + } fish_in_gameobject; + + // ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS = 75 + struct + { + uint32 skillLine; // 3 + uint32 spellCount; // 4 + } learn_skilline_spell; + + // ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL = 76 + struct + { + uint32 unused; // 3 + uint32 duelCount; // 4 + } win_duel; + + // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_POWER = 96 + struct + { + uint32 powerType; // 3 mana=0, 1=rage, 3=energy, 6=runic power + } highest_power; + + // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_STAT = 97 + struct + { + uint32 statType; // 3 4=spirit, 3=int, 2=stamina, 1=agi, 0=strength + } highest_stat; + + // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER = 98 + struct + { + uint32 spellSchool; // 3 + } highest_spellpower; + + // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_RATING = 100 + struct + { + uint32 ratingType; // 3 + } highest_rating; + + // ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE = 109 + struct + { + uint32 lootType; // 3 3=fishing, 2=pickpocket, 4=disentchant + uint32 lootTypeCount; // 4 + } loot_type; + + // ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE = 112 + struct + { + uint32 skillLine; // 3 + uint32 spellCount; // 4 + } learn_skill_line; + + // ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL = 113 + struct + { + uint32 unused; // 3 + uint32 killCount; // 4 + } honorable_kill; + + struct + { + uint32 field3; // 3 main requirement + uint32 count; // 4 main requirement count + uint32 additionalRequirement1_type; // 5 additional requirement 1 type + uint32 additionalRequirement1_value; // 6 additional requirement 1 value + uint32 additionalRequirement2_type; // 7 additional requirement 2 type + uint32 additionalRequirement2_value; // 8 additional requirement 1 value + } raw; + }; + //char* name[16]; // 9-24 + //uint32 name_flags; // 25 + uint32 completionFlag; // 26 + uint32 groupFlag; // 27 + //uint32 unk1; // 28 + uint32 timeLimit; // 29 time limit in seconds + //uint32 showOrder; // 30 show order +}; + +struct AreaTableEntry +{ + uint32 ID; // 0 + uint32 mapid; // 1 + uint32 zone; // 2 if 0 then it's zone, else it's zone id of this area + uint32 exploreFlag; // 3, main index + uint32 flags; // 4, unknown value but 312 for all cities + // 5-9 unused + int32 area_level; // 10 + char* area_name[16]; // 11-26 + // 27, string flags, unused + uint32 team; // 28 +}; + +struct AreaGroupEntry +{ + uint32 AreaGroupId; // 0 + uint32 AreaId[7]; // 1-7 +}; + +struct AreaTriggerEntry +{ + uint32 id; // 0 m_ID + uint32 mapid; // 1 m_ContinentID + float x; // 2 m_x + float y; // 3 m_y + float z; // 4 m_z + float radius; // 5 m_radius + float box_x; // 6 m_box_length + float box_y; // 7 m_box_width + float box_z; // 8 m_box_heigh + float box_orientation; // 9 m_box_yaw +}; + +struct AuctionHouseEntry +{ + uint32 houseId; // 0 index + uint32 faction; // 1 id of faction.dbc for player factions associated with city + uint32 depositPercent; // 2 1/3 from real + uint32 cutPercent; // 3 + //char* name[16]; // 4-19 + // 20 string flag, unused +}; + +struct BankBagSlotPricesEntry +{ + uint32 ID; + uint32 price; +}; + +struct BarberShopStyleEntry +{ + uint32 Id; // 0 + uint32 type; // 1 value 0 -> hair, value 2 -> facialhair + //char* name[16]; // 2-17 name of hair style + //uint32 name_flags; // 18 + //uint32 unk_name[16]; // 19-34, all empty + //uint32 unk_flags; // 35 + //float CostMultiplier; // 36 values 1 and 0.75 + uint32 race; // 37 race + uint32 gender; // 38 0 -> male, 1 -> female + uint32 hair_id; // 39 real ID to hair/facial hair +}; + +struct BattlemasterListEntry +{ + uint32 id; // 0 + int32 mapid[8]; // 1-8 mapid + uint32 type; // 9 (3 - BG, 4 - arena) + uint32 minlvl; // 10 + uint32 maxlvl; // 11 + uint32 maxplayersperteam; // 12 + // 13 minplayers + // 14 0 or 9 + // 15 + char* name[16]; // 16-31 + // 32 string flag, unused + // 33 unused +}; + +#define MAX_OUTFIT_ITEMS 24 + +struct CharStartOutfitEntry +{ + //uint32 Id; // 0 + uint32 RaceClassGender; // 1 (UNIT_FIELD_BYTES_0 & 0x00FFFFFF) comparable (0 byte = race, 1 byte = class, 2 byte = gender) + int32 ItemId[MAX_OUTFIT_ITEMS]; // 2-13 + //int32 ItemDisplayId[MAX_OUTFIT_ITEMS]; // 14-25 not required at server side + //int32 ItemInventorySlot[MAX_OUTFIT_ITEMS]; // 26-37 not required at server side + //uint32 Unknown1; // 38, unique values (index-like with gaps ordered in other way as ids) + //uint32 Unknown2; // 39 + //uint32 Unknown3; // 40 +}; + +struct CharTitlesEntry +{ + uint32 ID; // 0, title ids, for example in Quest::GetCharTitleId() + //uint32 unk1; // 1 flags? + //char* name[16]; // 2-17, unused + // 18 string flag, unused + //char* name2[16]; // 19-34, unused + // 35 string flag, unused + uint32 bit_index; // 36 used in PLAYER_CHOSEN_TITLE and 1<<index in PLAYER__FIELD_KNOWN_TITLES +}; + +struct ChatChannelsEntry +{ + uint32 ChannelID; // 0 + uint32 flags; // 1 + char* pattern[16]; // 3-18 + // 19 string flags, unused + //char* name[16]; // 20-35 unused + // 36 string flag, unused +}; + +struct ChrClassesEntry +{ + uint32 ClassID; // 0 + // 1, unused + uint32 powerType; // 2 + // 3-4, unused + //char* name[16]; // 5-20 unused + // 21 string flag, unused + //char* nameFemale[16]; // 21-36 unused, if different from base (male) case + // 37 string flag, unused + //char* nameNeutralGender[16]; // 38-53 unused, if different from base (male) case + // 54 string flag, unused + // 55, unused + uint32 spellfamily; // 56 + // 57, unused + uint32 CinematicSequence; // 58 id from CinematicSequences.dbc + uint32 addon; // 59 (0 - original race, 1 - tbc addon, ...) +}; + +struct ChrRacesEntry +{ + uint32 RaceID; // 0 + // 1 unused + uint32 FactionID; // 2 facton template id + // 3 unused + uint32 model_m; // 4 + uint32 model_f; // 5 + // 6-7 unused + uint32 TeamID; // 8 (7-Alliance 1-Horde) + // 9-12 unused + uint32 CinematicSequence; // 13 id from CinematicSequences.dbc + char* name[16]; // 14-29 used for DBC language detection/selection + // 30 string flags, unused + //char* nameFemale[16]; // 31-46, if different from base (male) case + // 47 string flags, unused + //char* nameNeutralGender[16]; // 48-63, if different from base (male) case + // 64 string flags, unused + // 65-67 unused + uint32 addon; // 68 (0 - original race, 1 - tbc addon, ...) +}; + +/* not used +struct CinematicCameraEntry +{ + uint32 id; // 0 index + char* filename; // 1 + uint32 soundid; // 2 in SoundEntries.dbc or 0 + float start_x; // 3 + float start_y; // 4 + float start_z; // 5 + float unk6; // 6 speed? +}; +*/ + +struct CinematicSequencesEntry +{ + uint32 Id; // 0 index + //uint32 unk1; // 1 always 0 + //uint32 cinematicCamera; // 2 id in CinematicCamera.dbc + // 3-9 always 0 +}; + +struct CreatureDisplayInfoEntry +{ + uint32 Displayid; // 0 m_ID + // 1 m_modelID + // 2 m_soundID + // 3 m_extendedDisplayInfoID + float scale; // 4 m_creatureModelScale + // 5 m_creatureModelAlpha + // 6-8 m_textureVariation[3] + // 9 m_portraitTextureName + // 10 m_sizeClass + // 11 m_bloodID + // 12 m_NPCSoundID + // 13 m_particleColorID + // 14 m_creatureGeosetData + // 15 m_objectEffectPackageID +}; + +struct CreatureFamilyEntry +{ + uint32 ID; // 0 m_ID + float minScale; // 1 m_minScale + uint32 minScaleLevel; // 2 m_minScaleLevel + float maxScale; // 3 m_maxScale + uint32 maxScaleLevel; // 4 m_maxScaleLevel + uint32 skillLine[2]; // 5-6 m_skillLine + uint32 petFoodMask; // 7 m_petFoodMask + int32 petTalentType; // 8 m_petTalentType + // 9 m_categoryEnumID + char* Name[16]; // 10-25 m_name_lang + // 26 string flags + // 27 m_iconFile +}; + +struct CreatureSpellDataEntry +{ + uint32 ID; // 0 m_ID + //uint32 spellId[4]; // 1-4 m_spells[4] + //uint32 availability[4]; // 4-7 m_availability[4] +}; + +struct CreatureTypeEntry +{ + uint32 ID; // 0 m_ID + //char* Name[16]; // 1-16 name + // 17 string flags + //uint32 no_expirience; // 18 no exp? critters, non-combat pets, gas cloud. +}; + +/* not used +struct CurrencyCategoryEntry +{ + uint32 ID; // 0 + uint32 Unk1; // 1 0 for known categories and 3 for unknown one (3.0.9) + char* Name[16]; // 2-17 name + // // 18 string flags +}; +*/ + +struct CurrencyTypesEntry +{ + //uint32 ID; // 0 not used + uint32 ItemId; // 1 used as real index + uint32 BitIndex; // 2 bit index in PLAYER_FIELD_KNOWN_CURRENCIES (1 << (index-1)) +}; + +struct DurabilityCostsEntry +{ + uint32 Itemlvl; // 0 + uint32 multiplier[29]; // 1-29 +}; + +struct DurabilityQualityEntry +{ + uint32 Id; // 0 + float quality_mod; // 1 +}; + +struct EmotesTextEntry +{ + uint32 Id; + uint32 textid; +}; + +struct FactionEntry +{ + uint32 ID; // 0 m_ID + int32 reputationListID; // 1 m_reputationIndex + uint32 BaseRepRaceMask[4]; // 2-5 m_reputationRaceMask + uint32 BaseRepClassMask[4]; // 6-9 m_reputationClassMask + int32 BaseRepValue[4]; // 10-13 m_reputationBase + uint32 ReputationFlags[4]; // 14-17 m_reputationFlags + uint32 team; // 18 m_parentFactionID + char* name[16]; // 19-34 m_name_lang + // 35 string flags + //char* description[16]; // 36-51 m_description_lang + // 52 string flags +}; + +struct FactionTemplateEntry +{ + uint32 ID; // 0 m_ID + uint32 faction; // 1 m_faction + uint32 factionFlags; // 2 m_flags + uint32 ourMask; // 3 m_factionGroup + uint32 friendlyMask; // 4 m_friendGroup + uint32 hostileMask; // 5 m_enemyGroup + uint32 enemyFaction[4]; // 6 m_enemies[4] + uint32 friendFaction[4]; // 10 m_friend[4] + //------------------------------------------------------- end structure + + // helpers + bool IsFriendlyTo(FactionTemplateEntry const& entry) const + { + if(ID == entry.ID) + return true; + if(entry.faction) + { + for(int i = 0; i < 4; ++i) + if (enemyFaction[i] == entry.faction) + return false; + for(int i = 0; i < 4; ++i) + if (friendFaction[i] == entry.faction) + return true; + } + return (friendlyMask & entry.ourMask) || (ourMask & entry.friendlyMask); + } + bool IsHostileTo(FactionTemplateEntry const& entry) const + { + if(ID == entry.ID) + return false; + if(entry.faction) + { + for(int i = 0; i < 4; ++i) + if (enemyFaction[i] == entry.faction) + return true; + for(int i = 0; i < 4; ++i) + if (friendFaction[i] == entry.faction) + return false; + } + return (hostileMask & entry.ourMask) != 0; + } + bool IsHostileToPlayers() const { return (hostileMask & FACTION_MASK_PLAYER) !=0; } + bool IsNeutralToAll() const + { + for(int i = 0; i < 4; ++i) + if (enemyFaction[i] != 0) + return false; + return hostileMask == 0 && friendlyMask == 0; + } + bool IsContestedGuardFaction() const { return (factionFlags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD)!=0; } +}; + +struct GemPropertiesEntry +{ + uint32 ID; + uint32 spellitemenchantement; + uint32 color; +}; + +struct GlyphPropertiesEntry +{ + uint32 Id; + uint32 SpellId; + uint32 TypeFlags; + uint32 Unk1; // GlyphIconId (SpellIcon.dbc) +}; + +struct GlyphSlotEntry +{ + uint32 Id; + uint32 TypeFlags; + uint32 Order; +}; + +// All Gt* DBC store data for 100 levels, some by 100 per class/race +#define GT_MAX_LEVEL 100 + +struct GtBarberShopCostBaseEntry +{ + float cost; +}; + +struct GtCombatRatingsEntry +{ + float ratio; +}; + +struct GtChanceToMeleeCritBaseEntry +{ + float base; +}; + +struct GtChanceToMeleeCritEntry +{ + float ratio; +}; + +struct GtChanceToSpellCritBaseEntry +{ + float base; +}; + +struct GtChanceToSpellCritEntry +{ + float ratio; +}; + +struct GtOCTRegenHPEntry +{ + float ratio; +}; + +//struct GtOCTRegenMPEntry +//{ +// float ratio; +//}; + +struct GtRegenHPPerSptEntry +{ + float ratio; +}; + +struct GtRegenMPPerSptEntry +{ + float ratio; +}; + +/* no used +struct HolidayDescriptionsEntry +{ + uint32 ID; // 0, this is NOT holiday id + //char* name[16] // 1-16 m_name_lang + // 17 name flags +}; +*/ + +/* no used +struct HolidayNamesEntry +{ + uint32 ID; // 0, this is NOT holiday id + //char* name[16] // 1-16 m_name_lang + // 17 name flags +}; +*/ + +struct HolidaysEntry +{ + uint32 ID; // 0, holiday id + //uint32 unk1; // 1 + //uint32 unk2; // 2 + //uint32 unk3[8] // 3-10, empty fields + //uint32 unk11[13] // 11-23, some unknown data (bit strings?) + //uint32 unk11[13] // 24-36, some empty fields (continue prev?) + //uint32 unk11[12] // 37-48, counters? + //uint32 holidayNameId; // 49, id for HolidayNames.dbc + //uint32 holidayDescriptionId; // 50, id for HolidayDescriptions.dbc + //uint32 unk51; // 51 + //uint32 unk52; // 52 + //uint32 unk53; // 53 +}; + +struct ItemEntry +{ + uint32 ID; // 0 + uint32 Class; // 1 + //uint32 SubClass; // 2 some items have strnage subclasses + int32 Unk0; // 3 + int32 Material; // 4 + uint32 DisplayId; // 5 + uint32 InventoryType; // 6 + uint32 Sheath; // 7 +}; + +struct ItemBagFamilyEntry +{ + uint32 ID; // 0 + //char* name[16] // 1-16 m_name_lang + // // 17 name flags +}; + +struct ItemDisplayInfoEntry +{ + uint32 ID; // 0 m_ID + // 1 m_modelName[2] + // 2 m_modelTexture[2] + // 3 m_inventoryIcon + // 4 m_geosetGroup[3] + // 5 m_flags + // 6 m_spellVisualID + // 7 m_groupSoundIndex + // 8 m_helmetGeosetVis[2] + // 9 m_texture[2] + // 10 m_itemVisual[8] + // 11 m_particleColorID +}; + +//struct ItemCondExtCostsEntry +//{ +// uint32 ID; +// uint32 condExtendedCost; // ItemPrototype::CondExtendedCost +// uint32 itemextendedcostentry; // ItemPrototype::ExtendedCost +// uint32 arenaseason; // arena season number(1-4) +//}; + +struct ItemExtendedCostEntry +{ + uint32 ID; // 0 extended-cost entry id + uint32 reqhonorpoints; // 1 required honor points + uint32 reqarenapoints; // 2 required arena points + uint32 reqitem[5]; // 3-7 required item id + uint32 reqitemcount[5]; // 8-12 required count of 1st item + uint32 reqpersonalarenarating; // 13 required personal arena rating +}; + +struct ItemLimitCategoryEntry +{ + uint32 ID; // 0 Id + //char* name[16] // 1-16 m_name_lang + // 17 name flags + uint32 maxCount; // max allowed equipped as item or in gem slot + //uint32 unk; // 1 for prismatic gems only... +}; + +struct ItemRandomPropertiesEntry +{ + uint32 ID; // 0 m_ID + //char* internalName // 1 m_Name + uint32 enchant_id[5]; // 2-6 m_Enchantment + //char* nameSuffix[16] // 7-22 m_name_lang + // 23 name flags +}; + +struct ItemRandomSuffixEntry +{ + uint32 ID; // 0 m_ID + //char* name[16] // 1-16 m_name_lang + // 17, name flags + // 18 m_internalName + uint32 enchant_id[5]; // 19-21 m_enchantment + uint32 prefix[5]; // 22-24 m_allocationPct +}; + +struct ItemSetEntry +{ + //uint32 id // 0 m_ID + char* name[16]; // 1-16 m_name_lang + // 17 string flags, unused + //uint32 itemId[17]; // 18-34 m_itemID + uint32 spells[8]; // 35-42 m_setSpellID + uint32 items_to_triggerspell[8]; // 43-50 m_setThreshold + uint32 required_skill_id; // 51 m_requiredSkill + uint32 required_skill_value; // 52 m_requiredSkillRank +}; + +#define MAX_LOCK_CASE 8 + +struct LockEntry +{ + uint32 ID; // 0 m_ID + uint32 Type[MAX_LOCK_CASE]; // 1-8 m_Type + uint32 Index[MAX_LOCK_CASE]; // 9-16 m_Index + uint32 Skill[MAX_LOCK_CASE]; // 17-24 m_Skill + //uint32 Action[MAX_LOCK_CASE]; // 25-32 m_Action +}; + +struct MailTemplateEntry +{ + uint32 ID; // 0 + //char* subject[16]; // 1-16 + // 17 name flags, unused + //char* content[16]; // 18-33 +}; + +struct MapEntry +{ + uint32 MapID; // 0 + //char* internalname; // 1 unused + uint32 map_type; // 2 + // 3 0 or 1 for battlegrounds (not arenas) + char* name[16]; // 4-19 + // 20 name flags, unused + uint32 linked_zone; // 21 common zone for instance and continent map + //char* hordeIntro[16]; // 23-37 text for PvP Zones + // 38 intro text flags + //char* allianceIntro[16]; // 39-54 text for PvP Zones + // 55 intro text flags + uint32 multimap_id; // 56 + // 57 + //chat* unknownText1[16]; // 58-73 unknown empty text fields, possible normal Intro text. + // 74 text flags + //chat* heroicIntroText[16]; // 75-90 heroic mode requirement text + // 91 text flags + //chat* unknownText2[16]; // 92-107 unknown empty text fields + // 108 text flags + int32 entrance_map; // 109 map_id of entrance map + float entrance_x; // 110 entrance x coordinate (if exist single entry) + float entrance_y; // 111 entrance y coordinate (if exist single entry) + uint32 resetTimeRaid; // 112 + uint32 resetTimeHeroic; // 113 + // 114 all 0 + // 115 -1, 0 and 720 + uint32 addon; // 116 (0-original maps,1-tbc addon) + // 117 some kind of time? + + // Helpers + uint32 Expansion() const { return addon; } + + + bool IsDungeon() const { return map_type == MAP_INSTANCE || map_type == MAP_RAID; } + bool Instanceable() const { return map_type == MAP_INSTANCE || map_type == MAP_RAID || map_type == MAP_BATTLEGROUND || map_type == MAP_ARENA; } + bool IsRaid() const { return map_type == MAP_RAID; } + bool IsBattleGround() const { return map_type == MAP_BATTLEGROUND; } + bool IsBattleArena() const { return map_type == MAP_ARENA; } + bool IsBattleGroundOrArena() const { return map_type == MAP_BATTLEGROUND || map_type == MAP_ARENA; } + bool SupportsHeroicMode() const { return resetTimeHeroic != 0; } + bool HasResetTime() const { return resetTimeHeroic || resetTimeRaid; } + + bool IsMountAllowed() const + { + return !IsDungeon() || + MapID==209 || MapID==269 || MapID==309 || // TanarisInstance, CavernsOfTime, Zul'gurub + MapID==509 || MapID==534 || MapID==560 || // AhnQiraj, HyjalPast, HillsbradPast + MapID==568 || MapID==580 || MapID==615 || // ZulAman, Sunwell Plateau, Obsidian Sanctrum + MapID==616; // Eye Of Eternity + } + + bool IsContinent() const + { + return MapID == 0 || MapID == 1 || MapID == 530 || MapID == 571; + } +}; + +struct MovieEntry +{ + uint32 Id; // 0 index + //char* filename; // 1 + //uint32 unk2; // 2 always 100 +}; + +struct QuestSortEntry +{ + uint32 id; // 0 m_ID + //char* name[16]; // 1-16 m_SortName_lang + // 17 name flags +}; + +struct RandomPropertiesPointsEntry +{ + //uint32 Id; // 0 hidden key + uint32 itemLevel; // 1 + uint32 EpicPropertiesPoints[5]; // 2-6 + uint32 RarePropertiesPoints[5]; // 7-11 + uint32 UncommonPropertiesPoints[5]; // 12-16 +}; + +struct ScalingStatDistributionEntry +{ + uint32 Id; + uint32 StatMod[10]; + uint32 Modifier[10]; + uint32 MaxLevel; +}; + +struct ScalingStatValuesEntry +{ + uint32 Id; + uint32 Level; + uint32 Multiplier[17]; +}; + +//struct SkillLineCategoryEntry{ +// uint32 id; // 0 m_ID +// char* name[16]; // 1-17 m_name_lang +// // 18 string flag +// uint32 displayOrder; // 19 m_sortIndex +//}; + +//struct SkillRaceClassInfoEntry{ +// uint32 id; // 0 m_ID +// uint32 skillId; // 1 m_skillID +// uint32 raceMask; // 2 m_raceMask +// uint32 classMask; // 3 m_classMask +// uint32 flags; // 4 m_flags +// uint32 reqLevel; // 5 m_minLevel +// uint32 skillTierId; // 6 m_skillTierID +// uint32 skillCostID; // 7 m_skillCostIndex +//}; + +//struct SkillTiersEntry{ +// uint32 id; // 0 m_ID +// uint32 skillValue[16]; // 1-17 m_cost +// uint32 maxSkillValue[16]; // 18-32 m_valueMax +//}; + +struct SkillLineEntry +{ + uint32 id; // 0 m_ID + int32 categoryId; // 1 m_categoryID + //uint32 skillCostID; // 2 m_skillCostsID + char* name[16]; // 3-18 m_displayName_lang + // 19 string flags + //char* description[16]; // 20-35 m_description_lang + // 36 string flags + uint32 spellIcon; // 37 m_spellIconID + //char* alternateVerb[16]; // 38-53 m_alternateVerb_lang + // 54 string flags + // 55 m_canLink +}; + +struct SkillLineAbilityEntry +{ + uint32 id; // 0 m_ID + uint32 skillId; // 1 m_skillLine + uint32 spellId; // 2 m_spell + uint32 racemask; // 3 m_raceMask + uint32 classmask; // 4 m_classMask + //uint32 racemaskNot; // 5 m_excludeRace + //uint32 classmaskNot; // 6 m_excludeClass + uint32 req_skill_value; // 7 m_minSkillLineRank + uint32 forward_spellid; // 8 m_supercededBySpell + uint32 learnOnGetSkill; // 9 m_acquireMethod + uint32 max_value; // 10 m_trivialSkillLineRankHigh + uint32 min_value; // 11 m_trivialSkillLineRankLow + //uint32 characterPoints[2]; // 12-13 m_characterPoints[2] +}; + +struct SoundEntriesEntry +{ + uint32 Id; // 0 m_ID + //uint32 Type; // 1 m_soundType + //char* InternalName; // 2 m_name + //char* FileName[10]; // 3-12 m_File[10] + //uint32 Unk13[10]; // 13-22 m_Freq[10] + //char* Path; // 23 m_DirectoryBase + // 24 m_volumeFloat + // 25 m_flags + // 26 m_minDistance + // 27 m_distanceCutoff + // 28 m_EAXDef +}; + +#define MAX_SPELL_EFFECTS 3 + +struct SpellEntry +{ + uint32 Id; // 0 m_ID + uint32 Category; // 1 m_category + uint32 Dispel; // 2 m_dispelType + uint32 Mechanic; // 3 m_mechanic + uint32 Attributes; // 4 m_attribute + uint32 AttributesEx; // 5 m_attributesEx + uint32 AttributesEx2; // 6 m_attributesExB + uint32 AttributesEx3; // 7 m_attributesExC + uint32 AttributesEx4; // 8 m_attributesExD + uint32 AttributesEx5; // 9 m_attributesExE + //uint32 AttributesEx6; // 10 m_attributesExF not used + uint32 Stances; // 11 m_shapeshiftMask + uint32 StancesNot; // 12 m_shapeshiftExclude + uint32 Targets; // 13 m_targets + uint32 TargetCreatureType; // 14 m_targetCreatureType + uint32 RequiresSpellFocus; // 15 m_requiresSpellFocus + uint32 FacingCasterFlags; // 16 m_facingCasterFlags + uint32 CasterAuraState; // 17 m_casterAuraState + uint32 TargetAuraState; // 18 m_targetAuraState + uint32 CasterAuraStateNot; // 19 m_excludeCasterAuraState + uint32 TargetAuraStateNot; // 20 m_excludeTargetAuraState + uint32 casterAuraSpell; // 21 m_casterAuraSpell + uint32 targetAuraSpell; // 22 m_targetAuraSpell + uint32 excludeCasterAuraSpell; // 23 m_excludeCasterAuraSpell + uint32 excludeTargetAuraSpell; // 24 m_excludeTargetAuraSpell + uint32 CastingTimeIndex; // 25 m_castingTimeIndex + uint32 RecoveryTime; // 26 m_recoveryTime + uint32 CategoryRecoveryTime; // 27 m_categoryRecoveryTime + uint32 InterruptFlags; // 28 m_interruptFlags + uint32 AuraInterruptFlags; // 29 m_auraInterruptFlags + uint32 ChannelInterruptFlags; // 30 m_channelInterruptFlags + uint32 procFlags; // 31 m_procTypeMask + uint32 procChance; // 32 m_procChance + uint32 procCharges; // 33 m_procCharges + uint32 maxLevel; // 34 m_maxLevel + uint32 baseLevel; // 35 m_baseLevel + uint32 spellLevel; // 36 m_spellLevel + uint32 DurationIndex; // 37 m_durationIndex + uint32 powerType; // 38 m_powerType + uint32 manaCost; // 39 m_manaCost + uint32 manaCostPerlevel; // 40 m_manaCostPerLevel + uint32 manaPerSecond; // 41 m_manaPerSecond + uint32 manaPerSecondPerLevel; // 42 m_manaPerSecondPerLeve + uint32 rangeIndex; // 43 m_rangeIndex + float speed; // 44 m_speed + //uint32 modalNextSpell; // 45 m_modalNextSpell not used + uint32 StackAmount; // 46 m_cumulativeAura + uint32 Totem[2]; // 47-48 m_totem + int32 Reagent[8]; // 49-56 m_reagent + uint32 ReagentCount[8]; // 57-64 m_reagentCount + int32 EquippedItemClass; // 65 m_equippedItemClass (value) + int32 EquippedItemSubClassMask; // 66 m_equippedItemSubclass (mask) + int32 EquippedItemInventoryTypeMask; // 67 m_equippedItemInvTypes (mask) + uint32 Effect[MAX_SPELL_EFFECTS]; // 68-70 m_effect + int32 EffectDieSides[MAX_SPELL_EFFECTS]; // 71-73 m_effectDieSides + uint32 EffectBaseDice[MAX_SPELL_EFFECTS]; // 74-76 m_effectBaseDice + float EffectDicePerLevel[MAX_SPELL_EFFECTS]; // 77-79 m_effectDicePerLevel + float EffectRealPointsPerLevel[MAX_SPELL_EFFECTS]; // 80-82 m_effectRealPointsPerLevel + int32 EffectBasePoints[MAX_SPELL_EFFECTS]; // 83-85 m_effectBasePoints (don't must be used in spell/auras explicitly, must be used cached Spell::m_currentBasePoints) + uint32 EffectMechanic[MAX_SPELL_EFFECTS]; // 86-88 m_effectMechanic + uint32 EffectImplicitTargetA[MAX_SPELL_EFFECTS]; // 89-91 m_implicitTargetA + uint32 EffectImplicitTargetB[MAX_SPELL_EFFECTS]; // 92-94 m_implicitTargetB + uint32 EffectRadiusIndex[MAX_SPELL_EFFECTS]; // 95-97 m_effectRadiusIndex - spellradius.dbc + uint32 EffectApplyAuraName[MAX_SPELL_EFFECTS]; // 98-100 m_effectAura + uint32 EffectAmplitude[MAX_SPELL_EFFECTS]; // 101-103 m_effectAuraPeriod + float EffectMultipleValue[MAX_SPELL_EFFECTS]; // 104-106 m_effectAmplitude + uint32 EffectChainTarget[MAX_SPELL_EFFECTS]; // 107-109 m_effectChainTargets + uint32 EffectItemType[MAX_SPELL_EFFECTS]; // 110-112 m_effectItemType + int32 EffectMiscValue[MAX_SPELL_EFFECTS]; // 113-115 m_effectMiscValue + int32 EffectMiscValueB[MAX_SPELL_EFFECTS]; // 116-118 m_effectMiscValueB + uint32 EffectTriggerSpell[MAX_SPELL_EFFECTS]; // 119-121 m_effectTriggerSpell + float EffectPointsPerComboPoint[MAX_SPELL_EFFECTS]; // 122-124 m_effectPointsPerCombo + flag96 EffectSpellClassMask[MAX_SPELL_EFFECTS]; // + uint32 SpellVisual[2]; // 134-135 m_spellVisualID + uint32 SpellIconID; // 136 m_spellIconID + uint32 activeIconID; // 137 m_activeIconID + //uint32 spellPriority; // 138 not used + char* SpellName[16]; // 139-154 m_name_lang + //uint32 SpellNameFlag; // 155 not used + char* Rank[16]; // 156-171 m_nameSubtext_lang + //uint32 RankFlags; // 172 not used + //char* Description[16]; // 173-188 m_description_lang not used + //uint32 DescriptionFlags; // 189 not used + //char* ToolTip[16]; // 190-205 m_auraDescription_lang not used + //uint32 ToolTipFlags; // 206 not used + uint32 ManaCostPercentage; // 207 m_manaCostPct + uint32 StartRecoveryCategory; // 208 m_startRecoveryCategory + uint32 StartRecoveryTime; // 209 m_startRecoveryTime + uint32 MaxTargetLevel; // 210 m_maxTargetLevel + uint32 SpellFamilyName; // 211 m_spellClassSet + flag96 SpellFamilyFlags; // 212-214 + uint32 MaxAffectedTargets; // 215 m_maxTargets + uint32 DmgClass; // 216 m_defenseType + uint32 PreventionType; // 217 m_preventionType + //uint32 StanceBarOrder; // 218 m_stanceBarOrder not used + float DmgMultiplier[3]; // 219-221 m_effectChainAmplitude + //uint32 MinFactionId; // 222 m_minFactionID not used + //uint32 MinReputation; // 223 m_minReputation not used + //uint32 RequiredAuraVision; // 224 m_requiredAuraVision not used + uint32 TotemCategory[2]; // 225-226 m_requiredTotemCategoryID + int32 AreaGroupId; // 227 m_requiredAreaGroupId + uint32 SchoolMask; // 228 m_schoolMask + uint32 runeCostID; // 229 m_runeCostID + //uint32 spellMissileID; // 230 m_spellMissileID not used + + // helpers + int32 CalculateSimpleValue(uint8 eff) const { return EffectBasePoints[eff]+int32(EffectBaseDice[eff]); } + + private: + // prevent creating custom entries (copy data from original in fact) + SpellEntry(SpellEntry const&); // DON'T must have implementation +}; + +typedef std::set<uint32> SpellCategorySet; +typedef std::map<uint32,SpellCategorySet > SpellCategoryStore; +typedef std::set<uint32> PetFamilySpellsSet; +typedef std::map<uint32,PetFamilySpellsSet > PetFamilySpellsStore; + +struct SpellCastTimesEntry +{ + uint32 ID; // 0 + int32 CastTime; // 1 + //float CastTimePerLevel; // 2 unsure / per skill? + //int32 MinCastTime; // 3 unsure +}; + +struct SpellFocusObjectEntry +{ + uint32 ID; // 0 + //char* Name[16]; // 1-15 unused + // 16 string flags, unused +}; + +// stored in SQL table +struct SpellThreatEntry +{ + uint32 spellId; + int32 threat; +}; + +struct SpellRadiusEntry +{ + uint32 ID; + float radiusHostile; + //uint32 Unk //always 0 + float radiusFriend; +}; + +struct SpellRangeEntry +{ + uint32 ID; + float minRangeHostile; + float minRangeFriend; + float maxRangeHostile; + float maxRangeFriend; //friend means unattackable unit here + uint32 type; + //char* Name[16]; // 7-23 unused + // 24 string flags, unused + //char* Name2[16]; // 25-40 unused + // 41 string flags, unused +}; + +struct SpellRuneCostEntry +{ + uint32 ID; // 0 + uint32 RuneCost[3]; // 1-3 (0=blood, 1=frost, 2=unholy) + uint32 runePowerGain; // 4 + + bool NoRuneCost() const { return RuneCost[0] == 0 && RuneCost[1] == 0 && RuneCost[2] == 0; } + bool NoRunicPowerGain() const { return runePowerGain == 0; } +}; + +struct SpellShapeshiftEntry +{ + uint32 ID; // 0 + //uint32 buttonPosition; // 1 unused + //char* Name[16]; // 2-17 unused + //uint32 NameFlags; // 18 unused + uint32 flags1; // 19 + int32 creatureType; // 20 <=0 humanoid, other normal creature types + //uint32 unk1; // 21 unused + uint32 attackSpeed; // 22 + //uint32 modelID; // 23 unused, alliance modelid (where horde case?) + //uint32 unk2; // 24 unused + //uint32 unk3; // 25 unused + //uint32 unk4; // 26 unused + //uint32 unk5; // 27 unused + //uint32 unk6; // 28 unused + //uint32 unk7; // 29 unused + //uint32 unk8; // 30 unused + //uint32 unk9; // 31 unused + //uint32 unk10; // 32 unused + //uint32 unk11; // 33 unused + //uint32 unk12; // 34 unused +}; + +struct SpellDurationEntry +{ + uint32 ID; + int32 Duration[3]; +}; + +struct SpellItemEnchantmentEntry +{ + uint32 ID; // 0 m_ID + //uint32 charges; // 1 m_charges + uint32 type[3]; // 2-4 m_effect[3] + uint32 amount[3]; // 5-7 m_effectPointsMin[3] + //uint32 amount2[3] // 8-10 m_effectPointsMax[3] + uint32 spellid[3]; // 11-13 m_effectArg[3] + char* description[16]; // 14-29 m_name_lang[16] + //uint32 descriptionFlags; // 30 name flags + uint32 aura_id; // 31 m_itemVisual + uint32 slot; // 32 m_flags + uint32 GemID; // 33 m_src_itemID + uint32 EnchantmentCondition; // 34 m_condition_id + //uint32 requiredSkill; // 35 m_requiredSkillID + //uint32 requiredSkillValue; // 36 m_requiredSkillRank +}; + +struct SpellItemEnchantmentConditionEntry +{ + uint32 ID; // 0 m_ID + uint8 Color[5]; // 1-5 m_lt_operandType[5] + //uint32 LT_Operand[5]; // 6-10 m_lt_operand[5] + uint8 Comparator[5]; // 11-15 m_operator[5] + uint8 CompareColor[5]; // 15-20 m_rt_operandType[5] + uint32 Value[5]; // 21-25 m_rt_operand[5] + //uint8 Logic[5] // 25-30 m_logic[5] +}; + +struct StableSlotPricesEntry +{ + uint32 Slot; + uint32 Price; +}; + +struct SummonPropertiesEntry +{ + uint32 Id; // 0 + uint32 Category; // 1, 0 - can't be controlled?, 1 - something guardian?, 2 - pet?, 3 - something controllable?, 4 - taxi/mount? + uint32 Faction; // 2, 14 rows > 0 + uint32 Type; // 3, see enum + uint32 Slot; // 4, 0-6 + uint32 Flags; // 5 +}; + +#define MAX_TALENT_RANK 5 +#define MAX_PET_TALENT_RANK 3 // use in calculations, expected <= MAX_TALENT_RANK + +struct TalentEntry +{ + uint32 TalentID; // 0 + uint32 TalentTab; // 1 index in TalentTab.dbc (TalentTabEntry) + uint32 Row; // 2 + uint32 Col; // 3 + uint32 RankID[MAX_TALENT_RANK]; // 4-8 + // 9-12 not used, always 0, maybe not used high ranks + uint32 DependsOn; // 13 index in Talent.dbc (TalentEntry) + // 14-15 not used + uint32 DependsOnRank; // 16 + // 17-18 not used + //uint32 unk1; // 19, 0 or 1 + //uint32 unk2; // 20, all 0 + //uint32 unkFlags1; // 21, related to hunter pet talents + //uint32 unkFlags2; // 22, related to hunter pet talents +}; + +struct TalentTabEntry +{ + uint32 TalentTabID; // 0 + //char* name[16]; // 1-16, unused + //uint32 nameFlags; // 17, unused + //unit32 spellicon; // 18 + // 19 not used + uint32 ClassMask; // 20 + uint32 petTalentMask; // 21 + uint32 tabpage; // 22 + //char* internalname; // 23 +}; + +struct TaxiNodesEntry +{ + uint32 ID; // 0 m_ID + uint32 map_id; // 1 m_ContinentID + float x; // 2 m_x + float y; // 3 m_y + float z; // 4 m_z + //char* name[16]; // 5-21 m_Name_lang + // 22 string flags + uint32 MountCreatureID[2]; // 23-24 m_MountCreatureID[2] +}; + +struct TaxiPathEntry +{ + uint32 ID; // 0 m_ID + uint32 from; // 1 m_FromTaxiNode + uint32 to; // 2 m_ToTaxiNode + uint32 price; // 3 m_Cost +}; + +struct TaxiPathNodeEntry +{ + // 0 m_ID + uint32 path; // 1 m_PathID + uint32 index; // 2 m_NodeIndex + uint32 mapid; // 3 m_ContinentID + float x; // 4 m_LocX + float y; // 5 m_LocY + float z; // 6 m_LocZ + uint32 actionFlag; // 7 m_flags + uint32 delay; // 8 m_delay + // 9 m_arrivalEventID + // 10 m_departureEventID +}; + +struct TotemCategoryEntry +{ + uint32 ID; // 0 + //char* name[16]; // 1-16 + // 17 string flags, unused + uint32 categoryType; // 18 (one for specialization) + uint32 categoryMask; // 19 (compatibility mask for same type: different for totems, compatible from high to low for rods) +}; + +struct VehicleEntry +{ + uint32 m_ID; // 0 + uint32 m_flags; // 1 + float m_turnSpeed; // 2 + float m_pitchSpeed; // 3 + float m_pitchMin; // 4 + float m_pitchMax; // 5 + uint32 m_seatID[8]; // 6-13 + float m_mouseLookOffsetPitch; // 14 + float m_cameraFadeDistScalarMin; // 15 + float m_cameraFadeDistScalarMax; // 16 + float m_cameraPitchOffset; // 17 + int m_powerType[3]; // 18-20 + int m_powerToken[3]; // 21-23 + float m_facingLimitRight; // 24 + float m_facingLimitLeft; // 25 + float m_msslTrgtTurnLingering; // 26 + float m_msslTrgtPitchLingering; // 27 + float m_msslTrgtMouseLingering; // 28 + float m_msslTrgtEndOpacity; // 29 + float m_msslTrgtArcSpeed; // 30 + float m_msslTrgtArcRepeat; // 31 + float m_msslTrgtArcWidth; // 32 + float m_msslTrgtImpactRadius[2]; // 33-34 + char* m_msslTrgtArcTexture; // 35 + char* m_msslTrgtImpactTexture; // 36 + char* m_msslTrgtImpactModel[2]; // 37-38 + float m_cameraYawOffset; // 39 + uint32 m_uiLocomotionType; // 40 + float m_msslTrgtImpactTexRadius; // 41 + uint32 m_uiSeatIndicatorType; // 42 +}; + +struct VehicleSeatEntry +{ + uint32 m_ID; // 0 + uint32 m_flags; // 1 + int32 m_attachmentID; // 2 + float m_attachmentOffsetX; // 3 + float m_attachmentOffsetY; // 4 + float m_attachmentOffsetZ; // 5 + float m_enterPreDelay; // 6 + float m_enterSpeed; // 7 + float m_enterGravity; // 8 + float m_enterMinDuration; // 9 + float m_enterMaxDuration; // 10 + float m_enterMinArcHeight; // 11 + float m_enterMaxArcHeight; // 12 + int32 m_enterAnimStart; // 13 + int32 m_enterAnimLoop; // 14 + int32 m_rideAnimStart; // 15 + int32 m_rideAnimLoop; // 16 + int32 m_rideUpperAnimStart; // 17 + int32 m_rideUpperAnimLoop; // 18 + float m_exitPreDelay; // 19 + float m_exitSpeed; // 20 + float m_exitGravity; // 21 + float m_exitMinDuration; // 22 + float m_exitMaxDuration; // 23 + float m_exitMinArcHeight; // 24 + float m_exitMaxArcHeight; // 25 + int32 m_exitAnimStart; // 26 + int32 m_exitAnimLoop; // 27 + int32 m_exitAnimEnd; // 28 + float m_passengerYaw; // 29 + float m_passengerPitch; // 30 + float m_passengerRoll; // 31 + int32 m_passengerAttachmentID; // 32 + int32 m_vehicleEnterAnim; // 33 + int32 m_vehicleExitAnim; // 34 + int32 m_vehicleRideAnimLoop; // 35 + int32 m_vehicleEnterAnimBone; // 36 + int32 m_vehicleExitAnimBone; // 37 + int32 m_vehicleRideAnimLoopBone; // 38 + float m_vehicleEnterAnimDelay; // 39 + float m_vehicleExitAnimDelay; // 40 + uint32 m_vehicleAbilityDisplay; // 41 + uint32 m_enterUISoundID; // 42 + uint32 m_exitUISoundID; // 43 + int32 m_uiSkin; // 44 + uint32 m_flagsB; // 45 +}; + +struct WorldMapAreaEntry +{ + //uint32 ID; // 0 + uint32 map_id; // 1 + uint32 area_id; // 2 index (continent 0 areas ignored) + //char* internal_name // 3 + float y1; // 4 + float y2; // 5 + float x1; // 6 + float x2; // 7 + int32 virtual_map_id; // 8 -1 (map_id have correct map) other: virtual map where zone show (map_id - where zone in fact internally) + // int32 dungeonMap_id; // 9 pointer to DungeonMap.dbc (owerride x1,x2,y1,y2 coordinates) +}; + +struct WorldSafeLocsEntry +{ + uint32 ID; // 0 + uint32 map_id; // 1 + float x; // 2 + float y; // 3 + float z; // 4 + //char* name[16] // 5-20 name, unused + // 21 name flags, unused +}; + +struct WorldMapOverlayEntry +{ + uint32 ID; // 0 + uint32 areatableID[4]; // 2-5 +}; + +// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform +#if defined( __GNUC__ ) +#pragma pack() +#else +#pragma pack(pop) +#endif + +// Structures not used for casting to loaded DBC data and not required then packing +struct TalentSpellPos +{ + TalentSpellPos() : talent_id(0), rank(0) {} + TalentSpellPos(uint16 _talent_id, uint8 _rank) : talent_id(_talent_id), rank(_rank) {} + + uint16 talent_id; + uint8 rank; +}; + +typedef std::map<uint32,TalentSpellPos> TalentSpellPosMap; + +struct TaxiPathBySourceAndDestination +{ + TaxiPathBySourceAndDestination() : ID(0),price(0) {} + TaxiPathBySourceAndDestination(uint32 _id,uint32 _price) : ID(_id),price(_price) {} + + uint32 ID; + uint32 price; +}; +typedef std::map<uint32,TaxiPathBySourceAndDestination> TaxiPathSetForSource; +typedef std::map<uint32,TaxiPathSetForSource> TaxiPathSetBySource; + +struct TaxiPathNode +{ + TaxiPathNode() : mapid(0), x(0),y(0),z(0),actionFlag(0),delay(0) {} + TaxiPathNode(uint32 _mapid, float _x, float _y, float _z, uint32 _actionFlag, uint32 _delay) : mapid(_mapid), x(_x),y(_y),z(_z),actionFlag(_actionFlag),delay(_delay) {} + + uint32 mapid; + float x; + float y; + float z; + uint32 actionFlag; + uint32 delay; +}; +typedef std::vector<TaxiPathNode> TaxiPathNodeList; +typedef std::vector<TaxiPathNodeList> TaxiPathNodesByPath; + +#define TaxiMaskSize 12 +typedef uint32 TaxiMask[TaxiMaskSize]; +#endif + diff --git a/src/game/DBCfmt.h b/src/game/DBCfmt.h new file mode 100644 index 00000000000..23f9cc9c516 --- /dev/null +++ b/src/game/DBCfmt.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_DBCSFRM_H +#define MANGOS_DBCSFRM_H + +const char Achievementfmt[]="niixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiixixxxxxxxxxxxxxxxxxxii"; +const char AchievementCriteriafmt[]="niiiiiiiixxxxxxxxxxxxxxxxxiixix"; +const char AreaTableEntryfmt[]="iiinixxxxxissssssssssssssssxixxxxxxx"; +const char AreaGroupEntryfmt[]="niiiiiii"; +const char AreaTriggerEntryfmt[]="niffffffff"; +const char AuctionHouseEntryfmt[]="niiixxxxxxxxxxxxxxxxx"; +const char BankBagSlotPricesEntryfmt[]="ni"; +const char BarberShopStyleEntryfmt[]="nixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiii"; +const char BattlemasterListEntryfmt[]="niiiiiiiiiiiixxxssssssssssssssssxx"; +const char CharStartOutfitEntryfmt[]="diiiiiiiiiiiiiiiiiiiiiiiiixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +const char CharTitlesEntryfmt[]="nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxi"; +const char ChatChannelsEntryfmt[]="iixssssssssssssssssxxxxxxxxxxxxxxxxxx"; + // ChatChannelsEntryfmt, index not used (more compact store) +const char ChrClassesEntryfmt[]="nxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixii"; +const char ChrRacesEntryfmt[]="nxixiixxixxxxissssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxi"; +const char CinematicSequencesEntryfmt[]="nxxxxxxxxx"; +const char CreatureDisplayInfofmt[]="nxxxfxxxxxxxxxxx"; +const char CreatureFamilyfmt[]="nfifiiiiixssssssssssssssssxx"; +const char CreatureSpellDatafmt[]="nxxxxxxxx"; +const char CreatureTypefmt[]="nxxxxxxxxxxxxxxxxxx"; +const char CurrencyTypesfmt[]="xnxi"; +const char DurabilityCostsfmt[]="niiiiiiiiiiiiiiiiiiiiiiiiiiiii"; +const char DurabilityQualityfmt[]="nf"; +const char EmoteEntryfmt[]="nxixxxxxxxxxxxxxxxx"; +const char FactionEntryfmt[]="niiiiiiiiiiiiiiiiiissssssssssssssssxxxxxxxxxxxxxxxxxx"; +const char FactionTemplateEntryfmt[]="niiiiiiiiiiiii"; +const char GemPropertiesEntryfmt[]="nixxi"; +const char GlyphPropertiesfmt[]="niii"; +const char GlyphSlotfmt[]="nii"; +const char GtBarberShopCostBasefmt[]="f"; +const char GtCombatRatingsfmt[]="f"; +const char GtChanceToMeleeCritBasefmt[]="f"; +const char GtChanceToMeleeCritfmt[]="f"; +const char GtChanceToSpellCritBasefmt[]="f"; +const char GtChanceToSpellCritfmt[]="f"; +const char GtOCTRegenHPfmt[]="f"; +//const char GtOCTRegenMPfmt[]="f"; +const char GtRegenHPPerSptfmt[]="f"; +const char GtRegenMPPerSptfmt[]="f"; +const char Holidaysfmt[]="nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +const char Itemfmt[]="nixiiiii"; +const char ItemBagFamilyfmt[]="nxxxxxxxxxxxxxxxxx"; +//const char ItemDisplayTemplateEntryfmt[]="nxxxxxxxxxxixxxxxxxxxxx"; +//const char ItemCondExtCostsEntryfmt[]="xiii"; +const char ItemExtendedCostEntryfmt[]="niiiiiiiiiiiiix"; +const char ItemLimitCategoryEntryfmt[]="nxxxxxxxxxxxxxxxxxix"; +const char ItemRandomPropertiesfmt[]="nxiiiiixxxxxxxxxxxxxxxxx"; +const char ItemRandomSuffixfmt[]="nxxxxxxxxxxxxxxxxxxiiiiiiiiii"; +const char ItemSetEntryfmt[]="dssssssssssssssssxxxxxxxxxxxxxxxxxxiiiiiiiiiiiiiiiiii"; +const char LockEntryfmt[]="niiiiiiiiiiiiiiiiiiiiiiiixxxxxxxx"; +const char MailTemplateEntryfmt[]="nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +const char MapEntryfmt[]="nxixssssssssssssssssxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiffiixxix"; +const char MovieEntryfmt[]="nxx"; +const char QuestSortEntryfmt[]="nxxxxxxxxxxxxxxxxx"; +const char RandomPropertiesPointsfmt[]="niiiiiiiiiiiiiii"; +const char ScalingStatDistributionfmt[]="niiiiiiiiiiiiiiiiiiiii"; +const char ScalingStatValuesfmt[]="iniiiiiiiiiiiiiiiii"; +const char SkillLinefmt[]="nixssssssssssssssssxxxxxxxxxxxxxxxxxxixxxxxxxxxxxxxxxxxx"; +const char SkillLineAbilityfmt[]="niiiixxiiiiixx"; +const char SoundEntriesfmt[]="nxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +const char SpellCastTimefmt[]="nixx"; +const char SpellDurationfmt[]="niii"; +const char SpellEntryfmt[]="niiiiiiiiixiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiifxiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiffffffiiiiiiiiiiiiiiiiiiiiifffiiiiiiiiiiiiiiifffiiiiiiiiiiiiixssssssssssssssssxssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiiiiiiiiiiixfffxxxiiiiix"; +const char SpellFocusObjectfmt[]="nxxxxxxxxxxxxxxxxx"; +const char SpellItemEnchantmentfmt[]="nxiiiiiixxxiiissssssssssssssssxiiiixx"; +const char SpellItemEnchantmentConditionfmt[]="nbbbbbxxxxxbbbbbbbbbbiiiiiXXXXX"; +const char SpellRadiusfmt[]="nfxf"; +const char SpellRangefmt[]="nffffixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +const char SpellRuneCostfmt[]="niiii"; +const char SpellShapeshiftfmt[]="nxxxxxxxxxxxxxxxxxxiixixxxxxxxxxxxx"; +const char StableSlotPricesfmt[] = "ni"; +const char SummonPropertiesfmt[] = "niiiii"; +const char TalentEntryfmt[]="niiiiiiiixxxxixxixxxxxx"; +const char TalentTabEntryfmt[]="nxxxxxxxxxxxxxxxxxxxiiix"; +const char TaxiNodesEntryfmt[]="nifffxxxxxxxxxxxxxxxxxii"; +const char TaxiPathEntryfmt[]="niii"; +const char TaxiPathNodeEntryfmt[]="diiifffiixx"; +const char TotemCategoryEntryfmt[]="nxxxxxxxxxxxxxxxxxii"; +const char VehicleEntryfmt[]="niffffiiiiiiiiffffiiiiiifffffffffffssssfifi"; +const char VehicleSeatEntryfmt[]="niiffffffffffiiiiiifffffffiiifffiiiiiiiffiiiii"; +const char WorldMapAreaEntryfmt[]="xinxffffix"; +const char WorldSafeLocsEntryfmt[]="nifffxxxxxxxxxxxxxxxxx"; +const char WorldMapOverlayEntryfmt[]="nxiiiixxxxxxxxxxx"; + +#endif
\ No newline at end of file diff --git a/src/game/Debugcmds.cpp b/src/game/Debugcmds.cpp index 3bf64188e34..e50ca1677de 100644 --- a/src/game/Debugcmds.cpp +++ b/src/game/Debugcmds.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,37 +21,19 @@ #include "Common.h" #include "Database/DatabaseEnv.h" #include "WorldPacket.h" -#include "WorldSession.h" -#include "World.h" +#include "Vehicle.h" #include "Player.h" #include "Opcodes.h" #include "Chat.h" #include "Log.h" #include "Unit.h" -#include "ObjectAccessor.h" #include "GossipDef.h" #include "Language.h" -#include "MapManager.h" #include "BattleGroundMgr.h" #include <fstream> #include "ObjectMgr.h" -bool ChatHandler::HandleDebugInArcCommand(const char* /*args*/) -{ - Object *obj = getSelectedUnit(); - - if(!obj) - { - SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); - return true; - } - - SendSysMessage(LANG_NOT_IMPLEMENTED); - - return true; -} - -bool ChatHandler::HandleDebugSpellFailCommand(const char* args) +bool ChatHandler::HandleDebugSendSpellFailCommand(const char* args) { if(!args) return false; @@ -61,16 +43,31 @@ bool ChatHandler::HandleDebugSpellFailCommand(const char* args) return false; uint8 failnum = (uint8)atoi(px); + if(failnum==0 && *px!='0') + return false; + + char* p1 = strtok(NULL, " "); + uint8 failarg1 = p1 ? (uint8)atoi(p1) : 0; + + char* p2 = strtok(NULL, " "); + uint8 failarg2 = p2 ? (uint8)atoi(p2) : 0; + WorldPacket data(SMSG_CAST_FAILED, 5); + data << uint8(0); data << uint32(133); data << uint8(failnum); + if(p1 || p2) + data << uint32(failarg1); + if(p2) + data << uint32(failarg2); + m_session->SendPacket(&data); return true; } -bool ChatHandler::HandleSetPoiCommand(const char* args) +bool ChatHandler::HandleDebugSendPoiCommand(const char* args) { Player *pPlayer = m_session->GetPlayer(); Unit* target = getSelectedUnit(); @@ -89,9 +86,6 @@ bool ChatHandler::HandleSetPoiCommand(const char* args) return false; uint32 icon = atol(icon_text); - if ( icon < 0 ) - icon = 0; - uint32 flags = atol(flags_text); sLog.outDetail("Command : POI, NPC = %u, icon = %u flags = %u", target->GetGUIDLow(), icon,flags); @@ -99,7 +93,7 @@ bool ChatHandler::HandleSetPoiCommand(const char* args) return true; } -bool ChatHandler::HandleEquipErrorCommand(const char* args) +bool ChatHandler::HandleDebugSendEquipErrorCommand(const char* args) { if(!args) return false; @@ -109,7 +103,7 @@ bool ChatHandler::HandleEquipErrorCommand(const char* args) return true; } -bool ChatHandler::HandleSellErrorCommand(const char* args) +bool ChatHandler::HandleDebugSendSellErrorCommand(const char* args) { if(!args) return false; @@ -119,7 +113,7 @@ bool ChatHandler::HandleSellErrorCommand(const char* args) return true; } -bool ChatHandler::HandleBuyErrorCommand(const char* args) +bool ChatHandler::HandleDebugSendBuyErrorCommand(const char* args) { if(!args) return false; @@ -129,7 +123,7 @@ bool ChatHandler::HandleBuyErrorCommand(const char* args) return true; } -bool ChatHandler::HandleSendOpcodeCommand(const char* /*args*/) +bool ChatHandler::HandleDebugSendOpcodeCommand(const char* /*args*/) { Unit *unit = getSelectedUnit(); Player *player = NULL; @@ -192,14 +186,22 @@ bool ChatHandler::HandleSendOpcodeCommand(const char* /*args*/) ifs >> val6; data << val6; } - else if(type == "pguid") + else if(type == "appitsguid") { data.append(unit->GetPackGUID()); } - else if(type == "myguid") + else if(type == "appmyguid") { data.append(player->GetPackGUID()); } + else if(type == "myguid") + { + data << uint64(player->GetGUID()); + } + else if(type == "itsguid") + { + data << uint64(unit->GetGUID()); + } else if(type == "pos") { data << unit->GetPositionX(); @@ -221,12 +223,12 @@ bool ChatHandler::HandleSendOpcodeCommand(const char* /*args*/) ifs.close(); sLog.outDebug("Sending opcode %u", data.GetOpcode()); data.hexlike(); - ((Player*)unit)->GetSession()->SendPacket(&data); + player->GetSession()->SendPacket(&data); PSendSysMessage(LANG_COMMAND_OPCODESENT, data.GetOpcode(), unit->GetName()); return true; } -bool ChatHandler::HandleUpdateWorldStateCommand(const char* args) +bool ChatHandler::HandleDebugUpdateWorldStateCommand(const char* args) { char* w = strtok((char*)args, " "); char* s = strtok(NULL, " "); @@ -240,18 +242,94 @@ bool ChatHandler::HandleUpdateWorldStateCommand(const char* args) return true; } -bool ChatHandler::HandlePlaySound2Command(const char* args) +bool ChatHandler::HandleDebugPlayCinematicCommand(const char* args) { - if(!args) + // USAGE: .debug play cinematic #cinematicid + // #cinematicid - ID decimal number from CinemaicSequences.dbc (1st column) + if( !*args ) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + uint32 dwId = atoi((char*)args); + + if(!sCinematicSequencesStore.LookupEntry(dwId)) + { + PSendSysMessage(LANG_CINEMATIC_NOT_EXIST, dwId); + SetSentErrorMessage(true); + return false; + } + + m_session->GetPlayer()->SendCinematicStart(dwId); + return true; +} + +bool ChatHandler::HandleDebugPlayMovieCommand(const char* args) +{ + // USAGE: .debug play movie #movieid + // #movieid - ID decimal number from Movie.dbc (1st column) + if( !*args ) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + uint32 dwId = atoi((char*)args); + + if(!sMovieStore.LookupEntry(dwId)) + { + PSendSysMessage(LANG_MOVIE_NOT_EXIST, dwId); + SetSentErrorMessage(true); + return false; + } + + m_session->GetPlayer()->SendMovieStart(dwId); + return true; +} + +//Play sound +bool ChatHandler::HandleDebugPlaySoundCommand(const char* args) +{ + // USAGE: .debug playsound #soundid + // #soundid - ID decimal number from SoundEntries.dbc (1st column) + if( !*args ) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + uint32 dwSoundId = atoi((char*)args); + + if(!sSoundEntriesStore.LookupEntry(dwSoundId)) + { + PSendSysMessage(LANG_SOUND_NOT_EXIST, dwSoundId); + SetSentErrorMessage(true); return false; + } + + Unit* unit = getSelectedUnit(); + if(!unit) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + SetSentErrorMessage(true); + return false; + } + + if(m_session->GetPlayer()->GetSelection()) + unit->PlayDistanceSound(dwSoundId,m_session->GetPlayer()); + else + unit->PlayDirectSound(dwSoundId,m_session->GetPlayer()); - uint32 soundid = atoi(args); - m_session->GetPlayer()->PlaySound(soundid, false); + PSendSysMessage(LANG_YOU_HEAR_SOUND, dwSoundId); return true; } //Send notification in channel -bool ChatHandler::HandleSendChannelNotifyCommand(const char* args) +bool ChatHandler::HandleDebugSendChannelNotifyCommand(const char* args) { if(!args) return false; @@ -269,7 +347,7 @@ bool ChatHandler::HandleSendChannelNotifyCommand(const char* args) } //Send notification in chat -bool ChatHandler::HandleSendChatMsgCommand(const char* args) +bool ChatHandler::HandleDebugSendChatMsgCommand(const char* args) { if(!args) return false; @@ -282,15 +360,14 @@ bool ChatHandler::HandleSendChatMsgCommand(const char* args) return true; } -bool ChatHandler::HandleSendQuestPartyMsgCommand(const char* args) +bool ChatHandler::HandleDebugSendQuestPartyMsgCommand(const char* args) { uint32 msg = atol((char*)args); - if (msg >= 0) - m_session->GetPlayer()->SendPushToPartyResponse(m_session->GetPlayer(), msg); + m_session->GetPlayer()->SendPushToPartyResponse(m_session->GetPlayer(), msg); return true; } -bool ChatHandler::HandleGetLootRecipient(const char* /*args*/) +bool ChatHandler::HandleDebugGetLootRecipient(const char* /*args*/) { Creature* target = getSelectedCreature(); if(!target) @@ -300,15 +377,14 @@ bool ChatHandler::HandleGetLootRecipient(const char* /*args*/) return true; } -bool ChatHandler::HandleSendQuestInvalidMsgCommand(const char* args) +bool ChatHandler::HandleDebugSendQuestInvalidMsgCommand(const char* args) { uint32 msg = atol((char*)args); - if (msg >= 0) - m_session->GetPlayer()->SendCanTakeQuestResponse(msg); + m_session->GetPlayer()->SendCanTakeQuestResponse(msg); return true; } -bool ChatHandler::HandleGetItemState(const char* args) +bool ChatHandler::HandleDebugGetItemState(const char* args) { if (!args) return false; @@ -542,6 +618,12 @@ bool ChatHandler::HandleGetItemState(const char* args) return true; } +bool ChatHandler::HandleDebugBattlegroundCommand(const char * /*args*/) +{ + sBattleGroundMgr.ToggleTesting(); + return true; +} + bool ChatHandler::HandleDebugArenaCommand(const char * /*args*/) { sBattleGroundMgr.ToggleArenaTesting(); @@ -591,3 +673,107 @@ bool ChatHandler::HandleDebugHostilRefList(const char * /*args*/) return true; } +bool ChatHandler::HandleDebugSpawnVehicle(const char* args) +{ + if(!args) + return false; + + char* e = strtok((char*)args, " "); + char* i = strtok(NULL, " "); + + if (!e || !i) + return false; + + uint32 entry = (uint32)atoi(e); + uint32 id = (uint32)atoi(i); + + CreatureInfo const *ci = objmgr.GetCreatureTemplate(entry); + + if(!ci) + return false; + + VehicleEntry const *ve = sVehicleStore.LookupEntry(id); + + if(!ve) + return false; + + Vehicle *v = new Vehicle; + Map *map = m_session->GetPlayer()->GetMap(); + if(!v->Create(objmgr.GenerateLowGuid(HIGHGUID_VEHICLE), map, m_session->GetPlayer()->GetPhaseMask(), entry, id, m_session->GetPlayer()->GetTeam())) + { + delete v; + return false; + } + + float px, py, pz; + m_session->GetPlayer()->GetClosePoint(px, py, pz, m_session->GetPlayer()->GetObjectSize()); + + v->Relocate(px, py, pz, m_session->GetPlayer()->GetOrientation()); + + if(!v->IsPositionValid()) + { + sLog.outError("Vehicle (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)", + v->GetGUIDLow(), v->GetEntry(), v->GetPositionX(), v->GetPositionY()); + delete v; + return false; + } + + map->Add((Creature*)v); + + return true; +} + +bool ChatHandler::HandleDebugSendLargePacketCommand(const char* /*args*/) +{ + const char* stuffingString = "This is a dummy string to push the packet's size beyond 128000 bytes. "; + std::ostringstream ss; + while(ss.str().size() < 128000) + ss << stuffingString; + SendSysMessage(ss.str().c_str()); + return true; +} + +bool ChatHandler::HandleDebugSendSetPhaseShiftCommand(const char* args) +{ + if(!args) + return false; + + uint32 PhaseShift = atoi(args); + m_session->SendSetPhaseShift(PhaseShift); + return true; +} + +bool ChatHandler::HandleDebugSetItemFlagCommand(const char* args) +{ + if(!args) + return false; + + char* e = strtok((char*)args, " "); + char* f = strtok(NULL, " "); + + if (!e || !f) + return false; + + uint32 guid = (uint32)atoi(e); + uint32 flag = (uint32)atoi(f); + + Item *i = m_session->GetPlayer()->GetItemByGuid(MAKE_NEW_GUID(guid, 0, HIGHGUID_ITEM)); + + if(!i) + return false; + + i->SetUInt32Value(ITEM_FIELD_FLAGS, flag); + + return true; +} + +//show animation +bool ChatHandler::HandleDebugAnimCommand(const char* args) +{ + if (!*args) + return false; + + uint32 anim_id = atoi((char*)args); + m_session->GetPlayer()->HandleEmoteCommand(anim_id); + return true; +} diff --git a/src/game/DestinationHolder.cpp b/src/game/DestinationHolder.cpp index 904509aeef4..f2158e3b5be 100644 --- a/src/game/DestinationHolder.cpp +++ b/src/game/DestinationHolder.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/DestinationHolder.h b/src/game/DestinationHolder.h index 4c259d0dfde..94e12a1757d 100644 --- a/src/game/DestinationHolder.h +++ b/src/game/DestinationHolder.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/DestinationHolderImp.h b/src/game/DestinationHolderImp.h index fcc2d946de8..384ad9621a3 100644 --- a/src/game/DestinationHolderImp.h +++ b/src/game/DestinationHolderImp.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,7 +21,6 @@ #ifndef TRINITY_DESTINATIONHOLDERIMP_H #define TRINITY_DESTINATIONHOLDERIMP_H -#include "Creature.h" #include "MapManager.h" #include "DestinationHolder.h" @@ -85,24 +84,7 @@ DestinationHolder<TRAVELLER>::StartTravel(TRAVELLER &traveller, bool sendMove) i_fromY = traveller.GetPositionY(); i_fromZ = traveller.GetPositionZ(); - float dx = i_destX - i_fromX; - float dy = i_destY - i_fromY; - float dz = i_destZ - i_fromZ; - - float dist; - //Should be for Creature Flying and Swimming. - if(traveller.GetTraveller().hasUnitState(UNIT_STAT_IN_FLIGHT)) - dist = sqrt((dx*dx) + (dy*dy) + (dz*dz)); - else //Walking on the ground - dist = sqrt((dx*dx) + (dy*dy)); - - float speed; - if(traveller.GetTraveller().hasUnitState(UNIT_STAT_CHARGING)) - speed = SPEED_CHARGE * 0.001f; - else - speed = traveller.Speed() * 0.001f; // speed is in seconds so convert from second to millisecond - i_totalTravelTime = static_cast<uint32>(dist/speed); - + i_totalTravelTime = traveller.GetTotalTrevelTimeTo(i_destX,i_destY,i_destZ); i_timeElapsed = 0; if(sendMove) traveller.MoveTo(i_destX, i_destY, i_destZ, i_totalTravelTime); diff --git a/src/game/DuelHandler.cpp b/src/game/DuelHandler.cpp index 58506f949e9..aa6563a3f98 100644 --- a/src/game/DuelHandler.cpp +++ b/src/game/DuelHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,11 +21,9 @@ #include "Common.h" #include "WorldPacket.h" #include "WorldSession.h" -#include "World.h" #include "Log.h" #include "Opcodes.h" #include "UpdateData.h" -#include "MapManager.h" #include "Player.h" void WorldSession::HandleDuelAcceptedOpcode(WorldPacket& recvPacket) diff --git a/src/game/DynamicObject.cpp b/src/game/DynamicObject.cpp index 801626d5a9b..d294886619c 100644 --- a/src/game/DynamicObject.cpp +++ b/src/game/DynamicObject.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,16 +19,11 @@ */ #include "Common.h" -#include "GameObject.h" #include "UpdateMask.h" #include "Opcodes.h" -#include "WorldPacket.h" -#include "WorldSession.h" #include "World.h" #include "ObjectAccessor.h" #include "Database/DatabaseEnv.h" -#include "SpellAuras.h" -#include "MapManager.h" #include "GridNotifiers.h" #include "CellImpl.h" #include "GridNotifiersImpl.h" @@ -38,7 +33,7 @@ DynamicObject::DynamicObject() : WorldObject() m_objectType |= TYPEMASK_DYNAMICOBJECT; m_objectTypeId = TYPEID_DYNAMICOBJECT; // 2.3.2 - 0x58 - m_updateFlag = (UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID | UPDATEFLAG_HASPOSITION); + m_updateFlag = (UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID | UPDATEFLAG_HAS_POSITION); m_valuesCount = DYNAMICOBJECT_END; } @@ -58,8 +53,20 @@ void DynamicObject::RemoveFromWorld() ///- Remove the dynamicObject from the accessor if(IsInWorld()) { - ObjectAccessor::Instance().RemoveObject(this); + if(m_effIndex == 4) + { + if(Unit *caster = GetCaster()) + { + if(caster->GetTypeId() == TYPEID_PLAYER) + ((Player*)caster)->SetViewpoint(this, false); + } + else + { + sLog.outCrash("DynamicObject::RemoveFromWorld cannot find viewpoint owner"); + } + } WorldObject::RemoveFromWorld(); + ObjectAccessor::Instance().RemoveObject(this); } } @@ -67,12 +74,12 @@ bool DynamicObject::Create( uint32 guidlow, Unit *caster, uint32 spellId, uint32 { SetInstanceId(caster->GetInstanceId()); - WorldObject::_Create(guidlow, HIGHGUID_DYNAMICOBJECT, caster->GetMapId()); + WorldObject::_Create(guidlow, HIGHGUID_DYNAMICOBJECT, caster->GetMapId(), caster->GetPhaseMask()); Relocate(x,y,z,0); if(!IsPositionValid()) { - sLog.outError("ERROR: DynamicObject (spell %u eff %u) not created. Suggested coordinates isn't valid (X: %f Y: %f)",spellId,effIndex,GetPositionX(),GetPositionY()); + sLog.outError("DynamicObject (spell %u eff %u) not created. Suggested coordinates isn't valid (X: %f Y: %f)",spellId,effIndex,GetPositionX(),GetPositionY()); return false; } @@ -81,7 +88,7 @@ bool DynamicObject::Create( uint32 guidlow, Unit *caster, uint32 spellId, uint32 SetUInt64Value( DYNAMICOBJECT_CASTER, caster->GetGUID() ); SetUInt32Value( DYNAMICOBJECT_BYTES, 0x00000001 ); SetUInt32Value( DYNAMICOBJECT_SPELLID, spellId ); - SetFloatValue( DYNAMICOBJECT_RADIUS, radius * 2); + SetFloatValue( DYNAMICOBJECT_RADIUS, radius * 2); //diameter? SetFloatValue( DYNAMICOBJECT_POS_X, x ); SetFloatValue( DYNAMICOBJECT_POS_Y, y ); SetFloatValue( DYNAMICOBJECT_POS_Z, z ); @@ -139,6 +146,7 @@ void DynamicObject::Update(uint32 p_time) void DynamicObject::Delete() { SendObjectDeSpawnAnim(GetGUID()); + RemoveFromWorld(); AddObjectToRemoveList(); } @@ -147,13 +155,12 @@ void DynamicObject::Delay(int32 delaytime) m_aliveDuration -= delaytime; for(AffectedSet::iterator iunit= m_affected.begin();iunit != m_affected.end();++iunit) if (*iunit) - (*iunit)->DelayAura(m_spellId, m_effIndex, delaytime); + (*iunit)->DelayAura(m_spellId, GetCaster()->GetGUID() , delaytime); } bool DynamicObject::isVisibleForInState(Player const* u, bool inVisibleList) const { return IsInWorld() && u->IsInWorld() - && (IsWithinDistInMap(u,World::GetMaxVisibleDistanceForObject()+(inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f), false) - || GetCasterGUID() == u->GetGUID()); + && (IsWithinDistInMap(u->m_seer,World::GetMaxVisibleDistanceForObject()+(inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f), false)); } diff --git a/src/game/DynamicObject.h b/src/game/DynamicObject.h index 1bcd29998f8..714b639e552 100644 --- a/src/game/DynamicObject.h +++ b/src/game/DynamicObject.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -50,14 +50,11 @@ class DynamicObject : public WorldObject void Delay(int32 delaytime); bool isVisibleForInState(Player const* u, bool inVisibleList) const; - void Say(const char* text, uint32 language, uint64 TargetGuid) { MonsterSay(text,language,TargetGuid); } - void Yell(const char* text, uint32 language, uint64 TargetGuid) { MonsterYell(text,language,TargetGuid); } - void TextEmote(const char* text, uint64 TargetGuid) { MonsterTextEmote(text,TargetGuid); } - void Whisper(const char* text, uint64 receiver) { MonsterWhisper(text,receiver); } void Say(int32 textId, uint32 language, uint64 TargetGuid) { MonsterSay(textId,language,TargetGuid); } void Yell(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYell(textId,language,TargetGuid); } void TextEmote(int32 textId, uint64 TargetGuid) { MonsterTextEmote(textId,TargetGuid); } void Whisper(int32 textId,uint64 receiver) { MonsterWhisper(textId,receiver); } + void YellToZone(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYellToZone(textId,language,TargetGuid); } GridReference<DynamicObject> &GetGridRef() { return m_gridRef; } protected: diff --git a/src/game/FleeingMovementGenerator.cpp b/src/game/FleeingMovementGenerator.cpp index 87800d2edc1..8f9eb6073d5 100644 --- a/src/game/FleeingMovementGenerator.cpp +++ b/src/game/FleeingMovementGenerator.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -301,19 +301,26 @@ FleeingMovementGenerator<T>::Initialize(T &owner) if(!&owner) return; - Unit * fright = ObjectAccessor::GetUnit(owner, i_frightGUID); - if(!fright) - return; - _Init(owner); owner.CastStop(); owner.addUnitState(UNIT_STAT_FLEEING); owner.SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING); owner.SetUInt64Value(UNIT_FIELD_TARGET, 0); owner.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); - i_caster_x = fright->GetPositionX(); - i_caster_y = fright->GetPositionY(); - i_caster_z = fright->GetPositionZ(); + + if(Unit * fright = ObjectAccessor::GetUnit(owner, i_frightGUID)) + { + i_caster_x = fright->GetPositionX(); + i_caster_y = fright->GetPositionY(); + i_caster_z = fright->GetPositionZ(); + } + else + { + i_caster_x = owner.GetPositionX(); + i_caster_y = owner.GetPositionY(); + i_caster_z = owner.GetPositionZ(); + } + i_only_forward = true; i_cur_angle = 0.0f; i_last_distance_from_caster = 0.0f; diff --git a/src/game/FleeingMovementGenerator.h b/src/game/FleeingMovementGenerator.h index 6a4f3593370..a6957545d41 100644 --- a/src/game/FleeingMovementGenerator.h +++ b/src/game/FleeingMovementGenerator.h @@ -1,7 +1,7 @@ /* -* Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> +* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * -* Copyright (C) 2008 Trinity <http://www.trinitycore.org/> +* Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,7 +24,6 @@ #include "MovementGenerator.h" #include "DestinationHolder.h" #include "Traveller.h" -#include "MapManager.h" template<class T> class TRINITY_DLL_SPEC FleeingMovementGenerator diff --git a/src/game/FollowerRefManager.h b/src/game/FollowerRefManager.h index 136fdbfeadb..093c85adcee 100644 --- a/src/game/FollowerRefManager.h +++ b/src/game/FollowerRefManager.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/FollowerReference.cpp b/src/game/FollowerReference.cpp index 44c8e8d55de..173f9881c54 100644 --- a/src/game/FollowerReference.cpp +++ b/src/game/FollowerReference.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/FollowerReference.h b/src/game/FollowerReference.h index 16fa880bcfe..3c0b3dc9e7c 100644 --- a/src/game/FollowerReference.h +++ b/src/game/FollowerReference.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/Formulas.h b/src/game/Formulas.h index f54917be6f3..041c0621c74 100644 --- a/src/game/Formulas.h +++ b/src/game/Formulas.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,7 +34,7 @@ namespace Trinity } namespace XP { - typedef enum XPColorChar { RED, ORANGE, YELLOW, GREEN, GRAY }; + enum XPColorChar { RED, ORANGE, YELLOW, GREEN, GRAY }; inline uint32 GetGrayLevel(uint32 pl_level) { @@ -80,8 +80,17 @@ namespace Trinity inline uint32 BaseGain(uint32 pl_level, uint32 mob_level, ContentLevels content) { - //TODO: need modifier for CONTENT_71_80 different from CONTENT_61_70? - const uint32 nBaseExp = content == CONTENT_1_60 ? 45 : 235; + uint32 nBaseExp; + switch(content) + { + case CONTENT_1_60: nBaseExp = 45; break; + case CONTENT_61_70: nBaseExp = 235; break; + case CONTENT_71_80: nBaseExp = 580; break; + default: + sLog.outError("BaseGain: Unsupported content level %u",content); + nBaseExp = 45; break; + } + if( mob_level >= pl_level ) { uint32 nLevelDiff = mob_level - pl_level; @@ -118,66 +127,6 @@ namespace Trinity return (uint32)(xp_gain*sWorld.getRate(RATE_XP_KILL)); } - inline uint32 xp_Diff(uint32 lvl) - { - if( lvl < 29 ) - return 0; - if( lvl == 29 ) - return 1; - if( lvl == 30 ) - return 3; - if( lvl == 31 ) - return 6; - else - return (5*(lvl-30)); - } - - inline uint32 mxp(uint32 lvl) - { - if (lvl < 60) - { - return (45 + (5*lvl)); - } - else - { - return (235 + (5*lvl)); - } - } - - inline uint32 xp_to_level(uint32 lvl) - { - uint32 xp = 0; - if (lvl < 60) - { - xp = (8*lvl + xp_Diff(lvl)) * mxp(lvl); - } - else if (lvl == 60) - { - xp = (155 + mxp(lvl) * (1344 - 70 - ((69 - lvl) * (7 + (69 - lvl) * 8 - 1)/2))); - } - else if (lvl < 70) - { - xp = (155 + mxp(lvl) * (1344 - ((69-lvl) * (7 + (69 - lvl) * 8 - 1)/2))); - }else - { - // level higher than 70 is not supported - xp = (uint32)(779700 * (pow(sWorld.getRate(RATE_XP_PAST_70), (int32)lvl - 69))); - return ((xp < 0x7fffffff) ? xp : 0x7fffffff); - } - - // The XP to Level is always rounded to the nearest 100 points (50 rounded to high). - xp = ((xp + 50) / 100) * 100; // use additional () for prevent free association operations in C++ - - if ((lvl > 10) && (lvl < 60)) // compute discount added in 2.3.x - { - uint32 discount = (lvl < 28) ? (lvl - 10) : 18; - xp = (xp * (100 - discount)) / 100; // apply discount - xp = (xp / 100) * 100; // floor to hundreds - } - - return xp; - } - inline float xp_in_group_rate(uint32 count, bool isRaid) { if(isRaid) diff --git a/src/game/GameEvent.cpp b/src/game/GameEventMgr.cpp index ae2dc488b86..53d01529fb9 100644 --- a/src/game/GameEvent.cpp +++ b/src/game/GameEventMgr.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,9 +18,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "GameEvent.h" +#include "GameEventMgr.h" #include "World.h" #include "ObjectMgr.h" +#include "PoolHandler.h" #include "ProgressBar.h" #include "Language.h" #include "Log.h" @@ -30,9 +31,9 @@ #include "Player.h" #include "BattleGroundMgr.h" -INSTANTIATE_SINGLETON_1(GameEvent); +INSTANTIATE_SINGLETON_1(GameEventMgr); -bool GameEvent::CheckOneGameEvent(uint16 entry) const +bool GameEventMgr::CheckOneGameEvent(uint16 entry) const { time_t currenttime = time(NULL); // if the state is conditions or nextphase, then the event should be active @@ -62,7 +63,7 @@ bool GameEvent::CheckOneGameEvent(uint16 entry) const return false; } -uint32 GameEvent::NextCheck(uint16 entry) const +uint32 GameEventMgr::NextCheck(uint16 entry) const { time_t currenttime = time(NULL); @@ -96,7 +97,7 @@ uint32 GameEvent::NextCheck(uint16 entry) const return delay; } -bool GameEvent::StartEvent( uint16 event_id, bool overwrite ) +bool GameEventMgr::StartEvent( uint16 event_id, bool overwrite ) { if(mGameEvent[event_id].state == GAMEEVENT_NORMAL) { @@ -135,7 +136,7 @@ bool GameEvent::StartEvent( uint16 event_id, bool overwrite ) } } -void GameEvent::StopEvent( uint16 event_id, bool overwrite ) +void GameEventMgr::StopEvent( uint16 event_id, bool overwrite ) { bool serverwide_evt = mGameEvent[event_id].state != GAMEEVENT_NORMAL; @@ -167,7 +168,7 @@ void GameEvent::StopEvent( uint16 event_id, bool overwrite ) } } -void GameEvent::LoadFromDB() +void GameEventMgr::LoadFromDB() { { QueryResult *result = WorldDatabase.Query("SELECT MAX(entry) FROM game_event"); @@ -186,54 +187,68 @@ void GameEvent::LoadFromDB() mGameEvent.resize(max_event_id+1); } - QueryResult *result = WorldDatabase.Query("SELECT entry,UNIX_TIMESTAMP(start_time),UNIX_TIMESTAMP(end_time),occurence,length,description,world_event FROM game_event"); + QueryResult *result = WorldDatabase.Query("SELECT entry,UNIX_TIMESTAMP(start_time),UNIX_TIMESTAMP(end_time),occurence,length,holiday,description,world_event FROM game_event"); if( !result ) { mGameEvent.clear(); - sLog.outString(">> Table game_event is empty:"); + sLog.outString(">> Table game_event is empty!"); sLog.outString(); return; } uint32 count = 0; - barGoLink bar( result->GetRowCount() ); - do { - ++count; - Field *fields = result->Fetch(); + barGoLink bar( result->GetRowCount() ); + do + { + ++count; + Field *fields = result->Fetch(); - bar.step(); + bar.step(); - uint16 event_id = fields[0].GetUInt16(); - if(event_id==0) - { - sLog.outErrorDb("`game_event` game event id (%i) is reserved and can't be used.",event_id); - continue; - } + uint16 event_id = fields[0].GetUInt16(); + if(event_id==0) + { + sLog.outErrorDb("`game_event` game event id (%i) is reserved and can't be used.",event_id); + continue; + } - GameEventData& pGameEvent = mGameEvent[event_id]; - uint64 starttime = fields[1].GetUInt64(); - pGameEvent.start = time_t(starttime); - uint64 endtime = fields[2].GetUInt64(); - pGameEvent.end = time_t(endtime); - pGameEvent.occurence = fields[3].GetUInt32(); - pGameEvent.length = fields[4].GetUInt32(); - pGameEvent.description = fields[5].GetCppString(); - pGameEvent.state = (GameEventState)(fields[6].GetUInt8()); - pGameEvent.nextstart = 0; - - if(pGameEvent.length==0 && pGameEvent.state == GAMEEVENT_NORMAL) // length>0 is validity check - { - sLog.outErrorDb("`game_event` game event id (%i) isn't a world event and has length = 0, thus it can't be used.",event_id); - continue; - } + GameEventData& pGameEvent = mGameEvent[event_id]; + uint64 starttime = fields[1].GetUInt64(); + pGameEvent.start = time_t(starttime); + uint64 endtime = fields[2].GetUInt64(); + pGameEvent.end = time_t(endtime); + pGameEvent.occurence = fields[3].GetUInt32(); + pGameEvent.length = fields[4].GetUInt32(); + pGameEvent.holiday_id = fields[5].GetUInt32(); - } while( result->NextRow() ); + pGameEvent.state = (GameEventState)(fields[7].GetUInt8()); + pGameEvent.nextstart = 0; - sLog.outString(); - sLog.outString( ">> Loaded %u game events", count ); - delete result; + if(pGameEvent.length==0 && pGameEvent.state == GAMEEVENT_NORMAL) // length>0 is validity check + { + sLog.outErrorDb("`game_event` game event id (%i) isn't a world event and has length = 0, thus it can't be used.",event_id); + continue; + } + + if(pGameEvent.holiday_id) + { + if(!sHolidaysStore.LookupEntry(pGameEvent.holiday_id)) + { + sLog.outErrorDb("`game_event` game event id (%i) have not existed holiday id %u.",event_id,pGameEvent.holiday_id); + pGameEvent.holiday_id = 0; + } + } + + pGameEvent.description = fields[6].GetCppString(); + + } while( result->NextRow() ); + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u game events", count ); + } // load game event saves // 0 1 2 @@ -346,8 +361,8 @@ void GameEvent::LoadFromDB() count = 0; if( !result ) { - barGoLink bar2(1); - bar2.step(); + barGoLink bar(1); + bar.step(); sLog.outString(); sLog.outString(">> Loaded %u creatures in game events", count ); @@ -355,12 +370,12 @@ void GameEvent::LoadFromDB() else { - barGoLink bar2( result->GetRowCount() ); + barGoLink bar( result->GetRowCount() ); do { Field *fields = result->Fetch(); - bar2.step(); + bar.step(); uint32 guid = fields[0].GetUInt32(); int16 event_id = fields[1].GetInt16(); @@ -378,9 +393,10 @@ void GameEvent::LoadFromDB() crelist.push_back(guid); } while( result->NextRow() ); + delete result; + sLog.outString(); sLog.outString( ">> Loaded %u creatures in game events", count ); - delete result; } mGameEventGameobjectGuids.resize(mGameEvent.size()*2-1); @@ -391,8 +407,8 @@ void GameEvent::LoadFromDB() count = 0; if( !result ) { - barGoLink bar3(1); - bar3.step(); + barGoLink bar(1); + bar.step(); sLog.outString(); sLog.outString(">> Loaded %u gameobjects in game events", count ); @@ -400,12 +416,12 @@ void GameEvent::LoadFromDB() else { - barGoLink bar3( result->GetRowCount() ); + barGoLink bar( result->GetRowCount() ); do { Field *fields = result->Fetch(); - bar3.step(); + bar.step(); uint32 guid = fields[0].GetUInt32(); int16 event_id = fields[1].GetInt16(); @@ -423,10 +439,10 @@ void GameEvent::LoadFromDB() golist.push_back(guid); } while( result->NextRow() ); + delete result; + sLog.outString(); sLog.outString( ">> Loaded %u gameobjects in game events", count ); - - delete result; } mGameEventModelEquip.resize(mGameEvent.size()); @@ -439,8 +455,8 @@ void GameEvent::LoadFromDB() count = 0; if( !result ) { - barGoLink bar3(1); - bar3.step(); + barGoLink bar(1); + bar.step(); sLog.outString(); sLog.outString(">> Loaded %u model/equipment changes in game events", count ); @@ -448,12 +464,12 @@ void GameEvent::LoadFromDB() else { - barGoLink bar3( result->GetRowCount() ); + barGoLink bar( result->GetRowCount() ); do { Field *fields = result->Fetch(); - bar3.step(); + bar.step(); uint32 guid = fields[0].GetUInt32(); uint16 event_id = fields[1].GetUInt16(); @@ -483,10 +499,10 @@ void GameEvent::LoadFromDB() equiplist.push_back(std::pair<uint32, ModelEquip>(guid, newModelEquipSet)); } while( result->NextRow() ); + delete result; + sLog.outString(); sLog.outString( ">> Loaded %u model/equipment changes in game events", count ); - - delete result; } mGameEventCreatureQuests.resize(mGameEvent.size()); @@ -496,8 +512,8 @@ void GameEvent::LoadFromDB() count = 0; if( !result ) { - barGoLink bar3(1); - bar3.step(); + barGoLink bar(1); + bar.step(); sLog.outString(); sLog.outString(">> Loaded %u quests additions in game events", count ); @@ -505,12 +521,12 @@ void GameEvent::LoadFromDB() else { - barGoLink bar3( result->GetRowCount() ); + barGoLink bar( result->GetRowCount() ); do { Field *fields = result->Fetch(); - bar3.step(); + bar.step(); uint32 id = fields[0].GetUInt32(); uint32 quest = fields[1].GetUInt32(); uint16 event_id = fields[2].GetUInt16(); @@ -569,10 +585,10 @@ void GameEvent::LoadFromDB() questlist.push_back(QuestRelation(id, quest)); } while( result->NextRow() ); + delete result; + sLog.outString(); sLog.outString( ">> Loaded %u quests additions in game events", count ); - - delete result; } // Load quest to (event,condition) mapping @@ -913,9 +929,64 @@ void GameEvent::LoadFromDB() delete result; } + + //////////////////////// + // GameEventPool + //////////////////////// + + mGameEventPoolIds.resize(mGameEvent.size()*2-1); + // 1 2 + result = WorldDatabase.Query("SELECT pool_template.entry, game_event_pool.event " + "FROM pool_template JOIN game_event_pool ON pool_template.entry = game_event_pool.pool_entry"); + + count = 0; + if( !result ) + { + barGoLink bar2(1); + bar2.step(); + + sLog.outString(); + sLog.outString(">> Loaded %u pools in game events", count ); + } + else + { + + barGoLink bar2( result->GetRowCount() ); + do + { + Field *fields = result->Fetch(); + + bar2.step(); + + uint32 entry = fields[0].GetUInt16(); + int16 event_id = fields[1].GetInt16(); + + int32 internal_event_id = mGameEvent.size() + event_id - 1; + + if(internal_event_id < 0 || internal_event_id >= mGameEventPoolIds.size()) + { + sLog.outErrorDb("`game_event_pool` game event id (%i) is out of range compared to max event id in `game_event`",event_id); + continue; + } + + if (!poolhandler.CheckPool(entry)) + { + sLog.outErrorDb("Pool Id (%u) has all creatures or gameobjects with explicit chance sum <>100 and no equal chance defined. The pool system cannot pick one to spawn.", entry); + continue; + } + + ++count; + IdList& poollist = mGameEventPoolIds[internal_event_id]; + poollist.push_back(entry); + + } while( result->NextRow() ); + sLog.outString(); + sLog.outString( ">> Loaded %u pools in game events", count ); + delete result; + } } -uint32 GameEvent::GetNPCFlag(Creature * cr) +uint32 GameEventMgr::GetNPCFlag(Creature * cr) { uint32 mask = 0; uint32 guid = cr->GetDBTableGUIDLow(); @@ -932,7 +1003,7 @@ uint32 GameEvent::GetNPCFlag(Creature * cr) return mask; } -uint32 GameEvent::GetNpcTextId(uint32 guid) +uint32 GameEventMgr::GetNpcTextId(uint32 guid) { GuidEventNpcGossipIdMap::iterator itr = mNPCGossipIds.find(guid); if(itr != mNPCGossipIds.end()) @@ -941,7 +1012,7 @@ uint32 GameEvent::GetNpcTextId(uint32 guid) return 0; } -uint32 GameEvent::Initialize() // return the next event delay in ms +uint32 GameEventMgr::Initialize() // return the next event delay in ms { m_ActiveEvents.clear(); uint32 delay = Update(); @@ -950,7 +1021,7 @@ uint32 GameEvent::Initialize() // return the next e return delay; } -uint32 GameEvent::Update() // return the next event delay in ms +uint32 GameEventMgr::Update() // return the next event delay in ms { time_t currenttime = time(NULL); uint32 nextEventDelay = max_ge_check_delay; // 1 day @@ -1017,10 +1088,10 @@ uint32 GameEvent::Update() // return the next e for(std::set<uint16>::iterator itr = deactivate.begin(); itr != deactivate.end(); ++itr) StopEvent(*itr); sLog.outDetail("Next game event check in %u seconds.", nextEventDelay + 1); - return (nextEventDelay + 1) * 1000; // Add 1 second to be sure event has started/stopped at next call + return (nextEventDelay + 1) * IN_MILISECONDS; // Add 1 second to be sure event has started/stopped at next call } -void GameEvent::UnApplyEvent(uint16 event_id) +void GameEventMgr::UnApplyEvent(uint16 event_id) { sLog.outString("GameEvent %u \"%s\" removed.", event_id, mGameEvent[event_id].description.c_str()); // un-spawn positive event tagged objects @@ -1040,7 +1111,7 @@ void GameEvent::UnApplyEvent(uint16 event_id) UpdateBattleGroundSettings(); } -void GameEvent::ApplyNewEvent(uint16 event_id) +void GameEventMgr::ApplyNewEvent(uint16 event_id) { switch(sWorld.getConfig(CONFIG_EVENT_ANNOUNCE)) { @@ -1070,7 +1141,7 @@ void GameEvent::ApplyNewEvent(uint16 event_id) UpdateBattleGroundSettings(); } -void GameEvent::UpdateEventNPCFlags(uint16 event_id) +void GameEventMgr::UpdateEventNPCFlags(uint16 event_id) { // go through the creatures whose npcflags are changed in the event for(NPCFlagList::iterator itr = mGameEventNPCFlags[event_id].begin(); itr != mGameEventNPCFlags[event_id].end(); ++itr) @@ -1096,7 +1167,7 @@ void GameEvent::UpdateEventNPCFlags(uint16 event_id) } } -void GameEvent::UpdateBattleGroundSettings() +void GameEventMgr::UpdateBattleGroundSettings() { uint32 mask = 0; for(ActiveEvents::const_iterator itr = m_ActiveEvents.begin(); itr != m_ActiveEvents.end(); ++itr ) @@ -1104,7 +1175,7 @@ void GameEvent::UpdateBattleGroundSettings() sBattleGroundMgr.SetHolidayWeekends(mask); } -void GameEvent::UpdateEventNPCVendor(uint16 event_id, bool activate) +void GameEventMgr::UpdateEventNPCVendor(uint16 event_id, bool activate) { for(NPCVendorList::iterator itr = mGameEventVendors[event_id].begin(); itr != mGameEventVendors[event_id].end(); ++itr) { @@ -1115,13 +1186,13 @@ void GameEvent::UpdateEventNPCVendor(uint16 event_id, bool activate) } } -void GameEvent::GameEventSpawn(int16 event_id) +void GameEventMgr::GameEventSpawn(int16 event_id) { int32 internal_event_id = mGameEvent.size() + event_id - 1; if(internal_event_id < 0 || internal_event_id >= mGameEventCreatureGuids.size()) { - sLog.outError("GameEvent::GameEventSpawn attempt access to out of range mGameEventCreatureGuids element %i (size: %u)",internal_event_id,mGameEventCreatureGuids.size()); + sLog.outError("GameEventMgr::GameEventSpawn attempt access to out of range mGameEventCreatureGuids element %i (size: %u)",internal_event_id,mGameEventCreatureGuids.size()); return; } @@ -1154,7 +1225,7 @@ void GameEvent::GameEventSpawn(int16 event_id) if(internal_event_id < 0 || internal_event_id >= mGameEventGameobjectGuids.size()) { - sLog.outError("GameEvent::GameEventSpawn attempt access to out of range mGameEventGameobjectGuids element %i (size: %u)",internal_event_id,mGameEventGameobjectGuids.size()); + sLog.outError("GameEventMgr::GameEventSpawn attempt access to out of range mGameEventGameobjectGuids element %i (size: %u)",internal_event_id,mGameEventGameobjectGuids.size()); return; } @@ -1185,15 +1256,26 @@ void GameEvent::GameEventSpawn(int16 event_id) } } } + + if(internal_event_id < 0 || internal_event_id >= mGameEventPoolIds.size()) + { + sLog.outError("GameEventMgr::GameEventSpawn attempt access to out of range mGameEventPoolIds element %i (size: %u)",internal_event_id,mGameEventPoolIds.size()); + return; + } + + for (IdList::iterator itr = mGameEventPoolIds[internal_event_id].begin();itr != mGameEventPoolIds[internal_event_id].end();++itr) + { + poolhandler.SpawnPool(*itr); + } } -void GameEvent::GameEventUnspawn(int16 event_id) +void GameEventMgr::GameEventUnspawn(int16 event_id) { int32 internal_event_id = mGameEvent.size() + event_id - 1; if(internal_event_id < 0 || internal_event_id >= mGameEventCreatureGuids.size()) { - sLog.outError("GameEvent::GameEventUnspawn attempt access to out of range mGameEventCreatureGuids element %i (size: %u)",internal_event_id,mGameEventCreatureGuids.size()); + sLog.outError("GameEventMgr::GameEventUnspawn attempt access to out of range mGameEventCreatureGuids element %i (size: %u)",internal_event_id,mGameEventCreatureGuids.size()); return; } @@ -1217,7 +1299,7 @@ void GameEvent::GameEventUnspawn(int16 event_id) if(internal_event_id < 0 || internal_event_id >= mGameEventGameobjectGuids.size()) { - sLog.outError("GameEvent::GameEventUnspawn attempt access to out of range mGameEventGameobjectGuids element %i (size: %u)",internal_event_id,mGameEventGameobjectGuids.size()); + sLog.outError("GameEventMgr::GameEventUnspawn attempt access to out of range mGameEventGameobjectGuids element %i (size: %u)",internal_event_id,mGameEventGameobjectGuids.size()); return; } @@ -1235,9 +1317,19 @@ void GameEvent::GameEventUnspawn(int16 event_id) pGameobject->AddObjectToRemoveList(); } } + if(internal_event_id < 0 || internal_event_id >= mGameEventPoolIds.size()) + { + sLog.outError("GameEventMgr::GameEventUnspawn attempt access to out of range mGameEventPoolIds element %i (size: %u)",internal_event_id,mGameEventPoolIds.size()); + return; + } + + for (IdList::iterator itr = mGameEventPoolIds[internal_event_id].begin();itr != mGameEventPoolIds[internal_event_id].end();++itr) + { + poolhandler.DespawnPool(*itr); + } } -void GameEvent::ChangeEquipOrModel(int16 event_id, bool activate) +void GameEventMgr::ChangeEquipOrModel(int16 event_id, bool activate) { for(ModelEquipList::iterator itr = mGameEventModelEquip[event_id].begin();itr != mGameEventModelEquip[event_id].end();++itr) { @@ -1316,7 +1408,7 @@ void GameEvent::ChangeEquipOrModel(int16 event_id, bool activate) } } -bool GameEvent::hasCreatureQuestActiveEventExcept(uint32 quest_id, uint16 event_id) +bool GameEventMgr::hasCreatureQuestActiveEventExcept(uint32 quest_id, uint16 event_id) { for(ActiveEvents::iterator e_itr = m_ActiveEvents.begin(); e_itr != m_ActiveEvents.end(); ++e_itr) { @@ -1330,7 +1422,7 @@ bool GameEvent::hasCreatureQuestActiveEventExcept(uint32 quest_id, uint16 event_ return false; } -bool GameEvent::hasGameObjectQuestActiveEventExcept(uint32 quest_id, uint16 event_id) +bool GameEventMgr::hasGameObjectQuestActiveEventExcept(uint32 quest_id, uint16 event_id) { for(ActiveEvents::iterator e_itr = m_ActiveEvents.begin(); e_itr != m_ActiveEvents.end(); ++e_itr) { @@ -1343,7 +1435,7 @@ bool GameEvent::hasGameObjectQuestActiveEventExcept(uint32 quest_id, uint16 even } return false; } -bool GameEvent::hasCreatureActiveEventExcept(uint32 creature_id, uint16 event_id) +bool GameEventMgr::hasCreatureActiveEventExcept(uint32 creature_id, uint16 event_id) { for(ActiveEvents::iterator e_itr = m_ActiveEvents.begin(); e_itr != m_ActiveEvents.end(); ++e_itr) { @@ -1359,7 +1451,7 @@ bool GameEvent::hasCreatureActiveEventExcept(uint32 creature_id, uint16 event_id } return false; } -bool GameEvent::hasGameObjectActiveEventExcept(uint32 go_id, uint16 event_id) +bool GameEventMgr::hasGameObjectActiveEventExcept(uint32 go_id, uint16 event_id) { for(ActiveEvents::iterator e_itr = m_ActiveEvents.begin(); e_itr != m_ActiveEvents.end(); ++e_itr) { @@ -1376,7 +1468,7 @@ bool GameEvent::hasGameObjectActiveEventExcept(uint32 go_id, uint16 event_id) return false; } -void GameEvent::UpdateEventQuests(uint16 event_id, bool Activate) +void GameEventMgr::UpdateEventQuests(uint16 event_id, bool Activate) { QuestRelList::iterator itr; for (itr = mGameEventCreatureQuests[event_id].begin();itr != mGameEventCreatureQuests[event_id].end();++itr) @@ -1430,12 +1522,12 @@ void GameEvent::UpdateEventQuests(uint16 event_id, bool Activate) } }} -GameEvent::GameEvent() +GameEventMgr::GameEventMgr() { isSystemInit = false; } -void GameEvent::HandleQuestComplete(uint32 quest_id) +void GameEventMgr::HandleQuestComplete(uint32 quest_id) { // translate the quest to event and condition QuestIdToEventConditionMap::iterator itr = mQuestToEventConditions.find(quest_id); @@ -1481,7 +1573,7 @@ void GameEvent::HandleQuestComplete(uint32 quest_id) } } -bool GameEvent::CheckOneGameEventConditions(uint16 event_id) +bool GameEventMgr::CheckOneGameEventConditions(uint16 event_id) { for(std::map<uint32,GameEventFinishCondition>::iterator itr = mGameEvent[event_id].conditions.begin(); itr != mGameEvent[event_id].conditions.end(); ++itr) if(itr->second.done < itr->second.reqNum) @@ -1498,7 +1590,7 @@ bool GameEvent::CheckOneGameEventConditions(uint16 event_id) return true; } -void GameEvent::SaveWorldEventStateToDB(uint16 event_id) +void GameEventMgr::SaveWorldEventStateToDB(uint16 event_id) { CharacterDatabase.BeginTransaction(); CharacterDatabase.PExecute("DELETE FROM game_event_save WHERE event_id = '%u'",event_id); @@ -1509,7 +1601,7 @@ void GameEvent::SaveWorldEventStateToDB(uint16 event_id) CharacterDatabase.CommitTransaction(); } -void GameEvent::HandleWorldEventGossip(Player *plr, Creature *c) +void GameEventMgr::HandleWorldEventGossip(Player *plr, Creature *c) { // this function is used to send world state update before sending gossip menu // find the npc's gossip id (if set) in an active game event @@ -1521,7 +1613,7 @@ void GameEvent::HandleWorldEventGossip(Player *plr, Creature *c) SendWorldStateUpdate(plr, itr->second.first); } -void GameEvent::SendWorldStateUpdate(Player * plr, uint16 event_id) +void GameEventMgr::SendWorldStateUpdate(Player * plr, uint16 event_id) { std::map<uint32,GameEventFinishCondition>::iterator itr; for(itr = mGameEvent[event_id].conditions.begin(); itr !=mGameEvent[event_id].conditions.end(); ++itr) @@ -1533,3 +1625,14 @@ void GameEvent::SendWorldStateUpdate(Player * plr, uint16 event_id) } } +TRINITY_DLL_SPEC bool IsHolidayActive( HolidayIds id ) +{ + GameEventMgr::GameEventDataMap const& events = gameeventmgr.GetEventMap(); + GameEventMgr::ActiveEvents const& ae = gameeventmgr.GetActiveEventList(); + + for(GameEventMgr::ActiveEvents::const_iterator itr = ae.begin(); itr != ae.end(); ++itr) + if(events[id].holiday_id==id) + return true; + + return false; +} diff --git a/src/game/GameEvent.h b/src/game/GameEventMgr.h index 9f69aa9e6ea..a7b52f0ab72 100644 --- a/src/game/GameEvent.h +++ b/src/game/GameEventMgr.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,12 +18,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef TRINITY_GAMEEVENT_H -#define TRINITY_GAMEEVENT_H +#ifndef TRINITY_GAMEEVENT_MGR_H +#define TRINITY_GAMEEVENT_MGR_H +#include "Common.h" +#include "SharedDefines.h" #include "Platform/Define.h" -#include "Creature.h" -#include "GameObject.h" +#include "Policies/Singleton.h" #define max_ge_check_delay 86400 // 1 day in seconds @@ -54,11 +55,12 @@ struct GameEventQuestToEventConditionNum struct GameEventData { GameEventData() : start(1),end(0),nextstart(0),occurence(0),length(0),state(GAMEEVENT_NORMAL) {} - time_t start; // occurs after this time - time_t end; // occurs before this time - time_t nextstart; // after this time the follow-up events count this phase completed - uint32 occurence; // time between end and start - uint32 length; // length of the event (minutes) after finishing all conditions + time_t start; // occurs after this time + time_t end; // occurs before this time + time_t nextstart; // after this time the follow-up events count this phase completed + uint32 occurence; // time between end and start + uint32 length; // length of the event (minutes) after finishing all conditions + uint32 holiday_id; GameEventState state; // state of the game event, these are saved into the game_event table on change! std::map<uint32 /*condition id*/, GameEventFinishCondition> conditions; // conditions to finish std::set<uint16 /*gameevent id*/> prerequisite_events; // events that must be completed before starting this event @@ -85,11 +87,13 @@ struct NPCVendorEntry }; class Player; -class GameEvent +class Creature; + +class GameEventMgr { public: - GameEvent(); - ~GameEvent() {}; + GameEventMgr(); + ~GameEventMgr() {}; typedef std::set<uint16> ActiveEvents; typedef std::vector<GameEventData> GameEventDataMap; ActiveEvents const& GetActiveEventList() const { return m_ActiveEvents; } @@ -127,7 +131,9 @@ class GameEvent bool hasGameObjectActiveEventExcept(uint32 go_guid, uint16 event_id); protected: typedef std::list<uint32> GuidList; + typedef std::list<uint16> IdList; typedef std::vector<GuidList> GameEventGuidMap; + typedef std::vector<IdList> GameEventIdMap; typedef std::pair<uint32, ModelEquip> ModelEquipPair; typedef std::list<ModelEquipPair> ModelEquipList; typedef std::vector<ModelEquipList> GameEventModelEquipMap; @@ -149,6 +155,7 @@ class GameEvent GameEventModelEquipMap mGameEventModelEquip; GameEventGuidMap mGameEventCreatureGuids; GameEventGuidMap mGameEventGameobjectGuids; + GameEventIdMap mGameEventPoolIds; GameEventDataMap mGameEvent; GameEventBitmask mGameEventBattleGroundHolidays; QuestIdToEventConditionMap mQuestToEventConditions; @@ -158,6 +165,9 @@ class GameEvent bool isSystemInit; }; -#define gameeventmgr Trinity::Singleton<GameEvent>::Instance() +#define gameeventmgr Trinity::Singleton<GameEventMgr>::Instance() + +TRINITY_DLL_SPEC bool IsHolidayActive(HolidayIds id); + #endif diff --git a/src/game/GameObject.cpp b/src/game/GameObject.cpp index d1bb0362872..89a03cac016 100644 --- a/src/game/GameObject.cpp +++ b/src/game/GameObject.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,15 +22,14 @@ #include "QuestDef.h" #include "GameObject.h" #include "ObjectMgr.h" +#include "PoolHandler.h" #include "SpellMgr.h" #include "Spell.h" #include "UpdateMask.h" #include "Opcodes.h" #include "WorldPacket.h" -#include "WorldSession.h" #include "World.h" #include "Database/DatabaseEnv.h" -#include "MapManager.h" #include "LootMgr.h" #include "GridNotifiers.h" #include "GridNotifiersImpl.h" @@ -46,7 +45,7 @@ GameObject::GameObject() : WorldObject() m_objectType |= TYPEMASK_GAMEOBJECT; m_objectTypeId = TYPEID_GAMEOBJECT; // 2.3.2 - 0x58 - m_updateFlag = (UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID | UPDATEFLAG_HASPOSITION); + m_updateFlag = (UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID | UPDATEFLAG_HAS_POSITION); m_valuesCount = GAMEOBJECT_END; m_respawnTime = 0; @@ -64,9 +63,9 @@ GameObject::GameObject() : WorldObject() GameObject::~GameObject() { - if(m_uint32Values) // field array can be not exist if GameOBject not loaded + /*if(m_uint32Values) // field array can be not exist if GameOBject not loaded { - // crash possible at access to deleted GO in Unit::m_gameobj + // Possible crash at access to deleted GO in Unit::m_gameobj uint64 owner_guid = GetOwnerGUID(); if(owner_guid) { @@ -76,7 +75,7 @@ GameObject::~GameObject() else if(!IS_PLAYER_GUID(owner_guid)) sLog.outError("Delete GameObject (GUID: %u Entry: %u ) that have references in not found creature %u GO list. Crash possible later.",GetGUIDLow(),GetGOInfo()->id,GUID_LOPART(owner_guid)); } - } + }*/ } void GameObject::AddToWorld() @@ -94,20 +93,30 @@ void GameObject::RemoveFromWorld() ///- Remove the gameobject from the accessor if(IsInWorld()) { - ObjectAccessor::Instance().RemoveObject(this); + // Possible crash at access to deleted GO in Unit::m_gameobj + if(uint64 owner_guid = GetOwnerGUID()) + { + Unit* owner = ObjectAccessor::GetUnit(*this,owner_guid); + if(owner) + owner->RemoveGameObject(this,false); + else if(!IS_PLAYER_GUID(owner_guid)) + sLog.outError("Delete GameObject (GUID: %u Entry: %u ) that have references in not found creature %u GO list. Crash possible later.",GetGUIDLow(),GetGOInfo()->id,GUID_LOPART(owner_guid)); + } WorldObject::RemoveFromWorld(); + ObjectAccessor::Instance().RemoveObject(this); } } -bool GameObject::Create(uint32 guidlow, uint32 name_id, Map *map, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, uint32 go_state, uint32 ArtKit) +bool GameObject::Create(uint32 guidlow, uint32 name_id, Map *map, uint32 phaseMask, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, uint32 go_state, uint32 ArtKit) { Relocate(x,y,z,ang); SetMapId(map->GetId()); SetInstanceId(map->GetInstanceId()); + SetPhaseMask(phaseMask,false); if(!IsPositionValid()) { - sLog.outError("ERROR: Gameobject (GUID: %u Entry: %u ) not created. Suggested coordinates isn't valid (X: %f Y: %f)",guidlow,name_id,x,y); + sLog.outError("Gameobject (GUID: %u Entry: %u ) not created. Suggested coordinates isn't valid (X: %f Y: %f)",guidlow,name_id,x,y); return false; } @@ -131,19 +140,18 @@ bool GameObject::Create(uint32 guidlow, uint32 name_id, Map *map, float x, float SetFloatValue(GAMEOBJECT_POS_X, x); SetFloatValue(GAMEOBJECT_POS_Y, y); SetFloatValue(GAMEOBJECT_POS_Z, z); - SetFloatValue(GAMEOBJECT_FACING, ang); //this is not facing angle - SetFloatValue (GAMEOBJECT_ROTATION, rotation0); - SetFloatValue (GAMEOBJECT_ROTATION+1, rotation1); - SetFloatValue (GAMEOBJECT_ROTATION+2, rotation2); - SetFloatValue (GAMEOBJECT_ROTATION+3, rotation3); + SetFloatValue(GAMEOBJECT_PARENTROTATION+0, rotation0); + SetFloatValue(GAMEOBJECT_PARENTROTATION+1, rotation1); + + UpdateRotationFields(rotation2,rotation3); // GAMEOBJECT_FACING, GAMEOBJECT_ROTATION, GAMEOBJECT_PARENTROTATION+2/3 SetFloatValue(OBJECT_FIELD_SCALE_X, goinfo->size); SetUInt32Value(GAMEOBJECT_FACTION, goinfo->faction); SetUInt32Value(GAMEOBJECT_FLAGS, goinfo->flags); - SetUInt32Value(OBJECT_FIELD_ENTRY, goinfo->id); + SetEntry(goinfo->id); SetUInt32Value(GAMEOBJECT_DISPLAYID, goinfo->displayId); @@ -152,7 +160,7 @@ bool GameObject::Create(uint32 guidlow, uint32 name_id, Map *map, float x, float SetGoAnimProgress(animprogress); - SetUInt32Value (GAMEOBJECT_ARTKIT, ArtKit); + SetByteValue(GAMEOBJECT_BYTES_1, 2, ArtKit); // Spell charges for GAMEOBJECT_TYPE_SPELLCASTER (22) if (goinfo->type == GAMEOBJECT_TYPE_SPELLCASTER) @@ -270,7 +278,11 @@ void GameObject::Update(uint32 /*p_time*/) return; } // respawn timer - MapManager::Instance().GetMap(GetMapId(), this)->Add(this); + uint16 poolid = poolhandler.IsPartOfAPool(GetGUIDLow(), TYPEID_GAMEOBJECT); + if (poolid) + poolhandler.UpdatePool(poolid, GetGUIDLow(), TYPEID_GAMEOBJECT); + else + GetMap()->Add(this); break; } } @@ -316,18 +328,18 @@ void GameObject::Update(uint32 /*p_time*/) if(owner && NeedDespawn) // hunter trap { Trinity::AnyUnfriendlyNoTotemUnitInObjectRangeCheck u_check(this, owner, radius); - Trinity::UnitSearcher<Trinity::AnyUnfriendlyNoTotemUnitInObjectRangeCheck> checker(ok, u_check); + Trinity::UnitSearcher<Trinity::AnyUnfriendlyNoTotemUnitInObjectRangeCheck> checker(this, ok, u_check); CellLock<GridReadGuard> cell_lock(cell, p); TypeContainerVisitor<Trinity::UnitSearcher<Trinity::AnyUnfriendlyNoTotemUnitInObjectRangeCheck>, GridTypeMapContainer > grid_object_checker(checker); - cell_lock->Visit(cell_lock, grid_object_checker, *MapManager::Instance().GetMap(GetMapId(), this)); + cell_lock->Visit(cell_lock, grid_object_checker, *GetMap()); // or unfriendly player/pet if(!ok) { TypeContainerVisitor<Trinity::UnitSearcher<Trinity::AnyUnfriendlyNoTotemUnitInObjectRangeCheck>, WorldTypeMapContainer > world_object_checker(checker); - cell_lock->Visit(cell_lock, world_object_checker, *MapManager::Instance().GetMap(GetMapId(), this)); + cell_lock->Visit(cell_lock, world_object_checker, *GetMap()); } } else // environmental trap @@ -336,13 +348,13 @@ void GameObject::Update(uint32 /*p_time*/) // affect only players Player* p_ok = NULL; - Trinity::AnyPlayerInObjectRangeCheck p_check(this, radius); - Trinity::PlayerSearcher<Trinity::AnyPlayerInObjectRangeCheck> checker(p_ok, p_check); + MaNGOS::AnyPlayerInObjectRangeCheck p_check(this, radius); + MaNGOS::PlayerSearcher<MaNGOS::AnyPlayerInObjectRangeCheck> checker(this,p_ok, p_check); CellLock<GridReadGuard> cell_lock(cell, p); TypeContainerVisitor<Trinity::PlayerSearcher<Trinity::AnyPlayerInObjectRangeCheck>, WorldTypeMapContainer > world_object_checker(checker); - cell_lock->Visit(cell_lock, world_object_checker, *MapManager::Instance().GetMap(GetMapId(), this)); + cell_lock->Visit(cell_lock, world_object_checker, *GetMap()); ok = p_ok; } @@ -350,8 +362,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) @@ -401,7 +413,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(); @@ -420,7 +432,7 @@ void GameObject::Update(uint32 /*p_time*/) //burning flags in some battlegrounds, if you find better condition, just add it if (GetGoAnimProgress() > 0) { - SendObjectDeSpawnAnim(this->GetGUID()); + SendObjectDeSpawnAnim(GetGUID()); //reset flags SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags); } @@ -457,7 +469,7 @@ void GameObject::Refresh() return; if(isSpawned()) - MapManager::Instance().GetMap(GetMapId(), this)->Add(this); + GetMap()->Add(this); } void GameObject::AddUniqueUse(Player* player) @@ -473,21 +485,26 @@ void GameObject::Delete() SetGoState(1); SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags); - AddObjectToRemoveList(); + uint16 poolid = poolhandler.IsPartOfAPool(GetGUIDLow(), TYPEID_GAMEOBJECT); + if (poolid) + poolhandler.UpdatePool(poolid, GetGUIDLow(), TYPEID_GAMEOBJECT); + else + AddObjectToRemoveList(); } -void GameObject::getFishLoot(Loot *fishloot) +void GameObject::getFishLoot(Loot *fishloot, Player* loot_owner) { fishloot->clear(); - uint32 subzone = GetAreaId(); + uint32 zone, subzone; + GetZoneAndAreaId(zone,subzone); // if subzone loot exist use it if(LootTemplates_Fishing.HaveLootFor(subzone)) - fishloot->FillLoot(subzone, LootTemplates_Fishing, NULL); + fishloot->FillLoot(subzone, LootTemplates_Fishing, loot_owner,true); // else use zone loot else - fishloot->FillLoot(GetZoneId(), LootTemplates_Fishing, NULL); + fishloot->FillLoot(zone, LootTemplates_Fishing, loot_owner,true); } void GameObject::SaveToDB() @@ -501,10 +518,10 @@ void GameObject::SaveToDB() return; } - SaveToDB(GetMapId(), data->spawnMask); + SaveToDB(GetMapId(), data->spawnMask, data->phaseMask); } -void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask) +void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) { const GameObjectInfo *goI = GetGOInfo(); @@ -519,38 +536,40 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask) // data->guid = guid don't must be update at save data.id = GetEntry(); data.mapid = mapid; + data.phaseMask = phaseMask; data.posX = GetFloatValue(GAMEOBJECT_POS_X); data.posY = GetFloatValue(GAMEOBJECT_POS_Y); data.posZ = GetFloatValue(GAMEOBJECT_POS_Z); data.orientation = GetFloatValue(GAMEOBJECT_FACING); - data.rotation0 = GetFloatValue(GAMEOBJECT_ROTATION+0); - data.rotation1 = GetFloatValue(GAMEOBJECT_ROTATION+1); - data.rotation2 = GetFloatValue(GAMEOBJECT_ROTATION+2); - data.rotation3 = GetFloatValue(GAMEOBJECT_ROTATION+3); + data.rotation0 = GetFloatValue(GAMEOBJECT_PARENTROTATION+0); + data.rotation1 = GetFloatValue(GAMEOBJECT_PARENTROTATION+1); + data.rotation2 = GetFloatValue(GAMEOBJECT_PARENTROTATION+2); + data.rotation3 = GetFloatValue(GAMEOBJECT_PARENTROTATION+3); data.spawntimesecs = m_spawnedByDefault ? m_respawnDelayTime : -(int32)m_respawnDelayTime; data.animprogress = GetGoAnimProgress(); data.go_state = GetGoState(); data.spawnMask = spawnMask; - data.ArtKit = GetUInt32Value (GAMEOBJECT_ARTKIT); + data.ArtKit = GetGoArtKit(); // updated in DB std::ostringstream ss; ss << "INSERT INTO gameobject VALUES ( " << m_DBTableGuid << ", " - << GetUInt32Value (OBJECT_FIELD_ENTRY) << ", " + << GetEntry() << ", " << mapid << ", " - << (uint32)spawnMask << ", " + << uint32(spawnMask) << "," // cast to prevent save as symbol + << uint16(GetPhaseMask()) << "," // prevent out of range error << GetFloatValue(GAMEOBJECT_POS_X) << ", " << GetFloatValue(GAMEOBJECT_POS_Y) << ", " << GetFloatValue(GAMEOBJECT_POS_Z) << ", " << GetFloatValue(GAMEOBJECT_FACING) << ", " - << GetFloatValue(GAMEOBJECT_ROTATION) << ", " - << GetFloatValue(GAMEOBJECT_ROTATION+1) << ", " - << GetFloatValue(GAMEOBJECT_ROTATION+2) << ", " - << GetFloatValue(GAMEOBJECT_ROTATION+3) << ", " + << GetFloatValue(GAMEOBJECT_PARENTROTATION) << ", " + << GetFloatValue(GAMEOBJECT_PARENTROTATION+1) << ", " + << GetFloatValue(GAMEOBJECT_PARENTROTATION+2) << ", " + << GetFloatValue(GAMEOBJECT_PARENTROTATION+3) << ", " << m_respawnDelayTime << ", " - << GetGoAnimProgress() << ", " - << GetGoState() << ")"; + << (uint32)GetGoAnimProgress() << ", " + << (uint32)GetGoState() << ")"; WorldDatabase.BeginTransaction(); WorldDatabase.PExecuteLog("DELETE FROM gameobject WHERE guid = '%u'", m_DBTableGuid); @@ -564,12 +583,13 @@ bool GameObject::LoadFromDB(uint32 guid, Map *map) if( !data ) { - sLog.outErrorDb("ERROR: Gameobject (GUID: %u) not found in table `gameobject`, can't load. ",guid); + sLog.outErrorDb("Gameobject (GUID: %u) not found in table `gameobject`, can't load. ",guid); return false; } uint32 entry = data->id; - uint32 map_id = data->mapid; + //uint32 map_id = data->mapid; // already used before call + uint32 phaseMask = data->phaseMask; float x = data->posX; float y = data->posY; float z = data->posZ; @@ -587,40 +607,37 @@ bool GameObject::LoadFromDB(uint32 guid, Map *map) m_DBTableGuid = guid; if (map->GetInstanceId() != 0) guid = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT); - if (!Create(guid,entry, map, x, y, z, ang, rotation0, rotation1, rotation2, rotation3, animprogress, go_state, ArtKit) ) + if (!Create(guid,entry, map, phaseMask, x, y, z, ang, rotation0, rotation1, rotation2, rotation3, animprogress, go_state, ArtKit) ) return false; - switch(GetGOInfo()->type) + if(!GetDespawnPossibility()) + { + SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NODESPAWN); + m_spawnedByDefault = true; + m_respawnDelayTime = 0; + m_respawnTime = 0; + } + else { - case GAMEOBJECT_TYPE_DOOR: - case GAMEOBJECT_TYPE_BUTTON: - /* this code (in comment) isn't correct because in battlegrounds we need despawnable doors and buttons, pls remove - SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NODESPAWN); + if(data->spawntimesecs >= 0) + { m_spawnedByDefault = true; - m_respawnDelayTime = 0; - m_respawnTime = 0; - break;*/ - default: - if(data->spawntimesecs >= 0) - { - m_spawnedByDefault = true; - m_respawnDelayTime = data->spawntimesecs; - m_respawnTime = objmgr.GetGORespawnTime(m_DBTableGuid, map->GetInstanceId()); + m_respawnDelayTime = data->spawntimesecs; + m_respawnTime = objmgr.GetGORespawnTime(m_DBTableGuid, map->GetInstanceId()); - // ready to respawn - if(m_respawnTime && m_respawnTime <= time(NULL)) - { - m_respawnTime = 0; - objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0); - } - } - else + // ready to respawn + if(m_respawnTime && m_respawnTime <= time(NULL)) { - m_spawnedByDefault = false; - m_respawnDelayTime = -data->spawntimesecs; m_respawnTime = 0; + objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0); } - break; + } + else + { + m_spawnedByDefault = false; + m_respawnDelayTime = -data->spawntimesecs; + m_respawnTime = 0; + } } return true; @@ -730,14 +747,10 @@ bool GameObject::isVisibleForInState(Player const* u, bool inVisibleList) const if(owner && u->IsHostileTo(owner) && !canDetectTrap(u, GetDistance(u))) return false; } - - // Smuggled Mana Cell required 10 invisibility type detection/state - if(GetEntry()==187039 && ((u->m_detectInvisibilityMask | u->m_invisibilityMask) & (1<<10))==0) - return false; } // check distance - return IsWithinDistInMap(u,World::GetMaxVisibleDistanceForObject() + + return IsWithinDistInMap(u->m_seer,World::GetMaxVisibleDistanceForObject() + (inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f), false); } @@ -816,7 +829,20 @@ void GameObject::TriggeringLinkedGameObject( uint32 trapEntry, Unit* target) if(!trapSpell) // checked at load already return; - float range = GetSpellMaxRange(sSpellRangeStore.LookupEntry(trapSpell->rangeIndex)); + float range; + SpellRangeEntry const * srentry = sSpellRangeStore.LookupEntry(trapSpell->rangeIndex); + //get owner to check hostility of GameObject + if (GetSpellMaxRangeForHostile(srentry) == GetSpellMaxRangeForHostile(srentry)) + range = GetSpellMaxRangeForHostile(srentry); + else + { + Unit * owner=GetOwner(); + if (owner) + range = owner->GetSpellMaxRangeForTarget(target, srentry); + else + //if no owner assume that object is hostile to target + range = GetSpellMaxRangeForHostile(srentry); + } // search nearest linked GO GameObject* trapGO = NULL; @@ -826,18 +852,18 @@ void GameObject::TriggeringLinkedGameObject( uint32 trapEntry, Unit* target) Cell cell(p); cell.data.Part.reserved = ALL_DISTRICT; - Trinity::NearestGameObjectEntryInObjectRangeCheck go_check(*target,trapEntry,range); - Trinity::GameObjectLastSearcher<Trinity::NearestGameObjectEntryInObjectRangeCheck> checker(trapGO,go_check); + MaNGOS::NearestGameObjectEntryInObjectRangeCheck go_check(*target,trapEntry,range); + MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck> checker(this, trapGO,go_check); TypeContainerVisitor<Trinity::GameObjectLastSearcher<Trinity::NearestGameObjectEntryInObjectRangeCheck>, GridTypeMapContainer > object_checker(checker); CellLock<GridReadGuard> cell_lock(cell, p); - cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(GetMapId(), this)); + cell_lock->Visit(cell_lock, object_checker, *GetMap()); } // 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) @@ -847,13 +873,13 @@ GameObject* GameObject::LookupFishingHoleAround(float range) CellPair p(Trinity::ComputeCellPair(GetPositionX(),GetPositionY())); Cell cell(p); cell.data.Part.reserved = ALL_DISTRICT; - Trinity::NearestGameObjectFishingHole u_check(*this, range); - Trinity::GameObjectSearcher<Trinity::NearestGameObjectFishingHole> checker(ok, u_check); + MaNGOS::NearestGameObjectFishingHole u_check(*this, range); + MaNGOS::GameObjectSearcher<MaNGOS::NearestGameObjectFishingHole> checker(this, ok, u_check); CellLock<GridReadGuard> cell_lock(cell, p); TypeContainerVisitor<Trinity::GameObjectSearcher<Trinity::NearestGameObjectFishingHole>, GridTypeMapContainer > grid_object_checker(checker); - cell_lock->Visit(cell_lock, grid_object_checker, *MapManager::Instance().GetMap(GetMapId(), this)); + cell_lock->Visit(cell_lock, grid_object_checker, *GetMap()); return ok; } @@ -873,9 +899,9 @@ void GameObject::UseDoorOrButton(uint32 time_to_restore) } -void GameObject::SetGoArtKit(uint32 kit) +void GameObject::SetGoArtKit(uint8 kit) { - SetUInt32Value(GAMEOBJECT_ARTKIT, kit); + SetByteValue(GAMEOBJECT_BYTES_1, 2, kit); GameObjectData *data = const_cast<GameObjectData*>(objmgr.GetGOData(m_DBTableGuid)); if(data) data->ArtKit = kit; @@ -899,6 +925,7 @@ void GameObject::Use(Unit* user) // by default spell caster is user Unit* spellCaster = user; uint32 spellId = 0; + bool triggered = false; switch(GetGoType()) { @@ -980,7 +1007,7 @@ void GameObject::Use(Unit* user) // fallback, will always work player->TeleportTo(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation(),TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET); } - player->SetStandState(PLAYER_STATE_SIT_LOW_CHAIR+info->chair.height); + player->SetStandState(UNIT_STAND_STATE_SIT_LOW_CHAIR+info->chair.height); return; } //big gun, its a spell/aura @@ -1020,12 +1047,9 @@ void GameObject::Use(Unit* user) Player* player = (Player*)user; - if(info->camera.cinematicId) - { - WorldPacket data(SMSG_TRIGGER_CINEMATIC, 4); - data << info->camera.cinematicId; - player->GetSession()->SendPacket(&data); - } + if (info->camera.cinematicId) + player->SendCinematicStart(info->camera.cinematicId); + return; } //fishing bobber @@ -1047,11 +1071,12 @@ void GameObject::Use(Unit* user) // 2) if skill == base_zone_skill => 5% chance // 3) chance is linear dependence from (base_zone_skill-skill) - uint32 subzone = GetAreaId(); + uint32 zone, subzone; + GetZoneAndAreaId(zone,subzone); int32 zone_skill = objmgr.GetFishingBaseSkillLevel( subzone ); if(!zone_skill) - zone_skill = objmgr.GetFishingBaseSkillLevel( GetZoneId() ); + zone_skill = objmgr.GetFishingBaseSkillLevel( zone ); //provide error, no fishable zone or area should be 0 if(!zone_skill) @@ -1142,6 +1167,13 @@ void GameObject::Use(Unit* user) return; spellId = info->summoningRitual.spellId; + if(spellId==62330) // GO store not existed spell, replace by expected + { + // spell have reagent and mana cost but it not expected use its + // it triggered spell in fact casted at currently channeled GO + spellId = 61993; + triggered = true; + } // finish spell caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0); @@ -1199,7 +1231,10 @@ void GameObject::Use(Unit* user) if (level < info->meetingstone.minLevel || level > info->meetingstone.maxLevel) return; - spellId = 23598; + if(info->id==194097) + spellId = 61994; // Ritual of Summoning + else + spellId = 59782; // Summoning Stone Effect break; } @@ -1211,7 +1246,7 @@ void GameObject::Use(Unit* user) Player* player = (Player*)user; - if( player->isAllowUseBattleGroundObject() ) + if( player->CanUseBattleGroundObject() ) { // in battleground check BattleGround *bg = player->GetBattleGround(); @@ -1236,7 +1271,7 @@ void GameObject::Use(Unit* user) Player* player = (Player*)user; - if( player->isAllowUseBattleGroundObject() ) + if( player->CanUseBattleGroundObject() ) { // in battleground check BattleGround *bg = player->GetBattleGround(); @@ -1274,6 +1309,26 @@ void GameObject::Use(Unit* user) } break; } + case GAMEOBJECT_TYPE_BARBER_CHAIR: //32 + { + GameObjectInfo const* info = GetGOInfo(); + if(!info) + return; + + if(user->GetTypeId()!=TYPEID_PLAYER) + return; + + Player* player = (Player*)user; + + // fallback, will always work + player->TeleportTo(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation(),TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET); + + WorldPacket data(SMSG_ENABLE_BARBER_SHOP, 0); + player->GetSession()->SendPacket(&data); + + player->SetStandState(UNIT_STAND_STATE_SIT_LOW_CHAIR+info->barberChair.chairheight); + return; + } default: sLog.outDebug("Unknown Object Type %u", GetGoType()); break; @@ -1292,7 +1347,7 @@ void GameObject::Use(Unit* user) return; } - Spell *spell = new Spell(spellCaster, spellInfo, false); + Spell *spell = new Spell(spellCaster, spellInfo, triggered); // spell target is user of GO SpellCastTargets targets; @@ -1301,8 +1356,28 @@ void GameObject::Use(Unit* user) spell->prepare(&targets); } -void GameObject::CastSpell(Unit* target, uint32 spell) +void GameObject::CastSpell(Unit* target, uint32 spellId) { + SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId); + if(!spellInfo) + return; + + bool self = false; + for(int i = 0; i < 3; ++i) + { + if(spellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_CASTER) + { + self = true; + break; + } + } + + if(self) + { + target->CastSpell(target, spellInfo, true); + return; + } + //summon world trigger Creature *trigger = SummonTrigger(GetPositionX(), GetPositionY(), GetPositionZ(), 0, 1); if(!trigger) return; @@ -1311,12 +1386,12 @@ void GameObject::CastSpell(Unit* target, uint32 spell) if(Unit *owner = GetOwner()) { trigger->setFaction(owner->getFaction()); - trigger->CastSpell(target, spell, true, 0, 0, owner->GetGUID()); + trigger->CastSpell(target, spellInfo, true, 0, 0, owner->GetGUID()); } else { trigger->setFaction(14); - trigger->CastSpell(target, spell, true, 0, 0, target->GetGUID()); + trigger->CastSpell(target, spellInfo, true, 0, 0, target->GetGUID()); } //trigger->setDeathState(JUST_DIED); //trigger->RemoveCorpse(); @@ -1338,3 +1413,25 @@ const char* GameObject::GetNameForLocaleIdx(int32 loc_idx) const return GetName(); } +void GameObject::UpdateRotationFields(float rotation2 /*=0.0f*/, float rotation3 /*=0.0f*/) +{ + static double const atan_pow = atan(pow(2.0f, -20.0f)); + + SetFloatValue(GAMEOBJECT_FACING, GetOrientation()); + + double f_rot1 = sin(GetOrientation() / 2.0f); + double f_rot2 = cos(GetOrientation() / 2.0f); + + int64 i_rot1 = int64(f_rot1 / atan_pow *(f_rot2 >= 0 ? 1.0f : -1.0f)); + int64 rotation = (i_rot1 << 43 >> 43) & 0x00000000001FFFFF; + SetUInt64Value(GAMEOBJECT_ROTATION, rotation); + + if(rotation2==0.0f && rotation3==0.0f) + { + rotation2 = f_rot1; + rotation3 = f_rot2; + } + + SetFloatValue(GAMEOBJECT_PARENTROTATION+2, rotation2); + SetFloatValue(GAMEOBJECT_PARENTROTATION+3, rotation3); +} diff --git a/src/game/GameObject.h b/src/game/GameObject.h index 2eb51a6fbe4..b0019048827 100644 --- a/src/game/GameObject.h +++ b/src/game/GameObject.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -41,6 +41,7 @@ struct GameObjectInfo uint32 type; uint32 displayId; char *name; + char *IconName; char *castBarCaption; uint32 faction; uint32 flags; @@ -336,12 +337,12 @@ struct GameObjectInfo uint32 mapID; //0 uint32 difficulty; //1 } dungeonDifficulty; - //32 GAMEOBJECT_TYPE_DO_NOT_USE_YET + //32 GAMEOBJECT_TYPE_BARBER_CHAIR struct { - uint32 mapID; //0 - uint32 difficulty; //1 - } doNotUseYet; + uint32 chairheight; //0 + uint32 heightOffset; //1 + } barberChair; //33 GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING struct { @@ -350,6 +351,13 @@ struct GameObjectInfo uint32 state1Name; //2 uint32 state2Name; //3 } destructibleBuilding; + //34 GAMEOBJECT_TYPE_TRAPDOOR + struct + { + uint32 whenToPause; // 0 + uint32 startOpen; // 1 + uint32 autoClose; // 2 + } trapDoor; // not use for specific field access (only for output with loop by all filed), also this determinate max union size struct // GAMEOBJECT_TYPE_SPELLCASTER @@ -370,7 +378,8 @@ struct GameObjectLocale struct GameObjectData { uint32 id; // entry in gamobject_template - uint32 mapid; + uint16 mapid; + uint16 phaseMask; float posX; float posY; float posZ; @@ -419,7 +428,7 @@ class TRINITY_DLL_SPEC GameObject : public WorldObject void AddToWorld(); void RemoveFromWorld(); - bool Create(uint32 guidlow, uint32 name_id, Map *map, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, uint32 go_state, uint32 ArtKit = 0); + bool Create(uint32 guidlow, uint32 name_id, Map *map, uint32 phaseMask, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, uint32 go_state, uint32 ArtKit = 0); void Update(uint32 p_time); static GameObject* GetGameObject(WorldObject& object, uint64 guid); GameObjectInfo const* GetGOInfo() const; @@ -436,20 +445,19 @@ class TRINITY_DLL_SPEC GameObject : public WorldObject uint32 GetDBTableGUIDLow() const { return m_DBTableGuid; } - void Say(const char* text, uint32 language, uint64 TargetGuid) { MonsterSay(text,language,TargetGuid); } - void Yell(const char* text, uint32 language, uint64 TargetGuid) { MonsterYell(text,language,TargetGuid); } - void TextEmote(const char* text, uint64 TargetGuid) { MonsterTextEmote(text,TargetGuid); } - void Whisper(const char* text,uint64 receiver) { MonsterWhisper(text,receiver); } + void UpdateRotationFields(float rotation2 = 0.0f, float rotation3 = 0.0f); + void Say(int32 textId, uint32 language, uint64 TargetGuid) { MonsterSay(textId,language,TargetGuid); } void Yell(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYell(textId,language,TargetGuid); } void TextEmote(int32 textId, uint64 TargetGuid) { MonsterTextEmote(textId,TargetGuid); } void Whisper(int32 textId, uint64 receiver) { MonsterWhisper(textId,receiver); } + void YellToZone(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYellToZone(textId,language,TargetGuid); } // overwrite WorldObject function for proper name localization const char* GetNameForLocaleIdx(int32 locale_idx) const; void SaveToDB(); - void SaveToDB(uint32 mapid, uint8 spawnMask); + void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask); bool LoadFromDB(uint32 guid, Map *map); void DeleteFromDB(); void SetLootState(LootState s) { m_lootState = s; } @@ -474,6 +482,20 @@ class TRINITY_DLL_SPEC GameObject : public WorldObject } } + bool GetDespawnPossibility() const + { + switch(GetGoType()) + { + case GAMEOBJECT_TYPE_DOOR: return GetGOInfo()->door.noDamageImmune; + case GAMEOBJECT_TYPE_BUTTON: return GetGOInfo()->button.noDamageImmune; + case GAMEOBJECT_TYPE_QUESTGIVER: return GetGOInfo()->questgiver.noDamageImmune; + case GAMEOBJECT_TYPE_GOOBER: return GetGOInfo()->goober.noDamageImmune; + case GAMEOBJECT_TYPE_FLAGSTAND: return GetGOInfo()->flagstand.noDamageImmune; + case GAMEOBJECT_TYPE_FLAGDROP: return GetGOInfo()->flagdrop.noDamageImmune; + default: return true; + } + } + time_t GetRespawnTime() const { return m_respawnTime; } time_t GetRespawnTimeEx() const { @@ -503,15 +525,15 @@ class TRINITY_DLL_SPEC GameObject : public WorldObject void Delete(); void SetSpellId(uint32 id) { m_spellId = id;} uint32 GetSpellId() const { return m_spellId;} - void getFishLoot(Loot *loot); - GameobjectTypes GetGoType() const { return GameobjectTypes(GetUInt32Value(GAMEOBJECT_TYPE_ID)); } - void SetGoType(GameobjectTypes type) { SetUInt32Value(GAMEOBJECT_TYPE_ID, type); } - uint32 GetGoState() const { return GetUInt32Value(GAMEOBJECT_STATE); } - void SetGoState(uint32 state) { SetUInt32Value(GAMEOBJECT_STATE, state); } - uint32 GetGoArtKit() const { return GetUInt32Value(GAMEOBJECT_ARTKIT); } - void SetGoArtKit(uint32 artkit); - uint32 GetGoAnimProgress() const { return GetUInt32Value(GAMEOBJECT_ANIMPROGRESS); } - void SetGoAnimProgress(uint32 animprogress) { SetUInt32Value(GAMEOBJECT_ANIMPROGRESS, animprogress); } + void getFishLoot(Loot *loot, Player* loot_owner); + GameobjectTypes GetGoType() const { return GameobjectTypes(GetByteValue(GAMEOBJECT_BYTES_1, 1)); } + void SetGoType(GameobjectTypes type) { SetByteValue(GAMEOBJECT_BYTES_1, 1, type); } + uint8 GetGoState() const { return GetByteValue(GAMEOBJECT_BYTES_1, 0); } + void SetGoState(uint8 state) { SetByteValue(GAMEOBJECT_BYTES_1, 0, state); } + uint8 GetGoArtKit() const { return GetByteValue(GAMEOBJECT_BYTES_1, 2); } + void SetGoArtKit(uint8 artkit); + uint8 GetGoAnimProgress() const { return GetByteValue(GAMEOBJECT_BYTES_1, 3); } + void SetGoAnimProgress(uint8 animprogress) { SetByteValue(GAMEOBJECT_BYTES_1, 3, animprogress); } void Use(Unit* user); diff --git a/src/game/GlobalEvents.cpp b/src/game/GlobalEvents.cpp index a2dd89d34c0..a544113f5c8 100644 --- a/src/game/GlobalEvents.cpp +++ b/src/game/GlobalEvents.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -55,7 +55,7 @@ static void CorpsesEraseCallBack(QueryResult *result, bool bones) { if(!ObjectAccessor::Instance().ConvertCorpseForPlayer(player_guid)) { - sLog.outDebug("Corpse %u not found in world. Delete from DB.",guidlow); + sLog.outDebug("Corpse %u not found in world or bones creating forbidden. Delete from DB.",guidlow); CharacterDatabase.PExecute("DELETE FROM corpse WHERE guid = '%u'",guidlow); } } diff --git a/src/game/GlobalEvents.h b/src/game/GlobalEvents.h index 6ec6e6b60b1..aa99c47985e 100644 --- a/src/game/GlobalEvents.h +++ b/src/game/GlobalEvents.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/GossipDef.cpp b/src/game/GossipDef.cpp index b0f717eea9e..c362b23ff71 100644 --- a/src/game/GossipDef.cpp +++ b/src/game/GossipDef.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -127,7 +127,7 @@ bool PlayerMenu::GossipOptionCoded( unsigned int Selection ) void PlayerMenu::SendGossipMenu( uint32 TitleTextId, uint64 npcGUID ) { WorldPacket data( SMSG_GOSSIP_MESSAGE, (100) ); // guess size - data << npcGUID; + data << uint64(npcGUID); data << uint32(0); // new 2.4.0 data << uint32( TitleTextId ); data << uint32( mGossipMenu.MenuItemCount() ); // max count 0x0F @@ -153,7 +153,7 @@ void PlayerMenu::SendGossipMenu( uint32 TitleTextId, uint64 npcGUID ) data << uint32(questID); data << uint32( qItem.m_qIcon ); - data << uint32( pQuest ? pQuest->GetQuestLevel() : 0 ); + data << uint32(pSession->GetPlayer()->GetQuestLevel(pQuest)); std::string Title = pQuest->GetTitle(); int loc_idx = pSession->GetSessionDbLocaleIndex(); @@ -181,6 +181,7 @@ void PlayerMenu::CloseGossip() //sLog.outDebug( "WORLD: Sent SMSG_GOSSIP_COMPLETE" ); } +// Outdated void PlayerMenu::SendPointOfInterest( float X, float Y, uint32 Icon, uint32 Flags, uint32 Data, char const * locName ) { WorldPacket data( SMSG_GOSSIP_POI, (4+4+4+4+4+10) ); // guess size @@ -194,12 +195,43 @@ void PlayerMenu::SendPointOfInterest( float X, float Y, uint32 Icon, uint32 Flag //sLog.outDebug("WORLD: Sent SMSG_GOSSIP_POI"); } -void PlayerMenu::SendTalking( uint32 textID ) +void PlayerMenu::SendPointOfInterest( uint32 poi_id ) { - GossipText *pGossip; - std::string GossipStr; + PointOfInterest const* poi = objmgr.GetPointOfInterest(poi_id); + if(!poi) + { + sLog.outErrorDb("Requested send not existed POI (Id: %u), ignore.",poi_id); + return; + } + + std::string icon_name = poi->icon_name; + + int loc_idx = pSession->GetSessionDbLocaleIndex(); + if (loc_idx >= 0) + { + PointOfInterestLocale const *pl = objmgr.GetPointOfInterestLocale(poi_id); + if (pl) + { + if (pl->IconName.size() > size_t(loc_idx) && !pl->IconName[loc_idx].empty()) + icon_name = pl->IconName[loc_idx]; + } + } + + WorldPacket data( SMSG_GOSSIP_POI, (4+4+4+4+4+10) ); // guess size + data << uint32(poi->flags); + data << float(poi->x); + data << float(poi->y); + data << uint32(poi->icon); + data << uint32(poi->data); + data << icon_name; + + pSession->SendPacket( &data ); + //sLog.outDebug("WORLD: Sent SMSG_GOSSIP_POI"); +} - pGossip = objmgr.GetGossipText(textID); +void PlayerMenu::SendTalking( uint32 textID ) +{ + GossipText const* pGossip = objmgr.GetGossipText(textID); WorldPacket data( SMSG_NPC_TEXT_UPDATE, 100 ); // guess size data << textID; // can be < 0 @@ -259,14 +291,11 @@ void PlayerMenu::SendTalking( uint32 textID ) data << pGossip->Options[i].Language; - data << pGossip->Options[i].Emotes[0]._Delay; - data << pGossip->Options[i].Emotes[0]._Emote; - - data << pGossip->Options[i].Emotes[1]._Delay; - data << pGossip->Options[i].Emotes[1]._Emote; - - data << pGossip->Options[i].Emotes[2]._Delay; - data << pGossip->Options[i].Emotes[2]._Emote; + for(int j = 0; j < 3; ++j) + { + data << pGossip->Options[i].Emotes[j]._Delay; + data << pGossip->Options[i].Emotes[j]._Emote; + } } } pSession->SendPacket( &data ); @@ -373,7 +402,7 @@ void PlayerMenu::SendQuestGiverQuestList( QEmote eEmote, const std::string& Titl data << uint32(questID); data << uint32(qmi.m_qIcon); - data << uint32(pQuest ? pQuest->GetQuestLevel() : 0); + data << uint32(pSession->GetPlayer()->GetQuestLevel(pQuest)); data << title; } pSession->SendPacket( &data ); @@ -417,10 +446,14 @@ void PlayerMenu::SendQuestGiverQuestDetails( Quest const *pQuest, uint64 npcGUID } data << uint64(npcGUID); + data << uint64(0); // wotlk, something todo with quest sharing? data << uint32(pQuest->GetQuestId()); - data << Title << Details << Objectives; + data << Title; + data << Details; + data << Objectives; data << uint32(ActivateAccept); data << uint32(pQuest->GetSuggestedPlayers()); + data << uint8(0); // new wotlk if (pQuest->HasFlag(QUEST_FLAGS_HIDDEN_REWARDS)) { @@ -466,6 +499,7 @@ void PlayerMenu::SendQuestGiverQuestDetails( Quest const *pQuest, uint64 npcGUID data << uint32(pQuest->GetRewSpell()); // reward spell, this spell will display (icon) (casted if RewSpellCast==0) data << uint32(pQuest->GetRewSpellCast()); // casted spell data << uint32(pQuest->GetCharTitleId()); // CharTitleId, new 2.4.0, player gets this title (id from CharTitles) + data << uint32(pQuest->GetBonusTalents()); // bonus talents data << uint32(QUEST_EMOTE_COUNT); for (uint32 i=0; i < QUEST_EMOTE_COUNT; i++) @@ -514,7 +548,7 @@ void PlayerMenu::SendQuestQueryResponse( Quest const *pQuest ) data << uint32(pQuest->GetQuestId()); data << uint32(pQuest->GetQuestMethod()); // Accepted values: 0, 1 or 2. 0==IsAutoComplete() (skip objectives/details) - data << uint32(pQuest->GetQuestLevel()); // may be 0 + data << uint32(pQuest->GetQuestLevel()); // may be 0, static data, in other cases must be used dynamic level: Player::GetQuestLevel data << uint32(pQuest->GetZoneOrSort()); // zone or sort to display in quest log data << uint32(pQuest->GetType()); @@ -542,6 +576,8 @@ void PlayerMenu::SendQuestQueryResponse( Quest const *pQuest ) data << uint32(pQuest->GetSrcItemId()); data << uint32(pQuest->GetFlags() & 0xFFFF); data << uint32(pQuest->GetCharTitleId()); // CharTitleId, new 2.4.0, player gets this title (id from CharTitles) + data << uint32(pQuest->GetPlayersSlain()); // players slain + data << uint32(pQuest->GetBonusTalents()); // bonus talents int iI; @@ -588,15 +624,23 @@ void PlayerMenu::SendQuestQueryResponse( Quest const *pQuest ) data << uint32(pQuest->ReqCreatureOrGOId[iI]); } data << uint32(pQuest->ReqCreatureOrGOCount[iI]); + data << uint32(pQuest->ReqSourceId[iI]); + } + + for (iI = 0; iI < QUEST_OBJECTIVES_COUNT; ++iI) + { data << uint32(pQuest->ReqItemId[iI]); data << uint32(pQuest->ReqItemCount[iI]); } + data << uint32(0); // TODO: 5 item objective + data << uint32(0); + for (iI = 0; iI < QUEST_OBJECTIVES_COUNT; iI++) data << ObjectiveText[iI]; pSession->SendPacket( &data ); - sLog.outDebug( "WORLD: Sent SMSG_QUEST_QUERY_RESPONSE questid=%u",pQuest->GetQuestId() ); + sLog.outDebug( "WORLD: Sent SMSG_QUEST_QUERY_RESPONSE questid=%u", pQuest->GetQuestId() ); } void PlayerMenu::SendQuestGiverOfferReward( Quest const* pQuest, uint64 npcGUID, bool EnbleNext ) @@ -678,9 +722,10 @@ void PlayerMenu::SendQuestGiverOfferReward( Quest const* pQuest, uint64 npcGUID, data << uint32(0x08); // unused by client? data << uint32(pQuest->GetRewSpell()); // reward spell, this spell will display (icon) (casted if RewSpellCast==0) data << uint32(pQuest->GetRewSpellCast()); // casted spell - data << uint32(0x00); // unk, NOT honor + data << uint32(0); // unknown + data << uint32(pQuest->GetBonusTalents()); // bonus talents pSession->SendPacket( &data ); - sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_OFFER_REWARD NPCGuid=%u, questid=%u",GUID_LOPART(npcGUID),pQuest->GetQuestId() ); + sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_OFFER_REWARD NPCGuid=%u, questid=%u", GUID_LOPART(npcGUID), pQuest->GetQuestId() ); } void PlayerMenu::SendQuestGiverRequestItems( Quest const *pQuest, uint64 npcGUID, bool Completable, bool CloseOnCancel ) @@ -688,16 +733,8 @@ void PlayerMenu::SendQuestGiverRequestItems( Quest const *pQuest, uint64 npcGUID // We can always call to RequestItems, but this packet only goes out if there are actually // items. Otherwise, we'll skip straight to the OfferReward - // We may wish a better check, perhaps checking the real quest requirements - if (pQuest->GetRequestItemsText().empty()) - { - SendQuestGiverOfferReward(pQuest, npcGUID, true); - return; - } - - std::string Title,RequestItemsText; - Title = pQuest->GetTitle(); - RequestItemsText = pQuest->GetRequestItemsText(); + std::string Title = pQuest->GetTitle(); + std::string RequestItemsText = pQuest->GetRequestItemsText(); int loc_idx = pSession->GetSessionDbLocaleIndex(); if (loc_idx >= 0) @@ -712,6 +749,13 @@ void PlayerMenu::SendQuestGiverRequestItems( Quest const *pQuest, uint64 npcGUID } } + // We may wish a better check, perhaps checking the real quest requirements + if (RequestItemsText.empty()) + { + SendQuestGiverOfferReward(pQuest, npcGUID, true); + return; + } + WorldPacket data( SMSG_QUESTGIVER_REQUEST_ITEMS, 50 ); // guess size data << npcGUID; data << pQuest->GetQuestId(); diff --git a/src/game/GossipDef.h b/src/game/GossipDef.h index e2279550528..102a34b5913 100644 --- a/src/game/GossipDef.h +++ b/src/game/GossipDef.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,27 +30,27 @@ class WorldSession; #define GOSSIP_MAX_MENU_ITEMS 64 // client supported items unknown, but provided number must be enough #define DEFAULT_GOSSIP_MESSAGE 0xffffff -//POI defines +//POI icons. Many more exist, list not complete. enum Poi_Icon { - ICON_POI_0 = 0, // Grey ? - ICON_POI_1 = 1, // Red ? - ICON_POI_2 = 2, // Blue ? - ICON_POI_BWTOMB = 3, // Blue and White Tomb Stone - ICON_POI_HOUSE = 4, // House - ICON_POI_TOWER = 5, // Tower - ICON_POI_REDFLAG = 6, // Red Flag with Yellow ! - ICON_POI_TOMB = 7, // Tomb Stone - ICON_POI_BWTOWER = 8, // Blue and White Tower - ICON_POI_REDTOWER = 9, // Red Tower - ICON_POI_BLUETOWER = 10, // Blue Tower - ICON_POI_RWTOWER = 11, // Red and White Tower - ICON_POI_REDTOMB = 12, // Red Tomb Stone - ICON_POI_RWTOMB = 13, // Red and White Tomb Stone - ICON_POI_BLUETOMB = 14, // Blue Tomb Stone - ICON_POI_NOTHING = 15, // NOTHING - ICON_POI_16 = 16, // Red ? - ICON_POI_17 = 17, // Grey ? + ICON_POI_BLANK = 0, // Blank (not visible) + ICON_POI_GREY_AV_MINE = 1, // Grey mine lorry + ICON_POI_RED_AV_MINE = 2, // Red mine lorry + ICON_POI_BLUE_AV_MINE = 3, // Blue mine lorry + ICON_POI_BWTOMB = 4, // Blue and White Tomb Stone + ICON_POI_SMALL_HOUSE = 5, // Small house + ICON_POI_GREYTOWER = 6, // Grey Tower + ICON_POI_REDFLAG = 7, // Red Flag w/Yellow ! + ICON_POI_TOMBSTONE = 8, // Normal tomb stone (brown) + ICON_POI_BWTOWER = 9, // Blue and White Tower + ICON_POI_REDTOWER = 10, // Red Tower + ICON_POI_BLUETOWER = 11, // Blue Tower + ICON_POI_RWTOWER = 12, // Red and White Tower + ICON_POI_REDTOMB = 13, // Red Tomb Stone + ICON_POI_RWTOMB = 14, // Red and White Tomb Stone + ICON_POI_BLUETOMB = 15, // Blue Tomb Stone + ICON_POI_16 = 16, // Grey ? + ICON_POI_17 = 17, // Blue/White ? ICON_POI_18 = 18, // Blue ? ICON_POI_19 = 19, // Red and White ? ICON_POI_20 = 20, // Red ? @@ -189,6 +189,7 @@ class TRINITY_DLL_SPEC PlayerMenu void SendGossipMenu( uint32 TitleTextId, uint64 npcGUID ); void CloseGossip(); void SendPointOfInterest( float X, float Y, uint32 Icon, uint32 Flags, uint32 Data, const char * locName ); + void SendPointOfInterest( uint32 poi_id ); void SendTalking( uint32 textID ); void SendTalking( char const * title, char const * text ); diff --git a/src/game/GridDefines.h b/src/game/GridDefines.h index cb67897329f..a3fe011f402 100644 --- a/src/game/GridDefines.h +++ b/src/game/GridDefines.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -48,7 +48,7 @@ class Player; #define CENTER_GRID_OFFSET (SIZE_OF_GRIDS/2) -#define MIN_GRID_DELAY MINUTE*1000 +#define MIN_GRID_DELAY (MINUTE*IN_MILISECONDS) #define MIN_MAP_UPDATE_DELAY 50 #define SIZE_OF_GRID_CELL (SIZE_OF_GRIDS/MAX_NUMBER_OF_CELLS) @@ -57,7 +57,7 @@ class Player; #define TOTAL_NUMBER_OF_CELLS_PER_MAP (MAX_NUMBER_OF_GRIDS*MAX_NUMBER_OF_CELLS) -#define MAP_RESOLUTION 256 +#define MAP_RESOLUTION 128 #define MAP_SIZE (SIZE_OF_GRIDS*MAX_NUMBER_OF_GRIDS) #define MAP_HALFSIZE (MAP_SIZE/2) diff --git a/src/game/GridNotifiers.cpp b/src/game/GridNotifiers.cpp index e27abeccd0c..fe18dc35840 100644 --- a/src/game/GridNotifiers.cpp +++ b/src/game/GridNotifiers.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,7 +24,6 @@ #include "UpdateData.h" #include "Item.h" #include "Map.h" -#include "MapManager.h" #include "Transports.h" #include "ObjectAccessor.h" @@ -94,9 +93,9 @@ PlayerVisibilityNotifier::Notify() // Now do operations that required done at object visibility change to visible - // target aura duration for caster show only if target exist at caster client // send data at target visibility change (adding to client) for(std::set<WorldObject*>::const_iterator vItr = i_visibleNow.begin(); vItr != i_visibleNow.end(); ++vItr) + // target aura duration for caster show only if target exist at caster client if((*vItr)!=&i_player && (*vItr)->isType(TYPEMASK_UNIT)) i_player.SendInitialVisiblePackets((Unit*)(*vItr)); @@ -109,14 +108,20 @@ Deliverer::Visit(PlayerMapType &m) { for (PlayerMapType::iterator iter = m.begin(); iter != m.end(); ++iter) { - if (!i_dist || iter->getSource()->GetDistance(&i_source) <= i_dist) + //if (!i_source.InSamePhase(iter->getSource())) + // continue; + if(!iter->getSource()->InSamePhase(i_phaseMask)) + continue; + + if (!i_dist || iter->getSource()->GetDistance(&i_source) < i_dist) { // Send packet to all who are sharing the player's vision if (!iter->getSource()->GetSharedVisionList().empty()) { - SharedVisionList::const_iterator it = iter->getSource()->GetSharedVisionList().begin(); - for ( ; it != iter->getSource()->GetSharedVisionList().end(); ++it) - SendPacket(*it); + SharedVisionList::const_iterator i = iter->getSource()->GetSharedVisionList().begin(); + for ( ; i != iter->getSource()->GetSharedVisionList().end(); ++i) + if((*i)->m_seer == iter->getSource()) + SendPacket(*i); } VisitObject(iter->getSource()); @@ -129,14 +134,18 @@ Deliverer::Visit(CreatureMapType &m) { for (CreatureMapType::iterator iter = m.begin(); iter != m.end(); ++iter) { - if (!i_dist || iter->getSource()->GetDistance(&i_source) <= i_dist) + if(!iter->getSource()->InSamePhase(i_phaseMask)) + continue; + + if (!i_dist || iter->getSource()->GetDistance(&i_source) < i_dist) { // Send packet to all who are sharing the creature's vision if (!iter->getSource()->GetSharedVisionList().empty()) { - SharedVisionList::const_iterator it = iter->getSource()->GetSharedVisionList().begin(); - for ( ; it != iter->getSource()->GetSharedVisionList().end(); ++it) - SendPacket(*it); + SharedVisionList::const_iterator i = iter->getSource()->GetSharedVisionList().begin(); + for ( ; i != iter->getSource()->GetSharedVisionList().end(); ++i) + if((*i)->m_seer == iter->getSource()) + SendPacket(*i); } } } @@ -147,13 +156,18 @@ Deliverer::Visit(DynamicObjectMapType &m) { for (DynamicObjectMapType::iterator iter = m.begin(); iter != m.end(); ++iter) { - if (IS_PLAYER_GUID(iter->getSource()->GetCasterGUID())) + if(!iter->getSource()->InSamePhase(i_phaseMask)) + continue; + + if (!i_dist || iter->getSource()->GetDistance(&i_source) < i_dist) { - // Send packet back to the caster if the caster has vision of dynamic object - Player* caster = (Player*)iter->getSource()->GetCaster(); - if (caster && caster->GetUInt64Value(PLAYER_FARSIGHT) == iter->getSource()->GetGUID() && - (!i_dist || iter->getSource()->GetDistance(&i_source) <= i_dist)) - SendPacket(caster); + if (IS_PLAYER_GUID(iter->getSource()->GetCasterGUID())) + { + // Send packet back to the caster if the caster has vision of dynamic object + Player* caster = (Player*)iter->getSource()->GetCaster(); + if (caster && caster->m_seer == iter->getSource()) + SendPacket(caster); + } } } } @@ -204,9 +218,6 @@ ObjectUpdater::Visit(GridRefManager<T> &m) } } -template void ObjectUpdater::Visit<GameObject>(GameObjectMapType &); -template void ObjectUpdater::Visit<DynamicObject>(DynamicObjectMapType &); - bool CannibalizeObjectCheck::operator()(Corpse* u) { // ignore bones @@ -224,3 +235,5 @@ bool CannibalizeObjectCheck::operator()(Corpse* u) return false; } +template void ObjectUpdater::Visit<GameObject>(GameObjectMapType &); +template void ObjectUpdater::Visit<DynamicObject>(DynamicObjectMapType &); diff --git a/src/game/GridNotifiers.h b/src/game/GridNotifiers.h index b34eee36e7b..521a543c08c 100644 --- a/src/game/GridNotifiers.h +++ b/src/game/GridNotifiers.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -108,8 +108,10 @@ namespace Trinity std::set<uint64> plr_list; bool i_toPossessor; bool i_toSelf; + uint32 i_phaseMask; float i_dist; - Deliverer(WorldObject &src, WorldPacket *msg, bool to_possessor, bool to_self, float dist = 0.0f) : i_source(src), i_message(msg), i_toPossessor(to_possessor), i_toSelf(to_self), i_dist(dist) {} + Deliverer(WorldObject &src, WorldPacket *msg, bool to_possessor, bool to_self, float dist = 0.0f) + : i_source(src), i_message(msg), i_toPossessor(to_possessor), i_toSelf(to_self), i_dist(dist), i_phaseMask(src.GetPhaseMask()) {} void Visit(PlayerMapType &m); void Visit(CreatureMapType &m); void Visit(DynamicObjectMapType &m); @@ -208,10 +210,12 @@ namespace Trinity template<class Check> struct TRINITY_DLL_DECL WorldObjectSearcher { + uint32 i_phaseMask; WorldObject* &i_object; Check &i_check; - WorldObjectSearcher(WorldObject* & result, Check& check) : i_object(result),i_check(check) {} + WorldObjectSearcher(WorldObject const* searcher, WorldObject* & result, Check& check) + : i_phaseMask(searcher->GetPhaseMask()), i_object(result),i_check(check) {} void Visit(GameObjectMapType &m); void Visit(PlayerMapType &m); @@ -225,10 +229,12 @@ namespace Trinity template<class Check> struct TRINITY_DLL_DECL WorldObjectListSearcher { + uint32 i_phaseMask; std::list<WorldObject*> &i_objects; Check& i_check; - WorldObjectListSearcher(std::list<WorldObject*> &objects, Check & check) : i_objects(objects),i_check(check) {} + WorldObjectListSearcher(WorldObject const* searcher, std::list<WorldObject*> &objects, Check & check) + : i_phaseMask(searcher->GetPhaseMask()), i_objects(objects),i_check(check) {} void Visit(PlayerMapType &m); void Visit(CreatureMapType &m); @@ -242,37 +248,44 @@ namespace Trinity template<class Do> struct TRINITY_DLL_DECL WorldObjectWorker { + uint32 i_phaseMask; Do const& i_do; - explicit WorldObjectWorker(Do const& _do) : i_do(_do) {} + WorldObjectWorker(WorldObject const* searcher, Do const& _do) + : i_phaseMask(searcher->GetPhaseMask()), i_do(_do) {} void Visit(GameObjectMapType &m) { for(GameObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr) - i_do(itr->getSource()); + if(itr->getSource()->InSamePhase(i_phaseMask)) + i_do(itr->getSource()); } void Visit(PlayerMapType &m) { for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr) - i_do(itr->getSource()); + if(itr->getSource()->InSamePhase(i_phaseMask)) + i_do(itr->getSource()); } void Visit(CreatureMapType &m) { for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr) - i_do(itr->getSource()); + if(itr->getSource()->InSamePhase(i_phaseMask)) + i_do(itr->getSource()); } void Visit(CorpseMapType &m) { for(CorpseMapType::iterator itr=m.begin(); itr != m.end(); ++itr) - i_do(itr->getSource()); + if(itr->getSource()->InSamePhase(i_phaseMask)) + i_do(itr->getSource()); } void Visit(DynamicObjectMapType &m) { for(DynamicObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr) - i_do(itr->getSource()); + if(itr->getSource()->InSamePhase(i_phaseMask)) + i_do(itr->getSource()); } template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {} @@ -283,10 +296,12 @@ namespace Trinity template<class Check> struct TRINITY_DLL_DECL GameObjectSearcher { + uint32 i_phaseMask; GameObject* &i_object; Check &i_check; - GameObjectSearcher(GameObject* & result, Check& check) : i_object(result),i_check(check) {} + GameObjectSearcher(WorldObject const* searcher, GameObject* & result, Check& check) + : i_phaseMask(searcher->GetPhaseMask()), i_object(result),i_check(check) {} void Visit(GameObjectMapType &m); @@ -297,10 +312,12 @@ namespace Trinity template<class Check> struct TRINITY_DLL_DECL GameObjectLastSearcher { + uint32 i_phaseMask; GameObject* &i_object; Check& i_check; - GameObjectLastSearcher(GameObject* & result, Check& check) : i_object(result),i_check(check) {} + GameObjectLastSearcher(WorldObject const* searcher, GameObject* & result, Check& check) + : i_phaseMask(searcher->GetPhaseMask()), i_object(result), i_check(check) {} void Visit(GameObjectMapType &m); @@ -310,10 +327,12 @@ namespace Trinity template<class Check> struct TRINITY_DLL_DECL GameObjectListSearcher { + uint32 i_phaseMask; std::list<GameObject*> &i_objects; Check& i_check; - GameObjectListSearcher(std::list<GameObject*> &objects, Check & check) : i_objects(objects),i_check(check) {} + GameObjectListSearcher(WorldObject const* searcher, std::list<GameObject*> &objects, Check & check) + : i_phaseMask(searcher->GetPhaseMask()), i_objects(objects), i_check(check) {} void Visit(GameObjectMapType &m); @@ -326,10 +345,12 @@ namespace Trinity template<class Check> struct TRINITY_DLL_DECL UnitSearcher { + uint32 i_phaseMask; Unit* &i_object; Check & i_check; - UnitSearcher(Unit* & result, Check & check) : i_object(result),i_check(check) {} + UnitSearcher(WorldObject const* searcher, Unit* & result, Check & check) + : i_phaseMask(searcher->GetPhaseMask()), i_object(result),i_check(check) {} void Visit(CreatureMapType &m); void Visit(PlayerMapType &m); @@ -341,10 +362,12 @@ namespace Trinity template<class Check> struct TRINITY_DLL_DECL UnitLastSearcher { + uint32 i_phaseMask; Unit* &i_object; Check & i_check; - UnitLastSearcher(Unit* & result, Check & check) : i_object(result),i_check(check) {} + UnitLastSearcher(WorldObject const* searcher, Unit* & result, Check & check) + : i_phaseMask(searcher->GetPhaseMask()), i_object(result),i_check(check) {} void Visit(CreatureMapType &m); void Visit(PlayerMapType &m); @@ -356,10 +379,12 @@ namespace Trinity template<class Check> struct TRINITY_DLL_DECL UnitListSearcher { + uint32 i_phaseMask; std::list<Unit*> &i_objects; Check& i_check; - UnitListSearcher(std::list<Unit*> &objects, Check & check) : i_objects(objects),i_check(check) {} + UnitListSearcher(WorldObject const* searcher, std::list<Unit*> &objects, Check & check) + : i_phaseMask(searcher->GetPhaseMask()), i_objects(objects),i_check(check) {} void Visit(PlayerMapType &m); void Visit(CreatureMapType &m); @@ -372,10 +397,12 @@ namespace Trinity template<class Check> struct TRINITY_DLL_DECL CreatureSearcher { + uint32 i_phaseMask; Creature* &i_object; Check & i_check; - CreatureSearcher(Creature* & result, Check & check) : i_object(result),i_check(check) {} + CreatureSearcher(WorldObject const* searcher, Creature* & result, Check & check) + : i_phaseMask(searcher->GetPhaseMask()), i_object(result),i_check(check) {} void Visit(CreatureMapType &m); @@ -386,10 +413,12 @@ namespace Trinity template<class Check> struct TRINITY_DLL_DECL CreatureLastSearcher { + uint32 i_phaseMask; Creature* &i_object; Check & i_check; - CreatureLastSearcher(Creature* & result, Check & check) : i_object(result),i_check(check) {} + CreatureLastSearcher(WorldObject const* searcher, Creature* & result, Check & check) + : i_phaseMask(searcher->GetPhaseMask()), i_object(result),i_check(check) {} void Visit(CreatureMapType &m); @@ -399,10 +428,12 @@ namespace Trinity template<class Check> struct TRINITY_DLL_DECL CreatureListSearcher { + uint32 i_phaseMask; std::list<Creature*> &i_objects; Check& i_check; - CreatureListSearcher(std::list<Creature*> &objects, Check & check) : i_objects(objects),i_check(check) {} + CreatureListSearcher(WorldObject const* searcher, std::list<Creature*> &objects, Check & check) + : i_phaseMask(searcher->GetPhaseMask()), i_objects(objects),i_check(check) {} void Visit(CreatureMapType &m); @@ -414,10 +445,12 @@ namespace Trinity template<class Check> struct TRINITY_DLL_DECL PlayerSearcher { + uint32 i_phaseMask; Player* &i_object; Check & i_check; - PlayerSearcher(Player* & result, Check & check) : i_object(result),i_check(check) {} + PlayerSearcher(WorldObject const* searcher, Player* & result, Check & check) + : i_phaseMask(searcher->GetPhaseMask()), i_object(result),i_check(check) {} void Visit(PlayerMapType &m); @@ -427,14 +460,37 @@ namespace Trinity template<class Do> struct TRINITY_DLL_DECL PlayerWorker { + uint32 i_phaseMask; Do& i_do; - explicit PlayerWorker(Do& _do) : i_do(_do) {} + PlayerWorker(WorldObject const* searcher, Do& _do) + : i_phaseMask(searcher->GetPhaseMask()), i_do(_do) {} void Visit(PlayerMapType &m) { for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr) - i_do(itr->getSource()); + if(itr->getSource()->InSamePhase(i_phaseMask)) + i_do(itr->getSource()); + } + + template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {} + }; + + template<class Do> + struct TRINITY_DLL_DECL PlayerDistWorker + { + WorldObject const* i_searcher; + float i_dist; + Do& i_do; + + PlayerDistWorker(WorldObject const* searcher, float _dist, Do& _do) + : i_searcher(searcher), i_dist(_dist), i_do(_do) {} + + void Visit(PlayerMapType &m) + { + for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + if(itr->getSource()->InSamePhase(i_searcher) && itr->getSource()->GetDistance(i_searcher) <= i_dist) + i_do(itr->getSource()); } template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {} @@ -572,6 +628,62 @@ namespace Trinity // Unit checks + class MostHPMissingInRange + { + public: + MostHPMissingInRange(Unit const* obj, float range, uint32 hp) : i_obj(obj), i_range(range), i_hp(hp) {} + bool operator()(Unit* u) + { + if(u->isAlive() && u->isInCombat() && !i_obj->IsHostileTo(u) && i_obj->IsWithinDistInMap(u, i_range) && u->GetMaxHealth() - u->GetHealth() > i_hp) + { + i_hp = u->GetMaxHealth() - u->GetHealth(); + return true; + } + return false; + } + private: + Unit const* i_obj; + float i_range; + uint32 i_hp; + }; + + class FriendlyCCedInRange + { + public: + FriendlyCCedInRange(Unit const* obj, float range) : i_obj(obj), i_range(range) {} + bool operator()(Unit* u) + { + if(u->isAlive() && u->isInCombat() && !i_obj->IsHostileTo(u) && i_obj->IsWithinDistInMap(u, i_range) && + (u->isFeared() || u->isCharmed() || u->isFrozen() || u->hasUnitState(UNIT_STAT_STUNNED) || u->hasUnitState(UNIT_STAT_CONFUSED))) + { + return true; + } + return false; + } + private: + Unit const* i_obj; + float i_range; + }; + + class FriendlyMissingBuffInRange + { + public: + FriendlyMissingBuffInRange(Unit const* obj, float range, uint32 spellid) : i_obj(obj), i_range(range), i_spell(spellid) {} + bool operator()(Unit* u) + { + if(u->isAlive() && u->isInCombat() && !i_obj->IsHostileTo(u) && i_obj->IsWithinDistInMap(u, i_range) && + !(u->HasAura(i_spell, 0) || u->HasAura(i_spell, 1) || u->HasAura(i_spell, 2))) + { + return true; + } + return false; + } + private: + Unit const* i_obj; + float i_range; + uint32 i_spell; + }; + class AnyUnfriendlyUnitInObjectRangeCheck { public: @@ -859,63 +971,6 @@ namespace Trinity float i_range; }; - // Searchers used by ScriptedAI - class MostHPMissingInRange - { - public: - MostHPMissingInRange(Unit const* obj, float range, uint32 hp) : i_obj(obj), i_range(range), i_hp(hp) {} - bool operator()(Unit* u) - { - if(u->isAlive() && u->isInCombat() && !i_obj->IsHostileTo(u) && i_obj->IsWithinDistInMap(u, i_range) && u->GetMaxHealth() - u->GetHealth() > i_hp) - { - i_hp = u->GetMaxHealth() - u->GetHealth(); - return true; - } - return false; - } - private: - Unit const* i_obj; - float i_range; - uint32 i_hp; - }; - - class FriendlyCCedInRange - { - public: - FriendlyCCedInRange(Unit const* obj, float range) : i_obj(obj), i_range(range) {} - bool operator()(Unit* u) - { - if(u->isAlive() && u->isInCombat() && !i_obj->IsHostileTo(u) && i_obj->IsWithinDistInMap(u, i_range) && - (u->isFeared() || u->isCharmed() || u->isFrozen() || u->hasUnitState(UNIT_STAT_STUNNED) || u->hasUnitState(UNIT_STAT_CONFUSED))) - { - return true; - } - return false; - } - private: - Unit const* i_obj; - float i_range; - }; - - class FriendlyMissingBuffInRange - { - public: - FriendlyMissingBuffInRange(Unit const* obj, float range, uint32 spellid) : i_obj(obj), i_range(range), i_spell(spellid) {} - bool operator()(Unit* u) - { - if(u->isAlive() && u->isInCombat() && /*!i_obj->IsHostileTo(u)*/ i_obj->IsFriendlyTo(u) && i_obj->IsWithinDistInMap(u, i_range) && - !(u->HasAura(i_spell, 0) || u->HasAura(i_spell, 1) || u->HasAura(i_spell, 2))) - { - return true; - } - return false; - } - private: - Unit const* i_obj; - float i_range; - uint32 i_spell; - }; - class AllFriendlyCreaturesInGrid { public: @@ -963,6 +1018,49 @@ namespace Trinity float range; }; + // Player checks and do + + // Prepare using Builder localized packets with caching and send to player + template<class Builder> + class LocalizedPacketDo + { + public: + explicit LocalizedPacketDo(Builder& builder) : i_builder(builder) {} + + ~LocalizedPacketDo() + { + for(size_t i = 0; i < i_data_cache.size(); ++i) + delete i_data_cache[i]; + } + void operator()( Player* p ); + + private: + Builder& i_builder; + std::vector<WorldPacket*> i_data_cache; // 0 = default, i => i-1 locale index + }; + + // Prepare using Builder localized packets with caching and send to player + template<class Builder> + class LocalizedPacketListDo + { + public: + typedef std::vector<WorldPacket*> WorldPacketList; + explicit LocalizedPacketListDo(Builder& builder) : i_builder(builder) {} + + ~LocalizedPacketListDo() + { + for(size_t i = 0; i < i_data_cache.size(); ++i) + for(int j = 0; j < i_data_cache[i].size(); ++j) + delete i_data_cache[i][j]; + } + void operator()( Player* p ); + + private: + Builder& i_builder; + std::vector<WorldPacketList> i_data_cache; + // 0 = default, i => i-1 locale index + }; + #ifndef WIN32 template<> inline void PlayerRelocationNotifier::Visit<Creature>(CreatureMapType &); template<> inline void PlayerRelocationNotifier::Visit<Player>(PlayerMapType &); diff --git a/src/game/GridNotifiersImpl.h b/src/game/GridNotifiersImpl.h index a43a97cd898..7fc6c147f5b 100644 --- a/src/game/GridNotifiersImpl.h +++ b/src/game/GridNotifiersImpl.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -128,7 +128,7 @@ Trinity::CreatureRelocationNotifier::Visit(PlayerMapType &m) continue; iter->getSource()->UpdateVisibilityOf(&i_creature); - + PlayerCreatureRelocationWorker(iter->getSource(), &i_creature); } } @@ -144,7 +144,7 @@ Trinity::CreatureRelocationNotifier::Visit(CreatureMapType &m) { if(iter->getSource()->m_Notified) continue; - + if(!iter->getSource()->isAlive()) continue; @@ -202,11 +202,10 @@ inline void Trinity::DynamicObjectUpdater::VisitHelper(Unit* target) } // Check target immune to spell or aura - if (target->IsImmunedToSpell(spellInfo) || target->IsImmunedToSpellEffect(spellInfo->Effect[eff_index], spellInfo->EffectMechanic[eff_index])) + if (target->IsImmunedToSpell(spellInfo) || target->IsImmunedToSpellEffect(spellInfo, eff_index)) return; // Apply PersistentAreaAura on target - PersistentAreaAura* Aur = new PersistentAreaAura(spellInfo, eff_index, NULL, target, i_dynobject.GetCaster()); - target->AddAura(Aur); + target->AddAuraEffect(spellInfo->Id, eff_index, i_dynobject.GetCaster()); i_dynobject.AddAffected(target); } @@ -239,7 +238,10 @@ void Trinity::WorldObjectSearcher<Check>::Visit(GameObjectMapType &m) for(GameObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr) { - if(i_check(itr->getSource())) + if(!itr->getSource()->InSamePhase(i_phaseMask)) + continue; + + if (i_check(itr->getSource())) { i_object = itr->getSource(); return; @@ -256,6 +258,9 @@ void Trinity::WorldObjectSearcher<Check>::Visit(PlayerMapType &m) for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr) { + if(!itr->getSource()->InSamePhase(i_phaseMask)) + continue; + if(i_check(itr->getSource())) { i_object = itr->getSource(); @@ -273,6 +278,9 @@ void Trinity::WorldObjectSearcher<Check>::Visit(CreatureMapType &m) for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr) { + if(!itr->getSource()->InSamePhase(i_phaseMask)) + continue; + if(i_check(itr->getSource())) { i_object = itr->getSource(); @@ -290,6 +298,9 @@ void Trinity::WorldObjectSearcher<Check>::Visit(CorpseMapType &m) for(CorpseMapType::iterator itr=m.begin(); itr != m.end(); ++itr) { + if(!itr->getSource()->InSamePhase(i_phaseMask)) + continue; + if(i_check(itr->getSource())) { i_object = itr->getSource(); @@ -307,6 +318,9 @@ void Trinity::WorldObjectSearcher<Check>::Visit(DynamicObjectMapType &m) for(DynamicObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr) { + if(!itr->getSource()->InSamePhase(i_phaseMask)) + continue; + if(i_check(itr->getSource())) { i_object = itr->getSource(); @@ -319,40 +333,45 @@ template<class Check> void Trinity::WorldObjectListSearcher<Check>::Visit(PlayerMapType &m) { for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr) - if(i_check(itr->getSource())) - i_objects.push_back(itr->getSource()); + if(itr->getSource()->InSamePhase(i_phaseMask)) + if(i_check(itr->getSource())) + i_objects.push_back(itr->getSource()); } template<class Check> void Trinity::WorldObjectListSearcher<Check>::Visit(CreatureMapType &m) { for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr) - if(i_check(itr->getSource())) - i_objects.push_back(itr->getSource()); + if(itr->getSource()->InSamePhase(i_phaseMask)) + if(i_check(itr->getSource())) + i_objects.push_back(itr->getSource()); } template<class Check> void Trinity::WorldObjectListSearcher<Check>::Visit(CorpseMapType &m) { for(CorpseMapType::iterator itr=m.begin(); itr != m.end(); ++itr) - if(i_check(itr->getSource())) - i_objects.push_back(itr->getSource()); + if(itr->getSource()->InSamePhase(i_phaseMask)) + if(i_check(itr->getSource())) + i_objects.push_back(itr->getSource()); } template<class Check> void Trinity::WorldObjectListSearcher<Check>::Visit(GameObjectMapType &m) { for(GameObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr) - if(i_check(itr->getSource())) - i_objects.push_back(itr->getSource()); + if(itr->getSource()->InSamePhase(i_phaseMask)) + if(i_check(itr->getSource())) + i_objects.push_back(itr->getSource()); } template<class Check> void Trinity::WorldObjectListSearcher<Check>::Visit(DynamicObjectMapType &m) { for(DynamicObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr) - if(i_check(itr->getSource())) - i_objects.push_back(itr->getSource()); + if(itr->getSource()->InSamePhase(i_phaseMask)) + if(i_check(itr->getSource())) + i_objects.push_back(itr->getSource()); } // Gameobject searchers @@ -366,6 +385,9 @@ void Trinity::GameObjectSearcher<Check>::Visit(GameObjectMapType &m) for(GameObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr) { + if(!itr->getSource()->InSamePhase(i_phaseMask)) + continue; + if(i_check(itr->getSource())) { i_object = itr->getSource(); @@ -379,6 +401,9 @@ void Trinity::GameObjectLastSearcher<Check>::Visit(GameObjectMapType &m) { for(GameObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr) { + if(!itr->getSource()->InSamePhase(i_phaseMask)) + continue; + if(i_check(itr->getSource())) i_object = itr->getSource(); } @@ -388,8 +413,9 @@ template<class Check> void Trinity::GameObjectListSearcher<Check>::Visit(GameObjectMapType &m) { for(GameObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr) - if(i_check(itr->getSource())) - i_objects.push_back(itr->getSource()); + if(itr->getSource()->InSamePhase(i_phaseMask)) + if(i_check(itr->getSource())) + i_objects.push_back(itr->getSource()); } // Unit searchers @@ -403,6 +429,9 @@ void Trinity::UnitSearcher<Check>::Visit(CreatureMapType &m) for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr) { + if(!itr->getSource()->InSamePhase(i_phaseMask)) + continue; + if(i_check(itr->getSource())) { i_object = itr->getSource(); @@ -420,6 +449,9 @@ void Trinity::UnitSearcher<Check>::Visit(PlayerMapType &m) for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr) { + if(!itr->getSource()->InSamePhase(i_phaseMask)) + continue; + if(i_check(itr->getSource())) { i_object = itr->getSource(); @@ -433,6 +465,9 @@ void Trinity::UnitLastSearcher<Check>::Visit(CreatureMapType &m) { for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr) { + if(!itr->getSource()->InSamePhase(i_phaseMask)) + continue; + if(i_check(itr->getSource())) i_object = itr->getSource(); } @@ -443,6 +478,9 @@ void Trinity::UnitLastSearcher<Check>::Visit(PlayerMapType &m) { for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr) { + if(!itr->getSource()->InSamePhase(i_phaseMask)) + continue; + if(i_check(itr->getSource())) i_object = itr->getSource(); } @@ -452,16 +490,18 @@ template<class Check> void Trinity::UnitListSearcher<Check>::Visit(PlayerMapType &m) { for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr) - if(i_check(itr->getSource())) - i_objects.push_back(itr->getSource()); + if(itr->getSource()->InSamePhase(i_phaseMask)) + if(i_check(itr->getSource())) + i_objects.push_back(itr->getSource()); } template<class Check> void Trinity::UnitListSearcher<Check>::Visit(CreatureMapType &m) { for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr) - if(i_check(itr->getSource())) - i_objects.push_back(itr->getSource()); + if(itr->getSource()->InSamePhase(i_phaseMask)) + if(i_check(itr->getSource())) + i_objects.push_back(itr->getSource()); } // Creature searchers @@ -475,6 +515,9 @@ void Trinity::CreatureSearcher<Check>::Visit(CreatureMapType &m) for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr) { + if(!itr->getSource()->InSamePhase(i_phaseMask)) + continue; + if(i_check(itr->getSource())) { i_object = itr->getSource(); @@ -488,6 +531,9 @@ void Trinity::CreatureLastSearcher<Check>::Visit(CreatureMapType &m) { for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr) { + if(!itr->getSource()->InSamePhase(i_phaseMask)) + continue; + if(i_check(itr->getSource())) i_object = itr->getSource(); } @@ -497,8 +543,9 @@ template<class Check> void Trinity::CreatureListSearcher<Check>::Visit(CreatureMapType &m) { for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr) - if(i_check(itr->getSource())) - i_objects.push_back(itr->getSource()); + if(itr->getSource()->InSamePhase(i_phaseMask)) + if( i_check(itr->getSource())) + i_objects.push_back(itr->getSource()); } template<class Check> @@ -510,6 +557,9 @@ void Trinity::PlayerSearcher<Check>::Visit(PlayerMapType &m) for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr) { + if(!itr->getSource()->InSamePhase(i_phaseMask)) + continue; + if(i_check(itr->getSource())) { i_object = itr->getSource(); @@ -518,5 +568,53 @@ void Trinity::PlayerSearcher<Check>::Visit(PlayerMapType &m) } } -#endif // TRINITY_GRIDNOTIFIERSIMPL_H +template<class Builder> +void MaNGOS::LocalizedPacketDo<Builder>::operator()( Player* p ) +{ + uint32 loc_idx = p->GetSession()->GetSessionDbLocaleIndex(); + uint32 cache_idx = loc_idx+1; + WorldPacket* data; + + // create if not cached yet + if(i_data_cache.size() < cache_idx+1 || !i_data_cache[cache_idx]) + { + if(i_data_cache.size() < cache_idx+1) + i_data_cache.resize(cache_idx+1); + + data = new WorldPacket(SMSG_MESSAGECHAT, 200); + + i_builder(*data,loc_idx); + + i_data_cache[cache_idx] = data; + } + else + data = i_data_cache[cache_idx]; + + p->SendDirectMessage(data); +} + +template<class Builder> +void MaNGOS::LocalizedPacketListDo<Builder>::operator()( Player* p ) +{ + uint32 loc_idx = p->GetSession()->GetSessionDbLocaleIndex(); + uint32 cache_idx = loc_idx+1; + WorldPacketList* data_list; + + // create if not cached yet + if(i_data_cache.size() < cache_idx+1 || i_data_cache[cache_idx].empty()) + { + if(i_data_cache.size() < cache_idx+1) + i_data_cache.resize(cache_idx+1); + + data_list = &i_data_cache[cache_idx]; + + i_builder(*data_list,loc_idx); + } + else + data_list = &i_data_cache[cache_idx]; + + for(size_t i = 0; i < data_list->size(); ++i) + p->SendDirectMessage((*data_list)[i]); +} +#endif // MANGOS_GRIDNOTIFIERSIMPL_H diff --git a/src/game/GridStates.h b/src/game/GridStates.h index 167f30b8371..12d64ad58ca 100644 --- a/src/game/GridStates.h +++ b/src/game/GridStates.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/Group.cpp b/src/game/Group.cpp index 535a3d4392c..f4a977c3d7c 100644 --- a/src/game/Group.cpp +++ b/src/game/Group.cpp @@ -203,13 +203,24 @@ void Group::ConvertToRaid() _initRaidSubGroupsCounter(); - if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE groups SET isRaid = 1 WHERE leaderGuid='%u'", GUID_LOPART(m_leaderGuid)); + if(!isBGGroup()) + CharacterDatabase.PExecute("UPDATE groups SET isRaid = 1 WHERE leaderGuid='%u'", GUID_LOPART(m_leaderGuid)); SendUpdate(); + + // update quest related GO states (quest activity dependent from raid membership) + for(member_citerator citr = m_memberSlots.begin(); citr != m_memberSlots.end(); ++citr) + if(Player* player = objmgr.GetPlayer(citr->guid)) + player->UpdateForQuestsGO(); } bool Group::AddInvite(Player *player) { - if(!player || player->GetGroupInvite() || player->GetGroup()) + if( !player || player->GetGroupInvite() ) + return false; + Group* group = player->GetGroup(); + if( group && group->isBGGroup() ) + group = player->GetOriginalGroup(); + if( group ) return false; RemoveInvite(player); @@ -290,6 +301,10 @@ bool Group::AddMember(const uint64 &guid, const char* name) } player->SetGroupUpdateFlag(GROUP_UPDATE_FULL); UpdatePlayerOutOfRange(player); + + // quest related GO state dependent from raid memebership + if(isRaidGroup()) + player->UpdateForQuestsGO(); } return true; @@ -306,6 +321,10 @@ uint32 Group::RemoveMember(const uint64 &guid, const uint8 &method) if(Player *player = objmgr.GetPlayer( guid )) { + // quest related GO state dependent from raid membership + if(isRaidGroup()) + player->UpdateForQuestsGO(); + WorldPacket data; if(method == 1) @@ -314,9 +333,17 @@ uint32 Group::RemoveMember(const uint64 &guid, const uint8 &method) player->GetSession()->SendPacket( &data ); } - data.Initialize(SMSG_GROUP_LIST, 24); - data << uint64(0) << uint64(0) << uint64(0); - player->GetSession()->SendPacket(&data); + //we already removed player from group and in player->GetGroup() is his original group! + if( Group* group = player->GetGroup() ) + { + group->SendUpdate(); + } + else + { + data.Initialize(SMSG_GROUP_LIST, 24); + data << uint64(0) << uint64(0) << uint64(0); + player->GetSession()->SendPacket(&data); + } _homebindIfInstance(player); } @@ -325,7 +352,7 @@ uint32 Group::RemoveMember(const uint64 &guid, const uint8 &method) { WorldPacket data(SMSG_GROUP_SET_LEADER, (m_memberSlots.front().name.size()+1)); data << m_memberSlots.front().name; - BroadcastPacket(&data); + BroadcastPacket(&data, true); } SendUpdate(); @@ -348,7 +375,7 @@ void Group::ChangeLeader(const uint64 &guid) WorldPacket data(SMSG_GROUP_SET_LEADER, slot->name.size()+1); data << slot->name; - BroadcastPacket(&data); + BroadcastPacket(&data, true); SendUpdate(); } @@ -362,7 +389,22 @@ void Group::Disband(bool hideDestroy) if(!player) continue; - player->SetGroup(NULL); + //we cannot call _removeMember because it would invalidate member iterator + //if we are removing player from battleground raid + if( isBGGroup() ) + player->RemoveFromBattleGroundRaid(); + else + { + //we can remove player who is in battleground from his original group + if( player->GetOriginalGroup() == this ) + player->SetOriginalGroup(NULL); + else + player->SetGroup(NULL); + } + + // quest related GO state dependent from raid membership + if(isRaidGroup()) + player->UpdateForQuestsGO(); if(!player->GetSession()) continue; @@ -374,9 +416,17 @@ void Group::Disband(bool hideDestroy) player->GetSession()->SendPacket(&data); } - data.Initialize(SMSG_GROUP_LIST, 24); - data << uint64(0) << uint64(0) << uint64(0); - player->GetSession()->SendPacket(&data); + //we already removed player from group and in player->GetGroup() is his original group, send update + if( Group* group = player->GetGroup() ) + { + group->SendUpdate(); + } + else + { + data.Initialize(SMSG_GROUP_LIST, 24); + data << uint64(0) << uint64(0) << uint64(0); + player->GetSession()->SendPacket(&data); + } _homebindIfInstance(player); } @@ -730,6 +780,8 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers) if(player && player->GetSession()) { + player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT, roll->itemid, maxresul); + ItemPosCountVec dest; LootItem *item = &(roll->getLoot()->items[roll->itemSlot]); uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count ); @@ -775,6 +827,8 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers) if(player && player->GetSession()) { + player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT, roll->itemid, maxresul); + ItemPosCountVec dest; LootItem *item = &(roll->getLoot()->items[roll->itemSlot]); uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count ); @@ -820,7 +874,7 @@ void Group::SetTargetIcon(uint8 id, uint64 guid) data << (uint8)0; data << id; data << guid; - BroadcastPacket(&data); + BroadcastPacket(&data, true); } void Group::GetDataForXPAtKill(Unit const* victim, uint32& count,uint32& sum_level, Player* & member_with_max_level, Player* & not_gray_member_with_max_level) @@ -836,13 +890,10 @@ void Group::GetDataForXPAtKill(Unit const* victim, uint32& count,uint32& sum_lev ++count; sum_level += member->getLevel(); - // store maximum member level if(!member_with_max_level || member_with_max_level->getLevel() < member->getLevel()) member_with_max_level = member; - uint32 gray_level = Trinity::XP::GetGrayLevel(member->getLevel()); - // if the victim is higher level than the gray level of the currently examined group member, - // then set not_gray_member_with_max_level if needed. + uint32 gray_level = MaNGOS::XP::GetGrayLevel(member->getLevel()); if( victim->getLevel() > gray_level && (!not_gray_member_with_max_level || not_gray_member_with_max_level->getLevel() < member->getLevel())) not_gray_member_with_max_level = member; @@ -876,7 +927,7 @@ void Group::SendUpdate() for(member_citerator citr = m_memberSlots.begin(); citr != m_memberSlots.end(); ++citr) { player = objmgr.GetPlayer(citr->guid); - if(!player || !player->GetSession()) + if(!player || !player->GetSession() || player->GetGroup() != this ) continue; // guess size WorldPacket data(SMSG_GROUP_LIST, (1+1+1+1+8+4+GetMembersCount()*20)); @@ -890,11 +941,14 @@ void Group::SendUpdate() { if(citr->guid == citr2->guid) continue; + Player* member = objmgr.GetPlayer(citr2->guid); + uint8 onlineState = (member) ? MEMBER_STATUS_ONLINE : MEMBER_STATUS_OFFLINE; + onlineState = onlineState | ((isBGGroup()) ? MEMBER_STATUS_PVP : 0); data << citr2->name; data << (uint64)citr2->guid; // online-state - data << (uint8)(objmgr.GetPlayer(citr2->guid) ? 1 : 0); + data << (uint8)(onlineState); data << (uint8)(citr2->group); // groupid data << (uint8)(citr2->assistant?0x01:0); // 0x2 main assist, 0x4 main tank } @@ -906,7 +960,6 @@ void Group::SendUpdate() data << (uint64)m_looterGuid; // looter guid data << (uint8)m_lootThreshold; // loot threshold data << (uint8)m_difficulty; // Heroic Mod Group - } player->GetSession()->SendPacket( &data ); } @@ -929,12 +982,12 @@ void Group::UpdatePlayerOutOfRange(Player* pPlayer) } } -void Group::BroadcastPacket(WorldPacket *packet, int group, uint64 ignore) +void Group::BroadcastPacket(WorldPacket *packet, bool ignorePlayersInBGRaid, int group, uint64 ignore) { for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next()) { Player *pl = itr->getSource(); - if(!pl || (ignore != 0 && pl->GetGUID() == ignore)) + if(!pl || (ignore != 0 && pl->GetGUID() == ignore) || (ignorePlayersInBGRaid && pl->GetGroup() != this) ) continue; if (pl->GetSession() && (group==-1 || itr->getSubGroup()==group)) @@ -985,7 +1038,7 @@ bool Group::_addMember(const uint64 &guid, const char* name, bool isAssistant) } // We are raid group and no one slot is free if (!groupFound) - return false; + return false; } return _addMember(guid, name, isAssistant, groupid); @@ -1013,7 +1066,15 @@ bool Group::_addMember(const uint64 &guid, const char* name, bool isAssistant, u if(player) { player->SetGroupInvite(NULL); - player->SetGroup(this, group); + //if player is in group and he is being added to BG raid group, then call SetBattleGroundRaid() + if( player->GetGroup() && isBGGroup() ) + player->SetBattleGroundRaid(this, group); + //if player is in bg raid and we are adding him to normal group, then call SetOriginalGroup() + else if ( player->GetGroup() ) + player->SetOriginalGroup(this, group); + //if player is not in group, then call set group + else + player->SetGroup(this, group); // if the same group invites the player back, cancel the homebind timer InstanceGroupBind *bind = GetBoundInstance(player->GetMapId(), player->GetDifficulty()); if(bind && bind->save->GetInstanceId() == player->GetInstanceId()) @@ -1040,7 +1101,17 @@ bool Group::_removeMember(const uint64 &guid) Player *player = objmgr.GetPlayer(guid); if (player) { - player->SetGroup(NULL); + //if we are removing player from battleground raid + if( isBGGroup() ) + player->RemoveFromBattleGroundRaid(); + else + { + //we can remove player who is in battleground from his original group + if( player->GetOriginalGroup() == this ) + player->SetOriginalGroup(NULL); + else + player->SetGroup(NULL); + } } _removeRolls(guid); @@ -1232,12 +1303,17 @@ void Group::ChangeMembersGroup(Player *player, const uint8 &group) return; if(_setMembersGroup(player->GetGUID(), group)) { - uint8 prevSubGroup; - prevSubGroup = player->GetSubGroup(); - + uint8 prevSubGroup = player->GetSubGroup(); + if( player->GetGroup() == this ) + player->GetGroupRef().setSubGroup(group); + //if player is in BG raid, it is possible that he is also in normal raid - and that normal raid is stored in m_originalGroup reference + else + { + prevSubGroup = player->GetOriginalSubGroup(); + player->GetOriginalGroupRef().setSubGroup(group); + } SubGroupCounterDecrease(prevSubGroup); - player->GetGroupRef().setSubGroup(group); SendUpdate(); } } @@ -1315,7 +1391,7 @@ void Group::UpdateLooterGuid( Creature* creature, bool ifneed ) SendUpdate(); } -uint32 Group::CanJoinBattleGroundQueue(uint32 bgTypeId, uint32 bgQueueType, uint32 MinPlayerCount, uint32 MaxPlayerCount, bool isRated, uint32 arenaSlot) +uint32 Group::CanJoinBattleGroundQueue(BattleGroundTypeId bgTypeId, BattleGroundQueueTypeId bgQueueTypeId, uint32 MinPlayerCount, uint32 MaxPlayerCount, bool isRated, uint32 arenaSlot) { // check for min / max count uint32 memberscount = GetMembersCount(); @@ -1330,7 +1406,7 @@ uint32 Group::CanJoinBattleGroundQueue(uint32 bgTypeId, uint32 bgQueueType, uint if(!reference) return BG_JOIN_ERR_OFFLINE_MEMBER; - uint32 bgQueueId = reference->GetBattleGroundQueueIdFromLevel(); + BGQueueIdBasedOnLevel queue_id = reference->GetBattleGroundQueueIdFromLevel(bgTypeId); uint32 arenaTeamId = reference->GetArenaTeamId(arenaSlot); uint32 team = reference->GetTeam(); @@ -1345,13 +1421,13 @@ uint32 Group::CanJoinBattleGroundQueue(uint32 bgTypeId, uint32 bgQueueType, uint if(member->GetTeam() != team) return BG_JOIN_ERR_MIXED_FACTION; // not in the same battleground level braket, don't let join - if(member->GetBattleGroundQueueIdFromLevel() != bgQueueId) + if(member->GetBattleGroundQueueIdFromLevel(bgTypeId) != queue_id) return BG_JOIN_ERR_MIXED_LEVELS; // don't let join rated matches if the arena team id doesn't match if(isRated && member->GetArenaTeamId(arenaSlot) != arenaTeamId) return BG_JOIN_ERR_MIXED_ARENATEAM; // don't let join if someone from the group is already in that bg queue - if(member->InBattleGroundQueueForBattleGroundQueueType(bgQueueType)) + if(member->InBattleGroundQueueForBattleGroundQueueType(bgQueueTypeId)) return BG_JOIN_ERR_GROUP_MEMBER_ALREADY_IN_QUEUE; // check for deserter debuff in case not arena queue if(bgTypeId != BATTLEGROUND_AA && !member->CanJoinToBattleground()) @@ -1480,7 +1556,7 @@ InstanceGroupBind* Group::BindToInstance(InstanceSave *save, bool permanent, boo InstanceGroupBind& bind = m_boundInstances[save->GetDifficulty()][save->GetMapId()]; if(bind.save) { - // when a boss is killed or when copying the player's binds to the group + // when a boss is killed or when copying the players's binds to the group if(permanent != bind.perm || save != bind.save) if(!load) CharacterDatabase.PExecute("UPDATE group_instance SET instance = '%u', permanent = '%u' WHERE leaderGuid = '%u' AND instance = '%u'", save->GetInstanceId(), permanent, GUID_LOPART(GetLeaderGUID()), bind.save->GetInstanceId()); } diff --git a/src/game/Group.h b/src/game/Group.h index 76543dd4873..d09ef616897 100644 --- a/src/game/Group.h +++ b/src/game/Group.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +23,7 @@ #include "GroupReference.h" #include "GroupRefManager.h" +#include "BattleGround.h" #include "LootMgr.h" #include <map> @@ -66,24 +67,25 @@ enum GroupUpdateFlags { GROUP_UPDATE_FLAG_NONE = 0x00000000, // nothing GROUP_UPDATE_FLAG_STATUS = 0x00000001, // uint16, flags - GROUP_UPDATE_FLAG_CUR_HP = 0x00000002, // uint16 - GROUP_UPDATE_FLAG_MAX_HP = 0x00000004, // uint16 + GROUP_UPDATE_FLAG_CUR_HP = 0x00000002, // uint32 + GROUP_UPDATE_FLAG_MAX_HP = 0x00000004, // uint32 GROUP_UPDATE_FLAG_POWER_TYPE = 0x00000008, // uint8 GROUP_UPDATE_FLAG_CUR_POWER = 0x00000010, // uint16 GROUP_UPDATE_FLAG_MAX_POWER = 0x00000020, // uint16 GROUP_UPDATE_FLAG_LEVEL = 0x00000040, // uint16 GROUP_UPDATE_FLAG_ZONE = 0x00000080, // uint16 GROUP_UPDATE_FLAG_POSITION = 0x00000100, // uint16, uint16 - GROUP_UPDATE_FLAG_AURAS = 0x00000200, // uint64 mask, for each bit set uint16 spellid + uint8 unk + GROUP_UPDATE_FLAG_AURAS = 0x00000200, // uint64 mask, for each bit set uint32 spellid + uint8 unk GROUP_UPDATE_FLAG_PET_GUID = 0x00000400, // uint64 pet guid GROUP_UPDATE_FLAG_PET_NAME = 0x00000800, // pet name, NULL terminated string GROUP_UPDATE_FLAG_PET_MODEL_ID = 0x00001000, // uint16, model id - GROUP_UPDATE_FLAG_PET_CUR_HP = 0x00002000, // uint16 pet cur health - GROUP_UPDATE_FLAG_PET_MAX_HP = 0x00004000, // uint16 pet max health + GROUP_UPDATE_FLAG_PET_CUR_HP = 0x00002000, // uint32 pet cur health + GROUP_UPDATE_FLAG_PET_MAX_HP = 0x00004000, // uint32 pet max health GROUP_UPDATE_FLAG_PET_POWER_TYPE = 0x00008000, // uint8 pet power type GROUP_UPDATE_FLAG_PET_CUR_POWER = 0x00010000, // uint16 pet cur power GROUP_UPDATE_FLAG_PET_MAX_POWER = 0x00020000, // uint16 pet max power - GROUP_UPDATE_FLAG_PET_AURAS = 0x00040000, // uint64 mask, for each bit set uint16 spellid + uint8 unk, pet auras... + GROUP_UPDATE_FLAG_PET_AURAS = 0x00040000, // uint64 mask, for each bit set uint32 spellid + uint8 unk, pet auras... + GROUP_UPDATE_FLAG_VEHICLE_SEAT = 0x00080000, // uint32 vehicle_seat_id (index from VehicleSeat.dbc) GROUP_UPDATE_PET = 0x0007FC00, // all pet flags GROUP_UPDATE_FULL = 0x0007FFFF, // all known flags }; @@ -249,7 +251,7 @@ class TRINITY_DLL_SPEC Group void ConvertToRaid(); void SetBattlegroundGroup(BattleGround *bg) { m_bgGroup = bg; } - uint32 CanJoinBattleGroundQueue(uint32 bgTypeId, uint32 bgQueueType, uint32 MinPlayerCount, uint32 MaxPlayerCount, bool isRated, uint32 arenaSlot); + uint32 CanJoinBattleGroundQueue(BattleGroundTypeId bgTypeId, BattleGroundQueueTypeId bgQueueTypeId, uint32 MinPlayerCount, uint32 MaxPlayerCount, bool isRated, uint32 arenaSlot); void ChangeMembersGroup(const uint64 &guid, const uint8 &group); void ChangeMembersGroup(Player *player, const uint8 &group); @@ -291,7 +293,7 @@ class TRINITY_DLL_SPEC Group void SendUpdate(); void UpdatePlayerOutOfRange(Player* pPlayer); // ignore: GUID of player that will be ignored - void BroadcastPacket(WorldPacket *packet, int group=-1, uint64 ignore=0); + void BroadcastPacket(WorldPacket *packet, bool ignorePlayersInBGRaid, int group=-1, uint64 ignore=0); void BroadcastReadyCheck(WorldPacket *packet); void OfflineReadyCheck(); diff --git a/src/game/GroupHandler.cpp b/src/game/GroupHandler.cpp index f63505ae24a..b0094989e19 100644 --- a/src/game/GroupHandler.cpp +++ b/src/game/GroupHandler.cpp @@ -28,10 +28,11 @@ #include "ObjectMgr.h" #include "Player.h" #include "Group.h" -#include "ObjectAccessor.h" -#include "MapManager.h" #include "SocialMgr.h" #include "Util.h" +#include "SpellAuras.h" + +class Aura; /* differeces from off: -you can uninvite yourself - is is useful @@ -59,12 +60,6 @@ void WorldSession::HandleGroupInviteOpcode( WorldPacket & recv_data ) std::string membername; recv_data >> membername; - if(_player->InBattleGround()) - { - SendPartyResult(PARTY_OP_INVITE, membername, PARTY_RESULT_INVITE_RESTRICTED); - return; - } - // attempt add selected player // cheating @@ -107,15 +102,20 @@ void WorldSession::HandleGroupInviteOpcode( WorldPacket & recv_data ) return; } + Group *group = GetPlayer()->GetGroup(); + if( group && group->isBGGroup() ) + group = GetPlayer()->GetOriginalGroup(); + + Group *group2 = player->GetGroup(); + if( group2 && group2->isBGGroup() ) + group2 = player->GetOriginalGroup(); // player already in another group or invited - if(player->GetGroup() || player->GetGroupInvite() ) + if( group2 || player->GetGroupInvite() ) { SendPartyResult(PARTY_OP_INVITE, membername, PARTY_RESULT_ALREADY_IN_GROUP); return; } - Group *group = GetPlayer()->GetGroup(); - if(group) { // not have permissions for invite @@ -161,6 +161,7 @@ void WorldSession::HandleGroupInviteOpcode( WorldPacket & recv_data ) // ok, we do it WorldPacket data(SMSG_GROUP_INVITE, 10); // guess size + data << uint8(1); // ok data << GetPlayer()->GetName(); player->GetSession()->SendPacket(&data); @@ -193,12 +194,6 @@ void WorldSession::HandleGroupAcceptOpcode( WorldPacket & /*recv_data*/ ) Player* leader = objmgr.GetPlayer(group->GetLeaderGUID()); - if(leader && leader->InBattleGround()) - { - SendPartyResult(PARTY_OP_INVITE, "", PARTY_RESULT_INVITE_RESTRICTED); - return; - } - // forming a new group, create it if(!group->IsCreated()) { @@ -208,14 +203,10 @@ void WorldSession::HandleGroupAcceptOpcode( WorldPacket & /*recv_data*/ ) objmgr.AddGroup(group); } - // everything's fine, do it + // everything's fine, do it, PLAYER'S GROUP IS SET IN ADDMEMBER!!! if(!group->AddMember(GetPlayer()->GetGUID(), GetPlayer()->GetName())) return; - uint8 subgroup = group->GetMemberGroup(GetPlayer()->GetGUID()); - - GetPlayer()->SetGroup(group, subgroup); - group->BroadcastGroupUpdate(); } @@ -224,26 +215,16 @@ void WorldSession::HandleGroupDeclineOpcode( WorldPacket & /*recv_data*/ ) Group *group = GetPlayer()->GetGroupInvite(); if (!group) return; + // remember leader if online Player *leader = objmgr.GetPlayer(group->GetLeaderGUID()); - /** error handling **/ + // uninvite, group can be deleted + GetPlayer()->UninviteFromGroup(); + if(!leader || !leader->GetSession()) return; - /********************/ - - // everything's fine, do it - if(!group->IsCreated()) - { - // note: this means that if you invite more than one person - // and one of them declines before the first one accepts - // all invites will be cleared - // fixme: is that ok ? - group->RemoveAllInvites(); - delete group; - } - - GetPlayer()->SetGroupInvite(NULL); + // report WorldPacket data( SMSG_GROUP_DECLINE, 10 ); // guess size data << GetPlayer()->GetName(); leader->GetSession()->SendPacket( &data ); @@ -445,7 +426,7 @@ void WorldSession::HandleMinimapPingOpcode(WorldPacket& recv_data) data << GetPlayer()->GetGUID(); data << x; data << y; - GetPlayer()->GetGroup()->BroadcastPacket(&data, -1, GetPlayer()->GetGUID()); + GetPlayer()->GetGroup()->BroadcastPacket(&data, true, -1, GetPlayer()->GetGUID()); } void WorldSession::HandleRandomRollOpcode(WorldPacket& recv_data) @@ -472,7 +453,7 @@ void WorldSession::HandleRandomRollOpcode(WorldPacket& recv_data) data << roll; data << GetPlayer()->GetGUID(); if(GetPlayer()->GetGroup()) - GetPlayer()->GetGroup()->BroadcastPacket(&data); + GetPlayer()->GetGroup()->BroadcastPacket(&data, false); else SendPacket(&data); } @@ -555,8 +536,20 @@ void WorldSession::HandleGroupChangeSubGroupOpcode( WorldPacket & recv_data ) return; /********************/ + Player *movedPlayer=objmgr.GetPlayer(name.c_str()); + if(!movedPlayer) + return; + + //Do not allow leader to change group of player in combat + if (movedPlayer->isInCombat()) + { + WorldPacket data(SMSG_GROUP_SWAP_FAILED, (0)); + SendPacket(&data); + return; + } + // everything's fine, do it - group->ChangeMembersGroup(objmgr.GetPlayer(name.c_str()), groupNr); + group->ChangeMembersGroup(movedPlayer, groupNr); } void WorldSession::HandleGroupAssistantOpcode( WorldPacket & recv_data ) @@ -626,7 +619,7 @@ void WorldSession::HandleRaidReadyCheckOpcode( WorldPacket & recv_data ) // everything's fine, do it WorldPacket data(MSG_RAID_READY_CHECK, 8); data << GetPlayer()->GetGUID(); - group->BroadcastPacket(&data, -1); + group->BroadcastPacket(&data, false, -1); group->OfflineReadyCheck(); } @@ -691,10 +684,10 @@ void WorldSession::BuildPartyMemberStatsChangedPacket(Player *player, WorldPacke } if (mask & GROUP_UPDATE_FLAG_CUR_HP) - *data << (uint16) player->GetHealth(); + *data << (uint32) player->GetHealth(); if (mask & GROUP_UPDATE_FLAG_MAX_HP) - *data << (uint16) player->GetMaxHealth(); + *data << (uint32) player->GetMaxHealth(); Powers powerType = player->getPowerType(); if (mask & GROUP_UPDATE_FLAG_POWER_TYPE) @@ -717,14 +710,14 @@ void WorldSession::BuildPartyMemberStatsChangedPacket(Player *player, WorldPacke if (mask & GROUP_UPDATE_FLAG_AURAS) { - uint64 auramask = player->GetAuraUpdateMask(); + const uint64& auramask = player->GetAuraUpdateMaskForRaid(); *data << uint64(auramask); for(uint32 i = 0; i < MAX_AURAS; ++i) { if(auramask & (uint64(1) << i)) { - uint32 updatedAura=player->GetUInt32Value(uint16(UNIT_FIELD_AURA + i)); - *data << uint16(updatedAura); + Aura * pAura = player->GetVisibleAura(i); + *data << uint32(pAura ? pAura->GetId() : 0); *data << uint8(1); } } @@ -758,17 +751,17 @@ void WorldSession::BuildPartyMemberStatsChangedPacket(Player *player, WorldPacke if (mask & GROUP_UPDATE_FLAG_PET_CUR_HP) { if(pet) - *data << (uint16) pet->GetHealth(); + *data << (uint32) pet->GetHealth(); else - *data << (uint16) 0; + *data << (uint32) 0; } if (mask & GROUP_UPDATE_FLAG_PET_MAX_HP) { if(pet) - *data << (uint16) pet->GetMaxHealth(); + *data << (uint32) pet->GetMaxHealth(); else - *data << (uint16) 0; + *data << (uint32) 0; } if (mask & GROUP_UPDATE_FLAG_PET_POWER_TYPE) @@ -799,14 +792,14 @@ void WorldSession::BuildPartyMemberStatsChangedPacket(Player *player, WorldPacke { if(pet) { - uint64 auramask = pet->GetAuraUpdateMask(); + const uint64& auramask = pet->GetAuraUpdateMaskForRaid(); *data << uint64(auramask); for(uint32 i = 0; i < MAX_AURAS; ++i) { if(auramask & (uint64(1) << i)) { - uint32 updatedAura=pet->GetUInt32Value(uint16(UNIT_FIELD_AURA + i)); - *data << uint16(updatedAura); + Aura * pAura = pet->GetVisibleAura(i); + *data << uint32(pAura ? pAura->GetId() : 0); *data << uint8(1); } } @@ -829,6 +822,7 @@ void WorldSession::HandleRequestPartyMemberStatsOpcode( WorldPacket &recv_data ) if(!player) { WorldPacket data(SMSG_PARTY_MEMBER_STATS_FULL, 3+4+2); + data << uint8(0); // only for SMSG_PARTY_MEMBER_STATS_FULL, probably arena/bg related data.appendPackGUID(Guid); data << (uint32) GROUP_UPDATE_FLAG_STATUS; data << (uint16) MEMBER_STATUS_OFFLINE; @@ -839,6 +833,7 @@ void WorldSession::HandleRequestPartyMemberStatsOpcode( WorldPacket &recv_data ) Pet *pet = player->GetPet(); WorldPacket data(SMSG_PARTY_MEMBER_STATS_FULL, 4+2+2+2+1+2*6+8+1+8); + data << uint8(0); // only for SMSG_PARTY_MEMBER_STATS_FULL, probably arena/bg related data.append(player->GetPackGUID()); uint32 mask1 = 0x00040BFF; // common mask, real flags used 0x000040BFF @@ -848,8 +843,8 @@ void WorldSession::HandleRequestPartyMemberStatsOpcode( WorldPacket &recv_data ) Powers powerType = player->getPowerType(); data << (uint32) mask1; // group update mask data << (uint16) MEMBER_STATUS_ONLINE; // member's online status - data << (uint16) player->GetHealth(); // GROUP_UPDATE_FLAG_CUR_HP - data << (uint16) player->GetMaxHealth(); // GROUP_UPDATE_FLAG_MAX_HP + data << (uint32) player->GetHealth(); // GROUP_UPDATE_FLAG_CUR_HP + data << (uint32) player->GetMaxHealth(); // GROUP_UPDATE_FLAG_MAX_HP data << (uint8) powerType; // GROUP_UPDATE_FLAG_POWER_TYPE data << (uint16) player->GetPower(powerType); // GROUP_UPDATE_FLAG_CUR_POWER data << (uint16) player->GetMaxPower(powerType); // GROUP_UPDATE_FLAG_MAX_POWER @@ -863,11 +858,11 @@ void WorldSession::HandleRequestPartyMemberStatsOpcode( WorldPacket &recv_data ) data << (uint64) auramask; // placeholder for(uint8 i = 0; i < MAX_AURAS; ++i) { - if(uint32 aura = player->GetUInt32Value(UNIT_FIELD_AURA + i)) + if(Aura * pAura = player->GetVisibleAura(i)) { auramask |= (uint64(1) << i); - data << uint16(aura); - data << uint8(1); + data << (uint32) pAura->GetId(); + data << (uint8) 1; } } data.put<uint64>(maskPos,auramask); // GROUP_UPDATE_FLAG_AURAS @@ -878,8 +873,8 @@ void WorldSession::HandleRequestPartyMemberStatsOpcode( WorldPacket &recv_data ) data << (uint64) pet->GetGUID(); // GROUP_UPDATE_FLAG_PET_GUID data << pet->GetName(); // GROUP_UPDATE_FLAG_PET_NAME data << (uint16) pet->GetDisplayId(); // GROUP_UPDATE_FLAG_PET_MODEL_ID - data << (uint16) pet->GetHealth(); // GROUP_UPDATE_FLAG_PET_CUR_HP - data << (uint16) pet->GetMaxHealth(); // GROUP_UPDATE_FLAG_PET_MAX_HP + data << (uint32) pet->GetHealth(); // GROUP_UPDATE_FLAG_PET_CUR_HP + data << (uint32) pet->GetMaxHealth(); // GROUP_UPDATE_FLAG_PET_MAX_HP data << (uint8) petpowertype; // GROUP_UPDATE_FLAG_PET_POWER_TYPE data << (uint16) pet->GetPower(petpowertype); // GROUP_UPDATE_FLAG_PET_CUR_POWER data << (uint16) pet->GetMaxPower(petpowertype); // GROUP_UPDATE_FLAG_PET_MAX_POWER @@ -889,10 +884,10 @@ void WorldSession::HandleRequestPartyMemberStatsOpcode( WorldPacket &recv_data ) data << (uint64) petauramask; // placeholder for(uint8 i = 0; i < MAX_AURAS; ++i) { - if(uint32 petaura = pet->GetUInt32Value(UNIT_FIELD_AURA + i)) + if(Aura * pAura = pet->GetVisibleAura(i)) { petauramask |= (uint64(1) << i); - data << (uint16) petaura; + data << (uint32) pAura->GetId(); data << (uint8) 1; } } diff --git a/src/game/GroupRefManager.h b/src/game/GroupRefManager.h index 94741c30279..ab3b6417611 100644 --- a/src/game/GroupRefManager.h +++ b/src/game/GroupRefManager.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/GroupReference.cpp b/src/game/GroupReference.cpp index 24646e3059a..a09fc769dad 100644 --- a/src/game/GroupReference.cpp +++ b/src/game/GroupReference.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/GroupReference.h b/src/game/GroupReference.h index 0b338991109..da896cf02dd 100644 --- a/src/game/GroupReference.h +++ b/src/game/GroupReference.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/GuardAI.cpp b/src/game/GuardAI.cpp index 07a5bd9f819..710acede150 100644 --- a/src/game/GuardAI.cpp +++ b/src/game/GuardAI.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,107 +33,107 @@ int GuardAI::Permissible(const Creature *creature) return PERMIT_BASE_NO; } -GuardAI::GuardAI(Creature *c) : CreatureAI(c), i_creature(*c), i_victimGuid(0), i_state(STATE_NORMAL), i_tracker(TIME_INTERVAL_LOOK) +GuardAI::GuardAI(Creature *c) : CreatureAI(c), i_victimGuid(0), i_state(STATE_NORMAL), i_tracker(TIME_INTERVAL_LOOK) { } void GuardAI::MoveInLineOfSight(Unit *u) { // Ignore Z for flying creatures - if ( !i_creature.canFly() && i_creature.GetDistanceZ(u) > CREATURE_Z_ATTACK_RANGE ) + if ( !m_creature->canFly() && m_creature->GetDistanceZ(u) > CREATURE_Z_ATTACK_RANGE ) return; - if( !i_creature.getVictim() && i_creature.canAttack(u) && - ( u->IsHostileToPlayers() || i_creature.IsHostileTo(u) /*|| u->getVictim() && i_creature.IsFriendlyTo(u->getVictim())*/ ) && - u->isInAccessiblePlaceFor(&i_creature)) + if( !m_creature->getVictim() && m_creature->canAttack(u) && + ( u->IsHostileToPlayers() || m_creature->IsHostileTo(u) /*|| u->getVictim() && m_creature->IsFriendlyTo(u->getVictim())*/ ) && + u->isInAccessiblePlaceFor(m_creature)) { - float attackRadius = i_creature.GetAttackDistance(u); - if(i_creature.IsWithinDistInMap(u,attackRadius)) + float attackRadius = m_creature->GetAttackDistance(u); + if(m_creature->IsWithinDistInMap(u,attackRadius)) { //Need add code to let guard support player AttackStart(u); - //u->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + //u->RemoveAurasByType(SPELL_AURA_MOD_STEALTH); } } } void GuardAI::EnterEvadeMode() { - if( !i_creature.isAlive() ) + if( !m_creature->isAlive() ) { - DEBUG_LOG("Creature stopped attacking because he's dead [guid=%u]", i_creature.GetGUIDLow()); - i_creature.GetMotionMaster()->MoveIdle(); + DEBUG_LOG("Creature stopped attacking because he's dead [guid=%u]", m_creature->GetGUIDLow()); + m_creature->GetMotionMaster()->MoveIdle(); i_state = STATE_NORMAL; i_victimGuid = 0; - i_creature.CombatStop(); - i_creature.DeleteThreatList(); + m_creature->CombatStop(); + m_creature->DeleteThreatList(); return; } - Unit* victim = ObjectAccessor::GetUnit(i_creature, i_victimGuid ); + Unit* victim = ObjectAccessor::GetUnit(*m_creature, i_victimGuid ); if( !victim ) { - DEBUG_LOG("Creature stopped attacking because victim is non exist [guid=%u]", i_creature.GetGUIDLow()); + DEBUG_LOG("Creature stopped attacking because victim is non exist [guid=%u]", m_creature->GetGUIDLow()); } else if( !victim ->isAlive() ) { - DEBUG_LOG("Creature stopped attacking because victim is dead [guid=%u]", i_creature.GetGUIDLow()); + DEBUG_LOG("Creature stopped attacking because victim is dead [guid=%u]", m_creature->GetGUIDLow()); } else if( victim ->HasStealthAura() ) { - DEBUG_LOG("Creature stopped attacking because victim is using stealth [guid=%u]", i_creature.GetGUIDLow()); + DEBUG_LOG("Creature stopped attacking because victim is using stealth [guid=%u]", m_creature->GetGUIDLow()); } else if( victim ->isInFlight() ) { - DEBUG_LOG("Creature stopped attacking because victim is flying away [guid=%u]", i_creature.GetGUIDLow()); + DEBUG_LOG("Creature stopped attacking because victim is flying away [guid=%u]", m_creature->GetGUIDLow()); } else { - DEBUG_LOG("Creature stopped attacking because victim outran him [guid=%u]", i_creature.GetGUIDLow()); + DEBUG_LOG("Creature stopped attacking because victim outran him [guid=%u]", m_creature->GetGUIDLow()); } - i_creature.RemoveAllAuras(); - i_creature.DeleteThreatList(); + m_creature->RemoveAllAuras(); + m_creature->DeleteThreatList(); i_victimGuid = 0; - i_creature.CombatStop(); + m_creature->CombatStop(); i_state = STATE_NORMAL; // Remove TargetedMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead - if( i_creature.GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE ) - i_creature.GetMotionMaster()->MoveTargetedHome(); + if( m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE ) + m_creature->GetMotionMaster()->MoveTargetedHome(); } void GuardAI::UpdateAI(const uint32 /*diff*/) { - // update i_victimGuid if i_creature.getVictim() !=0 and changed + // update i_victimGuid if m_creature->getVictim() !=0 and changed if(!UpdateVictim()) return; - i_victimGuid = i_creature.getVictim()->GetGUID(); + i_victimGuid = m_creature->getVictim()->GetGUID(); - if( i_creature.isAttackReady() ) + if( m_creature->isAttackReady() ) { - if( i_creature.IsWithinMeleeRange(i_creature.getVictim())) + if( m_creature->IsWithinMeleeRange(m_creature->getVictim())) { - i_creature.AttackerStateUpdate(i_creature.getVictim()); - i_creature.resetAttackTimer(); + m_creature->AttackerStateUpdate(m_creature->getVictim()); + m_creature->resetAttackTimer(); } } } bool GuardAI::IsVisible(Unit *pl) const { - return i_creature.GetDistance(pl) < sWorld.getConfig(CONFIG_SIGHT_GUARDER) - && pl->isVisibleForOrDetect(&i_creature,true); + return m_creature->GetDistance(pl) < sWorld.getConfig(CONFIG_SIGHT_GUARDER) + && pl->isVisibleForOrDetect(m_creature,true); } void GuardAI::JustDied(Unit *killer) { if(Player* pkiller = killer->GetCharmerOrOwnerPlayerOrPlayerItself()) - i_creature.SendZoneUnderAttackMessage(pkiller); + m_creature->SendZoneUnderAttackMessage(pkiller); } diff --git a/src/game/GuardAI.h b/src/game/GuardAI.h index b7b3c79607b..db1bfe0229d 100644 --- a/src/game/GuardAI.h +++ b/src/game/GuardAI.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,7 +36,7 @@ class TRINITY_DLL_DECL GuardAI : public CreatureAI public: - GuardAI(Creature *c); + explicit GuardAI(Creature *c); void MoveInLineOfSight(Unit *); void EnterEvadeMode(); @@ -47,7 +47,6 @@ class TRINITY_DLL_DECL GuardAI : public CreatureAI static int Permissible(const Creature *); private: - Creature &i_creature; uint64 i_victimGuid; GuardState i_state; TimeTracker i_tracker; diff --git a/src/game/Guild.cpp b/src/game/Guild.cpp index 87dbb81c5c6..b35bdf300bf 100644 --- a/src/game/Guild.cpp +++ b/src/game/Guild.cpp @@ -21,7 +21,6 @@ #include "Database/DatabaseEnv.h" #include "WorldPacket.h" #include "WorldSession.h" -#include "MapManager.h" #include "Player.h" #include "Opcodes.h" #include "ObjectMgr.h" @@ -29,6 +28,7 @@ #include "Chat.h" #include "SocialMgr.h" #include "Util.h" +#include "Language.h" Guild::Guild() { @@ -52,27 +52,25 @@ Guild::~Guild() } -bool Guild::create(uint64 lGuid, std::string gname) +bool Guild::create(Player* leader, std::string gname) { - std::string rname; - std::string lName; - - if(!objmgr.GetPlayerNameByGUID(lGuid, lName)) - return false; if(objmgr.GetGuildByName(gname)) return false; - sLog.outDebug("GUILD: creating guild %s to leader: %u", gname.c_str(), GUID_LOPART(lGuid)); + WorldSession* lSession = leader->GetSession(); + if(!lSession) + return false; - leaderGuid = lGuid; + leaderGuid = leader->GetGUID(); name = gname; GINFO = ""; MOTD = "No message set."; guildbank_money = 0; purchased_tabs = 0; - Id = objmgr.GenerateGuildId(); + sLog.outDebug("GUILD: creating guild %s to leader: %u", gname.c_str(), GUID_LOPART(leaderGuid)); + // gname already assigned to Guild::name, use it to encode string for DB CharacterDatabase.escape_string(gname); @@ -90,18 +88,13 @@ bool Guild::create(uint64 lGuid, std::string gname) Id, gname.c_str(), GUID_LOPART(leaderGuid), dbGINFO.c_str(), dbMOTD.c_str(), EmblemStyle, EmblemColor, BorderStyle, BorderColor, BackgroundColor, guildbank_money); CharacterDatabase.CommitTransaction(); - rname = "Guild Master"; - CreateRank(rname,GR_RIGHT_ALL); - rname = "Officer"; - CreateRank(rname,GR_RIGHT_ALL); - rname = "Veteran"; - CreateRank(rname,GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK); - rname = "Member"; - CreateRank(rname,GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK); - rname = "Initiate"; - CreateRank(rname,GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK); + CreateRank(lSession->GetMangosString(LANG_GUILD_MASTER), GR_RIGHT_ALL); + CreateRank(lSession->GetMangosString(LANG_GUILD_OFFICER), GR_RIGHT_ALL); + CreateRank(lSession->GetMangosString(LANG_GUILD_VETERAN), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK); + CreateRank(lSession->GetMangosString(LANG_GUILD_MEMBER), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK); + CreateRank(lSession->GetMangosString(LANG_GUILD_INITIATE),GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK); - return AddMember(lGuid, (uint32)GR_GUILDMASTER); + return AddMember(leaderGuid, (uint32)GR_GUILDMASTER); } bool Guild::AddMember(uint64 plGuid, uint32 plRank) @@ -621,7 +614,7 @@ void Guild::CreateRank(std::string name_,uint32 rights) // name now can be used for encoding to DB CharacterDatabase.escape_string(name_); - CharacterDatabase.PExecute( "INSERT INTO guild_rank (guildid,rid,rname,rights) VALUES ('%u', '%u', '%s', '%u')", Id, m_ranks.size(), name_.c_str(), rights ); + CharacterDatabase.PExecute( "INSERT INTO guild_rank (guildid,rid,rname,rights) VALUES ('%u', '%u', '%s', '%u')", Id, (unsigned int)m_ranks.size(), name_.c_str(), rights ); } void Guild::AddRank(const std::string& name_,uint32 rights, uint32 money) @@ -785,6 +778,7 @@ void Guild::Query(WorldSession *session) data << uint32(BorderStyle); data << uint32(BorderColor); data << uint32(BackgroundColor); + data << uint32(0); // something new in WotLK session->SendPacket( &data ); sLog.outDebug( "WORLD: Sent (SMSG_GUILD_QUERY_RESPONSE)" ); @@ -1603,7 +1597,21 @@ void Guild::DisplayGuildBankLogs(WorldSession *session, uint8 TabId) { data << uint8((*itr)->LogEntry); data << uint64(MAKE_NEW_GUID((*itr)->PlayerGuid,0,HIGHGUID_PLAYER)); - data << uint32((*itr)->ItemOrMoney); + if ((*itr)->LogEntry == GUILD_BANK_LOG_DEPOSIT_MONEY || + (*itr)->LogEntry == GUILD_BANK_LOG_WITHDRAW_MONEY || + (*itr)->LogEntry == GUILD_BANK_LOG_REPAIR_MONEY || + (*itr)->LogEntry == GUILD_BANK_LOG_UNK1 || + (*itr)->LogEntry == GUILD_BANK_LOG_UNK2) + { + data << uint32((*itr)->ItemOrMoney); + } + else + { + data << uint32((*itr)->ItemOrMoney); + data << uint32((*itr)->ItemStackCount); + if ((*itr)->LogEntry == GUILD_BANK_LOG_MOVE_ITEM || (*itr)->LogEntry == GUILD_BANK_LOG_MOVE_ITEM2) + data << uint8((*itr)->DestTabId); // moved tab + } data << uint32(time(NULL)-(*itr)->TimeStamp); } session->SendPacket(&data); @@ -1619,10 +1627,21 @@ void Guild::DisplayGuildBankLogs(WorldSession *session, uint8 TabId) { data << uint8((*itr)->LogEntry); data << uint64(MAKE_NEW_GUID((*itr)->PlayerGuid,0,HIGHGUID_PLAYER)); - data << uint32((*itr)->ItemOrMoney); - data << uint8((*itr)->ItemStackCount); - if ((*itr)->LogEntry == GUILD_BANK_LOG_MOVE_ITEM || (*itr)->LogEntry == GUILD_BANK_LOG_MOVE_ITEM2) - data << uint8((*itr)->DestTabId); // moved tab + if ((*itr)->LogEntry == GUILD_BANK_LOG_DEPOSIT_MONEY || + (*itr)->LogEntry == GUILD_BANK_LOG_WITHDRAW_MONEY || + (*itr)->LogEntry == GUILD_BANK_LOG_REPAIR_MONEY || + (*itr)->LogEntry == GUILD_BANK_LOG_UNK1 || + (*itr)->LogEntry == GUILD_BANK_LOG_UNK2) + { + data << uint32((*itr)->ItemOrMoney); + } + else + { + data << uint32((*itr)->ItemOrMoney); + data << uint32((*itr)->ItemStackCount); + if ((*itr)->LogEntry == GUILD_BANK_LOG_MOVE_ITEM || (*itr)->LogEntry == GUILD_BANK_LOG_MOVE_ITEM2) + data << uint8((*itr)->DestTabId); // moved tab + } data << uint32(time(NULL)-(*itr)->TimeStamp); } session->SendPacket(&data); @@ -1704,7 +1723,7 @@ void Guild::AppendDisplayGuildBankSlot( WorldPacket& data, GuildBankTab const *t // SuffixFactor +4 data << (uint32) pItem->GetItemSuffixFactor(); // +12 // ITEM_FIELD_STACK_COUNT - data << uint8(pItem->GetCount()); + data << uint32(pItem->GetCount()); data << uint32(0); // +16 // Unknown value data << uint8(0); // unknown 2.4.2 if (uint32 Enchant0 = pItem->GetEnchantmentId(PERM_ENCHANTMENT_SLOT)) @@ -1961,6 +1980,9 @@ void Guild::SetGuildBankTabText(uint8 TabId, std::string text) CharacterDatabase.escape_string(text); CharacterDatabase.PExecute("UPDATE guild_bank_tab SET TabText='%s' WHERE guildid='%u' AND TabId='%u'", text.c_str(), Id, uint32(TabId)); + + // announce + SendGuildBankTabText(NULL,TabId); } void Guild::SendGuildBankTabText(WorldSession *session, uint8 TabId) @@ -1975,7 +1997,12 @@ void Guild::SendGuildBankTabText(WorldSession *session, uint8 TabId) WorldPacket data(MSG_QUERY_GUILD_BANK_TEXT, 1+tab->Text.size()+1); data << uint8(TabId); data << tab->Text; - session->SendPacket(&data); + + if(session) + session->SendPacket(&data); + else + BroadcastPacket(&data); + } bool GuildItemPosCount::isContainedIn(GuildItemPosCountVec const &vec) const diff --git a/src/game/Guild.h b/src/game/Guild.h index 5dc6c00111d..12dea1feb1f 100644 --- a/src/game/Guild.h +++ b/src/game/Guild.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -53,10 +53,11 @@ enum GuildRankRights GR_RIGHT_VIEWOFFNOTE = 0x00004040, GR_RIGHT_EOFFNOTE = 0x00008040, GR_RIGHT_MODIFY_GUILD_INFO = 0x00010040, - GR_RIGHT_REPAIR_FROM_GUILD = 0x00020000, // unused in 2.4.x?, Remove money withdraw capacity + GR_RIGHT_WITHDRAW_GOLD_LOCK = 0x00020000, // remove money withdraw capacity GR_RIGHT_WITHDRAW_REPAIR = 0x00040000, // withdraw for repair GR_RIGHT_WITHDRAW_GOLD = 0x00080000, // withdraw gold - GR_RIGHT_ALL = 0x000FF1FF + GR_RIGHT_CREATE_GUILD_EVENT = 0x00100000, // wotlk + GR_RIGHT_ALL = 0x001DF1FF }; enum Typecommand @@ -156,6 +157,8 @@ enum GuildBankLogEntries GUILD_BANK_LOG_WITHDRAW_MONEY = 5, GUILD_BANK_LOG_REPAIR_MONEY = 6, GUILD_BANK_LOG_MOVE_ITEM2 = 7, + GUILD_BANK_LOG_UNK1 = 8, + GUILD_BANK_LOG_UNK2 = 9, }; enum GuildEventLogEntryTypes @@ -217,12 +220,12 @@ struct GuildBankTab struct GuildItemPosCount { - GuildItemPosCount(uint8 _slot, uint8 _count) : slot(_slot), count(_count) {} + GuildItemPosCount(uint8 _slot, uint32 _count) : slot(_slot), count(_count) {} bool isContainedIn(std::vector<GuildItemPosCount> const& vec) const; uint8 slot; - uint8 count; + uint32 count; }; typedef std::vector<GuildItemPosCount> GuildItemPosCountVec; @@ -266,7 +269,7 @@ class Guild Guild(); ~Guild(); - bool create(uint64 lGuid, std::string gname); + bool create(Player* leader, std::string gname); void Disband(); typedef std::map<uint32, MemberSlot> MemberList; @@ -314,6 +317,15 @@ class Guild void BroadcastPacketToRank(WorldPacket *packet, uint32 rankId); void BroadcastPacket(WorldPacket *packet); + template<class Do> + void BroadcastWorker(Do& _do, Player* except = NULL) + { + for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) + if(Player *player = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER))) + if(player != except) + _do(player); + } + void CreateRank(std::string name,uint32 rights); void DelRank(); std::string GetRankName(uint32 rankId); diff --git a/src/game/GuildHandler.cpp b/src/game/GuildHandler.cpp index 77e9af49ed3..1b8ec5eb836 100644 --- a/src/game/GuildHandler.cpp +++ b/src/game/GuildHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,7 +26,6 @@ #include "Log.h" #include "Opcodes.h" #include "Guild.h" -#include "MapManager.h" #include "GossipDef.h" #include "SocialMgr.h" @@ -65,7 +64,7 @@ void WorldSession::HandleGuildCreateOpcode(WorldPacket& recvPacket) return; Guild *guild = new Guild; - if(!guild->create(GetPlayer()->GetGUID(),gname)) + if(!guild->create(GetPlayer(),gname)) { delete guild; return; @@ -375,7 +374,7 @@ void WorldSession::HandleGuildDemoteOpcode(WorldPacket& recvPacket) guild->ChangeRank(plGuid, (slot->RankId+1)); // Put record into guildlog - guild->LogGuildEvent(GUILD_EVENT_LOG_DEMOTE_PLAYER, GetPlayer()->GetGUIDLow(), GUID_LOPART(plGuid), (slot->RankId)); + guild->LogGuildEvent(GUILD_EVENT_LOG_DEMOTE_PLAYER, GetPlayer()->GetGUIDLow(), GUID_LOPART(plGuid), slot->RankId); WorldPacket data(SMSG_GUILD_EVENT, (2+30)); // guess size data << (uint8)GE_DEMOTION; @@ -663,7 +662,7 @@ void WorldSession::HandleGuildRankOpcode(WorldPacket& recvPacket) guild->SetRankName(rankId, rankname); if(rankId==GR_GUILDMASTER) // prevent loss leader rights - rights |= GR_RIGHT_ALL; + rights = GR_RIGHT_ALL; guild->SetRankRights(rankId, rights); @@ -794,7 +793,7 @@ void WorldSession::HandleGuildSaveEmblemOpcode(WorldPacket& recvPacket) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); recvPacket >> EmblemStyle; recvPacket >> EmblemColor; @@ -906,7 +905,7 @@ void WorldSession::HandleGuildBankQuery( WorldPacket & recv_data ) uint8 unk; recv_data >> GoGuid >> unk; - if (!objmgr.IsGuildVaultGameObject(_player, GoGuid)) + if (!objmgr.IsGameObjectOfTypeInRange(_player, GoGuid, GAMEOBJECT_TYPE_GUILD_BANK)) return; if (uint32 GuildId = GetPlayer()->GetGuildId()) @@ -930,7 +929,7 @@ void WorldSession::HandleGuildBankTabColon( WorldPacket & recv_data ) uint8 TabId,unk1; recv_data >> GoGuid >> TabId >> unk1; - if (!objmgr.IsGuildVaultGameObject(_player, GoGuid)) + if (!objmgr.IsGameObjectOfTypeInRange(_player, GoGuid, GAMEOBJECT_TYPE_GUILD_BANK)) return; uint32 GuildId = GetPlayer()->GetGuildId(); @@ -959,7 +958,7 @@ void WorldSession::HandleGuildBankDeposit( WorldPacket & recv_data ) if (!money) return; - if (!objmgr.IsGuildVaultGameObject(_player, GoGuid)) + if (!objmgr.IsGameObjectOfTypeInRange(_player, GoGuid, GAMEOBJECT_TYPE_GUILD_BANK)) return; uint32 GuildId = GetPlayer()->GetGuildId(); @@ -1007,7 +1006,7 @@ void WorldSession::HandleGuildBankWithdraw( WorldPacket & recv_data ) if (!money) return; - if (!objmgr.IsGuildVaultGameObject(_player, GoGuid)) + if (!objmgr.IsGameObjectOfTypeInRange(_player, GoGuid, GAMEOBJECT_TYPE_GUILD_BANK)) return; uint32 GuildId = GetPlayer()->GetGuildId(); @@ -1109,7 +1108,7 @@ void WorldSession::HandleGuildBankDepositItem( WorldPacket & recv_data ) return; } - if (!objmgr.IsGuildVaultGameObject(_player, GoGuid)) + if (!objmgr.IsGameObjectOfTypeInRange(_player, GoGuid, GAMEOBJECT_TYPE_GUILD_BANK)) return; uint32 GuildId = GetPlayer()->GetGuildId(); @@ -1564,7 +1563,7 @@ void WorldSession::HandleGuildBankBuyTab( WorldPacket & recv_data ) recv_data >> GoGuid; recv_data >> TabId; - if (!objmgr.IsGuildVaultGameObject(_player, GoGuid)) + if (!objmgr.IsGameObjectOfTypeInRange(_player, GoGuid, GAMEOBJECT_TYPE_GUILD_BANK)) return; uint32 GuildId = GetPlayer()->GetGuildId(); @@ -1621,7 +1620,7 @@ void WorldSession::HandleGuildBankModifyTab( WorldPacket & recv_data ) if(IconIndex.empty()) return; - if (!objmgr.IsGuildVaultGameObject(_player, GoGuid)) + if (!objmgr.IsGameObjectOfTypeInRange(_player, GoGuid, GAMEOBJECT_TYPE_GUILD_BANK)) return; uint32 GuildId = GetPlayer()->GetGuildId(); diff --git a/src/game/HomeMovementGenerator.cpp b/src/game/HomeMovementGenerator.cpp index ddf66ad220c..4f0ca2f8cec 100644 --- a/src/game/HomeMovementGenerator.cpp +++ b/src/game/HomeMovementGenerator.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,9 +20,8 @@ #include "HomeMovementGenerator.h" #include "Creature.h" +#include "CreatureAI.h" #include "Traveller.h" -#include "MapManager.h" -#include "ObjectAccessor.h" #include "DestinationHolderImp.h" #include "WorldPacket.h" @@ -70,11 +69,14 @@ HomeMovementGenerator<Creature>::Update(Creature &owner, const uint32& time_diff // restore orientation of not moving creature at returning to home if(owner.GetDefaultMovementType()==IDLE_MOTION_TYPE) { + //sLog.outDebug("Entering HomeMovement::GetDestination(z,y,z)"); owner.SetOrientation(ori); WorldPacket packet; owner.BuildHeartBeatMsg(&packet); owner.SendMessageToSet(&packet, false); } + + owner.AI()->JustReachedHome(); return false; } diff --git a/src/game/HomeMovementGenerator.h b/src/game/HomeMovementGenerator.h index 12d5f150405..964b5c3e801 100644 --- a/src/game/HomeMovementGenerator.h +++ b/src/game/HomeMovementGenerator.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -47,6 +47,7 @@ class TRINITY_DLL_SPEC HomeMovementGenerator<Creature> MovementGeneratorType GetMovementGeneratorType() { return HOME_MOTION_TYPE; } bool GetDestination(float& x, float& y, float& z) const { i_destinationHolder.GetDestination(x,y,z); return true; } + private: void _setTargetLocation(Creature &); DestinationHolder< Traveller<Creature> > i_destinationHolder; diff --git a/src/game/HostilRefManager.cpp b/src/game/HostilRefManager.cpp index f1cb465520e..4a51d54248d 100644 --- a/src/game/HostilRefManager.cpp +++ b/src/game/HostilRefManager.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,7 +21,7 @@ #include "HostilRefManager.h" #include "ThreatManager.h" #include "Unit.h" -#include "Database/DBCStructure.h" +#include "DBCStructure.h" #include "SpellMgr.h" HostilRefManager::~HostilRefManager() diff --git a/src/game/HostilRefManager.h b/src/game/HostilRefManager.h index e7addd5479b..316509e3908 100644 --- a/src/game/HostilRefManager.h +++ b/src/game/HostilRefManager.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/IdleMovementGenerator.cpp b/src/game/IdleMovementGenerator.cpp index c6598409b44..67f89303e40 100644 --- a/src/game/IdleMovementGenerator.cpp +++ b/src/game/IdleMovementGenerator.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,7 +19,7 @@ */ #include "IdleMovementGenerator.h" -#include "Creature.h" +#include "Unit.h" IdleMovementGenerator si_idleMovement; diff --git a/src/game/IdleMovementGenerator.h b/src/game/IdleMovementGenerator.h index ac5a4da529c..7ada9f08f52 100644 --- a/src/game/IdleMovementGenerator.h +++ b/src/game/IdleMovementGenerator.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/InstanceData.cpp b/src/game/InstanceData.cpp index ac607bfd7ac..808ebca2f59 100644 --- a/src/game/InstanceData.cpp +++ b/src/game/InstanceData.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,8 +30,8 @@ void InstanceData::SaveToDB() CharacterDatabase.PExecute("UPDATE instance SET data = '%s' WHERE id = '%d'", data.c_str(), instance->GetInstanceId()); } -void InstanceData::HandleGameObject(uint64 GUID, bool open, GameObject *go) -{ +void InstanceData::HandleGameObject(uint64 GUID, bool open, GameObject *go) +{ if(!go) go = instance->GetGameObjectInMap(GUID); if(go) diff --git a/src/game/InstanceData.h b/src/game/InstanceData.h index ec36a794525..c33beccfffb 100644 --- a/src/game/InstanceData.h +++ b/src/game/InstanceData.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -67,6 +67,10 @@ class TRINITY_DLL_SPEC InstanceData //called on creature creation virtual void OnCreatureCreate(Creature * /*creature*/, uint32 /*creature_entry*/) {} + //All-purpose data storage 64 bit + virtual uint64 GetData64(uint32 /*Data*/) { return 0; } + virtual void SetData64(uint32 /*Data*/, uint64 /*Value*/) { } + //All-purpose data storage 32 bit virtual uint32 GetData(uint32) { return 0; } virtual void SetData(uint32, uint32 data) {} diff --git a/src/game/InstanceSaveMgr.cpp b/src/game/InstanceSaveMgr.cpp index ba34f949a5f..d4232a71838 100644 --- a/src/game/InstanceSaveMgr.cpp +++ b/src/game/InstanceSaveMgr.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * * This program is free software; you can redistribute it and/or modify @@ -19,13 +19,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "InstanceSaveMgr.h" #include "Common.h" #include "Database/SQLStorage.h" #include "Player.h" #include "GridNotifiers.h" -#include "WorldSession.h" #include "Log.h" #include "GridStates.h" #include "CellImpl.h" @@ -37,7 +35,6 @@ #include "GridNotifiersImpl.h" #include "Config/ConfigEnv.h" #include "Transports.h" -#include "ObjectAccessor.h" #include "ObjectMgr.h" #include "World.h" #include "Group.h" @@ -142,9 +139,8 @@ void InstanceSaveManager::RemoveInstanceSave(uint32 InstanceId) } } -InstanceSave::InstanceSave(uint16 MapId, uint32 InstanceId, uint8 difficulty, - time_t resetTime, bool canReset) -: m_mapid(MapId), m_instanceid(InstanceId), m_resetTime(resetTime), +InstanceSave::InstanceSave(uint16 MapId, uint32 InstanceId, uint8 difficulty, time_t resetTime, bool canReset) +: m_resetTime(resetTime), m_instanceid(InstanceId), m_mapid(MapId), m_difficulty(difficulty), m_canReset(canReset) { } @@ -270,7 +266,7 @@ void InstanceSaveManager::CleanupInstances() // creature_respawn and gameobject_respawn are in another database // first, obtain total instance set - std::set< uint32 > InstanceSet; + std::set<uint32> InstanceSet; QueryResult *result = CharacterDatabase.Query("SELECT id FROM instance"); if( result ) { @@ -322,7 +318,7 @@ void InstanceSaveManager::PackInstances() // TODO: this can be done a LOT more efficiently // obtain set of all associations - std::set< uint32 > InstanceSet; + std::set<uint32> InstanceSet; // all valid ids are in the instance table // any associations to ids not in this table are assumed to be @@ -344,7 +340,7 @@ void InstanceSaveManager::PackInstances() uint32 InstanceNumber = 1; // we do assume std::set is sorted properly on integer value - for (std::set< uint32 >::iterator i = InstanceSet.begin(); i != InstanceSet.end(); ++i) + for (std::set<uint32>::iterator i = InstanceSet.begin(); i != InstanceSet.end(); ++i) { if (*i != InstanceNumber) { @@ -361,8 +357,8 @@ void InstanceSaveManager::PackInstances() bar.step(); } - sLog.outString(); sLog.outString( ">> Instance numbers remapped, next instance id is %u", InstanceNumber ); + sLog.outString(); } void InstanceSaveManager::LoadResetTimes() @@ -375,14 +371,14 @@ void InstanceSaveManager::LoadResetTimes() // get the current reset times for normal instances (these may need to be updated) // these are only kept in memory for InstanceSaves that are loaded later // resettime = 0 in the DB for raid/heroic instances so those are skipped - typedef std::map<uint32, std::pair<uint32, uint64> > ResetTimeMapType; + typedef std::map<uint32, std::pair<uint32, time_t> > ResetTimeMapType; ResetTimeMapType InstResetTime; QueryResult *result = CharacterDatabase.Query("SELECT id, map, resettime FROM instance WHERE resettime > 0"); if( result ) { do { - if(uint64 resettime = (*result)[2].GetUInt64()) + if(time_t resettime = time_t((*result)[2].GetUInt64())) { uint32 id = (*result)[0].GetUInt32(); uint32 mapid = (*result)[1].GetUInt32(); @@ -400,11 +396,11 @@ void InstanceSaveManager::LoadResetTimes() { Field *fields = result->Fetch(); uint32 instance = fields[1].GetUInt32(); - uint64 resettime = fields[0].GetUInt64() + 2 * HOUR; + time_t resettime = time_t(fields[0].GetUInt64() + 2 * HOUR); ResetTimeMapType::iterator itr = InstResetTime.find(instance); if(itr != InstResetTime.end() && itr->second.second != resettime) { - CharacterDatabase.DirectPExecute("UPDATE instance SET resettime = '"I64FMTD"' WHERE id = '%u'", resettime, instance); + CharacterDatabase.DirectPExecute("UPDATE instance SET resettime = '"I64FMTD"' WHERE id = '%u'", uint64(resettime), instance); itr->second.second = resettime; } } @@ -454,7 +450,7 @@ void InstanceSaveManager::LoadResetTimes() // add the global reset times to the priority queue for(uint32 i = 0; i < sInstanceTemplate.MaxEntry; i++) { - InstanceTemplate* temp = (InstanceTemplate*)objmgr.GetInstanceTemplate(i); + InstanceTemplate const* temp = objmgr.GetInstanceTemplate(i); if(!temp) continue; // only raid/heroic maps have a global reset time const MapEntry* entry = sMapStore.LookupEntry(temp->map); @@ -583,7 +579,7 @@ void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, bool warn, uint32 timeLe { // global reset for all instances of the given map // note: this isn't fast but it's meant to be executed very rarely - Map *map = (MapInstanced*)MapManager::Instance().GetBaseMap(mapid); + Map const *map = MapManager::Instance().GetBaseMap(mapid); if(!map->Instanceable()) return; uint64 now = (uint64)time(NULL); @@ -591,7 +587,7 @@ void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, bool warn, uint32 timeLe if(!warn) { // this is called one minute before the reset time - InstanceTemplate* temp = (InstanceTemplate*)objmgr.GetInstanceTemplate(mapid); + InstanceTemplate const* temp = objmgr.GetInstanceTemplate(mapid); if(!temp || !temp->reset_delay) { sLog.outError("InstanceSaveManager::ResetOrWarnAll: no instance template or reset delay for map %d", mapid); diff --git a/src/game/InstanceSaveMgr.h b/src/game/InstanceSaveMgr.h index 58b891b3a9f..2a913e5cedf 100644 --- a/src/game/InstanceSaveMgr.h +++ b/src/game/InstanceSaveMgr.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * * This program is free software; you can redistribute it and/or modify diff --git a/src/game/Item.cpp b/src/game/Item.cpp index 766bd81d640..26f49097b65 100644 --- a/src/game/Item.cpp +++ b/src/game/Item.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -206,6 +206,10 @@ bool ItemCanGoIntoBag(ItemPrototype const *pProto, ItemPrototype const *pBagProt if(!(pProto->BagFamily & BAG_FAMILY_MASK_LEATHERWORKING_SUPP)) return false; return true; + case ITEM_SUBCLASS_INSCRIPTION_CONTAINER: + if(!(pProto->BagFamily & BAG_FAMILY_MASK_INSCRIPTION_SUPP)) + return false; + return true; default: return false; } @@ -261,7 +265,7 @@ bool Item::Create( uint32 guidlow, uint32 itemid, Player const* owner) SetUInt32Value(ITEM_FIELD_MAXDURABILITY, itemProto->MaxDurability); SetUInt32Value(ITEM_FIELD_DURABILITY, itemProto->MaxDurability); - for(int i = 0; i < 5; ++i) + for(int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) SetSpellCharges(i,itemProto->Spells[i].SpellCharges); SetUInt32Value(ITEM_FIELD_FLAGS, itemProto->Flags); @@ -284,7 +288,7 @@ void Item::UpdateDuration(Player* owner, uint32 diff) } SetUInt32Value(ITEM_FIELD_DURATION, GetUInt32Value(ITEM_FIELD_DURATION) - diff); - SetState(ITEM_CHANGED); // save new time in database + SetState(ITEM_CHANGED, owner); // save new time in database } void Item::SaveToDB() @@ -346,7 +350,7 @@ bool Item::LoadFromDB(uint32 guid, uint64 owner_guid, QueryResult *result) if (!result) { - sLog.outError("ERROR: Item (GUID: %u owner: %u) not found in table `item_instance`, can't load. ",guid,GUID_LOPART(owner_guid)); + sLog.outError("Item (GUID: %u owner: %u) not found in table `item_instance`, can't load. ",guid,GUID_LOPART(owner_guid)); return false; } @@ -354,7 +358,7 @@ bool Item::LoadFromDB(uint32 guid, uint64 owner_guid, QueryResult *result) if(!LoadValues(fields[0].GetString())) { - sLog.outError("ERROR: Item #%d have broken data in `data` field. Can't be loaded.",guid); + sLog.outError("Item #%d have broken data in `data` field. Can't be loaded.",guid); if (delete_result) delete result; return false; } @@ -450,7 +454,7 @@ uint32 Item::GetSkill() const static uint32 item_armor_skills[MAX_ITEM_SUBCLASS_ARMOR] = { - 0,SKILL_CLOTH,SKILL_LEATHER,SKILL_MAIL,SKILL_PLATE_MAIL,0,SKILL_SHIELD,0,0,0 + 0,SKILL_CLOTH,SKILL_LEATHER,SKILL_MAIL,SKILL_PLATE_MAIL,0,SKILL_SHIELD,0,0,0,0 }; ItemPrototype const* proto = GetProto(); @@ -752,7 +756,12 @@ bool Item::IsFitToSpellRequirements(SpellEntry const* spellInfo) const if(spellInfo->EquippedItemInventoryTypeMask != 0) // 0 == any inventory type { - if((spellInfo->EquippedItemInventoryTypeMask & (1 << proto->InventoryType)) == 0) + // Special case - accept weapon type for main and offhand requirements + if(proto->InventoryType == INVTYPE_WEAPON && + (spellInfo->EquippedItemInventoryTypeMask & (1 << INVTYPE_WEAPONMAINHAND) || + spellInfo->EquippedItemInventoryTypeMask & (1 << INVTYPE_WEAPONOFFHAND))) + return true; + else if ((spellInfo->EquippedItemInventoryTypeMask & (1 << proto->InventoryType)) == 0) return false; // inventory type not present in mask } @@ -765,9 +774,9 @@ void Item::SetEnchantment(EnchantmentSlot slot, uint32 id, uint32 duration, uint if((GetEnchantmentId(slot) == id) && (GetEnchantmentDuration(slot) == duration) && (GetEnchantmentCharges(slot) == charges)) return; - SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_ID_OFFSET,id); - SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET,duration); - SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_CHARGES_OFFSET,charges); + SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_ID_OFFSET,id); + SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET,duration); + SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_CHARGES_OFFSET,charges); SetState(ITEM_CHANGED); } @@ -776,7 +785,7 @@ void Item::SetEnchantmentDuration(EnchantmentSlot slot, uint32 duration) if(GetEnchantmentDuration(slot) == duration) return; - SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET,duration); + SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET,duration); SetState(ITEM_CHANGED); } @@ -785,7 +794,7 @@ void Item::SetEnchantmentCharges(EnchantmentSlot slot, uint32 charges) if(GetEnchantmentCharges(slot) == charges) return; - SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_CHARGES_OFFSET,charges); + SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_CHARGES_OFFSET,charges); SetState(ITEM_CHANGED); } @@ -795,14 +804,14 @@ void Item::ClearEnchantment(EnchantmentSlot slot) return; for(uint8 x = 0; x < 3; ++x) - SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + x, 0); + SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + slot*MAX_ENCHANTMENT_OFFSET + x, 0); SetState(ITEM_CHANGED); } bool Item::GemsFitSockets() const { bool fits = true; - for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot) + for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+MAX_GEM_SOCKETS; ++enchant_slot) { uint8 SocketColor = GetProto()->Socket[enchant_slot-SOCK_ENCHANTMENT_SLOT].Color; @@ -842,7 +851,7 @@ bool Item::GemsFitSockets() const uint8 Item::GetGemCountWithID(uint32 GemID) const { uint8 count = 0; - for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot) + for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+MAX_GEM_SOCKETS; ++enchant_slot) { uint32 enchant_id = GetEnchantmentId(EnchantmentSlot(enchant_slot)); if(!enchant_id) @@ -858,6 +867,29 @@ uint8 Item::GetGemCountWithID(uint32 GemID) const return count; } +uint8 Item::GetGemCountWithLimitCategory(uint32 limitCategory) const +{ + uint8 count = 0; + for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+MAX_GEM_SOCKETS; ++enchant_slot) + { + uint32 enchant_id = GetEnchantmentId(EnchantmentSlot(enchant_slot)); + if(!enchant_id) + continue; + + SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id); + if(!enchantEntry) + continue; + + ItemPrototype const* gemProto = ObjectMgr::GetItemPrototype(enchantEntry->GemID); + if(!gemProto) + continue; + + if(gemProto->ItemLimitCategory==limitCategory) + ++count; + } + return count; +} + bool Item::IsLimitedToAnotherMapOrZone( uint32 cur_mapId, uint32 cur_zoneId) const { ItemPrototype const* proto = GetProto(); @@ -886,8 +918,8 @@ Item* Item::CreateItem( uint32 item, uint32 count, Player const* player ) ItemPrototype const *pProto = objmgr.GetItemPrototype( item ); if( pProto ) { - if ( count > pProto->Stackable ) - count = pProto->Stackable; + if ( count > pProto->GetMaxStackSize()) + count = pProto->GetMaxStackSize(); assert(count !=0 && "pProto->Stackable==0 but checked at loading already"); diff --git a/src/game/Item.h b/src/game/Item.h index 3417f2fcc6d..824375cea8d 100644 --- a/src/game/Item.h +++ b/src/game/Item.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -148,29 +148,32 @@ enum SellFailure // -1 from client enchantment slot number enum EnchantmentSlot { - PERM_ENCHANTMENT_SLOT = 0, - TEMP_ENCHANTMENT_SLOT = 1, - SOCK_ENCHANTMENT_SLOT = 2, - SOCK_ENCHANTMENT_SLOT_2 = 3, - SOCK_ENCHANTMENT_SLOT_3 = 4, - BONUS_ENCHANTMENT_SLOT = 5, - MAX_INSPECTED_ENCHANTMENT_SLOT = 6, - - PROP_ENCHANTMENT_SLOT_0 = 6, // used with RandomSuffix - PROP_ENCHANTMENT_SLOT_1 = 7, // used with RandomSuffix - PROP_ENCHANTMENT_SLOT_2 = 8, // used with RandomSuffix and RandomProperty - PROP_ENCHANTMENT_SLOT_3 = 9, // used with RandomProperty - PROP_ENCHANTMENT_SLOT_4 = 10, // used with RandomProperty - MAX_ENCHANTMENT_SLOT = 11 + PERM_ENCHANTMENT_SLOT = 0, + TEMP_ENCHANTMENT_SLOT = 1, + SOCK_ENCHANTMENT_SLOT = 2, + SOCK_ENCHANTMENT_SLOT_2 = 3, + SOCK_ENCHANTMENT_SLOT_3 = 4, + BONUS_ENCHANTMENT_SLOT = 5, + PRISMATIC_ENCHANTMENT_SLOT = 6, // added at apply special permanent enchantment + MAX_INSPECTED_ENCHANTMENT_SLOT = 7, + + PROP_ENCHANTMENT_SLOT_0 = 7, // used with RandomSuffix + PROP_ENCHANTMENT_SLOT_1 = 8, // used with RandomSuffix + PROP_ENCHANTMENT_SLOT_2 = 9, // used with RandomSuffix and RandomProperty + PROP_ENCHANTMENT_SLOT_3 = 10, // used with RandomProperty + PROP_ENCHANTMENT_SLOT_4 = 11, // used with RandomProperty + MAX_ENCHANTMENT_SLOT = 12 }; -#define MAX_VISIBLE_ITEM_OFFSET 16 // 16 fields per visible item (creator(2) + enchantments(12) + properties(1) + pad(1)) +#define MAX_VISIBLE_ITEM_OFFSET 18 // 18 fields per visible item (creator(2) + enchantments(13) + properties(1) + seed(1) + pad(1)) + +#define MAX_GEM_SOCKETS MAX_ITEM_PROTO_SOCKETS// (BONUS_ENCHANTMENT_SLOT-SOCK_ENCHANTMENT_SLOT) and item proto size, equal value expected enum EnchantmentOffset { ENCHANTMENT_ID_OFFSET = 0, ENCHANTMENT_DURATION_OFFSET = 1, - ENCHANTMENT_CHARGES_OFFSET = 2 + ENCHANTMENT_CHARGES_OFFSET = 2 // now here not only charges, but something new in wotlk }; #define MAX_ENCHANTMENT_OFFSET 3 @@ -211,6 +214,7 @@ class TRINITY_DLL_SPEC Item : public Object void SetBinding(bool val) { ApplyModFlag(ITEM_FIELD_FLAGS,ITEM_FLAGS_BINDED,val); } bool IsSoulBound() const { return HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_BINDED); } + bool IsAccountBound() const { return HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_BOA); } bool IsBindedNotWith(uint64 guid) const { return IsSoulBound() && GetOwnerGUID()!= guid; } bool IsBoundByEnchant() const; virtual void SaveToDB(); @@ -230,8 +234,9 @@ class TRINITY_DLL_SPEC Item : public Object uint32 GetCount() const { return GetUInt32Value (ITEM_FIELD_STACK_COUNT); } void SetCount(uint32 value) { SetUInt32Value (ITEM_FIELD_STACK_COUNT, value); } - uint32 GetMaxStackCount() const { return GetProto()->Stackable; } + uint32 GetMaxStackCount() const { return GetProto()->GetMaxStackSize(); } uint8 GetGemCountWithID(uint32 GemID) const; + uint8 GetGemCountWithLimitCategory(uint32 limitCategory) const; uint8 GetSlot() const {return m_slot;} Bag *GetContainer() { return m_container; } @@ -256,9 +261,9 @@ class TRINITY_DLL_SPEC Item : public Object void SetEnchantmentDuration(EnchantmentSlot slot, uint32 duration); void SetEnchantmentCharges(EnchantmentSlot slot, uint32 charges); void ClearEnchantment(EnchantmentSlot slot); - uint32 GetEnchantmentId(EnchantmentSlot slot) const { return GetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_ID_OFFSET);} - uint32 GetEnchantmentDuration(EnchantmentSlot slot) const { return GetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET);} - uint32 GetEnchantmentCharges(EnchantmentSlot slot) const { return GetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_CHARGES_OFFSET);} + uint32 GetEnchantmentId(EnchantmentSlot slot) const { return GetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_ID_OFFSET);} + uint32 GetEnchantmentDuration(EnchantmentSlot slot) const { return GetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET);} + uint32 GetEnchantmentCharges(EnchantmentSlot slot) const { return GetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_CHARGES_OFFSET);} void SendTimeUpdate(Player* owner); void UpdateDuration(Player* owner, uint32 diff); @@ -282,13 +287,10 @@ class TRINITY_DLL_SPEC Item : public Object uState = state; } - bool hasQuest(uint32 quest_id) const - { - ItemPrototype const *itemProto = GetProto(); - return itemProto && itemProto->StartQuest == quest_id; - } + bool hasQuest(uint32 quest_id) const { return GetProto()->StartQuest == quest_id; } bool hasInvolvedQuest(uint32 /*quest_id*/) const { return false; } - + bool IsPotion() const { return GetProto()->IsPotion(); } + bool IsConjuredConsumable() const { return GetProto()->IsConjuredConsumable(); } private: uint8 m_slot; Bag *m_container; diff --git a/src/game/ItemEnchantmentMgr.cpp b/src/game/ItemEnchantmentMgr.cpp index 3326fb56e96..e4b2d809582 100644 --- a/src/game/ItemEnchantmentMgr.cpp +++ b/src/game/ItemEnchantmentMgr.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/ItemEnchantmentMgr.h b/src/game/ItemEnchantmentMgr.h index 17cf634860e..ce627515aee 100644 --- a/src/game/ItemEnchantmentMgr.h +++ b/src/game/ItemEnchantmentMgr.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/ItemHandler.cpp b/src/game/ItemHandler.cpp index 0e46115de08..aef5ea829c6 100644 --- a/src/game/ItemHandler.cpp +++ b/src/game/ItemHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,7 +21,6 @@ #include "Common.h" #include "WorldPacket.h" #include "WorldSession.h" -#include "World.h" #include "Opcodes.h" #include "Log.h" #include "ObjectMgr.h" @@ -324,7 +323,7 @@ void WorldSession::HandleItemQuerySingleOpcode( WorldPacket & recv_data ) data << pProto->ItemId; data << pProto->Class; data << pProto->SubClass; - data << uint32(-1); // new 2.0.3, not exist in wdb cache? + data << int32(pProto->Unk0); // new 2.0.3, not exist in wdb cache? data << Name; data << uint8(0x00); //pProto->Name2; // blizz not send name there, just uint8(0x00); <-- \0 = empty string = empty name... data << uint8(0x00); //pProto->Name3; // blizz not send name there, just uint8(0x00); @@ -346,15 +345,18 @@ void WorldSession::HandleItemQuerySingleOpcode( WorldPacket & recv_data ) data << pProto->RequiredCityRank; data << pProto->RequiredReputationFaction; data << pProto->RequiredReputationRank; - data << pProto->MaxCount; - data << pProto->Stackable; + data << int32(pProto->MaxCount); + data << int32(pProto->Stackable); data << pProto->ContainerSlots; - for(int i = 0; i < 10; i++) + data << pProto->StatsCount; // item stats count + for(uint32 i = 0; i < pProto->StatsCount; ++i) { data << pProto->ItemStat[i].ItemStatType; data << pProto->ItemStat[i].ItemStatValue; } - for(int i = 0; i < 5; i++) + data << pProto->ScalingStatDistribution; // scaling stats distribution + data << pProto->ScalingStatValue; // some kind of flags used to determine stat values column + for(int i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i) { data << pProto->Damage[i].DamageMin; data << pProto->Damage[i].DamageMax; @@ -374,7 +376,7 @@ void WorldSession::HandleItemQuerySingleOpcode( WorldPacket & recv_data ) data << pProto->AmmoType; data << pProto->RangedModRange; - for(int s = 0; s < 5; s++) + for(int s = 0; s < MAX_ITEM_PROTO_SPELLS; ++s) { // send DBC data for cooldowns in same way as it used in Spell::SendSpellCooldown // use `item_template` or if not set then only use spell cooldowns @@ -417,7 +419,7 @@ void WorldSession::HandleItemQuerySingleOpcode( WorldPacket & recv_data ) data << pProto->PageMaterial; data << pProto->StartQuest; data << pProto->LockID; - data << pProto->Material; + data << int32(pProto->Material); data << pProto->Sheath; data << pProto->RandomProperty; data << pProto->RandomSuffix; @@ -428,7 +430,7 @@ void WorldSession::HandleItemQuerySingleOpcode( WorldPacket & recv_data ) data << pProto->Map; // Added in 1.12.x & 2.0.1 client branch data << pProto->BagFamily; data << pProto->TotemCategory; - for(int s = 0; s < 3; s++) + for(int s = 0; s < MAX_ITEM_PROTO_SOCKETS; ++s) { data << pProto->Socket[s].Color; data << pProto->Socket[s].Content; @@ -437,7 +439,8 @@ void WorldSession::HandleItemQuerySingleOpcode( WorldPacket & recv_data ) data << pProto->GemProperties; data << pProto->RequiredDisenchantSkill; data << pProto->ArmorDamageModifier; - data << uint32(0); // added in 2.4.2.8209, duration (seconds) + data << pProto->Duration; // added in 2.4.2.8209, duration (seconds) + data << pProto->ItemLimitCategory; // WotLK, ItemLimitCategory SendPacket( &data ); } else @@ -525,7 +528,7 @@ void WorldSession::HandleSellItemOpcode( WorldPacket & recv_data ) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); Item *pItem = _player->GetItemByGuid( itemguid ); if( pItem ) @@ -630,7 +633,7 @@ void WorldSession::HandleBuybackItem(WorldPacket & recv_data) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); Item *pItem = _player->GetItemFromBuyBackSlot( slot ); if( pItem ) @@ -717,7 +720,7 @@ void WorldSession::SendListInventory( uint64 vendorguid ) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); // Stop the npc if moving pCreature->StopMoving(); @@ -738,7 +741,7 @@ void WorldSession::SendListInventory( uint64 vendorguid ) float discountMod = _player->GetReputationPriceDiscount(pCreature); - for(int i = 0; i < numitems; i++ ) + for(int i = 0; i < numitems; ++i ) { if(VendorItem const* crItem = vItems->GetItem(i)) { @@ -824,10 +827,23 @@ void WorldSession::HandleAutoStoreBagItemOpcode( WorldPacket & recv_data ) _player->StoreItem( dest, pItem, true ); } -void WorldSession::HandleBuyBankSlotOpcode(WorldPacket& /*recvPacket*/) +void WorldSession::HandleBuyBankSlotOpcode(WorldPacket& recvPacket) { + CHECK_PACKET_SIZE(recvPacket, 8); + sLog.outDebug("WORLD: CMSG_BUY_BANK_SLOT"); + uint64 guid; + recvPacket >> guid; + + // cheating protection + Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid, UNIT_NPC_FLAG_BANKER); + if(!pCreature) + { + sLog.outDebug( "WORLD: HandleBuyBankSlotOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)) ); + return; + } + uint32 slot = _player->GetByteValue(PLAYER_BYTES_2, 2); // next slot @@ -845,6 +861,7 @@ void WorldSession::HandleBuyBankSlotOpcode(WorldPacket& /*recvPacket*/) if (_player->GetMoney() < price) return; + _player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT, slot); _player->SetByteValue(PLAYER_BYTES_2, 2, slot); _player->ModifyMoney(-int32(price)); } @@ -1106,120 +1123,193 @@ void WorldSession::HandleSocketOpcode(WorldPacket& recv_data) { sLog.outDebug("WORLD: CMSG_SOCKET_GEMS"); - CHECK_PACKET_SIZE(recv_data,8*4); - - uint64 guids[4]; - uint32 GemEnchants[3], OldEnchants[3]; - Item *Gems[3]; - bool SocketBonusActivated, SocketBonusToBeActivated; + CHECK_PACKET_SIZE(recv_data,8+8*MAX_GEM_SOCKETS); - for(int i = 0; i < 4; i++) - recv_data >> guids[i]; + uint64 item_guid; + uint64 gem_guids[MAX_GEM_SOCKETS]; - if(!guids[0]) + recv_data >> item_guid; + if(!item_guid) return; + for(int i = 0; i < MAX_GEM_SOCKETS; ++i) + recv_data >> gem_guids[i]; + //cheat -> tried to socket same gem multiple times - if((guids[1] && (guids[1] == guids[2] || guids[1] == guids[3])) || (guids[2] && (guids[2] == guids[3]))) + if ((gem_guids[0] && (gem_guids[0] == gem_guids[1] || gem_guids[0] == gem_guids[2])) || + (gem_guids[1] && (gem_guids[1] == gem_guids[2]))) return; - Item *itemTarget = _player->GetItemByGuid(guids[0]); + Item *itemTarget = _player->GetItemByGuid(item_guid); if(!itemTarget) //missing item to socket return; + ItemPrototype const* itemProto = itemTarget->GetProto(); + if(!itemProto) + return; + //this slot is excepted when applying / removing meta gem bonus - uint8 slot = itemTarget->IsEquipped() ? itemTarget->GetSlot() : NULL_SLOT; + uint8 slot = itemTarget->IsEquipped() ? itemTarget->GetSlot() : uint8(NULL_SLOT); - for(int i = 0; i < 3; i++) - Gems[i] = guids[i + 1] ? _player->GetItemByGuid(guids[i + 1]) : NULL; + Item *Gems[MAX_GEM_SOCKETS]; + for(int i = 0; i < MAX_GEM_SOCKETS; ++i) + Gems[i] = gem_guids[i] ? _player->GetItemByGuid(gem_guids[i]) : NULL; - GemPropertiesEntry const *GemProps[3]; - for(int i = 0; i < 3; ++i) //get geminfo from dbc storage - { + GemPropertiesEntry const *GemProps[MAX_GEM_SOCKETS]; + for(int i = 0; i < MAX_GEM_SOCKETS; ++i) //get geminfo from dbc storage GemProps[i] = (Gems[i]) ? sGemPropertiesStore.LookupEntry(Gems[i]->GetProto()->GemProperties) : NULL; - } - for(int i = 0; i < 3; ++i) //check for hack maybe + for(int i = 0; i < MAX_GEM_SOCKETS; ++i) //check for hack maybe { - // tried to put gem in socket where no socket exists / tried to put normal gem in meta socket + if (!GemProps[i]) + continue; + + // tried to put gem in socket where no socket exists (take care about prismatic sockets) + if (!itemProto->Socket[i].Color) + { + // no prismatic socket + if(!itemTarget->GetEnchantmentId(PRISMATIC_ENCHANTMENT_SLOT)) + return; + + // not first not-colored (not normaly used) socket + if(i!=0 && !itemProto->Socket[i-1].Color && (i+1 >= MAX_GEM_SOCKETS || itemProto->Socket[i+1].Color)) + return; + + // ok, this is first not colored socket for item with prismatic socket + } + + // tried to put normal gem in meta socket + if (itemProto->Socket[i].Color == SOCKET_COLOR_META && GemProps[i]->color != SOCKET_COLOR_META) + return; + // tried to put meta gem in normal socket - if( GemProps[i] && ( !itemTarget->GetProto()->Socket[i].Color || - itemTarget->GetProto()->Socket[i].Color == SOCKET_COLOR_META && GemProps[i]->color != SOCKET_COLOR_META || - itemTarget->GetProto()->Socket[i].Color != SOCKET_COLOR_META && GemProps[i]->color == SOCKET_COLOR_META ) ) + if (itemProto->Socket[i].Color != SOCKET_COLOR_META && GemProps[i]->color == SOCKET_COLOR_META) return; } - for(int i = 0; i < 3; ++i) //get new and old enchantments + uint32 GemEnchants[MAX_GEM_SOCKETS]; + uint32 OldEnchants[MAX_GEM_SOCKETS]; + for(int i = 0; i < MAX_GEM_SOCKETS; ++i) //get new and old enchantments { GemEnchants[i] = (GemProps[i]) ? GemProps[i]->spellitemenchantement : 0; OldEnchants[i] = itemTarget->GetEnchantmentId(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT+i)); } // check unique-equipped conditions - for(int i = 0; i < 3; ++i) + for(int i = 0; i < MAX_GEM_SOCKETS; ++i) { - if (Gems[i] && (Gems[i]->GetProto()->Flags & ITEM_FLAGS_UNIQUE_EQUIPPED)) + if(!Gems[i]) + continue; + + // continue check for case when attempt add 2 similar unique equipped gems in one item. + ItemPrototype const* iGemProto = Gems[i]->GetProto(); + + // unique item (for new and already placed bit removed enchantments + if (iGemProto->Flags & ITEM_FLAGS_UNIQUE_EQUIPPED) { - // for equipped item check all equipment for duplicate equipped gems - if(itemTarget->IsEquipped()) + for (int j = 0; j < MAX_GEM_SOCKETS; ++j) { - if(GetPlayer()->GetItemOrItemWithGemEquipped(Gems[i]->GetEntry())) + if(i==j) // skip self + continue; + + if (Gems[j]) { - _player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE, itemTarget, NULL ); - return; + if (iGemProto->ItemId == Gems[j]->GetEntry()) + { + _player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL ); + return; + } + } + else if(OldEnchants[j]) + { + if(SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(OldEnchants[j])) + { + if (iGemProto->ItemId == enchantEntry->GemID) + { + _player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL ); + return; + } + } } + } + } - // continue check for case when attempt add 2 similar unique equipped gems in one item. - for (int j = 0; j < 3; ++j) + // unique limit type item + int32 limit_newcount = 0; + if (iGemProto->ItemLimitCategory) + { + if(ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(iGemProto->ItemLimitCategory)) { - if ((i != j) && (Gems[j]) && (Gems[i]->GetProto()->ItemId == Gems[j]->GetProto()->ItemId)) + for (int j = 0; j < MAX_GEM_SOCKETS; ++j) + { + if (Gems[j]) + { + // destroyed gem + if (OldEnchants[j]) + { + if(SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(OldEnchants[j])) + if(ItemPrototype const* jProto = ObjectMgr::GetItemPrototype(enchantEntry->GemID)) + if (iGemProto->ItemLimitCategory == jProto->ItemLimitCategory) + --limit_newcount; + } + + // new gem + if (iGemProto->ItemLimitCategory == Gems[j]->GetProto()->ItemLimitCategory) + ++limit_newcount; + } + // existed gem + else if(OldEnchants[j]) + { + if(SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(OldEnchants[j])) + if(ItemPrototype const* jProto = ObjectMgr::GetItemPrototype(enchantEntry->GemID)) + if (iGemProto->ItemLimitCategory == jProto->ItemLimitCategory) + ++limit_newcount; + } + } + + if(limit_newcount > 0 && uint32(limit_newcount) > limitEntry->maxCount) { _player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL ); return; } } - for (int j = 0; j < 3; ++j) - { - if (OldEnchants[j]) - { - SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(OldEnchants[j]); - if(!enchantEntry) - continue; + } - if ((enchantEntry->GemID == Gems[i]->GetProto()->ItemId) && (i != j)) - { - _player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL ); - return; - } - } + // for equipped item check all equipment for duplicate equipped gems + if(itemTarget->IsEquipped()) + { + if(uint8 res = _player->CanEquipUniqueItem(Gems[i],slot,limit_newcount >= 0 ? limit_newcount : 0)) + { + _player->SendEquipError( res, itemTarget, NULL ); + return; } } } - SocketBonusActivated = itemTarget->GemsFitSockets(); //save state of socketbonus + bool SocketBonusActivated = itemTarget->GemsFitSockets(); //save state of socketbonus _player->ToggleMetaGemsActive(slot, false); //turn off all metagems (except for the target item) //if a meta gem is being equipped, all information has to be written to the item before testing if the conditions for the gem are met //remove ALL enchants - for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot) + for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+MAX_GEM_SOCKETS; ++enchant_slot) _player->ApplyEnchantment(itemTarget,EnchantmentSlot(enchant_slot),false); - for(int i = 0; i < 3; ++i) + for(int i = 0; i < MAX_GEM_SOCKETS; ++i) { if(GemEnchants[i]) { itemTarget->SetEnchantment(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT+i), GemEnchants[i],0,0); - if(Item* guidItem = _player->GetItemByGuid(guids[i + 1])) + if(Item* guidItem = _player->GetItemByGuid(gem_guids[i])) _player->DestroyItem(guidItem->GetBagSlot(), guidItem->GetSlot(), true ); } } - for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot) + for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+MAX_GEM_SOCKETS; ++enchant_slot) _player->ApplyEnchantment(itemTarget,EnchantmentSlot(enchant_slot),true); - SocketBonusToBeActivated = itemTarget->GemsFitSockets();//current socketbonus state + bool SocketBonusToBeActivated = itemTarget->GemsFitSockets();//current socketbonus state if(SocketBonusActivated ^ SocketBonusToBeActivated) //if there was a change... { _player->ApplyEnchantment(itemTarget,BONUS_ENCHANTMENT_SLOT,false); diff --git a/src/game/ItemPrototype.h b/src/game/ItemPrototype.h index 15b01faf5a2..3967b37622b 100644 --- a/src/game/ItemPrototype.h +++ b/src/game/ItemPrototype.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -57,10 +57,18 @@ enum ItemModType ITEM_MOD_CRIT_TAKEN_RATING = 34, ITEM_MOD_RESILIENCE_RATING = 35, ITEM_MOD_HASTE_RATING = 36, - ITEM_MOD_EXPERTISE_RATING = 37 + ITEM_MOD_EXPERTISE_RATING = 37, + ITEM_MOD_ATTACK_POWER = 38, + ITEM_MOD_RANGED_ATTACK_POWER = 39, + ITEM_MOD_FERAL_ATTACK_POWER = 40, + ITEM_MOD_SPELL_HEALING_DONE = 41, + ITEM_MOD_SPELL_DAMAGE_DONE = 42, + ITEM_MOD_MANA_REGENERATION = 43, + ITEM_MOD_ARMOR_PENETRATION_RATING = 44, + ITEM_MOD_SPELL_POWER = 45 }; -#define MAX_ITEM_MOD 38 +#define MAX_ITEM_MOD 46 enum ItemSpelltriggerType { @@ -68,6 +76,12 @@ enum ItemSpelltriggerType ITEM_SPELLTRIGGER_ON_EQUIP = 1, ITEM_SPELLTRIGGER_CHANCE_ON_HIT = 2, ITEM_SPELLTRIGGER_SOULSTONE = 4, + /* + * ItemSpelltriggerType 5 might have changed on 2.4.3/3.0.3: Such auras + * will be applied on item pickup and removed on item loss - maybe on the + * other hand the item is destroyed if the aura is removed ("removed on + * death" of spell 57348 makes me think so) + */ ITEM_SPELLTRIGGER_ON_NO_DELAY_USE = 5, // no equip cooldown ITEM_SPELLTRIGGER_LEARN_SPELL_ID = 6 // used in item_template.spell_2 with spell_id with SPELL_GENERIC_LEARN in spell_1 }; @@ -93,9 +107,11 @@ enum ITEM_FLAGS ITEM_FLAGS_CONJURED = 0x00000002, ITEM_FLAGS_OPENABLE = 0x00000004, ITEM_FLAGS_WRAPPED = 0x00000008, + ITEM_FLAGS_BROKEN = 0x00000010, // appears red icon (like when item durability==0) ITEM_FLAGS_WRAPPER = 0x00000200, // used or not used wrapper ITEM_FLAGS_PARTY_LOOT = 0x00000800, // determines if item is party loot or not ITEM_FLAGS_CHARTER = 0x00002000, // arena/guild charter + ITEM_FLAGS_PROSPECTABLE = 0x00040000, ITEM_FLAGS_UNIQUE_EQUIPPED = 0x00080000, ITEM_FLAGS_USEABLE_IN_ARENA = 0x00200000, ITEM_FLAGS_THROWABLE = 0x00400000, // not used in game for check trow possibility, only for item in game tooltip @@ -185,10 +201,11 @@ enum ItemClass ITEM_CLASS_QUEST = 12, ITEM_CLASS_KEY = 13, ITEM_CLASS_PERMANENT = 14, - ITEM_CLASS_JUNK = 15 + ITEM_CLASS_MISC = 15, + ITEM_CLASS_GLYPH = 16 }; -#define MAX_ITEM_CLASS 16 +#define MAX_ITEM_CLASS 17 enum ItemSubclassConsumable { @@ -214,10 +231,11 @@ enum ItemSubclassContainer ITEM_SUBCLASS_ENGINEERING_CONTAINER = 4, ITEM_SUBCLASS_GEM_CONTAINER = 5, ITEM_SUBCLASS_MINING_CONTAINER = 6, - ITEM_SUBCLASS_LEATHERWORKING_CONTAINER = 7 + ITEM_SUBCLASS_LEATHERWORKING_CONTAINER = 7, + ITEM_SUBCLASS_INSCRIPTION_CONTAINER = 8 }; -#define MAX_ITEM_SUBCLASS_CONTAINER 8 +#define MAX_ITEM_SUBCLASS_CONTAINER 9 enum ItemSubclassWeapon { @@ -272,10 +290,11 @@ enum ItemSubclassArmor ITEM_SUBCLASS_ARMOR_SHIELD = 6, ITEM_SUBCLASS_ARMOR_LIBRAM = 7, ITEM_SUBCLASS_ARMOR_IDOL = 8, - ITEM_SUBCLASS_ARMOR_TOTEM = 9 + ITEM_SUBCLASS_ARMOR_TOTEM = 9, + ITEM_SUBCLASS_ARMOR_SIGIL = 10 }; -#define MAX_ITEM_SUBCLASS_ARMOR 10 +#define MAX_ITEM_SUBCLASS_ARMOR 11 enum ItemSubclassReagent { @@ -310,10 +329,12 @@ enum ItemSubclassTradeGoods ITEM_SUBCLASS_ELEMENTAL = 10, ITEM_SUBCLASS_TRADE_GOODS_OTHER = 11, ITEM_SUBCLASS_ENCHANTING = 12, - ITEM_SUBCLASS_MATERIAL = 13 // Added in 2.4.2 + ITEM_SUBCLASS_MATERIAL = 13, + ITEM_SUBCLASS_ARMOR_ENCHANTMENT = 14, + ITEM_SUBCLASS_WEAPON_ENCHANTMENT = 15 }; -#define MAX_ITEM_SUBCLASS_TRADE_GOODS 14 +#define MAX_ITEM_SUBCLASS_TRADE_GOODS 16 enum ItemSubclassGeneric { @@ -423,7 +444,8 @@ const uint32 MaxItemSubclassValues[MAX_ITEM_CLASS] = MAX_ITEM_SUBCLASS_QUEST, MAX_ITEM_SUBCLASS_KEY, MAX_ITEM_SUBCLASS_PERMANENT, - MAX_ITEM_SUBCLASS_JUNK + MAX_ITEM_SUBCLASS_JUNK, + MAX_ITEM_SUBCLASS_GLYPH }; inline uint8 ItemSubClassToDurabilityMultiplierId(uint32 ItemClass, uint32 ItemSubClass) @@ -472,12 +494,17 @@ struct _Socket uint32 Content; }; +#define MAX_ITEM_PROTO_DAMAGES 5 +#define MAX_ITEM_PROTO_SOCKETS 3 +#define MAX_ITEM_PROTO_SPELLS 5 +#define MAX_ITEM_PROTO_STATS 10 + struct ItemPrototype { uint32 ItemId; uint32 Class; // id from ItemClass.dbc uint32 SubClass; // id from ItemSubClass.dbc - uint32 Unk0; + int32 Unk0; char* Name1; uint32 DisplayInfoID; // id from ItemDisplayInfo.dbc uint32 Quality; @@ -497,11 +524,14 @@ struct ItemPrototype uint32 RequiredCityRank; uint32 RequiredReputationFaction; // id from Faction.dbc uint32 RequiredReputationRank; - uint32 MaxCount; - uint32 Stackable; + int32 MaxCount; // <=0: no limit + int32 Stackable; // 0: not allowed, -1: put in player coin info tab and don't limit stacking (so 1 slot) uint32 ContainerSlots; - _ItemStat ItemStat[10]; - _Damage Damage[5]; + uint32 StatsCount; + _ItemStat ItemStat[MAX_ITEM_PROTO_STATS]; + uint32 ScalingStatDistribution; // id from ScalingStatDistribution.dbc + uint32 ScalingStatValue; // mask for selecting column in ScalingStatValues.dbc + _Damage Damage[MAX_ITEM_PROTO_DAMAGES]; uint32 Armor; uint32 HolyRes; uint32 FireRes; @@ -512,7 +542,7 @@ struct ItemPrototype uint32 Delay; uint32 AmmoType; float RangedModRange; - _Spell Spells[5]; + _Spell Spells[MAX_ITEM_PROTO_SPELLS]; uint32 Bonding; char* Description; uint32 PageText; @@ -520,7 +550,7 @@ struct ItemPrototype uint32 PageMaterial; uint32 StartQuest; // id from QuestCache.wdb uint32 LockID; - uint32 Material; // id from Material.dbc + int32 Material; // id from Material.dbc uint32 Sheath; uint32 RandomProperty; // id from ItemRandomProperties.dbc uint32 RandomSuffix; // id from ItemRandomSuffix.dbc @@ -529,19 +559,20 @@ struct ItemPrototype uint32 MaxDurability; uint32 Area; // id from AreaTable.dbc uint32 Map; // id from Map.dbc - uint32 BagFamily; // id from ItemBagFamily.dbc + uint32 BagFamily; // bit string (1 << id from ItemBagFamily.dbc) uint32 TotemCategory; // id from TotemCategory.dbc - _Socket Socket[3]; + _Socket Socket[MAX_ITEM_PROTO_SOCKETS]; uint32 socketBonus; // id from SpellItemEnchantment.dbc uint32 GemProperties; // id from GemProperties.dbc uint32 RequiredDisenchantSkill; float ArmorDamageModifier; + int32 Duration; // negative = realtime, positive = ingame time + uint32 ItemLimitCategory; // id from ItemLimitCategory.dbc uint32 ScriptId; uint32 DisenchantID; uint32 FoodType; uint32 MinMoneyLoot; uint32 MaxMoneyLoot; - int32 Duration; // negative = realtime, positive = ingame time // helpers bool CanChangeEquipStateInCombat() const @@ -563,6 +594,74 @@ struct ItemPrototype return false; } + + uint32 GetScalingStatValuesColumn() const + { + if(ScalingStatValue & 0x00000001) // stat mod + return 0; + if(ScalingStatValue & 0x00000002) // stat mod + return 1; + if(ScalingStatValue & 0x00000004) // stat mod + return 2; + if(ScalingStatValue & 0x00000008) // stat mod + return 3; + if(ScalingStatValue & 0x00000010) // stat mod + return 4; + if(ScalingStatValue & 0x00000020) // armor mod + return 5; + if(ScalingStatValue & 0x00000040) // armor mod + return 6; + if(ScalingStatValue & 0x00000080) // armor mod + return 7; + if(ScalingStatValue & 0x00000100) // armor mod + return 8; + if(ScalingStatValue & 0x00000200) // damage mod + return 9; + if(ScalingStatValue & 0x00000400) // damage mod + return 10; + if(ScalingStatValue & 0x00000800) // damage mod + return 11; + if(ScalingStatValue & 0x00001000) // damage mod + return 12; + if(ScalingStatValue & 0x00002000) // damage mod + return 13; + if(ScalingStatValue & 0x00004000) // damage mod + return 14; + if(ScalingStatValue & 0x00008000) // spell power + return 15; + if(ScalingStatValue & 0x00020000) // feral AP + return 16; + + return 0; + } + + uint32 GetMaxStackSize() const { return Stackable > 0 ? uint32(Stackable) : uint32(0x7FFFFFFF-1); } + + float getDPS() const + { + if (Delay == 0) + return 0; + float temp = 0; + for (int i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i) + temp+=Damage[i].DamageMin + Damage[i].DamageMax; + return temp*500/Delay; + } + + int32 getFeralBonus() const + { + // 0x02A5F3 - is mask for Melee weapon from ItemSubClassMask.dbc + if (Class == ITEM_CLASS_WEAPON && (1<<SubClass)&0x02A5F3) + { + int32 bonus = int32(getDPS()*14.0f) - 767; + if (bonus < 0) + return 0; + return bonus; + } + return 0; + } + + bool IsPotion() const { return Class==ITEM_CLASS_CONSUMABLE && SubClass==ITEM_SUBCLASS_POTION; } + bool IsConjuredConsumable() const { return Class == ITEM_CLASS_CONSUMABLE && (Flags & ITEM_FLAGS_CONJURED); } }; struct ItemLocale diff --git a/src/game/LFGHandler.cpp b/src/game/LFGHandler.cpp index 667b65327d5..85c196a9346 100644 --- a/src/game/LFGHandler.cpp +++ b/src/game/LFGHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/Language.h b/src/game/Language.h index f19ffe3fe15..28c0a81c879 100644 --- a/src/game/Language.h +++ b/src/game/Language.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -84,7 +84,8 @@ enum TrinityStrings LANG_MOTD_CURRENT = 56, LANG_USING_WORLD_DB = 57, LANG_USING_SCRIPT_LIB = 58, - // Room for more level 0 59-99 not used + LANG_USING_EVENT_AI = 59, + // Room for more level 0 60-99 not used // level 1 chat LANG_GLOBAL_NOTIFY = 100, @@ -170,7 +171,10 @@ enum TrinityStrings LANG_SOUND_NOT_EXIST = 170, LANG_TELEPORTED_TO_BY_CONSOLE = 171, LANG_CONSOLE_COMMAND = 172, - // Room for more level 1 173-199 not used + LANG_YOU_CHANGE_RUNIC_POWER = 173, + LANG_YOURS_RUNIC_POWER_CHANGED = 174, + LANG_LIQUID_STATUS = 175, + // Room for more level 1 176-199 not used // level 2 chat LANG_NO_SELECTION = 200, @@ -269,9 +273,9 @@ enum TrinityStrings LANG_COMMAND_WHISPERON = 285, LANG_COMMAND_WHISPEROFF = 286, LANG_COMMAND_CREATGUIDNOTFOUND = 287, - // TICKET STRINGS NEED REWRITE // 288-296 FREE + // TICKET STRINGS NEED REWRITE // 288-296 FREE - // END + // END LANG_COMMAND_SPAWNDIST = 297, LANG_COMMAND_SPAWNTIME = 298, LANG_COMMAND_MODIFY_HONOR = 299, @@ -322,6 +326,8 @@ enum TrinityStrings LANG_CREATURE_NOT_FOLLOW_YOU_NOW = 342, LANG_CREATURE_NON_TAMEABLE = 343, LANG_YOU_ALREADY_HAVE_PET = 344, + LANG_CUSTOMIZE_PLAYER = 345, + LANG_CUSTOMIZE_PLAYER_GUID = 346, // Room for more level 2 345-399 not used // level 3 chat @@ -529,9 +535,9 @@ enum TrinityStrings LANG_CHANGE_32BIT = 575, //log LANG_CHANGE_32BIT_FIELD = 576, - LANG_INVISIBLE_INVISIBLE = 577, - LANG_INVISIBLE_VISIBLE = 578, - LANG_SELECTED_TARGET_NOT_HAVE_VICTIM = 579, + LANG_INVISIBLE_INVISIBLE = 577, + LANG_INVISIBLE_VISIBLE = 578, + LANG_SELECTED_TARGET_NOT_HAVE_VICTIM = 579, LANG_COMMAND_LEARN_ALL_DEFAULT_AND_QUEST = 580, LANG_COMMAND_NEAROBJMESSAGE = 581, @@ -561,9 +567,11 @@ enum TrinityStrings // Battleground LANG_BG_A_WINS = 600, LANG_BG_H_WINS = 601, - LANG_BG_WS_ONE_MINUTE = 602, - LANG_BG_WS_HALF_MINUTE = 603, - LANG_BG_WS_BEGIN = 604, + + LANG_BG_WS_START_TWO_MINUTES = 753, + LANG_BG_WS_START_ONE_MINUTE = 602, + LANG_BG_WS_START_HALF_MINUTE = 603, + LANG_BG_WS_HAS_BEGUN = 604, LANG_BG_WS_CAPTURED_HF = 605, LANG_BG_WS_CAPTURED_AF = 606, @@ -577,9 +585,10 @@ enum TrinityStrings LANG_BG_WS_ALLIANCE_FLAG_RESPAWNED = 614, LANG_BG_WS_HORDE_FLAG_RESPAWNED = 615, - LANG_BG_EY_ONE_MINUTE = 636, - LANG_BG_EY_HALF_MINUTE = 637, - LANG_BG_EY_BEGIN = 638, + LANG_BG_EY_START_TWO_MINUTES = 755, + LANG_BG_EY_START_ONE_MINUTE = 636, + LANG_BG_EY_START_HALF_MINUTE = 637, + LANG_BG_EY_HAS_BEGUN = 638, LANG_BG_AB_ALLY = 650, LANG_BG_AB_HORDE = 651, @@ -592,9 +601,11 @@ enum TrinityStrings LANG_BG_AB_NODE_DEFENDED = 658, LANG_BG_AB_NODE_ASSAULTED = 659, LANG_BG_AB_NODE_CLAIMED = 660, - LANG_BG_AB_ONEMINTOSTART = 661, - LANG_BG_AB_HALFMINTOSTART = 662, - LANG_BG_AB_STARTED = 663, + + LANG_BG_AB_START_TWO_MINUTES = 754, + LANG_BG_AB_START_ONE_MINUTE = 661, + LANG_BG_AB_START_HALF_MINUTE = 662, + LANG_BG_AB_HAS_BEGUN = 663, LANG_BG_AB_A_NEAR_VICTORY = 664, LANG_BG_AB_H_NEAR_VICTORY = 665, LANG_BG_MARK_BY_MAIL = 666, @@ -625,7 +636,7 @@ enum TrinityStrings LANG_ARENA_ONE_MINUTE = 701, LANG_ARENA_THIRTY_SECONDS = 702, LANG_ARENA_FIFTEEN_SECONDS = 703, - LANG_ARENA_BEGUN = 704, + LANG_ARENA_HAS_BEGUN = 704, LANG_WAIT_BEFORE_SPEAKING = 705, LANG_NOT_EQUIPPED_ITEM = 706, @@ -636,76 +647,59 @@ enum TrinityStrings LANG_BG_QUEUE_ANNOUNCE_SELF = 711, LANG_BG_QUEUE_ANNOUNCE_WORLD = 712, - - LANG_YOUR_ARENA_LEVEL_REQ_ERROR = 713, -// LANG_HIS_ARENA_LEVEL_REQ_ERROR = 714, an opcode exists for this + // = 714, not used LANG_YOUR_BG_LEVEL_REQ_ERROR = 715, -// LANG_YOUR_ARENA_TEAM_FULL = 716, an opcode exists for this - - LANG_BG_AV_ALLY = 717, - LANG_BG_AV_HORDE = 718, - LANG_BG_AV_TOWER_TAKEN = 719, - LANG_BG_AV_TOWER_ASSAULTED = 720, - LANG_BG_AV_TOWER_DEFENDED = 721, - LANG_BG_AV_GRAVE_TAKEN = 722, - LANG_BG_AV_GRAVE_DEFENDED = 723, - LANG_BG_AV_GRAVE_ASSAULTED = 724, - - LANG_BG_AV_MINE_TAKEN = 725, - LANG_BG_AV_MINE_NORTH = 726, - LANG_BG_AV_MINE_SOUTH = 727, - - LANG_BG_AV_NODE_GRAVE_STORM_AID = 728, - LANG_BG_AV_NODE_TOWER_DUN_S = 729, - LANG_BG_AV_NODE_TOWER_DUN_N = 730, - LANG_BG_AV_NODE_GRAVE_STORMPIKE = 731, - LANG_BG_AV_NODE_TOWER_ICEWING = 732, - LANG_BG_AV_NODE_GRAVE_STONE = 733, - LANG_BG_AV_NODE_TOWER_STONE = 734, - LANG_BG_AV_NODE_GRAVE_SNOW = 735, - LANG_BG_AV_NODE_TOWER_ICE = 736, - LANG_BG_AV_NODE_GRAVE_ICE = 737, - LANG_BG_AV_NODE_TOWER_POINT = 738, - LANG_BG_AV_NODE_GRAVE_FROST = 739, - LANG_BG_AV_NODE_TOWER_FROST_E = 740, - LANG_BG_AV_NODE_TOWER_FROST_W = 741, - LANG_BG_AV_NODE_GRAVE_FROST_HUT = 742, - - LANG_BG_AV_ONEMINTOSTART = 743, - LANG_BG_AV_HALFMINTOSTART = 744, - LANG_BG_AV_STARTED = 745, - LANG_BG_AV_A_NEAR_LOSE = 746, - LANG_BG_AV_H_NEAR_LOSE = 747, - LANG_BG_AV_H_CAPTAIN_DEAD = 748, - LANG_BG_AV_A_CAPTAIN_DEAD = 749, + // LANG_YOUR_ARENA_TEAM_FULL = 716, an opcode exists for this + LANG_BG_STARTED_ANNOUNCE_WORLD = 717, + LANG_ARENA_QUEUE_ANNOUNCE_WORLD_JOIN= 718, + LANG_ARENA_QUEUE_ANNOUNCE_WORLD_EXIT= 719, + + LANG_BG_GROUP_TOO_LARGE = 720, // "Your group is too large for this battleground. Please regroup to join." + LANG_ARENA_GROUP_TOO_LARGE = 721, // "Your group is too large for this arena. Please regroup to join." + LANG_ARENA_YOUR_TEAM_ONLY = 722, // "Your group has members not in your arena team. Please regroup to join." + LANG_ARENA_NOT_ENOUGH_PLAYERS = 723, // "Your group does not have enough players to join this match." + LANG_ARENA_GOLD_WINS = 724, // "The Gold Team wins!" + LANG_ARENA_GREEN_WINS = 725, // "The Green Team wins!" +// = 726, not used + LANG_BG_GROUP_OFFLINE_MEMBER = 727, // "Your group has an offline member. Please remove him before joining." + LANG_BG_GROUP_MIXED_FACTION = 728, // "Your group has players from the opposing faction. You can't join the battleground as a group." + LANG_BG_GROUP_MIXED_LEVELS = 729, // "Your group has players from different battleground brakets. You can't join as group." + LANG_BG_GROUP_MEMBER_ALREADY_IN_QUEUE = 730, // "Someone in your party is already in this battleground queue. (S)he must leave it before joining as group." + LANG_BG_GROUP_MEMBER_DESERTER = 731, // "Someone in your party is Deserter. You can't join as group." + LANG_BG_GROUP_MEMBER_NO_FREE_QUEUE_SLOTS = 732, // "Someone in your party is already in three battleground queues. You cannot join as group." + + LANG_CANNOT_TELE_TO_BG = 733, // "You cannot teleport to a battleground or arena map." + LANG_CANNOT_SUMMON_TO_BG = 734, // "You cannot summon players to a battleground or arena map." + LANG_CANNOT_GO_TO_BG_GM = 735, // "You must be in GM mode to teleport to a player in a battleground." + LANG_CANNOT_GO_TO_BG_FROM_BG = 736, // "You cannot teleport to a battleground from another battleground. Please leave the current battleground first." + LANG_DEBUG_ARENA_ON = 737, + LANG_DEBUG_ARENA_OFF = 738, + LANG_DEBUG_BG_ON = 739, + LANG_DEBUG_BG_OFF = 740, + LANG_DIST_ARENA_POINTS_START = 741, + LANG_DIST_ARENA_POINTS_ONLINE_START = 742, + LANG_DIST_ARENA_POINTS_ONLINE_END = 743, + LANG_DIST_ARENA_POINTS_TEAM_START = 744, + LANG_DIST_ARENA_POINTS_TEAM_END = 745, + LANG_DIST_ARENA_POINTS_END = 746, +// = 747, not used +// = 748, not used +// = 749, not used + LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING = 750, // "Not enough players. This game will close in %u mins." + LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING_SECS = 751, // "Not enough players. This game will close in %u seconds." +// = 752, not used +// LANG_BG_WS_START_TWO_MINUTES = 753, - defined above +// LANG_BG_AB_START_TWO_MINUTES = 754, - defined above +// LANG_BG_EY_START_TWO_MINUTES = 755, - defined above + // Room for batleground/arena strings 756-799 not used // Room for BG/ARENA 750-769 not used LANG_ARENA_TESTING = 785, - LANG_AUTO_ANN = 786, LANG_ANNOUNCE_COLOR = 787, - LANG_BG_GROUP_TOO_LARGE = 1122, // "Your group is too large for this battleground. Please regroup to join." - LANG_ARENA_GROUP_TOO_LARGE = 1123, // "Your group is too large for this arena. Please regroup to join." - LANG_ARENA_YOUR_TEAM_ONLY = 1124, // "Your group has members not in your arena team. Please regroup to join." - LANG_ARENA_NOT_ENOUGH_PLAYERS = 1125, // "Your group does not have enough players to join this match." - LANG_ARENA_GOLD_WINS = 1126, // "The Gold Team wins!" - LANG_ARENA_GREEN_WINS = 1127, // "The Green Team wins!" - LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING = 1128, // The battleground will end soon, because there aren't enough players. Get more ppl or win already! - LANG_BG_GROUP_OFFLINE_MEMBER = 1129, // "Your group has an offline member. Please remove him before joining." - LANG_BG_GROUP_MIXED_FACTION = 1130, // "Your group has players from the opposing faction. You can't join the battleground as a group." - LANG_BG_GROUP_MIXED_LEVELS = 1131, // "Your group has players from different battleground brakets. You can't join as group." - LANG_BG_GROUP_MEMBER_ALREADY_IN_QUEUE = 1132, // "Someone in your party is already in this battleground queue. (S)he must leave it before joining as group." - LANG_BG_GROUP_MEMBER_DESERTER = 1133, // "Someone in your party is Deserter. You can't join as group." - LANG_BG_GROUP_MEMBER_NO_FREE_QUEUE_SLOTS = 1134, // "Someone in your party is already in three battleground queues. You cannot join as group." - - LANG_CANNOT_TELE_TO_BG = 1135, // "You cannot teleport to a battleground or arena map." - LANG_CANNOT_SUMMON_TO_BG = 1136, // "You cannot summon players to a battleground or arena map." - LANG_CANNOT_GO_TO_BG_GM = 1137, // "You must be in GM mode to teleport to a player in a battleground." - LANG_CANNOT_GO_TO_BG_FROM_BG = 1138, // "You cannot teleport to a battleground from another battleground. Please leave the current battleground first." - // in game strings LANG_PET_INVALID_NAME = 800, LANG_NOT_ENOUGH_GOLD = 801, @@ -717,7 +711,13 @@ enum TrinityStrings LANG_NEED_CHARACTER_NAME = 807, LANG_PLAYER_NOT_EXIST_OR_OFFLINE = 808, LANG_ACCOUNT_FOR_PLAYER_NOT_FOUND = 809, - // Room for in-game strings 810-999 not used + LANG_ACHIEVEMENT_EARNED = 810, + LANG_GUILD_MASTER = 811, + LANG_GUILD_OFFICER = 812, + LANG_GUILD_VETERAN = 813, + LANG_GUILD_MEMBER = 814, + LANG_GUILD_INITIATE = 815, + // Room for in-game strings 816-999 not used // Level 4 (CLI only commands) LANG_COMMAND_EXIT = 1000, @@ -732,7 +732,10 @@ enum TrinityStrings LANG_CHARACTER_DELETED = 1009, LANG_ACCOUNT_LIST_HEADER = 1010, LANG_ACCOUNT_LIST_ERROR = 1011, - // Room for more level 4 1012-1099 not used + LANG_ACCOUNT_LIST_BAR = 1012, + LANG_ACCOUNT_LIST_LINE = 1013, + LANG_ACCOUNT_LIST_EMPTY = 1014, + // Room for more level 4 1015-1099 not used // Level 3 (continue) LANG_ACCOUNT_SETADDON = 1100, @@ -757,25 +760,71 @@ enum TrinityStrings LANG_MUST_MALE_OR_FEMALE = 1119, LANG_YOU_CHANGE_GENDER = 1120, LANG_YOUR_GENDER_CHANGED = 1121, + LANG_SKILL_VALUES = 1122, + LANG_NO_PET_FOUND = 1123, + LANG_WRONG_PET_TYPE = 1124, + LANG_COMMAND_LEARN_PET_TALENTS = 1125, + LANG_RESET_PET_TALENTS = 1126, + LANG_RESET_PET_TALENTS_ONLINE = 1127, + // Room for more level 3 1128-1199 not used + + // AV + LANG_BG_AV_ALLY = 1200, + LANG_BG_AV_HORDE = 1201, + LANG_BG_AV_TOWER_TAKEN = 1202, + LANG_BG_AV_TOWER_ASSAULTED = 1203, + LANG_BG_AV_TOWER_DEFENDED = 1204, + LANG_BG_AV_GRAVE_TAKEN = 1205, + LANG_BG_AV_GRAVE_DEFENDED = 1206, + LANG_BG_AV_GRAVE_ASSAULTED = 1207, + + LANG_BG_AV_MINE_TAKEN = 1208, + LANG_BG_AV_MINE_NORTH = 1209, + LANG_BG_AV_MINE_SOUTH = 1210, + + LANG_BG_AV_NODE_GRAVE_STORM_AID = 1211, + LANG_BG_AV_NODE_TOWER_DUN_S = 1212, + LANG_BG_AV_NODE_TOWER_DUN_N = 1213, + LANG_BG_AV_NODE_GRAVE_STORMPIKE = 1214, + LANG_BG_AV_NODE_TOWER_ICEWING = 1215, + LANG_BG_AV_NODE_GRAVE_STONE = 1216, + LANG_BG_AV_NODE_TOWER_STONE = 1217, + LANG_BG_AV_NODE_GRAVE_SNOW = 1218, + LANG_BG_AV_NODE_TOWER_ICE = 1219, + LANG_BG_AV_NODE_GRAVE_ICE = 1220, + LANG_BG_AV_NODE_TOWER_POINT = 1221, + LANG_BG_AV_NODE_GRAVE_FROST = 1222, + LANG_BG_AV_NODE_TOWER_FROST_E = 1223, + LANG_BG_AV_NODE_TOWER_FROST_W = 1224, + LANG_BG_AV_NODE_GRAVE_FROST_HUT = 1225, + + LANG_BG_AV_ONEMINTOSTART = 1226, + LANG_BG_AV_HALFMINTOSTART = 1227, + LANG_BG_AV_STARTED = 1228, + LANG_BG_AV_A_NEAR_LOSE = 1229, + LANG_BG_AV_H_NEAR_LOSE = 1230, + LANG_BG_AV_H_CAPTAIN_DEAD = 1231, + LANG_BG_AV_A_CAPTAIN_DEAD = 1232, + // FREE IDS 1233-9999 // Ticket Strings 2000-2029 LANG_COMMAND_TICKETNEW = 2000, LANG_COMMAND_TICKETUPDATED = 2001, LANG_COMMAND_TICKETPLAYERABANDON = 2002, - LANG_COMMAND_TICKETCLOSED = 2003, - LANG_COMMAND_TICKETDELETED = 2004, + LANG_COMMAND_TICKETCLOSED = 2003, + LANG_COMMAND_TICKETDELETED = 2004, LANG_COMMAND_TICKETNOTEXIST = 2005, LANG_COMMAND_TICKETCLOSEFIRST = 2006, LANG_COMMAND_TICKETALREADYASSIGNED = 2007, - LANG_COMMAND_TICKETRELOAD = 2008, - LANG_COMMAND_TICKETSHOWLIST = 2009, - LANG_COMMAND_TICKETSHOWONLINELIST = 2010, - LANG_COMMAND_TICKETSHOWCLOSEDLIST = 2011, - LANG_COMMAND_TICKETASSIGNERROR_A = 2012, - LANG_COMMAND_TICKETASSIGNERROR_B = 2013, - LANG_COMMAND_TICKETNOTASSIGNED = 2014, + LANG_COMMAND_TICKETRELOAD = 2008, + LANG_COMMAND_TICKETSHOWLIST = 2009, + LANG_COMMAND_TICKETSHOWONLINELIST = 2010, + LANG_COMMAND_TICKETSHOWCLOSEDLIST = 2011, + LANG_COMMAND_TICKETASSIGNERROR_A = 2012, + LANG_COMMAND_TICKETASSIGNERROR_B = 2013, + LANG_COMMAND_TICKETNOTASSIGNED = 2014, LANG_COMMAND_TICKETUNASSIGNSECURITY = 2015, - LANG_COMMAND_TICKETCANNOTCLOSE = 2016, + LANG_COMMAND_TICKETCANNOTCLOSE = 2016, LANG_COMMAND_TICKETLISTGUID = 2017, LANG_COMMAND_TICKETLISTNAME = 2018, LANG_COMMAND_TICKETLISTAGE = 2019, @@ -784,9 +833,9 @@ enum TrinityStrings LANG_COMMAND_TICKETLISTMESSAGE = 2022, LANG_COMMAND_TICKETLISTCOMMENT = 2023, LANG_COMMAND_TICKETLISTADDCOMMENT = 2024, + LANG_COMMAND_TICKETLISTAGECREATE = 2025, - - // Trinity strings 5000-9999 + // Trinity strings 5000-9999 LANG_COMMAND_FREEZE = 5000, LANG_COMMAND_FREEZE_ERROR = 5001, LANG_COMMAND_FREEZE_WRONG = 5002, @@ -794,8 +843,8 @@ enum TrinityStrings LANG_COMMAND_NO_FROZEN_PLAYERS = 5004, LANG_COMMAND_LIST_FREEZE = 5005, LANG_COMMAND_FROZEN_PLAYERS = 5006, - LANG_INSTANCE_MUST_RAID_GRP = 5007, - LANG_INSTANCE_NOT_AS_GHOST = 5008, + LANG_INSTANCE_RAID_GROUP_ONLY = 5007, + //LANG_INSTANCE_NOT_AS_GHOST = 5008, LANG_COMMAND_PLAYED_TO_ALL = 5009, // Room for more Trinity strings 5010-9999 // Used for GM Announcements @@ -803,6 +852,16 @@ enum TrinityStrings LANG_GM_NOTIFY = 6614, LANG_GM_ANNOUNCE_COLOR = 6615, + LANG_WORLD_CLOSED = 7523, + LANG_WORLD_OPENED = 7524, + + // Debug commands + LANG_CINEMATIC_NOT_EXIST = 1200, + LANG_MOVIE_NOT_EXIST = 1201, + // Room for more debug 1202-1299 not used + + // FREE IDS 1300-9999 + // Use for not-in-offcial-sources patches // 10000-10999 // opvp hp @@ -867,9 +926,7 @@ enum TrinityStrings LANG_OPVP_EP_FLIGHT_CGT = 10053, LANG_OPVP_ZM_GOSSIP_ALLIANCE = 10054, LANG_OPVP_ZM_GOSSIP_HORDE = 10055, - LANG_NO_ENTER_HALL_OF_LEGENDS = 10056, - LANG_NO_ENTER_CHAMPIONS_HALL = 10057, - + // Use for custom patches 11000-11999 // NOT RESERVED IDS 12000-1999999999 diff --git a/src/game/Level0.cpp b/src/game/Level0.cpp index 62aa5d41709..5f8a6cf86ef 100644 --- a/src/game/Level0.cpp +++ b/src/game/Level0.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,13 +20,10 @@ #include "Common.h" #include "Database/DatabaseEnv.h" -#include "WorldPacket.h" -#include "WorldSession.h" #include "World.h" #include "Player.h" #include "Opcodes.h" #include "Chat.h" -#include "MapManager.h" #include "ObjectAccessor.h" #include "Language.h" #include "AccountMgr.h" @@ -105,6 +102,7 @@ bool ChatHandler::HandleServerInfoCommand(const char* /*args*/) //SendSysMessage(full); //PSendSysMessage(LANG_USING_SCRIPT_LIB,sWorld.GetScriptsVersion()); //PSendSysMessage(LANG_USING_WORLD_DB,sWorld.GetDBVersion()); + //PSendSysMessage(LANG_USING_EVENT_AI,sWorld.GetCreatureEventAIVersion()); PSendSysMessage(LANG_CONNECTED_USERS, activeClientsNum, maxActiveClientsNum, queuedClientsNum, maxQueuedClientsNum); PSendSysMessage(LANG_UPTIME, str.c_str()); PSendSysMessage("Update time diff: %u.", updateTime); @@ -130,7 +128,7 @@ bool ChatHandler::HandleDismountCommand(const char* /*args*/) } m_session->GetPlayer()->Unmount(); - m_session->GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); + m_session->GetPlayer()->RemoveAurasByType(SPELL_AURA_MOUNTED); return true; } @@ -148,7 +146,7 @@ bool ChatHandler::HandleSaveCommand(const char* /*args*/) // save or plan save after 20 sec (logout delay) if current next save time more this value and _not_ output any messages to prevent cheat planning uint32 save_interval = sWorld.getConfig(CONFIG_INTERVAL_SAVE); - if(save_interval==0 || save_interval > 20*1000 && player->GetSaveTimer() <= save_interval - 20*1000) + if(save_interval==0 || save_interval > 20*IN_MILISECONDS && player->GetSaveTimer() <= save_interval - 20*IN_MILISECONDS) player->SaveToDB(); return true; @@ -172,7 +170,7 @@ bool ChatHandler::HandleGMListIngameCommand(const char* /*args*/) first = false; } - SendSysMessage(itr->second->GetName()); + SendSysMessage(GetNameLink(itr->second).c_str()); } } diff --git a/src/game/Level1.cpp b/src/game/Level1.cpp index 489cc656243..452020e667b 100644 --- a/src/game/Level1.cpp +++ b/src/game/Level1.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,6 +40,7 @@ #include "VMapFactory.h" #endif +//-----------------------Npc Commands----------------------- bool ChatHandler::HandleNpcSayCommand(const char* args) { if(!*args) @@ -53,7 +54,7 @@ bool ChatHandler::HandleNpcSayCommand(const char* args) return false; } - pCreature->Say(args, LANG_UNIVERSAL, 0); + pCreature->MonsterSay(args, LANG_UNIVERSAL, 0); return true; } @@ -71,7 +72,7 @@ bool ChatHandler::HandleNpcYellCommand(const char* args) return false; } - pCreature->Yell(args, LANG_UNIVERSAL, 0); + pCreature->MonsterYell(args, LANG_UNIVERSAL, 0); return true; } @@ -91,7 +92,7 @@ bool ChatHandler::HandleNpcTextEmoteCommand(const char* args) return false; } - pCreature->TextEmote(args, 0); + pCreature->MonsterTextEmote(args, 0); return true; } @@ -115,18 +116,22 @@ bool ChatHandler::HandleNpcWhisperCommand(const char* args) uint64 receiver_guid= atol(receiver_str); - pCreature->Whisper(text,receiver_guid); + // check online security + if (HasLowerSecurity(objmgr.GetPlayer(receiver_guid), 0)) + return false; + + pCreature->MonsterWhisper(text,receiver_guid); return true; } +//---------------------------------------------------------- bool ChatHandler::HandleNameAnnounceCommand(const char* args) { WorldPacket data; if(!*args) return false; - //char str[1024]; - //sprintf(str, GetTrinityString(LANG_ANNOUNCE_COLOR), m_session->GetPlayer()->GetName(), args); + sWorld.SendWorldText(LANG_ANNOUNCE_COLOR, m_session->GetPlayer()->GetName(), args); return true; } @@ -194,7 +199,7 @@ bool ChatHandler::HandleGMNotifyCommand(const char* args) } //Enable\Dissable GM Mode -bool ChatHandler::HandleGMmodeCommand(const char* args) +bool ChatHandler::HandleGMCommand(const char* args) { if(!*args) { @@ -288,6 +293,7 @@ bool ChatHandler::HandleGMTicketListCommand(const char* args) std::stringstream ss; ss << PGetParseString(LANG_COMMAND_TICKETLISTGUID, (*itr)->guid); ss << PGetParseString(LANG_COMMAND_TICKETLISTNAME, (*itr)->name.c_str()); + ss << PGetParseString(LANG_COMMAND_TICKETLISTAGECREATE, (secsToTimeString(time(NULL) - (*itr)->createtime, true, false)).c_str()); ss << PGetParseString(LANG_COMMAND_TICKETLISTAGE, (secsToTimeString(time(NULL) - (*itr)->timestamp, true, false)).c_str()); if(objmgr.GetPlayerNameByGUID((*itr)->assignedToGM, gmname)) { @@ -311,6 +317,7 @@ bool ChatHandler::HandleGMTicketListOnlineCommand(const char* args) std::stringstream ss; ss << PGetParseString(LANG_COMMAND_TICKETLISTGUID, (*itr)->guid); ss << PGetParseString(LANG_COMMAND_TICKETLISTNAME, (*itr)->name.c_str()); + ss << PGetParseString(LANG_COMMAND_TICKETLISTAGECREATE, (secsToTimeString(time(NULL) - (*itr)->createtime, true, false)).c_str()); ss << PGetParseString(LANG_COMMAND_TICKETLISTAGE, (secsToTimeString(time(NULL) - (*itr)->timestamp, true, false)).c_str()); if(objmgr.GetPlayerNameByGUID((*itr)->assignedToGM, gmname)) { @@ -333,6 +340,7 @@ bool ChatHandler::HandleGMTicketListClosedCommand(const char* args) std::stringstream ss; ss << PGetParseString(LANG_COMMAND_TICKETLISTGUID, (*itr)->guid); ss << PGetParseString(LANG_COMMAND_TICKETLISTNAME, (*itr)->name.c_str()); + ss << PGetParseString(LANG_COMMAND_TICKETLISTAGECREATE, (secsToTimeString(time(NULL) - (*itr)->createtime, true, false)).c_str()); ss << PGetParseString(LANG_COMMAND_TICKETLISTAGE, (secsToTimeString(time(NULL) - (*itr)->timestamp, true, false)).c_str()); if(objmgr.GetPlayerNameByGUID((*itr)->assignedToGM, gmname)) { @@ -360,6 +368,7 @@ bool ChatHandler::HandleGMTicketGetByIdCommand(const char* args) std::stringstream ss; ss << PGetParseString(LANG_COMMAND_TICKETLISTGUID, ticket->guid); ss << PGetParseString(LANG_COMMAND_TICKETLISTNAME, ticket->name.c_str()); + ss << PGetParseString(LANG_COMMAND_TICKETLISTAGECREATE, (secsToTimeString(time(NULL) - ticket->createtime, true, false)).c_str()); ss << PGetParseString(LANG_COMMAND_TICKETLISTAGE, (secsToTimeString(time(NULL) - ticket->timestamp, true, false)).c_str()); if(objmgr.GetPlayerNameByGUID(ticket->assignedToGM, gmname)) { @@ -390,6 +399,7 @@ bool ChatHandler::HandleGMTicketGetByNameCommand(const char* args) std::stringstream ss; ss << PGetParseString(LANG_COMMAND_TICKETLISTGUID, ticket->guid); ss << PGetParseString(LANG_COMMAND_TICKETLISTNAME, ticket->name.c_str()); + ss << PGetParseString(LANG_COMMAND_TICKETLISTAGECREATE, (secsToTimeString(time(NULL) - ticket->createtime, true, false)).c_str()); ss << PGetParseString(LANG_COMMAND_TICKETLISTAGE, (secsToTimeString(time(NULL) - ticket->timestamp, true, false)).c_str()); if(objmgr.GetPlayerNameByGUID(ticket->assignedToGM, gmname)) { @@ -621,7 +631,7 @@ bool ChatHandler::HandleGMTicketReloadCommand(const char*) } //Enable\Dissable Invisible mode -bool ChatHandler::HandleVisibleCommand(const char* args) +bool ChatHandler::HandleGMVisibleCommand(const char* args) { if (!*args) { @@ -650,14 +660,16 @@ bool ChatHandler::HandleVisibleCommand(const char* args) return false; } + + bool ChatHandler::HandleGPSCommand(const char* args) { WorldObject *obj = NULL; if (*args) { - std::string name = args; - if(normalizePlayerName(name)) - obj = objmgr.GetPlayer(name.c_str()); + uint64 guid = extractGuidFromLink((char*)args); + if(guid) + obj = (WorldObject*)ObjectAccessor::GetObjectByTypeMask(*m_session->GetPlayer(),guid,TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT); if(!obj) { @@ -680,8 +692,8 @@ bool ChatHandler::HandleGPSCommand(const char* args) CellPair cell_val = Trinity::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY()); Cell cell(cell_val); - uint32 zone_id = obj->GetZoneId(); - uint32 area_id = obj->GetAreaId(); + uint32 zone_id, area_id; + obj->GetZoneAndAreaId(zone_id,area_id); MapEntry const* mapEntry = sMapStore.LookupEntry(obj->GetMapId()); AreaTableEntry const* zoneEntry = GetAreaEntryByAreaID(zone_id); @@ -708,22 +720,30 @@ bool ChatHandler::HandleGPSCommand(const char* args) obj->GetMapId(), (mapEntry ? mapEntry->name[m_session->GetSessionDbcLocale()] : "<unknown>" ), zone_id, (zoneEntry ? zoneEntry->area_name[m_session->GetSessionDbcLocale()] : "<unknown>" ), area_id, (areaEntry ? areaEntry->area_name[m_session->GetSessionDbcLocale()] : "<unknown>" ), + obj->GetPhaseMask(), obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), obj->GetOrientation(), cell.GridX(), cell.GridY(), cell.CellX(), cell.CellY(), obj->GetInstanceId(), zone_x, zone_y, ground_z, floor_z, have_map, have_vmap ); sLog.outDebug("Player %s GPS call for %s '%s' (%s: %u):", - GetName(), + m_session ? GetNameLink().c_str() : GetMangosString(LANG_CONSOLE_COMMAND), (obj->GetTypeId() == TYPEID_PLAYER ? "player" : "creature"), obj->GetName(), (obj->GetTypeId() == TYPEID_PLAYER ? "GUID" : "Entry"), (obj->GetTypeId() == TYPEID_PLAYER ? obj->GetGUIDLow(): obj->GetEntry()) ); sLog.outDebug(GetTrinityString(LANG_MAP_POSITION), obj->GetMapId(), (mapEntry ? mapEntry->name[sWorld.GetDefaultDbcLocale()] : "<unknown>" ), zone_id, (zoneEntry ? zoneEntry->area_name[sWorld.GetDefaultDbcLocale()] : "<unknown>" ), area_id, (areaEntry ? areaEntry->area_name[sWorld.GetDefaultDbcLocale()] : "<unknown>" ), + obj->GetPhaseMask(), obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), obj->GetOrientation(), cell.GridX(), cell.GridY(), cell.CellX(), cell.CellY(), obj->GetInstanceId(), zone_x, zone_y, ground_z, floor_z, have_map, have_vmap ); + LiquidData liquid_status; + ZLiquidStatus res = map->getLiquidStatus(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), MAP_ALL_LIQUIDS, &liquid_status); + if (res) + { + PSendSysMessage(LANG_LIQUID_STATUS, liquid_status.level, liquid_status.depth_level, liquid_status.type, res); + } return true; } @@ -733,9 +753,8 @@ bool ChatHandler::HandleNamegoCommand(const char* args) if(!*args) return false; - std::string name = args; - - if(!normalizePlayerName(name)) + std::string name = extractPlayerNameFromLink((char*)args); + if(name.empty()) { SendSysMessage(LANG_PLAYER_NOT_FOUND); SetSentErrorMessage(true); @@ -745,9 +764,14 @@ bool ChatHandler::HandleNamegoCommand(const char* args) Player *chr = objmgr.GetPlayer(name.c_str()); if (chr) { - if(chr->IsBeingTeleported()==true) + std::string nameLink = playerLink(name); + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + if(chr->IsBeingTeleported()) { - PSendSysMessage(LANG_IS_TELEPORTED, chr->GetName()); + PSendSysMessage(LANG_IS_TELEPORTED, nameLink.c_str()); SetSentErrorMessage(true); return false; } @@ -756,10 +780,25 @@ bool ChatHandler::HandleNamegoCommand(const char* args) if(pMap->IsBattleGroundOrArena()) { - // cannot summon to bg - PSendSysMessage(LANG_CANNOT_SUMMON_TO_BG,chr->GetName()); - SetSentErrorMessage(true); - return false; + // only allow if gm mode is on + if (!chr->isGameMaster()) + { + PSendSysMessage(LANG_CANNOT_GO_TO_BG_GM, nameLink.c_str()); + SetSentErrorMessage(true); + return false; + } + // if both players are in different bgs + else if (chr->GetBattleGroundId() && m_session->GetPlayer()->GetBattleGroundId() != chr->GetBattleGroundId()) + { + PSendSysMessage(LANG_CANNOT_GO_TO_BG_FROM_BG, nameLink.c_str()); + SetSentErrorMessage(true); + return false; + } + // all's well, set bg id + // when porting out from the bg, it will be reset to 0 + chr->SetBattleGroundId(m_session->GetPlayer()->GetBattleGroundId(), m_session->GetPlayer()->GetBattleGroundTypeId()); + // remember current position as entry point for return at bg end teleportation + chr->SetBattleGroundEntryPoint(chr->GetMapId(),chr->GetPositionX(),chr->GetPositionY(),chr->GetPositionZ(),chr->GetOrientation()); } else if(pMap->IsDungeon()) { @@ -767,7 +806,7 @@ bool ChatHandler::HandleNamegoCommand(const char* args) if( cMap->Instanceable() && cMap->GetInstanceId() != pMap->GetInstanceId() ) { // cannot summon from instance to instance - PSendSysMessage(LANG_CANNOT_SUMMON_TO_INST,chr->GetName()); + PSendSysMessage(LANG_CANNOT_SUMMON_TO_INST,nameLink.c_str()); SetSentErrorMessage(true); return false; } @@ -778,15 +817,15 @@ bool ChatHandler::HandleNamegoCommand(const char* args) (m_session->GetPlayer()->GetGroup()->GetLeaderGUID() != m_session->GetPlayer()->GetGUID()) ) // the last check is a bit excessive, but let it be, just in case { - PSendSysMessage(LANG_CANNOT_SUMMON_TO_INST,chr->GetName()); + PSendSysMessage(LANG_CANNOT_SUMMON_TO_INST,nameLink.c_str()); SetSentErrorMessage(true); return false; } } - PSendSysMessage(LANG_SUMMONING, chr->GetName(),""); + PSendSysMessage(LANG_SUMMONING, nameLink.c_str(),""); if (needReportToTarget(chr)) - ChatHandler(chr).PSendSysMessage(LANG_SUMMONED_BY, GetName()); + ChatHandler(chr).PSendSysMessage(LANG_SUMMONED_BY, GetNameLink().c_str()); // stop flight if need if(chr->isInFlight()) @@ -805,7 +844,13 @@ bool ChatHandler::HandleNamegoCommand(const char* args) } else if (uint64 guid = objmgr.GetPlayerGUIDByName(name)) { - PSendSysMessage(LANG_SUMMONING, name.c_str(),GetTrinityString(LANG_OFFLINE)); + // check offline security + if (HasLowerSecurity(NULL, guid)) + return false; + + std::string nameLink = playerLink(name); + + PSendSysMessage(LANG_SUMMONING, nameLink.c_str(),GetTrinityString(LANG_OFFLINE)); // in point where GM stay Player::SavePositionInDB(m_session->GetPlayer()->GetMapId(), @@ -833,9 +878,8 @@ bool ChatHandler::HandleGonameCommand(const char* args) Player* _player = m_session->GetPlayer(); - std::string name = args; - - if(!normalizePlayerName(name)) + std::string name = extractPlayerNameFromLink((char*)args); + if(name.empty()) { SendSysMessage(LANG_PLAYER_NOT_FOUND); SetSentErrorMessage(true); @@ -845,26 +889,34 @@ bool ChatHandler::HandleGonameCommand(const char* args) Player *chr = objmgr.GetPlayer(name.c_str()); if (chr) { + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + std::string chrNameLink = playerLink(name); + Map* cMap = chr->GetMap(); if(cMap->IsBattleGroundOrArena()) { // only allow if gm mode is on if (!_player->isGameMaster()) { - PSendSysMessage(LANG_CANNOT_GO_TO_BG_GM,chr->GetName()); + PSendSysMessage(LANG_CANNOT_GO_TO_BG_GM,chrNameLink.c_str()); SetSentErrorMessage(true); return false; } - // if already in a bg, don't let port to other - else if (_player->GetBattleGroundId()) + // if both players are in different bgs + else if (_player->GetBattleGroundId() && _player->GetBattleGroundId() != chr->GetBattleGroundId()) { - PSendSysMessage(LANG_CANNOT_GO_TO_BG_FROM_BG,chr->GetName()); + PSendSysMessage(LANG_CANNOT_GO_TO_BG_FROM_BG,chrNameLink.c_str()); SetSentErrorMessage(true); return false; } // all's well, set bg id // when porting out from the bg, it will be reset to 0 - _player->SetBattleGroundId(chr->GetBattleGroundId()); + _player->SetBattleGroundId(chr->GetBattleGroundId(), chr->GetBattleGroundTypeId()); + // remember current position as entry point for return at bg end teleportation + _player->SetBattleGroundEntryPoint(_player->GetMapId(),_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetOrientation()); } else if(cMap->IsDungeon()) { @@ -878,7 +930,7 @@ bool ChatHandler::HandleGonameCommand(const char* args) // we are in group, we can go only if we are in the player group if (_player->GetGroup() != chr->GetGroup()) { - PSendSysMessage(LANG_CANNOT_GO_TO_INST_PARTY,chr->GetName()); + PSendSysMessage(LANG_CANNOT_GO_TO_INST_PARTY,chrNameLink.c_str()); SetSentErrorMessage(true); return false; } @@ -888,7 +940,7 @@ bool ChatHandler::HandleGonameCommand(const char* args) // we are not in group, let's verify our GM mode if (!_player->isGameMaster()) { - PSendSysMessage(LANG_CANNOT_GO_TO_INST_GM,chr->GetName()); + PSendSysMessage(LANG_CANNOT_GO_TO_INST_GM,chrNameLink.c_str()); SetSentErrorMessage(true); return false; } @@ -912,10 +964,9 @@ bool ChatHandler::HandleGonameCommand(const char* args) _player->SetDifficulty(chr->GetDifficulty()); } - PSendSysMessage(LANG_APPEARING_AT, chr->GetName()); - + PSendSysMessage(LANG_APPEARING_AT, chrNameLink.c_str()); if (_player->IsVisibleGloballyFor(chr)) - ChatHandler(chr).PSendSysMessage(LANG_APPEARING_TO, _player->GetName()); + ChatHandler(chr).PSendSysMessage(LANG_APPEARING_TO, GetNameLink().c_str()); // stop flight if need if(_player->isInFlight()) @@ -938,7 +989,13 @@ bool ChatHandler::HandleGonameCommand(const char* args) if (uint64 guid = objmgr.GetPlayerGUIDByName(name)) { - PSendSysMessage(LANG_APPEARING_AT, name.c_str()); + // check offline security + if (HasLowerSecurity(NULL, guid)) + return false; + + std::string nameLink = playerLink(name); + + PSendSysMessage(LANG_APPEARING_AT, nameLink.c_str()); // to point where player stay (if loaded) float x,y,z,o; @@ -977,12 +1034,15 @@ bool ChatHandler::HandleRecallCommand(const char* args) chr = getSelectedPlayer(); if(!chr) chr = m_session->GetPlayer(); + + // check online security + else if (HasLowerSecurity(chr, 0)) + return false; } else { - std::string name = args; - - if(!normalizePlayerName(name)) + std::string name = extractPlayerNameFromLink((char*)args); + if(name.empty()) { SendSysMessage(LANG_PLAYER_NOT_FOUND); SetSentErrorMessage(true); @@ -997,11 +1057,15 @@ bool ChatHandler::HandleRecallCommand(const char* args) SetSentErrorMessage(true); return false; } + + // check online security + if (HasLowerSecurity(chr, 0)) + return false; } if(chr->IsBeingTeleported()) { - PSendSysMessage(LANG_IS_TELEPORTED, chr->GetName()); + PSendSysMessage(LANG_IS_TELEPORTED, GetNameLink(chr).c_str()); SetSentErrorMessage(true); return false; } @@ -1035,6 +1099,10 @@ bool ChatHandler::HandleModifyKnownTitlesCommand(const char* args) return false; } + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + uint64 titles2 = titles; for(int i=1; i < sCharTitlesStore.GetNumRows(); ++i) @@ -1084,9 +1152,13 @@ bool ChatHandler::HandleModifyHPCommand(const char* args) return false; } - PSendSysMessage(LANG_YOU_CHANGE_HP, chr->GetName(), hp, hpm); + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + PSendSysMessage(LANG_YOU_CHANGE_HP, GetNameLink(chr).c_str(), hp, hpm); if (needReportToTarget(chr)) - ChatHandler(chr).PSendSysMessage(LANG_YOURS_HP_CHANGED, GetName(), hp, hpm); + ChatHandler(chr).PSendSysMessage(LANG_YOURS_HP_CHANGED, GetNameLink().c_str(), hp, hpm); chr->SetMaxHealth( hpm ); chr->SetHealth( hp ); @@ -1128,9 +1200,13 @@ bool ChatHandler::HandleModifyManaCommand(const char* args) return false; } - PSendSysMessage(LANG_YOU_CHANGE_MANA, chr->GetName(), mana, manam); + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + PSendSysMessage(LANG_YOU_CHANGE_MANA, GetNameLink(chr).c_str(), mana, manam); if (needReportToTarget(chr)) - ChatHandler(chr).PSendSysMessage(LANG_YOURS_MANA_CHANGED, GetName(), mana, manam); + ChatHandler(chr).PSendSysMessage(LANG_YOURS_MANA_CHANGED, GetNameLink().c_str(), mana, manam); chr->SetMaxPower(POWER_MANA,manam ); chr->SetPower(POWER_MANA, mana ); @@ -1173,9 +1249,13 @@ bool ChatHandler::HandleModifyEnergyCommand(const char* args) return false; } - PSendSysMessage(LANG_YOU_CHANGE_ENERGY, chr->GetName(), energy/10, energym/10); + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + PSendSysMessage(LANG_YOU_CHANGE_ENERGY, GetNameLink(chr).c_str(), energy/10, energym/10); if (needReportToTarget(chr)) - ChatHandler(chr).PSendSysMessage(LANG_YOURS_ENERGY_CHANGED, GetName(), energy/10, energym/10); + ChatHandler(chr).PSendSysMessage(LANG_YOURS_ENERGY_CHANGED, GetNameLink().c_str(), energy/10, energym/10); chr->SetMaxPower(POWER_ENERGY,energym ); chr->SetPower(POWER_ENERGY, energy ); @@ -1220,9 +1300,13 @@ bool ChatHandler::HandleModifyRageCommand(const char* args) return false; } - PSendSysMessage(LANG_YOU_CHANGE_RAGE, chr->GetName(), rage/10, ragem/10); + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + PSendSysMessage(LANG_YOU_CHANGE_RAGE, GetNameLink(chr).c_str(), rage/10, ragem/10); if (needReportToTarget(chr)) - ChatHandler(chr).PSendSysMessage(LANG_YOURS_RAGE_CHANGED, GetName(), rage/10, ragem/10); + ChatHandler(chr).PSendSysMessage(LANG_YOURS_RAGE_CHANGED, GetNameLink().c_str(), rage/10, ragem/10); chr->SetMaxPower(POWER_RAGE,ragem ); chr->SetPower(POWER_RAGE, rage ); @@ -1230,6 +1314,40 @@ bool ChatHandler::HandleModifyRageCommand(const char* args) return true; } +// Edit Player Runic Power +bool ChatHandler::HandleModifyRunicPowerCommand(const char* args) +{ + if(!*args) + return false; + + int32 rune = atoi((char*)args)*10; + int32 runem = atoi((char*)args)*10; + + if (rune <= 0 || runem <= 0 || runem < rune) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + Player *chr = getSelectedPlayer(); + if (chr == NULL) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + PSendSysMessage(LANG_YOU_CHANGE_RUNIC_POWER, GetNameLink(chr).c_str(), rune/10, runem/10); + if (needReportToTarget(chr)) + ChatHandler(chr).PSendSysMessage(LANG_YOURS_RUNIC_POWER_CHANGED, GetNameLink().c_str(), rune/10, runem/10); + + chr->SetMaxPower(POWER_RUNIC_POWER,runem ); + chr->SetPower(POWER_RUNIC_POWER, rune ); + + return true; +} + //Edit Player Faction bool ChatHandler::HandleModifyFactionCommand(const char* args) { @@ -1344,9 +1462,13 @@ bool ChatHandler::HandleModifySpellCommand(const char* args) return false; } - PSendSysMessage(LANG_YOU_CHANGE_SPELLFLATID, spellflatid, val, mark, chr->GetName()); + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + PSendSysMessage(LANG_YOU_CHANGE_SPELLFLATID, spellflatid, val, mark, GetNameLink(chr).c_str()); if (needReportToTarget(chr)) - ChatHandler(chr).PSendSysMessage(LANG_YOURS_SPELLFLATID_CHANGED, GetName(), spellflatid, val, mark); + ChatHandler(chr).PSendSysMessage(LANG_YOURS_SPELLFLATID_CHANGED, GetNameLink().c_str(), spellflatid, val, mark); WorldPacket data(SMSG_SET_FLAT_SPELL_MODIFIER, (1+1+2+2)); data << uint8(spellflatid); @@ -1374,6 +1496,11 @@ bool ChatHandler::HandleModifyTalentCommand (const char* args) SetSentErrorMessage(true); return false; } + + // check online security + if (HasLowerSecurity(player, 0)) + return false; + player->SetFreeTalentPoints(tp); return true; } @@ -1398,21 +1525,25 @@ bool ChatHandler::HandleTaxiCheatCommand(const char* args) chr=m_session->GetPlayer(); } + // check online security + else if (HasLowerSecurity(chr, 0)) + return false; + if (argstr == "on") { chr->SetTaxiCheater(true); - PSendSysMessage(LANG_YOU_GIVE_TAXIS, chr->GetName()); + PSendSysMessage(LANG_YOU_GIVE_TAXIS, GetNameLink(chr).c_str()); if (needReportToTarget(chr)) - ChatHandler(chr).PSendSysMessage(LANG_YOURS_TAXIS_ADDED, GetName()); + ChatHandler(chr).PSendSysMessage(LANG_YOURS_TAXIS_ADDED, GetNameLink().c_str()); return true; } if (argstr == "off") { chr->SetTaxiCheater(false); - PSendSysMessage(LANG_YOU_REMOVE_TAXIS, chr->GetName()); + PSendSysMessage(LANG_YOU_REMOVE_TAXIS, GetNameLink(chr).c_str()); if (needReportToTarget(chr)) - ChatHandler(chr).PSendSysMessage(LANG_YOURS_TAXIS_REMOVED, GetName()); + ChatHandler(chr).PSendSysMessage(LANG_YOURS_TAXIS_REMOVED, GetNameLink().c_str()); return true; } @@ -1445,16 +1576,22 @@ bool ChatHandler::HandleModifyASpeedCommand(const char* args) return false; } + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + std::string chrNameLink = GetNameLink(chr); + if(chr->isInFlight()) { - PSendSysMessage(LANG_CHAR_IN_FLIGHT,chr->GetName()); + PSendSysMessage(LANG_CHAR_IN_FLIGHT,chrNameLink.c_str()); SetSentErrorMessage(true); return false; } - PSendSysMessage(LANG_YOU_CHANGE_ASPEED, ASpeed, chr->GetName()); + PSendSysMessage(LANG_YOU_CHANGE_ASPEED, ASpeed, chrNameLink.c_str()); if (needReportToTarget(chr)) - ChatHandler(chr).PSendSysMessage(LANG_YOURS_ASPEED_CHANGED, GetName(), ASpeed); + ChatHandler(chr).PSendSysMessage(LANG_YOURS_ASPEED_CHANGED, GetNameLink().c_str(), ASpeed); chr->SetSpeed(MOVE_WALK, ASpeed,true); chr->SetSpeed(MOVE_RUN, ASpeed,true); @@ -1487,16 +1624,22 @@ bool ChatHandler::HandleModifySpeedCommand(const char* args) return false; } + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + std::string chrNameLink = GetNameLink(chr); + if(chr->isInFlight()) { - PSendSysMessage(LANG_CHAR_IN_FLIGHT,chr->GetName()); + PSendSysMessage(LANG_CHAR_IN_FLIGHT,chrNameLink.c_str()); SetSentErrorMessage(true); return false; } - PSendSysMessage(LANG_YOU_CHANGE_SPEED, Speed, chr->GetName()); + PSendSysMessage(LANG_YOU_CHANGE_SPEED, Speed, chrNameLink.c_str()); if (needReportToTarget(chr)) - ChatHandler(chr).PSendSysMessage(LANG_YOURS_SPEED_CHANGED, GetName(), Speed); + ChatHandler(chr).PSendSysMessage(LANG_YOURS_SPEED_CHANGED, GetNameLink().c_str(), Speed); chr->SetSpeed(MOVE_RUN,Speed,true); @@ -1526,16 +1669,22 @@ bool ChatHandler::HandleModifySwimCommand(const char* args) return false; } + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + std::string chrNameLink = GetNameLink(chr); + if(chr->isInFlight()) { - PSendSysMessage(LANG_CHAR_IN_FLIGHT,chr->GetName()); + PSendSysMessage(LANG_CHAR_IN_FLIGHT,chrNameLink.c_str()); SetSentErrorMessage(true); return false; } - PSendSysMessage(LANG_YOU_CHANGE_SWIM_SPEED, Swim, chr->GetName()); + PSendSysMessage(LANG_YOU_CHANGE_SWIM_SPEED, Swim, chrNameLink.c_str()); if (needReportToTarget(chr)) - ChatHandler(chr).PSendSysMessage(LANG_YOURS_SWIM_SPEED_CHANGED, GetName(), Swim); + ChatHandler(chr).PSendSysMessage(LANG_YOURS_SWIM_SPEED_CHANGED, GetNameLink().c_str(), Swim); chr->SetSpeed(MOVE_SWIM,Swim,true); @@ -1565,16 +1714,22 @@ bool ChatHandler::HandleModifyBWalkCommand(const char* args) return false; } + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + std::string chrNameLink = GetNameLink(chr); + if(chr->isInFlight()) { - PSendSysMessage(LANG_CHAR_IN_FLIGHT,chr->GetName()); + PSendSysMessage(LANG_CHAR_IN_FLIGHT,chrNameLink.c_str()); SetSentErrorMessage(true); return false; } - PSendSysMessage(LANG_YOU_CHANGE_BACK_SPEED, BSpeed, chr->GetName()); + PSendSysMessage(LANG_YOU_CHANGE_BACK_SPEED, BSpeed, chrNameLink.c_str()); if (needReportToTarget(chr)) - ChatHandler(chr).PSendSysMessage(LANG_YOURS_BACK_SPEED_CHANGED, GetName(), BSpeed); + ChatHandler(chr).PSendSysMessage(LANG_YOURS_BACK_SPEED_CHANGED, GetNameLink().c_str(), BSpeed); chr->SetSpeed(MOVE_RUN_BACK,BSpeed,true); @@ -1604,9 +1759,13 @@ bool ChatHandler::HandleModifyFlyCommand(const char* args) return false; } - PSendSysMessage(LANG_YOU_CHANGE_FLY_SPEED, FSpeed, chr->GetName()); + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + PSendSysMessage(LANG_YOU_CHANGE_FLY_SPEED, FSpeed, GetNameLink(chr).c_str()); if (needReportToTarget(chr)) - ChatHandler(chr).PSendSysMessage(LANG_YOURS_FLY_SPEED_CHANGED, GetName(), FSpeed); + ChatHandler(chr).PSendSysMessage(LANG_YOURS_FLY_SPEED_CHANGED, GetNameLink().c_str(), FSpeed); chr->SetSpeed(MOVE_FLIGHT,FSpeed,true); @@ -1635,9 +1794,13 @@ bool ChatHandler::HandleModifyScaleCommand(const char* args) return false; } - PSendSysMessage(LANG_YOU_CHANGE_SIZE, Scale, chr->GetName()); + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + PSendSysMessage(LANG_YOU_CHANGE_SIZE, Scale, GetNameLink(chr).c_str()); if (needReportToTarget(chr)) - ChatHandler(chr).PSendSysMessage(LANG_YOURS_SIZE_CHANGED, GetName(), Scale); + ChatHandler(chr).PSendSysMessage(LANG_YOURS_SIZE_CHANGED, GetNameLink().c_str(), Scale); chr->SetFloatValue(OBJECT_FIELD_SCALE_X, Scale); @@ -1878,9 +2041,13 @@ bool ChatHandler::HandleModifyMountCommand(const char* args) return false; } - PSendSysMessage(LANG_YOU_GIVE_MOUNT, chr->GetName()); + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + PSendSysMessage(LANG_YOU_GIVE_MOUNT, GetNameLink(chr).c_str()); if (needReportToTarget(chr)) - ChatHandler(chr).PSendSysMessage(LANG_MOUNT_GIVED, GetName()); + ChatHandler(chr).PSendSysMessage(LANG_MOUNT_GIVED, GetNameLink().c_str()); chr->SetUInt32Value( UNIT_FIELD_FLAGS , 0x001000 ); chr->Mount(mId); @@ -1915,6 +2082,10 @@ bool ChatHandler::HandleModifyMoneyCommand(const char* args) return false; } + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + int32 addmoney = atoi((char*)args); uint32 moneyuser = chr->GetMoney(); @@ -1926,25 +2097,25 @@ bool ChatHandler::HandleModifyMoneyCommand(const char* args) sLog.outDetail(GetTrinityString(LANG_CURRENT_MONEY), moneyuser, addmoney, newmoney); if(newmoney <= 0 ) { - PSendSysMessage(LANG_YOU_TAKE_ALL_MONEY, chr->GetName()); + PSendSysMessage(LANG_YOU_TAKE_ALL_MONEY, GetNameLink(chr).c_str()); if (needReportToTarget(chr)) - ChatHandler(chr).PSendSysMessage(LANG_YOURS_ALL_MONEY_GONE, GetName()); + ChatHandler(chr).PSendSysMessage(LANG_YOURS_ALL_MONEY_GONE, GetNameLink().c_str()); chr->SetMoney(0); } else { - PSendSysMessage(LANG_YOU_TAKE_MONEY, abs(addmoney), chr->GetName()); + PSendSysMessage(LANG_YOU_TAKE_MONEY, abs(addmoney), GetNameLink(chr).c_str()); if (needReportToTarget(chr)) - ChatHandler(chr).PSendSysMessage(LANG_YOURS_MONEY_TAKEN, GetName(), abs(addmoney)); + ChatHandler(chr).PSendSysMessage(LANG_YOURS_MONEY_TAKEN, GetNameLink().c_str(), abs(addmoney)); chr->SetMoney( newmoney ); } } else { - PSendSysMessage(LANG_YOU_GIVE_MONEY, addmoney, chr->GetName()); + PSendSysMessage(LANG_YOU_GIVE_MONEY, addmoney, GetNameLink(chr).c_str()); if (needReportToTarget(chr)) - ChatHandler(chr).PSendSysMessage(LANG_YOURS_MONEY_GIVEN, GetName(), addmoney); + ChatHandler(chr).PSendSysMessage(LANG_YOURS_MONEY_GIVEN, GetNameLink().c_str(), addmoney); chr->ModifyMoney( addmoney ); } @@ -1953,20 +2124,24 @@ bool ChatHandler::HandleModifyMoneyCommand(const char* args) return true; } -//Edit Player field +//Edit Unit field bool ChatHandler::HandleModifyBitCommand(const char* args) { if( !*args ) return false; - Player *chr = getSelectedPlayer(); - if (chr == NULL) + Unit *unit = getSelectedUnit(); + if (!unit) { SendSysMessage(LANG_NO_CHAR_SELECTED); SetSentErrorMessage(true); return false; } + // check online security + if (unit->GetTypeId() == TYPEID_PLAYER && HasLowerSecurity((Player *)unit, 0)) + return false; + char* pField = strtok((char*)args, " "); if (!pField) return false; @@ -1978,13 +2153,12 @@ bool ChatHandler::HandleModifyBitCommand(const char* args) uint16 field = atoi(pField); uint32 bit = atoi(pBit); - if (field < 1 || field >= PLAYER_END) + if (field < OBJECT_END || field >= unit->GetValuesCount()) { SendSysMessage(LANG_BAD_VALUE); SetSentErrorMessage(true); return false; } - if (bit < 1 || bit > 32) { SendSysMessage(LANG_BAD_VALUE); @@ -1992,17 +2166,16 @@ bool ChatHandler::HandleModifyBitCommand(const char* args) return false; } - if ( chr->HasFlag( field, (1<<(bit-1)) ) ) + if ( unit->HasFlag( field, (1<<(bit-1)) ) ) { - chr->RemoveFlag( field, (1<<(bit-1)) ); + unit->RemoveFlag( field, (1<<(bit-1)) ); PSendSysMessage(LANG_REMOVE_BIT, bit, field); } else { - chr->SetFlag( field, (1<<(bit-1)) ); + unit->SetFlag( field, (1<<(bit-1)) ); PSendSysMessage(LANG_SET_BIT, bit, field); } - return true; } @@ -2019,11 +2192,15 @@ bool ChatHandler::HandleModifyHonorCommand (const char* args) return false; } + // check online security + if (HasLowerSecurity(target, 0)) + return false; + int32 amount = (uint32)atoi(args); target->ModifyHonorPoints(amount); - PSendSysMessage(LANG_COMMAND_MODIFY_HONOR, target->GetName(), target->GetHonorPoints()); + PSendSysMessage(LANG_COMMAND_MODIFY_HONOR, GetNameLink(target).c_str(), target->GetHonorPoints()); return true; } @@ -2126,8 +2303,10 @@ bool ChatHandler::HandleLookupAreaCommand(const char* args) } } } + if (counter == 0) // if counter == 0 then we found nth SendSysMessage (LANG_COMMAND_NOAREAFOUND); + return true; } @@ -2209,35 +2388,6 @@ bool ChatHandler::HandleWhispersCommand(const char* args) return false; } -//Play sound -bool ChatHandler::HandlePlaySoundCommand(const char* args) -{ - // USAGE: .debug playsound #soundid - // #soundid - ID decimal number from SoundEntries.dbc (1st column) - // this file have about 5000 sounds. - // In this realization only caller can hear this sound. - if( *args ) - { - uint32 dwSoundId = atoi((char*)args); - - if( !sSoundEntriesStore.LookupEntry(dwSoundId) ) - { - PSendSysMessage(LANG_SOUND_NOT_EXIST, dwSoundId); - SetSentErrorMessage(true); - return false; - } - - WorldPacket data(SMSG_PLAY_OBJECT_SOUND,4+8); - data << uint32(dwSoundId) << m_session->GetPlayer()->GetGUID(); - m_session->SendPacket(&data); - - PSendSysMessage(LANG_YOU_HEAR_SOUND, dwSoundId); - return true; - } - - return false; -} - //Save all players in the world bool ChatHandler::HandleSaveAllCommand(const char* /*args*/) { @@ -2254,9 +2404,13 @@ bool ChatHandler::HandleSendMailCommand(const char* args) // format: name "subject text" "mail text" - char* pName = strtok((char*)args, " "); - if(!pName) + std::string name = extractPlayerNameFromLink((char*)args); + if(name.empty()) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); return false; + } char* tail1 = strtok(NULL, ""); if(!tail1) @@ -2294,18 +2448,10 @@ bool ChatHandler::HandleSendMailCommand(const char* args) if (!msgText) return false; - // pName, msgSubject, msgText isn't NUL after prev. check - std::string name = pName; + // msgSubject, msgText isn't NUL after prev. check std::string subject = msgSubject; std::string text = msgText; - if(!normalizePlayerName(name)) - { - SendSysMessage(LANG_PLAYER_NOT_FOUND); - SetSentErrorMessage(true); - return false; - } - uint64 receiver_guid = objmgr.GetPlayerGUIDByName(name); if(!receiver_guid) { @@ -2314,7 +2460,6 @@ bool ChatHandler::HandleSendMailCommand(const char* args) return false; } - uint32 mailId = objmgr.GenerateMailID(); // from console show not existed sender uint32 sender_guidlo = m_session ? m_session->GetPlayer()->GetGUIDLow() : 0; @@ -2326,24 +2471,19 @@ bool ChatHandler::HandleSendMailCommand(const char* args) WorldSession::SendMailTo(receiver,messagetype, stationery, sender_guidlo, GUID_LOPART(receiver_guid), subject, itemTextId, NULL, 0, 0, MAIL_CHECK_MASK_NONE); - PSendSysMessage(LANG_MAIL_SENT, name.c_str()); + std::string nameLink = playerLink(name); + PSendSysMessage(LANG_MAIL_SENT, nameLink.c_str()); return true; } // teleport player to given game_tele.entry -bool ChatHandler::HandleNameTeleCommand(const char * args) +bool ChatHandler::HandleTeleNameCommand(const char * args) { if(!*args) return false; - char* pName = strtok((char*)args, " "); - - if(!pName) - return false; - - std::string name = pName; - - if(!normalizePlayerName(name)) + std::string name = extractPlayerNameFromLink((char*)args); + if(name.empty()) { SendSysMessage(LANG_PLAYER_NOT_FOUND); SetSentErrorMessage(true); @@ -2374,17 +2514,22 @@ bool ChatHandler::HandleNameTeleCommand(const char * args) Player *chr = objmgr.GetPlayer(name.c_str()); if (chr) { + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + std::string chrNameLink = playerLink(name); if(chr->IsBeingTeleported()==true) { - PSendSysMessage(LANG_IS_TELEPORTED, chr->GetName()); + PSendSysMessage(LANG_IS_TELEPORTED, chrNameLink.c_str()); SetSentErrorMessage(true); return false; } - PSendSysMessage(LANG_TELEPORTING_TO, chr->GetName(),"", tele->name.c_str()); + PSendSysMessage(LANG_TELEPORTING_TO, chrNameLink.c_str(),"", tele->name.c_str()); if (needReportToTarget(chr)) - ChatHandler(chr).PSendSysMessage(LANG_TELEPORTED_TO_BY, GetName()); + ChatHandler(chr).PSendSysMessage(LANG_TELEPORTED_TO_BY, GetNameLink().c_str()); // stop flight if need if(chr->isInFlight()) @@ -2398,10 +2543,17 @@ bool ChatHandler::HandleNameTeleCommand(const char * args) chr->TeleportTo(tele->mapId,tele->position_x,tele->position_y,tele->position_z,tele->orientation); } - else if (uint64 guid = objmgr.GetPlayerGUIDByName(name.c_str())) + else if (uint64 guid = objmgr.GetPlayerGUIDByName(name)) { - PSendSysMessage(LANG_TELEPORTING_TO, name.c_str(), GetTrinityString(LANG_OFFLINE), tele->name.c_str()); - Player::SavePositionInDB(tele->mapId,tele->position_x,tele->position_y,tele->position_z,tele->orientation,MapManager::Instance().GetZoneId(tele->mapId,tele->position_x,tele->position_y),guid); + // check offline security + if (HasLowerSecurity(NULL, guid)) + return false; + + std::string nameLink = playerLink(name); + + PSendSysMessage(LANG_TELEPORTING_TO, nameLink.c_str(), GetMangosString(LANG_OFFLINE), tele->name.c_str()); + Player::SavePositionInDB(tele->mapId,tele->position_x,tele->position_y,tele->position_z,tele->orientation, + MapManager::Instance().GetZoneId(tele->mapId,tele->position_x,tele->position_y,tele->position_z),guid); } else PSendSysMessage(LANG_NO_PLAYER, name.c_str()); @@ -2410,7 +2562,7 @@ bool ChatHandler::HandleNameTeleCommand(const char * args) } //Teleport group to given game_tele.entry -bool ChatHandler::HandleGroupTeleCommand(const char * args) +bool ChatHandler::HandleTeleGroupCommand(const char * args) { if(!*args) return false; @@ -2423,6 +2575,10 @@ bool ChatHandler::HandleGroupTeleCommand(const char * args) return false; } + // check online security + if (HasLowerSecurity(player, 0)) + return false; + // id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r GameTele const* tele = extractGameTeleFromLink((char*)args); if(!tele) @@ -2439,10 +2595,13 @@ bool ChatHandler::HandleGroupTeleCommand(const char * args) SetSentErrorMessage(true); return false; } + + std::string nameLink = GetNameLink(player); + Group *grp = player->GetGroup(); if(!grp) { - PSendSysMessage(LANG_NOT_IN_GROUP,player->GetName()); + PSendSysMessage(LANG_NOT_IN_GROUP,nameLink.c_str()); SetSentErrorMessage(true); return false; } @@ -2454,15 +2613,21 @@ bool ChatHandler::HandleGroupTeleCommand(const char * args) if(!pl || !pl->GetSession() ) continue; + // check online security + if (HasLowerSecurity(pl, 0)) + return false; + + std::string plNameLink = GetNameLink(pl); + if(pl->IsBeingTeleported()) { - PSendSysMessage(LANG_IS_TELEPORTED, pl->GetName()); + PSendSysMessage(LANG_IS_TELEPORTED, plNameLink.c_str()); continue; } - PSendSysMessage(LANG_TELEPORTING_TO, pl->GetName(),"", tele->name.c_str()); + PSendSysMessage(LANG_TELEPORTING_TO, plNameLink.c_str(),"", tele->name.c_str()); if (needReportToTarget(pl)) - ChatHandler(pl).PSendSysMessage(LANG_TELEPORTED_TO_BY, GetName()); + ChatHandler(pl).PSendSysMessage(LANG_TELEPORTED_TO_BY, nameLink.c_str()); // stop flight if need if(pl->isInFlight()) @@ -2486,9 +2651,8 @@ bool ChatHandler::HandleGroupgoCommand(const char* args) if(!*args) return false; - std::string name = args; - - if(!normalizePlayerName(name)) + std::string name = extractPlayerNameFromLink((char*)args); + if(name.empty()) { SendSysMessage(LANG_PLAYER_NOT_FOUND); SetSentErrorMessage(true); @@ -2503,16 +2667,22 @@ bool ChatHandler::HandleGroupgoCommand(const char* args) return false; } + // check online security + if (HasLowerSecurity(player, 0)) + return false; + Group *grp = player->GetGroup(); + std::string nameLink = playerLink(name); + if(!grp) { - PSendSysMessage(LANG_NOT_IN_GROUP,player->GetName()); + PSendSysMessage(LANG_NOT_IN_GROUP,nameLink.c_str()); SetSentErrorMessage(true); return false; } - Map* gmMap = MapManager::Instance().GetMap(m_session->GetPlayer()->GetMapId(),m_session->GetPlayer()); + Map* gmMap = m_session->GetPlayer()->GetMap(); bool to_instance = gmMap->Instanceable(); // we are in instance, and can summon only player in our group with us as lead @@ -2533,29 +2703,35 @@ bool ChatHandler::HandleGroupgoCommand(const char* args) if(!pl || pl==m_session->GetPlayer() || !pl->GetSession() ) continue; + // check online security + if (HasLowerSecurity(pl, 0)) + return false; + + std::string plNameLink = playerLink(pl->GetName()); + if(pl->IsBeingTeleported()==true) { - PSendSysMessage(LANG_IS_TELEPORTED, pl->GetName()); + PSendSysMessage(LANG_IS_TELEPORTED, plNameLink.c_str()); SetSentErrorMessage(true); return false; } if (to_instance) { - Map* plMap = MapManager::Instance().GetMap(pl->GetMapId(),pl); + Map* plMap = pl->GetMap(); if ( plMap->Instanceable() && plMap->GetInstanceId() != gmMap->GetInstanceId() ) { // cannot summon from instance to instance - PSendSysMessage(LANG_CANNOT_SUMMON_TO_INST,pl->GetName()); + PSendSysMessage(LANG_CANNOT_SUMMON_TO_INST,plNameLink.c_str()); SetSentErrorMessage(true); return false; } } - PSendSysMessage(LANG_SUMMONING, pl->GetName(),""); + PSendSysMessage(LANG_SUMMONING, plNameLink.c_str(),""); if (needReportToTarget(pl)) - ChatHandler(pl).PSendSysMessage(LANG_SUMMONED_BY, GetName()); + ChatHandler(pl).PSendSysMessage(LANG_SUMMONED_BY, GetNameLink().c_str()); // stop flight if need if(pl->isInFlight()) @@ -2689,6 +2865,11 @@ bool ChatHandler::HandleGoZoneXYCommand(const char* args) float x = (float)atof(px); float y = (float)atof(py); + + // prevent accept wrong numeric args + if (x==0.0f && *px!='0' || y==0.0f && *py!='0') + return false; + uint32 areaid = cAreaId ? (uint32)atoi(cAreaId) : _player->GetZoneId(); AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(areaid); @@ -2785,7 +2966,7 @@ bool ChatHandler::HandleGoGridCommand(const char* args) return true; } -bool ChatHandler::HandleDrunkCommand(const char* args) +bool ChatHandler::HandleModifyDrunkCommand(const char* args) { if(!*args) return false; diff --git a/src/game/Level2.cpp b/src/game/Level2.cpp index 26a701fc507..116fea3ac46 100644 --- a/src/game/Level2.cpp +++ b/src/game/Level2.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -10,19 +10,16 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "Common.h" #include "Database/DatabaseEnv.h" -#include "WorldPacket.h" -#include "WorldSession.h" -#include "World.h" #include "ObjectMgr.h" #include "Player.h" #include "Item.h" @@ -33,9 +30,11 @@ #include "MapManager.h" #include "Language.h" #include "World.h" -#include "GameEvent.h" +#include "GameEventMgr.h" #include "SpellMgr.h" +#include "PoolHandler.h" #include "AccountMgr.h" +#include "TicketMgr.h" #include "WaypointManager.h" #include "Util.h" #include <cctype> @@ -45,6 +44,7 @@ #include "GlobalEvents.h" #include "TargetedMovementGenerator.h" // for HandleNpcUnFollowCommand +#include "CreatureGroups.h" static uint32 ReputationRankStrIndex[MAX_REPUTATION_RANK] = { @@ -58,11 +58,13 @@ bool ChatHandler::HandleMuteCommand(const char* args) if (!*args) return false; - char *charname = strtok((char*)args, " "); - if (!charname) + std::string name = extractPlayerNameFromLink((char*)args); + if(name.empty()) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); return false; - - std::string cname = charname; + } char *timetonotspeak = strtok(NULL, " "); if(!timetonotspeak) @@ -70,14 +72,7 @@ bool ChatHandler::HandleMuteCommand(const char* args) uint32 notspeaktime = (uint32) atoi(timetonotspeak); - if(!normalizePlayerName(cname)) - { - SendSysMessage(LANG_PLAYER_NOT_FOUND); - SetSentErrorMessage(true); - return false; - } - - uint64 guid = objmgr.GetPlayerGUIDByName(cname.c_str()); + uint64 guid = objmgr.GetPlayerGUIDByName(name); if(!guid) { SendSysMessage(LANG_PLAYER_NOT_FOUND); @@ -87,27 +82,11 @@ bool ChatHandler::HandleMuteCommand(const char* args) Player *chr = objmgr.GetPlayer(guid); - // check security - uint32 account_id = 0; - uint32 security = 0; - - if (chr) - { - account_id = chr->GetSession()->GetAccountId(); - security = chr->GetSession()->GetSecurity(); - } - else - { - account_id = objmgr.GetPlayerAccountIdByGUID(guid); - security = accmgr.GetSecurity(account_id); - } - - if(m_session && security >= m_session->GetSecurity()) - { - SendSysMessage(LANG_YOURS_SECURITY_IS_LOW); - SetSentErrorMessage(true); + // must have strong lesser security level + if(HasLowerSecurity (chr,guid,true)) return false; - } + + uint32 account_id = chr ? chr->GetSession()->GetAccountId() : objmgr.GetPlayerAccountIdByGUID(guid); time_t mutetime = time(NULL) + notspeaktime*60; @@ -119,7 +98,9 @@ bool ChatHandler::HandleMuteCommand(const char* args) if(chr) ChatHandler(chr).PSendSysMessage(LANG_YOUR_CHAT_DISABLED, notspeaktime); - PSendSysMessage(LANG_YOU_DISABLE_CHAT, cname.c_str(), notspeaktime); + std::string nameLink = playerLink(name); + + PSendSysMessage(LANG_YOU_DISABLE_CHAT, nameLink.c_str(), notspeaktime); return true; } @@ -130,20 +111,15 @@ bool ChatHandler::HandleUnmuteCommand(const char* args) if (!*args) return false; - char *charname = strtok((char*)args, " "); - if (!charname) - return false; - - std::string cname = charname; - - if(!normalizePlayerName(cname)) + std::string name = extractPlayerNameFromLink((char*)args); + if(name.empty()) { SendSysMessage(LANG_PLAYER_NOT_FOUND); SetSentErrorMessage(true); return false; } - uint64 guid = objmgr.GetPlayerGUIDByName(cname.c_str()); + uint64 guid = objmgr.GetPlayerGUIDByName(name); if(!guid) { SendSysMessage(LANG_PLAYER_NOT_FOUND); @@ -153,27 +129,11 @@ bool ChatHandler::HandleUnmuteCommand(const char* args) Player *chr = objmgr.GetPlayer(guid); - // check security - uint32 account_id = 0; - uint32 security = 0; - - if (chr) - { - account_id = chr->GetSession()->GetAccountId(); - security = chr->GetSession()->GetSecurity(); - } - else - { - account_id = objmgr.GetPlayerAccountIdByGUID(guid); - security = accmgr.GetSecurity(account_id); - } - - if(m_session && security >= m_session->GetSecurity()) - { - SendSysMessage(LANG_YOURS_SECURITY_IS_LOW); - SetSentErrorMessage(true); + // must have strong lesser security level + if(HasLowerSecurity (chr,guid,true)) return false; - } + + uint32 account_id = chr ? chr->GetSession()->GetAccountId() : objmgr.GetPlayerAccountIdByGUID(guid); if (chr) { @@ -192,155 +152,51 @@ bool ChatHandler::HandleUnmuteCommand(const char* args) if(chr) ChatHandler(chr).PSendSysMessage(LANG_YOUR_CHAT_ENABLED); - PSendSysMessage(LANG_YOU_ENABLE_CHAT, cname.c_str()); - return true; -} - -bool ChatHandler::HandleTargetObjectCommand(const char* args) -{ - Player* pl = m_session->GetPlayer(); - QueryResult *result; - GameEvent::ActiveEvents const& activeEventsList = gameeventmgr.GetActiveEventList(); - if(*args) - { - int32 id = atoi((char*)args); - if(id) - result = WorldDatabase.PQuery("SELECT guid, id, position_x, position_y, position_z, orientation, map, (POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) AS order_ FROM gameobject WHERE map = '%i' AND id = '%u' ORDER BY order_ ASC LIMIT 1", - pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(), pl->GetMapId(),id); - else - { - std::string name = args; - WorldDatabase.escape_string(name); - result = WorldDatabase.PQuery( - "SELECT guid, id, position_x, position_y, position_z, orientation, map, (POW(position_x - %f, 2) + POW(position_y - %f, 2) + POW(position_z - %f, 2)) AS order_ " - "FROM gameobject,gameobject_template WHERE gameobject_template.entry = gameobject.id AND map = %i AND name "_LIKE_" "_CONCAT3_("'%%'","'%s'","'%%'")" ORDER BY order_ ASC LIMIT 1", - pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(), pl->GetMapId(),name.c_str()); - } - } - else - { - std::ostringstream eventFilter; - eventFilter << " AND (event IS NULL "; - bool initString = true; - - for (GameEvent::ActiveEvents::const_iterator itr = activeEventsList.begin(); itr != activeEventsList.end(); ++itr) - { - if (initString) - { - eventFilter << "OR event IN (" <<*itr; - initString =false; - } - else - eventFilter << "," << *itr; - } - - if (!initString) - eventFilter << "))"; - else - eventFilter << ")"; + std::string nameLink = playerLink(name); - result = WorldDatabase.PQuery("SELECT gameobject.guid, id, position_x, position_y, position_z, orientation, map, " - "(POW(position_x - %f, 2) + POW(position_y - %f, 2) + POW(position_z - %f, 2)) AS order_ FROM gameobject " - "LEFT OUTER JOIN game_event_gameobject on gameobject.guid=game_event_gameobject.guid WHERE map = '%i' %s ORDER BY order_ ASC LIMIT 1", - m_session->GetPlayer()->GetPositionX(), m_session->GetPlayer()->GetPositionY(), m_session->GetPlayer()->GetPositionZ(), m_session->GetPlayer()->GetMapId(),eventFilter.str().c_str()); - } - - if (!result) - { - SendSysMessage(LANG_COMMAND_TARGETOBJNOTFOUND); - return true; - } - - Field *fields = result->Fetch(); - uint32 lowguid = fields[0].GetUInt32(); - uint32 id = fields[1].GetUInt32(); - float x = fields[2].GetFloat(); - float y = fields[3].GetFloat(); - float z = fields[4].GetFloat(); - float o = fields[5].GetFloat(); - int mapid = fields[6].GetUInt16(); - delete result; - - GameObjectInfo const* goI = objmgr.GetGameObjectInfo(id); - - if (!goI) - { - PSendSysMessage(LANG_GAMEOBJECT_NOT_EXIST,id); - return false; - } - - GameObject* target = ObjectAccessor::GetGameObject(*m_session->GetPlayer(),MAKE_NEW_GUID(lowguid,id,HIGHGUID_GAMEOBJECT)); - - PSendSysMessage(LANG_GAMEOBJECT_DETAIL, lowguid, goI->name, lowguid, id, x, y, z, mapid, o); - - if(target) - { - int32 curRespawnDelay = target->GetRespawnTimeEx()-time(NULL); - if(curRespawnDelay < 0) - curRespawnDelay = 0; - - std::string curRespawnDelayStr = secsToTimeString(curRespawnDelay,true); - std::string defRespawnDelayStr = secsToTimeString(target->GetRespawnDelay(),true); - - PSendSysMessage(LANG_COMMAND_RAWPAWNTIMES, defRespawnDelayStr.c_str(),curRespawnDelayStr.c_str()); - } + PSendSysMessage(LANG_YOU_ENABLE_CHAT, nameLink.c_str()); return true; } -//teleport to gameobject -bool ChatHandler::HandleGoObjectCommand(const char* args) +bool ChatHandler::HandleGoTicketCommand(const char * args) { - if(!*args) + if(!*args) return false; - Player* _player = m_session->GetPlayer(); + char *cstrticket_id = strtok((char*)args, " "); - // number or [name] Shift-click form |color|Hgameobject:go_guid|h[name]|h|r - char* cId = extractKeyFromLink((char*)args,"Hgameobject"); - if(!cId) + if(!cstrticket_id) return false; - int32 guid = atoi(cId); - if(!guid) + uint64 ticket_id = atoi(cstrticket_id); + if(!ticket_id) return false; - float x, y, z, ort; - int mapid; - - // by DB guid - if (GameObjectData const* go_data = objmgr.GetGOData(guid)) + GM_Ticket *ticket = ticketmgr.GetGMTicket(ticket_id); + if(!ticket) { - x = go_data->posX; - y = go_data->posY; - z = go_data->posZ; - ort = go_data->orientation; - mapid = go_data->mapid; - } - else - { - SendSysMessage(LANG_COMMAND_GOOBJNOTFOUND); - SetSentErrorMessage(true); - return false; + SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); + return true; } - if(!MapManager::IsValidMapCoord(mapid,x,y,z,ort)) - { - PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid); - SetSentErrorMessage(true); - return false; - } + float x, y, z; + int mapid; - // stop flight if need + x = ticket->pos_x; + y = ticket->pos_y; + z = ticket->pos_z; + mapid = ticket->map; + + Player* _player = m_session->GetPlayer(); if(_player->isInFlight()) { _player->GetMotionMaster()->MovementExpired(); _player->m_taxi.ClearTaxiDestinations(); } - // save only in non-flight case - else + else _player->SaveRecallPosition(); - _player->TeleportTo(mapid, x, y, z, ort); + _player->TeleportTo(mapid, x, y, z, 1, 0); return true; } @@ -435,15 +291,15 @@ bool ChatHandler::HandleGoGraveyardCommand(const char* args) } /** \brief Teleport the GM to the specified creature - * - * .gocreature <GUID> --> TP using creature.guid - * .gocreature azuregos --> TP player to the mob with this name - * Warning: If there is more than one mob with this name - * you will be teleported to the first one that is found. - * .gocreature id 6109 --> TP player to the mob, that has this creature_template.entry - * Warning: If there is more than one mob with this "id" - * you will be teleported to the first one that is found. - */ +* +* .gocreature <GUID> --> TP using creature.guid +* .gocreature azuregos --> TP player to the mob with this name +* Warning: If there is more than one mob with this name +* you will be teleported to the first one that is found. +* .gocreature id 6109 --> TP player to the mob, that has this creature_template.entry +* Warning: If there is more than one mob with this "id" +* you will be teleported to the first one that is found. +*/ //teleport to creature bool ChatHandler::HandleGoCreatureCommand(const char* args) { @@ -541,434 +397,180 @@ bool ChatHandler::HandleGoCreatureCommand(const char* args) return true; } -bool ChatHandler::HandleGUIDCommand(const char* /*args*/) +//teleport to gameobject +bool ChatHandler::HandleGoObjectCommand(const char* args) { - uint64 guid = m_session->GetPlayer()->GetSelection(); - - if (guid == 0) - { - SendSysMessage(LANG_NO_SELECTION); - SetSentErrorMessage(true); + if(!*args) return false; - } - PSendSysMessage(LANG_OBJECT_GUID, GUID_LOPART(guid), GUID_HIPART(guid)); - return true; -} + Player* _player = m_session->GetPlayer(); -bool ChatHandler::HandleLookupFactionCommand(const char* args) -{ - if (!*args) + // number or [name] Shift-click form |color|Hgameobject:go_guid|h[name]|h|r + char* cId = extractKeyFromLink((char*)args,"Hgameobject"); + if(!cId) return false; - // Can be NULL at console call - Player *target = getSelectedPlayer (); - - std::string namepart = args; - std::wstring wnamepart; - - if (!Utf8toWStr (namepart,wnamepart)) + int32 guid = atoi(cId); + if(!guid) return false; - // converting string that we try to find to lower case - wstrToLower (wnamepart); - - uint32 counter = 0; // Counter for figure out that we found smth. + float x, y, z, ort; + int mapid; - for (uint32 id = 0; id < sFactionStore.GetNumRows(); ++id) + // by DB guid + if (GameObjectData const* go_data = objmgr.GetGOData(guid)) { - FactionEntry const *factionEntry = sFactionStore.LookupEntry (id); - if (factionEntry) - { - FactionState const* repState = NULL; - if(target) - { - FactionStateList::const_iterator repItr = target->m_factions.find (factionEntry->reputationListID); - if(repItr != target->m_factions.end()) - repState = &repItr->second; - } - - int loc = m_session ? m_session->GetSessionDbcLocale() : sWorld.GetDefaultDbcLocale(); - std::string name = factionEntry->name[loc]; - if(name.empty()) - continue; - - if (!Utf8FitTo(name, wnamepart)) - { - loc = 0; - for(; loc < MAX_LOCALE; ++loc) - { - if(m_session && loc==m_session->GetSessionDbcLocale()) - continue; - - name = factionEntry->name[loc]; - if(name.empty()) - continue; - - if (Utf8FitTo(name, wnamepart)) - break; - } - } - - if(loc < MAX_LOCALE) - { - // send faction in "id - [faction] rank reputation [visible] [at war] [own team] [unknown] [invisible] [inactive]" format - // or "id - [faction] [no reputation]" format - std::ostringstream ss; - if (m_session) - ss << id << " - |cffffffff|Hfaction:" << id << "|h[" << name << " " << localeNames[loc] << "]|h|r"; - else - ss << id << " - " << name << " " << localeNames[loc]; - - if (repState) // and then target!=NULL also - { - ReputationRank rank = target->GetReputationRank(factionEntry); - std::string rankName = GetTrinityString(ReputationRankStrIndex[rank]); - - ss << " " << rankName << "|h|r (" << target->GetReputation(factionEntry) << ")"; - - if(repState->Flags & FACTION_FLAG_VISIBLE) - ss << GetTrinityString(LANG_FACTION_VISIBLE); - if(repState->Flags & FACTION_FLAG_AT_WAR) - ss << GetTrinityString(LANG_FACTION_ATWAR); - if(repState->Flags & FACTION_FLAG_PEACE_FORCED) - ss << GetTrinityString(LANG_FACTION_PEACE_FORCED); - if(repState->Flags & FACTION_FLAG_HIDDEN) - ss << GetTrinityString(LANG_FACTION_HIDDEN); - if(repState->Flags & FACTION_FLAG_INVISIBLE_FORCED) - ss << GetTrinityString(LANG_FACTION_INVISIBLE_FORCED); - if(repState->Flags & FACTION_FLAG_INACTIVE) - ss << GetTrinityString(LANG_FACTION_INACTIVE); - } - else - ss << GetTrinityString(LANG_FACTION_NOREPUTATION); - - SendSysMessage(ss.str().c_str()); - counter++; - } - } + x = go_data->posX; + y = go_data->posY; + z = go_data->posZ; + ort = go_data->orientation; + mapid = go_data->mapid; } - - if (counter == 0) // if counter == 0 then we found nth - SendSysMessage(LANG_COMMAND_FACTION_NOTFOUND); - return true; -} - -bool ChatHandler::HandleModifyRepCommand(const char * args) -{ - if (!*args) return false; - - Player* target = NULL; - target = getSelectedPlayer(); - - if(!target) + else { - SendSysMessage(LANG_PLAYER_NOT_FOUND); + SendSysMessage(LANG_COMMAND_GOOBJNOTFOUND); SetSentErrorMessage(true); return false; } - char* factionTxt = extractKeyFromLink((char*)args,"Hfaction"); - if(!factionTxt) - return false; - - uint32 factionId = atoi(factionTxt); - - int32 amount = 0; - char *rankTxt = strtok(NULL, " "); - if (!factionTxt || !rankTxt) - return false; - - amount = atoi(rankTxt); - if ((amount == 0) && (rankTxt[0] != '-') && !isdigit(rankTxt[0])) - { - std::string rankStr = rankTxt; - std::wstring wrankStr; - if(!Utf8toWStr(rankStr,wrankStr)) - return false; - wstrToLower( wrankStr ); - - int r = 0; - amount = -42000; - for (; r < MAX_REPUTATION_RANK; ++r) - { - std::string rank = GetTrinityString(ReputationRankStrIndex[r]); - if(rank.empty()) - continue; - - std::wstring wrank; - if(!Utf8toWStr(rank,wrank)) - continue; - - wstrToLower(wrank); - - if(wrank.substr(0,wrankStr.size())==wrankStr) - { - char *deltaTxt = strtok(NULL, " "); - if (deltaTxt) - { - int32 delta = atoi(deltaTxt); - if ((delta < 0) || (delta > Player::ReputationRank_Length[r] -1)) - { - PSendSysMessage(LANG_COMMAND_FACTION_DELTA, (Player::ReputationRank_Length[r]-1)); - SetSentErrorMessage(true); - return false; - } - amount += delta; - } - break; - } - amount += Player::ReputationRank_Length[r]; - } - if (r >= MAX_REPUTATION_RANK) - { - PSendSysMessage(LANG_COMMAND_FACTION_INVPARAM, rankTxt); - SetSentErrorMessage(true); - return false; - } - } - - FactionEntry const *factionEntry = sFactionStore.LookupEntry(factionId); - - if (!factionEntry) + if(!MapManager::IsValidMapCoord(mapid,x,y,z,ort)) { - PSendSysMessage(LANG_COMMAND_FACTION_UNKNOWN, factionId); + PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid); SetSentErrorMessage(true); return false; } - if (factionEntry->reputationListID < 0) + // stop flight if need + if(_player->isInFlight()) { - PSendSysMessage(LANG_COMMAND_FACTION_NOREP_ERROR, factionEntry->name[m_session->GetSessionDbcLocale()], factionId); - SetSentErrorMessage(true); - return false; + _player->GetMotionMaster()->MovementExpired(); + _player->m_taxi.ClearTaxiDestinations(); } + // save only in non-flight case + else + _player->SaveRecallPosition(); - target->SetFactionReputation(factionEntry,amount); - PSendSysMessage(LANG_COMMAND_MODIFY_REP, factionEntry->name[m_session->GetSessionDbcLocale()], factionId, target->GetName(), target->GetReputation(factionId)); + _player->TeleportTo(mapid, x, y, z, ort); return true; } -bool ChatHandler::HandleNameCommand(const char* args) +bool ChatHandler::HandleGameObjectTargetCommand(const char* args) { - /* Temp. disabled - if(!*args) + Player* pl = m_session->GetPlayer(); + QueryResult *result; + GameEventMgr::ActiveEvents const& activeEventsList = gameeventmgr.GetActiveEventList(); + if(*args) + { + // number or [name] Shift-click form |color|Hgameobject_entry:go_id|h[name]|h|r + char* cId = extractKeyFromLink((char*)args,"Hgameobject_entry"); + if(!cId) return false; - if(strlen((char*)args)>75) + uint32 id = atol(cId); + + if(id) + result = WorldDatabase.PQuery("SELECT guid, id, position_x, position_y, position_z, orientation, map, (POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) AS order_ FROM gameobject WHERE map = '%i' AND id = '%u' ORDER BY order_ ASC LIMIT 1", + pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(), pl->GetMapId(),id); + else { - PSendSysMessage(LANG_TOO_LONG_NAME, strlen((char*)args)-75); - return true; + std::string name = cId; + WorldDatabase.escape_string(name); + result = WorldDatabase.PQuery( + "SELECT guid, id, position_x, position_y, position_z, orientation, map, (POW(position_x - %f, 2) + POW(position_y - %f, 2) + POW(position_z - %f, 2)) AS order_ " + "FROM gameobject,gameobject_template WHERE gameobject_template.entry = gameobject.id AND map = %i AND name "_LIKE_" "_CONCAT3_("'%%'","'%s'","'%%'")" ORDER BY order_ ASC LIMIT 1", + pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(), pl->GetMapId(),name.c_str()); } + } + else + { + std::ostringstream eventFilter; + eventFilter << " AND (event IS NULL "; + bool initString = true; - for (uint8 i = 0; i < strlen(args); i++) + for (GameEventMgr::ActiveEvents::const_iterator itr = activeEventsList.begin(); itr != activeEventsList.end(); ++itr) { - if(!isalpha(args[i]) && args[i]!=' ') + if (initString) { - SendSysMessage(LANG_CHARS_ONLY); - return false; + eventFilter << "OR event IN (" <<*itr; + initString =false; } + else + eventFilter << "," << *itr; } - uint64 guid; - guid = m_session->GetPlayer()->GetSelection(); - if (guid == 0) - { - SendSysMessage(LANG_NO_SELECTION); - return true; - } - - Creature* pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), guid); - - if(!pCreature) - { - SendSysMessage(LANG_SELECT_CREATURE); - return true; - } - - pCreature->SetName(args); - uint32 idname = objmgr.AddCreatureTemplate(pCreature->GetName()); - pCreature->SetUInt32Value(OBJECT_FIELD_ENTRY, idname); - - pCreature->SaveToDB(); - */ - - return true; -} - -bool ChatHandler::HandleSubNameCommand(const char* /*args*/) -{ - /* Temp. disabled - - if(!*args) - args = ""; - - if(strlen((char*)args)>75) - { + if (!initString) + eventFilter << "))"; + else + eventFilter << ")"; - PSendSysMessage(LANG_TOO_LONG_SUBNAME, strlen((char*)args)-75); - return true; + result = WorldDatabase.PQuery("SELECT gameobject.guid, id, position_x, position_y, position_z, orientation, map, " + "(POW(position_x - %f, 2) + POW(position_y - %f, 2) + POW(position_z - %f, 2)) AS order_ FROM gameobject " + "LEFT OUTER JOIN game_event_gameobject on gameobject.guid=game_event_gameobject.guid WHERE map = '%i' %s ORDER BY order_ ASC LIMIT 10", + m_session->GetPlayer()->GetPositionX(), m_session->GetPlayer()->GetPositionY(), m_session->GetPlayer()->GetPositionZ(), m_session->GetPlayer()->GetMapId(),eventFilter.str().c_str()); } - for (uint8 i = 0; i < strlen(args); i++) - { - if(!isalpha(args[i]) && args[i]!=' ') - { - SendSysMessage(LANG_CHARS_ONLY); - return false; - } - } - uint64 guid; - guid = m_session->GetPlayer()->GetSelection(); - if (guid == 0) + if (!result) { - SendSysMessage(LANG_NO_SELECTION); + SendSysMessage(LANG_COMMAND_TARGETOBJNOTFOUND); return true; } - Creature* pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), guid); + bool found = false; + float x, y, z, o; + uint32 lowguid, id; + uint16 mapid, pool_id; - if(!pCreature) + do { - SendSysMessage(LANG_SELECT_CREATURE); - return true; - } - - uint32 idname = objmgr.AddCreatureSubName(pCreature->GetName(),args,pCreature->GetUInt32Value(UNIT_FIELD_DISPLAYID)); - pCreature->SetUInt32Value(OBJECT_FIELD_ENTRY, idname); - - pCreature->SaveToDB(); - */ - return true; -} - -//move item to other slot -bool ChatHandler::HandleItemMoveCommand(const char* args) -{ - if(!*args) - return false; - uint8 srcslot, dstslot; - - char* pParam1 = strtok((char*)args, " "); - if (!pParam1) - return false; - - char* pParam2 = strtok(NULL, " "); - if (!pParam2) - return false; - - srcslot = (uint8)atoi(pParam1); - dstslot = (uint8)atoi(pParam2); - - if(srcslot==dstslot) - return true; - - if(!m_session->GetPlayer()->IsValidPos(INVENTORY_SLOT_BAG_0,srcslot)) - return false; - - if(!m_session->GetPlayer()->IsValidPos(INVENTORY_SLOT_BAG_0,dstslot)) - return false; - - uint16 src = ((INVENTORY_SLOT_BAG_0 << 8) | srcslot); - uint16 dst = ((INVENTORY_SLOT_BAG_0 << 8) | dstslot); - - m_session->GetPlayer()->SwapItem( src, dst ); - - return true; -} - -//add spawn of creature -bool ChatHandler::HandleNpcAddCommand(const char* args) -{ - if(!*args) - return false; - char* charID = strtok((char*)args, " "); - if (!charID) - return false; - - char* team = strtok(NULL, " "); - int32 teamval = 0; - if (team) { teamval = atoi(team); } - if (teamval < 0) { teamval = 0; } - - uint32 id = atoi(charID); + Field *fields = result->Fetch(); + lowguid = fields[0].GetUInt32(); + id = fields[1].GetUInt32(); + x = fields[2].GetFloat(); + y = fields[3].GetFloat(); + z = fields[4].GetFloat(); + o = fields[5].GetFloat(); + mapid = fields[6].GetUInt16(); + pool_id = poolhandler.IsPartOfAPool(lowguid, TYPEID_GAMEOBJECT); + if (!pool_id || (pool_id && poolhandler.IsSpawnedObject(pool_id, lowguid, TYPEID_GAMEOBJECT))) + found = true; + } while( result->NextRow() && (!found) ); - Player *chr = m_session->GetPlayer(); - float x = chr->GetPositionX(); - float y = chr->GetPositionY(); - float z = chr->GetPositionZ(); - float o = chr->GetOrientation(); - Map *map = chr->GetMap(); + delete result; - Creature* pCreature = new Creature; - if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, id, (uint32)teamval)) + if (!found) { - delete pCreature; + PSendSysMessage(LANG_GAMEOBJECT_NOT_EXIST,id); return false; } - pCreature->Relocate(x,y,z,o); + GameObjectInfo const* goI = objmgr.GetGameObjectInfo(id); - if(!pCreature->IsPositionValid()) + if (!goI) { - sLog.outError("ERROR: Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY()); - delete pCreature; + PSendSysMessage(LANG_GAMEOBJECT_NOT_EXIST,id); return false; } - pCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode())); - - uint32 db_guid = pCreature->GetDBTableGUIDLow(); - - // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells(); - pCreature->LoadFromDB(db_guid, map); - - map->Add(pCreature); - objmgr.AddCreatureToGrid(db_guid, objmgr.GetCreatureData(db_guid)); - return true; -} + GameObject* target = ObjectAccessor::GetGameObject(*m_session->GetPlayer(),MAKE_NEW_GUID(lowguid,id,HIGHGUID_GAMEOBJECT)); -bool ChatHandler::HandleNpcDeleteCommand(const char* args) -{ - Creature* unit = NULL; + PSendSysMessage(LANG_GAMEOBJECT_DETAIL, lowguid, goI->name, lowguid, id, x, y, z, mapid, o); - if(*args) + if(target) { - // number or [name] Shift-click form |color|Hcreature:creature_guid|h[name]|h|r - char* cId = extractKeyFromLink((char*)args,"Hcreature"); - if(!cId) - return false; - - uint32 lowguid = atoi(cId); - if(!lowguid) - return false; + int32 curRespawnDelay = target->GetRespawnTimeEx()-time(NULL); + if(curRespawnDelay < 0) + curRespawnDelay = 0; - if (CreatureData const* cr_data = objmgr.GetCreatureData(lowguid)) - unit = ObjectAccessor::GetCreature(*m_session->GetPlayer(), MAKE_NEW_GUID(lowguid, cr_data->id, HIGHGUID_UNIT)); - } - else - unit = getSelectedCreature(); + std::string curRespawnDelayStr = secsToTimeString(curRespawnDelay,true); + std::string defRespawnDelayStr = secsToTimeString(target->GetRespawnDelay(),true); - if(!unit || unit->isPet() || unit->isTotem()) - { - SendSysMessage(LANG_SELECT_CREATURE); - SetSentErrorMessage(true); - return false; + PSendSysMessage(LANG_COMMAND_RAWPAWNTIMES, defRespawnDelayStr.c_str(),curRespawnDelayStr.c_str()); } - - // Delete the creature - unit->CombatStop(); - unit->DeleteFromDB(); - unit->CleanupsBeforeDelete(); - unit->AddObjectToRemoveList(); - - SendSysMessage(LANG_COMMAND_DELCREATMESSAGE); - return true; } //delete object by selection or guid -bool ChatHandler::HandleDelObjectCommand(const char* args) +bool ChatHandler::HandleGameObjectDeleteCommand(const char* args) { // number or [name] Shift-click form |color|Hgameobject:go_guid|h[name]|h|r char* cId = extractKeyFromLink((char*)args,"Hgameobject"); @@ -1016,7 +618,7 @@ bool ChatHandler::HandleDelObjectCommand(const char* args) } //turn selected object -bool ChatHandler::HandleTurnObjectCommand(const char* args) +bool ChatHandler::HandleGameObjectTurnCommand(const char* args) { // number or [name] Shift-click form |color|Hgameobject:go_id|h[name]|h|r char* cId = extractKeyFromLink((char*)args,"Hgameobject"); @@ -1053,109 +655,24 @@ bool ChatHandler::HandleTurnObjectCommand(const char* args) o = chr->GetOrientation(); } - float rot2 = sin(o/2); - float rot3 = cos(o/2); - - Map* map = MapManager::Instance().GetMap(obj->GetMapId(),obj); + Map* map = obj->GetMap(); map->Remove(obj,false); obj->Relocate(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), o); - - obj->SetFloatValue(GAMEOBJECT_FACING, o); - obj->SetFloatValue(GAMEOBJECT_ROTATION+2, rot2); - obj->SetFloatValue(GAMEOBJECT_ROTATION+3, rot3); + obj->UpdateRotationFields(); map->Add(obj); obj->SaveToDB(); obj->Refresh(); - PSendSysMessage(LANG_COMMAND_TURNOBJMESSAGE, obj->GetGUIDLow(), o); - - return true; -} - -//move selected creature -bool ChatHandler::HandleNpcMoveCommand(const char* args) -{ - uint32 lowguid = 0; - - Creature* pCreature = getSelectedCreature(); - - if(!pCreature) - { - // number or [name] Shift-click form |color|Hcreature:creature_guid|h[name]|h|r - char* cId = extractKeyFromLink((char*)args,"Hcreature"); - if(!cId) - return false; - - uint32 lowguid = atoi(cId); - - /* FIXME: impossibel without entry - if(lowguid) - pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_GUID(lowguid,HIGHGUID_UNIT)); - */ - - // Attempting creature load from DB data - if(!pCreature) - { - CreatureData const* data = objmgr.GetCreatureData(lowguid); - if(!data) - { - PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowguid); - SetSentErrorMessage(true); - return false; - } + PSendSysMessage(LANG_COMMAND_TURNOBJMESSAGE, obj->GetGUIDLow(), obj->GetGOInfo()->name, obj->GetGUIDLow(), o); - uint32 map_id = data->mapid; - - if(m_session->GetPlayer()->GetMapId()!=map_id) - { - PSendSysMessage(LANG_COMMAND_CREATUREATSAMEMAP, lowguid); - SetSentErrorMessage(true); - return false; - } - } - else - { - lowguid = pCreature->GetDBTableGUIDLow(); - } - } - else - { - lowguid = pCreature->GetDBTableGUIDLow(); - } - - float x = m_session->GetPlayer()->GetPositionX(); - float y = m_session->GetPlayer()->GetPositionY(); - float z = m_session->GetPlayer()->GetPositionZ(); - float o = m_session->GetPlayer()->GetOrientation(); - - if (pCreature) - { - if(CreatureData const* data = objmgr.GetCreatureData(pCreature->GetDBTableGUIDLow())) - { - const_cast<CreatureData*>(data)->posX = x; - const_cast<CreatureData*>(data)->posY = y; - const_cast<CreatureData*>(data)->posZ = z; - const_cast<CreatureData*>(data)->orientation = o; - } - MapManager::Instance().GetMap(pCreature->GetMapId(),pCreature)->CreatureRelocation(pCreature,x, y, z,o); - pCreature->GetMotionMaster()->Initialize(); - if(pCreature->isAlive()) // dead creature will reset movement generator at respawn - { - pCreature->setDeathState(JUST_DIED); - pCreature->Respawn(); - } - } - - WorldDatabase.PExecuteLog("UPDATE creature SET position_x = '%f', position_y = '%f', position_z = '%f', orientation = '%f' WHERE guid = '%u'", x, y, z, o, lowguid); - PSendSysMessage(LANG_COMMAND_CREATUREMOVED); return true; } //move selected object -bool ChatHandler::HandleMoveObjectCommand(const char* args) +bool ChatHandler::HandleGameObjectMoveCommand(const char* args) { // number or [name] Shift-click form |color|Hgameobject:go_guid|h[name]|h|r char* cId = extractKeyFromLink((char*)args,"Hgameobject"); @@ -1187,7 +704,7 @@ bool ChatHandler::HandleMoveObjectCommand(const char* args) { Player *chr = m_session->GetPlayer(); - Map* map = MapManager::Instance().GetMap(obj->GetMapId(),obj); + Map* map = obj->GetMap(); map->Remove(obj,false); obj->Relocate(chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), obj->GetOrientation()); @@ -1213,7 +730,7 @@ bool ChatHandler::HandleMoveObjectCommand(const char* args) return false; } - Map* map = MapManager::Instance().GetMap(obj->GetMapId(),obj); + Map* map = obj->GetMap(); map->Remove(obj,false); obj->Relocate(x, y, z, obj->GetOrientation()); @@ -1227,25 +744,422 @@ bool ChatHandler::HandleMoveObjectCommand(const char* args) obj->SaveToDB(); obj->Refresh(); - PSendSysMessage(LANG_COMMAND_MOVEOBJMESSAGE, obj->GetGUIDLow()); + PSendSysMessage(LANG_COMMAND_MOVEOBJMESSAGE, obj->GetGUIDLow(), obj->GetGOInfo()->name, obj->GetGUIDLow()); return true; } -//demorph player or unit -bool ChatHandler::HandleDeMorphCommand(const char* /*args*/) +//spawn go +bool ChatHandler::HandleGameObjectAddCommand(const char* args) { - Unit *target = getSelectedUnit(); + if (!*args) + return false; + + // number or [name] Shift-click form |color|Hgameobject_entry:go_id|h[name]|h|r + char* cId = extractKeyFromLink((char*)args,"Hgameobject_entry"); + if(!cId) + return false; + + uint32 id = atol(cId); + if(!id) + return false; + + char* spawntimeSecs = strtok(NULL, " "); + + const GameObjectInfo *goI = objmgr.GetGameObjectInfo(id); + + if (!goI) + { + PSendSysMessage(LANG_GAMEOBJECT_NOT_EXIST,id); + SetSentErrorMessage(true); + return false; + } + + Player *chr = m_session->GetPlayer(); + float x = float(chr->GetPositionX()); + float y = float(chr->GetPositionY()); + float z = float(chr->GetPositionZ()); + float o = float(chr->GetOrientation()); + Map *map = chr->GetMap(); + + GameObject* pGameObj = new GameObject; + uint32 db_lowGUID = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT); + + if(!pGameObj->Create(db_lowGUID, goI->id, map, chr->GetPhaseMaskForSpawn(), x, y, z, o, 0.0f, 0.0f, 0.0f, 0.0f, 0, 1)) + { + delete pGameObj; + return false; + } + + if( spawntimeSecs ) + { + uint32 value = atoi((char*)spawntimeSecs); + pGameObj->SetRespawnTime(value); + //sLog.outDebug("*** spawntimeSecs: %d", value); + } + + // fill the gameobject data and save to the db + pGameObj->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()),chr->GetPhaseMaskForSpawn()); + + // this will generate a new guid if the object is in an instance + if(!pGameObj->LoadFromDB(db_lowGUID, map)) + { + delete pGameObj; + return false; + } + + sLog.outDebug(GetMangosString(LANG_GAMEOBJECT_CURRENT), goI->name, db_lowGUID, x, y, z, o); + + map->Add(pGameObj); + + // TODO: is it really necessary to add both the real and DB table guid here ? + objmgr.AddGameobjectToGrid(db_lowGUID, objmgr.GetGOData(db_lowGUID)); + + PSendSysMessage(LANG_GAMEOBJECT_ADD,id,goI->name,db_lowGUID,x,y,z); + return true; +} + +//set pahsemask for selected object +bool ChatHandler::HandleGameObjectPhaseCommand(const char* args) +{ + // number or [name] Shift-click form |color|Hgameobject:go_id|h[name]|h|r + char* cId = extractKeyFromLink((char*)args,"Hgameobject"); + if(!cId) + return false; + + uint32 lowguid = atoi(cId); + if(!lowguid) + return false; + + GameObject* obj = NULL; + + // by DB guid + if (GameObjectData const* go_data = objmgr.GetGOData(lowguid)) + obj = GetObjectGlobalyWithGuidOrNearWithDbGuid(lowguid,go_data->id); + + if(!obj) + { + PSendSysMessage(LANG_COMMAND_OBJNOTFOUND, lowguid); + SetSentErrorMessage(true); + return false; + } + + char* phaseStr = strtok (NULL, " "); + uint32 phasemask = phaseStr? atoi(phaseStr) : 0; + if ( phasemask == 0 ) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + obj->SetPhaseMask(phasemask,true); + obj->SaveToDB(); + return true; +} + +bool ChatHandler::HandleGameObjectNearCommand(const char* args) +{ + float distance = (!*args) ? 10 : atol(args); + uint32 count = 0; + + Player* pl = m_session->GetPlayer(); + QueryResult *result = WorldDatabase.PQuery("SELECT guid, id, position_x, position_y, position_z, map, " + "(POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) AS order_ " + "FROM gameobject WHERE map='%u' AND (POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) <= '%f' ORDER BY order_", + pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(), + pl->GetMapId(),pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(),distance*distance); + + if (result) + { + do + { + Field *fields = result->Fetch(); + uint32 guid = fields[0].GetUInt32(); + uint32 entry = fields[1].GetUInt32(); + float x = fields[2].GetFloat(); + float y = fields[3].GetFloat(); + float z = fields[4].GetFloat(); + int mapid = fields[5].GetUInt16(); + + GameObjectInfo const * gInfo = objmgr.GetGameObjectInfo(entry); + + if(!gInfo) + continue; + + PSendSysMessage(LANG_GO_LIST_CHAT, guid, guid, gInfo->name, x, y, z, mapid); + + ++count; + } while (result->NextRow()); + + delete result; + } + + PSendSysMessage(LANG_COMMAND_NEAROBJMESSAGE,distance,count); + return true; +} + +bool ChatHandler::HandleGUIDCommand(const char* /*args*/) +{ + uint64 guid = m_session->GetPlayer()->GetSelection(); + + if (guid == 0) + { + SendSysMessage(LANG_NO_SELECTION); + SetSentErrorMessage(true); + return false; + } + + PSendSysMessage(LANG_OBJECT_GUID, GUID_LOPART(guid), GUID_HIPART(guid)); + return true; +} + +bool ChatHandler::HandleLookupFactionCommand(const char* args) +{ + if (!*args) + return false; + + // Can be NULL at console call + Player *target = getSelectedPlayer (); + + std::string namepart = args; + std::wstring wnamepart; + + if (!Utf8toWStr (namepart,wnamepart)) + return false; + + // converting string that we try to find to lower case + wstrToLower (wnamepart); + + uint32 counter = 0; // Counter for figure out that we found smth. + + for (uint32 id = 0; id < sFactionStore.GetNumRows(); ++id) + { + FactionEntry const *factionEntry = sFactionStore.LookupEntry (id); + if (factionEntry) + { + FactionState const* repState = target ? target->GetReputationMgr().GetState(factionEntry) : NULL; + + int loc = m_session ? m_session->GetSessionDbcLocale() : sWorld.GetDefaultDbcLocale(); + std::string name = factionEntry->name[loc]; + if(name.empty()) + continue; + + if (!Utf8FitTo(name, wnamepart)) + { + loc = 0; + for(; loc < MAX_LOCALE; ++loc) + { + if(m_session && loc==m_session->GetSessionDbcLocale()) + continue; + + name = factionEntry->name[loc]; + if(name.empty()) + continue; + + if (Utf8FitTo(name, wnamepart)) + break; + } + } + + if(loc < MAX_LOCALE) + { + // send faction in "id - [faction] rank reputation [visible] [at war] [own team] [unknown] [invisible] [inactive]" format + // or "id - [faction] [no reputation]" format + std::ostringstream ss; + if (m_session) + ss << id << " - |cffffffff|Hfaction:" << id << "|h[" << name << " " << localeNames[loc] << "]|h|r"; + else + ss << id << " - " << name << " " << localeNames[loc]; + + if (repState) // and then target!=NULL also + { + ReputationRank rank = target->GetReputationMgr().GetRank(factionEntry); + std::string rankName = GetMangosString(ReputationRankStrIndex[rank]); + + ss << " " << rankName << "|h|r (" << target->GetReputationMgr().GetReputation(factionEntry) << ")"; + + if(repState->Flags & FACTION_FLAG_VISIBLE) + ss << GetTrinityString(LANG_FACTION_VISIBLE); + if(repState->Flags & FACTION_FLAG_AT_WAR) + ss << GetTrinityString(LANG_FACTION_ATWAR); + if(repState->Flags & FACTION_FLAG_PEACE_FORCED) + ss << GetTrinityString(LANG_FACTION_PEACE_FORCED); + if(repState->Flags & FACTION_FLAG_HIDDEN) + ss << GetTrinityString(LANG_FACTION_HIDDEN); + if(repState->Flags & FACTION_FLAG_INVISIBLE_FORCED) + ss << GetTrinityString(LANG_FACTION_INVISIBLE_FORCED); + if(repState->Flags & FACTION_FLAG_INACTIVE) + ss << GetTrinityString(LANG_FACTION_INACTIVE); + } + else + ss << GetTrinityString(LANG_FACTION_NOREPUTATION); + + SendSysMessage(ss.str().c_str()); + counter++; + } + } + } + + if (counter == 0) // if counter == 0 then we found nth + SendSysMessage(LANG_COMMAND_FACTION_NOTFOUND); + return true; +} + +bool ChatHandler::HandleModifyRepCommand(const char * args) +{ + if (!*args) return false; + + Player* target = NULL; + target = getSelectedPlayer(); + if(!target) - target = m_session->GetPlayer(); + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } - target->DeMorph(); + // check online security + if (HasLowerSecurity(target, 0)) + return false; + + char* factionTxt = extractKeyFromLink((char*)args,"Hfaction"); + if(!factionTxt) + return false; + + uint32 factionId = atoi(factionTxt); + + int32 amount = 0; + char *rankTxt = strtok(NULL, " "); + if (!factionTxt || !rankTxt) + return false; + + amount = atoi(rankTxt); + if ((amount == 0) && (rankTxt[0] != '-') && !isdigit(rankTxt[0])) + { + std::string rankStr = rankTxt; + std::wstring wrankStr; + if(!Utf8toWStr(rankStr,wrankStr)) + return false; + wstrToLower( wrankStr ); + + int r = 0; + amount = -42000; + for (; r < MAX_REPUTATION_RANK; ++r) + { + std::string rank = GetTrinityString(ReputationRankStrIndex[r]); + if(rank.empty()) + continue; + + std::wstring wrank; + if(!Utf8toWStr(rank,wrank)) + continue; + + wstrToLower(wrank); + + if(wrank.substr(0,wrankStr.size())==wrankStr) + { + char *deltaTxt = strtok(NULL, " "); + if (deltaTxt) + { + int32 delta = atoi(deltaTxt); + if ((delta < 0) || (delta > ReputationMgr::PointsInRank[r] -1)) + { + PSendSysMessage(LANG_COMMAND_FACTION_DELTA, (ReputationMgr::PointsInRank[r]-1)); + SetSentErrorMessage(true); + return false; + } + amount += delta; + } + break; + } + amount += ReputationMgr::PointsInRank[r]; + } + if (r >= MAX_REPUTATION_RANK) + { + PSendSysMessage(LANG_COMMAND_FACTION_INVPARAM, rankTxt); + SetSentErrorMessage(true); + return false; + } + } + + FactionEntry const *factionEntry = sFactionStore.LookupEntry(factionId); + if (!factionEntry) + { + PSendSysMessage(LANG_COMMAND_FACTION_UNKNOWN, factionId); + SetSentErrorMessage(true); + return false; + } + + if (factionEntry->reputationListID < 0) + { + PSendSysMessage(LANG_COMMAND_FACTION_NOREP_ERROR, factionEntry->name[m_session->GetSessionDbcLocale()], factionId); + SetSentErrorMessage(true); + return false; + } + + target->GetReputationMgr().SetReputation(factionEntry,amount); + PSendSysMessage(LANG_COMMAND_MODIFY_REP, factionEntry->name[m_session->GetSessionDbcLocale()], factionId, + GetNameLink(target).c_str(), target->GetReputationMgr().GetReputation(factionEntry)); + return true; +} + +//-----------------------Npc Commands----------------------- +//add spawn of creature +bool ChatHandler::HandleNpcAddCommand(const char* args) +{ + if(!*args) + return false; + char* charID = extractKeyFromLink((char*)args,"Hcreature_entry"); + if(!charID) + return false; + + char* team = strtok(NULL, " "); + int32 teamval = 0; + if (team) { teamval = atoi(team); } + if (teamval < 0) { teamval = 0; } + + uint32 id = atoi(charID); + + Player *chr = m_session->GetPlayer(); + float x = chr->GetPositionX(); + float y = chr->GetPositionY(); + float z = chr->GetPositionZ(); + float o = chr->GetOrientation(); + Map *map = chr->GetMap(); + + Creature* pCreature = new Creature; + if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMaskForSpawn(), id, (uint32)teamval)) + { + delete pCreature; + return false; + } + + pCreature->Relocate(x,y,z,o); + + if(!pCreature->IsPositionValid()) + { + sLog.outError("Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY()); + delete pCreature; + return false; + } + + pCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); + + uint32 db_guid = pCreature->GetDBTableGUIDLow(); + + // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells(); + pCreature->LoadFromDB(db_guid, map); + + map->Add(pCreature); + objmgr.AddCreatureToGrid(db_guid, objmgr.GetCreatureData(db_guid)); return true; } //add item in vendorlist -bool ChatHandler::HandleAddVendorItemCommand(const char* args) +bool ChatHandler::HandleNpcAddVendorItemCommand(const char* args) { if (!*args) return false; @@ -1292,7 +1206,7 @@ bool ChatHandler::HandleAddVendorItemCommand(const char* args) } //del item from vendor list -bool ChatHandler::HandleDelVendorItemCommand(const char* args) +bool ChatHandler::HandleNpcDelVendorItemCommand(const char* args) { if (!*args) return false; @@ -1314,7 +1228,6 @@ bool ChatHandler::HandleDelVendorItemCommand(const char* args) } uint32 itemId = atol(pitem); - if(!objmgr.RemoveVendorItem(vendor->GetEntry(),itemId)) { PSendSysMessage(LANG_ITEM_NOT_IN_LIST,itemId); @@ -1370,6 +1283,8 @@ bool ChatHandler::HandleNpcAddMoveCommand(const char* args) Player* player = m_session->GetPlayer(); + //WaypointMgr.AddLastNode(lowguid, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetOrientation(), wait, 0); + // update movement type WorldDatabase.PExecuteLog("UPDATE creature SET MovementType = '%u' WHERE guid = '%u'", WAYPOINT_MOTION_TYPE,lowguid); if(pCreature && pCreature->GetWaypointPath()) @@ -1384,10 +1299,193 @@ bool ChatHandler::HandleNpcAddMoveCommand(const char* args) pCreature->SaveToDB(); } + SendSysMessage(LANG_WAYPOINT_ADDED); + return true; } -/** +//change level of creature or pet +bool ChatHandler::HandleNpcChangeLevelCommand(const char* args) +{ + if (!*args) + return false; + + uint8 lvl = (uint8) atoi((char*)args); + if ( lvl < 1 || lvl > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) + 3) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + Creature* pCreature = getSelectedCreature(); + if(!pCreature) + { + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + if(pCreature->isPet()) + { + ((Pet*)pCreature)->GivePetLevel(lvl); + } + else + { + pCreature->SetMaxHealth( 100 + 30*lvl); + pCreature->SetHealth( 100 + 30*lvl); + pCreature->SetLevel( lvl); + pCreature->SaveToDB(); + } + + return true; +} + +//set npcflag of creature +bool ChatHandler::HandleNpcFlagCommand(const char* args) +{ + if (!*args) + return false; + + uint32 npcFlags = (uint32) atoi((char*)args); + + Creature* pCreature = getSelectedCreature(); + + if(!pCreature) + { + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + pCreature->SetUInt32Value(UNIT_NPC_FLAGS, npcFlags); + + WorldDatabase.PExecuteLog("UPDATE creature_template SET npcflag = '%u' WHERE entry = '%u'", npcFlags, pCreature->GetEntry()); + + SendSysMessage(LANG_VALUE_SAVED_REJOIN); + + return true; +} + +bool ChatHandler::HandleNpcDeleteCommand(const char* args) +{ + Creature* unit = NULL; + + if(*args) + { + // number or [name] Shift-click form |color|Hcreature:creature_guid|h[name]|h|r + char* cId = extractKeyFromLink((char*)args,"Hcreature"); + if(!cId) + return false; + + uint32 lowguid = atoi(cId); + if(!lowguid) + return false; + + if (CreatureData const* cr_data = objmgr.GetCreatureData(lowguid)) + unit = ObjectAccessor::GetCreature(*m_session->GetPlayer(), MAKE_NEW_GUID(lowguid, cr_data->id, HIGHGUID_UNIT)); + } + else + unit = getSelectedCreature(); + + if(!unit || unit->isPet() || unit->isTotem() || unit->isVehicle()) + { + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + // Delete the creature + unit->CombatStop(); + unit->DeleteFromDB(); + unit->CleanupsBeforeDelete(); + unit->AddObjectToRemoveList(); + + SendSysMessage(LANG_COMMAND_DELCREATMESSAGE); + + return true; +} + +//move selected creature +bool ChatHandler::HandleNpcMoveCommand(const char* args) +{ + uint32 lowguid = 0; + + Creature* pCreature = getSelectedCreature(); + + if(!pCreature) + { + // number or [name] Shift-click form |color|Hcreature:creature_guid|h[name]|h|r + char* cId = extractKeyFromLink((char*)args,"Hcreature"); + if(!cId) + return false; + + uint32 lowguid = atoi(cId); + + /* FIXME: impossibel without entry + if(lowguid) + pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_GUID(lowguid,HIGHGUID_UNIT)); + */ + + // Attempting creature load from DB data + if(!pCreature) + { + CreatureData const* data = objmgr.GetCreatureData(lowguid); + if(!data) + { + PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowguid); + SetSentErrorMessage(true); + return false; + } + + uint32 map_id = data->mapid; + + if(m_session->GetPlayer()->GetMapId()!=map_id) + { + PSendSysMessage(LANG_COMMAND_CREATUREATSAMEMAP, lowguid); + SetSentErrorMessage(true); + return false; + } + } + else + { + lowguid = pCreature->GetDBTableGUIDLow(); + } + } + else + { + lowguid = pCreature->GetDBTableGUIDLow(); + } + + float x = m_session->GetPlayer()->GetPositionX(); + float y = m_session->GetPlayer()->GetPositionY(); + float z = m_session->GetPlayer()->GetPositionZ(); + float o = m_session->GetPlayer()->GetOrientation(); + + if (pCreature) + { + if(CreatureData const* data = objmgr.GetCreatureData(pCreature->GetDBTableGUIDLow())) + { + const_cast<CreatureData*>(data)->posX = x; + const_cast<CreatureData*>(data)->posY = y; + const_cast<CreatureData*>(data)->posZ = z; + const_cast<CreatureData*>(data)->orientation = o; + } + pCreature->GetMap()->CreatureRelocation(pCreature,x, y, z,o); + pCreature->GetMotionMaster()->Initialize(); + if(pCreature->isAlive()) // dead creature will reset movement generator at respawn + { + pCreature->setDeathState(JUST_DIED); + pCreature->Respawn(); + } + } + + WorldDatabase.PExecuteLog("UPDATE creature SET position_x = '%f', position_y = '%f', position_z = '%f', orientation = '%f' WHERE guid = '%u'", x, y, z, o, lowguid); + PSendSysMessage(LANG_COMMAND_CREATUREMOVED); + return true; +} + +/**HandleNpcSetMoveTypeCommand * Set the movement type for an NPC.<br/> * <br/> * Valid movement types are: @@ -1502,6 +1600,10 @@ bool ChatHandler::HandleNpcSetMoveTypeCommand(const char* args) else return false; + // update movement type + //if(doNotDelete == false) + // WaypointMgr.DeletePath(lowguid); + if(pCreature) { // update movement type @@ -1527,23 +1629,49 @@ bool ChatHandler::HandleNpcSetMoveTypeCommand(const char* args) } return true; -} // HandleNpcSetMoveTypeCommand +} -//change level of creature or pet -bool ChatHandler::HandleChangeLevelCommand(const char* args) +//set model of creature +bool ChatHandler::HandleNpcSetModelCommand(const char* args) { if (!*args) return false; - uint8 lvl = (uint8) atoi((char*)args); - if ( lvl < 1 || lvl > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) + 3) + uint32 displayId = (uint32) atoi((char*)args); + + Creature *pCreature = getSelectedCreature(); + + if(!pCreature || pCreature->isPet()) { - SendSysMessage(LANG_BAD_VALUE); + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + pCreature->SetDisplayId(displayId); + pCreature->SetNativeDisplayId(displayId); + + pCreature->SaveToDB(); + + return true; +} +//set faction of creature +bool ChatHandler::HandleNpcFactionIdCommand(const char* args) +{ + if (!*args) + return false; + + uint32 factionId = (uint32) atoi((char*)args); + + if (!sFactionTemplateStore.LookupEntry(factionId)) + { + PSendSysMessage(LANG_WRONG_FACTION, factionId); SetSentErrorMessage(true); return false; } Creature* pCreature = getSelectedCreature(); + if(!pCreature) { SendSysMessage(LANG_SELECT_CREATURE); @@ -1551,126 +1679,441 @@ bool ChatHandler::HandleChangeLevelCommand(const char* args) return false; } - if(pCreature->isPet()) + pCreature->setFaction(factionId); + + // faction is set in creature_template - not inside creature + + // update in memory + if(CreatureInfo const *cinfo = pCreature->GetCreatureInfo()) { - ((Pet*)pCreature)->GivePetLevel(lvl); + const_cast<CreatureInfo*>(cinfo)->faction_A = factionId; + const_cast<CreatureInfo*>(cinfo)->faction_H = factionId; + } + + // and DB + WorldDatabase.PExecuteLog("UPDATE creature_template SET faction_A = '%u', faction_H = '%u' WHERE entry = '%u'", factionId, factionId, pCreature->GetEntry()); + + return true; +} +//set spawn dist of creature +bool ChatHandler::HandleNpcSpawnDistCommand(const char* args) +{ + if(!*args) + return false; + + float option = atof((char*)args); + if (option < 0.0f) + { + SendSysMessage(LANG_BAD_VALUE); + return false; } + + MovementGeneratorType mtype = IDLE_MOTION_TYPE; + if (option >0.0f) + mtype = RANDOM_MOTION_TYPE; + + Creature *pCreature = getSelectedCreature(); + uint32 u_guidlow = 0; + + if (pCreature) + u_guidlow = pCreature->GetDBTableGUIDLow(); else + return false; + + pCreature->SetRespawnRadius((float)option); + pCreature->SetDefaultMovementType(mtype); + pCreature->GetMotionMaster()->Initialize(); + if(pCreature->isAlive()) // dead creature will reset movement generator at respawn { - pCreature->SetMaxHealth( 100 + 30*lvl); - pCreature->SetHealth( 100 + 30*lvl); - pCreature->SetLevel( lvl); - pCreature->SaveToDB(); + pCreature->setDeathState(JUST_DIED); + pCreature->Respawn(); } + WorldDatabase.PExecuteLog("UPDATE creature SET spawndist=%f, MovementType=%i WHERE guid=%u",option,mtype,u_guidlow); + PSendSysMessage(LANG_COMMAND_SPAWNDIST,option); return true; } - -//set npcflag of creature -bool ChatHandler::HandleNpcFlagCommand(const char* args) +//spawn time handling +bool ChatHandler::HandleNpcSpawnTimeCommand(const char* args) { - if (!*args) + if(!*args) return false; - uint32 npcFlags = (uint32) atoi((char*)args); + char* stime = strtok((char*)args, " "); - Creature* pCreature = getSelectedCreature(); + if (!stime) + return false; - if(!pCreature) + int i_stime = atoi((char*)stime); + + if (i_stime < 0) { - SendSysMessage(LANG_SELECT_CREATURE); + SendSysMessage(LANG_BAD_VALUE); SetSentErrorMessage(true); return false; } - pCreature->SetUInt32Value(UNIT_NPC_FLAGS, npcFlags); + Creature *pCreature = getSelectedCreature(); + uint32 u_guidlow = 0; - WorldDatabase.PExecuteLog("UPDATE creature_template SET npcflag = '%u' WHERE entry = '%u'", npcFlags, pCreature->GetEntry()); + if (pCreature) + u_guidlow = pCreature->GetDBTableGUIDLow(); + else + return false; - SendSysMessage(LANG_VALUE_SAVED_REJOIN); + WorldDatabase.PExecuteLog("UPDATE creature SET spawntimesecs=%i WHERE guid=%u",i_stime,u_guidlow); + pCreature->SetRespawnDelay((uint32)i_stime); + PSendSysMessage(LANG_COMMAND_SPAWNTIME,i_stime); return true; } - -//set model of creature -bool ChatHandler::HandleNpcSetModelCommand(const char* args) +//npc follow handling +bool ChatHandler::HandleNpcFollowCommand(const char* /*args*/) { - if (!*args) + Player *player = m_session->GetPlayer(); + Creature *creature = getSelectedCreature(); + + if(!creature) + { + PSendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); return false; + } - uint32 displayId = (uint32) atoi((char*)args); + // Follow player - Using pet's default dist and angle + creature->GetMotionMaster()->MoveFollow(player, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); - Creature *pCreature = getSelectedCreature(); + PSendSysMessage(LANG_CREATURE_FOLLOW_YOU_NOW, creature->GetName()); + return true; +} +//npc unfollow handling +bool ChatHandler::HandleNpcUnFollowCommand(const char* /*args*/) +{ + Player *player = m_session->GetPlayer(); + Creature *creature = getSelectedCreature(); - if(!pCreature || pCreature->isPet()) + if(!creature) { - SendSysMessage(LANG_SELECT_CREATURE); + PSendSysMessage(LANG_SELECT_CREATURE); SetSentErrorMessage(true); return false; } - pCreature->SetDisplayId(displayId); - pCreature->SetNativeDisplayId(displayId); + if (/*creature->GetMotionMaster()->empty() ||*/ + creature->GetMotionMaster()->GetCurrentMovementGeneratorType ()!=TARGETED_MOTION_TYPE) + { + PSendSysMessage(LANG_CREATURE_NOT_FOLLOW_YOU); + SetSentErrorMessage(true); + return false; + } - pCreature->SaveToDB(); + TargetedMovementGenerator<Creature> const* mgen + = static_cast<TargetedMovementGenerator<Creature> const*>((creature->GetMotionMaster()->top())); + + if(mgen->GetTarget()!=player) + { + PSendSysMessage(LANG_CREATURE_NOT_FOLLOW_YOU); + SetSentErrorMessage(true); + return false; + } + // reset movement + creature->GetMotionMaster()->MovementExpired(true); + + PSendSysMessage(LANG_CREATURE_NOT_FOLLOW_YOU_NOW, creature->GetName()); return true; } +//npc tame handling +bool ChatHandler::HandleNpcTameCommand(const char* /*args*/) +{ + Creature *creatureTarget = getSelectedCreature (); + if (!creatureTarget || creatureTarget->isPet ()) + { + PSendSysMessage (LANG_SELECT_CREATURE); + SetSentErrorMessage (true); + return false; + } -//morph creature or player -bool ChatHandler::HandleMorphCommand(const char* args) + Player *player = m_session->GetPlayer (); + + if(player->GetPetGUID ()) + { + SendSysMessage (LANG_YOU_ALREADY_HAVE_PET); + SetSentErrorMessage (true); + return false; + } + + CreatureInfo const* cInfo = creatureTarget->GetCreatureInfo(); + + if (!cInfo->isTameable ()) + { + PSendSysMessage (LANG_CREATURE_NON_TAMEABLE,cInfo->Entry); + SetSentErrorMessage (true); + return false; + } + + // Everything looks OK, create new pet + Pet* pet = player->CreateTamedPetFrom (creatureTarget); + if (!pet) + { + PSendSysMessage (LANG_CREATURE_NON_TAMEABLE,cInfo->Entry); + SetSentErrorMessage (true); + return false; + } + + // place pet before player + float x,y,z; + player->GetClosePoint (x,y,z,creatureTarget->GetObjectSize (),CONTACT_DISTANCE); + pet->Relocate (x,y,z,M_PI-player->GetOrientation ()); + + // set pet to defensive mode by default (some classes can't control controlled pets in fact). + pet->SetReactState(REACT_DEFENSIVE); + + // calculate proper level + uint32 level = (creatureTarget->getLevel() < (player->getLevel() - 5)) ? (player->getLevel() - 5) : creatureTarget->getLevel(); + + // prepare visual effect for levelup + pet->SetUInt32Value(UNIT_FIELD_LEVEL, level - 1); + + // add to world + pet->GetMap()->Add((Creature*)pet); + + // visual effect for levelup + pet->SetUInt32Value(UNIT_FIELD_LEVEL, level); + + // caster have pet now + player->SetGuardian(pet, true); + + pet->SavePetToDB(PET_SAVE_AS_CURRENT); + player->PetSpellInitialize(); + + return true; +} +//npc phasemask handling +//change phasemask of creature or pet +bool ChatHandler::HandleNpcSetPhaseCommand(const char* args) { if (!*args) return false; - uint16 display_id = (uint16)atoi((char*)args); + uint32 phasemask = (uint32) atoi((char*)args); + if ( phasemask == 0 ) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } - Unit *target = getSelectedUnit(); - if(!target) - target = m_session->GetPlayer(); + Creature* pCreature = getSelectedCreature(); + if(!pCreature) + { + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } - target->SetDisplayId(display_id); + pCreature->SetPhaseMask(phasemask,true); + + if(!pCreature->isPet()) + pCreature->SaveToDB(); return true; } - -//set faction of creature -bool ChatHandler::HandleNpcFactionIdCommand(const char* args) +//npc deathstate handling +bool ChatHandler::HandleNpcSetDeathStateCommand(const char* args) { if (!*args) return false; - uint32 factionId = (uint32) atoi((char*)args); + Creature* pCreature = getSelectedCreature(); + if(!pCreature || pCreature->isPet()) + { + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } - if (!sFactionTemplateStore.LookupEntry(factionId)) + if (strncmp(args, "on", 3) == 0) + pCreature->SetDeadByDefault(true); + else if (strncmp(args, "off", 4) == 0) + pCreature->SetDeadByDefault(false); + else { - PSendSysMessage(LANG_WRONG_FACTION, factionId); + SendSysMessage(LANG_USE_BOL); SetSentErrorMessage(true); return false; } - Creature* pCreature = getSelectedCreature(); + pCreature->SaveToDB(); + pCreature->Respawn(); + + return true; +} + +//TODO: NpcCommands that need to be fixed : + +bool ChatHandler::HandleNpcNameCommand(const char* /*args*/) +{ + /* Temp. disabled + if(!*args) + return false; + + if(strlen((char*)args)>75) + { + PSendSysMessage(LANG_TOO_LONG_NAME, strlen((char*)args)-75); + return true; + } + + for (uint8 i = 0; i < strlen(args); i++) + { + if(!isalpha(args[i]) && args[i]!=' ') + { + SendSysMessage(LANG_CHARS_ONLY); + return false; + } + } + + uint64 guid; + guid = m_session->GetPlayer()->GetSelection(); + if (guid == 0) + { + SendSysMessage(LANG_NO_SELECTION); + return true; + } + + Creature* pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), guid); if(!pCreature) { SendSysMessage(LANG_SELECT_CREATURE); - SetSentErrorMessage(true); - return false; + return true; } - pCreature->setFaction(factionId); + pCreature->SetName(args); + uint32 idname = objmgr.AddCreatureTemplate(pCreature->GetName()); + pCreature->SetUInt32Value(OBJECT_FIELD_ENTRY, idname); - // faction is set in creature_template - not inside creature + pCreature->SaveToDB(); + */ - // update in memory - if(CreatureInfo const *cinfo = pCreature->GetCreatureInfo()) + return true; +} + +bool ChatHandler::HandleNpcSubNameCommand(const char* /*args*/) +{ + /* Temp. disabled + + if(!*args) + args = ""; + + if(strlen((char*)args)>75) { - const_cast<CreatureInfo*>(cinfo)->faction_A = factionId; - const_cast<CreatureInfo*>(cinfo)->faction_H = factionId; + + PSendSysMessage(LANG_TOO_LONG_SUBNAME, strlen((char*)args)-75); + return true; } - // and DB - WorldDatabase.PExecuteLog("UPDATE creature_template SET faction_A = '%u', faction_H = '%u' WHERE entry = '%u'", factionId, factionId, pCreature->GetEntry()); + for (uint8 i = 0; i < strlen(args); i++) + { + if(!isalpha(args[i]) && args[i]!=' ') + { + SendSysMessage(LANG_CHARS_ONLY); + return false; + } + } + uint64 guid; + guid = m_session->GetPlayer()->GetSelection(); + if (guid == 0) + { + SendSysMessage(LANG_NO_SELECTION); + return true; + } + + Creature* pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), guid); + + if(!pCreature) + { + SendSysMessage(LANG_SELECT_CREATURE); + return true; + } + + uint32 idname = objmgr.AddCreatureSubName(pCreature->GetName(),args,pCreature->GetUInt32Value(UNIT_FIELD_DISPLAYID)); + pCreature->SetUInt32Value(OBJECT_FIELD_ENTRY, idname); + + pCreature->SaveToDB(); + */ + return true; +} + +//move item to other slot +bool ChatHandler::HandleItemMoveCommand(const char* args) +{ + if(!*args) + return false; + uint8 srcslot, dstslot; + + char* pParam1 = strtok((char*)args, " "); + if (!pParam1) + return false; + + char* pParam2 = strtok(NULL, " "); + if (!pParam2) + return false; + + srcslot = (uint8)atoi(pParam1); + dstslot = (uint8)atoi(pParam2); + + if(srcslot==dstslot) + return true; + + if(!m_session->GetPlayer()->IsValidPos(INVENTORY_SLOT_BAG_0,srcslot)) + return false; + + if(!m_session->GetPlayer()->IsValidPos(INVENTORY_SLOT_BAG_0,dstslot)) + return false; + + uint16 src = ((INVENTORY_SLOT_BAG_0 << 8) | srcslot); + uint16 dst = ((INVENTORY_SLOT_BAG_0 << 8) | dstslot); + + m_session->GetPlayer()->SwapItem( src, dst ); + + return true; +} + +//demorph player or unit +bool ChatHandler::HandleDeMorphCommand(const char* /*args*/) +{ + Unit *target = getSelectedUnit(); + if(!target) + target = m_session->GetPlayer(); + + + // check online security + else if (target->GetTypeId() == TYPEID_PLAYER && HasLowerSecurity((Player*)target, 0)) + return false; + + target->DeMorph(); + + return true; +} + +//morph creature or player +bool ChatHandler::HandleModifyMorphCommand(const char* args) +{ + if (!*args) + return false; + + uint16 display_id = (uint16)atoi((char*)args); + + Unit *target = getSelectedUnit(); + if(!target) + target = m_session->GetPlayer(); + + // check online security + else if (target->GetTypeId() == TYPEID_PLAYER && HasLowerSecurity((Player*)target, 0)) + return false; + + target->SetDisplayId(display_id); return true; } @@ -1688,7 +2131,7 @@ bool ChatHandler::HandleKickPlayerCommand(const char *args) kicker = m_session->GetPlayer()->GetName(); if(!kickName) - { + { Player* player = getSelectedPlayer(); if(!player) { @@ -1704,14 +2147,16 @@ bool ChatHandler::HandleKickPlayerCommand(const char *args) return false; } + // check online security + if (HasLowerSecurity(player, 0)) + return false; + if(sWorld.getConfig(CONFIG_SHOW_KICK_IN_WORLD) == 1) { - sWorld.SendWorldText(LANG_COMMAND_KICKMESSAGE, player->GetName(), kicker.c_str(), reason.c_str()); } else { - PSendSysMessage(LANG_COMMAND_KICKMESSAGE, player->GetName(), kicker.c_str(), reason.c_str()); } @@ -1719,8 +2164,8 @@ bool ChatHandler::HandleKickPlayerCommand(const char *args) } else { - std::string name = kickName; - if(!normalizePlayerName(name)) + std::string name = extractPlayerNameFromLink((char*)kickName); + if(name.empty()) { SendSysMessage(LANG_PLAYER_NOT_FOUND); SetSentErrorMessage(true); @@ -1742,34 +2187,56 @@ bool ChatHandler::HandleKickPlayerCommand(const char *args) return false; } - if(m_session && player->GetSession()->GetSecurity() > m_session->GetSecurity()) + if(HasLowerSecurity(player, 0)) { SendSysMessage(LANG_YOURS_SECURITY_IS_LOW); //maybe replacement string for this later on SetSentErrorMessage(true); return false; } - if(sWorld.KickPlayer(name.c_str())) + std::string nameLink = playerLink(name); + + if(sWorld.KickPlayer(name)) { if(sWorld.getConfig(CONFIG_SHOW_KICK_IN_WORLD) == 1) { - - sWorld.SendWorldText(LANG_COMMAND_KICKMESSAGE, name.c_str(), kicker.c_str(), reason.c_str()); + sWorld.SendWorldText(LANG_COMMAND_KICKMESSAGE, nameLink.c_str(), kicker.c_str(), reason.c_str()); } else { - PSendSysMessage(LANG_COMMAND_KICKMESSAGE, name.c_str(), kicker.c_str(), reason.c_str()); + PSendSysMessage(LANG_COMMAND_KICKMESSAGE,nameLink.c_str()); } } else { - PSendSysMessage(LANG_COMMAND_KICKNOTFOUNDPLAYER, name.c_str()); + PSendSysMessage(LANG_COMMAND_KICKNOTFOUNDPLAYER,nameLink.c_str()); return false; } } return true; } +//set temporary phase mask for player +bool ChatHandler::HandleModifyPhaseCommand(const char* args) +{ + if (!*args) + return false; + + uint32 phasemask = (uint32)atoi((char*)args); + + Unit *target = getSelectedUnit(); + if(!target) + target = m_session->GetPlayer(); + + // check online security + else if (target->GetTypeId() == TYPEID_PLAYER && HasLowerSecurity((Player*)target, 0)) + return false; + + target->SetPhaseMask(phasemask,true); + + return true; +} + //show info of player bool ChatHandler::HandlePInfoCommand(const char* args) { @@ -1783,12 +2250,8 @@ bool ChatHandler::HandlePInfoCommand(const char* args) if (px) { - name = px; - + name = extractPlayerNameFromLink(px); if(name.empty()) - return false; - - if(!normalizePlayerName(name)) { SendSysMessage(LANG_PLAYER_NOT_FOUND); SetSentErrorMessage(true); @@ -1829,6 +2292,10 @@ bool ChatHandler::HandlePInfoCommand(const char* args) // get additional information from Player object if(target) { + // check online security + if (HasLowerSecurity(target, 0)) + return false; + targetGUID = target->GetGUID(); name = target->GetName(); // re-read for case getSelectedPlayer() target accId = target->GetSession()->GetAccountId(); @@ -1840,6 +2307,11 @@ bool ChatHandler::HandlePInfoCommand(const char* args) // get additional information from DB else { + // check offline security + if (HasLowerSecurity(NULL, targetGUID)) + return false; + + // 0 QueryResult *result = CharacterDatabase.PQuery("SELECT totaltime FROM characters WHERE guid = '%u'", GUID_LOPART(targetGUID)); if (!result) { @@ -1890,7 +2362,9 @@ bool ChatHandler::HandlePInfoCommand(const char* args) delete result; } - PSendSysMessage(LANG_PINFO_ACCOUNT, (target?"":GetTrinityString(LANG_OFFLINE)), name.c_str(), GUID_LOPART(targetGUID), username.c_str(), accId, security, last_ip.c_str(), last_login.c_str(), latency); + std::string nameLink = playerLink(name); + + PSendSysMessage(LANG_PINFO_ACCOUNT, (target?"":GetMangosString(LANG_OFFLINE)), nameLink.c_str(), GUID_LOPART(targetGUID), username.c_str(), accId, security, last_ip.c_str(), last_login.c_str(), latency); std::string timeStr = secsToTimeString(total_player_time,true,true); uint32 gold = money /GOLD; @@ -1908,18 +2382,16 @@ bool ChatHandler::HandlePInfoCommand(const char* args) return false; } - char* FactionName; - for(FactionStateList::const_iterator itr = target->m_factions.begin(); itr != target->m_factions.end(); ++itr) + FactionStateList const& targetFSL = target->GetReputationMgr().GetStateList(); + for(FactionStateList::const_iterator itr = targetFSL.begin(); itr != targetFSL.end(); ++itr) { FactionEntry const *factionEntry = sFactionStore.LookupEntry(itr->second.ID); - if (factionEntry) - FactionName = factionEntry->name[m_session->GetSessionDbcLocale()]; - else - FactionName = "#Not found#"; - ReputationRank rank = target->GetReputationRank(factionEntry); - std::string rankName = GetTrinityString(ReputationRankStrIndex[rank]); + char const* factionName = factionEntry ? factionEntry->name[m_session->GetSessionDbcLocale()] : "#Not found#"; + ReputationRank rank = target->GetReputationMgr().GetRank(factionEntry); + std::string rankName = GetMangosString(ReputationRankStrIndex[rank]); std::ostringstream ss; - ss << itr->second.ID << ": |cffffffff|Hfaction:" << itr->second.ID << "|h[" << FactionName << "]|h|r " << rankName << "|h|r (" << target->GetReputation(factionEntry) << ")"; + ss << itr->second.ID << ": |cffffffff|Hfaction:" << itr->second.ID << "|h[" << factionName << "]|h|r " << rankName << "|h|r (" + << target->GetReputationMgr().GetReputation(factionEntry) << ")"; if(itr->second.Flags & FACTION_FLAG_VISIBLE) ss << GetTrinityString(LANG_FACTION_VISIBLE); @@ -1940,81 +2412,215 @@ bool ChatHandler::HandlePInfoCommand(const char* args) return true; } -//set spawn dist of creature -bool ChatHandler::HandleNpcSpawnDistCommand(const char* args) +/*//show tickets +void ChatHandler::ShowTicket(uint64 guid, char const* text, char const* time) { - if(!*args) - return false; + std::string name; + if(!objmgr.GetPlayerNameByGUID(guid,name)) + name = GetTrinityString(LANG_UNKNOWN); - float option = atof((char*)args); - if (option < 0.0f) + std::string nameLink = playerLink(name); + + PSendSysMessage(LANG_COMMAND_TICKETVIEW, nameLink.c_str(),time,text); +} + +//ticket commands +bool ChatHandler::HandleTicketCommand(const char* args) +{ + char* px = strtok((char*)args, " "); + + // ticket<end> + if (!px) { - SendSysMessage(LANG_BAD_VALUE); - return false; + if(!m_session) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + + size_t count = ticketmgr.GetTicketCount(); + + bool accept = m_session->GetPlayer()->isAcceptTickets(); + + PSendSysMessage(LANG_COMMAND_TICKETCOUNT, count, accept ? GetTrinityString(LANG_ON) : GetTrinityString(LANG_OFF)); + return true; } - MovementGeneratorType mtype = IDLE_MOTION_TYPE; - if (option >0.0f) - mtype = RANDOM_MOTION_TYPE; + // ticket on + if(strncmp(px,"on",3) == 0) + { + if(!m_session) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } - Creature *pCreature = getSelectedCreature(); - uint32 u_guidlow = 0; + m_session->GetPlayer()->SetAcceptTicket(true); + SendSysMessage(LANG_COMMAND_TICKETON); + return true; + } - if (pCreature) - u_guidlow = pCreature->GetDBTableGUIDLow(); - else - return false; + // ticket off + if(strncmp(px,"off",4) == 0) + { + if(!m_session) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } - pCreature->SetRespawnRadius((float)option); - pCreature->SetDefaultMovementType(mtype); - pCreature->GetMotionMaster()->Initialize(); - if(pCreature->isAlive()) // dead creature will reset movement generator at respawn + m_session->GetPlayer()->SetAcceptTicket(false); + SendSysMessage(LANG_COMMAND_TICKETOFF); + return true; + } + + // ticket #num + int num = atoi(px); + if(num > 0) { - pCreature->setDeathState(JUST_DIED); - pCreature->Respawn(); + QueryResult *result = CharacterDatabase.PQuery("SELECT guid,ticket_text,ticket_lastchange FROM character_ticket ORDER BY ticket_id ASC "_OFFSET_, num-1); + + if(!result) + { + PSendSysMessage(LANG_COMMAND_TICKENOTEXIST, num); + SetSentErrorMessage(true); + return false; + } + + Field* fields = result->Fetch(); + + uint32 guid = fields[0].GetUInt32(); + char const* text = fields[1].GetString(); + char const* time = fields[2].GetString(); + + ShowTicket(MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER),text,time); + delete result; + return true; } - WorldDatabase.PExecuteLog("UPDATE creature SET spawndist=%f, MovementType=%i WHERE guid=%u",option,mtype,u_guidlow); - PSendSysMessage(LANG_COMMAND_SPAWNDIST,option); + std::string name = extractPlayerNameFromLink(px); + if(name.empty()) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + + uint64 guid = objmgr.GetPlayerGUIDByName(name); + + if(!guid) + return false; + + // ticket $char_name + GMTicket* ticket = ticketmgr.GetGMTicket(GUID_LOPART(guid)); + if(!ticket) + return false; + + std::string time = TimeToTimestampStr(ticket->GetLastUpdate()); + + ShowTicket(guid, ticket->GetText(), time.c_str()); + return true; } -bool ChatHandler::HandleNpcSpawnTimeCommand(const char* args) +//dell all tickets +bool ChatHandler::HandleDelTicketCommand(const char *args) { - if(!*args) + char* px = strtok((char*)args, " "); + if (!px) return false; - char* stime = strtok((char*)args, " "); + // delticket all + if(strncmp(px,"all",4) == 0) + { + ticketmgr.DeleteAll(); + SendSysMessage(LANG_COMMAND_ALLTICKETDELETED); + return true; + } - if (!stime) - return false; + int num = (uint32)atoi(px); - int i_stime = atoi((char*)stime); + // delticket #num + if(num > 0) + { + QueryResult* result = CharacterDatabase.PQuery("SELECT guid FROM character_ticket ORDER BY ticket_id ASC "_OFFSET_,num-1); + if(!result) + { + PSendSysMessage(LANG_COMMAND_TICKENOTEXIST, num); + SetSentErrorMessage(true); + return false; + } + Field* fields = result->Fetch(); + uint32 guid = fields[0].GetUInt32(); + delete result; - if (i_stime < 0) + ticketmgr.Delete(guid); + + //notify player + if(Player* pl = objmgr.GetPlayer(MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER))) + { + pl->GetSession()->SendGMTicketGetTicket(0x0A, 0); + PSendSysMessage(LANG_COMMAND_TICKETPLAYERDEL, GetNameLink(pl).c_str()); + } + else + PSendSysMessage(LANG_COMMAND_TICKETDEL); + + return true; + } + + std::string name = extractPlayerNameFromLink(px); + if(name.empty()) { - SendSysMessage(LANG_BAD_VALUE); + SendSysMessage(LANG_PLAYER_NOT_FOUND); SetSentErrorMessage(true); return false; } - Creature *pCreature = getSelectedCreature(); - uint32 u_guidlow = 0; + uint64 guid = objmgr.GetPlayerGUIDByName(name); - if (pCreature) - u_guidlow = pCreature->GetDBTableGUIDLow(); - else + if(!guid) return false; - WorldDatabase.PExecuteLog("UPDATE creature SET spawntimesecs=%i WHERE guid=%u",i_stime,u_guidlow); - pCreature->SetRespawnDelay((uint32)i_stime); - PSendSysMessage(LANG_COMMAND_SPAWNTIME,i_stime); + // delticket $char_name + ticketmgr.Delete(GUID_LOPART(guid)); + + // notify players about ticket deleting + if(Player* sender = objmgr.GetPlayer(guid)) + sender->GetSession()->SendGMTicketGetTicket(0x0A,0); + std::string nameLink = playerLink(name); + + PSendSysMessage(LANG_COMMAND_TICKETPLAYERDEL,nameLink.c_str()); return true; -} +}*/ + /////WAYPOINT COMMANDS +/** + * Add a waypoint to a creature. + * + * The user can either select an npc or provide its GUID. + * + * The user can even select a visual waypoint - then the new waypoint + * is placed *after* the selected one - this makes insertion of new + * waypoints possible. + * + * eg: + * .wp add 12345 + * -> adds a waypoint to the npc with the GUID 12345 + * + * .wp add + * -> adds a waypoint to the currently selected creature + * + * + * @param args if the user did not provide a GUID, it is NULL + * + * @return true - command did succeed, false - something went wrong + */ bool ChatHandler::HandleWpAddCommand(const char* args) { sLog.outDebug("DEBUG: HandleWpAddCommand"); @@ -2134,7 +2740,7 @@ bool ChatHandler::HandleWpLoadPathCommand(const char *args) target->LoadPath(pathid); target->SetDefaultMovementType(WAYPOINT_MOTION_TYPE); target->GetMotionMaster()->Initialize(); - target->Say("Path loaded.",0,0); + target->MonsterSay("Path loaded.",0,0); return true; } @@ -2177,7 +2783,7 @@ bool ChatHandler::HandleWpUnLoadPathCommand(const char *args) target->SetDefaultMovementType(IDLE_MOTION_TYPE); target->GetMotionMaster()->MoveTargetedHome(); target->GetMotionMaster()->Initialize(); - target->Say("Path unloaded.",0,0); + target->MonsterSay("Path unloaded.",0,0); return true; } PSendSysMessage("%s%s|r", "|cffff33ff", "Target have no loaded path."); @@ -2569,7 +3175,7 @@ bool ChatHandler::HandleWpModifyCommand(const char* args) wpCreature->AddObjectToRemoveList(); // re-create Creature* wpCreature2 = new Creature; - if (!wpCreature2->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, VISUAL_WAYPOINT, 0)) + if (!wpCreature2->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMaskForSpawn(), VISUAL_WAYPOINT, 0)) { PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, VISUAL_WAYPOINT); delete wpCreature2; @@ -2579,12 +3185,12 @@ bool ChatHandler::HandleWpModifyCommand(const char* args) if(!wpCreature2->IsPositionValid()) { - sLog.outError("ERROR: Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",wpCreature2->GetGUIDLow(),wpCreature2->GetEntry(),wpCreature2->GetPositionX(),wpCreature2->GetPositionY()); + sLog.outError("Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",wpCreature2->GetGUIDLow(),wpCreature2->GetEntry(),wpCreature2->GetPositionX(),wpCreature2->GetPositionY()); delete wpCreature2; return false; } - wpCreature2->SaveToDB(map->GetId(), (1 << map->GetSpawnMode())); + wpCreature2->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells(); wpCreature2->LoadFromDB(wpCreature2->GetDBTableGUIDLow(), map); map->Add(wpCreature2); @@ -2618,7 +3224,6 @@ bool ChatHandler::HandleWpModifyCommand(const char* args) } PSendSysMessage(LANG_WAYPOINT_CHANGED_NO, show_str); - return true; } @@ -2800,7 +3405,7 @@ bool ChatHandler::HandleWpShowCommand(const char* args) float o = chr->GetOrientation(); Creature* wpCreature = new Creature; - if (!wpCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, id, 0)) + if (!wpCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMaskForSpawn(), id, 0)) { PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id); delete wpCreature; @@ -2812,7 +3417,7 @@ bool ChatHandler::HandleWpShowCommand(const char* args) if(!wpCreature->IsPositionValid()) { - sLog.outError("ERROR: Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",wpCreature->GetGUIDLow(),wpCreature->GetEntry(),wpCreature->GetPositionX(),wpCreature->GetPositionY()); + sLog.outError("Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",wpCreature->GetGUIDLow(),wpCreature->GetEntry(),wpCreature->GetPositionX(),wpCreature->GetPositionY()); delete wpCreature; delete result; return false; @@ -2822,7 +3427,8 @@ bool ChatHandler::HandleWpShowCommand(const char* args) // set "wpguid" column to the visual waypoint WorldDatabase.PExecuteLog("UPDATE waypoint_data SET wpguid = '%u' WHERE id = '%u' and point = '%u'", wpCreature->GetGUIDLow(), pathid, point); - wpCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode())); + wpCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); + // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells(); wpCreature->LoadFromDB(wpCreature->GetDBTableGUIDLow(),map); map->Add(wpCreature); @@ -2863,7 +3469,7 @@ bool ChatHandler::HandleWpShowCommand(const char* args) Map *map = chr->GetMap(); Creature* pCreature = new Creature; - if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT),map, id, 0)) + if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT),map, chr->GetPhaseMaskForSpawn(), id, 0)) { PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id); delete pCreature; @@ -2875,13 +3481,13 @@ bool ChatHandler::HandleWpShowCommand(const char* args) if(!pCreature->IsPositionValid()) { - sLog.outError("ERROR: Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY()); + sLog.outError("Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY()); delete pCreature; delete result; return false; } - pCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode())); + pCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); pCreature->LoadFromDB(pCreature->GetDBTableGUIDLow(), map); map->Add(pCreature); @@ -2928,7 +3534,7 @@ bool ChatHandler::HandleWpShowCommand(const char* args) Map *map = chr->GetMap(); Creature* pCreature = new Creature; - if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, id, 0)) + if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMaskForSpawn(), id, 0)) { PSendSysMessage(LANG_WAYPOINT_NOTCREATED, id); delete pCreature; @@ -2940,13 +3546,13 @@ bool ChatHandler::HandleWpShowCommand(const char* args) if(!pCreature->IsPositionValid()) { - sLog.outError("ERROR: Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY()); + sLog.outError("Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY()); delete pCreature; delete result; return false; } - pCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode())); + pCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); pCreature->LoadFromDB(pCreature->GetDBTableGUIDLow(), map); map->Add(pCreature); @@ -3028,9 +3634,8 @@ bool ChatHandler::HandleRenameCommand(const char* args) if(px) { - oldname = px; - - if(!normalizePlayerName(oldname)) + oldname = extractPlayerNameFromLink(px); + if(oldname.empty()) { SendSysMessage(LANG_PLAYER_NOT_FOUND); SetSentErrorMessage(true); @@ -3057,104 +3662,85 @@ bool ChatHandler::HandleRenameCommand(const char* args) if(target) { - PSendSysMessage(LANG_RENAME_PLAYER, target->GetName()); + // check online security + if (HasLowerSecurity(target, 0)) + return false; + + PSendSysMessage(LANG_RENAME_PLAYER, GetNameLink(target).c_str()); target->SetAtLoginFlag(AT_LOGIN_RENAME); CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '1' WHERE guid = '%u'", target->GetGUIDLow()); } else { - PSendSysMessage(LANG_RENAME_PLAYER_GUID, oldname.c_str(), GUID_LOPART(targetGUID)); + // check offline security + if (HasLowerSecurity(NULL, targetGUID)) + return false; + + std::string oldNameLink = playerLink(oldname); + + PSendSysMessage(LANG_RENAME_PLAYER_GUID, oldNameLink.c_str(), GUID_LOPART(targetGUID)); CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '1' WHERE guid = '%u'", GUID_LOPART(targetGUID)); } return true; } -//spawn go -bool ChatHandler::HandleGameObjectCommand(const char* args) +// customize characters +bool ChatHandler::HandleCustomizeCommand(const char* args) { - if (!*args) - return false; + Player* target = NULL; + uint64 targetGUID = 0; + std::string oldname; - char* pParam1 = strtok((char*)args, " "); - if (!pParam1) - return false; + char* px = strtok((char*)args, " "); - uint32 id = atoi((char*)pParam1); - if(!id) - return false; + if(px) + { + oldname = extractPlayerNameFromLink(px); + if(oldname.empty()) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } - char* spawntimeSecs = strtok(NULL, " "); + target = objmgr.GetPlayer(oldname.c_str()); - const GameObjectInfo *goI = objmgr.GetGameObjectInfo(id); + if (!target) + targetGUID = objmgr.GetPlayerGUIDByName(oldname); + } - if (!goI) + if(!target && !targetGUID) { - PSendSysMessage(LANG_GAMEOBJECT_NOT_EXIST,id); - SetSentErrorMessage(true); - return false; + target = getSelectedPlayer(); } - Player *chr = m_session->GetPlayer(); - float x = float(chr->GetPositionX()); - float y = float(chr->GetPositionY()); - float z = float(chr->GetPositionZ()); - float o = float(chr->GetOrientation()); - Map *map = chr->GetMap(); - - float rot2 = sin(o/2); - float rot3 = cos(o/2); - - GameObject* pGameObj = new GameObject; - uint32 db_lowGUID = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT); - - if(!pGameObj->Create(db_lowGUID, goI->id, map, x, y, z, o, 0, 0, rot2, rot3, 0, 1)) + if(!target && !targetGUID) { - delete pGameObj; + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); return false; } - if( spawntimeSecs ) + if(target) { - uint32 value = atoi((char*)spawntimeSecs); - pGameObj->SetRespawnTime(value); - //sLog.outDebug("*** spawntimeSecs: %d", value); + PSendSysMessage(LANG_CUSTOMIZE_PLAYER, GetNameLink(target).c_str()); + target->SetAtLoginFlag(AT_LOGIN_CUSTOMIZE); + CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '8' WHERE guid = '%u'", target->GetGUIDLow()); } - - // fill the gameobject data and save to the db - pGameObj->SaveToDB(map->GetId(), (1 << map->GetSpawnMode())); - - // this will generate a new guid if the object is in an instance - if(!pGameObj->LoadFromDB(db_lowGUID, map)) + else { - delete pGameObj; - return false; - } - - sLog.outDebug(GetTrinityString(LANG_GAMEOBJECT_CURRENT), goI->name, db_lowGUID, x, y, z, o); + std::string oldNameLink = playerLink(oldname); - map->Add(pGameObj); - - // TODO: is it really necessary to add both the real and DB table guid here ? - objmgr.AddGameobjectToGrid(db_lowGUID, objmgr.GetGOData(db_lowGUID)); - - PSendSysMessage(LANG_GAMEOBJECT_ADD,id,goI->name,db_lowGUID,x,y,z); - return true; -} - -//show animation -bool ChatHandler::HandleAnimCommand(const char* args) -{ - if (!*args) - return false; + PSendSysMessage(LANG_CUSTOMIZE_PLAYER_GUID, oldNameLink.c_str(), GUID_LOPART(targetGUID)); + CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '8' WHERE guid = '%u'", GUID_LOPART(targetGUID)); + } - uint32 anim_id = atoi((char*)args); - m_session->GetPlayer()->HandleEmoteCommand(anim_id); return true; } //change standstate -bool ChatHandler::HandleStandStateCommand(const char* args) +bool ChatHandler::HandleModifyStandStateCommand(const char* args) { if (!*args) return false; @@ -3165,7 +3751,7 @@ bool ChatHandler::HandleStandStateCommand(const char* args) return true; } -bool ChatHandler::HandleAddHonorCommand(const char* args) +bool ChatHandler::HandleHonorAddCommand(const char* args) { if (!*args) return false; @@ -3178,6 +3764,10 @@ bool ChatHandler::HandleAddHonorCommand(const char* args) return false; } + // check online security + if (HasLowerSecurity(target, 0)) + return false; + uint32 amount = (uint32)atoi(args); target->RewardHonor(NULL, 1, amount); return true; @@ -3193,11 +3783,15 @@ bool ChatHandler::HandleHonorAddKillCommand(const char* /*args*/) return false; } + // check online security + if (target->GetTypeId() == TYPEID_PLAYER && HasLowerSecurity((Player*)target, 0)) + return false; + m_session->GetPlayer()->RewardHonor(target, 1); return true; } -bool ChatHandler::HandleUpdateHonorFieldsCommand(const char* /*args*/) +bool ChatHandler::HandleHonorUpdateCommand(const char* /*args*/) { Player *target = getSelectedPlayer(); if(!target) @@ -3207,6 +3801,10 @@ bool ChatHandler::HandleUpdateHonorFieldsCommand(const char* /*args*/) return false; } + // check online security + if (HasLowerSecurity(target, 0)) + return false; + target->UpdateHonorFields(); return true; } @@ -3227,8 +3825,8 @@ bool ChatHandler::HandleLookupEventCommand(const char* args) uint32 counter = 0; - GameEvent::GameEventDataMap const& events = gameeventmgr.GetEventMap(); - GameEvent::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList(); + GameEventMgr::GameEventDataMap const& events = gameeventmgr.GetEventMap(); + GameEventMgr::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList(); for(uint32 id = 0; id < events.size(); ++id ) { @@ -3261,12 +3859,12 @@ bool ChatHandler::HandleEventActiveListCommand(const char* args) { uint32 counter = 0; - GameEvent::GameEventDataMap const& events = gameeventmgr.GetEventMap(); - GameEvent::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList(); + GameEventMgr::GameEventDataMap const& events = gameeventmgr.GetEventMap(); + GameEventMgr::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList(); char const* active = GetTrinityString(LANG_ACTIVE); - for(GameEvent::ActiveEvents::const_iterator itr = activeEvents.begin(); itr != activeEvents.end(); ++itr ) + for(GameEventMgr::ActiveEvents::const_iterator itr = activeEvents.begin(); itr != activeEvents.end(); ++itr ) { uint32 event_id = *itr; GameEventData const& eventData = events[event_id]; @@ -3297,7 +3895,7 @@ bool ChatHandler::HandleEventInfoCommand(const char* args) uint32 event_id = atoi(cId); - GameEvent::GameEventDataMap const& events = gameeventmgr.GetEventMap(); + GameEventMgr::GameEventDataMap const& events = gameeventmgr.GetEventMap(); if(event_id >=events.size()) { @@ -3314,7 +3912,7 @@ bool ChatHandler::HandleEventInfoCommand(const char* args) return false; } - GameEvent::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList(); + GameEventMgr::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList(); bool active = activeEvents.find(event_id) != activeEvents.end(); char const* activeStr = active ? GetTrinityString(LANG_ACTIVE) : ""; @@ -3346,7 +3944,7 @@ bool ChatHandler::HandleEventStartCommand(const char* args) int32 event_id = atoi(cId); - GameEvent::GameEventDataMap const& events = gameeventmgr.GetEventMap(); + GameEventMgr::GameEventDataMap const& events = gameeventmgr.GetEventMap(); if(event_id < 1 || event_id >=events.size()) { @@ -3363,7 +3961,7 @@ bool ChatHandler::HandleEventStartCommand(const char* args) return false; } - GameEvent::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList(); + GameEventMgr::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList(); if(activeEvents.find(event_id) != activeEvents.end()) { PSendSysMessage(LANG_EVENT_ALREADY_ACTIVE,event_id); @@ -3387,7 +3985,7 @@ bool ChatHandler::HandleEventStopCommand(const char* args) int32 event_id = atoi(cId); - GameEvent::GameEventDataMap const& events = gameeventmgr.GetEventMap(); + GameEventMgr::GameEventDataMap const& events = gameeventmgr.GetEventMap(); if(event_id < 1 || event_id >=events.size()) { @@ -3404,7 +4002,7 @@ bool ChatHandler::HandleEventStopCommand(const char* args) return false; } - GameEvent::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList(); + GameEventMgr::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList(); if(activeEvents.find(event_id) == activeEvents.end()) { @@ -3423,9 +4021,8 @@ bool ChatHandler::HandleCombatStopCommand(const char* args) if(*args) { - std::string playername = args; - - if(!normalizePlayerName(playername)) + std::string playername = extractPlayerNameFromLink((char*)args); + if(playername.empty()) { SendSysMessage(LANG_PLAYER_NOT_FOUND); SetSentErrorMessage(true); @@ -3449,6 +4046,10 @@ bool ChatHandler::HandleCombatStopCommand(const char* args) player = m_session->GetPlayer(); } + // check online security + if (HasLowerSecurity(player, 0)) + return false; + player->CombatStop(); player->getHostilRefManager().deleteReferences(); return true; @@ -3487,7 +4088,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); } } } @@ -3560,7 +4161,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); @@ -3667,6 +4268,13 @@ bool ChatHandler::LookupPlayerSearchCommand(QueryResult* result, int32 limit) delete result; + if(i==0) // empty accounts only + { + PSendSysMessage(LANG_NO_PLAYERS_FOUND); + SetSentErrorMessage(true); + return false; + } + return true; } @@ -3688,68 +4296,50 @@ bool ChatHandler::HandleRepairitemsCommand(const char* /*args*/) return false; } + // check online security + if (HasLowerSecurity(target, 0)) + return false; + // Repair items target->DurabilityRepairAll(false, 0, false); - PSendSysMessage(LANG_YOU_REPAIR_ITEMS, target->GetName()); + PSendSysMessage(LANG_YOU_REPAIR_ITEMS, GetNameLink(target).c_str()); if(needReportToTarget(target)) - ChatHandler(target).PSendSysMessage(LANG_YOUR_ITEMS_REPAIRED, GetName()); + ChatHandler(target).PSendSysMessage(LANG_YOUR_ITEMS_REPAIRED, GetNameLink().c_str()); return true; } -bool ChatHandler::HandleNpcFollowCommand(const char* /*args*/) +bool ChatHandler::HandleWaterwalkCommand(const char* args) { - Player *player = m_session->GetPlayer(); - Creature *creature = getSelectedCreature(); - - if(!creature) - { - PSendSysMessage(LANG_SELECT_CREATURE); - SetSentErrorMessage(true); + if(!*args) return false; - } - - // Follow player - Using pet's default dist and angle - creature->GetMotionMaster()->MoveFollow(player, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); - PSendSysMessage(LANG_CREATURE_FOLLOW_YOU_NOW, creature->GetName()); - return true; -} - -bool ChatHandler::HandleNpcUnFollowCommand(const char* /*args*/) -{ - Player *player = m_session->GetPlayer(); - Creature *creature = getSelectedCreature(); + Player *player = getSelectedPlayer(); - if(!creature) + if(!player) { - PSendSysMessage(LANG_SELECT_CREATURE); + PSendSysMessage(LANG_NO_CHAR_SELECTED); SetSentErrorMessage(true); return false; } - if (/*creature->GetMotionMaster()->empty() ||*/ - creature->GetMotionMaster()->GetCurrentMovementGeneratorType ()!=TARGETED_MOTION_TYPE) - { - PSendSysMessage(LANG_CREATURE_NOT_FOLLOW_YOU); - SetSentErrorMessage(true); + // check online security + if (HasLowerSecurity(player, 0)) return false; - } - TargetedMovementGenerator<Creature> const* mgen - = static_cast<TargetedMovementGenerator<Creature> const*>((creature->GetMotionMaster()->top())); - - if(mgen->GetTarget()!=player) + if (strncmp(args, "on", 3) == 0) + player->SetMovement(MOVE_WATER_WALK); // ON + else if (strncmp(args, "off", 4) == 0) + player->SetMovement(MOVE_LAND_WALK); // OFF + else { - PSendSysMessage(LANG_CREATURE_NOT_FOLLOW_YOU); - SetSentErrorMessage(true); + SendSysMessage(LANG_USE_BOL); return false; } - // reset movement - creature->GetMotionMaster()->MovementExpired(true); - - PSendSysMessage(LANG_CREATURE_NOT_FOLLOW_YOU_NOW, creature->GetName()); + PSendSysMessage(LANG_YOU_SET_WATERWALK, args, GetNameLink(player).c_str()); + if(needReportToTarget(player)) + ChatHandler(player).PSendSysMessage(LANG_YOUR_WATERWALK_SET, args, GetNameLink().c_str()); return true; } @@ -3782,7 +4372,7 @@ bool ChatHandler::HandleCreatePetCommand(const char* args) } // Everything looks OK, create new pet - Pet* pet = new Pet(HUNTER_PET); + Pet* pet = new Pet(player, HUNTER_PET); if(!pet->CreateBaseAtCreature(creatureTarget)) { @@ -3795,7 +4385,6 @@ bool ChatHandler::HandleCreatePetCommand(const char* args) creatureTarget->RemoveCorpse(); creatureTarget->SetHealth(0); // just for nice GM-mode view - pet->SetUInt64Value(UNIT_FIELD_SUMMONEDBY, player->GetGUID()); pet->SetUInt64Value(UNIT_FIELD_CREATEDBY, player->GetGUID()); pet->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, player->getFaction()); @@ -3811,16 +4400,15 @@ bool ChatHandler::HandleCreatePetCommand(const char* args) pet->GetCharmInfo()->SetPetNumber(objmgr.GeneratePetNumber(), true); // this enables pet details window (Shift+P) - pet->AIM_Initialize(); pet->InitPetCreateSpells(); pet->SetHealth(pet->GetMaxHealth()); - MapManager::Instance().GetMap(pet->GetMapId(), pet)->Add((Creature*)pet); + pet->GetMap()->Add((Creature*)pet); // visual effect for levelup pet->SetUInt32Value(UNIT_FIELD_LEVEL,creatureTarget->getLevel()); - player->SetPet(pet); + player->SetGuardian(pet, true); pet->SavePetToDB(PET_SAVE_AS_CURRENT); player->PetSpellInitialize(); @@ -3912,7 +4500,7 @@ bool ChatHandler::HandlePetTpCommand(const char *args) uint32 tp = atol(args); - pet->SetTP(tp); + //pet->SetTP(tp); PSendSysMessage("Pet's tp changed to %u", tp); return true; @@ -4024,9 +4612,9 @@ bool ChatHandler::HandleNpcAddFormationCommand(const char* args) } uint32 lowguid = pCreature->GetDBTableGUIDLow(); - if(pCreature->GetFormationID()) + if(pCreature->GetFormation()) { - PSendSysMessage("Selected creature is already member of group %u", pCreature->GetFormationID()); + PSendSysMessage("Selected creature is already member of group %u", pCreature->GetFormation()->GetId()); return false; } @@ -4034,12 +4622,11 @@ bool ChatHandler::HandleNpcAddFormationCommand(const char* args) return false; Player *chr = m_session->GetPlayer(); - FormationMember *group_member; + FormationInfo *group_member; - group_member = new FormationMember; + group_member = new FormationInfo; group_member->follow_angle = pCreature->GetAngle(chr) - chr->GetOrientation(); group_member->follow_dist = sqrtf(pow(chr->GetPositionX() - pCreature->GetPositionX(),int(2))+pow(chr->GetPositionY()-pCreature->GetPositionY(),int(2))); - group_member->memberGUID = lowguid; group_member->leaderGUID = leaderGUID; group_member->groupAI = 0; @@ -4053,4 +4640,3 @@ bool ChatHandler::HandleNpcAddFormationCommand(const char* args) return true; } - diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp index 73d1fa1e5c7..8122166ef31 100644 --- a/src/game/Level3.cpp +++ b/src/game/Level3.cpp @@ -36,7 +36,6 @@ #include "Guild.h" #include "ObjectAccessor.h" #include "MapManager.h" -#include "SpellAuras.h" #include "ScriptCalls.h" #include "Language.h" #include "GridNotifiersImpl.h" @@ -526,19 +525,11 @@ bool ChatHandler::HandleAHBotOptionsCommand(const char* args) } //reload commands -bool ChatHandler::HandleReloadCommand(const char* arg) -{ - // this is error catcher for wrong table name in .reload commands - PSendSysMessage("Db table with name starting from '%s' not found and can't be reloaded.",arg); - SetSentErrorMessage(true); - return false; -} - bool ChatHandler::HandleReloadAllCommand(const char*) { - HandleReloadAreaTriggerTeleportCommand(""); HandleReloadSkillFishingBaseLevelCommand(""); + HandleReloadAllAchievementCommand(""); HandleReloadAllAreaCommand(""); HandleReloadAllLootCommand(""); HandleReloadAllNpcCommand(""); @@ -554,6 +545,13 @@ bool ChatHandler::HandleReloadAllCommand(const char*) return true; } +bool ChatHandler::HandleReloadAllAchievementCommand(const char*) +{ + HandleReloadAchievementCriteriaDataCommand(""); + HandleReloadAchievementRewardCommand(""); + return true; +} + bool ChatHandler::HandleReloadAllAreaCommand(const char*) { //HandleReloadQuestAreaTriggersCommand(""); -- reloaded in HandleReloadAllQuestCommand @@ -577,6 +575,7 @@ bool ChatHandler::HandleReloadAllNpcCommand(const char* /*args*/) HandleReloadNpcOptionCommand("a"); HandleReloadNpcTrainerCommand("a"); HandleReloadNpcVendorCommand("a"); + HandleReloadPointsOfInterestCommand("a"); return true; } @@ -618,10 +617,12 @@ bool ChatHandler::HandleReloadAllSpellCommand(const char*) HandleReloadSkillExtraItemTemplateCommand("a"); HandleReloadSpellAffectCommand("a"); HandleReloadSpellRequiredCommand("a"); + HandleReloadSpellAreaCommand("a"); HandleReloadSpellElixirCommand("a"); HandleReloadSpellLearnSpellCommand("a"); HandleReloadSpellLinkedSpellCommand("a"); HandleReloadSpellProcEventCommand("a"); + HandleReloadSpellBonusesCommand("a"); HandleReloadSpellScriptTargetCommand("a"); HandleReloadSpellTargetPositionCommand("a"); HandleReloadSpellThreatsCommand("a"); @@ -639,11 +640,13 @@ bool ChatHandler::HandleReloadAllItemCommand(const char*) bool ChatHandler::HandleReloadAllLocalesCommand(const char* /*args*/) { + HandleReloadLocalesAchievementRewardCommand("a"); HandleReloadLocalesCreatureCommand("a"); HandleReloadLocalesGameobjectCommand("a"); HandleReloadLocalesItemCommand("a"); HandleReloadLocalesNpcTextCommand("a"); HandleReloadLocalesPageTextCommand("a"); + HandleReloadLocalesPointsOfInterestCommand("a"); HandleReloadLocalesQuestCommand("a"); return true; } @@ -656,6 +659,22 @@ bool ChatHandler::HandleReloadConfigCommand(const char* /*args*/) return true; } +bool ChatHandler::HandleReloadAchievementCriteriaDataCommand(const char*) +{ + sLog.outString( "Re-Loading Additional Achievement Criteria Data..." ); + achievementmgr.LoadAchievementCriteriaData(); + SendGlobalSysMessage("DB table `achievement_criteria_data` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadAchievementRewardCommand(const char*) +{ + sLog.outString( "Re-Loading Achievement Reward Data..." ); + achievementmgr.LoadRewards(); + SendGlobalSysMessage("DB table `achievement_reward` reloaded."); + return true; +} + bool ChatHandler::HandleReloadAreaTriggerTavernCommand(const char*) { sLog.outString( "Re-Loading Tavern Area Triggers..." ); @@ -732,6 +751,11 @@ bool ChatHandler::HandleReloadQuestTemplateCommand(const char*) sLog.outString( "Re-Loading Quest Templates..." ); objmgr.LoadQuests(); SendGlobalGMSysMessage("DB table `quest_template` (quest definitions) reloaded."); + + /// dependent also from `gameobject` but this table not reloaded anyway + sLog.outString( "Re-Loading GameObjects for quests..." ); + objmgr.LoadGameObjectForQuests(); + SendGlobalGMSysMessage("Data GameObjects for quests reloaded."); return true; } @@ -780,6 +804,15 @@ bool ChatHandler::HandleReloadLootTemplatesItemCommand(const char*) return true; } +bool ChatHandler::HandleReloadLootTemplatesMillingCommand(const char*) +{ + sLog.outString( "Re-Loading Loot Tables... (`milling_loot_template`)" ); + LoadLootTemplates_Milling(); + LootTemplates_Milling.CheckLootRefs(); + SendGlobalSysMessage("DB table `milling_loot_template` reloaded."); + return true; +} + bool ChatHandler::HandleReloadLootTemplatesPickpocketingCommand(const char*) { sLog.outString( "Re-Loading Loot Tables... (`pickpocketing_loot_template`)" ); @@ -824,6 +857,15 @@ bool ChatHandler::HandleReloadLootTemplatesSkinningCommand(const char*) return true; } +bool ChatHandler::HandleReloadLootTemplatesSpellCommand(const char*) +{ + sLog.outString( "Re-Loading Loot Tables... (`spell_loot_template`)" ); + LoadLootTemplates_Spell(); + LootTemplates_Spell.CheckLootRefs(); + SendGlobalSysMessage("DB table `spell_loot_template` reloaded."); + return true; +} + bool ChatHandler::HandleReloadTrinityStringCommand(const char*) { sLog.outString( "Re-Loading trinity_string Table!" ); @@ -864,6 +906,14 @@ bool ChatHandler::HandleReloadNpcVendorCommand(const char*) return true; } +bool ChatHandler::HandleReloadPointsOfInterestCommand(const char*) +{ + sLog.outString( "Re-Loading `points_of_interest` Table!" ); + objmgr.LoadPointsOfInterest(); + SendGlobalSysMessage("DB table `points_of_interest` reloaded."); + return true; +} + bool ChatHandler::HandleReloadReservedNameCommand(const char*) { sLog.outString( "Loading ReservedNames... (`reserved_name`)" ); @@ -904,6 +954,14 @@ bool ChatHandler::HandleReloadSpellAffectCommand(const char*) return true; } +bool ChatHandler::HandleReloadSpellAreaCommand(const char*) +{ + sLog.outString( "Re-Loading SpellArea Data..." ); + spellmgr.LoadSpellAreas(); + SendGlobalSysMessage("DB table `spell_area` (spell dependences from area/quest/auras state) reloaded."); + return true; +} + bool ChatHandler::HandleReloadSpellRequiredCommand(const char*) { sLog.outString( "Re-Loading Spell Required Data... " ); @@ -944,6 +1002,14 @@ bool ChatHandler::HandleReloadSpellProcEventCommand(const char*) return true; } +bool ChatHandler::HandleReloadSpellBonusesCommand(const char*) +{ + sLog.outString( "Re-Loading Spell Bonus Data..." ); + spellmgr.LoadSpellBonusess(); + SendGlobalSysMessage("DB table `spell_bonus_data` (spell damage/healing coefficients) reloaded."); + return true; +} + bool ChatHandler::HandleReloadSpellScriptTargetCommand(const char*) { sLog.outString( "Re-Loading SpellsScriptTarget..." ); @@ -1153,6 +1219,14 @@ bool ChatHandler::HandleReloadSpellDisabledCommand(const char* /*arg*/) return true; } +bool ChatHandler::HandleReloadLocalesAchievementRewardCommand(const char*) +{ + sLog.outString( "Re-Loading Locales Achievement Reward Data..." ); + achievementmgr.LoadRewardLocales(); + SendGlobalSysMessage("DB table `locales_achievement_reward` reloaded."); + return true; +} + bool ChatHandler::HandleReloadLocalesCreatureCommand(const char* /*arg*/) { sLog.outString( "Re-Loading Locales Creature ..."); @@ -1193,6 +1267,14 @@ bool ChatHandler::HandleReloadLocalesPageTextCommand(const char* /*arg*/) return true; } +bool ChatHandler::HandleReloadLocalesPointsOfInterestCommand(const char* /*arg*/) +{ + sLog.outString( "Re-Loading Locales Points Of Interest ... "); + objmgr.LoadPointOfInterestLocales(); + SendGlobalSysMessage("DB table `locales_points_of_interest` reloaded."); + return true; +} + bool ChatHandler::HandleReloadLocalesQuestCommand(const char* /*arg*/) { sLog.outString( "Re-Loading Locales Quest ... "); @@ -1339,19 +1421,10 @@ bool ChatHandler::HandleAccountSetPasswordCommand(const char* args) return false; } - uint32 targetSecurity = accmgr.GetSecurity(targetAccountId); - - /// m_session==NULL only for console - uint32 plSecurity = m_session ? m_session->GetSecurity() : SEC_CONSOLE; - /// can set password only for target with less security /// This is also reject self apply in fact - if (targetSecurity >= plSecurity) - { - SendSysMessage (LANG_YOURS_SECURITY_IS_LOW); - SetSentErrorMessage (true); + if(HasLowerSecurityAccount (NULL,targetAccountId,true)) return false; - } if (strcmp(szPassword1,szPassword2)) { @@ -1384,21 +1457,6 @@ bool ChatHandler::HandleAccountSetPasswordCommand(const char* args) return true; } -bool ChatHandler::HandleAllowMovementCommand(const char* /*args*/) -{ - if(sWorld.getAllowMovement()) - { - sWorld.SetAllowMovement(false); - SendSysMessage(LANG_CREATURE_MOVE_DISABLED); - } - else - { - sWorld.SetAllowMovement(true); - SendSysMessage(LANG_CREATURE_MOVE_ENABLED); - } - return true; -} - bool ChatHandler::HandleMaxSkillCommand(const char* /*args*/) { Player* SelectedPlayer = getSelectedPlayer(); @@ -1429,7 +1487,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); @@ -1455,9 +1512,11 @@ bool ChatHandler::HandleSetSkillCommand(const char* args) return false; } + std::string tNameLink = GetNameLink(target); + if(!target->GetSkillValue(skill)) { - PSendSysMessage(LANG_SET_SKILL_ERROR, target->GetName(), skill, sl->name[0]); + PSendSysMessage(LANG_SET_SKILL_ERROR, tNameLink.c_str(), skill, sl->name[0]); SetSentErrorMessage(true); return false; } @@ -1468,7 +1527,7 @@ bool ChatHandler::HandleSetSkillCommand(const char* args) return false; target->SetSkill(skill, level, max); - PSendSysMessage(LANG_SET_SKILL, skill, sl->name[0], target->GetName(), level, max); + PSendSysMessage(LANG_SET_SKILL, skill, sl->name[0], tNameLink.c_str(), level, max); return true; } @@ -1479,27 +1538,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) @@ -1509,13 +1553,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; } @@ -1530,10 +1574,12 @@ bool ChatHandler::HandleCooldownCommand(const char* args) return false; } + std::string tNameLink = GetNameLink(target); + if (!*args) { target->RemoveAllSpellCooldown(); - PSendSysMessage(LANG_REMOVEALL_COOLDOWN, target->GetName()); + PSendSysMessage(LANG_REMOVEALL_COOLDOWN, tNameLink.c_str()); } else { @@ -1544,7 +1590,7 @@ bool ChatHandler::HandleCooldownCommand(const char* args) if(!sSpellStore.LookupEntry(spell_id)) { - PSendSysMessage(LANG_UNKNOWN_SPELL, target==m_session->GetPlayer() ? GetTrinityString(LANG_YOU) : target->GetName()); + PSendSysMessage(LANG_UNKNOWN_SPELL, target==m_session->GetPlayer() ? GetMangosString(LANG_YOU) : tNameLink.c_str()); SetSentErrorMessage(true); return false; } @@ -1554,8 +1600,8 @@ bool ChatHandler::HandleCooldownCommand(const char* args) data << uint64(target->GetGUID()); target->GetSession()->SendPacket(&data); target->RemoveSpellCooldown(spell_id); - PSendSysMessage(LANG_REMOVE_COOLDOWN, spell_id, target==m_session->GetPlayer() ? GetTrinityString(LANG_YOU) : target->GetName()); - } + PSendSysMessage(LANG_REMOVE_COOLDOWN, spell_id, target==m_session->GetPlayer() ? GetMangosString(LANG_YOU) : tNameLink.c_str()); + } return true; } @@ -2180,7 +2226,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); @@ -2220,7 +2266,7 @@ bool ChatHandler::HandleLearnAllGMCommand(const char* /*args*/) continue; } - m_session->GetPlayer()->learnSpell(spell); + m_session->GetPlayer()->learnSpell(spell,false); } SendSysMessage(LANG_LEARNING_GM_SKILLS); @@ -2247,6 +2293,10 @@ bool ChatHandler::HandleLearnAllMySpellsCommand(const char* /*args*/) if(!spellInfo) continue; + // skip server-side/triggered spells + if(spellInfo->spellLevel==0) + continue; + // skip wrong class/race skills if(!m_session->GetPlayer()->IsSpellFitByClassAndRace(spellInfo->Id)) continue; @@ -2255,8 +2305,6 @@ bool ChatHandler::HandleLearnAllMySpellsCommand(const char* /*args*/) if( spellInfo->SpellFamilyName != family) continue; - //TODO: skip triggered spells - // skip spells with first rank learned as talent (and all talents then also) uint32 first_rank = spellmgr.GetFirstSpellInChain(spellInfo->Id); if(GetTalentSpellCost(first_rank) > 0 ) @@ -2266,31 +2314,92 @@ 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) +bool ChatHandler::HandleLearnAllMyTalentsCommand(const char* /*args*/) { - SpellChainNode const* node; - do + Player* player = m_session->GetPlayer(); + uint32 classMask = player->getClassMask(); + + for (uint32 i = 0; i < sTalentStore.GetNumRows(); i++) { - node = spellmgr.GetSpellChainNode(spellid); - player->learnSpell(spellid); - if (!node) - break; - spellid=node->next; + TalentEntry const *talentInfo = sTalentStore.LookupEntry(i); + if(!talentInfo) + continue; + + TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab ); + if(!talentTabInfo) + continue; + + if( (classMask & talentTabInfo->ClassMask) == 0 ) + continue; + + // search highest talent rank + uint32 spellid = 0; + + for(int rank = MAX_TALENT_RANK-1; rank >= 0; --rank) + { + if(talentInfo->RankID[rank]!=0) + { + spellid = talentInfo->RankID[rank]; + break; + } + } + + if(!spellid) // ??? none spells in talent + continue; + + SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellid); + if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer(),false)) + continue; + + // learn highest rank of talent and learn all non-talent spell ranks (recursive by tree) + player->learnSpellHighRank(spellid); } - while (node->next); + + SendSysMessage(LANG_COMMAND_LEARN_CLASS_TALENTS); + return true; } -bool ChatHandler::HandleLearnAllMyTalentsCommand(const char* /*args*/) +bool ChatHandler::HandleLearnAllMyPetTalentsCommand(const char* /*args*/) { Player* player = m_session->GetPlayer(); - uint32 classMask = player->getClassMask(); + + Pet* pet = player->GetPet(); + if(!pet) + { + SendSysMessage(LANG_NO_PET_FOUND); + SetSentErrorMessage(true); + return false; + } + + CreatureInfo const *ci = pet->GetCreatureInfo(); + if(!ci) + { + SendSysMessage(LANG_WRONG_PET_TYPE); + SetSentErrorMessage(true); + return false; + } + + CreatureFamilyEntry const *pet_family = sCreatureFamilyStore.LookupEntry(ci->family); + if(!pet_family) + { + SendSysMessage(LANG_WRONG_PET_TYPE); + SetSentErrorMessage(true); + return false; + } + + if(pet_family->petTalentType < 0) // not hunter pet + { + SendSysMessage(LANG_WRONG_PET_TYPE); + SetSentErrorMessage(true); + return false; + } for (uint32 i = 0; i < sTalentStore.GetNumRows(); i++) { @@ -2302,13 +2411,14 @@ bool ChatHandler::HandleLearnAllMyTalentsCommand(const char* /*args*/) if(!talentTabInfo) continue; - if( (classMask & talentTabInfo->ClassMask) == 0 ) + // prevent learn talent for different family (cheating) + if(((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask)==0) continue; // search highest talent rank uint32 spellid = 0; - int rank = 4; - for(; rank >= 0; --rank) + + for(int rank = MAX_TALENT_RANK-1; rank >= 0; --rank) { if(talentInfo->RankID[rank]!=0) { @@ -2324,14 +2434,11 @@ 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) + pet->learnSpellHighRank(spellid); } - SendSysMessage(LANG_COMMAND_LEARN_CLASS_TALENTS); + SendSysMessage(LANG_COMMAND_LEARN_PET_TALENTS); return true; } @@ -2339,7 +2446,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; @@ -2347,13 +2454,11 @@ bool ChatHandler::HandleLearnAllLangCommand(const char* /*args*/) bool ChatHandler::HandleLearnAllDefaultCommand(const char* args) { - char* pName = strtok((char*)args, ""); Player *player = NULL; - if (pName) + if (*args) { - std::string name = pName; - - if(!normalizePlayerName(name)) + std::string name = extractPlayerNameFromLink((char*)args); + if(name.empty()) { SendSysMessage(LANG_PLAYER_NOT_FOUND); SetSentErrorMessage(true); @@ -2375,7 +2480,7 @@ bool ChatHandler::HandleLearnAllDefaultCommand(const char* args) player->learnDefaultSpells(); player->learnQuestRewardedSpells(); - PSendSysMessage(LANG_COMMAND_LEARN_ALL_DEFAULT_AND_QUEST,player->GetName()); + PSendSysMessage(LANG_COMMAND_LEARN_ALL_DEFAULT_AND_QUEST,GetNameLink(player).c_str()); return true; } @@ -2395,25 +2500,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,GetNameLink(targetPlayer).c_str()); SetSentErrorMessage(true); return false; } - targetPlayer->learnSpell(spell); + if(allRanks) + targetPlayer->learnSpellHighRank(spell); + else + targetPlayer->learnSpell(spell,false); return true; } @@ -2483,7 +2594,7 @@ bool ChatHandler::HandleAddItemCommand(const char* args) if (count < 0) { plTarget->DestroyItemCount(itemId, -count, true, false); - PSendSysMessage(LANG_REMOVEITEM, itemId, -count, plTarget->GetName()); + PSendSysMessage(LANG_REMOVEITEM, itemId, -count, GetNameLink(plTarget).c_str()); return true; } @@ -2893,48 +3004,7 @@ bool ChatHandler::HandleListObjectCommand(const char* args) return true; } -bool ChatHandler::HandleNearObjectCommand(const char* args) -{ - float distance = (!*args) ? 10 : atol(args); - uint32 count = 0; - - Player* pl = m_session->GetPlayer(); - QueryResult *result = WorldDatabase.PQuery("SELECT guid, id, position_x, position_y, position_z, map, " - "(POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) AS order_ " - "FROM gameobject WHERE map='%u' AND (POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) <= '%f' ORDER BY order_", - pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(), - pl->GetMapId(),pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(),distance*distance); - - if (result) - { - do - { - Field *fields = result->Fetch(); - uint32 guid = fields[0].GetUInt32(); - uint32 entry = fields[1].GetUInt32(); - float x = fields[2].GetFloat(); - float y = fields[3].GetFloat(); - float z = fields[4].GetFloat(); - int mapid = fields[5].GetUInt16(); - - GameObjectInfo const * gInfo = objmgr.GetGameObjectInfo(entry); - - if(!gInfo) - continue; - - PSendSysMessage(LANG_GO_LIST_CHAT, guid, guid, gInfo->name, x, y, z, mapid); - - ++count; - } while (result->NextRow()); - - delete result; - } - - PSendSysMessage(LANG_COMMAND_NEAROBJMESSAGE,distance,count); - return true; -} - -bool ChatHandler::HandleObjectStateCommand(const char* args) +bool ChatHandler::HandleGameObjectStateCommand(const char* args) { // number or [name] Shift-click form |color|Hgameobject:go_id|h[name]|h|r char* cId = extractKeyFromLink((char*)args, "Hgameobject"); @@ -2968,8 +3038,6 @@ bool ChatHandler::HandleObjectStateCommand(const char* args) gobj->SetGoState(state); return true; - - return true; } bool ChatHandler::HandleListCreatureCommand(const char* args) @@ -3223,15 +3291,25 @@ bool ChatHandler::HandleLookupSkillCommand(const char* args) if(loc < MAX_LOCALE) { + char valStr[50] = ""; char const* knownStr = ""; if(target && target->HasSkill(id)) + { knownStr = GetTrinityString(LANG_KNOWN); + uint32 curValue = target->GetPureSkillValue(id); + uint32 maxValue = target->GetPureMaxSkillValue(id); + uint32 permValue = target->GetSkillPermBonusValue(id); + uint32 tempValue = target->GetSkillTempBonusValue(id); + + char const* valFormat = GetTrinityString(LANG_SKILL_VALUES); + snprintf(valStr,50,valFormat,curValue,maxValue,permValue,tempValue); + } // send skill in "id - [namedlink locale]" format if (m_session) - PSendSysMessage(LANG_SKILL_LIST_CHAT,id,id,name.c_str(),localeNames[loc],knownStr); + PSendSysMessage(LANG_SKILL_LIST_CHAT,id,id,name.c_str(),localeNames[loc],knownStr,valStr); else - PSendSysMessage(LANG_SKILL_LIST_CONSOLE,id,name.c_str(),localeNames[loc],knownStr); + PSendSysMessage(LANG_SKILL_LIST_CONSOLE,id,name.c_str(),localeNames[loc],knownStr,valStr); ++counter; } @@ -3298,7 +3376,7 @@ bool ChatHandler::HandleLookupSpellCommand(const char* args) bool talent = (talentCost > 0); bool passive = IsPassiveSpell(id); - bool active = target && (target->HasAura(id,0) || target->HasAura(id,1) || target->HasAura(id,2)); + bool active = target && target->HasAura(id); // unit32 used to prevent interpreting uint8 as char at output // find rank of learned spell for learning spell, or talent rank @@ -3618,7 +3696,7 @@ bool ChatHandler::HandleGuildCreateCommand(const char* args) } Guild *guild = new Guild; - if (!guild->create (player->GetGUID (),guildname)) + if (!guild->create (player,guildname)) { delete guild; SendSysMessage (LANG_GUILD_NOT_CREATED); @@ -3645,11 +3723,11 @@ bool ChatHandler::HandleGuildInviteCommand(const char *args) if (!targetGuild) return false; - std::string plName = par1; - if (!normalizePlayerName (plName)) + std::string plName = extractPlayerNameFromLink(par1); + if(plName.empty()) { - SendSysMessage (LANG_PLAYER_NOT_FOUND); - SetSentErrorMessage (true); + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); return false; } @@ -3657,10 +3735,10 @@ bool ChatHandler::HandleGuildInviteCommand(const char *args) if (Player* targetPlayer = ObjectAccessor::Instance ().FindPlayerByName (plName.c_str ())) plGuid = targetPlayer->GetGUID (); else - plGuid = objmgr.GetPlayerGUIDByName (plName.c_str ()); + plGuid = objmgr.GetPlayerGUIDByName (plName); if (!plGuid) - false; + return false; // player's guild membership checked in AddMember before add if (!targetGuild->AddMember (plGuid,targetGuild->GetLowestRank ())) @@ -3678,11 +3756,11 @@ bool ChatHandler::HandleGuildUninviteCommand(const char *args) if(!par1) return false; - std::string plName = par1; - if (!normalizePlayerName (plName)) + std::string plName = extractPlayerNameFromLink(par1); + if(plName.empty()) { - SendSysMessage (LANG_PLAYER_NOT_FOUND); - SetSentErrorMessage (true); + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); return false; } @@ -3695,7 +3773,7 @@ bool ChatHandler::HandleGuildUninviteCommand(const char *args) } else { - plGuid = objmgr.GetPlayerGUIDByName (plName.c_str ()); + plGuid = objmgr.GetPlayerGUIDByName (plName); glId = Player::GetGuildIdFromDB (plGuid); } @@ -3720,14 +3798,16 @@ bool ChatHandler::HandleGuildRankCommand(const char *args) char* par2 = strtok (NULL, " "); if (!par1 || !par2) return false; - std::string plName = par1; - if (!normalizePlayerName (plName)) + + std::string plName = extractPlayerNameFromLink(par1); + if(plName.empty()) { - SendSysMessage (LANG_PLAYER_NOT_FOUND); - SetSentErrorMessage (true); + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); return false; } + uint64 plGuid = 0; uint32 glId = 0; if (Player* targetPlayer = ObjectAccessor::Instance ().FindPlayerByName (plName.c_str ())) @@ -3737,7 +3817,7 @@ bool ChatHandler::HandleGuildRankCommand(const char *args) } else { - plGuid = objmgr.GetPlayerGUIDByName (plName.c_str ()); + plGuid = objmgr.GetPlayerGUIDByName (plName); glId = Player::GetGuildIdFromDB (plGuid); } @@ -3777,90 +3857,37 @@ bool ChatHandler::HandleGuildDeleteCommand(const char* args) return true; } -bool ChatHandler::HandleGetDistanceCommand(const char* /*args*/) +bool ChatHandler::HandleGetDistanceCommand(const char* args) { - Unit* pUnit = getSelectedUnit(); - - if(!pUnit) - { - SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); - SetSentErrorMessage(true); - return false; - } + WorldObject* obj = NULL; - PSendSysMessage(LANG_DISTANCE, m_session->GetPlayer()->GetDistance(pUnit),m_session->GetPlayer()->GetDistance2d(pUnit)); - - return true; -} - -// FIX-ME!!! - -bool ChatHandler::HandleAddWeaponCommand(const char* /*args*/) -{ - /*if (!*args) - return false; - - uint64 guid = m_session->GetPlayer()->GetSelection(); - if (guid == 0) + if (*args) { - SendSysMessage(LANG_NO_SELECTION); - return true; - } - - Creature *pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), guid); + uint64 guid = extractGuidFromLink((char*)args); + if(guid) + obj = (WorldObject*)ObjectAccessor::GetObjectByTypeMask(*m_session->GetPlayer(),guid,TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT); - if(!pCreature) - { - SendSysMessage(LANG_SELECT_CREATURE); - return true; - } - - char* pSlotID = strtok((char*)args, " "); - if (!pSlotID) - return false; - - char* pItemID = strtok(NULL, " "); - if (!pItemID) - return false; - - uint32 ItemID = atoi(pItemID); - uint32 SlotID = atoi(pSlotID); - - ItemPrototype* tmpItem = objmgr.GetItemPrototype(ItemID); - - bool added = false; - if(tmpItem) - { - switch(SlotID) + if(!obj) { - case 1: - pCreature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, ItemID); - added = true; - break; - case 2: - pCreature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY_01, ItemID); - added = true; - break; - case 3: - pCreature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY_02, ItemID); - added = true; - break; - default: - PSendSysMessage(LANG_ITEM_SLOT_NOT_EXIST,SlotID); - added = false; - break; - } - if(added) - { - PSendSysMessage(LANG_ITEM_ADDED_TO_SLOT,ItemID,tmpItem->Name1,SlotID); + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; } } else { - PSendSysMessage(LANG_ITEM_NOT_FOUND,ItemID); - return true; + obj = getSelectedUnit(); + + if(!obj) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + SetSentErrorMessage(true); + return false; + } } - */ + + PSendSysMessage(LANG_DISTANCE, m_session->GetPlayer()->GetDistance(obj),m_session->GetPlayer()->GetDistance2d(obj)); + return true; } @@ -3875,6 +3902,12 @@ bool ChatHandler::HandleDieCommand(const char* /*args*/) return false; } + if(target->GetTypeId()==TYPEID_PLAYER) + { + if(HasLowerSecurity((Player*)target,0,false)) + return false; + } + if( target->isAlive() ) { //m_session->GetPlayer()->DealDamage(target, target->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); @@ -3926,7 +3959,7 @@ bool ChatHandler::HandleDamageCommand(const char * args) SpellSchoolMask schoolmask = SpellSchoolMask(1 << school); if ( schoolmask & SPELL_SCHOOL_MASK_NORMAL ) - damage = m_session->GetPlayer()->CalcArmorReducedDamage(target, damage); + damage = m_session->GetPlayer()->CalcArmorReducedDamage(target, damage, NULL, BASE_ATTACK); char* spellStr = strtok((char*)NULL, " "); @@ -3976,7 +4009,7 @@ bool ChatHandler::HandleModifyArenaCommand(const char * args) target->ModifyArenaPoints(amount); - PSendSysMessage(LANG_COMMAND_MODIFY_ARENA, target->GetName(), target->GetArenaPoints()); + PSendSysMessage(LANG_COMMAND_MODIFY_ARENA, GetNameLink(target).c_str(), target->GetArenaPoints()); return true; } @@ -3987,8 +4020,8 @@ bool ChatHandler::HandleReviveCommand(const char* args) if (*args) { - std::string name = args; - if(!normalizePlayerName(name)) + std::string name = extractPlayerNameFromLink((char*)args); + if(name.empty()) { SendSysMessage(LANG_PLAYER_NOT_FOUND); SetSentErrorMessage(true); @@ -4015,10 +4048,6 @@ bool ChatHandler::HandleReviveCommand(const char* args) bool ChatHandler::HandleAuraCommand(const char* args) { - char* px = strtok((char*)args, " "); - if (!px) - return false; - Unit *target = getSelectedUnit(); if(!target) { @@ -4027,8 +4056,11 @@ bool ChatHandler::HandleAuraCommand(const char* args) return false; } - uint32 spellID = (uint32)atoi(px); + // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form + uint32 spellID = extractSpellIdFromLink((char*)args); + SpellEntry const *spellInfo = sSpellStore.LookupEntry( spellID ); + uint8 eff_mask=0; if(spellInfo) { for(uint32 i = 0;i<3;i++) @@ -4040,21 +4072,18 @@ bool ChatHandler::HandleAuraCommand(const char* args) eff == SPELL_EFFECT_APPLY_AURA || eff == SPELL_EFFECT_PERSISTENT_AREA_AURA ) { - Aura *Aur = CreateAura(spellInfo, i, NULL, target); - target->AddAura(Aur); + eff_mask|=1<<i; } } } + Aura *Aur = new Aura(spellInfo, eff_mask, NULL, target); + target->AddAura(Aur); return true; } bool ChatHandler::HandleUnAuraCommand(const char* args) { - char* px = strtok((char*)args, " "); - if (!px) - return false; - Unit *target = getSelectedUnit(); if(!target) { @@ -4070,7 +4099,11 @@ bool ChatHandler::HandleUnAuraCommand(const char* args) return true; } - uint32 spellID = (uint32)atoi(px); + // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form + uint32 spellID = extractSpellIdFromLink((char*)args); + if(!spellID) + return false; + target->RemoveAurasDueToSpell(spellID); return true; @@ -4121,7 +4154,7 @@ bool ChatHandler::HandleLinkGraveCommand(const char* args) return false; } - if(objmgr.AddGraveYardLink(g_id,player->GetZoneId(),g_team)) + if(objmgr.AddGraveYardLink(g_id,zoneId,g_team)) PSendSysMessage(LANG_COMMAND_GRAVEYARDLINKED, g_id,zoneId); else PSendSysMessage(LANG_COMMAND_GRAVEYARDALRLINKED, g_id,zoneId); @@ -4145,6 +4178,7 @@ bool ChatHandler::HandleNearGraveCommand(const char* args) return false; Player* player = m_session->GetPlayer(); + uint32 zone_id = player->GetZoneId(); WorldSafeLocsEntry const* graveyard = objmgr.GetClosestGraveYard( player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(),player->GetMapId(),g_team); @@ -4153,7 +4187,7 @@ bool ChatHandler::HandleNearGraveCommand(const char* args) { uint32 g_id = graveyard->ID; - GraveYardData const* data = objmgr.FindGraveYardData(g_id,player->GetZoneId()); + GraveYardData const* data = objmgr.FindGraveYardData(g_id,zone_id); if (!data) { PSendSysMessage(LANG_COMMAND_GRAVEYARDERROR,g_id); @@ -4172,7 +4206,7 @@ bool ChatHandler::HandleNearGraveCommand(const char* args) else if(g_team == ALLIANCE) team_name = GetTrinityString(LANG_COMMAND_GRAVEYARD_ALLIANCE); - PSendSysMessage(LANG_COMMAND_GRAVEYARDNEAREST, g_id,team_name.c_str(),player->GetZoneId()); + PSendSysMessage(LANG_COMMAND_GRAVEYARDNEAREST, g_id,team_name.c_str(),zone_id); } else { @@ -4186,29 +4220,51 @@ bool ChatHandler::HandleNearGraveCommand(const char* args) team_name = GetTrinityString(LANG_COMMAND_GRAVEYARD_ALLIANCE); if(g_team == ~uint32(0)) - PSendSysMessage(LANG_COMMAND_ZONENOGRAVEYARDS, player->GetZoneId()); + PSendSysMessage(LANG_COMMAND_ZONENOGRAVEYARDS, zone_id); else - PSendSysMessage(LANG_COMMAND_ZONENOGRAFACTION, player->GetZoneId(),team_name.c_str()); + PSendSysMessage(LANG_COMMAND_ZONENOGRAFACTION, zone_id,team_name.c_str()); } return true; } -//play npc emote -bool ChatHandler::HandleNpcPlayEmoteCommand(const char* args) +//-----------------------Npc Commands----------------------- +bool ChatHandler::HandleNpcAllowMovementCommand(const char* /*args*/) { - uint32 emote = atoi((char*)args); + if(sWorld.getAllowMovement()) + { + sWorld.SetAllowMovement(false); + SendSysMessage(LANG_CREATURE_MOVE_DISABLED); + } + else + { + sWorld.SetAllowMovement(true); + SendSysMessage(LANG_CREATURE_MOVE_ENABLED); + } + return true; +} - Creature* target = getSelectedCreature(); - if(!target) +bool ChatHandler::HandleNpcChangeEntryCommand(const char *args) +{ + if (!*args) + return false; + + uint32 newEntryNum = atoi(args); + if(!newEntryNum) + return false; + + Unit* unit = getSelectedUnit(); + if(!unit || unit->GetTypeId() != TYPEID_UNIT) { SendSysMessage(LANG_SELECT_CREATURE); SetSentErrorMessage(true); return false; } - - target->SetUInt32Value(UNIT_NPC_EMOTESTATE,emote); - + Creature* creature = (Creature*)unit; + if(creature->UpdateEntry(newEntryNum)) + SendSysMessage(LANG_DONE); + else + SendSysMessage(LANG_ERROR); return true; } @@ -4257,6 +4313,95 @@ bool ChatHandler::HandleNpcInfoCommand(const char* /*args*/) return true; } +//play npc emote +bool ChatHandler::HandleNpcPlayEmoteCommand(const char* args) +{ + uint32 emote = atoi((char*)args); + + Creature* target = getSelectedCreature(); + if(!target) + { + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + target->SetUInt32Value(UNIT_NPC_EMOTESTATE,emote); + + return true; +} + +//TODO: NpcCommands that needs to be fixed : + +bool ChatHandler::HandleNpcAddWeaponCommand(const char* /*args*/) +{ + /*if (!*args) + return false; + + uint64 guid = m_session->GetPlayer()->GetSelection(); + if (guid == 0) + { + SendSysMessage(LANG_NO_SELECTION); + return true; + } + + Creature *pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), guid); + + if(!pCreature) + { + SendSysMessage(LANG_SELECT_CREATURE); + return true; + } + + char* pSlotID = strtok((char*)args, " "); + if (!pSlotID) + return false; + + char* pItemID = strtok(NULL, " "); + if (!pItemID) + return false; + + uint32 ItemID = atoi(pItemID); + uint32 SlotID = atoi(pSlotID); + + ItemPrototype* tmpItem = objmgr.GetItemPrototype(ItemID); + + bool added = false; + if(tmpItem) + { + switch(SlotID) + { + case 1: + pCreature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, ItemID); + added = true; + break; + case 2: + pCreature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY_01, ItemID); + added = true; + break; + case 3: + pCreature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY_02, ItemID); + added = true; + break; + default: + PSendSysMessage(LANG_ITEM_SLOT_NOT_EXIST,SlotID); + added = false; + break; + } + + if(added) + PSendSysMessage(LANG_ITEM_ADDED_TO_SLOT,ItemID,tmpItem->Name1,SlotID); + } + else + { + PSendSysMessage(LANG_ITEM_NOT_FOUND,ItemID); + return true; + } + */ + return true; +} +//---------------------------------------------------------- + bool ChatHandler::HandleExploreCheatCommand(const char* args) { if (!*args) @@ -4274,15 +4419,15 @@ bool ChatHandler::HandleExploreCheatCommand(const char* args) if (flag != 0) { - PSendSysMessage(LANG_YOU_SET_EXPLORE_ALL, chr->GetName()); + PSendSysMessage(LANG_YOU_SET_EXPLORE_ALL, GetNameLink(chr).c_str()); if (needReportToTarget(chr)) - ChatHandler(chr).PSendSysMessage(LANG_YOURS_EXPLORE_SET_ALL,GetName()); + ChatHandler(chr).PSendSysMessage(LANG_YOURS_EXPLORE_SET_ALL,GetNameLink().c_str()); } else { - PSendSysMessage(LANG_YOU_SET_EXPLORE_NOTHING, chr->GetName()); + PSendSysMessage(LANG_YOU_SET_EXPLORE_NOTHING, GetNameLink(chr).c_str()); if (needReportToTarget(chr)) - ChatHandler(chr).PSendSysMessage(LANG_YOURS_EXPLORE_SET_NOTHING,GetName()); + ChatHandler(chr).PSendSysMessage(LANG_YOURS_EXPLORE_SET_NOTHING,GetNameLink().c_str()); } for (uint8 i=0; i<128; i++) @@ -4319,36 +4464,6 @@ bool ChatHandler::HandleHoverCommand(const char* args) return true; } -bool ChatHandler::HandleWaterwalkCommand(const char* args) -{ - if(!args) - return false; - - Player *player = getSelectedPlayer(); - if(!player) - { - PSendSysMessage(LANG_NO_CHAR_SELECTED); - SetSentErrorMessage(true); - return false; - } - - if (strncmp(args, "on", 3) == 0) - player->SetMovement(MOVE_WATER_WALK); // ON - else if (strncmp(args, "off", 4) == 0) - player->SetMovement(MOVE_LAND_WALK); // OFF - else - { - SendSysMessage(LANG_USE_BOL); - return false; - } - - PSendSysMessage(LANG_YOU_SET_WATERWALK, args, player->GetName()); - if(needReportToTarget(player)) - ChatHandler(player).PSendSysMessage(LANG_YOUR_WATERWALK_SET, args, GetName()); - return true; - -} - bool ChatHandler::HandleLevelUpCommand(const char* args) { char* px = strtok((char*)args, " "); @@ -4380,8 +4495,8 @@ bool ChatHandler::HandleLevelUpCommand(const char* args) if(pname) // player by name { - name = pname; - if(!normalizePlayerName(name)) + name = extractPlayerNameFromLink(pname); + if(name.empty()) { SendSysMessage(LANG_PLAYER_NOT_FOUND); SetSentErrorMessage(true); @@ -4449,7 +4564,10 @@ bool ChatHandler::HandleLevelUpCommand(const char* args) } if(m_session->GetPlayer() != chr) // including chr==NULL - PSendSysMessage(LANG_YOU_CHANGE_LVL,name.c_str(),newlevel); + { + std::string nameLink = playerLink(name); + PSendSysMessage(LANG_YOU_CHANGE_LVL,nameLink.c_str(),newlevel); + } return true; } @@ -4458,8 +4576,6 @@ bool ChatHandler::HandleShowAreaCommand(const char* args) if (!*args) return false; - int area = atoi((char*)args); - Player *chr = getSelectedPlayer(); if (chr == NULL) { @@ -4468,10 +4584,11 @@ bool ChatHandler::HandleShowAreaCommand(const char* args) return false; } + int area = GetAreaFlagByAreaID(atoi((char*)args)); int offset = area / 32; uint32 val = (uint32)(1 << (area % 32)); - if(offset >= 128) + if(area<0 || offset >= 128) { SendSysMessage(LANG_BAD_VALUE); SetSentErrorMessage(true); @@ -4490,8 +4607,6 @@ bool ChatHandler::HandleHideAreaCommand(const char* args) if (!*args) return false; - int area = atoi((char*)args); - Player *chr = getSelectedPlayer(); if (chr == NULL) { @@ -4500,10 +4615,11 @@ bool ChatHandler::HandleHideAreaCommand(const char* args) return false; } + int area = GetAreaFlagByAreaID(atoi((char*)args)); int offset = area / 32; uint32 val = (uint32)(1 << (area % 32)); - if(offset >= 128) + if(area<0 || offset >= 128) { SendSysMessage(LANG_BAD_VALUE); SetSentErrorMessage(true); @@ -4517,7 +4633,7 @@ bool ChatHandler::HandleHideAreaCommand(const char* args) return true; } -bool ChatHandler::HandleUpdate(const char* args) +bool ChatHandler::HandleDebugUpdate(const char* args) { if(!*args) return false; @@ -4617,7 +4733,7 @@ bool ChatHandler::HandleChangeWeather(const char* args) return true; } -bool ChatHandler::HandleSetValue(const char* args) +bool ChatHandler::HandleDebugSetValue(const char* args) { if(!*args) return false; @@ -4668,7 +4784,7 @@ bool ChatHandler::HandleSetValue(const char* args) return true; } -bool ChatHandler::HandleGetValue(const char* args) +bool ChatHandler::HandleDebugGetValue(const char* args) { if(!*args) return false; @@ -4717,11 +4833,19 @@ bool ChatHandler::HandleGetValue(const char* args) return true; } -bool ChatHandler::HandleSet32Bit(const char* args) +bool ChatHandler::HandleDebugSet32Bit(const char* args) { if(!*args) return false; + Unit* target = getSelectedUnit(); + if(!target) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + SetSentErrorMessage(true); + return false; + } + char* px = strtok((char*)args, " "); char* py = strtok(NULL, " "); @@ -4735,13 +4859,14 @@ bool ChatHandler::HandleSet32Bit(const char* args) sLog.outDebug(GetTrinityString(LANG_SET_32BIT), Opcode, Value); - m_session->GetPlayer( )->SetUInt32Value( Opcode , 2^Value ); + uint32 iValue = Value ? 1 << (Value - 1) : 0; + target->SetUInt32Value( Opcode , iValue); - PSendSysMessage(LANG_SET_32BIT_FIELD, Opcode,1); + PSendSysMessage(LANG_SET_32BIT_FIELD, Opcode, iValue); return true; } -bool ChatHandler::HandleMod32Value(const char* args) +bool ChatHandler::HandleDebugMod32Value(const char* args) { if(!*args) return false; @@ -4773,7 +4898,7 @@ bool ChatHandler::HandleMod32Value(const char* args) return true; } -bool ChatHandler::HandleAddTeleCommand(const char * args) +bool ChatHandler::HandleTeleAddCommand(const char * args) { if(!*args) return false; @@ -4813,7 +4938,7 @@ bool ChatHandler::HandleAddTeleCommand(const char * args) return true; } -bool ChatHandler::HandleDelTeleCommand(const char * args) +bool ChatHandler::HandleTeleDelCommand(const char * args) { if(!*args) return false; @@ -4849,43 +4974,104 @@ 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->GetEffectMask(), + itr->second->GetAuraCharges(), itr->second->GetStackAmount(),itr->second->GetAuraSlot(), + 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->GetEffectMask(), + itr->second->GetAuraCharges(), itr->second->GetStackAmount(),itr->second->GetAuraSlot(), + 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++) { - Unit::AuraList const& uAuraList = unit->GetAurasByType(AuraType(i)); + Unit::AuraEffectList const& uAuraList = unit->GetAurasByType(AuraType(i)); if (uAuraList.empty()) continue; PSendSysMessage(LANG_COMMAND_TARGET_LISTAURATYPE, uAuraList.size(), i); - for (Unit::AuraList::const_iterator itr = uAuraList.begin(); itr != uAuraList.end(); ++itr) + for (Unit::AuraEffectList::const_iterator itr = uAuraList.begin(); itr != uAuraList.end(); ++itr) { - bool talent = GetTalentSpellCost((*itr)->GetId()) > 0; + char const* name = (*itr)->GetSpellProto()->SpellName[m_session->GetSessionDbcLocale()]; + + std::ostringstream ss_name; + ss_name << "|cffffffff|Hspell:" << (*itr)->GetId() << "|h[" << name << "]|h|r"; + 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())); + (*itr)->GetAmount()); } } return true; } +bool ChatHandler::HandleResetAchievementsCommand (const char * args) +{ + char* pName = strtok((char*)args, ""); + Player *player = NULL; + uint64 guid = 0; + if (pName) + { + std::string name = extractPlayerNameFromLink(pName); + if(name.empty()) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + + guid = objmgr.GetPlayerGUIDByName(name); + player = objmgr.GetPlayer(guid); + } + else + { + player = getSelectedPlayer(); + if(player) + guid = player->GetGUID(); + } + + if(!player && !guid) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + return true; + } + + if(player) + player->GetAchievementMgr().Reset(); + else if(guid) + AchievementMgr::DeleteFromDB(GUID_LOPART(guid)); + + return true; +} + bool ChatHandler::HandleResetHonorCommand (const char * args) { char* pName = strtok((char*)args, ""); Player *player = NULL; if (pName) { - std::string name = pName; - if(!normalizePlayerName(name)) + std::string name = extractPlayerNameFromLink(pName); + if(name.empty()) { SendSysMessage(LANG_PLAYER_NOT_FOUND); SetSentErrorMessage(true); return false; } - uint64 guid = objmgr.GetPlayerGUIDByName(name.c_str()); + uint64 guid = objmgr.GetPlayerGUIDByName(name); player = objmgr.GetPlayer(guid); } else @@ -4920,19 +5106,6 @@ static bool HandleResetStatsOrLevelHelper(Player* player) uint8 powertype = cEntry->powerType; - uint32 unitfield; - if(powertype == POWER_RAGE) - unitfield = 0x1100EE00; - else if(powertype == POWER_ENERGY) - unitfield = 0x00000000; - else if(powertype == POWER_MANA) - unitfield = 0x0000EE00; - else - { - sLog.outError("Invalid default powertype %u for player (class %u)",powertype,player->getClass()); - return false; - } - // reset m_form if no aura if(!player->HasAuraType(SPELL_AURA_MOD_SHAPESHIFT)) player->m_form = FORM_NONE; @@ -4962,9 +5135,7 @@ static bool HandleResetStatsOrLevelHelper(Player* player) } } - // set UNIT_FIELD_BYTES_1 to init state but preserve m_form value - player->SetUInt32Value(UNIT_FIELD_BYTES_1, unitfield); - player->SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY | UNIT_BYTE2_FLAG_UNK5 ); + player->SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_PVP ); player->SetByteValue(UNIT_FIELD_BYTES_2, 3, player->m_form); player->SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); @@ -4982,15 +5153,15 @@ bool ChatHandler::HandleResetLevelCommand(const char * args) Player *player = NULL; if (pName) { - std::string name = pName; - if(!normalizePlayerName(name)) + std::string name = extractPlayerNameFromLink(pName); + if(name.empty()) { SendSysMessage(LANG_PLAYER_NOT_FOUND); SetSentErrorMessage(true); return false; } - uint64 guid = objmgr.GetPlayerGUIDByName(name.c_str()); + uint64 guid = objmgr.GetPlayerGUIDByName(name); player = objmgr.GetPlayer(guid); } else @@ -5006,17 +5177,27 @@ bool ChatHandler::HandleResetLevelCommand(const char * args) if(!HandleResetStatsOrLevelHelper(player)) return false; - player->SetLevel(1); + // set starting level + uint32 start_level = player->getClass() != CLASS_DEATH_KNIGHT + ? sWorld.getConfig(CONFIG_START_PLAYER_LEVEL) + : sWorld.getConfig(CONFIG_START_HEROIC_PLAYER_LEVEL); + + player->SetLevel(start_level); + player->InitRunes(); player->InitStatsForLevel(true); player->InitTaxiNodesForLevel(); + player->InitGlyphsForLevel(); player->InitTalentForLevel(); player->SetUInt32Value(PLAYER_XP,0); // reset level to summoned pet - Pet* pet = player->GetPet(); - if(pet && pet->getPetType()==SUMMON_PET) + Guardian* pet = player->GetGuardianPet(); + if(pet) + { pet->InitStatsForLevel(1); - + if(pet->isPet()) + ((Pet*)pet)->InitTalentForLevel(); + } return true; } @@ -5026,15 +5207,15 @@ bool ChatHandler::HandleResetStatsCommand(const char * args) Player *player = NULL; if (pName) { - std::string name = pName; - if(!normalizePlayerName(name)) + std::string name = extractPlayerNameFromLink(pName); + if(name.empty()) { SendSysMessage(LANG_PLAYER_NOT_FOUND); SetSentErrorMessage(true); return false; } - uint64 guid = objmgr.GetPlayerGUIDByName(name.c_str()); + uint64 guid = objmgr.GetPlayerGUIDByName(name); player = objmgr.GetPlayer(guid); } else @@ -5050,8 +5231,10 @@ bool ChatHandler::HandleResetStatsCommand(const char * args) if(!HandleResetStatsOrLevelHelper(player)) return false; + player->InitRunes(); player->InitStatsForLevel(true); player->InitTaxiNodesForLevel(); + player->InitGlyphsForLevel(); player->InitTalentForLevel(); return true; @@ -5064,9 +5247,8 @@ bool ChatHandler::HandleResetSpellsCommand(const char * args) uint64 playerGUID = 0; if (pName) { - std::string name = pName; - - if(!normalizePlayerName(name)) + std::string name = extractPlayerNameFromLink(pName); + if(name.empty()) { SendSysMessage(LANG_PLAYER_NOT_FOUND); SetSentErrorMessage(true); @@ -5075,7 +5257,7 @@ bool ChatHandler::HandleResetSpellsCommand(const char * args) player = objmgr.GetPlayer(name.c_str()); if(!player) - playerGUID = objmgr.GetPlayerGUIDByName(name.c_str()); + playerGUID = objmgr.GetPlayerGUIDByName(name); } else player = getSelectedPlayer(); @@ -5092,9 +5274,8 @@ bool ChatHandler::HandleResetSpellsCommand(const char * args) player->resetSpells(); ChatHandler(player).SendSysMessage(LANG_RESET_SPELLS); - if(m_session->GetPlayer()!=player) - PSendSysMessage(LANG_RESET_SPELLS_ONLINE,player->GetName()); + PSendSysMessage(LANG_RESET_SPELLS_ONLINE,GetNameLink(player).c_str()); } else { @@ -5112,8 +5293,8 @@ bool ChatHandler::HandleResetTalentsCommand(const char * args) uint64 playerGUID = 0; if (pName) { - std::string name = pName; - if(!normalizePlayerName(name)) + std::string name = extractPlayerNameFromLink(pName); + if(name.empty()) { SendSysMessage(LANG_PLAYER_NOT_FOUND); SetSentErrorMessage(true); @@ -5122,34 +5303,47 @@ bool ChatHandler::HandleResetTalentsCommand(const char * args) player = objmgr.GetPlayer(name.c_str()); if(!player) - playerGUID = objmgr.GetPlayerGUIDByName(name.c_str()); + playerGUID = objmgr.GetPlayerGUIDByName(name); } else player = getSelectedPlayer(); - if(!player && !playerGUID) - { - SendSysMessage(LANG_NO_CHAR_SELECTED); - SetSentErrorMessage(true); - return false; - } - if(player) { player->resetTalents(true); ChatHandler(player).SendSysMessage(LANG_RESET_TALENTS); - if(m_session->GetPlayer()!=player) - PSendSysMessage(LANG_RESET_TALENTS_ONLINE,player->GetName()); + PSendSysMessage(LANG_RESET_TALENTS_ONLINE,GetNameLink(player).c_str()); + + return true; } - else + else if (playerGUID) { CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '%u' WHERE guid = '%u'",uint32(AT_LOGIN_RESET_TALENTS), GUID_LOPART(playerGUID) ); - PSendSysMessage(LANG_RESET_TALENTS_OFFLINE,pName); + std::string nameLink = playerLink(pName); + PSendSysMessage(LANG_RESET_TALENTS_OFFLINE,nameLink.c_str()); + return true; + } + // Try reset talenents as Hunter Pet + Creature* creature = getSelectedCreature(); + if (creature && creature->isPet() && ((Pet *)creature)->getPetType() == HUNTER_PET) + { + ((Pet *)creature)->resetTalents(true); + Unit *owner = creature->GetOwner(); + if (owner && owner->GetTypeId() == TYPEID_PLAYER) + { + player = (Player *)owner; + ChatHandler(player).SendSysMessage(LANG_RESET_PET_TALENTS); + if(m_session->GetPlayer()!=player) + PSendSysMessage(LANG_RESET_PET_TALENTS_ONLINE,GetNameLink(player).c_str()); + } + return true; } - return true; + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; } bool ChatHandler::HandleResetAllCommand(const char * args) @@ -5333,7 +5527,7 @@ bool ChatHandler::HandleServerIdleShutDownCommand(const char* args) return true; } -bool ChatHandler::HandleAddQuest(const char* args) +bool ChatHandler::HandleQuestAdd(const char* args) { Player* player = getSelectedPlayer(); if(!player) @@ -5387,7 +5581,7 @@ bool ChatHandler::HandleAddQuest(const char* args) return true; } -bool ChatHandler::HandleRemoveQuest(const char* args) +bool ChatHandler::HandleQuestRemove(const char* args) { Player* player = getSelectedPlayer(); if(!player) @@ -5437,7 +5631,7 @@ bool ChatHandler::HandleRemoveQuest(const char* args) return true; } -bool ChatHandler::HandleCompleteQuest(const char* args) +bool ChatHandler::HandleQuestComplete(const char* args) { Player* player = getSelectedPlayer(); if(!player) @@ -5511,12 +5705,10 @@ bool ChatHandler::HandleCompleteQuest(const char* args) if(uint32 repFaction = pQuest->GetRepObjectiveFaction()) { uint32 repValue = pQuest->GetRepObjectiveValue(); - uint32 curRep = player->GetReputation(repFaction); + uint32 curRep = player->GetReputationMgr().GetReputation(repFaction); if(curRep < repValue) - { - FactionEntry const *factionEntry = sFactionStore.LookupEntry(repFaction); - player->SetFactionReputation(factionEntry,repValue); - } + if(FactionEntry const *factionEntry = sFactionStore.LookupEntry(repFaction)) + player->GetReputationMgr().SetReputation(factionEntry,repValue); } // If the quest requires money @@ -5545,7 +5737,7 @@ bool ChatHandler::HandleBanIPCommand(const char* args) bool ChatHandler::HandleBanHelper(BanMode mode, const char* args) { - if(!args) + if (!*args) return false; char* cnameOrIP = strtok ((char*)args, " "); @@ -5633,7 +5825,7 @@ bool ChatHandler::HandleUnBanIPCommand(const char* args) bool ChatHandler::HandleUnBanHelper(BanMode mode, const char* args) { - if(!args) + if (!*args) return false; char* cnameOrIP = strtok ((char*)args, " "); @@ -5676,7 +5868,7 @@ bool ChatHandler::HandleUnBanHelper(BanMode mode, const char* args) bool ChatHandler::HandleBanInfoAccountCommand(const char* args) { - if(!args) + if (!*args) return false; char* cname = strtok((char*)args, ""); @@ -5703,15 +5895,11 @@ bool ChatHandler::HandleBanInfoAccountCommand(const char* args) bool ChatHandler::HandleBanInfoCharacterCommand(const char* args) { - if(!args) - return false; - - char* cname = strtok ((char*)args, ""); - if(!cname) + if (!*args) return false; - std::string name = cname; - if(!normalizePlayerName(name)) + std::string name = extractPlayerNameFromLink((char*)args); + if(name.empty()) { SendSysMessage(LANG_PLAYER_NOT_FOUND); SetSentErrorMessage(true); @@ -5766,7 +5954,7 @@ bool ChatHandler::HandleBanInfoHelper(uint32 accountid, char const* accountname) bool ChatHandler::HandleBanInfoIPCommand(const char* args) { - if(!args) + if (!*args) return false; char* cIP = strtok ((char*)args, ""); @@ -6023,8 +6211,8 @@ bool ChatHandler::HandleRespawnCommand(const char* /*args*/) cell.data.Part.reserved = ALL_DISTRICT; cell.SetNoCreate(); - Trinity::RespawnDo u_do; - Trinity::WorldObjectWorker<Trinity::RespawnDo> worker(u_do); + MaNGOS::RespawnDo u_do; + MaNGOS::WorldObjectWorker<MaNGOS::RespawnDo> worker(pl,u_do); TypeContainerVisitor<Trinity::WorldObjectWorker<Trinity::RespawnDo>, GridTypeMapContainer > obj_worker(worker); CellLock<GridReadGuard> cell_lock(cell, p); @@ -6033,14 +6221,14 @@ bool ChatHandler::HandleRespawnCommand(const char* /*args*/) return true; } -bool ChatHandler::HandleFlyModeCommand(const char* args) +bool ChatHandler::HandleGMFlyCommand(const char* args) { - if(!args) + if (!*args) return false; - Unit *unit = getSelectedUnit(); - if (!unit || (unit->GetTypeId() != TYPEID_PLAYER)) - unit = m_session->GetPlayer(); + Player *target = getSelectedPlayer(); + if (!target) + target = m_session->GetPlayer(); WorldPacket data(12); if (strncmp(args, "on", 3) == 0) @@ -6052,16 +6240,16 @@ bool ChatHandler::HandleFlyModeCommand(const char* args) SendSysMessage(LANG_USE_BOL); return false; } - data.append(unit->GetPackGUID()); + data.append(target->GetPackGUID()); data << uint32(0); // unknown - unit->SendMessageToSet(&data, true); - PSendSysMessage(LANG_COMMAND_FLYMODE_STATUS, unit->GetName(), args); + target->SendMessageToSet(&data, true); + PSendSysMessage(LANG_COMMAND_FLYMODE_STATUS, GetNameLink(target).c_str(), args); return true; } -bool ChatHandler::HandleLoadPDumpCommand(const char *args) +bool ChatHandler::HandlePDumpLoadCommand(const char *args) { - if(!args) + if (!*args) return false; char * file = strtok((char*)args, " "); @@ -6170,33 +6358,9 @@ bool ChatHandler::HandleLoadPDumpCommand(const char *args) return true; } -bool ChatHandler::HandleNpcChangeEntryCommand(const char *args) +bool ChatHandler::HandlePDumpWriteCommand(const char *args) { - if(!args) - return false; - - uint32 newEntryNum = atoi(args); - if(!newEntryNum) - return false; - - Unit* unit = getSelectedUnit(); - if(!unit || unit->GetTypeId() != TYPEID_UNIT) - { - SendSysMessage(LANG_SELECT_CREATURE); - SetSentErrorMessage(true); - return false; - } - Creature* creature = (Creature*)unit; - if(creature->UpdateEntry(newEntryNum)) - SendSysMessage(LANG_DONE); - else - SendSysMessage(LANG_ERROR); - return true; -} - -bool ChatHandler::HandleWritePDumpCommand(const char *args) -{ - if(!args) + if (!*args) return false; char* file = strtok((char*)args, " "); @@ -6211,12 +6375,11 @@ bool ChatHandler::HandleWritePDumpCommand(const char *args) guid = atoi(p2); else { - std::string name = p2; - - if (!normalizePlayerName (name)) + std::string name = extractPlayerNameFromLink(p2); + if(name.empty()) { - SendSysMessage (LANG_PLAYER_NOT_FOUND); - SetSentErrorMessage (true); + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); return false; } @@ -6767,6 +6930,29 @@ bool ChatHandler::HandleServerSetMotdCommand(const char* args) return true; } +/// Set whether we accept new clients +bool ChatHandler::HandleServerSetClosedCommand(const char* args) +{ + std::string arg = args; + + if(args == "on") + { + SendSysMessage(LANG_WORLD_CLOSED); + sWorld.SetClosed(true); + return true; + } + if(args == "off") + { + SendSysMessage(LANG_WORLD_OPENED); + sWorld.SetClosed(false); + return true; + } + + SendSysMessage(LANG_USE_BOL); + SetSentErrorMessage(true); + return false; +} + /// Set/Unset the expansion level for an account bool ChatHandler::HandleAccountSetAddonCommand(const char* args) { @@ -6808,8 +6994,15 @@ bool ChatHandler::HandleAccountSetAddonCommand(const char* args) SetSentErrorMessage(true); return false; } + } + // Let set addon state only for lesser (strong) security level + // or to self account + if (m_session && m_session->GetAccountId () != account_id && + HasLowerSecurityAccount (NULL,account_id,true)) + return false; + int lev=atoi(szExp); //get int anyway (0 if error) if(lev < 0) return false; @@ -6828,9 +7021,13 @@ bool ChatHandler::HandleSendItemsCommand(const char* args) // format: name "subject text" "mail text" item1[:count1] item2[:count2] ... item12[:count12] - char* pName = strtok((char*)args, " "); - if(!pName) + std::string name = extractPlayerNameFromLink((char*)args); + if(name.empty()) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); return false; + } char* tail1 = strtok(NULL, ""); if(!tail1) @@ -6868,8 +7065,7 @@ bool ChatHandler::HandleSendItemsCommand(const char* args) if (!msgText) return false; - // pName, msgSubject, msgText isn't NUL after prev. check - std::string name = pName; + // msgSubject, msgText isn't NUL after prev. check std::string subject = msgSubject; std::string text = msgText; @@ -6904,17 +7100,17 @@ bool ChatHandler::HandleSendItemsCommand(const char* args) } uint32 item_count = itemCountStr ? atoi(itemCountStr) : 1; - if(item_count < 1 || item_proto->MaxCount && item_count > item_proto->MaxCount) + if(item_count < 1 || item_proto->MaxCount > 0 && item_count > uint32(item_proto->MaxCount)) { PSendSysMessage(LANG_COMMAND_INVALID_ITEM_COUNT, item_count,item_id); SetSentErrorMessage(true); return false; } - while(item_count > item_proto->Stackable) + while(item_count > item_proto->GetMaxStackSize()) { - items.push_back(ItemPair(item_id,item_proto->Stackable)); - item_count -= item_proto->Stackable; + items.push_back(ItemPair(item_id,item_proto->GetMaxStackSize())); + item_count -= item_proto->GetMaxStackSize(); } items.push_back(ItemPair(item_id,item_count)); @@ -6927,13 +7123,6 @@ bool ChatHandler::HandleSendItemsCommand(const char* args) } } - if(!normalizePlayerName(name)) - { - SendSysMessage(LANG_PLAYER_NOT_FOUND); - SetSentErrorMessage(true); - return false; - } - uint64 receiver_guid = objmgr.GetPlayerGUIDByName(name); if(!receiver_guid) { @@ -6965,7 +7154,8 @@ bool ChatHandler::HandleSendItemsCommand(const char* args) WorldSession::SendMailTo(receiver,messagetype, stationery, sender_guidlo, GUID_LOPART(receiver_guid), subject, itemTextId, &mi, 0, 0, MAIL_CHECK_MASK_NONE); - PSendSysMessage(LANG_MAIL_SENT, name.c_str()); + std::string nameLink = playerLink(name); + PSendSysMessage(LANG_MAIL_SENT, nameLink.c_str()); return true; } @@ -6977,9 +7167,13 @@ bool ChatHandler::HandleSendMoneyCommand(const char* args) /// format: name "subject text" "mail text" money - char* pName = strtok((char*)args, " "); - if (!pName) + std::string name = extractPlayerNameFromLink((char*)args); + if(name.empty()) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); return false; + } char* tail1 = strtok(NULL, ""); if (!tail1) @@ -7022,18 +7216,10 @@ bool ChatHandler::HandleSendMoneyCommand(const char* args) if (money <= 0) return false; - // pName, msgSubject, msgText isn't NUL after prev. check - std::string name = pName; + // msgSubject, msgText isn't NUL after prev. check std::string subject = msgSubject; std::string text = msgText; - if (!normalizePlayerName(name)) - { - SendSysMessage(LANG_PLAYER_NOT_FOUND); - SetSentErrorMessage(true); - return false; - } - uint64 receiver_guid = objmgr.GetPlayerGUIDByName(name); if (!receiver_guid) { @@ -7042,8 +7228,6 @@ bool ChatHandler::HandleSendMoneyCommand(const char* args) return false; } - uint32 mailId = objmgr.GenerateMailID(); - // from console show not existed sender uint32 sender_guidlo = m_session ? m_session->GetPlayer()->GetGUIDLow() : 0; @@ -7055,7 +7239,8 @@ bool ChatHandler::HandleSendMoneyCommand(const char* args) WorldSession::SendMailTo(receiver,messagetype, stationery, sender_guidlo, GUID_LOPART(receiver_guid), subject, itemTextId, NULL, money, 0, MAIL_CHECK_MASK_NONE); - PSendSysMessage(LANG_MAIL_SENT, name.c_str()); + std::string nameLink = playerLink(name); + PSendSysMessage(LANG_MAIL_SENT, nameLink.c_str()); return true; } @@ -7063,15 +7248,16 @@ bool ChatHandler::HandleSendMoneyCommand(const char* args) bool ChatHandler::HandleSendMessageCommand(const char* args) { ///- Get the command line arguments - char* name_str = strtok((char*)args, " "); - char* msg_str = strtok(NULL, ""); - - if(!name_str || !msg_str) + std::string name = extractPlayerNameFromLink((char*)args); + if(name.empty()) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); return false; + } - std::string name = name_str; - - if(!normalizePlayerName(name)) + char* msg_str = strtok(NULL, ""); + if(!msg_str) return false; ///- Find the player and check that he is not logging out. @@ -7096,7 +7282,8 @@ bool ChatHandler::HandleSendMessageCommand(const char* args) rPlayer->GetSession()->SendAreaTriggerMessage("|cffff0000[Message from administrator]:|r"); //Confirmation message - PSendSysMessage(LANG_SENDMESSAGE,name.c_str(),msg_str); + std::string nameLink = playerLink(name); + PSendSysMessage(LANG_SENDMESSAGE,nameLink.c_str(),msg_str); return true; } @@ -7120,30 +7307,27 @@ bool ChatHandler::HandleModifyGenderCommand(const char *args) return false; } + PlayerInfo const* info = objmgr.GetPlayerInfo(player->getRace(), player->getClass()); + if(!info) + return false; + char const* gender_str = (char*)args; int gender_len = strlen(gender_str); - uint32 displayId = player->GetNativeDisplayId(); - char const* gender_full = NULL; - uint32 new_displayId = displayId; Gender gender; - if(!strncmp(gender_str,"male",gender_len)) // MALE + if(!strncmp(gender_str, "male", gender_len)) // MALE { if(player->getGender() == GENDER_MALE) return true; - gender_full = "male"; - new_displayId = player->getRace() == RACE_BLOODELF ? displayId+1 : displayId-1; gender = GENDER_MALE; } - else if (!strncmp(gender_str,"female",gender_len)) // FEMALE + else if (!strncmp(gender_str, "female", gender_len)) // FEMALE { if(player->getGender() == GENDER_FEMALE) return true; - gender_full = "female"; - new_displayId = player->getRace() == RACE_BLOODELF ? displayId-1 : displayId+1; gender = GENDER_FEMALE; } else @@ -7158,12 +7342,16 @@ bool ChatHandler::HandleModifyGenderCommand(const char *args) player->SetByteValue(PLAYER_BYTES_3, 0, gender); // Change display ID - player->SetDisplayId(new_displayId); - player->SetNativeDisplayId(new_displayId); + player->SetDisplayId(gender ? info->displayId_f : info->displayId_m); + player->SetNativeDisplayId(gender ? info->displayId_f : info->displayId_m); + + char const* gender_full = gender ? "female" : "male"; + + PSendSysMessage(LANG_YOU_CHANGE_GENDER, GetNameLink(player).c_str(), gender_full); - PSendSysMessage(LANG_YOU_CHANGE_GENDER, player->GetName(),gender_full); if (needReportToTarget(player)) - ChatHandler(player).PSendSysMessage(LANG_YOUR_GENDER_CHANGED, gender_full,GetName()); + ChatHandler(player).PSendSysMessage(LANG_YOUR_GENDER_CHANGED, gender_full, GetNameLink().c_str()); + return true; } @@ -7251,26 +7439,10 @@ bool ChatHandler::HandleFreezeCommand(const char *args) } } - //stop movement and disable spells - uint32 spellID = 9454; //m_session->GetPlayer()->CastSpell(player,spellID,false); - SpellEntry const *spellInfo = sSpellStore.LookupEntry( spellID ); - if(spellInfo) //TODO: Change the duration of the aura to -1 instead of 5000000 - { - for(uint32 i = 0;i<3;i++) - { - uint8 eff = spellInfo->Effect[i]; - if (eff>=TOTAL_SPELL_EFFECTS) - continue; - if( eff == SPELL_EFFECT_APPLY_AREA_AURA_PARTY || eff == SPELL_EFFECT_APPLY_AURA || - eff == SPELL_EFFECT_PERSISTENT_AREA_AURA || eff == SPELL_EFFECT_APPLY_AREA_AURA_FRIEND || - eff == SPELL_EFFECT_APPLY_AREA_AURA_ENEMY) - { - Aura *Aur = CreateAura(spellInfo, i, NULL, player); - player->AddAura(Aur); - } - } - } + SpellEntry const *spellInfo = sSpellStore.LookupEntry( 9454 ); + Aura *Aur = new Aura(spellInfo, 1, NULL, player); + player->AddAura(Aur); //save player player->SaveToDB(); @@ -7309,8 +7481,7 @@ bool ChatHandler::HandleUnFreezeCommand(const char *args) player->RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); //allow movement and spells - uint32 spellID = 9454; - player->RemoveAurasDueToSpell(spellID); + player->RemoveAurasDueToSpell(9454); //save player player->SaveToDB(); @@ -7426,9 +7597,9 @@ bool ChatHandler::HandleUnPossessCommand(const char* args) Unit* pUnit = getSelectedUnit(); if(!pUnit) pUnit = m_session->GetPlayer(); - pUnit->RemoveSpellsCausingAura(SPELL_AURA_MOD_CHARM); - pUnit->RemoveSpellsCausingAura(SPELL_AURA_MOD_POSSESS_PET); - pUnit->RemoveSpellsCausingAura(SPELL_AURA_MOD_POSSESS); + pUnit->RemoveAurasByType(SPELL_AURA_MOD_CHARM); + pUnit->RemoveAurasByType(SPELL_AURA_MOD_POSSESS_PET); + pUnit->RemoveAurasByType(SPELL_AURA_MOD_POSSESS); return true; } diff --git a/src/game/LootHandler.cpp b/src/game/LootHandler.cpp index 6ca7ae1c535..6aded9b1f97 100644 --- a/src/game/LootHandler.cpp +++ b/src/game/LootHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -154,6 +154,7 @@ void WorldSession::HandleAutostoreLootItemOpcode( WorldPacket & recv_data ) --loot->unlootedCount; player->SendNewItem(newitem, uint32(item->count), false, false, true); + player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM, item->itemid, item->count); } else player->SendEquipError( msg, NULL, NULL ); @@ -233,6 +234,7 @@ void WorldSession::HandleLootMoneyOpcode( WorldPacket & /*recv_data*/ ) for (std::vector<Player*>::iterator i = playersNear.begin(); i != playersNear.end(); ++i) { (*i)->ModifyMoney( money_per_player ); + (*i)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY, money_per_player); //Offset surely incorrect, but works WorldPacket data( SMSG_LOOT_MONEY_NOTIFY, 4 ); data << uint32(money_per_player); @@ -240,7 +242,10 @@ void WorldSession::HandleLootMoneyOpcode( WorldPacket & /*recv_data*/ ) } } else + { player->ModifyMoney( pLoot->gold ); + player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY, pLoot->gold); + } pLoot->gold = 0; pLoot->NotifyMoneyRemoved(); } @@ -326,7 +331,7 @@ void WorldSession::DoLootRelease( uint64 lguid ) int32 ReqValue = 175; LockEntry const *lockInfo = sLockStore.LookupEntry(go->GetGOInfo()->chest.lockId); if(lockInfo) - ReqValue = lockInfo->requiredminingskill; + ReqValue = lockInfo->Skill[0]; float skill = float(player->GetSkillValue(SKILL_MINING))/(ReqValue+25); double chance = pow(0.8*chance_rate,4*(1/double(max_amount))*double(uses)); if(roll_chance_f(100*chance+skill)) @@ -383,14 +388,22 @@ void WorldSession::DoLootRelease( uint64 lguid ) Item *pItem = player->GetItemByGuid(lguid ); if(!pItem) return; - if( (pItem->GetProto()->BagFamily & BAG_FAMILY_MASK_MINING_SUPP) && - pItem->GetProto()->Class == ITEM_CLASS_TRADE_GOODS && - pItem->GetCount() >= 5) + + ItemPrototype const* proto = pItem->GetProto(); + + // destroy only 5 items from stack in case prospecting and milling + if( (proto->BagFamily & (BAG_FAMILY_MASK_MINING_SUPP|BAG_FAMILY_MASK_HERBS)) && + proto->Class == ITEM_CLASS_TRADE_GOODS) { pItem->m_lootGenerated = false; pItem->loot.clear(); - uint32 count = 5; + uint32 count = pItem->GetCount(); + + // >=5 checked in spell code, but will work for cheating cases also with removing from another stacks. + if(count > 5) + count = 5; + player->DestroyItemCount(pItem, count, true); } else @@ -477,7 +490,7 @@ void WorldSession::HandleLootMasterGiveOpcode( WorldPacket & recv_data ) if (slotid > pLoot->items.size()) { - sLog.outDebug("AutoLootItem: Player %s might be using a hack! (slot %d, size %d)",GetPlayer()->GetName(), slotid, pLoot->items.size()); + sLog.outDebug("AutoLootItem: Player %s might be using a hack! (slot %d, size %lu)",GetPlayer()->GetName(), slotid, (unsigned long)pLoot->items.size()); return; } @@ -495,6 +508,7 @@ void WorldSession::HandleLootMasterGiveOpcode( WorldPacket & recv_data ) // not move item from loot to target inventory Item * newitem = target->StoreNewItem( dest, item.itemid, true, item.randomPropertyId ); target->SendNewItem(newitem, uint32(item.count), false, false, true ); + target->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM, item.itemid, item.count); // mark as looted item.count=0; diff --git a/src/game/LootMgr.cpp b/src/game/LootMgr.cpp index c8511c3deeb..a3dd4167be2 100644 --- a/src/game/LootMgr.cpp +++ b/src/game/LootMgr.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,6 +25,7 @@ #include "World.h" #include "Util.h" #include "SharedDefines.h" +#include "SpellMgr.h" static Rates const qualityToRate[MAX_ITEM_QUALITY] = { RATE_DROP_ITEM_POOR, // ITEM_QUALITY_POOR @@ -36,16 +37,18 @@ static Rates const qualityToRate[MAX_ITEM_QUALITY] = { RATE_DROP_ITEM_ARTIFACT, // ITEM_QUALITY_ARTIFACT }; -LootStore LootTemplates_Creature( "creature_loot_template", "creature entry"); -LootStore LootTemplates_Disenchant( "disenchant_loot_template", "item disenchant id"); -LootStore LootTemplates_Fishing( "fishing_loot_template", "area id"); -LootStore LootTemplates_Gameobject( "gameobject_loot_template", "gameobject entry"); -LootStore LootTemplates_Item( "item_loot_template", "item entry"); -LootStore LootTemplates_Pickpocketing("pickpocketing_loot_template","creature pickpocket lootid"); -LootStore LootTemplates_Prospecting( "prospecting_loot_template", "item entry"); -LootStore LootTemplates_QuestMail( "quest_mail_loot_template", "quest id"); -LootStore LootTemplates_Reference( "reference_loot_template", "reference id"); -LootStore LootTemplates_Skinning( "skinning_loot_template", "creature skinning id"); +LootStore LootTemplates_Creature( "creature_loot_template", "creature entry", true); +LootStore LootTemplates_Disenchant( "disenchant_loot_template", "item disenchant id", true); +LootStore LootTemplates_Fishing( "fishing_loot_template", "area id", true); +LootStore LootTemplates_Gameobject( "gameobject_loot_template", "gameobject entry", true); +LootStore LootTemplates_Item( "item_loot_template", "item entry", true); +LootStore LootTemplates_Milling( "milling_loot_template", "item entry (herb)", true); +LootStore LootTemplates_Pickpocketing("pickpocketing_loot_template","creature pickpocket lootid", true); +LootStore LootTemplates_Prospecting( "prospecting_loot_template", "item entry (ore)", true); +LootStore LootTemplates_QuestMail( "quest_mail_loot_template", "quest id (with mail template)",false); +LootStore LootTemplates_Reference( "reference_loot_template", "reference id", false); +LootStore LootTemplates_Skinning( "skinning_loot_template", "creature skinning id", true); +LootStore LootTemplates_Spell( "spell_loot_template", "spell id (explicitly discovering ability)",false); class LootTemplate::LootGroup // A set of loot definitions for items (refs are not allowed) @@ -114,11 +117,18 @@ void LootStore::LoadLootTable() float chanceOrQuestChance = fields[2].GetFloat(); uint8 group = fields[3].GetUInt8(); int32 mincountOrRef = fields[4].GetInt32(); - uint8 maxcount = fields[5].GetUInt8(); + uint32 maxcount = fields[5].GetUInt32(); ConditionType condition = (ConditionType)fields[6].GetUInt8(); uint32 cond_value1 = fields[7].GetUInt32(); uint32 cond_value2 = fields[8].GetUInt32(); + if(maxcount > std::numeric_limits<uint8>::max()) + { + sLog.outErrorDb("Table '%s' entry %d item %d: maxcount value (%u) to large. must be less %u - skipped", GetName(), entry, item, maxcount,std::numeric_limits<uint8>::max()); + continue; // error already printed to log/console. + } + + if(!PlayerCondition::IsValid(condition,cond_value1, cond_value2)) { sLog.outErrorDb("... in table '%s' entry %u item %u", GetName(), entry, item); @@ -159,7 +169,7 @@ void LootStore::LoadLootTable() Verify(); // Checks validity of the loot store sLog.outString(); - sLog.outString( ">> Loaded %u loot definitions (%d templates)", count, m_LootTemplates.size()); + sLog.outString( ">> Loaded %u loot definitions (%lu templates)", count, (unsigned long)m_LootTemplates.size()); } else { @@ -230,17 +240,17 @@ void LootStore::ReportNotExistedId(uint32 id) const // Checks if the entry (quest, non-quest, reference) takes it's chance (at loot generation) // RATE_DROP_ITEMS is no longer used for all types of entries -bool LootStoreItem::Roll() const +bool LootStoreItem::Roll(bool rate) const { if(chance>=100.f) return true; if(mincountOrRef < 0) // reference case - return roll_chance_f(chance*sWorld.getRate(RATE_DROP_ITEM_REFERENCED)); + return roll_chance_f(chance* (rate ? sWorld.getRate(RATE_DROP_ITEM_REFERENCED) : 1.0f)); ItemPrototype const *pProto = objmgr.GetItemPrototype(itemid); - float qualityModifier = pProto ? sWorld.getRate(qualityToRate[pProto->Quality]) : 1.0f; + float qualityModifier = pProto && rate ? sWorld.getRate(qualityToRate[pProto->Quality]) : 1.0f; return roll_chance_f(chance*qualityModifier); } @@ -248,6 +258,12 @@ bool LootStoreItem::Roll() const // Checks correctness of values bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const { + if(group >= 1 << 7) // it stored in 7 bit field + { + sLog.outErrorDb("Table '%s' entry %d item %d: group (%u) must be less %u - skipped", store.GetName(), entry, itemid, group, 1 << 7); + return false; + } + if (mincountOrRef == 0) { sLog.outErrorDb("Table '%s' entry %d item %d: wrong mincountOrRef (%d) - skipped", store.GetName(), entry, itemid, mincountOrRef); @@ -275,6 +291,13 @@ bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const store.GetName(), entry, itemid, chance); return false; } + + if( maxcount < mincountOrRef) // wrong max count + { + sLog.outErrorDb("Table '%s' entry %d item %d: max count (%u) less that min count (%i) - skipped", store.GetName(), entry, itemid, uint32(maxcount), mincountOrRef); + return false; + } + } else // mincountOrRef < 0 { @@ -366,8 +389,12 @@ void Loot::AddItem(LootStoreItem const & item) } // Calls processor of corresponding LootTemplate (which handles everything including references) -void Loot::FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner) +void Loot::FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner, bool personal) { + // Must be provided + if(!loot_owner) + return; + LootTemplate const* tab = store.GetLootFor(loot_id); if (!tab) @@ -379,37 +406,36 @@ void Loot::FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner) items.reserve(MAX_NR_LOOT_ITEMS); quest_items.reserve(MAX_NR_QUEST_ITEMS); - tab->Process(*this, store); // Processing is done there, callback via Loot::AddItem() + tab->Process(*this, store,store.IsRatesAllowed ()); // Processing is done there, callback via Loot::AddItem() - // Setting access rights fow group-looting case - if(!loot_owner) - return; + // Setting access rights for group loot case Group * pGroup=loot_owner->GetGroup(); - if(!pGroup) - return; - for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) + if(!personal && pGroup) { - //fill the quest item map for every player in the recipient's group - Player* pl = itr->getSource(); - if(!pl) - continue; - uint32 plguid = pl->GetGUIDLow(); - QuestItemMap::iterator qmapitr = PlayerQuestItems.find(plguid); - if (qmapitr == PlayerQuestItems.end()) - { - FillQuestLoot(pl); - } - qmapitr = PlayerFFAItems.find(plguid); - if (qmapitr == PlayerFFAItems.end()) - { - FillFFALoot(pl); - } - qmapitr = PlayerNonQuestNonFFAConditionalItems.find(plguid); - if (qmapitr == PlayerNonQuestNonFFAConditionalItems.end()) - { - FillNonQuestNonFFAConditionalLoot(pl); - } + for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) + if(Player* pl = itr->getSource()) + FillNotNormalLootFor(pl); } + // ... for personal loot + else + FillNotNormalLootFor(loot_owner); +} + +void Loot::FillNotNormalLootFor(Player* pl) +{ + uint32 plguid = pl->GetGUIDLow(); + + QuestItemMap::iterator qmapitr = PlayerQuestItems.find(plguid); + if (qmapitr == PlayerQuestItems.end()) + FillQuestLoot(pl); + + qmapitr = PlayerFFAItems.find(plguid); + if (qmapitr == PlayerFFAItems.end()) + FillFFALoot(pl); + + qmapitr = PlayerNonQuestNonFFAConditionalItems.find(plguid); + if (qmapitr == PlayerNonQuestNonFFAConditionalItems.end()) + FillNonQuestNonFFAConditionalLoot(pl); } QuestItemList* Loot::FillFFALoot(Player* player) @@ -640,6 +666,12 @@ LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, QuestItem **qite return item; } +uint32 Loot::GetMaxSlotInLootFor(Player* player) const +{ + QuestItemMap::const_iterator itr = PlayerQuestItems.find(player->GetGUIDLow()); + return items.size() + (itr != PlayerQuestItems.end() ? itr->second->size() : 0); +} + ByteBuffer& operator<<(ByteBuffer& b, LootItem const& li) { b << uint32(li.itemid); @@ -653,12 +685,19 @@ ByteBuffer& operator<<(ByteBuffer& b, LootItem const& li) ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) { + if (lv.permission == NONE_PERMISSION) + { + b << uint32(0); //gold + b << uint8(0); // item count + return b; // nothing output more + } + Loot &l = lv.loot; uint8 itemsShown = 0; //gold - b << uint32(lv.permission!=NONE_PERMISSION ? l.gold : 0); + b << uint32(l.gold); size_t count_pos = b.wpos(); // pos of item count byte b << uint8(0); // item count placeholder @@ -697,19 +736,21 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) } break; } - case NONE_PERMISSION: default: return b; // nothing output more } - if (lv.qlist) + QuestItemMap const& lootPlayerQuestItems = l.GetPlayerQuestItems(); + QuestItemMap::const_iterator q_itr = lootPlayerQuestItems.find(lv.viewer->GetGUIDLow()); + if (q_itr != lootPlayerQuestItems.end()) { - for (QuestItemList::iterator qi = lv.qlist->begin() ; qi != lv.qlist->end(); ++qi) + QuestItemList *q_list = q_itr->second; + for (QuestItemList::iterator qi = q_list->begin() ; qi != q_list->end(); ++qi) { LootItem &item = l.quest_items[qi->index]; if (!qi->is_looted && !item.is_looted) { - b << uint8(l.items.size() + (qi - lv.qlist->begin())); + b << uint8(l.items.size() + (qi - q_list->begin())); b << item; b << uint8(0); // allow loot ++itemsShown; @@ -717,9 +758,12 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) } } - if (lv.ffalist) + QuestItemMap const& lootPlayerFFAItems = l.GetPlayerFFAItems(); + QuestItemMap::const_iterator ffa_itr = lootPlayerFFAItems.find(lv.viewer->GetGUIDLow()); + if (ffa_itr != lootPlayerFFAItems.end()) { - for (QuestItemList::iterator fi = lv.ffalist->begin() ; fi != lv.ffalist->end(); ++fi) + QuestItemList *ffa_list = ffa_itr->second; + for (QuestItemList::iterator fi = ffa_list->begin() ; fi != ffa_list->end(); ++fi) { LootItem &item = l.items[fi->index]; if (!fi->is_looted && !item.is_looted) @@ -731,9 +775,12 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) } } - if (lv.conditionallist) + QuestItemMap const& lootPlayerNonQuestNonFFAConditionalItems = l.GetPlayerNonQuestNonFFAConditionalItems(); + QuestItemMap::const_iterator nn_itr = lootPlayerNonQuestNonFFAConditionalItems.find(lv.viewer->GetGUIDLow()); + if (nn_itr != lootPlayerNonQuestNonFFAConditionalItems.end()) { - for (QuestItemList::iterator ci = lv.conditionallist->begin() ; ci != lv.conditionallist->end(); ++ci) + QuestItemList *conditional_list = nn_itr->second; + for (QuestItemList::iterator ci = conditional_list->begin() ; ci != conditional_list->end(); ++ci) { LootItem &item = l.items[ci->index]; if (!ci->is_looted && !item.is_looted) @@ -776,7 +823,6 @@ LootStoreItem const * LootTemplate::LootGroup::Roll() const if(ExplicitlyChanced[i].chance>=100.f) return &ExplicitlyChanced[i]; - ItemPrototype const *pProto = objmgr.GetItemPrototype(ExplicitlyChanced[i].itemid); Roll -= ExplicitlyChanced[i].chance; if (Roll < 0) return &ExplicitlyChanced[i]; @@ -900,7 +946,7 @@ void LootTemplate::AddEntry(LootStoreItem& item) } // Rolls for every item in the template and adds the rolled items the the loot -void LootTemplate::Process(Loot& loot, LootStore const& store, uint8 groupId) const +void LootTemplate::Process(Loot& loot, LootStore const& store, bool rate, uint8 groupId) const { if (groupId) // Group reference uses own processing of the group { @@ -914,7 +960,7 @@ void LootTemplate::Process(Loot& loot, LootStore const& store, uint8 groupId) co // Rolling non-grouped items for (LootStoreItemList::const_iterator i = Entries.begin() ; i != Entries.end() ; ++i ) { - if ( !i->Roll() ) + if (!i->Roll(rate)) continue; // Bad luck for the entry if (i->mincountOrRef < 0) // References processing @@ -925,7 +971,7 @@ void LootTemplate::Process(Loot& loot, LootStore const& store, uint8 groupId) co continue; // Error message already printed at loading stage for (uint32 loop=0; loop < i->maxcount; ++loop )// Ref multiplicator - Referenced->Process(loot, store, i->group); // Ref processing + Referenced->Process(loot, store, rate, i->group); } else // Plain entries (not a reference, not grouped) loot.AddItem(*i); // Chance is already checked, just add @@ -995,7 +1041,7 @@ bool LootTemplate::HasQuestDropForPlayer(LootTemplateMap const& store, Player co // Now checking groups for (LootGroups::const_iterator i = Groups.begin(); i != Groups.end(); ++i ) - if (i->HasQuestDrop()) + if (i->HasQuestDropForPlayer(player)) return true; return false; @@ -1137,6 +1183,29 @@ void LoadLootTemplates_Item() LootTemplates_Item.ReportUnusedIds(ids_set); } +void LoadLootTemplates_Milling() +{ + LootIdSet ids_set; + LootTemplates_Milling.LoadAndCollectLootIds(ids_set); + + // remove real entries and check existence loot + for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i ) + { + ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype>(i); + if(!proto) + continue; + + if((proto->BagFamily & BAG_FAMILY_MASK_HERBS)==0) + continue; + + if(ids_set.count(proto->ItemId)) + ids_set.erase(proto->ItemId); + } + + // output error for any still listed (not referenced from appropriate table) ids + LootTemplates_Milling.ReportUnusedIds(ids_set); +} + void LoadLootTemplates_Pickpocketing() { LootIdSet ids_set, ids_setUsed; @@ -1170,9 +1239,17 @@ void LoadLootTemplates_Prospecting() // remove real entries and check existence loot for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i ) - if(ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype>(i)) - if(ids_set.count(proto->ItemId)) - ids_set.erase(proto->ItemId); + { + ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype>(i); + if(!proto) + continue; + + if((proto->BagFamily & BAG_FAMILY_MASK_MINING_SUPP)==0) + continue; + + if(ids_set.count(proto->ItemId)) + ids_set.erase(proto->ItemId); + } // output error for any still listed (not referenced from appropriate table) ids LootTemplates_Prospecting.ReportUnusedIds(ids_set); @@ -1186,8 +1263,17 @@ void LoadLootTemplates_QuestMail() // remove real entries and check existence loot ObjectMgr::QuestMap const& questMap = objmgr.GetQuestTemplates(); for(ObjectMgr::QuestMap::const_iterator itr = questMap.begin(); itr != questMap.end(); ++itr ) + { + if(!itr->second->GetRewMailTemplateId()) + continue; + if(ids_set.count(itr->first)) ids_set.erase(itr->first); + /* disabled reporting: some quest mails not include items + else + LootTemplates_QuestMail.ReportNotExistedId(itr->first); + */ + } // output error for any still listed (not referenced from appropriate table) ids LootTemplates_QuestMail.ReportUnusedIds(ids_set); @@ -1219,6 +1305,37 @@ void LoadLootTemplates_Skinning() LootTemplates_Skinning.ReportUnusedIds(ids_set); } +void LoadLootTemplates_Spell() +{ + LootIdSet ids_set; + LootTemplates_Spell.LoadAndCollectLootIds(ids_set); + + // remove real entries and check existence loot + for(uint32 spell_id = 1; spell_id < sSpellStore.GetNumRows(); ++spell_id) + { + SpellEntry const* spellInfo = sSpellStore.LookupEntry (spell_id); + if(!spellInfo) + continue; + + // possible cases + if( !IsLootCraftingSpell(spellInfo)) + continue; + + if(!ids_set.count(spell_id)) + { + // not report about not trainable spells (optionally supported by DB) except with SPELL_ATTR_EX2_UNK14 (clams) + // 61756 (Northrend Inscription Research (FAST QA VERSION) for example + if ((spellInfo->Attributes & SPELL_ATTR_UNK5) || (spellInfo->AttributesEx2 & SPELL_ATTR_EX2_UNK14)) + LootTemplates_Spell.ReportNotExistedId(spell_id); + } + else + ids_set.erase(spell_id); + } + + // output error for any still listed (not referenced from appropriate table) ids + LootTemplates_QuestMail.ReportUnusedIds(ids_set); +} + void LoadLootTemplates_Reference() { LootIdSet ids_set; @@ -1229,6 +1346,7 @@ void LoadLootTemplates_Reference() LootTemplates_Fishing.CheckLootRefs(&ids_set); LootTemplates_Gameobject.CheckLootRefs(&ids_set); LootTemplates_Item.CheckLootRefs(&ids_set); + LootTemplates_Milling.CheckLootRefs(&ids_set); LootTemplates_Pickpocketing.CheckLootRefs(&ids_set); LootTemplates_Skinning.CheckLootRefs(&ids_set); LootTemplates_Disenchant.CheckLootRefs(&ids_set); diff --git a/src/game/LootMgr.h b/src/game/LootMgr.h index 8c64531bf4a..79d292cf8d4 100644 --- a/src/game/LootMgr.h +++ b/src/game/LootMgr.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -65,19 +65,19 @@ struct LootStoreItem uint32 itemid; // id of the item float chance; // always positive, chance to drop for both quest and non-quest items, chance to be used for refs int32 mincountOrRef; // mincount for drop items (positive) or minus referenced TemplateleId (negative) - uint8 group :8; + uint8 group :7; + bool needs_quest :1; // quest drop (negative ChanceOrQuestChance in DB) uint8 maxcount :8; // max drop count for the item (mincountOrRef positive) or Ref multiplicator (mincountOrRef negative) uint16 conditionId :16; // additional loot condition Id - bool needs_quest :1; // quest drop (negative ChanceOrQuestChance in DB) // Constructor, converting ChanceOrQuestChance -> (chance, needs_quest) // displayid is filled in IsValid() which must be called after LootStoreItem(uint32 _itemid, float _chanceOrQuestChance, int8 _group, uint8 _conditionId, int32 _mincountOrRef, uint8 _maxcount) : itemid(_itemid), chance(fabs(_chanceOrQuestChance)), mincountOrRef(_mincountOrRef), - group(_group), maxcount(_maxcount), conditionId(_conditionId), - needs_quest(_chanceOrQuestChance < 0) {} + group(_group), needs_quest(_chanceOrQuestChance < 0), maxcount(_maxcount), conditionId(_conditionId) + {} - bool Roll() const; // Checks if the entry takes it's chance (at loot generation) + bool Roll(bool rate) const; // Checks if the entry takes it's chance (at loot generation) bool IsValid(LootStore const& store, uint32 entry) const; // Checks correctness of values }; @@ -129,7 +129,8 @@ typedef std::set<uint32> LootIdSet; class LootStore { public: - explicit LootStore(char const* name, char const* entryName) : m_name(name), m_entryName(entryName) {} + explicit LootStore(char const* name, char const* entryName, bool ratesAllowed) + : m_name(name), m_entryName(entryName), m_ratesAllowed(ratesAllowed) {} virtual ~LootStore() { Clear(); } void Verify() const; @@ -147,6 +148,7 @@ class LootStore char const* GetName() const { return m_name; } char const* GetEntryName() const { return m_entryName; } + bool IsRatesAllowed() const { return m_ratesAllowed; } protected: void LoadLootTable(); void Clear(); @@ -154,6 +156,7 @@ class LootStore LootTemplateMap m_LootTemplates; char const* m_name; char const* m_entryName; + bool m_ratesAllowed; }; class LootTemplate @@ -165,7 +168,7 @@ class LootTemplate // Adds an entry to the group (at loading stage) void AddEntry(LootStoreItem& item); // Rolls for every item in the template and adds the rolled items the the loot - void Process(Loot& loot, LootStore const& store, uint8 GroupId = 0) const; + void Process(Loot& loot, LootStore const& store, bool rate, uint8 GroupId = 0) const; // True if template includes at least 1 quest drop entry bool HasQuestDrop(LootTemplateMap const& store, uint8 GroupId = 0) const; @@ -207,19 +210,20 @@ class LootValidatorRefManager : public RefManager<Loot, LootValidatorRef> }; //===================================================== +struct LootView; + +ByteBuffer& operator<<(ByteBuffer& b, LootItem const& li); +ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv); struct Loot { + friend ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv); + QuestItemMap const& GetPlayerQuestItems() const { return PlayerQuestItems; } QuestItemMap const& GetPlayerFFAItems() const { return PlayerFFAItems; } QuestItemMap const& GetPlayerNonQuestNonFFAConditionalItems() const { return PlayerNonQuestNonFFAConditionalItems; } - QuestItemList* FillFFALoot(Player* player); - QuestItemList* FillQuestLoot(Player* player); - QuestItemList* FillNonQuestNonFFAConditionalLoot(Player* player); - std::vector<LootItem> items; - std::vector<LootItem> quest_items; uint32 gold; uint8 unlootedCount; @@ -235,18 +239,19 @@ struct Loot // void clear(); void clear() { - items.clear(); gold = 0; PlayersLooting.clear(); for (QuestItemMap::iterator itr = PlayerQuestItems.begin(); itr != PlayerQuestItems.end(); ++itr) delete itr->second; + PlayerQuestItems.clear(); + for (QuestItemMap::iterator itr = PlayerFFAItems.begin(); itr != PlayerFFAItems.end(); ++itr) delete itr->second; + PlayerFFAItems.clear(); + for (QuestItemMap::iterator itr = PlayerNonQuestNonFFAConditionalItems.begin(); itr != PlayerNonQuestNonFFAConditionalItems.end(); ++itr) delete itr->second; - - PlayerQuestItems.clear(); - PlayerFFAItems.clear(); PlayerNonQuestNonFFAConditionalItems.clear(); + PlayersLooting.clear(); items.clear(); quest_items.clear(); gold = 0; @@ -264,13 +269,21 @@ struct Loot void RemoveLooter(uint64 GUID) { PlayersLooting.erase(GUID); } void generateMoneyLoot(uint32 minAmount, uint32 maxAmount); - void FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner); + void FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner, bool personal); // Inserts the item into the loot (called by LootTemplate processors) void AddItem(LootStoreItem const & item); LootItem* LootItemInSlot(uint32 lootslot, Player* player, QuestItem** qitem = NULL, QuestItem** ffaitem = NULL, QuestItem** conditem = NULL); + uint32 GetMaxSlotInLootFor(Player* player) const; + private: + void FillNotNormalLootFor(Player* player); + QuestItemList* FillFFALoot(Player* player); + QuestItemList* FillQuestLoot(Player* player); + QuestItemList* FillNonQuestNonFFAConditionalLoot(Player* player); + + std::vector<LootItem> quest_items; std::set<uint64> PlayersLooting; QuestItemMap PlayerQuestItems; QuestItemMap PlayerFFAItems; @@ -278,40 +291,41 @@ struct Loot // All rolls are registered here. They need to know, when the loot is not valid anymore LootValidatorRefManager i_LootValidatorRefManager; - }; struct LootView { Loot &loot; - QuestItemList *qlist; - QuestItemList *ffalist; - QuestItemList *conditionallist; Player *viewer; PermissionTypes permission; - LootView(Loot &_loot, QuestItemList *_qlist, QuestItemList *_ffalist, QuestItemList *_conditionallist, Player *_viewer,PermissionTypes _permission = ALL_PERMISSION) - : loot(_loot), qlist(_qlist), ffalist(_ffalist), conditionallist(_conditionallist), viewer(_viewer), permission(_permission) {} + LootView(Loot &_loot, Player *_viewer,PermissionTypes _permission = ALL_PERMISSION) + : loot(_loot), viewer(_viewer), permission(_permission) {} }; extern LootStore LootTemplates_Creature; extern LootStore LootTemplates_Fishing; extern LootStore LootTemplates_Gameobject; extern LootStore LootTemplates_Item; +extern LootStore LootTemplates_Milling; extern LootStore LootTemplates_Pickpocketing; extern LootStore LootTemplates_Skinning; extern LootStore LootTemplates_Disenchant; extern LootStore LootTemplates_Prospecting; extern LootStore LootTemplates_QuestMail; +extern LootStore LootTemplates_Spell; void LoadLootTemplates_Creature(); void LoadLootTemplates_Fishing(); void LoadLootTemplates_Gameobject(); void LoadLootTemplates_Item(); +void LoadLootTemplates_Milling(); void LoadLootTemplates_Pickpocketing(); void LoadLootTemplates_Skinning(); void LoadLootTemplates_Disenchant(); void LoadLootTemplates_Prospecting(); void LoadLootTemplates_QuestMail(); + +void LoadLootTemplates_Spell(); void LoadLootTemplates_Reference(); inline void LoadLootTables() @@ -320,15 +334,16 @@ inline void LoadLootTables() LoadLootTemplates_Fishing(); LoadLootTemplates_Gameobject(); LoadLootTemplates_Item(); + LoadLootTemplates_Milling(); LoadLootTemplates_Pickpocketing(); LoadLootTemplates_Skinning(); LoadLootTemplates_Disenchant(); LoadLootTemplates_Prospecting(); LoadLootTemplates_QuestMail(); + LoadLootTemplates_Spell(); + LoadLootTemplates_Reference(); } -ByteBuffer& operator<<(ByteBuffer& b, LootItem const& li); -ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv); #endif diff --git a/src/game/Mail.cpp b/src/game/Mail.cpp index 5698fdf03f2..97a684a2700 100644 --- a/src/game/Mail.cpp +++ b/src/game/Mail.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,8 +29,8 @@ #include "UpdateMask.h" #include "Unit.h" #include "Language.h" -#include "Database/DBCStores.h" #include "AuctionHouseBot.h" +#include "DBCStores.h" void MailItem::deleteItem( bool inDB ) { @@ -55,6 +55,9 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data ) recv_data >> mailbox; recv_data >> receiver; + if (!objmgr.IsGameObjectOfTypeInRange(_player, mailbox, GAMEOBJECT_TYPE_MAILBOX)) + return; + // recheck CHECK_PACKET_SIZE(recv_data, 8+(receiver.size()+1)+1+1+4+4+1+4+4+8+1); @@ -125,9 +128,9 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data ) return; } - uint32 reqmoney = money + 30; - if (items_count) - reqmoney = money + (30 * items_count); + uint32 cost = items_count ? 30 * items_count : 30; // price hardcoded in client + + uint32 reqmoney = cost + money; if (pl->GetMoney() < reqmoney) { @@ -210,6 +213,7 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data ) } pl->ModifyMoney( -int32(reqmoney) ); + pl->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL, cost); bool needItemDelay = false; @@ -276,6 +280,10 @@ void WorldSession::HandleMarkAsRead(WorldPacket & recv_data ) uint64 mailbox; uint32 mailId; recv_data >> mailbox; + + if (!objmgr.IsGameObjectOfTypeInRange(_player, mailbox, GAMEOBJECT_TYPE_MAILBOX)) + return; + recv_data >> mailId; Player *pl = _player; Mail *m = pl->GetMail(mailId); @@ -299,6 +307,10 @@ void WorldSession::HandleMailDelete(WorldPacket & recv_data ) uint32 mailId; recv_data >> mailbox; recv_data >> mailId; + + if (!objmgr.IsGameObjectOfTypeInRange(_player, mailbox, GAMEOBJECT_TYPE_MAILBOX)) + return; + Player* pl = _player; pl->m_mailsUpdated = true; Mail *m = pl->GetMail(mailId); @@ -314,6 +326,10 @@ void WorldSession::HandleReturnToSender(WorldPacket & recv_data ) uint64 mailbox; uint32 mailId; recv_data >> mailbox; + + if (!objmgr.IsGameObjectOfTypeInRange(_player, mailbox, GAMEOBJECT_TYPE_MAILBOX)) + return; + recv_data >> mailId; Player *pl = _player; Mail *m = pl->GetMail(mailId); @@ -418,6 +434,10 @@ void WorldSession::HandleTakeItem(WorldPacket & recv_data ) uint32 mailId; uint32 itemId; recv_data >> mailbox; + + if (!objmgr.IsGameObjectOfTypeInRange(_player, mailbox, GAMEOBJECT_TYPE_MAILBOX)) + return; + recv_data >> mailId; recv_data >> itemId; // item guid low? Player* pl = _player; @@ -509,6 +529,10 @@ void WorldSession::HandleTakeMoney(WorldPacket & recv_data ) uint32 mailId; recv_data >> mailbox; recv_data >> mailId; + + if (!objmgr.IsGameObjectOfTypeInRange(_player, mailbox, GAMEOBJECT_TYPE_MAILBOX)) + return; + Player *pl = _player; Mail* m = pl->GetMail(mailId); @@ -540,9 +564,8 @@ void WorldSession::HandleGetMail(WorldPacket & recv_data ) uint64 mailbox; recv_data >> mailbox; - //GameObject* obj = ObjectAccessor::GetGameObject(_player, mailbox); - //if(!obj || !obj->IsMailBox()) - // return; + if (!objmgr.IsGameObjectOfTypeInRange(_player, mailbox, GAMEOBJECT_TYPE_MAILBOX)) + return; Player* pl = _player; @@ -601,7 +624,7 @@ void WorldSession::HandleGetMail(WorldPacket & recv_data ) data << (uint32) (*itr)->mailTemplateId; // mail template (MailTemplate.dbc) data << (*itr)->subject; // Subject string - once 00, when mail type = 3 - data << (uint8) item_count; + data << (uint8) item_count; // client limit is 0x10 for(uint8 i = 0; i < item_count; ++i) { @@ -612,7 +635,7 @@ void WorldSession::HandleGetMail(WorldPacket & recv_data ) data << (uint32) (item ? item->GetGUIDLow() : 0); // entry data << (uint32) (item ? item->GetEntry() : 0); - for(uint8 j = 0; j < 6; ++j) + for(uint8 j = 0; j < MAX_INSPECTED_ENCHANTMENT_SLOT; ++j) { // unsure data << (uint32) (item ? item->GetEnchantmentCharges((EnchantmentSlot)j) : 0); @@ -626,13 +649,15 @@ void WorldSession::HandleGetMail(WorldPacket & recv_data ) // unk data << (uint32) (item ? item->GetItemSuffixFactor() : 0); // stack count - data << (uint8) (item ? item->GetCount() : 0); + data << (uint32) (item ? item->GetCount() : 0); // charges data << (uint32) (item ? item->GetSpellCharges() : 0); // durability data << (uint32) (item ? item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY) : 0); // durability data << (uint32) (item ? item->GetUInt32Value(ITEM_FIELD_DURABILITY) : 0); + // unknown wotlk + data << (uint8) 0; } mails_count += 1; @@ -676,6 +701,9 @@ void WorldSession::HandleMailCreateTextItem(WorldPacket & recv_data ) recv_data >> mailbox >> mailId; + if (!objmgr.IsGameObjectOfTypeInRange(_player, mailbox, GAMEOBJECT_TYPE_MAILBOX)) + return; + Player *pl = _player; Mail* m = pl->GetMail(mailId); diff --git a/src/game/Mail.h b/src/game/Mail.h index 784028e6d5d..acf0d9346fe 100644 --- a/src/game/Mail.h +++ b/src/game/Mail.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/Makefile.am b/src/game/Makefile.am new file mode 100644 index 00000000000..47c072a1188 --- /dev/null +++ b/src/game/Makefile.am @@ -0,0 +1,847 @@ +# Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> +# +# Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +## Process this file with automake to produce Makefile.in + +## Sub-directories to parse + +## CPP flags for includes, defines, etc. +AM_CPPFLAGS = $(TRINI_INCLUDES) -I$(top_builddir)/src/shared -I$(srcdir) -I$(srcdir)/../../dep/include -I$(srcdir)/../framework -I$(srcdir)/../shared -I$(srcdir)/../shared/vmap -I$(srcdir)/../realmd -DSYSCONFDIR=\"$(sysconfdir)/\" + +## Build MaNGOS game library as convenience library. +# All libraries will be convenience libraries. Might be changed to shared +# later. +noinst_LIBRARIES = libmangosgame.a + +# libmangossgame library will later be reused by ... +libmangosgame_a_SOURCES = \ +<<<<<<< HEAD:src/game/Makefile.am +<<<<<<< HEAD:src/game/Makefile.am + AccountMgr.cpp \ + AccountMgr.h \ + AchievementMgr.h \ + AchievementMgr.cpp \ + AddonHandler.cpp \ + AddonHandler.h \ + AggressorAI.cpp \ + AggressorAI.h \ + AnimalRandomMovementGenerator.h \ + ArenaTeam.cpp \ + ArenaTeam.h \ + ArenaTeamHandler.cpp \ + AuctionHouseBot.cpp \ + AuctionHouseBot.h \ + AuctionHouseHandler.cpp \ + AuctionHouseMgr.cpp \ + AuctionHouseMgr.h \ + Bag.cpp \ + Bag.h \ + BattleGround.cpp \ + BattleGroundAA.cpp \ + BattleGroundAB.cpp \ + BattleGroundAV.cpp \ + BattleGroundBE.cpp \ + BattleGroundDS.cpp \ + BattleGroundEY.cpp \ + BattleGroundNA.cpp \ + BattleGroundRL.cpp \ + BattleGroundRV.cpp \ + BattleGroundSA.cpp \ + BattleGroundWS.cpp \ + BattleGround.h \ + BattleGroundAA.h \ + BattleGroundAB.h \ + BattleGroundAV.h \ + BattleGroundBE.h \ + BattleGroundDS.h \ + BattleGroundEY.h \ + BattleGroundNA.h \ + BattleGroundRL.h \ + BattleGroundRV.h \ + BattleGroundSA.h \ + BattleGroundWS.h \ + BattleGroundHandler.cpp \ + BattleGroundMgr.cpp \ + BattleGroundMgr.h \ + Calendar.cpp \ + Calendar.h \ + CalendarHandler.cpp \ + Cell.h \ + CellImpl.h \ + Channel.cpp \ + Channel.h \ + ChannelHandler.cpp \ + ChannelMgr.h \ + CharacterHandler.cpp \ + Chat.cpp \ + Chat.h \ + ChatHandler.cpp \ + CombatHandler.cpp \ + ConfusedMovementGenerator.cpp \ + ConfusedMovementGenerator.h \ + Corpse.cpp \ + Corpse.h \ + CreatureAI.cpp \ + CreatureAI.h \ + CreatureAIImpl.h \ + CreatureAIRegistry.cpp \ + CreatureAIRegistry.h \ + CreatureAISelector.cpp \ + CreatureAISelector.h \ + CreatureGroups.cpp \ + CreatureGroups.h \ + Creature.cpp \ + Creature.h \ + debugcmds.cpp \ + DestinationHolder.cpp \ + DestinationHolder.h \ + DestinationHolderImp.h \ + DuelHandler.cpp \ + DynamicObject.cpp \ + DynamicObject.h \ + FleeingMovementGenerator.cpp \ + FleeingMovementGenerator.h \ + Formulas.h \ + GameEventMgr.cpp \ + GameEventMgr.h \ + GameObject.cpp \ + GameObject.h \ + GlobalEvents.cpp \ + GlobalEvents.h \ + GossipDef.cpp \ + GossipDef.h \ + GridDefines.h \ + GridNotifiers.cpp \ + GridNotifiers.h \ + GridNotifiersImpl.h \ + GridStates.cpp \ + GridStates.h \ + Group.cpp \ + Group.h \ + GroupHandler.cpp \ + GuardAI.cpp \ + GuardAI.h \ + Guild.cpp \ + Guild.h \ + GuildHandler.cpp \ + HomeMovementGenerator.cpp \ + HomeMovementGenerator.h \ + HostilRefManager.cpp \ + HostilRefManager.h \ + IdleMovementGenerator.cpp \ + IdleMovementGenerator.h \ + InstanceData.cpp \ + InstanceData.h \ + InstanceSaveMgr.cpp \ + InstanceSaveMgr.h \ + Item.cpp \ + Item.h \ + ItemEnchantmentMgr.cpp \ + ItemEnchantmentMgr.h \ + ItemHandler.cpp \ + ItemPrototype.h \ + Language.h \ + Level0.cpp \ + Level1.cpp \ + Level2.cpp \ + Level3.cpp \ + LFGHandler.cpp \ + LootHandler.cpp \ + LootMgr.cpp \ + LootMgr.h \ + Mail.cpp \ + Mail.h \ + Map.cpp \ + Map.h \ + MapInstanced.cpp \ + MapInstanced.h \ + MapManager.cpp \ + MapManager.h \ + MapReference.h \ + MapRefManager.h \ + MiscHandler.cpp \ + MotionMaster.cpp \ + MotionMaster.h \ + MovementGenerator.cpp \ + MovementGenerator.h \ + MovementGeneratorImpl.h \ + MovementHandler.cpp \ + NPCHandler.cpp \ + NPCHandler.h \ + NullCreatureAI.cpp \ + NullCreatureAI.h \ + ObjectAccessor.cpp \ + ObjectAccessor.h \ + Object.cpp \ + ObjectDefines.h \ + ObjectGridLoader.cpp \ + ObjectGridLoader.h \ + Object.h \ + ObjectMgr.cpp \ + ObjectMgr.h \ + ObjectPosSelector.cpp \ + ObjectPosSelector.h \ + Opcodes.cpp \ + Opcodes.h \ + OutdoorPvP.cpp \ + OutdoorPvP.h \ + OutdoorPvPEP.cpp \ + OutdoorPvPEP.h \ + OutdoorPvPHP.cpp \ + OutdoorPvPHP.h \ + OutdoorPvPMgr.cpp \ + OutdoorPvPMgr.h \ + OutdoorPvPNA.cpp \ + OutdoorPvPNA.h \ + OutdoorPvPObjectiveAI.cpp \ + OutdoorPvPObjectiveAI.h \ + OutdoorPvPSI.cpp \ + OutdoorPvPSI.h \ + OutdoorPvPTF.cpp \ + OutdoorPvPTF.h \ + OutdoorPvPZM.cpp \ + OutdoorPvPZM.h \ + Path.h \ + PetAI.cpp \ + PetAI.h \ + Pet.cpp \ + Pet.h \ + PetHandler.cpp \ + PetitionsHandler.cpp \ + Player.cpp \ + Player.h \ + PlayerDump.cpp \ + PlayerDump.h \ + PossessedAI.cpp \ + PossessedAI.h \ + PointMovementGenerator.cpp \ + PointMovementGenerator.h \ + PoolHandler.cpp \ + PoolHandler.h \ + QueryHandler.cpp \ + QuestDef.cpp \ + QuestDef.h \ + QuestHandler.cpp \ + RandomMovementGenerator.cpp \ + RandomMovementGenerator.h \ + ReactorAI.cpp \ + ReactorAI.h \ + ScriptCalls.cpp \ + ScriptCalls.h \ + SharedDefines.h \ + SkillHandler.cpp \ + SpellAuraDefines.h \ + SpellAuras.cpp \ + SpellAuras.h \ + Spell.cpp \ + SpellEffects.cpp \ + Spell.h \ + SkillDiscovery.cpp \ + SkillDiscovery.h \ + SkillExtraItems.cpp \ + SkillExtraItems.h \ + SpellHandler.cpp \ + SocialMgr.cpp \ + SocialMgr.h \ + SpellMgr.cpp \ + SpellMgr.h \ + StatSystem.cpp \ + TargetedMovementGenerator.cpp \ + TargetedMovementGenerator.h \ + TaxiHandler.cpp \ + TemporarySummon.cpp \ + TemporarySummon.h \ + TotemAI.cpp \ + TotemAI.h \ + Totem.cpp \ + Totem.h \ + TradeHandler.cpp \ + Transports.cpp \ + Transports.h \ + ThreatManager.cpp \ + ThreatManager.h \ + TicketHandler.cpp \ + TicketMgr.cpp \ + TicketMgr.h \ + Traveller.h \ + Unit.cpp \ + Unit.h \ + UnitEvents.h \ + UpdateData.cpp \ + UpdateData.h \ + UpdateFields.h \ + UpdateMask.h \ + Vehicle.cpp \ + Vehicle.h \ + VoiceChatHandler.cpp \ + WaypointManager.cpp \ + WaypointManager.h \ + WaypointMovementGenerator.cpp \ + WaypointMovementGenerator.h \ + Weather.cpp \ + Weather.h \ + World.cpp \ + World.h \ + WorldLog.cpp \ + WorldLog.h \ + WorldSession.cpp \ + WorldSession.h \ + WorldSocket.cpp \ + WorldSocket.h \ + WorldSocketMgr.cpp \ + WorldSocketMgr.h \ + FollowerReference.cpp \ + FollowerReference.h \ + FollowerRefManager.h \ + GroupReference.cpp \ + GroupReference.h \ + GroupRefManager.h +======= + AccountMgr.cpp \ + AccountMgr.h \ + AchievementMgr.h \ + AchievementMgr.cpp \ + AggressorAI.cpp \ + AggressorAI.h \ + AnimalRandomMovementGenerator.h \ + ArenaTeam.cpp \ + ArenaTeam.h \ + ArenaTeamHandler.cpp \ + AuctionHouseHandler.cpp \ + AuctionHouseMgr.cpp \ + AuctionHouseMgr.h \ + Bag.cpp \ + Bag.h \ + BattleGround.cpp \ + BattleGroundAA.cpp \ + BattleGroundAB.cpp \ + BattleGroundAV.cpp \ + BattleGroundBE.cpp \ + BattleGroundDS.cpp \ + BattleGroundEY.cpp \ + BattleGroundNA.cpp \ + BattleGroundRL.cpp \ + BattleGroundRV.cpp \ + BattleGroundSA.cpp \ + BattleGroundWS.cpp \ + BattleGround.h \ + BattleGroundAA.h \ + BattleGroundAB.h \ + BattleGroundAV.h \ + BattleGroundBE.h \ + BattleGroundDS.h \ + BattleGroundEY.h \ + BattleGroundNA.h \ + BattleGroundRL.h \ + BattleGroundRV.h \ + BattleGroundSA.h \ + BattleGroundWS.h \ + BattleGroundHandler.cpp \ + BattleGroundMgr.cpp \ + BattleGroundMgr.h \ + Calendar.cpp \ + Calendar.h \ + CalendarHandler.cpp \ + Cell.h \ + CellImpl.h \ + Channel.cpp \ + Channel.h \ + ChannelHandler.cpp \ + ChannelMgr.h \ + CharacterHandler.cpp \ + Chat.cpp \ + Chat.h \ + ChatHandler.cpp \ + CombatHandler.cpp \ + ConfusedMovementGenerator.cpp \ + ConfusedMovementGenerator.h \ + Corpse.cpp \ + Corpse.h \ + CreatureAI.cpp \ + CreatureAI.h \ + CreatureAIImpl.h \ + CreatureAIRegistry.cpp \ + CreatureAIRegistry.h \ + CreatureAISelector.cpp \ + CreatureAISelector.h \ + Creature.cpp \ + Creature.h \ + DBCEnums.h \ + DBCfmt.h \ + DBCStores.cpp \ + DBCStores.h \ + DBCStructure.h \ + debugcmds.cpp \ + DestinationHolder.cpp \ + DestinationHolder.h \ + DestinationHolderImp.h \ + DuelHandler.cpp \ + DynamicObject.cpp \ + DynamicObject.h \ + FleeingMovementGenerator.cpp \ + FleeingMovementGenerator.h \ + Formulas.h \ + GameEventMgr.cpp \ + GameEventMgr.h \ + GameObject.cpp \ + GameObject.h \ + GlobalEvents.cpp \ + GlobalEvents.h \ + GMTicketHandler.cpp \ + GMTicketMgr.cpp \ + GMTicketMgr.h \ + GossipDef.cpp \ + GossipDef.h \ + GridDefines.h \ + GridNotifiers.cpp \ + GridNotifiers.h \ + GridNotifiersImpl.h \ + GridStates.cpp \ + GridStates.h \ + Group.cpp \ + Group.h \ + GroupHandler.cpp \ + GuardAI.cpp \ + GuardAI.h \ + Guild.cpp \ + Guild.h \ + GuildHandler.cpp \ + HomeMovementGenerator.cpp \ + HomeMovementGenerator.h \ + HostilRefManager.cpp \ + HostilRefManager.h \ + IdleMovementGenerator.cpp \ + IdleMovementGenerator.h \ + InstanceData.cpp \ + InstanceData.h \ + InstanceSaveMgr.cpp \ + InstanceSaveMgr.h \ + Item.cpp \ + Item.h \ + ItemEnchantmentMgr.cpp \ + ItemEnchantmentMgr.h \ + ItemHandler.cpp \ + ItemPrototype.h \ + Language.h \ + Level0.cpp \ + Level1.cpp \ + Level2.cpp \ + Level3.cpp \ + LFGHandler.cpp \ + LootHandler.cpp \ + LootMgr.cpp \ + LootMgr.h \ + Mail.cpp \ + Mail.h \ + Map.cpp \ + Map.h \ + MapInstanced.cpp \ + MapInstanced.h \ + MapManager.cpp \ + MapManager.h \ + MapReference.h \ + MapRefManager.h \ + MiscHandler.cpp \ + MotionMaster.cpp \ + MotionMaster.h \ + MovementGenerator.cpp \ + MovementGenerator.h \ + MovementGeneratorImpl.h \ + MovementHandler.cpp \ + NPCHandler.cpp \ + NPCHandler.h \ + NullCreatureAI.cpp \ + NullCreatureAI.h \ + ObjectAccessor.cpp \ + ObjectAccessor.h \ + Object.cpp \ + ObjectDefines.h \ + ObjectGridLoader.cpp \ + ObjectGridLoader.h \ + Object.h \ + ObjectMgr.cpp \ + ObjectMgr.h \ + ObjectPosSelector.cpp \ + ObjectPosSelector.h \ + Opcodes.cpp \ + Opcodes.h \ + Path.h \ + PetAI.cpp \ + PetAI.h \ + Pet.cpp \ + Pet.h \ + PetHandler.cpp \ + PetitionsHandler.cpp \ + Player.cpp \ + Player.h \ + PlayerDump.cpp \ + PlayerDump.h \ + PointMovementGenerator.cpp \ + PointMovementGenerator.h \ + PoolHandler.cpp \ + PoolHandler.h \ + QueryHandler.cpp \ + QuestDef.cpp \ + QuestDef.h \ + QuestHandler.cpp \ + RandomMovementGenerator.cpp \ + RandomMovementGenerator.h \ + ReactorAI.cpp \ + ReactorAI.h \ + ReputationMgr.cpp \ + ReputationMgr.h \ + ScriptCalls.cpp \ + ScriptCalls.h \ + SharedDefines.h \ + SkillHandler.cpp \ + SpellAuraDefines.h \ + SpellAuras.cpp \ + SpellAuras.h \ + Spell.cpp \ + SpellEffects.cpp \ + Spell.h \ + SkillDiscovery.cpp \ + SkillDiscovery.h \ + SkillExtraItems.cpp \ + SkillExtraItems.h \ + SpellHandler.cpp \ + SocialMgr.cpp \ + SocialMgr.h \ + SpellMgr.cpp \ + SpellMgr.h \ + StatSystem.cpp \ + TargetedMovementGenerator.cpp \ + TargetedMovementGenerator.h \ + TaxiHandler.cpp \ + TemporarySummon.cpp \ + TemporarySummon.h \ + TotemAI.cpp \ + TotemAI.h \ + Totem.cpp \ + Totem.h \ + TradeHandler.cpp \ + Transports.cpp \ + Transports.h \ + ThreatManager.cpp \ + ThreatManager.h \ + Traveller.h \ + Unit.cpp \ + Unit.h \ + UnitEvents.h \ + UpdateData.cpp \ + UpdateData.h \ + UpdateFields.h \ + UpdateMask.h \ + Vehicle.cpp \ + Vehicle.h \ + VoiceChatHandler.cpp \ + WaypointManager.cpp \ + WaypointManager.h \ + WaypointMovementGenerator.cpp \ + WaypointMovementGenerator.h \ + Weather.cpp \ + Weather.h \ + World.cpp \ + World.h \ + WorldLog.cpp \ + WorldLog.h \ + WorldSession.cpp \ + WorldSession.h \ + WorldSocket.cpp \ + WorldSocket.h \ + WorldSocketMgr.cpp \ + WorldSocketMgr.h \ + FollowerReference.cpp \ + FollowerReference.h \ + FollowerRefManager.h \ + GroupReference.cpp \ + GroupReference.h \ + GroupRefManager.h +>>>>>>> 2429aaf2276d689e101ed88285f18449dbe4280d:src/game/Makefile.am +======= + AccountMgr.cpp \ + AccountMgr.h \ + AchievementMgr.h \ + AchievementMgr.cpp \ + AggressorAI.cpp \ + AggressorAI.h \ + AnimalRandomMovementGenerator.h \ + ArenaTeam.cpp \ + ArenaTeam.h \ + ArenaTeamHandler.cpp \ + AuctionHouseHandler.cpp \ + AuctionHouseMgr.cpp \ + AuctionHouseMgr.h \ + Bag.cpp \ + Bag.h \ + BattleGround.cpp \ + BattleGroundAA.cpp \ + BattleGroundAB.cpp \ + BattleGroundAV.cpp \ + BattleGroundBE.cpp \ + BattleGroundDS.cpp \ + BattleGroundEY.cpp \ + BattleGroundNA.cpp \ + BattleGroundRL.cpp \ + BattleGroundRV.cpp \ + BattleGroundSA.cpp \ + BattleGroundWS.cpp \ + BattleGround.h \ + BattleGroundAA.h \ + BattleGroundAB.h \ + BattleGroundAV.h \ + BattleGroundBE.h \ + BattleGroundDS.h \ + BattleGroundEY.h \ + BattleGroundNA.h \ + BattleGroundRL.h \ + BattleGroundRV.h \ + BattleGroundSA.h \ + BattleGroundWS.h \ + BattleGroundHandler.cpp \ + BattleGroundMgr.cpp \ + BattleGroundMgr.h \ + Calendar.cpp \ + Calendar.h \ + CalendarHandler.cpp \ + Cell.h \ + CellImpl.h \ + Channel.cpp \ + Channel.h \ + ChannelHandler.cpp \ + ChannelMgr.h \ + CharacterHandler.cpp \ + Chat.cpp \ + Chat.h \ + ChatHandler.cpp \ + CombatHandler.cpp \ + ConfusedMovementGenerator.cpp \ + ConfusedMovementGenerator.h \ + Corpse.cpp \ + Corpse.h \ + CreatureAI.cpp \ + CreatureAI.h \ + CreatureAIImpl.h \ + CreatureAIRegistry.cpp \ + CreatureAIRegistry.h \ + CreatureAISelector.cpp \ + CreatureAISelector.h \ + CreatureEventAI.cpp \ + CreatureEventAI.h \ + CreatureEventAIMgr.cpp \ + CreatureEventAIMgr.h \ + Creature.cpp \ + Creature.h \ + DBCEnums.h \ + DBCfmt.h \ + DBCStores.cpp \ + DBCStores.h \ + DBCStructure.h \ + debugcmds.cpp \ + DestinationHolder.cpp \ + DestinationHolder.h \ + DestinationHolderImp.h \ + DuelHandler.cpp \ + DynamicObject.cpp \ + DynamicObject.h \ + FleeingMovementGenerator.cpp \ + FleeingMovementGenerator.h \ + Formulas.h \ + GameEventMgr.cpp \ + GameEventMgr.h \ + GameObject.cpp \ + GameObject.h \ + GlobalEvents.cpp \ + GlobalEvents.h \ + GMTicketHandler.cpp \ + GMTicketMgr.cpp \ + GMTicketMgr.h \ + GossipDef.cpp \ + GossipDef.h \ + GridDefines.h \ + GridNotifiers.cpp \ + GridNotifiers.h \ + GridNotifiersImpl.h \ + GridStates.cpp \ + GridStates.h \ + Group.cpp \ + Group.h \ + GroupHandler.cpp \ + GuardAI.cpp \ + GuardAI.h \ + Guild.cpp \ + Guild.h \ + GuildHandler.cpp \ + HomeMovementGenerator.cpp \ + HomeMovementGenerator.h \ + HostilRefManager.cpp \ + HostilRefManager.h \ + IdleMovementGenerator.cpp \ + IdleMovementGenerator.h \ + InstanceData.cpp \ + InstanceData.h \ + InstanceSaveMgr.cpp \ + InstanceSaveMgr.h \ + Item.cpp \ + Item.h \ + ItemEnchantmentMgr.cpp \ + ItemEnchantmentMgr.h \ + ItemHandler.cpp \ + ItemPrototype.h \ + Language.h \ + Level0.cpp \ + Level1.cpp \ + Level2.cpp \ + Level3.cpp \ + LFGHandler.cpp \ + LootHandler.cpp \ + LootMgr.cpp \ + LootMgr.h \ + Mail.cpp \ + Mail.h \ + Map.cpp \ + Map.h \ + MapInstanced.cpp \ + MapInstanced.h \ + MapManager.cpp \ + MapManager.h \ + MapReference.h \ + MapRefManager.h \ + MiscHandler.cpp \ + MotionMaster.cpp \ + MotionMaster.h \ + MovementGenerator.cpp \ + MovementGenerator.h \ + MovementGeneratorImpl.h \ + MovementHandler.cpp \ + NPCHandler.cpp \ + NPCHandler.h \ + NullCreatureAI.cpp \ + NullCreatureAI.h \ + ObjectAccessor.cpp \ + ObjectAccessor.h \ + Object.cpp \ + ObjectDefines.h \ + ObjectGridLoader.cpp \ + ObjectGridLoader.h \ + Object.h \ + ObjectMgr.cpp \ + ObjectMgr.h \ + ObjectPosSelector.cpp \ + ObjectPosSelector.h \ + Opcodes.cpp \ + Opcodes.h \ + Path.h \ + PetAI.cpp \ + PetAI.h \ + Pet.cpp \ + Pet.h \ + PetHandler.cpp \ + PetitionsHandler.cpp \ + Player.cpp \ + Player.h \ + PlayerDump.cpp \ + PlayerDump.h \ + PointMovementGenerator.cpp \ + PointMovementGenerator.h \ + PoolHandler.cpp \ + PoolHandler.h \ + QueryHandler.cpp \ + QuestDef.cpp \ + QuestDef.h \ + QuestHandler.cpp \ + RandomMovementGenerator.cpp \ + RandomMovementGenerator.h \ + ReactorAI.cpp \ + ReactorAI.h \ + ReputationMgr.cpp \ + ReputationMgr.h \ + ScriptCalls.cpp \ + ScriptCalls.h \ + SharedDefines.h \ + SkillHandler.cpp \ + SpellAuraDefines.h \ + SpellAuras.cpp \ + SpellAuras.h \ + Spell.cpp \ + SpellEffects.cpp \ + Spell.h \ + SkillDiscovery.cpp \ + SkillDiscovery.h \ + SkillExtraItems.cpp \ + SkillExtraItems.h \ + SpellHandler.cpp \ + SocialMgr.cpp \ + SocialMgr.h \ + SpellMgr.cpp \ + SpellMgr.h \ + StatSystem.cpp \ + TargetedMovementGenerator.cpp \ + TargetedMovementGenerator.h \ + TaxiHandler.cpp \ + TemporarySummon.cpp \ + TemporarySummon.h \ + TotemAI.cpp \ + TotemAI.h \ + Totem.cpp \ + Totem.h \ + TradeHandler.cpp \ + Transports.cpp \ + Transports.h \ + ThreatManager.cpp \ + ThreatManager.h \ + Traveller.h \ + Unit.cpp \ + Unit.h \ + UnitEvents.h \ + UpdateData.cpp \ + UpdateData.h \ + UpdateFields.h \ + UpdateMask.h \ + Vehicle.cpp \ + Vehicle.h \ + VoiceChatHandler.cpp \ + WaypointManager.cpp \ + WaypointManager.h \ + WaypointMovementGenerator.cpp \ + WaypointMovementGenerator.h \ + Weather.cpp \ + Weather.h \ + World.cpp \ + World.h \ + WorldLog.cpp \ + WorldLog.h \ + WorldSession.cpp \ + WorldSession.h \ + WorldSocket.cpp \ + WorldSocket.h \ + WorldSocketMgr.cpp \ + WorldSocketMgr.h \ + FollowerReference.cpp \ + FollowerReference.h \ + FollowerRefManager.h \ + GroupReference.cpp \ + GroupReference.h \ + GroupRefManager.h +>>>>>>> 5a6594330caefc0dc00a5fe792dcb0e344b457cb:src/game/Makefile.am + +## Link against shared library +libmangosgame_a_LIBADD = ../shared/libmangosshared.a ../shared/Auth/libmangosauth.a ../shared/Config/libmangosconfig.a ../shared/Database/libmangosdatabase.a ../shared/vmap/libmangosvmaps.a + +## Additional files to include when running 'make dist' +# Nothing yet. diff --git a/src/game/Map.cpp b/src/game/Map.cpp index b12c7b477c0..d53c617e374 100644 --- a/src/game/Map.cpp +++ b/src/game/Map.cpp @@ -21,7 +21,6 @@ #include "MapManager.h" #include "Player.h" #include "GridNotifiers.h" -#include "WorldSession.h" #include "Log.h" #include "GridStates.h" #include "CellImpl.h" @@ -44,9 +43,6 @@ #define DEFAULT_GRID_EXPIRY 300 #define MAX_GRID_LOAD_TIME 50 -// magic *.map header -const char MAP_MAGIC[] = "MAP_2.50"; - GridState* si_GridStates[MAX_GRID_STATE]; Map::~Map() @@ -54,11 +50,11 @@ Map::~Map() UnloadAll(); } -bool Map::ExistMap(uint32 mapid,int x,int y) +bool Map::ExistMap(uint32 mapid,int gx,int gy) { int len = sWorld.GetDataPath().length()+strlen("maps/%03u%02u%02u.map")+1; char* tmp = new char[len]; - snprintf(tmp, len, (char *)(sWorld.GetDataPath()+"maps/%03u%02u%02u.map").c_str(),mapid,x,y); + snprintf(tmp, len, (char *)(sWorld.GetDataPath()+"maps/%03u%02u%02u.map").c_str(),mapid,gx,gy); FILE *pf=fopen(tmp,"rb"); @@ -69,9 +65,10 @@ bool Map::ExistMap(uint32 mapid,int x,int y) return false; } - char magic[8]; - fread(magic,1,8,pf); - if(strncmp(MAP_MAGIC,magic,8)) + map_fileheader header; + fread(&header, sizeof(header), 1, pf); + if (header.mapMagic != uint32(MAP_MAGIC) || + header.versionMagic != uint32(MAP_VERSION_MAGIC)) { sLog.outError("Map file '%s' is non-compatible version (outdated?). Please, create new using ad.exe program.",tmp); delete [] tmp; @@ -84,17 +81,17 @@ bool Map::ExistMap(uint32 mapid,int x,int y) return true; } -bool Map::ExistVMap(uint32 mapid,int x,int y) +bool Map::ExistVMap(uint32 mapid,int gx,int gy) { if(VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager()) { if(vmgr->isMapLoadingEnabled()) { // x and y are swapped !! => fixed now - bool exists = vmgr->existsMap((sWorld.GetDataPath()+ "vmaps").c_str(), mapid, x,y); + bool exists = vmgr->existsMap((sWorld.GetDataPath()+ "vmaps").c_str(), mapid, gx,gy); if(!exists) { - std::string name = vmgr->getDirFileName(mapid,x,y); + std::string name = vmgr->getDirFileName(mapid,gx,gy); sLog.outError("VMap file '%s' is missing or point to wrong version vmap file, redo vmaps with latest vmap_assembler.exe program", (sWorld.GetDataPath()+"vmaps/"+name).c_str()); return false; } @@ -104,91 +101,70 @@ bool Map::ExistVMap(uint32 mapid,int x,int y) return true; } -void Map::LoadVMap(int x,int y) +void Map::LoadVMap(int gx,int gy) { // x and y are swapped !! - int vmapLoadResult = VMAP::VMapFactory::createOrGetVMapManager()->loadMap((sWorld.GetDataPath()+ "vmaps").c_str(), GetId(), x,y); + int vmapLoadResult = VMAP::VMapFactory::createOrGetVMapManager()->loadMap((sWorld.GetDataPath()+ "vmaps").c_str(), GetId(), gx,gy); switch(vmapLoadResult) { case VMAP::VMAP_LOAD_RESULT_OK: - sLog.outDetail("VMAP loaded name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), x,y, x,y); + sLog.outDetail("VMAP loaded name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx,gy,gx,gy); break; case VMAP::VMAP_LOAD_RESULT_ERROR: - sLog.outDetail("Could not load VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), x,y, x,y); + sLog.outDetail("Could not load VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx,gy,gx,gy); break; case VMAP::VMAP_LOAD_RESULT_IGNORED: - DEBUG_LOG("Ignored VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), x,y, x,y); + DEBUG_LOG("Ignored VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx,gy,gx,gy); break; } } -void Map::LoadMap(uint32 mapid, uint32 instanceid, int x,int y) +void Map::LoadMap(int gx,int gy) { - if( instanceid != 0 ) + if( i_InstanceId != 0 ) { - if(GridMaps[x][y]) + if(GridMaps[gx][gy]) return; - Map* baseMap = const_cast<Map*>(MapManager::Instance().GetBaseMap(mapid)); - - // load gridmap for base map - if (!baseMap->GridMaps[x][y]) - baseMap->EnsureGridCreated(GridPair(63-x,63-y)); + Map* baseMap = const_cast<Map*>(MapManager::Instance().GetBaseMap(i_id)); -//+++ if (!baseMap->GridMaps[x][y]) don't check for GridMaps[gx][gy], we need the management for vmaps -// return; + // load grid map for base map + if (!baseMap->GridMaps[gx][gy]) + baseMap->EnsureGridCreated(GridPair(63-gx,63-gy)); - ((MapInstanced*)(baseMap))->AddGridMapReference(GridPair(x,y)); - GridMaps[x][y] = baseMap->GridMaps[x][y]; + ((MapInstanced*)(baseMap))->AddGridMapReference(GridPair(gx,gy)); + GridMaps[gx][gy] = baseMap->GridMaps[gx][gy]; return; } //map already load, delete it before reloading (Is it necessary? Do we really need the ability the reload maps during runtime?) - if(GridMaps[x][y]) + if(GridMaps[gx][gy]) { - sLog.outDetail("Unloading already loaded map %u before reloading.",mapid); - delete (GridMaps[x][y]); - GridMaps[x][y]=NULL; + sLog.outDetail("Unloading already loaded map %u before reloading.",i_id); + delete (GridMaps[gx][gy]); + GridMaps[gx][gy]=NULL; } // map file name char *tmp=NULL; - // Pihhan: dataPath length + "maps/" + 3+2+2+ ".map" length may be > 32 ! int len = sWorld.GetDataPath().length()+strlen("maps/%03u%02u%02u.map")+1; tmp = new char[len]; - snprintf(tmp, len, (char *)(sWorld.GetDataPath()+"maps/%03u%02u%02u.map").c_str(),mapid,x,y); + snprintf(tmp, len, (char *)(sWorld.GetDataPath()+"maps/%03u%02u%02u.map").c_str(),i_id,gx,gy); sLog.outDetail("Loading map %s",tmp); // loading data - FILE *pf=fopen(tmp,"rb"); - if(!pf) + GridMaps[gx][gy] = new GridMap(); + if (!GridMaps[gx][gy]->loadData(tmp)) { - delete [] tmp; - return; + sLog.outError("Error load map file: \n %s\n", tmp); } - - char magic[8]; - fread(magic,1,8,pf); - if(strncmp(MAP_MAGIC,magic,8)) - { - sLog.outError("Map file '%s' is non-compatible version (outdated?). Please, create new using ad.exe program.",tmp); - delete [] tmp; - fclose(pf); //close file before return - return; - } - delete [] tmp; - - GridMap * buf= new GridMap; - fread(buf,1,sizeof(GridMap),pf); - fclose(pf); - - GridMaps[x][y] = buf; + delete [] tmp; } -void Map::LoadMapAndVMap(uint32 mapid, uint32 instanceid, int x,int y) +void Map::LoadMapAndVMap(int gx,int gy) { - LoadMap(mapid,instanceid,x,y); - if(instanceid == 0) - LoadVMap(x, y); // Only load the data for the base map + LoadMap(gx,gy); + if(i_InstanceId == 0) + LoadVMap(gx, gy); // Only load the data for the base map } void Map::InitStateMachine() @@ -256,7 +232,7 @@ template<> void Map::AddToGrid(Creature* obj, NGridType *grid, Cell const& cell) { // add to world object registry in grid - if(obj->isPet() || obj->IsTempWorldObject) + if(obj->isWorldCreature() || obj->IsTempWorldObject) { (*grid)(cell.CellX(), cell.CellY()).AddWorldObject<Creature>(obj, obj->GetGUID()); } @@ -308,7 +284,7 @@ template<> void Map::RemoveFromGrid(Creature* obj, NGridType *grid, Cell const& cell) { // remove from world object registry in grid - if(obj->isPet() || obj->IsTempWorldObject) + if(obj->isWorldCreature() || obj->IsTempWorldObject) { (*grid)(cell.CellX(), cell.CellY()).RemoveWorldObject<Creature>(obj, obj->GetGUID()); } @@ -410,7 +386,7 @@ Map::EnsureGridCreated(const GridPair &p) Guard guard(*this); if(!getNGrid(p.x_coord, p.y_coord)) { - sLog.outDebug("Loading grid[%u,%u] for map %u", p.x_coord, p.y_coord, i_id); + sLog.outDebug("Creating grid[%u,%u] for map %u instance %u", p.x_coord, p.y_coord, i_id, i_InstanceId); setNGrid(new NGridType(p.x_coord*MAX_NUMBER_OF_GRIDS + p.y_coord, p.x_coord, p.y_coord, i_gridExpiry, sWorld.getConfig(CONFIG_GRID_UNLOAD)), p.x_coord, p.y_coord); @@ -425,20 +401,20 @@ Map::EnsureGridCreated(const GridPair &p) int gy=63-p.y_coord; if(!GridMaps[gx][gy]) - Map::LoadMapAndVMap(i_id,i_InstanceId,gx,gy); + LoadMapAndVMap(gx,gy); } } } void -Map::EnsureGridLoaded(const Cell &cell, Player *player) +Map::EnsureGridLoadedAtEnter(const Cell &cell, Player *player) { - EnsureGridCreated(GridPair(cell.GridX(), cell.GridY())); - NGridType *grid = getNGrid(cell.GridX(), cell.GridY()); + NGridType *grid; - assert(grid != NULL); - if (!isGridObjectDataLoaded(cell.GridX(), cell.GridY())) + if(EnsureGridLoaded(cell)) { + grid = getNGrid(cell.GridX(), cell.GridY()); + if (player) { player->SendDelayResponse(MAX_GRID_LOAD_TIME); @@ -449,19 +425,37 @@ Map::EnsureGridLoaded(const Cell &cell, Player *player) DEBUG_LOG("Active object nearby triggers of loading grid [%u,%u] on map %u", cell.GridX(), cell.GridY(), i_id); } + ResetGridExpiry(*getNGrid(cell.GridX(), cell.GridY()), 0.1f); + grid->SetGridState(GRID_STATE_ACTIVE); + } + else + grid = getNGrid(cell.GridX(), cell.GridY()); + + if (player) + AddToGrid(player,grid,cell); +} + +bool Map::EnsureGridLoaded(const Cell &cell) +{ + EnsureGridCreated(GridPair(cell.GridX(), cell.GridY())); + NGridType *grid = getNGrid(cell.GridX(), cell.GridY()); + + assert(grid != NULL); + if( !isGridObjectDataLoaded(cell.GridX(), cell.GridY()) ) + { + sLog.outDebug("Loading grid[%u,%u] for map %u instance %u", cell.GridX(), cell.GridY(), i_id, i_InstanceId); + ObjectGridLoader loader(*grid, this, cell); loader.LoadN(); - setGridObjectDataLoaded(true, cell.GridX(), cell.GridY()); // Add resurrectable corpses to world object list in grid ObjectAccessor::Instance().AddCorpsesToGrid(GridPair(cell.GridX(),cell.GridY()),(*grid)(cell.CellX(), cell.CellY()), this); - ResetGridExpiry(*getNGrid(cell.GridX(), cell.GridY()), 0.1f); - grid->SetGridState(GRID_STATE_ACTIVE); + setGridObjectDataLoaded(true,cell.GridX(), cell.GridY()); + return true; } - - if(player) - AddToGrid(player,grid,cell); + + return false; } void Map::LoadGrid(float x, float y) @@ -480,7 +474,7 @@ bool Map::Add(Player *player) // update player state for other player and visa-versa CellPair p = Trinity::ComputeCellPair(player->GetPositionX(), player->GetPositionY()); Cell cell(p); - EnsureGridLoaded(cell, player); + EnsureGridLoadedAtEnter(cell, player); player->AddToWorld(); SendInitSelf(player); @@ -508,7 +502,7 @@ Map::Add(T *obj) Cell cell(p); if(obj->isActiveObject()) - EnsureGridLoaded(cell); + EnsureGridLoadedAtEnter(cell); else EnsureGridCreated(GridPair(cell.GridX(), cell.GridY())); @@ -517,12 +511,14 @@ Map::Add(T *obj) AddToGrid(obj,grid,cell); obj->AddToWorld(); - + if(obj->isActiveObject()) AddToActive(obj); DEBUG_LOG("Object %u enters grid[%u,%u]", GUID_LOPART(obj->GetGUID()), cell.GridX(), cell.GridY()); + //something, such as vehicle, needs to be update immediately + //if(obj->GetTypeId() != TYPEID_UNIT) UpdateObjectVisibility(obj,cell,p); //AddNotifier(obj); @@ -730,6 +726,13 @@ void Map::Update(const uint32 &t_diff) } } } + + if(plr->m_seer != plr && !plr->hasUnitState(UNIT_STAT_ONVEHICLE)) + { + Trinity::PlayerVisibilityNotifier notifier(*plr); + VisitAll(plr->m_seer->GetPositionX(), plr->m_seer->GetPositionY(), World::GetMaxVisibleDistance(), notifier); + notifier.Notify(); + } } // non-player active objects @@ -747,33 +750,6 @@ void Map::Update(const uint32 &t_diff) if(!obj->IsInWorld()) continue; - // Update bindsight players - /*if(obj->isType(TYPEMASK_UNIT)) - { - if(!((Unit*)obj)->GetSharedVisionList().empty()) - for(SharedVisionList::const_iterator itr = ((Unit*)obj)->GetSharedVisionList().begin(); itr != ((Unit*)obj)->GetSharedVisionList().end(); ++itr) - { - if(!*itr) - { - sLog.outError("unit %u has invalid shared vision player, list size %u", obj->GetEntry(), ((Unit*)obj)->GetSharedVisionList().size()); - continue; - } - Trinity::PlayerVisibilityNotifier notifier(**itr); - VisitAll(obj->GetPositionX(), obj->GetPositionY(), World::GetMaxVisibleDistance(), notifier); - notifier.Notify(); - } - } - else */if(obj->GetTypeId() == TYPEID_DYNAMICOBJECT) - { - if(Unit *caster = ((DynamicObject*)obj)->GetCaster()) - if(caster->GetTypeId() == TYPEID_PLAYER && caster->GetUInt64Value(PLAYER_FARSIGHT) == obj->GetGUID()) - { - Trinity::PlayerVisibilityNotifier notifier(*((Player*)caster)); - VisitAll(obj->GetPositionX(), obj->GetPositionY(), World::GetMaxVisibleDistance(), notifier); - notifier.Notify(); - } - } - CellPair standing_cell(Trinity::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY())); // Check for correctness of standing_cell, it also avoids problems with update_cell @@ -948,7 +924,7 @@ Map::PlayerRelocation(Player *player, float x, float y, float z, float orientati if( !old_cell.DiffGrid(new_cell) ) AddToGrid(player, oldGrid,new_cell); else - EnsureGridLoaded(new_cell, player); + EnsureGridLoadedAtEnter(new_cell, player); } AddUnitToNotify(player); @@ -1067,7 +1043,7 @@ bool Map::CreatureCellRelocation(Creature *c, Cell new_cell) // in diff. grids but active creature if(c->isActiveObject()) { - EnsureGridLoaded(new_cell); + EnsureGridLoadedAtEnter(new_cell); #ifdef TRINITY_DEBUG if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0) @@ -1178,7 +1154,11 @@ bool Map::UnloadGrid(const uint32 &x, const uint32 &y, bool unloadAll) { if (i_InstanceId == 0) { - if(GridMaps[gx][gy]) delete (GridMaps[gx][gy]); + if(GridMaps[gx][gy]) + { + GridMaps[gx][gy]->unloadData(); + delete GridMaps[gx][gy]; + } // x and y are swapped VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(GetId(), gy, gx); } @@ -1203,103 +1183,529 @@ void Map::UnloadAll() } } -float Map::GetHeight(float x, float y, float z, bool pUseVmaps) const +//***************************** +// Grid function +//***************************** +GridMap::GridMap() { - GridPair p = Trinity::ComputeGridPair(x, y); + m_flags = 0; + // Area data + m_gridArea = 0; + m_area_map = NULL; + // Height level data + m_gridHeight = INVALID_HEIGHT; + m_gridGetHeight = &GridMap::getHeightFromFlat; + m_V9 = NULL; + m_V8 = NULL; + // Liquid data + m_liquidType = 0; + m_liquid_offX = 0; + m_liquid_offY = 0; + m_liquid_width = 0; + m_liquid_height = 0; + m_liquidLevel = INVALID_HEIGHT; + m_liquid_type = NULL; + m_liquid_map = NULL; +} - // half opt method - int gx=(int)(32-x/SIZE_OF_GRIDS); //grid x - int gy=(int)(32-y/SIZE_OF_GRIDS); //grid y +GridMap::~GridMap() +{ + unloadData(); +} - float lx=128*(32 -x/SIZE_OF_GRIDS - gx); - float ly=128*(32 -y/SIZE_OF_GRIDS - gy); +bool GridMap::loadData(char *filename) +{ + // Unload old data if exist + unloadData(); - // ensure GridMap is loaded - const_cast<Map*>(this)->EnsureGridCreated(GridPair(63-gx,63-gy)); + map_fileheader header; + // Not return error if file not found + FILE *in = fopen(filename, "rb"); + if (!in) + return true; + fread(&header, sizeof(header),1,in); + if (header.mapMagic == uint32(MAP_MAGIC) && + header.versionMagic == uint32(MAP_VERSION_MAGIC)) + { + // loadup area data + if (header.areaMapOffset && !loadAreaData(in, header.areaMapOffset, header.areaMapSize)) + { + sLog.outError("Error loading map area data\n"); + fclose(in); + return false; + } + // loadup height data + if (header.heightMapOffset && !loadHeihgtData(in, header.heightMapOffset, header.heightMapSize)) + { + sLog.outError("Error loading map height data\n"); + fclose(in); + return false; + } + // loadup liquid data + if (header.liquidMapOffset && !loadLiquidData(in, header.liquidMapOffset, header.liquidMapSize)) + { + sLog.outError("Error loading map liquids data\n"); + fclose(in); + return false; + } + fclose(in); + return true; + } + sLog.outError("Map file '%s' is non-compatible version (outdated?). Please, create new using ad.exe program.", filename); + fclose(in); + return false; +} - // find raw .map surface under Z coordinates - float mapHeight; - if(GridMap* gmap = GridMaps[gx][gy]) +void GridMap::unloadData() +{ + if (m_area_map) delete[] m_area_map; + if (m_V9) delete[] m_V9; + if (m_V8) delete[] m_V8; + if (m_liquid_type) delete[] m_liquid_type; + if (m_liquid_map) delete[] m_liquid_map; + m_area_map = NULL; + m_V9 = NULL; + m_V8 = NULL; + m_liquid_type = NULL; + m_liquid_map = NULL; + m_gridGetHeight = &GridMap::getHeightFromFlat; +} + +bool GridMap::loadAreaData(FILE *in, uint32 offset, uint32 size) +{ + map_areaHeader header; + fseek(in, offset, SEEK_SET); + fread(&header, sizeof(header), 1, in); + if (header.fourcc != uint32(MAP_AREA_MAGIC)) + return false; + + m_gridArea = header.gridArea; + if (!(header.flags & MAP_AREA_NO_AREA)) { - int lx_int = (int)lx; - int ly_int = (int)ly; + m_area_map = new uint16 [16*16]; + fread(m_area_map, sizeof(uint16), 16*16, in); + } + return true; +} - lx -= lx_int; - ly -= ly_int; +bool GridMap::loadHeihgtData(FILE *in, uint32 offset, uint32 size) +{ + map_heightHeader header; + fseek(in, offset, SEEK_SET); + fread(&header, sizeof(header), 1, in); + if (header.fourcc != uint32(MAP_HEIGTH_MAGIC)) + return false; - float a,b,c; - if (lx+ly < 1) + m_gridHeight = header.gridHeight; + if (!(header.flags & MAP_HEIGHT_NO_HIGHT)) + { + if ((header.flags & MAP_HEIGHT_AS_INT16)) { - if (lx > ly) - { - // 1 - float h1 = gmap->v9[lx_int][ly_int]; - float h2 = gmap->v9[lx_int+1][ly_int]; - float h5 = 2 * gmap->v8[lx_int][ly_int]; - a = h2-h1; - b = h5-h1-h2; - c = h1; - } - else - { - // 2 - float h1 = gmap->v9[lx_int][ly_int]; - float h3 = gmap->v9[lx_int][ly_int+1]; - float h5 = 2 * gmap->v8[lx_int][ly_int]; - a = h5 - h1 - h3; - b = h3 - h1; - c = h1; - } + m_uint16_V9 = new uint16 [129*129]; + m_uint16_V8 = new uint16 [128*128]; + fread(m_uint16_V9, sizeof(uint16), 129*129, in); + fread(m_uint16_V8, sizeof(uint16), 128*128, in); + m_gridIntHeightMultiplier = (header.gridMaxHeight - header.gridHeight) / 65535; + m_gridGetHeight = &GridMap::getHeightFromUint16; + } + else if ((header.flags & MAP_HEIGHT_AS_INT8)) + { + m_uint8_V9 = new uint8 [129*129]; + m_uint8_V8 = new uint8 [128*128]; + fread(m_uint8_V9, sizeof(uint8), 129*129, in); + fread(m_uint8_V8, sizeof(uint8), 128*128, in); + m_gridIntHeightMultiplier = (header.gridMaxHeight - header.gridHeight) / 255; + m_gridGetHeight = &GridMap::getHeightFromUint8; } else { - if (lx > ly) - { - // 3 - float h2 = gmap->v9[lx_int+1][ly_int]; - float h4 = gmap->v9[lx_int+1][ly_int+1]; - float h5 = 2 * gmap->v8[lx_int][ly_int]; - a = h2 + h4 - h5; - b = h4 - h2; - c = h5 - h4; - } - else - { - // 4 - float h3 = gmap->v9[lx_int][ly_int+1]; - float h4 = gmap->v9[lx_int+1][ly_int+1]; - float h5 = 2 * gmap->v8[lx_int][ly_int]; - a = h4 - h3; - b = h3 + h4 - h5; - c = h5 - h4; - } + m_V9 = new float [129*129]; + m_V8 = new float [128*128]; + fread(m_V9, sizeof(float), 129*129, in); + fread(m_V8, sizeof(float), 128*128, in); + m_gridGetHeight = &GridMap::getHeightFromFloat; + } + } + else + m_gridGetHeight = &GridMap::getHeightFromFlat; + return true; +} + +bool GridMap::loadLiquidData(FILE *in, uint32 offset, uint32 size) +{ + map_liquidHeader header; + fseek(in, offset, SEEK_SET); + fread(&header, sizeof(header), 1, in); + if (header.fourcc != uint32(MAP_LIQUID_MAGIC)) + return false; + + m_liquidType = header.liquidType; + m_liquid_offX = header.offsetX; + m_liquid_offY = header.offsetY; + m_liquid_width = header.width; + m_liquid_height= header.height; + m_liquidLevel = header.liquidLevel; + + if (!(header.flags&MAP_LIQUID_NO_TYPE)) + { + m_liquid_type = new uint8 [16*16]; + fread(m_liquid_type, sizeof(uint8), 16*16, in); + } + if (!(header.flags&MAP_LIQUID_NO_HIGHT)) + { + m_liquid_map = new float [m_liquid_width*m_liquid_height]; + fread(m_liquid_map, sizeof(float), m_liquid_width*m_liquid_height, in); + } + return true; +} + +uint16 GridMap::getArea(float x, float y) +{ + if (!m_area_map) + return m_gridArea; + + x = 16 * (32 - x/SIZE_OF_GRIDS); + y = 16 * (32 - y/SIZE_OF_GRIDS); + int lx = (int)x & 15; + int ly = (int)y & 15; + return m_area_map[lx*16 + ly]; +} + +float GridMap::getHeightFromFlat(float x, float y) const +{ + return m_gridHeight; +} + +float GridMap::getHeightFromFloat(float x, float y) const +{ + if (!m_V8 || !m_V9) + return m_gridHeight; + + x = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS); + y = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS); + + int x_int = (int)x; + int y_int = (int)y; + x -= x_int; + y -= y_int; + x_int&=(MAP_RESOLUTION - 1); + y_int&=(MAP_RESOLUTION - 1); + + // Height stored as: h5 - its v8 grid, h1-h4 - its v9 grid + // +--------------> X + // | h1-------h2 Coordinates is: + // | | \ 1 / | h1 0,0 + // | | \ / | h2 0,1 + // | | 2 h5 3 | h3 1,0 + // | | / \ | h4 1,1 + // | | / 4 \ | h5 1/2,1/2 + // | h3-------h4 + // V Y + // For find height need + // 1 - detect triangle + // 2 - solve linear equation from triangle points + // Calculate coefficients for solve h = a*x + b*y + c + + float a,b,c; + // Select triangle: + if (x+y < 1) + { + if (x > y) + { + // 1 triangle (h1, h2, h5 points) + float h1 = m_V9[(x_int )*129 + y_int]; + float h2 = m_V9[(x_int+1)*129 + y_int]; + float h5 = 2 * m_V8[x_int*128 + y_int]; + a = h2-h1; + b = h5-h1-h2; + c = h1; + } + else + { + // 2 triangle (h1, h3, h5 points) + float h1 = m_V9[x_int*129 + y_int ]; + float h3 = m_V9[x_int*129 + y_int+1]; + float h5 = 2 * m_V8[x_int*128 + y_int]; + a = h5 - h1 - h3; + b = h3 - h1; + c = h1; + } + } + else + { + if (x > y) + { + // 3 triangle (h2, h4, h5 points) + float h2 = m_V9[(x_int+1)*129 + y_int ]; + float h4 = m_V9[(x_int+1)*129 + y_int+1]; + float h5 = 2 * m_V8[x_int*128 + y_int]; + a = h2 + h4 - h5; + b = h4 - h2; + c = h5 - h4; + } + else + { + // 4 triangle (h3, h4, h5 points) + float h3 = m_V9[(x_int )*129 + y_int+1]; + float h4 = m_V9[(x_int+1)*129 + y_int+1]; + float h5 = 2 * m_V8[x_int*128 + y_int]; + a = h4 - h3; + b = h3 + h4 - h5; + c = h5 - h4; + } + } + // Calculate height + return a * x + b * y + c; +} + +float GridMap::getHeightFromUint8(float x, float y) const +{ + if (!m_uint8_V8 || !m_uint8_V9) + return m_gridHeight; + + x = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS); + y = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS); + + int x_int = (int)x; + int y_int = (int)y; + x -= x_int; + y -= y_int; + x_int&=(MAP_RESOLUTION - 1); + y_int&=(MAP_RESOLUTION - 1); + + int32 a, b, c; + uint8 *V9_h1_ptr = &m_uint8_V9[x_int*128 + x_int + y_int]; + if (x+y < 1) + { + if (x > y) + { + // 1 triangle (h1, h2, h5 points) + int32 h1 = V9_h1_ptr[ 0]; + int32 h2 = V9_h1_ptr[129]; + int32 h5 = 2 * m_uint8_V8[x_int*128 + y_int]; + a = h2-h1; + b = h5-h1-h2; + c = h1; + } + else + { + // 2 triangle (h1, h3, h5 points) + int32 h1 = V9_h1_ptr[0]; + int32 h3 = V9_h1_ptr[1]; + int32 h5 = 2 * m_uint8_V8[x_int*128 + y_int]; + a = h5 - h1 - h3; + b = h3 - h1; + c = h1; + } + } + else + { + if (x > y) + { + // 3 triangle (h2, h4, h5 points) + int32 h2 = V9_h1_ptr[129]; + int32 h4 = V9_h1_ptr[130]; + int32 h5 = 2 * m_uint8_V8[x_int*128 + y_int]; + a = h2 + h4 - h5; + b = h4 - h2; + c = h5 - h4; + } + else + { + // 4 triangle (h3, h4, h5 points) + int32 h3 = V9_h1_ptr[ 1]; + int32 h4 = V9_h1_ptr[130]; + int32 h5 = 2 * m_uint8_V8[x_int*128 + y_int]; + a = h4 - h3; + b = h3 + h4 - h5; + c = h5 - h4; + } + } + // Calculate height + return (float)((a * x) + (b * y) + c)*m_gridIntHeightMultiplier + m_gridHeight; +} + +float GridMap::getHeightFromUint16(float x, float y) const +{ + if (!m_uint16_V8 || !m_uint16_V9) + return m_gridHeight; + + x = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS); + y = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS); + + int x_int = (int)x; + int y_int = (int)y; + x -= x_int; + y -= y_int; + x_int&=(MAP_RESOLUTION - 1); + y_int&=(MAP_RESOLUTION - 1); + + int32 a, b, c; + uint16 *V9_h1_ptr = &m_uint16_V9[x_int*128 + x_int + y_int]; + if (x+y < 1) + { + if (x > y) + { + // 1 triangle (h1, h2, h5 points) + int32 h1 = V9_h1_ptr[ 0]; + int32 h2 = V9_h1_ptr[129]; + int32 h5 = 2 * m_uint16_V8[x_int*128 + y_int]; + a = h2-h1; + b = h5-h1-h2; + c = h1; + } + else + { + // 2 triangle (h1, h3, h5 points) + int32 h1 = V9_h1_ptr[0]; + int32 h3 = V9_h1_ptr[1]; + int32 h5 = 2 * m_uint16_V8[x_int*128 + y_int]; + a = h5 - h1 - h3; + b = h3 - h1; + c = h1; + } + } + else + { + if (x > y) + { + // 3 triangle (h2, h4, h5 points) + int32 h2 = V9_h1_ptr[129]; + int32 h4 = V9_h1_ptr[130]; + int32 h5 = 2 * m_uint16_V8[x_int*128 + y_int]; + a = h2 + h4 - h5; + b = h4 - h2; + c = h5 - h4; } - float _mapheight = a * lx + b * ly + c; - - // In some very rare case this will happen. Need find a better way. - if(lx_int == MAP_RESOLUTION) --lx_int; - if(ly_int == MAP_RESOLUTION) --ly_int; - - /* - float zi[4]; - // Probe 4 nearest points (except border cases) - zi[0] = gmap->Z[lx_int][ly_int]; - zi[1] = lx < MAP_RESOLUTION-1 ? gmap->Z[lx_int+1][ly_int] : zi[0]; - zi[2] = ly < MAP_RESOLUTION-1 ? gmap->Z[lx_int][ly_int+1] : zi[0]; - zi[3] = lx < MAP_RESOLUTION-1 && ly < MAP_RESOLUTION-1 ? gmap->Z[lx_int+1][ly_int+1] : zi[0]; - // Recalculate them like if their x,y positions were in the range 0,1 - float b[4]; - b[0] = zi[0]; - b[1] = zi[1]-zi[0]; - b[2] = zi[2]-zi[0]; - b[3] = zi[0]-zi[1]-zi[2]+zi[3]; - // Normalize the dx and dy to be in range 0..1 - float fact_x = lx - lx_int; - float fact_y = ly - ly_int; - // Use the simplified bilinear equation, as described in [url="http://en.wikipedia.org/wiki/Bilinear_interpolation"]http://en.wikipedia.org/wiki/Bilinear_interpolation[/url] - float _mapheight = b[0] + (b[1]*fact_x) + (b[2]*fact_y) + (b[3]*fact_x*fact_y); - - */ + else + { + // 4 triangle (h3, h4, h5 points) + int32 h3 = V9_h1_ptr[ 1]; + int32 h4 = V9_h1_ptr[130]; + int32 h5 = 2 * m_uint16_V8[x_int*128 + y_int]; + a = h4 - h3; + b = h3 + h4 - h5; + c = h5 - h4; + } + } + // Calculate height + return (float)((a * x) + (b * y) + c)*m_gridIntHeightMultiplier + m_gridHeight; +} + +float GridMap::getLiquidLevel(float x, float y) +{ + if (!m_liquid_map) + return m_liquidLevel; + + x = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS); + y = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS); + + int cx_int = ((int)x & (MAP_RESOLUTION-1)) - m_liquid_offY; + int cy_int = ((int)y & (MAP_RESOLUTION-1)) - m_liquid_offX; + + if (cx_int < 0 || cx_int >=m_liquid_height) + return INVALID_HEIGHT; + if (cy_int < 0 || cy_int >=m_liquid_width ) + return INVALID_HEIGHT; + + return m_liquid_map[cx_int*m_liquid_width + cy_int]; +} + +uint8 GridMap::getTerrainType(float x, float y) +{ + if (!m_liquid_type) + return m_liquidType; + + x = 16 * (32 - x/SIZE_OF_GRIDS); + y = 16 * (32 - y/SIZE_OF_GRIDS); + int lx = (int)x & 15; + int ly = (int)y & 15; + return m_liquid_type[lx*16 + ly]; +} + +// Get water state on map +inline ZLiquidStatus GridMap::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData *data) +{ + // Check water type (if no water return) + if (!m_liquid_type && !m_liquidType) + return LIQUID_MAP_NO_WATER; + + // Get cell + float cx = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS); + float cy = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS); + + int x_int = (int)cx & (MAP_RESOLUTION-1); + int y_int = (int)cy & (MAP_RESOLUTION-1); + + // Check water type in cell + uint8 type = m_liquid_type ? m_liquid_type[(x_int>>3)*16 + (y_int>>3)] : m_liquidType; + if (type == 0) + return LIQUID_MAP_NO_WATER; + + // Check req liquid type mask + if (ReqLiquidType && !(ReqLiquidType&type)) + return LIQUID_MAP_NO_WATER; + + // Check water level: + // Check water height map + int lx_int = x_int - m_liquid_offY; + int ly_int = y_int - m_liquid_offX; + if (lx_int < 0 || lx_int >=m_liquid_height) + return LIQUID_MAP_NO_WATER; + if (ly_int < 0 || ly_int >=m_liquid_width ) + return LIQUID_MAP_NO_WATER; + + // Get water level + float liquid_level = m_liquid_map ? m_liquid_map[lx_int*m_liquid_width + ly_int] : m_liquidLevel; + // Get ground level (sub 0.2 for fix some errors) + float ground_level = getHeight(x, y); + + // Check water level and ground level + if (liquid_level < ground_level || z < ground_level - 2) + return LIQUID_MAP_NO_WATER; + + // All ok in water -> store data + if (data) + { + data->type = type; + data->level = liquid_level; + data->depth_level = ground_level; + } + + // For speed check as int values + int delta = int((liquid_level - z) * 10); + + // Get position delta + if (delta > 20) // Under water + return LIQUID_MAP_UNDER_WATER; + if (delta > 0 ) // In water + return LIQUID_MAP_IN_WATER; + if (delta > -1) // Walk on water + return LIQUID_MAP_WATER_WALK; + // Above water + return LIQUID_MAP_ABOVE_WATER; +} + +inline GridMap *Map::GetGrid(float x, float y) +{ + // half opt method + int gx=(int)(32-x/SIZE_OF_GRIDS); //grid x + int gy=(int)(32-y/SIZE_OF_GRIDS); //grid y + + // ensure GridMap is loaded + EnsureGridCreated(GridPair(63-gx,63-gy)); + + return GridMaps[gx][gy]; +} + +float Map::GetHeight(float x, float y, float z, bool pUseVmaps) const +{ + // find raw .map surface under Z coordinates + float mapHeight; + if(GridMap *gmap = const_cast<Map*>(this)->GetGrid(x, y)) + { + float _mapheight = gmap->getHeight(x,y); + // look from a bit higher pos to find the floor, ignore under surface case if(z + 2.0f > _mapheight) mapHeight = _mapheight; @@ -1375,76 +1781,122 @@ float Map::GetVmapHeight(float x, float y, float z, bool useMaps) const return vmapHeight; } -uint16 Map::GetAreaFlag(float x, float y ) const +uint16 Map::GetAreaFlag(float x, float y, float z) const { - //local x,y coords - float lx,ly; - int gx,gy; - GridPair p = Trinity::ComputeGridPair(x, y); - - // half opt method - gx=(int)(32-x/SIZE_OF_GRIDS) ; //grid x - gy=(int)(32-y/SIZE_OF_GRIDS); //grid y - - lx=16*(32 -x/SIZE_OF_GRIDS - gx); - ly=16*(32 -y/SIZE_OF_GRIDS - gy); - //DEBUG_LOG("my %d %d si %d %d",gx,gy,p.x_coord,p.y_coord); - - // ensure GridMap is loaded - const_cast<Map*>(this)->EnsureGridCreated(GridPair(63-gx,63-gy)); - - if(GridMaps[gx][gy]) - return GridMaps[gx][gy]->area_flag[(int)(lx)][(int)(ly)]; + uint16 areaflag; + if(GridMap *gmap = const_cast<Map*>(this)->GetGrid(x, y)) + areaflag = gmap->getArea(x, y); // this used while not all *.map files generated (instances) else - return GetAreaFlagByMapId(i_id); + areaflag = GetAreaFlagByMapId(i_id); + + //FIXME: some hacks for areas above or underground for ground area + // required for area specific spells/etc, until map/vmap data + // not provided correct areaflag with this hacks + switch(areaflag) + { + // Acherus: The Ebon Hold (Plaguelands: The Scarlet Enclave) + case 1984: // Plaguelands: The Scarlet Enclave + case 2076: // Death's Breach (Plaguelands: The Scarlet Enclave) + case 2745: // The Noxious Pass (Plaguelands: The Scarlet Enclave) + if(z > 350.0f) areaflag = 2048; break; + // Acherus: The Ebon Hold (Eastern Plaguelands) + case 856: // The Noxious Glade (Eastern Plaguelands) + case 2456: // Death's Breach (Eastern Plaguelands) + if(z > 350.0f) areaflag = 1950; break; + // Dalaran + case 1593: // Crystalsong Forest + case 2484: // The Twilight Rivulet (Crystalsong Forest) + case 2492: // Forlorn Woods (Crystalsong Forest) + if (x > 5568.0f && x < 6116.0f && y > 282.0f && y < 982.0f && z > 563.0f) areaflag = 2153; break; + // Maw of Neltharion (cave) + case 164: // Dragonblight + case 1797: // Obsidian Dragonshrine (Dragonblight) + case 1827: // Wintergrasp + case 2591: // The Cauldron of Flames (Wintergrasp) + if (x > 4364.0f && x < 4632.0f && y > 1545.0f && y < 1886.0f && z < 200.0f) areaflag = 1853; break; + // Undercity (sewers enter and path) + case 179: // Tirisfal Glades + if (x > 1595.0f && x < 1699.0f && y > 535.0f && y < 643.5f && z < 30.5f) areaflag = 685; break; + // Undercity (Royal Quarter) + case 210: // Silverpine Forest + case 316: // The Shining Strand (Silverpine Forest) + case 438: // Lordamere Lake (Silverpine Forest) + if (x > 1237.0f && x < 1401.0f && y > 284.0f && y < 440.0f && z < -40.0f) areaflag = 685; break; + // Undercity (cave and ground zone, part of royal quarter) + case 607: // Ruins of Lordaeron (Tirisfal Glades) + // ground and near to ground (by city walls) + if(z > 0.0f) + { + if (x > 1510.0f && x < 1839.0f && y > 29.77f && y < 433.0f) areaflag = 685; + } + // more wide underground, part of royal quarter + else + { + if (x > 1299.0f && x < 1839.0f && y > 10.0f && y < 440.0f) areaflag = 685; + } + break; + // The Makers' Perch (ground) and Makers' Overlook (ground and cave) + case 1335: // Sholazar Basin + // The Makers' Perch ground (fast box) + if (x > 6100.0f && x < 6250.0f && y > 5650.0f && y < 5800.0f) + { + // nice slow circle + if ((x-6183.0f)*(x-6183.0f)+(y-5717.0f)*(y-5717.0f) < 2500.0f) + areaflag = 2189; + } + // Makers' Overlook (ground and cave) + else if (x > 5634.48f && x < 5774.53f && y < 3475.0f && z > 300.0f) + { + if(y > 3380.26f || y > 3265.0f && z < 360.0f) areaflag = 2187; + } + break; + // The Makers' Perch (underground) + case 2147: // The Stormwright's Shelf (Sholazar Basin) + if (x > 6199.0f && x < 6283.0f && y > 5705.0f && y < 5817.0f && z < 38.0f) areaflag = 2189; break; + // Makers' Overlook (deep cave) + case 267: // Icecrown + if (x > 5684.0f && x < 5798.0f && y > 3035.0f && y < 3367.0f && z < 358.0f) areaflag = 2187; break; + // Wyrmrest Temple (Dragonblight) + case 1814: // Path of the Titans (Dragonblight) + case 1897: // The Dragon Wastes (Dragonblight) + // fast box + if (x > 3400.0f && x < 3700.0f && y > 130.0f && y < 420.0f) + { + // nice slow circle + if ((x-3546.87f)*(x-3546.87f)+(y-272.71f)*(y-272.71f) < 19600.0f) areaflag = 1791; + } + break; + } + + return areaflag; } uint8 Map::GetTerrainType(float x, float y ) const { - //local x,y coords - float lx,ly; - int gx,gy; - - // half opt method - gx=(int)(32-x/SIZE_OF_GRIDS) ; //grid x - gy=(int)(32-y/SIZE_OF_GRIDS); //grid y - - lx=16*(32 -x/SIZE_OF_GRIDS - gx); - ly=16*(32 -y/SIZE_OF_GRIDS - gy); - - // ensure GridMap is loaded - const_cast<Map*>(this)->EnsureGridCreated(GridPair(63-gx,63-gy)); - - if(GridMaps[gx][gy]) - return GridMaps[gx][gy]->terrain_type[(int)(lx)][(int)(ly)]; + if(GridMap *gmap = const_cast<Map*>(this)->GetGrid(x, y)) + return gmap->getTerrainType(x, y); else return 0; } -float Map::GetWaterLevel(float x, float y ) const +ZLiquidStatus Map::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData *data) const { - //local x,y coords - float lx,ly; - int gx,gy; - - // half opt method - gx=(int)(32-x/SIZE_OF_GRIDS) ; //grid x - gy=(int)(32-y/SIZE_OF_GRIDS); //grid y - - lx=128*(32 -x/SIZE_OF_GRIDS - gx); - ly=128*(32 -y/SIZE_OF_GRIDS - gy); - - // ensure GridMap is loaded - const_cast<Map*>(this)->EnsureGridCreated(GridPair(63-gx,63-gy)); + if(GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y)) + return gmap->getLiquidStatus(x, y, z, ReqLiquidType, data); + else + return LIQUID_MAP_NO_WATER; +} - if(GridMaps[gx][gy]) - return GridMaps[gx][gy]->liquid_level[(int)(lx)][(int)(ly)]; +float Map::GetWaterLevel(float x, float y ) const +{ + if(GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y)) + return gmap->getLiquidLevel(x, y); else return 0; } -uint32 Map::GetAreaId(uint16 areaflag,uint32 map_id) +uint32 Map::GetAreaIdByAreaFlag(uint16 areaflag,uint32 map_id) { AreaTableEntry const *entry = GetAreaEntryByAreaFlagAndMap(areaflag,map_id); @@ -1454,7 +1906,7 @@ uint32 Map::GetAreaId(uint16 areaflag,uint32 map_id) return 0; } -uint32 Map::GetZoneId(uint16 areaflag,uint32 map_id) +uint32 Map::GetZoneIdByAreaFlag(uint16 areaflag,uint32 map_id) { AreaTableEntry const *entry = GetAreaEntryByAreaFlagAndMap(areaflag,map_id); @@ -1464,26 +1916,37 @@ uint32 Map::GetZoneId(uint16 areaflag,uint32 map_id) return 0; } -bool Map::IsInWater(float x, float y, float pZ) const +void Map::GetZoneAndAreaIdByAreaFlag(uint32& zoneid, uint32& areaid, uint16 areaflag,uint32 map_id) { - // This method is called too often to use vamps for that (4. parameter = false). - // The pZ pos is taken anyway for future use - float z = GetHeight(x,y,pZ,false); // use .map base surface height + AreaTableEntry const *entry = GetAreaEntryByAreaFlagAndMap(areaflag,map_id); - // underground or instance without vmap - if(z <= INVALID_HEIGHT) - return false; + areaid = entry ? entry->ID : 0; + zoneid = entry ? (( entry->zone != 0 ) ? entry->zone : entry->ID) : 0; +} - float water_z = GetWaterLevel(x,y); - uint8 flag = GetTerrainType(x,y); - return (z < (water_z-2)) && (flag & 0x01); +bool Map::IsInWater(float x, float y, float pZ) const +{ + // Check surface in x, y point for liquid + if (GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y)) + { + LiquidData liquid_status; + if (getLiquidStatus(x, y, pZ, MAP_ALL_LIQUIDS, &liquid_status)) + { + if (liquid_status.level - liquid_status.depth_level > 2) + return true; + } + } + return false; } bool Map::IsUnderWater(float x, float y, float z) const { - float water_z = GetWaterLevel(x,y); - uint8 flag = GetTerrainType(x,y); - return (z < (water_z-2)) && (flag & 0x01); + if (GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y)) + { + if (getLiquidStatus(x, y, z, MAP_LIQUID_TYPE_WATER|MAP_LIQUID_TYPE_OCEAN)&LIQUID_MAP_UNDER_WATER) + return true; + } + return false; } bool Map::CheckGridIntegrity(Creature* c, bool moved) const @@ -1573,7 +2036,8 @@ void Map::SendInitTransports( Player * player ) for (MapManager::TransportSet::iterator i = tset.begin(); i != tset.end(); ++i) { - if((*i) != player->GetTransport()) // send data for current transport in other place + // send data for current transport in other place + if((*i) != player->GetTransport() && (*i)->GetMapId()==i_id) { hasTransport = true; (*i)->BuildCreateUpdateBlockForPlayer(&transData, player); @@ -1600,7 +2064,7 @@ void Map::SendRemoveTransports( Player * player ) // except used transport for (MapManager::TransportSet::iterator i = tset.begin(); i != tset.end(); ++i) - if(player->GetTransport() != (*i)) + if((*i) != player->GetTransport() && (*i)->GetMapId()!=i_id) (*i)->BuildOutOfRangeUpdateBlock(&transData); WorldPacket packet; @@ -1657,7 +2121,7 @@ void Map::RemoveAllObjectsInRemoveList() switch(obj->GetTypeId()) { case TYPEID_UNIT: - if(!((Creature*)obj)->isPet()) + if(!((Creature*)obj)->isWorldCreature()) SwitchGridContainers((Creature*)obj, on); break; } @@ -1804,8 +2268,9 @@ template void Map::Remove(DynamicObject *, bool); /* ******* Dungeon Instance Maps ******* */ InstanceMap::InstanceMap(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode) - : Map(id, expiry, InstanceId, SpawnMode), i_data(NULL), - m_resetAfterUnload(false), m_unloadWhenEmpty(false) + : Map(id, expiry, InstanceId, SpawnMode), + m_resetAfterUnload(false), m_unloadWhenEmpty(false), + i_data(NULL), i_script_id(0) { // the timer is started by default, and stopped when the first player joins // this make sure it gets unloaded if for some reason no player joins @@ -1834,10 +2299,10 @@ bool InstanceMap::CanEnter(Player *player) } // cannot enter if the instance is full (player cap), GMs don't count - InstanceTemplate const* iTemplate = objmgr.GetInstanceTemplate(GetId()); - if (!player->isGameMaster() && GetPlayersCountExceptGMs() >= iTemplate->maxPlayers) + uint32 maxPlayers = GetMaxPlayers(); + if (!player->isGameMaster() && GetPlayersCountExceptGMs() >= maxPlayers) { - sLog.outDetail("MAP: Instance '%u' of map '%s' cannot have more than '%u' players. Player '%s' rejected", GetInstanceId(), GetMapName(), iTemplate->maxPlayers, player->GetName()); + sLog.outDetail("MAP: Instance '%u' of map '%s' cannot have more than '%u' players. Player '%s' rejected", GetInstanceId(), GetMapName(), maxPlayers, player->GetName()); player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAX_PLAYERS); return false; } @@ -1867,79 +2332,81 @@ bool InstanceMap::Add(Player *player) if(!CanEnter(player)) return false; - // get or create an instance save for the map - InstanceSave *mapSave = sInstanceSaveManager.GetInstanceSave(GetInstanceId()); - if(!mapSave) + // Dungeon only code + if(IsDungeon()) { - sLog.outDetail("InstanceMap::Add: creating instance save for map %d spawnmode %d with instance id %d", GetId(), GetSpawnMode(), GetInstanceId()); - mapSave = sInstanceSaveManager.AddInstanceSave(GetId(), GetInstanceId(), GetSpawnMode(), 0, true); - } - - // check for existing instance binds - InstancePlayerBind *playerBind = player->GetBoundInstance(GetId(), GetSpawnMode()); - if(playerBind && playerBind->perm) - { - // cannot enter other instances if bound permanently - if(playerBind->save != mapSave) + // get or create an instance save for the map + InstanceSave *mapSave = sInstanceSaveManager.GetInstanceSave(GetInstanceId()); + if(!mapSave) { - sLog.outError("InstanceMap::Add: player %s(%d) is permanently bound to instance %d,%d,%d,%d,%d,%d but he is being put in instance %d,%d,%d,%d,%d,%d", player->GetName(), player->GetGUIDLow(), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(), playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), mapSave->GetPlayerCount(), mapSave->GetGroupCount(), mapSave->CanReset()); - assert(false); + sLog.outDetail("InstanceMap::Add: creating instance save for map %d spawnmode %d with instance id %d", GetId(), GetSpawnMode(), GetInstanceId()); + mapSave = sInstanceSaveManager.AddInstanceSave(GetId(), GetInstanceId(), GetSpawnMode(), 0, true); } - } - else - { - Group *pGroup = player->GetGroup(); - if(pGroup) + + // check for existing instance binds + InstancePlayerBind *playerBind = player->GetBoundInstance(GetId(), GetSpawnMode()); + if(playerBind && playerBind->perm) { - // solo saves should be reset when entering a group - InstanceGroupBind *groupBind = pGroup->GetBoundInstance(GetId(), GetSpawnMode()); - if(playerBind) + // cannot enter other instances if bound permanently + if(playerBind->save != mapSave) { - sLog.outError("InstanceMap::Add: player %s(%d) is being put in instance %d,%d,%d,%d,%d,%d but he is in group %d and is bound to instance %d,%d,%d,%d,%d,%d!", player->GetName(), player->GetGUIDLow(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), mapSave->GetPlayerCount(), mapSave->GetGroupCount(), mapSave->CanReset(), GUID_LOPART(pGroup->GetLeaderGUID()), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(), playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset()); - if(groupBind) sLog.outError("InstanceMap::Add: the group is bound to instance %d,%d,%d,%d,%d,%d", groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty(), groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount(), groupBind->save->CanReset()); - sLog.outError("InstanceMap::Add: do not let player %s enter instance otherwise crash will happen", player->GetName()); - return false; - //player->UnbindInstance(GetId(), GetSpawnMode()); - //assert(false); + sLog.outError("InstanceMap::Add: player %s(%d) is permanently bound to instance %d,%d,%d,%d,%d,%d but he is being put in instance %d,%d,%d,%d,%d,%d", player->GetName(), player->GetGUIDLow(), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(), playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), mapSave->GetPlayerCount(), mapSave->GetGroupCount(), mapSave->CanReset()); + assert(false); } - // bind to the group or keep using the group save - if(!groupBind) - pGroup->BindToInstance(mapSave, false); - else + } + else + { + Group *pGroup = player->GetGroup(); + if(pGroup) { - // cannot jump to a different instance without resetting it - if(groupBind->save != mapSave) + // solo saves should be reset when entering a group + InstanceGroupBind *groupBind = pGroup->GetBoundInstance(GetId(), GetSpawnMode()); + if(playerBind) { - sLog.outError("InstanceMap::Add: player %s(%d) is being put in instance %d,%d,%d but he is in group %d which is bound to instance %d,%d,%d!", player->GetName(), player->GetGUIDLow(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), GUID_LOPART(pGroup->GetLeaderGUID()), groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty()); - if(mapSave) - sLog.outError("MapSave players: %d, group count: %d", mapSave->GetPlayerCount(), mapSave->GetGroupCount()); - else - sLog.outError("MapSave NULL"); - if(groupBind->save) - sLog.outError("GroupBind save players: %d, group count: %d", groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount()); - else - sLog.outError("GroupBind save NULL"); - assert(false); + sLog.outError("InstanceMap::Add: player %s(%d) is being put in instance %d,%d,%d,%d,%d,%d but he is in group %d and is bound to instance %d,%d,%d,%d,%d,%d!", player->GetName(), player->GetGUIDLow(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), mapSave->GetPlayerCount(), mapSave->GetGroupCount(), mapSave->CanReset(), GUID_LOPART(pGroup->GetLeaderGUID()), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(), playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset()); + if(groupBind) sLog.outError("InstanceMap::Add: the group is bound to instance %d,%d,%d,%d,%d,%d", groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty(), groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount(), groupBind->save->CanReset()); + //assert(false); + return false; } - // if the group/leader is permanently bound to the instance - // players also become permanently bound when they enter - if(groupBind->perm) + // bind to the group or keep using the group save + if(!groupBind) + pGroup->BindToInstance(mapSave, false); + else { - WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4); - data << uint32(0); - player->GetSession()->SendPacket(&data); - player->BindToInstance(mapSave, true); + // cannot jump to a different instance without resetting it + if(groupBind->save != mapSave) + { + sLog.outError("InstanceMap::Add: player %s(%d) is being put in instance %d,%d,%d but he is in group %d which is bound to instance %d,%d,%d!", player->GetName(), player->GetGUIDLow(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), GUID_LOPART(pGroup->GetLeaderGUID()), groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty()); + if(mapSave) + sLog.outError("MapSave players: %d, group count: %d", mapSave->GetPlayerCount(), mapSave->GetGroupCount()); + else + sLog.outError("MapSave NULL"); + if(groupBind->save) + sLog.outError("GroupBind save players: %d, group count: %d", groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount()); + else + sLog.outError("GroupBind save NULL"); + assert(false); + } + // if the group/leader is permanently bound to the instance + // players also become permanently bound when they enter + if(groupBind->perm) + { + WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4); + data << uint32(0); + player->GetSession()->SendPacket(&data); + player->BindToInstance(mapSave, true); + } } } - } - else - { - // set up a solo bind or continue using it - if(!playerBind) - player->BindToInstance(mapSave, false); else - // cannot jump to a different instance without resetting it - assert(playerBind->save == mapSave); + { + // set up a solo bind or continue using it + if(!playerBind) + player->BindToInstance(mapSave, false); + else + // cannot jump to a different instance without resetting it + assert(playerBind->save == mapSave); + } } } @@ -1948,7 +2415,6 @@ bool InstanceMap::Add(Player *player) // first player enters (no players yet) SetResetSchedule(false); - player->SendInitWorldStates(); sLog.outDetail("MAP: Player '%s' entered the instance '%u' of map '%s'", player->GetName(), GetInstanceId(), GetMapName()); // initialize unload state m_unloadTimer = 0; @@ -2075,10 +2541,13 @@ bool InstanceMap::Reset(uint8 method) void InstanceMap::PermBindAllPlayers(Player *player) { + if(!IsDungeon()) + return; + InstanceSave *save = sInstanceSaveManager.GetInstanceSave(GetInstanceId()); if(!save) { - sLog.outError("Cannot bind players, no instance save available for map!\n"); + sLog.outError("Cannot bind players, no instance save available for map!"); return; } @@ -2104,12 +2573,6 @@ void InstanceMap::PermBindAllPlayers(Player *player) } } -time_t InstanceMap::GetResetTime() -{ - InstanceSave *save = sInstanceSaveManager.GetInstanceSave(GetInstanceId()); - return save ? save->GetDifficulty() : DIFFICULTY_NORMAL; -} - void InstanceMap::UnloadAll() { if(HavePlayers()) @@ -2139,7 +2602,7 @@ void InstanceMap::SetResetSchedule(bool on) // only for normal instances // the reset time is only scheduled when there are no payers inside // it is assumed that the reset time will rarely (if ever) change while the reset is scheduled - if(!HavePlayers() && !IsRaid() && !IsHeroic()) + if(IsDungeon() && !HavePlayers() && !IsRaid() && !IsHeroic()) { InstanceSave *save = sInstanceSaveManager.GetInstanceSave(GetInstanceId()); if(!save) sLog.outError("InstanceMap::SetResetSchedule: cannot turn schedule %s, no save available for instance %d of %d", on ? "on" : "off", GetInstanceId(), GetId()); @@ -2147,6 +2610,14 @@ void InstanceMap::SetResetSchedule(bool on) } } +uint32 InstanceMap::GetMaxPlayers() const +{ + InstanceTemplate const* iTemplate = objmgr.GetInstanceTemplate(GetId()); + if(!iTemplate) + return 0; + return IsHeroic() ? iTemplate->maxPlayersHeroic : iTemplate->maxPlayers; +} + /* ******* Battleground Instance Maps ******* */ BattleGroundMap::BattleGroundMap(uint32 id, time_t expiry, uint32 InstanceId) @@ -2204,7 +2675,7 @@ void BattleGroundMap::UnloadAll() { if(Player * plr = m_mapRefManager.getFirst()->getSource()) { - plr->TeleportTo(plr->m_homebindMapId, plr->m_homebindX, plr->m_homebindY, plr->m_homebindZ, plr->GetOrientation()); + plr->TeleportTo(plr->GetBattleGroundEntryPoint()); // TeleportTo removes the player from this map (if the map exists) -> calls BattleGroundMap::Remove -> invalidates the iterator. // just in case, remove the player from the list explicitly here as well to prevent a possible infinite loop // note that this remove is not needed if the code works well in other places diff --git a/src/game/Map.h b/src/game/Map.h index 82136a0cc7e..3d533da1675 100644 --- a/src/game/Map.h +++ b/src/game/Map.h @@ -26,7 +26,7 @@ #include "zthread/Lockable.h" #include "zthread/Mutex.h" #include "zthread/FairReadWriteLock.h" -#include "Database/DBCStructure.h" +#include "DBCStructure.h" #include "GridDefines.h" #include "Cell.h" #include "Timer.h" @@ -43,6 +43,8 @@ class InstanceData; class Group; class InstanceSave; class WorldObject; +class TempSummon; +class CreatureGroup; namespace ZThread { @@ -70,15 +72,135 @@ typedef RGuard<GridRWLock, ZThread::Lockable> GridReadGuard; typedef WGuard<GridRWLock, ZThread::Lockable> GridWriteGuard; typedef Trinity::SingleThreaded<GridRWLock>::Lock NullGuard; -typedef struct +//****************************************** +// Map file format defines +//****************************************** +#define MAP_MAGIC 'SPAM' +#define MAP_VERSION_MAGIC '0.1w' +#define MAP_AREA_MAGIC 'AERA' +#define MAP_HEIGTH_MAGIC 'TGHM' +#define MAP_LIQUID_MAGIC 'QILM' + +struct map_fileheader{ + uint32 mapMagic; + uint32 versionMagic; + uint32 areaMapOffset; + uint32 areaMapSize; + uint32 heightMapOffset; + uint32 heightMapSize; + uint32 liquidMapOffset; + uint32 liquidMapSize; +}; + +#define MAP_AREA_NO_AREA 0x0001 +struct map_areaHeader{ + uint32 fourcc; + uint16 flags; + uint16 gridArea; +}; + +#define MAP_HEIGHT_NO_HIGHT 0x0001 +#define MAP_HEIGHT_AS_INT16 0x0002 +#define MAP_HEIGHT_AS_INT8 0x0004 + +struct map_heightHeader{ + uint32 fourcc; + uint32 flags; + float gridHeight; + float gridMaxHeight; +}; + +#define MAP_LIQUID_NO_TYPE 0x0001 +#define MAP_LIQUID_NO_HIGHT 0x0002 +struct map_liquidHeader{ + uint32 fourcc; + uint16 flags; + uint16 liquidType; + uint8 offsetX; + uint8 offsetY; + uint8 width; + uint8 height; + float liquidLevel; +}; + +enum ZLiquidStatus{ + LIQUID_MAP_NO_WATER = 0x00000000, + LIQUID_MAP_ABOVE_WATER = 0x00000001, + LIQUID_MAP_WATER_WALK = 0x00000002, + LIQUID_MAP_IN_WATER = 0x00000004, + LIQUID_MAP_UNDER_WATER = 0x00000008 +}; + +#define MAP_LIQUID_TYPE_NO_WATER 0x00 +#define MAP_LIQUID_TYPE_WATER 0x01 +#define MAP_LIQUID_TYPE_OCEAN 0x02 +#define MAP_LIQUID_TYPE_MAGMA 0x04 +#define MAP_LIQUID_TYPE_SLIME 0x08 + +#define MAP_ALL_LIQUIDS (MAP_LIQUID_TYPE_WATER | MAP_LIQUID_TYPE_OCEAN | MAP_LIQUID_TYPE_MAGMA | MAP_LIQUID_TYPE_SLIME) + +#define MAP_LIQUID_TYPE_DARK_WATER 0x10 +#define MAP_LIQUID_TYPE_WMO_WATER 0x20 + +struct LiquidData{ + uint32 type; + float level; + float depth_level; +}; + +class GridMap { - uint16 area_flag[16][16]; - uint8 terrain_type[16][16]; - float liquid_level[128][128]; - float v9[16 * 8 + 1][16 * 8 + 1]; - float v8[16 * 8][16 * 8]; - //float Z[MAP_RESOLUTION][MAP_RESOLUTION]; -}GridMap; + uint32 m_flags; + // Area data + uint16 m_gridArea; + uint16 *m_area_map; + // Height level data + float m_gridHeight; + float m_gridIntHeightMultiplier; + union{ + float *m_V9; + uint16 *m_uint16_V9; + uint8 *m_uint8_V9; + }; + union{ + float *m_V8; + uint16 *m_uint16_V8; + uint8 *m_uint8_V8; + }; + // Liquid data + uint16 m_liquidType; + uint8 m_liquid_offX; + uint8 m_liquid_offY; + uint8 m_liquid_width; + uint8 m_liquid_height; + float m_liquidLevel; + uint8 *m_liquid_type; + float *m_liquid_map; + + bool loadAreaData(FILE *in, uint32 offset, uint32 size); + bool loadHeihgtData(FILE *in, uint32 offset, uint32 size); + bool loadLiquidData(FILE *in, uint32 offset, uint32 size); + + // Get height functions and pointers + typedef float (GridMap::*pGetHeightPtr) (float x, float y) const; + pGetHeightPtr m_gridGetHeight; + float getHeightFromFloat(float x, float y) const; + float getHeightFromUint16(float x, float y) const; + float getHeightFromUint8(float x, float y) const; + float getHeightFromFlat(float x, float y) const; + +public: + GridMap(); + ~GridMap(); + bool loadData(char *filaname); + void unloadData(); + + uint16 getArea(float x, float y); + inline float getHeight(float x, float y) {return (this->*m_gridGetHeight)(x, y);} + float getLiquidLevel(float x, float y); + uint8 getTerrainType(float x, float y); + ZLiquidStatus getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData *data = 0); +}; struct CreatureMover { @@ -98,9 +220,10 @@ struct CreatureMover struct InstanceTemplate { uint32 map; - uint32 parent; + uint32 parent; uint32 maxPlayers; - uint32 reset_delay; + uint32 maxPlayersHeroic; + uint32 reset_delay; // FIX ME: now exist normal/heroic raids with possible different time of reset. uint32 access_id; float startLocX; float startLocY; @@ -126,6 +249,8 @@ typedef UNORDERED_MAP<Creature*, CreatureMover> CreatureMoveList; #define INVALID_HEIGHT -100000.0f // for check, must be equal to VMAP_INVALID_HEIGHT, real value for unknown height is VMAP_INVALID_HEIGHT_VALUE #define MIN_UNLOAD_DELAY 1 // immediate unload +typedef std::map<uint32/*leaderDBGUID*/, CreatureGroup*> CreatureGroupHolderType; + class TRINITY_DLL_SPEC Map : public GridRefManager<NGridType>, public Trinity::ObjectLevelLockable<Map, ZThread::Mutex> { friend class MapReference; @@ -179,9 +304,8 @@ class TRINITY_DLL_SPEC Map : public GridRefManager<NGridType>, public Trinity::O time_t GetGridExpiry(void) const { return i_gridExpiry; } uint32 GetId(void) const { return i_id; } - static bool ExistMap(uint32 mapid, int x, int y); - static bool ExistVMap(uint32 mapid, int x, int y); - void LoadMapAndVMap(uint32 mapid, uint32 instanceid, int x, int y); + static bool ExistMap(uint32 mapid, int gx, int gy); + static bool ExistVMap(uint32 mapid, int gx, int gy); static void InitStateMachine(); static void DeleteStateMachine(); @@ -192,22 +316,30 @@ class TRINITY_DLL_SPEC Map : public GridRefManager<NGridType>, public Trinity::O float GetVmapHeight(float x, float y, float z, bool useMaps) const; bool IsInWater(float x, float y, float z) const; // does not use z pos. This is for future use - uint16 GetAreaFlag(float x, float y ) const; + ZLiquidStatus getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData *data = 0) const; + + uint16 GetAreaFlag(float x, float y, float z) const; uint8 GetTerrainType(float x, float y ) const; float GetWaterLevel(float x, float y ) const; bool IsUnderWater(float x, float y, float z) const; - static uint32 GetAreaId(uint16 areaflag,uint32 map_id); - static uint32 GetZoneId(uint16 areaflag,uint32 map_id); + static uint32 GetAreaIdByAreaFlag(uint16 areaflag,uint32 map_id); + static uint32 GetZoneIdByAreaFlag(uint16 areaflag,uint32 map_id); + static void GetZoneAndAreaIdByAreaFlag(uint32& zoneid, uint32& areaid, uint16 areaflag,uint32 map_id); - uint32 GetAreaId(float x, float y) const + uint32 GetAreaId(float x, float y, float z) const { - return GetAreaId(GetAreaFlag(x,y),i_id); + return GetAreaIdByAreaFlag(GetAreaFlag(x,y,z),i_id); } - uint32 GetZoneId(float x, float y) const + uint32 GetZoneId(float x, float y, float z) const { - return GetZoneId(GetAreaFlag(x,y),i_id); + return GetZoneIdByAreaFlag(GetAreaFlag(x,y,z),i_id); + } + + void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, float x, float y, float z) const + { + GetZoneAndAreaIdByAreaFlag(zoneid,areaid,GetAreaFlag(x,y,z),i_id); } virtual void MoveAllCreaturesInMoveList(); @@ -218,8 +350,8 @@ class TRINITY_DLL_SPEC Map : public GridRefManager<NGridType>, public Trinity::O // assert print helper bool CheckGridIntegrity(Creature* c, bool moved) const; - uint32 GetInstanceId() { return i_InstanceId; } - uint8 GetSpawnMode() { return (i_spawnMode); } + uint32 GetInstanceId() const { return i_InstanceId; } + uint8 GetSpawnMode() const { return (i_spawnMode); } virtual bool CanEnter(Player* /*player*/) { return true; } const char* GetMapName() const; @@ -231,6 +363,17 @@ class TRINITY_DLL_SPEC Map : public GridRefManager<NGridType>, public Trinity::O bool IsBattleGround() const { return i_mapEntry && i_mapEntry->IsBattleGround(); } bool IsBattleArena() const { return i_mapEntry && i_mapEntry->IsBattleArena(); } bool IsBattleGroundOrArena() const { return i_mapEntry && i_mapEntry->IsBattleGroundOrArena(); } + bool GetEntrancePos(int32 &mapid, float &x, float &y) + { + if(!i_mapEntry) + return false; + if(i_mapEntry->entrance_map < 0) + return false; + mapid = i_mapEntry->entrance_map; + x = i_mapEntry->entrance_x; + y = i_mapEntry->entrance_y; + return true; + } void AddObjectToRemoveList(WorldObject *obj); void AddObjectToSwitchList(WorldObject *obj, bool on); @@ -274,9 +417,16 @@ class TRINITY_DLL_SPEC Map : public GridRefManager<NGridType>, public Trinity::O template<class NOTIFIER> void VisitAll(const float &x, const float &y, float radius, NOTIFIER ¬ifier); template<class NOTIFIER> void VisitWorld(const float &x, const float &y, float radius, NOTIFIER ¬ifier); template<class NOTIFIER> void VisitGrid(const float &x, const float &y, float radius, NOTIFIER ¬ifier); + + TempSummon *SummonCreature(uint32 entry, float x, float y, float z, float angle, SummonPropertiesEntry const *properties = NULL, uint32 duration = 0, Unit *summoner = NULL); + + CreatureGroupHolderType CreatureGroupHolder; + private: - void LoadVMap(int pX, int pY); - void LoadMap(uint32 mapid, uint32 instanceid, int x,int y); + void LoadMapAndVMap(int gx, int gy); + void LoadVMap(int gx, int gy); + void LoadMap(int gx,int gy); + GridMap *GetGrid(float x, float y); void SetTimer(uint32 t) { i_gridExpiry = t < MIN_GRID_DELAY ? MIN_GRID_DELAY : t; } //uint64 CalculateGridMask(const uint32 &y) const; @@ -292,8 +442,9 @@ class TRINITY_DLL_SPEC Map : public GridRefManager<NGridType>, public Trinity::O CreatureMoveList i_creaturesToMove; bool loaded(const GridPair &) const; - void EnsureGridLoaded(const Cell&, Player* player = NULL); - void EnsureGridCreated(const GridPair &); + void EnsureGridCreated(const GridPair &); + bool EnsureGridLoaded(Cell const&); + void EnsureGridLoadedAtEnter(Cell const&, Player* player = NULL); void buildNGridLinkage(NGridType* pNGridType) { pNGridType->link(this); } @@ -404,11 +555,11 @@ class TRINITY_DLL_SPEC InstanceMap : public Map uint32 GetScriptId() { return i_script_id; } InstanceData* GetInstanceData() { return i_data; } void PermBindAllPlayers(Player *player); - time_t GetResetTime(); void UnloadAll(); bool CanEnter(Player* player); void SendResetWarnings(uint32 timeLeft) const; void SetResetSchedule(bool on); + uint32 GetMaxPlayers() const; private: bool m_resetAfterUnload; bool m_unloadWhenEmpty; diff --git a/src/game/MapInstanced.cpp b/src/game/MapInstanced.cpp index fcdc2f76c11..99d677afffa 100644 --- a/src/game/MapInstanced.cpp +++ b/src/game/MapInstanced.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/MapInstanced.h b/src/game/MapInstanced.h index d34a5cf2dfb..70e59065577 100644 --- a/src/game/MapInstanced.h +++ b/src/game/MapInstanced.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -52,7 +52,7 @@ class TRINITY_DLL_DECL MapInstanced : public Map SetUnloadReferenceLock(GridPair(63-p.x_coord, 63-p.y_coord), true); } - void RemoveGridMapReference(const GridPair &p) + void RemoveGridMapReference(GridPair const& p) { --GridMapReference[p.x_coord][p.y_coord]; if (!GridMapReference[p.x_coord][p.y_coord]) diff --git a/src/game/MapManager.cpp b/src/game/MapManager.cpp index 6a8cd130691..061e0252a13 100644 --- a/src/game/MapManager.cpp +++ b/src/game/MapManager.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,6 +32,7 @@ #include "CellImpl.h" #include "Corpse.h" #include "ObjectMgr.h" +#include "Language.h" #define CLASS_LOCK Trinity::ClassLevelLockable<MapManager, ZThread::Mutex> INSTANTIATE_SINGLETON_2(MapManager, CLASS_LOCK); @@ -165,7 +166,7 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player) { // probably there must be special opcode, because client has this string constant in GlobalStrings.lua // TODO: this is not a good place to send the message - player->GetSession()->SendAreaTriggerMessage(player->GetSession()->GetTrinityString(810), mapName); + player->GetSession()->SendAreaTriggerMessage(player->GetSession()->GetTrinityString(LANG_INSTANCE_RAID_GROUP_ONLY), mapName); sLog.outDebug("MAP: Player '%s' must be in a raid group to enter instance of '%s'", player->GetName(), mapName); return false; } @@ -175,7 +176,8 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player) //The player has a heroic mode and tries to enter into instance which has no a heroic mode if (!entry->SupportsHeroicMode() && player->GetDifficulty() == DIFFICULTY_HEROIC) { - player->SendTransferAborted(mapid, TRANSFER_ABORT_DIFFICULTY2); //Send aborted message + //Send aborted message + player->SendTransferAborted(mapid, TRANSFER_ABORT_DIFFICULTY, DIFFICULTY_HEROIC); return false; } @@ -213,7 +215,7 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player) InstanceTemplate const* instance = objmgr.GetInstanceTemplate(mapid); if(!instance) return false; - + return player->Satisfy(objmgr.GetAccessRequirement(instance->access_id), mapid, true); } else @@ -238,7 +240,7 @@ void MapManager::RemoveBonesFromMap(uint32 mapid, uint64 guid, float x, float y) } void -MapManager::Update(time_t diff) +MapManager::Update(uint32 diff) { i_timer.Update(diff); if( !i_timer.Passed() ) @@ -284,7 +286,8 @@ bool MapManager::ExistMapAndVMap(uint32 mapid, float x,float y) bool MapManager::IsValidMAP(uint32 mapid) { MapEntry const* mEntry = sMapStore.LookupEntry(mapid); - return mEntry && (!mEntry->Instanceable() || objmgr.GetInstanceTemplate(mapid)); + return mEntry && (!mEntry->IsDungeon() || objmgr.GetInstanceTemplate(mapid)); + // TODO: add check for battleground template } /*void MapManager::LoadGrid(int mapid, float x, float y, const WorldObject* obj, bool no_unload) diff --git a/src/game/MapManager.h b/src/game/MapManager.h index ce2e9ab012b..143248de162 100644 --- a/src/game/MapManager.h +++ b/src/game/MapManager.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -47,18 +47,28 @@ class TRINITY_DLL_DECL MapManager : public Trinity::Singleton<MapManager, Trinit Map const* GetBaseMap(uint32 id) const { return const_cast<MapManager*>(this)->_GetBaseMap(id); } void DeleteInstance(uint32 mapid, uint32 instanceId); - inline uint16 GetAreaFlag(uint32 mapid, float x, float y) const + uint16 GetAreaFlag(uint32 mapid, float x, float y, float z) const { Map const* m = GetBaseMap(mapid); - return m->GetAreaFlag(x, y); + return m->GetAreaFlag(x, y, z); + } + uint32 GetAreaId(uint32 mapid, float x, float y, float z) const + { + return Map::GetAreaIdByAreaFlag(GetAreaFlag(mapid, x, y, z),mapid); + } + uint32 GetZoneId(uint32 mapid, float x, float y, float z) const + { + return Map::GetZoneIdByAreaFlag(GetAreaFlag(mapid, x, y, z),mapid); + } + void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, uint32 mapid, float x, float y, float z) + { + Map::GetZoneAndAreaIdByAreaFlag(zoneid,areaid,GetAreaFlag(mapid, x, y, z),mapid); } - inline uint32 GetAreaId(uint32 mapid, float x, float y) { return Map::GetAreaId(GetAreaFlag(mapid, x, y),mapid); } - inline uint32 GetZoneId(uint32 mapid, float x, float y) { return Map::GetZoneId(GetAreaFlag(mapid, x, y),mapid); } void Initialize(void); - void Update(time_t); + void Update(uint32); - inline void SetGridCleanUpDelay(uint32 t) + void SetGridCleanUpDelay(uint32 t) { if( t < MIN_GRID_DELAY ) i_gridCleanUpDelay = MIN_GRID_DELAY; @@ -66,7 +76,7 @@ class TRINITY_DLL_DECL MapManager : public Trinity::Singleton<MapManager, Trinit i_gridCleanUpDelay = t; } - inline void SetMapUpdateInterval(uint32 t) + void SetMapUpdateInterval(uint32 t) { if( t > MIN_MAP_UPDATE_DELAY ) t = MIN_MAP_UPDATE_DELAY; @@ -96,6 +106,11 @@ class TRINITY_DLL_DECL MapManager : public Trinity::Singleton<MapManager, Trinit return IsValidMAP(mapid) && Trinity::IsValidMapCoord(x,y,z,o); } + static bool IsValidMapCoord(WorldLocation const& loc) + { + return IsValidMapCoord(loc.mapid,loc.x,loc.y,loc.z,loc.o); + } + void DoDelayedMovesAndRemoves(); void LoadTransports(); @@ -108,7 +123,7 @@ class TRINITY_DLL_DECL MapManager : public Trinity::Singleton<MapManager, Trinit bool CanPlayerEnter(uint32 mapid, Player* player); void RemoveBonesFromMap(uint32 mapid, uint64 guid, float x, float y); - inline uint32 GenerateInstanceId() { return ++i_MaxInstanceId; } + uint32 GenerateInstanceId() { return ++i_MaxInstanceId; } void InitMaxInstanceId(); /* statistics */ diff --git a/src/game/MapRefManager.h b/src/game/MapRefManager.h index cf8170a7bb3..4337aa75fd9 100644 --- a/src/game/MapRefManager.h +++ b/src/game/MapRefManager.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://getmangos.com/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/MapReference.h b/src/game/MapReference.h index 50a7dcea5a4..ae485af7487 100644 --- a/src/game/MapReference.h +++ b/src/game/MapReference.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://getmangos.com/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/MiscHandler.cpp b/src/game/MiscHandler.cpp index 14da3a94360..0ced3a57201 100644 --- a/src/game/MiscHandler.cpp +++ b/src/game/MiscHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,12 +37,10 @@ #include "Chat.h" #include "ScriptCalls.h" #include <zlib/zlib.h> -#include "MapManager.h" #include "ObjectAccessor.h" #include "Object.h" #include "BattleGround.h" #include "OutdoorPvP.h" -#include "SpellAuras.h" #include "Pet.h" #include "SocialMgr.h" #include "CellImpl.h" @@ -120,7 +118,7 @@ void WorldSession::HandleGossipSelectOptionOpcode( WorldPacket & recv_data ) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); if(!code.empty()) { @@ -356,7 +354,7 @@ void WorldSession::HandleLogoutRequestOpcode( WorldPacket & /*recv_data*/ ) //Can not logout if... if( GetPlayer()->isInCombat() || //...is in combat GetPlayer()->duel || //...is in Duel - GetPlayer()->HasAura(9454,0) || //...is frozen by GM via freeze command + GetPlayer()->HasAura(9454) || //...is frozen by GM via freeze command //...is jumping ...is falling GetPlayer()->HasUnitMovementFlag(MOVEMENTFLAG_JUMPING | MOVEMENTFLAG_FALLING)) { @@ -380,13 +378,13 @@ void WorldSession::HandleLogoutRequestOpcode( WorldPacket & /*recv_data*/ ) // not set flags if player can't free move to prevent lost state at logout cancel if(GetPlayer()->CanFreeMove()) { - GetPlayer()->SetStandState(PLAYER_STATE_SIT); + GetPlayer()->SetStandState(UNIT_STAND_STATE_SIT); WorldPacket data( SMSG_FORCE_MOVE_ROOT, (8+4) ); // guess size data.append(GetPlayer()->GetPackGUID()); data << (uint32)2; SendPacket( &data ); - GetPlayer()->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE); + GetPlayer()->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); } WorldPacket data( SMSG_LOGOUT_RESPONSE, 5 ); @@ -420,10 +418,10 @@ void WorldSession::HandleLogoutCancelOpcode( WorldPacket & /*recv_data*/ ) SendPacket( &data ); //! Stand Up - GetPlayer()->SetStandState(PLAYER_STATE_NONE); + GetPlayer()->SetStandState(UNIT_STAND_STATE_STAND); //! DISABLE_ROTATE - GetPlayer()->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE); + GetPlayer()->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); } sLog.outDebug( "WORLD: sent SMSG_LOGOUT_CANCEL_ACK Message" ); @@ -437,10 +435,12 @@ void WorldSession::HandleTogglePvP( WorldPacket & recv_data ) bool newPvPStatus; recv_data >> newPvPStatus; GetPlayer()->ApplyModFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP, newPvPStatus); + GetPlayer()->ApplyModFlag(PLAYER_FLAGS, PLAYER_FLAGS_PVP_TIMER, !newPvPStatus); } else { GetPlayer()->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP); + GetPlayer()->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_PVP_TIMER); } if(GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP)) @@ -469,9 +469,11 @@ void WorldSession::HandleZoneUpdateOpcode( WorldPacket & recv_data ) sLog.outDetail("WORLD: Recvd ZONE_UPDATE: %u", newZone); - GetPlayer()->UpdateZone(newZone); - - GetPlayer()->SendInitWorldStates(true,newZone); + // use server size data + uint32 newzone, newarea; + GetPlayer()->GetZoneAndAreaId(newzone,newarea); + GetPlayer()->UpdateZone(newzone,newarea); + //GetPlayer()->SendInitWorldStates(true,newZone); } void WorldSession::HandleSetTargetOpcode( WorldPacket & recv_data ) @@ -489,7 +491,8 @@ void WorldSession::HandleSetTargetOpcode( WorldPacket & recv_data ) if(!unit) return; - _player->SetFactionVisibleForFactionTemplateId(unit->getFaction()); + if(FactionTemplateEntry const* factionTemplateEntry = sFactionTemplateStore.LookupEntry(unit->getFaction())) + _player->GetReputationMgr().SetVisible(factionTemplateEntry); } void WorldSession::HandleSetSelectionOpcode( WorldPacket & recv_data ) @@ -506,7 +509,8 @@ void WorldSession::HandleSetSelectionOpcode( WorldPacket & recv_data ) if(!unit) return; - _player->SetFactionVisibleForFactionTemplateId(unit->getFaction()); + if(FactionTemplateEntry const* factionTemplateEntry = sFactionTemplateStore.LookupEntry(unit->getFaction())) + _player->GetReputationMgr().SetVisible(factionTemplateEntry); } void WorldSession::HandleStandStateChangeOpcode( WorldPacket & recv_data ) @@ -562,12 +566,12 @@ void WorldSession::HandleAddFriendOpcodeCallBack(QueryResult *result, uint32 acc uint64 friendGuid; uint32 team; FriendsResult friendResult; - + WorldSession * session = sWorld.FindSession(accountId); if(!session || !session->GetPlayer()) return; - + friendResult = FRIEND_NOT_FOUND; friendGuid = 0; @@ -651,12 +655,12 @@ void WorldSession::HandleAddIgnoreOpcodeCallBack(QueryResult *result, uint32 acc { uint64 IgnoreGuid; FriendsResult ignoreResult; - + WorldSession * session = sWorld.FindSession(accountId); if(!session || !session->GetPlayer()) return; - + ignoreResult = FRIEND_IGNORE_NOT_FOUND; IgnoreGuid = 0; @@ -675,7 +679,7 @@ void WorldSession::HandleAddIgnoreOpcodeCallBack(QueryResult *result, uint32 acc else { ignoreResult = FRIEND_IGNORE_ADDED; - + // ignore list full if(!session->GetPlayer()->GetSocial()->AddToSocialList(GUID_LOPART(IgnoreGuid), true)) ignoreResult = FRIEND_IGNORE_FULL; @@ -902,7 +906,7 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPacket & recv_data) GetPlayer()->SetRestType(REST_TYPE_IN_TAVERN); if(sWorld.IsFFAPvPRealm()) - GetPlayer()->RemoveFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP); + GetPlayer()->RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); return; } @@ -934,16 +938,96 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPacket & recv_data) GetPlayer()->TeleportTo(at->target_mapId,at->target_X,at->target_Y,at->target_Z,at->target_Orientation,TELE_TO_NOT_LEAVE_TRANSPORT); } -void WorldSession::HandleUpdateAccountData(WorldPacket &/*recv_data*/) +void WorldSession::HandleUpdateAccountData(WorldPacket &recv_data) { sLog.outDetail("WORLD: Received CMSG_UPDATE_ACCOUNT_DATA"); - //recv_data.hexlike(); + + CHECK_PACKET_SIZE(recv_data, 4+4+4); + + uint32 type, timestamp, decompressedSize; + recv_data >> type >> timestamp >> decompressedSize; + + sLog.outDebug("UAD: type %u, time %u, decompressedSize %u", type, timestamp, decompressedSize); + + if(type > NUM_ACCOUNT_DATA_TYPES) + return; + + if(decompressedSize == 0) // erase + { + SetAccountData(type, 0, ""); + + WorldPacket data(SMSG_UPDATE_ACCOUNT_DATA_COMPLETE, 4+4); + data << uint32(type); + data << uint32(0); + SendPacket(&data); + + return; + } + + if(decompressedSize > 0xFFFF) + { + sLog.outError("UAD: Account data packet too big, size %u", decompressedSize); + return; + } + + ByteBuffer dest; + dest.resize(decompressedSize); + + uLongf realSize = decompressedSize; + if(uncompress(const_cast<uint8*>(dest.contents()), &realSize, const_cast<uint8*>(recv_data.contents() + recv_data.rpos()), recv_data.size() - recv_data.rpos()) != Z_OK) + { + sLog.outError("UAD: Failed to decompress account data"); + return; + } + + std::string adata; + dest >> adata; + + SetAccountData(type, timestamp, adata); + + WorldPacket data(SMSG_UPDATE_ACCOUNT_DATA_COMPLETE, 4+4); + data << uint32(type); + data << uint32(0); + SendPacket(&data); } -void WorldSession::HandleRequestAccountData(WorldPacket& /*recv_data*/) +void WorldSession::HandleRequestAccountData(WorldPacket& recv_data) { sLog.outDetail("WORLD: Received CMSG_REQUEST_ACCOUNT_DATA"); - //recv_data.hexlike(); + + CHECK_PACKET_SIZE(recv_data, 4); + + uint32 type; + recv_data >> type; + + sLog.outDebug("RAD: type %u", type); + + if(type > NUM_ACCOUNT_DATA_TYPES) + return; + + AccountData *adata = GetAccountData(type); + + uint32 size = adata->Data.size(); + + ByteBuffer dest; + dest.resize(size); + + uLongf destSize = size; + if(size && compress(const_cast<uint8*>(dest.contents()), &destSize, (uint8*)adata->Data.c_str(), size) != Z_OK) + { + sLog.outDebug("RAD: Failed to compress account data"); + return; + } + + dest.resize(destSize); + + WorldPacket data (SMSG_UPDATE_ACCOUNT_DATA, 8+4+4+4+destSize); + data << uint64(_player->GetGUID()); // player guid + data << uint32(type); // type (0-7) + data << uint32(adata->Time); // unix time + data << uint32(size); // decompressed length + data.append(dest); // compressed data + SendPacket(&data); } void WorldSession::HandleSetActionButtonOpcode(WorldPacket& recv_data) @@ -970,7 +1054,7 @@ void WorldSession::HandleSetActionButtonOpcode(WorldPacket& recv_data) } else if(type==ACTION_BUTTON_SPELL) { - sLog.outDetail( "MISC: Added Action %u into button %u", action, button ); + sLog.outDetail( "MISC: Added Spell %u into button %u", action, button ); GetPlayer()->addActionButton(button,action,type,misc); } else if(type==ACTION_BUTTON_ITEM) @@ -1087,22 +1171,6 @@ void WorldSession::HandleMoveRootAck(WorldPacket&/* recv_data*/) */ } -void WorldSession::HandleMoveTeleportAck(WorldPacket&/* recv_data*/) -{ - /* - CHECK_PACKET_SIZE(recv_data,8+4); - - sLog.outDebug("MSG_MOVE_TELEPORT_ACK"); - uint64 guid; - uint32 flags, time; - - recv_data >> guid; - recv_data >> flags >> time; - DEBUG_LOG("Guid " I64FMTD,guid); - DEBUG_LOG("Flags %u, time %u",flags, time/1000); - */ -} - void WorldSession::HandleSetActionBar(WorldPacket& recv_data) { CHECK_PACKET_SIZE(recv_data,1); @@ -1190,7 +1258,7 @@ void WorldSession::HandleInspectOpcode(WorldPacket& recv_data) // find talent rank uint32 curtalent_maxrank = 0; - for(uint32 k = 5; k > 0; --k) + for(uint32 k = MAX_TALENT_RANK; k > 0; --k) { if(talentInfo->RankID[k-1] && plr->HasSpell(talentInfo->RankID[k-1])) { @@ -1427,22 +1495,18 @@ void WorldSession::HandleFarSightOpcode( WorldPacket & recv_data ) uint8 apply; recv_data >> apply; - CellPair pair; - switch(apply) { case 0: - _player->SetFarsightVision(false); - pair = Trinity::ComputeCellPair(_player->GetPositionX(), _player->GetPositionY()); - sLog.outDebug("Player %u set vision to himself", _player->GetGUIDLow()); + sLog.outDebug("Player %u set vision to self", _player->GetGUIDLow()); + _player->SetSeer(_player); break; case 1: - _player->SetFarsightVision(true); - if (WorldObject* obj = _player->GetFarsightTarget()) - pair = Trinity::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY()); + sLog.outDebug("Added FarSight " I64FMT " to player %u", _player->GetUInt64Value(PLAYER_FARSIGHT), _player->GetGUIDLow()); + if(WorldObject *target = _player->GetViewpoint()) + _player->SetSeer(target); else - return; - sLog.outDebug("Added FarSight " I64FMT " to player %u", _player->GetFarSight(), _player->GetGUIDLow()); + sLog.outError("Player %s requests non-existing seer", _player->GetName()); break; default: sLog.outDebug("Unhandled mode in CMSG_FAR_SIGHT: %u", apply); @@ -1475,11 +1539,11 @@ void WorldSession::HandleChooseTitleOpcode( WorldPacket & recv_data ) GetPlayer()->SetUInt32Value(PLAYER_CHOSEN_TITLE, title); } -void WorldSession::HandleAllowMoveAckOpcode( WorldPacket & recv_data ) +void WorldSession::HandleTimeSyncResp( WorldPacket & recv_data ) { CHECK_PACKET_SIZE(recv_data, 4+4); - sLog.outDebug("CMSG_ALLOW_MOVE_ACK"); + sLog.outDebug("CMSG_TIME_SYNC_RESP"); uint32 counter, time_; recv_data >> counter >> time_; @@ -1549,26 +1613,6 @@ void WorldSession::HandleDungeonDifficultyOpcode( WorldPacket & recv_data ) } } -void WorldSession::HandleNewUnknownOpcode( WorldPacket & recv_data ) -{ - sLog.outDebug("New Unknown Opcode %u", recv_data.GetOpcode()); - recv_data.hexlike(); - /* - New Unknown Opcode 837 - STORAGE_SIZE: 60 - 02 00 00 00 00 00 00 00 | 00 00 00 00 01 20 00 00 - 89 EB 33 01 71 5C 24 C4 | 15 03 35 45 74 47 8B 42 - BA B8 1B 40 00 00 00 00 | 00 00 00 00 77 66 42 BF - 23 91 26 3F 00 00 60 41 | 00 00 00 00 - - New Unknown Opcode 837 - STORAGE_SIZE: 44 - 02 00 00 00 00 00 00 00 | 00 00 00 00 00 00 80 00 - 7B 80 34 01 84 EA 2B C4 | 5F A1 36 45 C9 39 1C 42 - BA B8 1B 40 CE 06 00 00 | 00 00 80 3F - */ -} - void WorldSession::HandleDismountOpcode( WorldPacket & /*recv_data*/ ) { sLog.outDebug("WORLD: CMSG_CANCEL_MOUNT_AURA"); @@ -1588,7 +1632,7 @@ void WorldSession::HandleDismountOpcode( WorldPacket & /*recv_data*/ ) } _player->Unmount(); - _player->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); + _player->RemoveAurasByType(SPELL_AURA_MOUNTED); } void WorldSession::HandleMoveFlyModeChangeAckOpcode( WorldPacket & recv_data ) @@ -1636,3 +1680,34 @@ void WorldSession::HandleSetTaxiBenchmarkOpcode( WorldPacket & recv_data ) sLog.outDebug("Client used \"/timetest %d\" command", mode); } +void WorldSession::HandleSpellClick( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data, 8); + + uint64 guid; + recv_data >> guid; + + Vehicle *vehicle = ObjectAccessor::GetVehicle(guid); + + if(!vehicle) + { + sLog.outError("Player %s cannot find vehicle %u", _player->GetName(), guid); + return; + } + + _player->EnterVehicle(vehicle); +} + +void WorldSession::HandleInspectAchievements( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data, 1); + uint64 guid; + if(!recv_data.readPackGUID(guid)) + return; + + Player *player = objmgr.GetPlayer(guid); + if(!player) + return; + + player->GetAchievementMgr().SendRespondInspectAchievements(_player); +} diff --git a/src/game/MotionMaster.cpp b/src/game/MotionMaster.cpp index 9beecf9c5cf..51e684c5a6c 100644 --- a/src/game/MotionMaster.cpp +++ b/src/game/MotionMaster.cpp @@ -50,17 +50,20 @@ MotionMaster::Initialize() if(curr) DirectDelete(curr); } - // set new default movement generator + InitDefault(); +} + +// set new default movement generator +void MotionMaster::InitDefault() +{ if(i_owner->GetTypeId() == TYPEID_UNIT) { MovementGenerator* movement = FactorySelector::selectMovementGenerator((Creature*)i_owner); - push( movement == NULL ? &si_idleMovement : movement ); - InitTop(); + Mutate(movement == NULL ? &si_idleMovement : movement, MOTION_SLOT_IDLE); } else { - push(&si_idleMovement); - needInit[MOTION_SLOT_IDLE] = false; + Mutate(&si_idleMovement, MOTION_SLOT_IDLE); } } @@ -200,28 +203,22 @@ MotionMaster::MoveTargetedHome() Clear(false); - if(i_owner->GetTypeId()==TYPEID_UNIT && !((Creature*)i_owner)->GetCharmerOrOwnerGUID()) + if(Unit *target = i_owner->GetCharmerOrOwner()) { - DEBUG_LOG("Creature (Entry: %u GUID: %u) targeted home", i_owner->GetEntry(), i_owner->GetGUIDLow()); - Mutate(new HomeMovementGenerator<Creature>(), MOTION_SLOT_ACTIVE); + DEBUG_LOG("Pet or controlled unit (Entry: %u GUID: %u) targeting home", + i_owner->GetEntry(), i_owner->GetGUIDLow() ); + + MoveFollow(target, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE, MOTION_SLOT_IDLE); } - else if(i_owner->GetTypeId()==TYPEID_UNIT && ((Creature*)i_owner)->GetCharmerOrOwnerGUID()) + else if(i_owner->GetTypeId() == TYPEID_UNIT) { - DEBUG_LOG("Pet or controlled creature (Entry: %u GUID: %u) targeting home", - i_owner->GetEntry(), i_owner->GetGUIDLow() ); - Unit *target = ((Creature*)i_owner)->GetCharmerOrOwner(); - if(target) - { - i_owner->addUnitState(UNIT_STAT_FOLLOW); - DEBUG_LOG("Following %s (GUID: %u)", - target->GetTypeId()==TYPEID_PLAYER ? "player" : "creature", - target->GetTypeId()==TYPEID_PLAYER ? target->GetGUIDLow() : ((Creature*)target)->GetDBTableGUIDLow() ); - Mutate(new TargetedMovementGenerator<Creature>(*target,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE), MOTION_SLOT_ACTIVE); - } + DEBUG_LOG("Creature (Entry: %u GUID: %u) targeted home", i_owner->GetEntry(), i_owner->GetGUIDLow()); + Mutate(new HomeMovementGenerator<Creature>(), MOTION_SLOT_ACTIVE); } else { sLog.outError("Player (GUID: %u) attempt targeted home", i_owner->GetGUIDLow() ); + return; } } @@ -268,7 +265,7 @@ MotionMaster::MoveChase(Unit* target, float dist, float angle) } void -MotionMaster::MoveFollow(Unit* target, float dist, float angle) +MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlot slot) { // ignore movement request if target not exist if(!target || target == i_owner) @@ -280,7 +277,7 @@ MotionMaster::MoveFollow(Unit* target, float dist, float angle) DEBUG_LOG("Player (GUID: %u) follow to %s (GUID: %u)", i_owner->GetGUIDLow(), target->GetTypeId()==TYPEID_PLAYER ? "player" : "creature", target->GetTypeId()==TYPEID_PLAYER ? i_owner->GetGUIDLow() : ((Creature*)i_owner)->GetDBTableGUIDLow() ); - Mutate(new TargetedMovementGenerator<Player>(*target,dist,angle), MOTION_SLOT_ACTIVE); + Mutate(new TargetedMovementGenerator<Player>(*target,dist,angle), slot); } else { @@ -288,7 +285,7 @@ MotionMaster::MoveFollow(Unit* target, float dist, float angle) i_owner->GetEntry(), i_owner->GetGUIDLow(), target->GetTypeId()==TYPEID_PLAYER ? "player" : "creature", target->GetTypeId()==TYPEID_PLAYER ? target->GetGUIDLow() : ((Creature*)target)->GetDBTableGUIDLow() ); - Mutate(new TargetedMovementGenerator<Creature>(*target,dist,angle), MOTION_SLOT_ACTIVE); + Mutate(new TargetedMovementGenerator<Creature>(*target,dist,angle), slot); } } @@ -308,13 +305,59 @@ MotionMaster::MovePoint(uint32 id, float x, float y, float z) } } +void MotionMaster::MoveKnockbackFrom(float srcX, float srcY, float speedXY, float speedZ) +{ + //this function may make players fall below map + if(i_owner->GetTypeId()==TYPEID_PLAYER) + return; + + float x, y, z; + float dist = speedXY * speedZ * 0.1f; + i_owner->GetNearPoint(i_owner, x, y, z, i_owner->GetObjectSize(), dist, i_owner->GetAngle(srcX, srcY) + M_PI); + MoveJump(x, y, z, speedXY, speedZ); +} + +void MotionMaster::MoveJumpTo(float angle, float speedXY, float speedZ) +{ + //this function may make players fall below map + if(i_owner->GetTypeId()==TYPEID_PLAYER) + return; + + float x, y, z; + float dist = speedXY * speedZ * 0.1f; + i_owner->GetClosePoint(x, y, z, i_owner->GetObjectSize(), dist, angle); + MoveJump(x, y, z, speedXY, speedZ); +} + +void MotionMaster::MoveJump(float x, float y, float z, float speedXY, float speedZ) +{ + uint32 moveFlag = MOVEFLAG_JUMP | MOVEFLAG_WALK; + uint32 time = speedZ * 100; + i_owner->SendMonsterMove(x, y, z, moveFlag, time, speedZ); + + i_owner->addUnitState(UNIT_STAT_CHARGING | UNIT_STAT_JUMPING); + i_owner->m_TempSpeed = speedXY; + if(i_owner->GetTypeId()==TYPEID_PLAYER) + { + DEBUG_LOG("Player (GUID: %u) jump to point (X: %f Y: %f Z: %f)", i_owner->GetGUIDLow(), x, y, z ); + Mutate(new PointMovementGenerator<Player>(0,x,y,z), MOTION_SLOT_CONTROLLED); + } + else + { + DEBUG_LOG("Creature (Entry: %u GUID: %u) jump to point (X: %f Y: %f Z: %f)", + i_owner->GetEntry(), i_owner->GetGUIDLow(), x, y, z ); + Mutate(new PointMovementGenerator<Creature>(0,x,y,z), MOTION_SLOT_CONTROLLED); + } +} + void -MotionMaster::MoveCharge(float x, float y, float z) +MotionMaster::MoveCharge(float x, float y, float z, float speed) { if(Impl[MOTION_SLOT_CONTROLLED] && Impl[MOTION_SLOT_CONTROLLED]->GetMovementGeneratorType() != DISTRACT_MOTION_TYPE) return; i_owner->addUnitState(UNIT_STAT_CHARGING); + i_owner->m_TempSpeed = speed; if(i_owner->GetTypeId()==TYPEID_PLAYER) { DEBUG_LOG("Player (GUID: %u) charge point (X: %f Y: %f Z: %f)", i_owner->GetGUIDLow(), x, y, z ); @@ -484,7 +527,7 @@ void MotionMaster::DirectDelete(_Ty curr) void MotionMaster::DelayedDelete(_Ty curr) { - sLog.outError("CRASH ALARM! Unit (Entry %u) is trying to delete its updating MG (Type %u)!", i_owner->GetEntry(), curr->GetMovementGeneratorType()); + sLog.outCrash("Unit (Entry %u) is trying to delete its updating MG (Type %u)!", i_owner->GetEntry(), curr->GetMovementGeneratorType()); if(isStatic(curr)) return; if(!m_expList) @@ -494,9 +537,9 @@ void MotionMaster::DelayedDelete(_Ty curr) bool MotionMaster::GetDestination(float &x, float &y, float &z) { - if(empty()) + if(empty()) return false; - - return top()->GetDestination(x,y,z); + + return top()->GetDestination(x,y,z); } diff --git a/src/game/MotionMaster.h b/src/game/MotionMaster.h index d68a94b7a1f..ea71495d216 100644 --- a/src/game/MotionMaster.h +++ b/src/game/MotionMaster.h @@ -63,6 +63,9 @@ enum MMCleanFlag MMCF_RESET = 2 // Flag if need top()->Reset() }; +// assume it is 25 yard per 0.6 second +#define SPEED_CHARGE 42.0f + class TRINITY_DLL_SPEC MotionMaster //: private std::stack<MovementGenerator *> { private: @@ -73,7 +76,7 @@ class TRINITY_DLL_SPEC MotionMaster //: private std::stack<MovementGenerator *> typedef std::vector<_Ty> ExpireList; int i_top; - bool empty() const { return i_top < 0; } + bool empty() const { return (i_top < 0); } void pop() { Impl[i_top] = NULL; --i_top; } void push(_Ty _Val) { ++i_top; Impl[i_top] = _Val; } @@ -92,6 +95,7 @@ class TRINITY_DLL_SPEC MotionMaster //: private std::stack<MovementGenerator *> ~MotionMaster(); void Initialize(); + void InitDefault(); int size() const { return i_top + 1; } _Ty top() const { return Impl[i_top]; } @@ -131,12 +135,15 @@ class TRINITY_DLL_SPEC MotionMaster //: private std::stack<MovementGenerator *> void MoveIdle(MovementSlot slot = MOTION_SLOT_ACTIVE); void MoveTargetedHome(); void MoveRandom(float spawndist = 0.0f); - void MoveFollow(Unit* target, float dist, float angle); + void MoveFollow(Unit* target, float dist, float angle, MovementSlot slot = MOTION_SLOT_ACTIVE); void MoveChase(Unit* target, float dist = 0.0f, float angle = 0.0f); void MoveConfused(); void MoveFleeing(Unit* enemy); void MovePoint(uint32 id, float x,float y,float z); - void MoveCharge(float x, float y, float z); + void MoveCharge(float x, float y, float z, float speed = SPEED_CHARGE); + void MoveKnockbackFrom(float srcX, float srcY, float speedXY, float speedZ); + void MoveJumpTo(float angle, float speedXY, float speedZ); + void MoveJump(float x, float y, float z, float speedXY, float speedZ); void MoveTaxiFlight(uint32 path, uint32 pathnode); void MoveDistract(uint32 time); void MovePath(uint32 path_id, bool repeatable); diff --git a/src/game/MovementGenerator.cpp b/src/game/MovementGenerator.cpp index ee314ffae3f..7784df315c9 100644 --- a/src/game/MovementGenerator.cpp +++ b/src/game/MovementGenerator.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/MovementGenerator.h b/src/game/MovementGenerator.h index 49d0ce8798a..cb352b7f1c6 100644 --- a/src/game/MovementGenerator.h +++ b/src/game/MovementGenerator.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/MovementGeneratorImpl.h b/src/game/MovementGeneratorImpl.h index 7197adb5dfe..fc676704d3a 100644 --- a/src/game/MovementGeneratorImpl.h +++ b/src/game/MovementGeneratorImpl.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/MovementHandler.cpp b/src/game/MovementHandler.cpp index 2eb3bab12e7..64c4fecdb53 100644 --- a/src/game/MovementHandler.cpp +++ b/src/game/MovementHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,14 +23,15 @@ #include "WorldSession.h" #include "Opcodes.h" #include "Log.h" -#include "World.h" #include "Corpse.h" #include "Player.h" +#include "Vehicle.h" #include "MapManager.h" #include "Transports.h" #include "BattleGround.h" #include "WaypointMovementGenerator.h" #include "InstanceSaveMgr.h" +#include "ObjectMgr.h" void WorldSession::HandleMoveWorldportAckOpcode( WorldPacket & /*recv_data*/ ) { @@ -40,6 +41,10 @@ void WorldSession::HandleMoveWorldportAckOpcode( WorldPacket & /*recv_data*/ ) void WorldSession::HandleMoveWorldportAckOpcode() { + // ignore unexpected far teleports + if(!GetPlayer()->IsBeingTeleportedFar()) + return; + // get the teleport destination WorldLocation &loc = GetPlayer()->GetTeleportDest(); @@ -58,7 +63,7 @@ void WorldSession::HandleMoveWorldportAckOpcode() if(GetPlayer()->m_InstanceValid == false && !mInstance) GetPlayer()->m_InstanceValid = true; - GetPlayer()->SetSemaphoreTeleport(false); + GetPlayer()->SetSemaphoreTeleportFar(false); // relocate the player to the teleport destination GetPlayer()->SetMapId(loc.mapid); @@ -78,7 +83,6 @@ void WorldSession::HandleMoveWorldportAckOpcode() { sLog.outDebug("WORLD: teleport of player %s (%d) to location %d,%f,%f,%f,%f failed", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow(), loc.mapid, loc.x, loc.y, loc.z, loc.o); // teleport the player home - GetPlayer()->SetDontMove(false); if(!GetPlayer()->TeleportTo(GetPlayer()->m_homebindMapId, GetPlayer()->m_homebindX, GetPlayer()->m_homebindY, GetPlayer()->m_homebindZ, GetPlayer()->GetOrientation())) { // the player must always be able to teleport home @@ -87,6 +91,26 @@ void WorldSession::HandleMoveWorldportAckOpcode() } return; } + + // battleground state prepare (in case join to BG), at relogin/tele player not invited + // only add to bg group and object, if the player was invited (else he entered through command) + if(_player->InBattleGround()) + { + // cleanup seting if outdated + if(!mEntry->IsBattleGroundOrArena()) + { + _player->SetBattleGroundId(0, BATTLEGROUND_TYPE_NONE); // We're not in BG. + // reset destination bg team + _player->SetBGTeam(0); + } + // join to bg case + else if(BattleGround *bg = _player->GetBattleGround()) + { + if(_player->IsInvitedForBattleGroundInstance(_player->GetBattleGroundId())) + bg->AddPlayer(_player); + } + } + GetPlayer()->SendInitialPacketsAfterAddToMap(); // flight fast teleport case @@ -95,7 +119,6 @@ void WorldSession::HandleMoveWorldportAckOpcode() if(!_player->InBattleGround()) { // short preparations to continue flight - GetPlayer()->SetDontMove(false); FlightPathMovementGenerator* flight = (FlightPathMovementGenerator*)(GetPlayer()->GetMotionMaster()->top()); flight->Initialize(*GetPlayer()); return; @@ -129,113 +152,77 @@ void WorldSession::HandleMoveWorldportAckOpcode() // mount allow check if(!mEntry->IsMountAllowed()) - _player->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); - - // battleground state prepare - // only add to bg group and object, if the player was invited (else he entered through command) - if(_player->InBattleGround() && _player->IsInvitedForBattleGroundInstance(_player->GetBattleGroundId())) - { - BattleGround *bg = _player->GetBattleGround(); - if(bg) - { - bg->AddPlayer(_player); - if(bg->GetMapId() == _player->GetMapId()) // we teleported to bg - { - // get the team this way, because arenas might 'override' the teams. - uint32 team = bg->GetPlayerTeam(_player->GetGUID()); - if(!team) - team = _player->GetTeam(); - if(!bg->GetBgRaid(team)) // first player joined - { - Group *group = new Group; - bg->SetBgRaid(team, group); - group->Create(_player->GetGUIDLow(), _player->GetName()); - } - else // raid already exist - { - bg->GetBgRaid(team)->AddMember(_player->GetGUID(), _player->GetName()); - } - } - } - } + _player->RemoveAurasByType(SPELL_AURA_MOUNTED); // honorless target if(GetPlayer()->pvpInfo.inHostileArea) GetPlayer()->CastSpell(GetPlayer(), 2479, true); // resummon pet - if(GetPlayer()->m_temporaryUnsummonedPetNumber) - { - Pet* NewPet = new Pet; - if(!NewPet->LoadPetFromDB(GetPlayer(), 0, GetPlayer()->m_temporaryUnsummonedPetNumber, true)) - delete NewPet; - - GetPlayer()->m_temporaryUnsummonedPetNumber = 0; - } - - GetPlayer()->SetDontMove(false); + GetPlayer()->ResummonPetTemporaryUnSummonedIfAny(); } -void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) +void WorldSession::HandleMoveTeleportAck(WorldPacket& recv_data) { - CHECK_PACKET_SIZE(recv_data, 4+1+4+4+4+4+4); + CHECK_PACKET_SIZE(recv_data,8+4); - /* extract packet */ - MovementInfo movementInfo; - uint32 MovementFlags; + sLog.outDebug("MSG_MOVE_TELEPORT_ACK"); + uint64 guid; + uint32 flags, time; - recv_data >> MovementFlags; - recv_data >> movementInfo.unk1; - recv_data >> movementInfo.time; - recv_data >> movementInfo.x; - recv_data >> movementInfo.y; - recv_data >> movementInfo.z; - recv_data >> movementInfo.o; + recv_data >> guid; + recv_data >> flags >> time; + DEBUG_LOG("Guid " I64FMTD,guid); + DEBUG_LOG("Flags %u, time %u",flags, time/IN_MILISECONDS); - if(MovementFlags & MOVEMENTFLAG_ONTRANSPORT) - { - // recheck - CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+8+4+4+4+4+4); - - recv_data >> movementInfo.t_guid; - recv_data >> movementInfo.t_x; - recv_data >> movementInfo.t_y; - recv_data >> movementInfo.t_z; - recv_data >> movementInfo.t_o; - recv_data >> movementInfo.t_time; - } + Unit *mover = _player->m_mover; + Player *plMover = mover->GetTypeId()==TYPEID_PLAYER ? (Player*)mover : NULL; - if(MovementFlags & (MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING2)) - { - // recheck - CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4); + if(!plMover || !plMover->IsBeingTeleportedNear()) + return; - recv_data >> movementInfo.s_pitch; // pitch, -1.55=looking down, 0=looking straight forward, +1.55=looking up - } + if(guid != plMover->GetGUID()) + return; - // recheck - CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4); + plMover->SetSemaphoreTeleportNear(false); - recv_data >> movementInfo.fallTime; // duration of last jump (when in jump duration from jump begin to now) + uint32 old_zone = plMover->GetZoneId(); - if(MovementFlags & MOVEMENTFLAG_JUMPING) - { - // recheck - CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4+4+4+4); + WorldLocation const& dest = plMover->GetTeleportDest(); - recv_data >> movementInfo.j_unk; // constant, but different when jumping in water and on land? - recv_data >> movementInfo.j_sinAngle; // sin of angle between orientation0 and players orientation - recv_data >> movementInfo.j_cosAngle; // cos of angle between orientation0 and players orientation - recv_data >> movementInfo.j_xyspeed; // speed of xy movement - } + plMover->SetPosition(dest.x, dest.y, dest.z, dest.o, true); - if(MovementFlags & MOVEMENTFLAG_SPLINE) - { - // recheck - CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4); + uint32 newzone, newarea; + plMover->GetZoneAndAreaId(newzone,newarea); + plMover->UpdateZone(newzone,newarea); - recv_data >> movementInfo.u_unk1; // unknown + // new zone + if(old_zone != newzone) + { + // honorless target + if(plMover->pvpInfo.inHostileArea) + plMover->CastSpell(plMover, 2479, true); } + + // resummon pet + GetPlayer()->ResummonPetTemporaryUnSummonedIfAny(); +} + +void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) +{ + uint32 opcode = recv_data.GetOpcode(); + //sLog.outDebug("WORLD: Recvd %s (%u, 0x%X) opcode", LookupOpcodeName(opcode), opcode, opcode); + + Unit *mover = _player->m_mover; + Player *plMover = mover->GetTypeId()==TYPEID_PLAYER ? (Player*)mover : NULL; + + // ignore, waiting processing in WorldSession::HandleMoveWorldportAckOpcode and WorldSession::HandleMoveTeleportAck + if(plMover && plMover->IsBeingTeleported()) + return; + + /* extract packet */ + MovementInfo movementInfo; + ReadMovementInfo(recv_data, &movementInfo); /*----------------*/ if(recv_data.size() != recv_data.rpos()) @@ -248,22 +235,8 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) if (!Trinity::IsValidMapCoord(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o)) return; - // Handle possessed unit movement separately - Unit* pos_unit = GetPlayer()->GetCharm(); - if (pos_unit && pos_unit->isPossessed()) // can be charmed but not possessed - { - HandlePossessedMovement(recv_data, movementInfo, MovementFlags); - return; - } - - if (GetPlayer()->GetDontMove()) - return; - - //Save movement flags - GetPlayer()->SetUnitMovementFlags(MovementFlags); - /* handle special cases */ - if (MovementFlags & MOVEMENTFLAG_ONTRANSPORT) + if (movementInfo.flags & MOVEMENTFLAG_ONTRANSPORT) { // transports size limited // (also received at zeppelin leave by some reason with t_* as absolute in continent coordinates, can be safely skipped) @@ -275,141 +248,114 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) return; // if we boarded a transport, add us to it - if (!GetPlayer()->m_transport) + if (plMover && !plMover->m_transport) { // elevators also cause the client to send MOVEMENTFLAG_ONTRANSPORT - just unmount if the guid can be found in the transport list for (MapManager::TransportSet::iterator iter = MapManager::Instance().m_Transports.begin(); iter != MapManager::Instance().m_Transports.end(); ++iter) { if ((*iter)->GetGUID() == movementInfo.t_guid) { - // unmount before boarding - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); - - GetPlayer()->m_transport = (*iter); - (*iter)->AddPassenger(GetPlayer()); + plMover->m_transport = (*iter); + (*iter)->AddPassenger(plMover); break; } } } } - else if (GetPlayer()->m_transport) // if we were on a transport, leave + else if (plMover && plMover->m_transport) // if we were on a transport, leave { - GetPlayer()->m_transport->RemovePassenger(GetPlayer()); - GetPlayer()->m_transport = NULL; + plMover->m_transport->RemovePassenger(plMover); + plMover->m_transport = NULL; movementInfo.t_x = 0.0f; movementInfo.t_y = 0.0f; movementInfo.t_z = 0.0f; movementInfo.t_o = 0.0f; movementInfo.t_time = 0; + movementInfo.t_seat = -1; } // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map). - if (recv_data.GetOpcode() == MSG_MOVE_FALL_LAND && !GetPlayer()->isInFlight()) - GetPlayer()->HandleFallDamage(movementInfo); + if (opcode == MSG_MOVE_FALL_LAND && plMover && !plMover->isInFlight()) + plMover->HandleFall(movementInfo); - if(((MovementFlags & MOVEMENTFLAG_SWIMMING) != 0) != GetPlayer()->IsInWater()) + if (plMover && ((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != plMover->IsInWater()) { // now client not include swimming flag in case jumping under water - GetPlayer()->SetInWater( !GetPlayer()->IsInWater() || GetPlayer()->GetBaseMap()->IsUnderWater(movementInfo.x, movementInfo.y, movementInfo.z) ); + plMover->SetInWater( !plMover->IsInWater() || plMover->GetBaseMap()->IsUnderWater(movementInfo.x, movementInfo.y, movementInfo.z) ); } /*----------------------*/ /* process position-change */ - recv_data.put<uint32>(5, getMSTime()); // offset flags(4) + unk(1) - WorldPacket data(recv_data.GetOpcode(), (GetPlayer()->GetPackGUID().size()+recv_data.size())); - data.append(GetPlayer()->GetPackGUID()); + recv_data.put<uint32>(6, getMSTime()); // fix time, offset flags(4) + unk(2) + WorldPacket data(recv_data.GetOpcode(), (mover->GetPackGUID().size()+recv_data.size())); + data.append(mover->GetPackGUID()); // use mover guid data.append(recv_data.contents(), recv_data.size()); GetPlayer()->SendMessageToSet(&data, false); - GetPlayer()->SetPosition(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o); - GetPlayer()->m_movementInfo = movementInfo; - if (GetPlayer()->m_lastFallTime >= movementInfo.fallTime || GetPlayer()->m_lastFallZ <=movementInfo.z || recv_data.GetOpcode() == MSG_MOVE_FALL_LAND) - GetPlayer()->SetFallInformation(movementInfo.fallTime, movementInfo.z); - - if(GetPlayer()->isMovingOrTurning()) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - - if(movementInfo.z < -500.0f) - GetPlayer()->HandleFallUnderMap(); -} - -void WorldSession::HandlePossessedMovement(WorldPacket& recv_data, MovementInfo& movementInfo, uint32& MovementFlags) -{ - // Whatever the client is controlling, it will send the GUID of the original player. - // If current player is controlling, it must be handled like the controlled player sent these opcodes - - Unit* pos_unit = GetPlayer()->GetCharm(); - - if (pos_unit->GetTypeId() == TYPEID_PLAYER && ((Player*)pos_unit)->GetDontMove()) - return; - - //Save movement flags - pos_unit->SetUnitMovementFlags(MovementFlags); - - // Remove possession if possessed unit enters a transport - if (MovementFlags & MOVEMENTFLAG_ONTRANSPORT) - { - GetPlayer()->Uncharm(); - return; - } - - recv_data.put<uint32>(5, getMSTime()); - WorldPacket data(recv_data.GetOpcode(), pos_unit->GetPackGUID().size()+recv_data.size()); - data.append(pos_unit->GetPackGUID()); - data.append(recv_data.contents(), recv_data.size()); - // Send the packet to self but not to the possessed player; for creatures the first bool is irrelevant - pos_unit->SendMessageToSet(&data, true, false); - - // Possessed is a player - if (pos_unit->GetTypeId() == TYPEID_PLAYER) + if(plMover) // nothing is charmed, or player charmed { - Player* plr = (Player*)pos_unit; - - if (recv_data.GetOpcode() == MSG_MOVE_FALL_LAND) - plr->HandleFallDamage(movementInfo); - - if(((MovementFlags & MOVEMENTFLAG_SWIMMING) != 0) != plr->IsInWater()) - { - // Now client not include swimming flag in case jumping under water - plr->SetInWater( !plr->IsInWater() || plr->GetBaseMap()->IsUnderWater(movementInfo.x, movementInfo.y, movementInfo.z) ); - } - - plr->SetPosition(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o, false); - plr->m_movementInfo = movementInfo; + plMover->SetPosition(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o); + plMover->m_movementInfo = movementInfo; + plMover->SetUnitMovementFlags(movementInfo.flags); + plMover->UpdateFallInformationIfNeed(movementInfo,recv_data.GetOpcode()); - if(plr->isMovingOrTurning()) - plr->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + if(plMover->isMovingOrTurning()) + plMover->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); if(movementInfo.z < -500.0f) { - GetPlayer()->Uncharm(); - plr->HandleFallUnderMap(); + if(plMover->InBattleGround() + && plMover->GetBattleGround() + && plMover->GetBattleGround()->HandlePlayerUnderMap(_player)) + { + // do nothing, the handle already did if returned true + } + else + { + // NOTE: this is actually called many times while falling + // even after the player has been teleported away + // TODO: discard movement packets after the player is rooted + if(plMover->isAlive()) + { + plMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); + // pl can be alive if GM/etc + if(!plMover->isAlive()) + { + // change the death state to CORPSE to prevent the death timer from + // starting in the next player update + plMover->KillPlayer(); + plMover->BuildPlayerRepop(); + } + } + + // cancel the death timer here if started + plMover->RepopAtGraveyard(); + } } } - else // Possessed unit is a creature + else // creature charmed { - Map* map = MapManager::Instance().GetMap(pos_unit->GetMapId(), pos_unit); - map->CreatureRelocation((Creature*)pos_unit, movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o); + if(Map *map = mover->GetMap()) + { + //if(GetPlayer()->m_seer != mover) + if(((Creature*)mover)->isVehicle()) + map->PlayerRelocation(GetPlayer(), movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o); + map->CreatureRelocation((Creature*)mover, movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o); + } + mover->SetUnitMovementFlags(movementInfo.flags); } } void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recv_data) { - CHECK_PACKET_SIZE(recv_data, 8+4+4+1+4+4+4+4+4); + //sLog.outDebug("WORLD: Recvd %s (%u, 0x%X) opcode", LookupOpcodeName(recv_data.GetOpcode()), recv_data.GetOpcode(), recv_data.GetOpcode()); + + CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+8+4); /* extract packet */ uint64 guid; - uint8 unkB; - uint32 unk1, flags, time, fallTime; - float x, y, z, orientation; - - uint64 t_GUID; - float t_x, t_y, t_z, t_o; - uint32 t_time; - float s_pitch; - float j_unk1, j_sinAngle, j_cosAngle, j_xyspeed; - float u_unk1; + uint32 unk1; float newspeed; recv_data >> guid; @@ -420,47 +366,10 @@ void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recv_data) // continue parse packet - recv_data >> unk1; - recv_data >> flags >> unkB >> time; - recv_data >> x >> y >> z >> orientation; - if (flags & MOVEMENTFLAG_ONTRANSPORT) - { - // recheck - CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+8+4+4+4+4+4); - - recv_data >> t_GUID; - recv_data >> t_x >> t_y >> t_z >> t_o >> t_time; - } - if (flags & (MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING2)) - { - // recheck - CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4); + recv_data >> unk1; // counter or moveEvent - recv_data >> s_pitch; // pitch, -1.55=looking down, 0=looking straight forward, +1.55=looking up - } - - // recheck - CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4); - - recv_data >> fallTime; // duration of last jump (when in jump duration from jump begin to now) - - if ((flags & MOVEMENTFLAG_JUMPING) || (flags & MOVEMENTFLAG_FALLING)) - { - // recheck - CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4+4+4+4); - - recv_data >> j_unk1; // ?constant, but different when jumping in water and on land? - recv_data >> j_sinAngle >> j_cosAngle; // sin + cos of angle between orientation0 and players orientation - recv_data >> j_xyspeed; // speed of xy movement - } - - if(flags & MOVEMENTFLAG_SPLINE) - { - // recheck - CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4); - - recv_data >> u_unk1; // unknown - } + MovementInfo movementInfo; + ReadMovementInfo(recv_data, &movementInfo); // recheck CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4); @@ -473,7 +382,7 @@ void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recv_data) UnitMoveType move_type; UnitMoveType force_move_type; - static char const* move_type_name[MAX_MOVE_TYPE] = { "Walk", "Run", "RunBack", "Swim", "SwimBack", "TurnRate", "Flight", "FlightBack" }; + static char const* move_type_name[MAX_MOVE_TYPE] = { "Walk", "Run", "RunBack", "Swim", "SwimBack", "TurnRate", "Flight", "FlightBack", "PitchRate" }; uint16 opcode = recv_data.GetOpcode(); switch(opcode) @@ -486,6 +395,7 @@ void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recv_data) case CMSG_FORCE_TURN_RATE_CHANGE_ACK: move_type = MOVE_TURN_RATE; force_move_type = MOVE_TURN_RATE; break; case CMSG_FORCE_FLIGHT_SPEED_CHANGE_ACK: move_type = MOVE_FLIGHT; force_move_type = MOVE_FLIGHT; break; case CMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE_ACK: move_type = MOVE_FLIGHT_BACK; force_move_type = MOVE_FLIGHT_BACK; break; + case CMSG_FORCE_PITCH_RATE_CHANGE_ACK: move_type = MOVE_PITCH_RATE; force_move_type = MOVE_PITCH_RATE; break; default: sLog.outError("WorldSession::HandleForceSpeedChangeAck: Unknown move type opcode: %u", opcode); return; @@ -520,20 +430,60 @@ void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recv_data) void WorldSession::HandleSetActiveMoverOpcode(WorldPacket &recv_data) { sLog.outDebug("WORLD: Recvd CMSG_SET_ACTIVE_MOVER"); + recv_data.hexlike(); - CHECK_PACKET_SIZE(recv_data,8); + CHECK_PACKET_SIZE(recv_data, 8); uint64 guid; recv_data >> guid; - WorldPacket data(SMSG_TIME_SYNC_REQ, 4); // new 2.0.x, enable movement - data << uint32(0x00000000); // on blizz it increments periodically - SendPacket(&data); + if(_player->m_mover->GetGUID() != guid) + { + sLog.outError("HandleSetActiveMoverOpcode: incorrect mover guid: mover is " I64FMT " and should be " I64FMT, _player->m_mover->GetGUID(), guid); + return; + } } -void WorldSession::HandleNotActiveMoverOpcode(WorldPacket& /*recv_data*/) +void WorldSession::HandleMoveNotActiveMover(WorldPacket &recv_data) { sLog.outDebug("WORLD: Recvd CMSG_MOVE_NOT_ACTIVE_MOVER"); + recv_data.hexlike(); + + CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+8); + + uint64 old_mover_guid; + recv_data >> old_mover_guid; + + if(_player->m_mover->GetGUID() == old_mover_guid) + { + sLog.outError("HandleMoveNotActiveMover: incorrect mover guid: mover is " I64FMT " and should be " I64FMT " instead of " I64FMT, _player->m_mover->GetGUID(), _player->GetGUID(), old_mover_guid); + return; + } + + MovementInfo mi; + ReadMovementInfo(recv_data, &mi); + _player->m_movementInfo = mi; +} + +void WorldSession::HandleDismissControlledVehicle(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: Recvd CMSG_DISMISS_CONTROLLED_VEHICLE"); + recv_data.hexlike(); + + uint64 vehicleGUID = _player->GetCharmGUID(); + + if(!vehicleGUID) // something wrong here... + return; + + MovementInfo mi; + ReadMovementInfo(recv_data, &mi); + _player->m_movementInfo = mi; + + // using charm guid, because we don't have vehicle guid... + if(Vehicle *vehicle = ObjectAccessor::GetVehicle(vehicleGUID)) + { + _player->ExitVehicle(vehicle); + } } void WorldSession::HandleMountSpecialAnimOpcode(WorldPacket& /*recvdata*/) diff --git a/src/game/NPCHandler.cpp b/src/game/NPCHandler.cpp index df027a48acd..8d1427c86b8 100644 --- a/src/game/NPCHandler.cpp +++ b/src/game/NPCHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,17 +25,14 @@ #include "WorldSession.h" #include "Opcodes.h" #include "Log.h" -#include "World.h" #include "ObjectMgr.h" #include "SpellMgr.h" #include "Player.h" #include "GossipDef.h" -#include "SpellAuras.h" #include "UpdateMask.h" #include "ScriptCalls.h" #include "ObjectAccessor.h" #include "Creature.h" -#include "MapManager.h" #include "Pet.h" #include "BattleGroundMgr.h" #include "BattleGround.h" @@ -57,7 +54,7 @@ void WorldSession::HandleTabardVendorActivateOpcode( WorldPacket & recv_data ) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); SendTabardVendorActivate(guid); } @@ -88,7 +85,7 @@ void WorldSession::HandleBankerActivateOpcode( WorldPacket & recv_data ) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); SendShowBank(guid); } @@ -129,7 +126,7 @@ void WorldSession::SendTrainerList( uint64 guid, const std::string& strTitle ) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); // trainer list loaded at check; if(!unit->isCanTrainingOf(_player,true)) @@ -162,29 +159,29 @@ void WorldSession::SendTrainerList( uint64 guid, const std::string& strTitle ) float fDiscountMod = _player->GetReputationPriceDiscount(unit); uint32 count = 0; - for(TrainerSpellList::const_iterator itr = trainer_spells->spellList.begin(); itr != trainer_spells->spellList.end(); ++itr) + for(TrainerSpellMap::const_iterator itr = trainer_spells->spellList.begin(); itr != trainer_spells->spellList.end(); ++itr) { - TrainerSpell const* tSpell = *itr; + TrainerSpell const* tSpell = &itr->second; - if(!_player->IsSpellFitByClassAndRace(tSpell->spell)) + if(!_player->IsSpellFitByClassAndRace(tSpell->learnedSpell)) continue; ++count; - bool primary_prof_first_rank = spellmgr.IsPrimaryProfessionFirstRankSpell(tSpell->spell); + bool primary_prof_first_rank = spellmgr.IsPrimaryProfessionFirstRankSpell(tSpell->learnedSpell); - SpellChainNode const* chain_node = spellmgr.GetSpellChainNode(tSpell->spell); + SpellChainNode const* chain_node = spellmgr.GetSpellChainNode(tSpell->learnedSpell); uint32 req_spell = spellmgr.GetSpellRequired(tSpell->spell); - data << uint32(tSpell->spell); + data << uint32(tSpell->spell); // learned spell (or cast-spell in profession case) data << uint8(_player->GetTrainerSpellState(tSpell)); - data << uint32(floor(tSpell->spellcost * fDiscountMod)); + data << uint32(floor(tSpell->spellCost * fDiscountMod)); data << uint32(primary_prof_first_rank ? 1 : 0); // primary prof. learn confirmation dialog data << uint32(primary_prof_first_rank ? 1 : 0); // must be equal prev. field to have learn button in enabled state - data << uint8(tSpell->reqlevel); - data << uint32(tSpell->reqskill); - data << uint32(tSpell->reqskillvalue); + data << uint8(tSpell->reqLevel); + data << uint32(tSpell->reqSkill); + data << uint32(tSpell->reqSkillValue); data << uint32(chain_node && chain_node->prev ? chain_node->prev : req_spell); data << uint32(chain_node && chain_node->prev ? req_spell : 0); data << uint32(0); @@ -215,7 +212,7 @@ void WorldSession::HandleTrainerBuySpellOpcode( WorldPacket & recv_data ) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); if(!unit->isCanTrainingOf(_player,true)) return; @@ -235,12 +232,14 @@ void WorldSession::HandleTrainerBuySpellOpcode( WorldPacket & recv_data ) return; // apply reputation discount - uint32 nSpellCost = uint32(floor(trainer_spell->spellcost * _player->GetReputationPriceDiscount(unit))); + uint32 nSpellCost = uint32(floor(trainer_spell->spellCost * _player->GetReputationPriceDiscount(unit))); // check money requirement if(_player->GetMoney() < nSpellCost ) return; + _player->ModifyMoney( -int32(nSpellCost) ); + WorldPacket data(SMSG_PLAY_SPELL_VISUAL, 12); // visual effect on trainer data << uint64(guid) << uint32(0xB3); SendPacket(&data); @@ -249,13 +248,15 @@ void WorldSession::HandleTrainerBuySpellOpcode( WorldPacket & recv_data ) data << uint64(_player->GetGUID()) << uint32(0x016A); SendPacket(&data); - _player->ModifyMoney( -int32(nSpellCost) ); - - // learn explicitly to prevent lost money at lags, learning spell will be only show spell animation - _player->learnSpell(trainer_spell->spell); + // learn explicitly or cast explicitly + if(trainer_spell->IsCastable ()) + //FIXME: prof. spell entry in trainer list not marked gray until list re-open. + _player->CastSpell(_player,trainer_spell->spell,true); + else + _player->learnSpell(spellId,false); data.Initialize(SMSG_TRAINER_BUY_SUCCEEDED, 12); - data << uint64(guid) << uint32(spellId); + data << uint64(guid) << uint32(trainer_spell->spell); SendPacket(&data); } @@ -278,7 +279,7 @@ void WorldSession::HandleGossipHelloOpcode( WorldPacket & recv_data ) GetPlayer()->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TALK); // remove fake death //if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - // GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + // GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); if( unit->isArmorer() || unit->isCivilian() || unit->isQuestGiver() || unit->isServiceProvider()) { @@ -336,7 +337,7 @@ void WorldSession::HandleGossipHelloOpcode( WorldPacket & recv_data ) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); if(!code.empty()) { @@ -369,7 +370,7 @@ void WorldSession::HandleSpiritHealerActivateOpcode( WorldPacket & recv_data ) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); SendSpiritResurrect(); } @@ -430,7 +431,7 @@ void WorldSession::HandleBinderActivateOpcode( WorldPacket & recv_data ) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); SendBindPoint(unit); } @@ -438,11 +439,12 @@ void WorldSession::HandleBinderActivateOpcode( WorldPacket & recv_data ) void WorldSession::SendBindPoint(Creature *npc) { uint32 bindspell = 3286; + uint32 zone_id = _player->GetZoneId(); // update sql homebind - CharacterDatabase.PExecute("UPDATE character_homebind SET map = '%u', zone = '%u', position_x = '%f', position_y = '%f', position_z = '%f' WHERE guid = '%u'", _player->GetMapId(), _player->GetZoneId(), _player->GetPositionX(), _player->GetPositionY(), _player->GetPositionZ(), _player->GetGUIDLow()); + CharacterDatabase.PExecute("UPDATE character_homebind SET map = '%u', zone = '%u', position_x = '%f', position_y = '%f', position_z = '%f' WHERE guid = '%u'", _player->GetMapId(), zone_id, _player->GetPositionX(), _player->GetPositionY(), _player->GetPositionZ(), _player->GetGUIDLow()); _player->m_homebindMapId = _player->GetMapId(); - _player->m_homebindZoneId = _player->GetZoneId(); + _player->m_homebindZoneId = zone_id; _player->m_homebindX = _player->GetPositionX(); _player->m_homebindY = _player->GetPositionY(); _player->m_homebindZ = _player->GetPositionZ(); @@ -461,25 +463,24 @@ void WorldSession::SendBindPoint(Creature *npc) data << float(_player->GetPositionY()); data << float(_player->GetPositionZ()); data << uint32(_player->GetMapId()); - data << uint32(_player->GetZoneId()); + data << uint32(zone_id); SendPacket( &data ); DEBUG_LOG("New Home Position X is %f",_player->GetPositionX()); DEBUG_LOG("New Home Position Y is %f",_player->GetPositionY()); DEBUG_LOG("New Home Position Z is %f",_player->GetPositionZ()); DEBUG_LOG("New Home MapId is %u",_player->GetMapId()); - DEBUG_LOG("New Home ZoneId is %u",_player->GetZoneId()); + DEBUG_LOG("New Home ZoneId is %u",zone_id); // zone update data.Initialize( SMSG_PLAYERBOUND, 8+4 ); data << uint64(_player->GetGUID()); - data << uint32(_player->GetZoneId()); + data << uint32(zone_id); SendPacket( &data ); _player->PlayerTalkClass->CloseGossip(); } -//Need fix void WorldSession::HandleListStabledPetsOpcode( WorldPacket & recv_data ) { CHECK_PACKET_SIZE(recv_data,8); @@ -498,11 +499,11 @@ void WorldSession::HandleListStabledPetsOpcode( WorldPacket & recv_data ) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); // remove mounts this fix bug where getting pet from stable while mounted deletes pet. if(GetPlayer()->IsMounted()) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); + GetPlayer()->RemoveAurasByType(SPELL_AURA_MOUNTED); SendStablePet(npcGUID); } @@ -516,7 +517,9 @@ void WorldSession::SendStablePet(uint64 guid ) Pet *pet = _player->GetPet(); + size_t wpos = data.wpos(); data << uint8(0); // place holder for slot show number + data << uint8(GetPlayer()->m_stableSlots); uint8 num = 0; // counter for place holder @@ -528,13 +531,13 @@ void WorldSession::SendStablePet(uint64 guid ) data << uint32(pet->GetEntry()); data << uint32(pet->getLevel()); data << pet->GetName(); // petname - data << uint32(pet->GetLoyaltyLevel()); // loyalty - data << uint8(0x01); // client slot 1 == current pet (0) + data << uint8(1); // 1 = current, 2/3 = in stable (any from 4,5,... create problems with proper show) ++num; } - // 0 1 2 3 4 5 6 - QueryResult* result = CharacterDatabase.PQuery("SELECT owner, slot, id, entry, level, loyalty, name FROM character_pet WHERE owner = '%u' AND slot > 0 AND slot < 3",_player->GetGUIDLow()); + // 0 1 2 3 4 + QueryResult* result = CharacterDatabase.PQuery("SELECT owner, id, entry, level, name FROM character_pet WHERE owner = '%u' AND slot >= '%u' AND slot <= '%u' ORDER BY slot", + _player->GetGUIDLow(),PET_SAVE_FIRST_STABLE_SLOT,PET_SAVE_LAST_STABLE_SLOT); if(result) { @@ -542,12 +545,11 @@ void WorldSession::SendStablePet(uint64 guid ) { Field *fields = result->Fetch(); - data << uint32(fields[2].GetUInt32()); // petnumber - data << uint32(fields[3].GetUInt32()); // creature entry - data << uint32(fields[4].GetUInt32()); // level - data << fields[6].GetString(); // name - data << uint32(fields[5].GetUInt32()); // loyalty - data << uint8(fields[1].GetUInt32()+1); // slot + data << uint32(fields[1].GetUInt32()); // petnumber + data << uint32(fields[2].GetUInt32()); // creature entry + data << uint32(fields[3].GetUInt32()); // level + data << fields[4].GetString(); // name + data << uint8(2); // 1 = current, 2/3 = in stable (any from 4,5,... create problems with proper show) ++num; }while( result->NextRow() ); @@ -555,7 +557,7 @@ void WorldSession::SendStablePet(uint64 guid ) delete result; } - data.put<uint8>(8, num); // set real data to placeholder + data.put<uint8>(wpos, num); // set real data to placeholder SendPacket(&data); } @@ -580,7 +582,7 @@ void WorldSession::HandleStablePet( WorldPacket & recv_data ) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); Pet *pet = _player->GetPet(); @@ -596,7 +598,8 @@ void WorldSession::HandleStablePet( WorldPacket & recv_data ) uint32 free_slot = 1; - QueryResult *result = CharacterDatabase.PQuery("SELECT owner,slot,id FROM character_pet WHERE owner = '%u' AND slot > 0 AND slot < 3 ORDER BY slot ",_player->GetGUIDLow()); + QueryResult *result = CharacterDatabase.PQuery("SELECT owner,slot,id FROM character_pet WHERE owner = '%u' AND slot >= '%u' AND slot <= '%u' ORDER BY slot ", + _player->GetGUIDLow(),PET_SAVE_FIRST_STABLE_SLOT,PET_SAVE_LAST_STABLE_SLOT); if(result) { do @@ -605,11 +608,16 @@ void WorldSession::HandleStablePet( WorldPacket & recv_data ) uint32 slot = fields[1].GetUInt32(); - if(slot==free_slot) // this slot not free - ++free_slot; + // slots ordered in query, and if not equal then free + if(slot!=free_slot) + break; + + // this slot not free, skip + ++free_slot; }while( result->NextRow() ); + + delete result; } - delete result; if( free_slot > 0 && free_slot <= GetPlayer()->m_stableSlots) { @@ -641,15 +649,14 @@ void WorldSession::HandleUnstablePet( WorldPacket & recv_data ) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); WorldPacket data(SMSG_STABLE_RESULT, 200); // guess size Pet* pet = _player->GetPet(); if(pet && pet->isAlive()) { - uint8 i = 0x06; - data << uint8(i); + data << uint8(0x06); SendPacket(&data); return; } @@ -660,13 +667,14 @@ void WorldSession::HandleUnstablePet( WorldPacket & recv_data ) Pet *newpet = NULL; - QueryResult *result = CharacterDatabase.PQuery("SELECT entry FROM character_pet WHERE owner = '%u' AND id = '%u' AND slot > 0 AND slot < 3",_player->GetGUIDLow(),petnumber); + QueryResult *result = CharacterDatabase.PQuery("SELECT entry FROM character_pet WHERE owner = '%u' AND id = '%u' AND slot >='%u' AND slot <= '%u'", + _player->GetGUIDLow(),petnumber,PET_SAVE_FIRST_STABLE_SLOT,PET_SAVE_LAST_STABLE_SLOT); if(result) { Field *fields = result->Fetch(); uint32 petentry = fields[0].GetUInt32(); - newpet = new Pet(HUNTER_PET); + newpet = new Pet(_player, HUNTER_PET); if(!newpet->LoadPetFromDB(_player,petentry,petnumber)) { delete newpet; @@ -700,11 +708,11 @@ void WorldSession::HandleBuyStableSlot( WorldPacket & recv_data ) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); WorldPacket data(SMSG_STABLE_RESULT, 200); - if(GetPlayer()->m_stableSlots < 2) // max slots amount = 2 + if(GetPlayer()->m_stableSlots < MAX_PET_STABLES) { StableSlotPricesEntry const *SlotPrice = sStableSlotPricesStore.LookupEntry(GetPlayer()->m_stableSlots+1); if(_player->GetMoney() >= SlotPrice->Price) @@ -746,7 +754,7 @@ void WorldSession::HandleStableSwapPet( WorldPacket & recv_data ) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); WorldPacket data(SMSG_STABLE_RESULT, 200); // guess size @@ -756,7 +764,8 @@ void WorldSession::HandleStableSwapPet( WorldPacket & recv_data ) return; // find swapped pet slot in stable - QueryResult *result = CharacterDatabase.PQuery("SELECT slot,entry FROM character_pet WHERE owner = '%u' AND id = '%u'",_player->GetGUIDLow(),pet_number); + QueryResult *result = CharacterDatabase.PQuery("SELECT slot,entry FROM character_pet WHERE owner = '%u' AND id = '%u'", + _player->GetGUIDLow(),pet_number); if(!result) return; @@ -766,11 +775,11 @@ void WorldSession::HandleStableSwapPet( WorldPacket & recv_data ) uint32 petentry = fields[1].GetUInt32(); delete result; - // move alive pet to slot or delele dead pet + // move alive pet to slot or delete dead pet _player->RemovePet(pet,pet->isAlive() ? PetSaveMode(slot) : PET_SAVE_AS_DELETED); // summon unstabled pet - Pet *newpet = new Pet; + Pet *newpet = new Pet(_player); if(!newpet->LoadPetFromDB(_player,petentry,pet_number)) { delete newpet; @@ -802,7 +811,7 @@ void WorldSession::HandleRepairItemOpcode( WorldPacket & recv_data ) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); // reputation discount float discountMod = _player->GetReputationPriceDiscount(unit); diff --git a/src/game/NPCHandler.h b/src/game/NPCHandler.h index e359878173f..4d0395a0c87 100644 --- a/src/game/NPCHandler.h +++ b/src/game/NPCHandler.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -60,7 +60,6 @@ struct GossipTextOption struct GossipText { - uint32 Text_ID; GossipTextOption Options[8]; }; diff --git a/src/game/NullCreatureAI.cpp b/src/game/NullCreatureAI.cpp index 8bea03d0216..be59ed6e40d 100644 --- a/src/game/NullCreatureAI.cpp +++ b/src/game/NullCreatureAI.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/NullCreatureAI.h b/src/game/NullCreatureAI.h index d20d727690a..d5c12d8c9a0 100644 --- a/src/game/NullCreatureAI.h +++ b/src/game/NullCreatureAI.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,7 +26,7 @@ class TRINITY_DLL_DECL PassiveAI : public CreatureAI { public: - PassiveAI(Creature *c) : CreatureAI(c) {} + explicit PassiveAI(Creature *c) : CreatureAI(c) {} ~PassiveAI() {} void MoveInLineOfSight(Unit *) {} @@ -39,7 +39,7 @@ class TRINITY_DLL_DECL PassiveAI : public CreatureAI class TRINITY_DLL_DECL PossessedAI : public PassiveAI { public: - PossessedAI(Creature *c) : PassiveAI(c) {} + explicit PossessedAI(Creature *c) : PassiveAI(c) {} void AttackStart(Unit *target); void UpdateAI(const uint32); @@ -52,7 +52,7 @@ class TRINITY_DLL_DECL PossessedAI : public PassiveAI class TRINITY_DLL_DECL NullCreatureAI : public PassiveAI { public: - NullCreatureAI(Creature *c) : PassiveAI(c) {} + explicit NullCreatureAI(Creature *c) : PassiveAI(c) {} void UpdateAI(const uint32) {} void EnterEvadeMode() {} @@ -61,7 +61,7 @@ class TRINITY_DLL_DECL NullCreatureAI : public PassiveAI class TRINITY_DLL_DECL CritterAI : public PassiveAI { public: - CritterAI(Creature *c) : PassiveAI(c) {} + explicit CritterAI(Creature *c) : PassiveAI(c) {} void DamageTaken(Unit *done_by, uint32 & /*damage*/); void EnterEvadeMode(); diff --git a/src/game/Object.cpp b/src/game/Object.cpp index 956b327820a..c22d00628b2 100644 --- a/src/game/Object.cpp +++ b/src/game/Object.cpp @@ -27,8 +27,8 @@ #include "Object.h" #include "Creature.h" #include "Player.h" +#include "Vehicle.h" #include "ObjectMgr.h" -#include "WorldSession.h" #include "UpdateData.h" #include "UpdateMask.h" #include "Util.h" @@ -44,6 +44,7 @@ #include "GridNotifiersImpl.h" #include "TemporarySummon.h" +#include "Totem.h" uint32 GuidHigh2TypeId(uint32 guid_hi) { @@ -58,8 +59,9 @@ uint32 GuidHigh2TypeId(uint32 guid_hi) case HIGHGUID_DYNAMICOBJECT:return TYPEID_DYNAMICOBJECT; case HIGHGUID_CORPSE: return TYPEID_CORPSE; case HIGHGUID_MO_TRANSPORT: return TYPEID_GAMEOBJECT; + case HIGHGUID_VEHICLE: return TYPEID_UNIT; } - return 10; // unknown + return MAX_TYPEID; // unknown } Object::Object( ) @@ -83,17 +85,16 @@ Object::~Object( ) //if(m_objectUpdated) // ObjectAccessor::Instance().RemoveUpdateObject(this); - if(m_uint32Values) + if(IsInWorld()) { - if(IsInWorld()) - { - ///- Do NOT call RemoveFromWorld here, if the object is a player it will crash - sLog.outError("Object::~Object - guid="I64FMTD", typeid=%d deleted but still in world!!", GetGUID(), GetTypeId()); - assert(false); - } + sLog.outCrash("Object::~Object - guid="I64FMTD", typeid=%d deleted but still in world!!", GetGUID(), GetTypeId()); + assert(false); + } - assert(!m_objectUpdated); + assert(!m_objectUpdated); + if(m_uint32Values) + { //DEBUG_LOG("Object desctr 1 check (%p)",(void*)this); delete [] m_uint32Values; delete [] m_uint32Values_mirror; @@ -152,7 +153,7 @@ void Object::BuildCreateUpdateBlockForPlayer(UpdateData *data, Player *target) c if(target == this) // building packet for oneself flags |= UPDATEFLAG_SELF; - if(flags & UPDATEFLAG_HASPOSITION) + if(flags & UPDATEFLAG_HAS_POSITION) { // UPDATETYPE_CREATE_OBJECT2 dynamic objects, corpses... if(isType(TYPEMASK_DYNAMICOBJECT) || isType(TYPEMASK_CORPSE) || isType(TYPEMASK_PLAYER)) @@ -178,6 +179,12 @@ void Object::BuildCreateUpdateBlockForPlayer(UpdateData *data, Player *target) c break; } } + + if(isType(TYPEMASK_UNIT)) + { + if(((Unit*)this)->getVictim()) + flags |= UPDATEFLAG_HAS_TARGET; + } } //sLog.outDebug("BuildCreateUpdate: update-type: %u, object-type: %u got flags: %X, flags2: %X", updatetype, m_objectTypeId, flags, flags2); @@ -248,11 +255,18 @@ void Object::DestroyForPlayer(Player *target) const WorldPacket data(SMSG_DESTROY_OBJECT, 8); data << GetGUID(); + data << uint8(0); // WotLK (bool) target->GetSession()->SendPacket( &data ); } -void Object::_BuildMovementUpdate(ByteBuffer * data, uint8 flags, uint32 flags2 ) const +void Object::_BuildMovementUpdate(ByteBuffer * data, uint8 flags, uint32 flags2) const { + uint16 unk_flags = ((GetTypeId() == TYPEID_PLAYER) ? ((Player*)this)->m_movementInfo.unk1 : 0); + + if(GetTypeId() == TYPEID_UNIT) + if(((Creature*)this)->isVehicle()) + unk_flags |= 0x20; // always allow pitch + *data << (uint8)flags; // update flags // 0x20 @@ -271,7 +285,7 @@ void Object::_BuildMovementUpdate(ByteBuffer * data, uint8 flags, uint32 flags2 { flags2 = ((Player*)this)->GetUnitMovementFlags(); - if(((Player*)this)->GetTransport()) + if(((Player*)this)->GetTransport() || ((Player*)this)->hasUnitState(UNIT_STAT_ONVEHICLE)) flags2 |= MOVEMENTFLAG_ONTRANSPORT; else flags2 &= ~MOVEMENTFLAG_ONTRANSPORT; @@ -289,12 +303,12 @@ void Object::_BuildMovementUpdate(ByteBuffer * data, uint8 flags, uint32 flags2 } *data << uint32(flags2); // movement flags - *data << uint8(0); // unk 2.3.0 + *data << uint16(unk_flags); // unknown 2.3.0 *data << uint32(getMSTime()); // time (in milliseconds) } // 0x40 - if (flags & UPDATEFLAG_HASPOSITION) + if (flags & UPDATEFLAG_HAS_POSITION) { // 0x02 if(flags & UPDATEFLAG_TRANSPORT && ((GameObject*)this)->GetGoType() == GAMEOBJECT_TYPE_MO_TRANSPORT) @@ -321,18 +335,22 @@ void Object::_BuildMovementUpdate(ByteBuffer * data, uint8 flags, uint32 flags2 { if(GetTypeId() == TYPEID_PLAYER) { - *data << (uint64)((Player*)this)->GetTransport()->GetGUID(); + if(((Player*)this)->hasUnitState(UNIT_STAT_ONVEHICLE)) + *data << (uint64)((Player*)this)->GetCharmGUID(); + else + *data << (uint64)((Player*)this)->GetTransport()->GetGUID(); *data << (float)((Player*)this)->GetTransOffsetX(); *data << (float)((Player*)this)->GetTransOffsetY(); *data << (float)((Player*)this)->GetTransOffsetZ(); *data << (float)((Player*)this)->GetTransOffsetO(); *data << (uint32)((Player*)this)->GetTransTime(); + *data << (int8)((Player*)this)->GetTransSeat(); } //TrinIty currently not have support for other than player on transport } // 0x02200000 - if(flags2 & (MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING2)) + if((flags2 & (MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING2)) || (unk_flags & 0x20)) { if(GetTypeId() == TYPEID_PLAYER) *data << (float)((Player*)this)->m_movementInfo.s_pitch; @@ -381,6 +399,7 @@ void Object::_BuildMovementUpdate(ByteBuffer * data, uint8 flags, uint32 flags2 *data << ((Unit*)this)->GetSpeed( MOVE_FLIGHT ); *data << ((Unit*)this)->GetSpeed( MOVE_FLIGHT_BACK ); *data << ((Unit*)this)->GetSpeed( MOVE_TURN_RATE ); + *data << ((Unit*)this)->GetSpeed( MOVE_PITCH_RATE ); // 0x08000000 if(flags2 & MOVEMENTFLAG_SPLINE2) @@ -445,6 +464,8 @@ void Object::_BuildMovementUpdate(ByteBuffer * data, uint8 flags, uint32 flags2 *data << path.GetNodes()[i].z; } + *data << uint8(0); // added in 3.0.8 + /*for(uint32 i = 0; i < poscount; i++) { // path points @@ -482,7 +503,7 @@ void Object::_BuildMovementUpdate(ByteBuffer * data, uint8 flags, uint32 flags2 break; case TYPEID_PLAYER: if(flags & UPDATEFLAG_SELF) - *data << uint32(0x00000015); // unk, can be 0x15 or 0x22 + *data << uint32(0x0000002F); // unk, can be 0x15 or 0x22 else *data << uint32(0x00000008); // unk, can be 0x7 or 0x8 break; @@ -505,6 +526,15 @@ void Object::_BuildMovementUpdate(ByteBuffer * data, uint8 flags, uint32 flags2 case TYPEID_CORPSE: *data << uint32(GetGUIDHigh()); // GetGUIDHigh() break; + case TYPEID_UNIT: + *data << uint32(0x0000000B); // unk, can be 0xB or 0xC + break; + case TYPEID_PLAYER: + if(flags & UPDATEFLAG_SELF) + *data << uint32(0x0000002F); // unk, can be 0x15 or 0x22 + else + *data << uint32(0x00000008); // unk, can be 0x7 or 0x8 + break; default: *data << uint32(0x00000000); // unk break; @@ -512,9 +542,12 @@ void Object::_BuildMovementUpdate(ByteBuffer * data, uint8 flags, uint32 flags2 } // 0x4 - if(flags & UPDATEFLAG_FULLGUID) + if(flags & UPDATEFLAG_HAS_TARGET) // packed guid (current target guid) { - *data << uint8(0); // packed guid (probably target guid) + if(Unit *victim = ((Unit*)this)->getVictim()) + data->append(victim->GetPackGUID()); + else + *data << uint8(0); } // 0x2 @@ -522,6 +555,13 @@ void Object::_BuildMovementUpdate(ByteBuffer * data, uint8 flags, uint32 flags2 { *data << uint32(getMSTime()); // ms time } + + // 0x80 + if(flags & UPDATEFLAG_VEHICLE) // unused for now + { + *data << uint32(((Vehicle*)this)->GetVehicleId()); // vehicle id + *data << float(0); // facing adjustment + } } void Object::_BuildValuesUpdate(uint8 updatetype, ByteBuffer * data, UpdateMask *updateMask, Player *target) const @@ -537,10 +577,10 @@ void Object::_BuildValuesUpdate(uint8 updatetype, ByteBuffer * data, UpdateMask if ( ((GameObject*)this)->ActivateToQuest(target) || target->isGameMaster()) { IsActivateToQuest = true; - updateMask->SetBit(GAMEOBJECT_DYN_FLAGS); + updateMask->SetBit(GAMEOBJECT_DYNAMIC); } - if (GetUInt32Value(GAMEOBJECT_ARTKIT)) - updateMask->SetBit(GAMEOBJECT_ARTKIT); + if (((GameObject*)this)->GetGoArtKit()) + updateMask->SetBit(GAMEOBJECT_BYTES_1); } } else //case UPDATETYPE_VALUES @@ -551,8 +591,8 @@ void Object::_BuildValuesUpdate(uint8 updatetype, ByteBuffer * data, UpdateMask { IsActivateToQuest = true; } - updateMask->SetBit(GAMEOBJECT_DYN_FLAGS); - updateMask->SetBit(GAMEOBJECT_ANIMPROGRESS); + updateMask->SetBit(GAMEOBJECT_DYNAMIC); + updateMask->SetBit(GAMEOBJECT_BYTES_1); } } @@ -625,33 +665,32 @@ void Object::_BuildValuesUpdate(uint8 updatetype, ByteBuffer * data, UpdateMask // FG: pretend that OTHER players in own group are friendly ("blue") else if(index == UNIT_FIELD_BYTES_2 || index == UNIT_FIELD_FACTIONTEMPLATE) { - bool ch = false; - if(target->GetTypeId() == TYPEID_PLAYER && GetTypeId() == TYPEID_PLAYER && target != this) + bool ch = false; + if(GetTypeId() == TYPEID_PLAYER && target != this + && ((Player*)this)->IsInSameRaidWith(target)) { - if(target->IsInSameGroupWith((Player*)this) || target->IsInSameRaidWith((Player*)this)) - { - if(index == UNIT_FIELD_BYTES_2) + // Allow targetting opposite faction in party when enabled in config + if(sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP) && index == UNIT_FIELD_BYTES_2) { DEBUG_LOG("-- VALUES_UPDATE: Sending '%s' the blue-group-fix from '%s' (flag)", target->GetName(), ((Player*)this)->GetName()); - *data << ( m_uint32Values[ index ] & ((UNIT_BYTE2_FLAG_SANCTUARY | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5) << 8) ); // this flag is at uint8 offset 1 !! + *data << ( m_uint32Values[ index ] & ((UNIT_BYTE2_FLAG_SANCTUARY /*| UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5*/) << 8) ); // this flag is at uint8 offset 1 !! ch = true; } - else if(index == UNIT_FIELD_FACTIONTEMPLATE) + else { FactionTemplateEntry const *ft1, *ft2; ft1 = ((Player*)this)->getFactionTemplateEntry(); - ft2 = ((Player*)target)->getFactionTemplateEntry(); + ft2 = target->getFactionTemplateEntry(); if(ft1 && ft2 && !ft1->IsFriendlyTo(*ft2)) { - uint32 faction = ((Player*)target)->getFaction(); // pretend that all other HOSTILE players have own faction, to allow follow, heal, rezz (trade wont work) + uint32 faction = target->getFaction(); // pretend that all other HOSTILE players have own faction, to allow follow, heal, rezz (trade wont work) DEBUG_LOG("-- VALUES_UPDATE: Sending '%s' the blue-group-fix from '%s' (faction %u)", target->GetName(), ((Player*)this)->GetName(), faction); *data << uint32(faction); ch = true; } } } - } if(!ch) *data << m_uint32Values[ index ]; } @@ -670,7 +709,7 @@ void Object::_BuildValuesUpdate(uint8 updatetype, ByteBuffer * data, UpdateMask if( updateMask->GetBit( index ) ) { // send in current format (float as float, uint32 as uint32) - if ( index == GAMEOBJECT_DYN_FLAGS ) + if ( index == GAMEOBJECT_DYNAMIC ) { if(IsActivateToQuest ) { @@ -710,11 +749,10 @@ void Object::_BuildValuesUpdate(uint8 updatetype, ByteBuffer * data, UpdateMask void Object::ClearUpdateMask(bool remove) { - for( uint16 index = 0; index < m_valuesCount; index ++ ) - { - if(m_uint32Values_mirror[index]!= m_uint32Values[index]) - m_uint32Values_mirror[index] = m_uint32Values[index]; - } + uint32 *temp = m_uint32Values; + + memcpy(m_uint32Values_mirror, m_uint32Values, m_valuesCount*sizeof(uint32)); + if(m_objectUpdated) { if(remove) @@ -758,18 +796,23 @@ bool Object::LoadValues(const char* data) void Object::_SetUpdateBits(UpdateMask *updateMask, Player* /*target*/) const { - for( uint16 index = 0; index < m_valuesCount; index ++ ) + uint32 *value = m_uint32Values; + uint32 *mirror = m_uint32Values_mirror; + + for(uint16 index = 0; index < m_valuesCount; ++index, ++value, ++mirror) { - if(m_uint32Values_mirror[index]!= m_uint32Values[index]) + if(*mirror != *value) updateMask->SetBit(index); } } void Object::_SetCreateBits(UpdateMask *updateMask, Player* /*target*/) const { - for( uint16 index = 0; index < m_valuesCount; index++ ) + uint32 *value = m_uint32Values; + + for(uint16 index = 0; index < m_valuesCount; ++index, ++value) { - if(GetUInt32Value(index) != 0) + if(*value) updateMask->SetBit(index); } } @@ -831,6 +874,48 @@ void Object::SetUInt64Value( uint16 index, const uint64 &value ) } } +bool Object::AddUInt64Value(uint16 index, const uint64 &value) +{ + ASSERT( index + 1 < m_valuesCount || PrintIndexError( index , true ) ); + if(value && !*((uint64*)&(m_uint32Values[index]))) + { + m_uint32Values[ index ] = *((uint32*)&value); + m_uint32Values[ index + 1 ] = *(((uint32*)&value) + 1); + + if(m_inWorld) + { + if(!m_objectUpdated) + { + ObjectAccessor::Instance().AddUpdateObject(this); + m_objectUpdated = true; + } + } + return true; + } + return false; +} + +bool Object::RemoveUInt64Value(uint16 index, const uint64 &value) +{ + ASSERT( index + 1 < m_valuesCount || PrintIndexError( index , true ) ); + if(value && *((uint64*)&(m_uint32Values[index])) == value) + { + m_uint32Values[ index ] = 0; + m_uint32Values[ index + 1 ] = 0; + + if(m_inWorld) + { + if(!m_objectUpdated) + { + ObjectAccessor::Instance().AddUpdateObject(this); + m_objectUpdated = true; + } + } + return true; + } + return false; +} + void Object::SetFloatValue( uint16 index, float value ) { ASSERT( index < m_valuesCount || PrintIndexError( index , true ) ); @@ -1051,6 +1136,8 @@ bool Object::PrintIndexError(uint32 index, bool set) const } WorldObject::WorldObject() + : m_mapId(0), m_InstanceId(0), m_phaseMask(PHASEMASK_NORMAL), + m_positionX(0.0f), m_positionY(0.0f), m_positionZ(0.0f), m_orientation(0.0f) { m_positionX = 0.0f; m_positionY = 0.0f; @@ -1062,8 +1149,6 @@ WorldObject::WorldObject() m_name = ""; - mSemaphoreTeleport = false; - m_isActive = false; IsTempWorldObject = false; } @@ -1072,8 +1157,8 @@ void WorldObject::SetWorldObject(bool on) { if(!IsInWorld()) return; - - GetMap()->AddObjectToSwitchList(this, on); + + GetMap()->AddObjectToSwitchList(this, on); } void WorldObject::setActive( bool on ) @@ -1109,21 +1194,27 @@ void WorldObject::setActive( bool on ) } } -void WorldObject::_Create( uint32 guidlow, HighGuid guidhigh, uint32 mapid ) +void WorldObject::_Create( uint32 guidlow, HighGuid guidhigh, uint32 mapid, uint32 phaseMask ) { Object::_Create(guidlow, 0, guidhigh); m_mapId = mapid; + m_phaseMask = phaseMask; } uint32 WorldObject::GetZoneId() const { - return MapManager::Instance().GetBaseMap(m_mapId)->GetZoneId(m_positionX,m_positionY); + return MapManager::Instance().GetBaseMap(m_mapId)->GetZoneId(m_positionX,m_positionY,m_positionZ); } uint32 WorldObject::GetAreaId() const { - return MapManager::Instance().GetBaseMap(m_mapId)->GetAreaId(m_positionX,m_positionY); + return MapManager::Instance().GetBaseMap(m_mapId)->GetAreaId(m_positionX,m_positionY,m_positionZ); +} + +void WorldObject::GetZoneAndAreaId(uint32& zoneid, uint32& areaid) const +{ + MapManager::Instance().GetBaseMap(m_mapId)->GetZoneAndAreaId(zoneid,areaid,m_positionX,m_positionY,m_positionZ); } InstanceData* WorldObject::GetInstanceData() @@ -1152,6 +1243,13 @@ float WorldObject::GetDistance2d(float x, float y) const return ( dist > 0 ? dist : 0); } +float WorldObject::GetExactDistance2d(const float x, const float y) const +{ + float dx = GetPositionX() - x; + float dy = GetPositionY() - y; + return sqrt((dx*dx) + (dy*dy)); +} + float WorldObject::GetDistance(const float x, const float y, const float z) const { float dx = GetPositionX() - x; @@ -1238,6 +1336,25 @@ float WorldObject::GetAngle( const float x, const float y ) const return ang; } +void WorldObject::GetSinCos(const float x, const float y, float &vsin, float &vcos) +{ + float dx = GetPositionX() - x; + float dy = GetPositionY() - y; + + if(dx < 0.001f && dy < 0.001f) + { + float angle = rand_norm()*2*M_PI; + vcos = cos(angle); + vsin = sin(angle); + } + else + { + float dist = sqrt((dx*dx) + (dy*dy)); + vcos = dx / dist; + vsin = dy / dist; + } +} + bool WorldObject::HasInArc(const float arcangle, const WorldObject* obj) const { // always have self in arc @@ -1359,50 +1476,17 @@ void Object::ForceValuesUpdateAtIndex(uint32 i) namespace Trinity { - class MessageChatLocaleCacheDo + class MonsterChatBuilder { public: - MessageChatLocaleCacheDo(WorldObject const& obj, ChatMsg msgtype, int32 textId, uint32 language, uint64 targetGUID, float dist) - : i_object(obj), i_msgtype(msgtype), i_textId(textId), i_language(language), - i_targetGUID(targetGUID), i_dist(dist) - { - } - - ~MessageChatLocaleCacheDo() - { - for(int i = 0; i < i_data_cache.size(); ++i) - delete i_data_cache[i]; - } - - void operator()(Player* p) + MonsterChatBuilder(WorldObject const& obj, ChatMsg msgtype, int32 textId, uint32 language, uint64 targetGUID) + : i_object(obj), i_msgtype(msgtype), i_textId(textId), i_language(language), i_targetGUID(targetGUID) {} + void operator()(WorldPacket& data, int32 loc_idx) { - // skip far away players - if(p->GetDistance(&i_object) > i_dist) - return; + char const* text = objmgr.GetMangosString(i_textId,loc_idx); - uint32 loc_idx = p->GetSession()->GetSessionDbLocaleIndex(); - uint32 cache_idx = loc_idx+1; - WorldPacket* data; - - // create if not cached yet - if(i_data_cache.size() < cache_idx+1 || !i_data_cache[cache_idx]) - { - if(i_data_cache.size() < cache_idx+1) - i_data_cache.resize(cache_idx+1); - - char const* text = objmgr.GetTrinityString(i_textId,loc_idx); - - data = new WorldPacket(SMSG_MESSAGECHAT, 200); - - // TODO: i_object.GetName() also must be localized? - i_object.BuildMonsterChat(data,i_msgtype,text,i_language,i_object.GetNameForLocaleIdx(loc_idx),i_targetGUID); - - i_data_cache[cache_idx] = data; - } - else - data = i_data_cache[cache_idx]; - - p->SendDirectMessage(data); + // TODO: i_object.GetName() also must be localized? + i_object.BuildMonsterChat(&data,i_msgtype,text,i_language,i_object.GetNameForLocaleIdx(loc_idx),i_targetGUID); } private: @@ -1411,8 +1495,6 @@ namespace Trinity int32 i_textId; uint32 i_language; uint64 i_targetGUID; - float i_dist; - std::vector<WorldPacket*> i_data_cache; // 0 = default, i => i-1 locale index }; } // namespace Trinity @@ -1424,9 +1506,10 @@ void WorldObject::MonsterSay(int32 textId, uint32 language, uint64 TargetGuid) cell.data.Part.reserved = ALL_DISTRICT; cell.SetNoCreate(); - Trinity::MessageChatLocaleCacheDo say_do(*this, CHAT_MSG_MONSTER_SAY, textId,language,TargetGuid,sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY)); - Trinity::PlayerWorker<Trinity::MessageChatLocaleCacheDo> say_worker(say_do); - TypeContainerVisitor<Trinity::PlayerWorker<Trinity::MessageChatLocaleCacheDo>, WorldTypeMapContainer > message(say_worker); + MaNGOS::MonsterChatBuilder say_build(*this, CHAT_MSG_MONSTER_SAY, textId,language,TargetGuid); + MaNGOS::LocalizedPacketDo<MaNGOS::MonsterChatBuilder> say_do(say_build); + MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::MonsterChatBuilder> > say_worker(this,sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY),say_do); + TypeContainerVisitor<MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::MonsterChatBuilder> >, WorldTypeMapContainer > message(say_worker); CellLock<GridReadGuard> cell_lock(cell, p); cell_lock->Visit(cell_lock, message, *GetMap()); } @@ -1439,13 +1522,27 @@ void WorldObject::MonsterYell(int32 textId, uint32 language, uint64 TargetGuid) cell.data.Part.reserved = ALL_DISTRICT; cell.SetNoCreate(); - Trinity::MessageChatLocaleCacheDo say_do(*this, CHAT_MSG_MONSTER_YELL, textId,language,TargetGuid,sWorld.getConfig(CONFIG_LISTEN_RANGE_YELL)); - Trinity::PlayerWorker<Trinity::MessageChatLocaleCacheDo> say_worker(say_do); - TypeContainerVisitor<Trinity::PlayerWorker<Trinity::MessageChatLocaleCacheDo>, WorldTypeMapContainer > message(say_worker); + MaNGOS::MonsterChatBuilder say_build(*this, CHAT_MSG_MONSTER_YELL, textId,language,TargetGuid); + MaNGOS::LocalizedPacketDo<MaNGOS::MonsterChatBuilder> say_do(say_build); + MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::MonsterChatBuilder> > say_worker(this,sWorld.getConfig(CONFIG_LISTEN_RANGE_YELL),say_do); + TypeContainerVisitor<MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::MonsterChatBuilder> >, WorldTypeMapContainer > message(say_worker); CellLock<GridReadGuard> cell_lock(cell, p); cell_lock->Visit(cell_lock, message, *GetMap()); } +void WorldObject::MonsterYellToZone(int32 textId, uint32 language, uint64 TargetGuid) +{ + MaNGOS::MonsterChatBuilder say_build(*this, CHAT_MSG_MONSTER_YELL, textId,language,TargetGuid); + MaNGOS::LocalizedPacketDo<MaNGOS::MonsterChatBuilder> say_do(say_build); + + uint32 zoneid = GetZoneId(); + + Map::PlayerList const& pList = GetMap()->GetPlayers(); + for(Map::PlayerList::const_iterator itr = pList.begin(); itr != pList.end(); ++itr) + if(itr->getSource()->GetZoneId()==zoneid) + say_do(itr->getSource()); +} + void WorldObject::MonsterTextEmote(int32 textId, uint64 TargetGuid, bool IsBossEmote) { CellPair p = Trinity::ComputeCellPair(GetPositionX(), GetPositionY()); @@ -1454,9 +1551,10 @@ void WorldObject::MonsterTextEmote(int32 textId, uint64 TargetGuid, bool IsBossE cell.data.Part.reserved = ALL_DISTRICT; cell.SetNoCreate(); - Trinity::MessageChatLocaleCacheDo say_do(*this, IsBossEmote ? CHAT_MSG_RAID_BOSS_EMOTE : CHAT_MSG_MONSTER_EMOTE, textId,LANG_UNIVERSAL,TargetGuid,sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE)); - Trinity::PlayerWorker<Trinity::MessageChatLocaleCacheDo> say_worker(say_do); - TypeContainerVisitor<Trinity::PlayerWorker<Trinity::MessageChatLocaleCacheDo>, WorldTypeMapContainer > message(say_worker); + MaNGOS::MonsterChatBuilder say_build(*this, IsBossEmote ? CHAT_MSG_RAID_BOSS_EMOTE : CHAT_MSG_MONSTER_EMOTE, textId,LANG_UNIVERSAL,TargetGuid); + MaNGOS::LocalizedPacketDo<MaNGOS::MonsterChatBuilder> say_do(say_build); + MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::MonsterChatBuilder> > say_worker(this,sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE),say_do); + TypeContainerVisitor<MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::MonsterChatBuilder> >, WorldTypeMapContainer > message(say_worker); CellLock<GridReadGuard> cell_lock(cell, p); cell_lock->Visit(cell_lock, message, *GetMap()); } @@ -1471,7 +1569,7 @@ void WorldObject::MonsterWhisper(int32 textId, uint64 receiver, bool IsBossWhisp char const* text = objmgr.GetTrinityString(textId,loc_idx); WorldPacket data(SMSG_MESSAGECHAT, 200); - BuildMonsterChat(&data,IsBossWhisper ? CHAT_MSG_RAID_BOSS_WHISPER : CHAT_MSG_MONSTER_WHISPER,text,LANG_UNIVERSAL,GetName(),receiver); + BuildMonsterChat(&data,IsBossWhisper ? CHAT_MSG_RAID_BOSS_WHISPER : CHAT_MSG_MONSTER_WHISPER,text,LANG_UNIVERSAL,GetNameForLocaleIdx(loc_idx),receiver); player->GetSession()->SendPacket(&data); } @@ -1508,13 +1606,23 @@ void WorldObject::BuildHeartBeatMsg(WorldPacket *data) const data->Initialize(MSG_MOVE_HEARTBEAT, 32); data->append(GetPackGUID()); *data << uint32(((Unit*)this)->GetUnitMovementFlags()); // movement flags - *data << uint8(0); // 2.3.0 + *data << uint16(0); // 2.3.0 *data << getMSTime(); // time *data << m_positionX; *data << m_positionY; *data << m_positionZ; *data << m_orientation; - *data << uint32(0); + if(GetTypeId() == TYPEID_PLAYER && ((Unit*)this)->hasUnitState(UNIT_STAT_ONVEHICLE)) + { + *data << uint64(((Unit*)this)->GetCharmGUID()); + *data << float(((Player*)this)->GetTransOffsetX()); + *data << float(((Player*)this)->GetTransOffsetY()); + *data << float(((Player*)this)->GetTransOffsetZ()); + *data << float(((Player*)this)->GetTransOffsetO()); + *data << uint32(((Player*)this)->GetTransTime()); + *data << uint8(((Player*)this)->GetTransSeat()); + } + *data << uint32(0); //fall time } void WorldObject::BuildTeleportAckMsg(WorldPacket *data, float x, float y, float z, float ang) const @@ -1527,12 +1635,22 @@ void WorldObject::BuildTeleportAckMsg(WorldPacket *data, float x, float y, float data->append(GetPackGUID()); *data << uint32(0); // this value increments every time *data << uint32(((Unit*)this)->GetUnitMovementFlags()); // movement flags - *data << uint8(0); // 2.3.0 + *data << uint16(0); // 2.3.0 *data << getMSTime(); // time *data << x; *data << y; *data << z; *data << ang; + if(GetTypeId() == TYPEID_PLAYER && ((Unit*)this)->hasUnitState(UNIT_STAT_ONVEHICLE)) + { + *data << uint64(((Unit*)this)->GetCharmGUID()); + *data << float(((Player*)this)->GetTransOffsetX()); + *data << float(((Player*)this)->GetTransOffsetY()); + *data << float(((Player*)this)->GetTransOffsetZ()); + *data << float(((Player*)this)->GetTransOffsetO()); + *data << uint32(((Player*)this)->GetTransTime()); + *data << uint8(((Player*)this)->GetTransSeat()); + } *data << uint32(0); } @@ -1582,60 +1700,126 @@ void WorldObject::AddObjectToRemoveList() map->AddObjectToRemoveList(this); } -Creature* WorldObject::SummonCreature(uint32 id, float x, float y, float z, float ang,TempSummonType spwtype,uint32 despwtime) +TempSummon *Map::SummonCreature(uint32 entry, float x, float y, float z, float angle, SummonPropertiesEntry const *properties, uint32 duration, Unit *summoner) { - TemporarySummon* pCreature = new TemporarySummon(GetGUID()); + uint32 mask = SUMMON_MASK_SUMMON; + if(properties) + { + if(properties->Category == SUMMON_CATEGORY_PET + || properties->Type == SUMMON_TYPE_GUARDIAN + || properties->Type == SUMMON_TYPE_MINION) + mask = SUMMON_MASK_GUARDIAN; + else if(properties->Type == SUMMON_TYPE_TOTEM) + mask = SUMMON_MASK_TOTEM; + else if(properties->Category == SUMMON_CATEGORY_VEHICLE + || properties->Type == SUMMON_TYPE_VEHICLE) + mask = SUMMON_MASK_VEHICLE; + } - uint32 team = 0; - if (GetTypeId()==TYPEID_PLAYER) - team = ((Player*)this)->GetTeam(); + uint32 phase = PHASEMASK_NORMAL, team = 0; + if(summoner) + { + phase = summoner->GetPhaseMask(); + if(summoner->GetTypeId() == TYPEID_PLAYER) + team = ((Player*)summoner)->GetTeam(); + } + + TempSummon *summon = NULL; + switch(mask) + { + case SUMMON_MASK_SUMMON: summon = new TempSummon (properties, summoner); break; + case SUMMON_MASK_GUARDIAN: summon = new Guardian (properties, summoner); break; + case SUMMON_MASK_TOTEM: summon = new Totem (properties, summoner); break; + default: return NULL; + } + if(!summon->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), this, phase, entry, team)) + { + delete summon; + return NULL; + } - if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), GetMap(), id, team)) + summon->Relocate(x, y, z, angle); + if(!summon->IsPositionValid()) { - delete pCreature; + sLog.outError("Creature (guidlow %d, entry %d) not summoned. Suggested coordinates isn't valid (X: %f Y: %f)",summon->GetGUIDLow(),summon->GetEntry(),summon->GetPositionX(),summon->GetPositionY()); + delete summon; return NULL; } + Add((Creature*)summon); + summon->InitSummon(duration); + + return summon; +} + +TempSummon* WorldObject::SummonCreature(uint32 entry, float x, float y, float z, float ang, TempSummonType spwtype, uint32 duration) +{ + Map *map = FindMap(); + if(!map) + return NULL; + if (x == 0.0f && y == 0.0f && z == 0.0f) - GetClosePoint(x, y, z, pCreature->GetObjectSize()); + GetClosePoint(x, y, z, GetObjectSize()); + + TempSummon *pCreature = map->SummonCreature(entry, x, y, z, ang, NULL, duration, GetTypeId() == TYPEID_UNIT ? (Unit*)this : NULL); + if(!pCreature) + return NULL; + + pCreature->SetHomePosition(x, y, z, ang); + pCreature->SetTempSummonType(spwtype); - pCreature->Relocate(x, y, z, ang); + return pCreature; +} - if(!pCreature->IsPositionValid()) +Vehicle* WorldObject::SummonVehicle(uint32 entry, float x, float y, float z, float ang) +{ + CreatureInfo const *ci = objmgr.GetCreatureTemplate(entry); + if(!ci) + return NULL; + + uint32 id = ci->VehicleId; //temp store id here + if(!id) id = 156; + VehicleEntry const *ve = sVehicleStore.LookupEntry(id); + if(!ve) + return NULL; + + Vehicle *v = new Vehicle; + Map *map = GetMap(); + uint32 team = 0; + if (GetTypeId()==TYPEID_PLAYER) + team = ((Player*)this)->GetTeam(); + if(!v->Create(objmgr.GenerateLowGuid(HIGHGUID_VEHICLE), map, GetPhaseMask(), entry, id, team)) { - sLog.outError("ERROR: Creature (guidlow %d, entry %d) not summoned. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY()); - delete pCreature; + delete v; return NULL; } - pCreature->SetHomePosition(x, y, z, ang); - pCreature->Summon(spwtype, despwtime); - - if(GetTypeId()==TYPEID_UNIT && ((Creature*)this)->IsAIEnabled) - ((Creature*)this)->AI()->JustSummoned(pCreature); + v->Relocate(x, y, z, ang); - if(pCreature->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_TRIGGER && pCreature->m_spells[0]) + if(!v->IsPositionValid()) { - if(GetTypeId() == TYPEID_UNIT || GetTypeId() == TYPEID_PLAYER) - pCreature->setFaction(((Unit*)this)->getFaction()); - pCreature->CastSpell(pCreature, pCreature->m_spells[0], false, 0, 0, GetGUID()); + sLog.outError("ERROR: Vehicle (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)", + v->GetGUIDLow(), v->GetEntry(), v->GetPositionX(), v->GetPositionY()); + delete v; + return NULL; } - //return the creature therewith the summoner has access to it - return pCreature; + map->Add((Creature*)v); + + return v; } Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetType petType, uint32 duration) { - Pet* pet = new Pet(petType); + Pet* pet = new Pet(this, petType); if(petType == SUMMON_PET && pet->LoadPetFromDB(this, entry)) { // Remove Demonic Sacrifice auras (known pet) - Unit::AuraList const& auraClassScripts = GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); - for(Unit::AuraList::const_iterator itr = auraClassScripts.begin();itr!=auraClassScripts.end();) + Unit::AuraEffectList const& auraClassScripts = GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); + for(Unit::AuraEffectList::const_iterator itr = auraClassScripts.begin();itr!=auraClassScripts.end();) { - if((*itr)->GetModifier()->m_miscvalue==2228) + if((*itr)->GetMiscValue()==2228) { RemoveAurasDueToSpell((*itr)->GetId()); itr = auraClassScripts.begin(); @@ -1659,7 +1843,7 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy Map *map = GetMap(); uint32 pet_number = objmgr.GeneratePetNumber(); - if(!pet->Create(objmgr.GenerateLowGuid(HIGHGUID_PET), map, entry, pet_number)) + if(!pet->Create(objmgr.GenerateLowGuid(HIGHGUID_PET), map, GetPhaseMask(), entry, pet_number)) { sLog.outError("no such creature entry %u", entry); delete pet; @@ -1675,15 +1859,12 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy return NULL; } - pet->SetOwnerGUID(GetGUID()); pet->SetCreatorGUID(GetGUID()); pet->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, getFaction()); // this enables pet details window (Shift+P) pet->GetCharmInfo()->SetPetNumber(pet_number, false); - pet->AIM_Initialize(); - map->Add((Creature*)pet); pet->setPowerType(POWER_MANA); @@ -1691,12 +1872,12 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy pet->SetUInt32Value(UNIT_FIELD_BYTES_1,0); pet->InitStatsForLevel(getLevel()); + SetGuardian(pet, true); + switch(petType) { - case GUARDIAN_PET: case POSSESSED_PET: pet->SetUInt32Value(UNIT_FIELD_FLAGS,0); - AddGuardian(pet); break; case SUMMON_PET: pet->SetUInt32Value(UNIT_FIELD_BYTES_0, 2048); @@ -1706,7 +1887,6 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy pet->SetPower(POWER_MANA, pet->GetMaxPower(POWER_MANA)); pet->InitPetCreateSpells(); pet->SavePetToDB(PET_SAVE_AS_CURRENT); - SetPet(pet); PetSpellInitialize(); break; } @@ -1714,10 +1894,10 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy if(petType == SUMMON_PET) { // Remove Demonic Sacrifice auras (known pet) - Unit::AuraList const& auraClassScripts = GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); - for(Unit::AuraList::const_iterator itr = auraClassScripts.begin();itr!=auraClassScripts.end();) + Unit::AuraEffectList const& auraClassScripts = GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); + for(Unit::AuraEffectList::const_iterator itr = auraClassScripts.begin();itr!=auraClassScripts.end();) { - if((*itr)->GetModifier()->m_miscvalue==2228) + if((*itr)->GetMiscValue()==2228) { RemoveAurasDueToSpell((*itr)->GetId()); itr = auraClassScripts.begin(); @@ -1746,7 +1926,7 @@ GameObject* WorldObject::SummonGameObject(uint32 entry, float x, float y, float } Map *map = GetMap(); GameObject *go = new GameObject(); - if(!go->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT),entry,map,x,y,z,ang,rotation0,rotation1,rotation2,rotation3,100,1)) + if(!go->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), entry, map, GetPhaseMask(), x,y,z,ang,rotation0,rotation1,rotation2,rotation3,100,1)) { delete go; return NULL; @@ -1808,5 +1988,31 @@ void WorldObject::GetGroundPoint(float &x, float &y, float &z, float dist, float UpdateGroundPositionZ(x, y, z); } +void WorldObject::SetPhaseMask(uint32 newPhaseMask, bool update) +{ + m_phaseMask = newPhaseMask; + if(update && IsInWorld()) + ObjectAccessor::UpdateObjectVisibility(this); +} + +void WorldObject::PlayDistanceSound( uint32 sound_id, Player* target /*= NULL*/ ) +{ + WorldPacket data(SMSG_PLAY_OBJECT_SOUND,4+8); + data << uint32(sound_id); + data << GetGUID(); + if (target) + target->SendDirectMessage( &data ); + else + SendMessageToSet( &data, true ); +} +void WorldObject::PlayDirectSound( uint32 sound_id, Player* target /*= NULL*/ ) +{ + WorldPacket data(SMSG_PLAY_SOUND, 4); + data << uint32(sound_id); + if (target) + target->SendDirectMessage( &data ); + else + SendMessageToSet( &data, true ); +} diff --git a/src/game/Object.h b/src/game/Object.h index ecba2a77f65..d46abd6d263 100644 --- a/src/game/Object.h +++ b/src/game/Object.h @@ -28,7 +28,6 @@ #include "GameSystem/GridReference.h" #include "ObjectDefines.h" #include "GridDefines.h" -#include "CreatureAI.h" #include "Map.h" #include <set> @@ -50,13 +49,14 @@ enum TypeMask TYPEMASK_OBJECT = 0x0001, TYPEMASK_ITEM = 0x0002, TYPEMASK_CONTAINER = 0x0006, // TYPEMASK_ITEM | 0x0004 - TYPEMASK_UNIT = 0x0008, + TYPEMASK_UNIT = 0x0008, //creature or player TYPEMASK_PLAYER = 0x0010, TYPEMASK_GAMEOBJECT = 0x0020, TYPEMASK_DYNAMICOBJECT = 0x0040, TYPEMASK_CORPSE = 0x0080, TYPEMASK_AIGROUP = 0x0100, - TYPEMASK_AREATRIGGER = 0x0200 + TYPEMASK_AREATRIGGER = 0x0200, + TYPEMASK_SEER = TYPEMASK_UNIT | TYPEMASK_DYNAMICOBJECT }; enum TypeID @@ -72,6 +72,7 @@ enum TypeID TYPEID_AIGROUP = 8, TYPEID_AREATRIGGER = 9 }; +#define MAX_TYPEID 10 uint32 GuidHigh2TypeId(uint32 guid_hi); @@ -87,6 +88,12 @@ enum TempSummonType TEMPSUMMON_MANUAL_DESPAWN = 8 // despawns when UnSummon() is called }; +enum PhaseMasks +{ + PHASEMASK_NORMAL = 0x00000001, + PHASEMASK_ANYWHERE = 0xFFFFFFFF +}; + class WorldPacket; class UpdateData; class ByteBuffer; @@ -96,6 +103,9 @@ class Player; class UpdateMask; class InstanceData; class GameObject; +class TempSummon; +class Vehicle; +class CreatureAI; typedef UNORDERED_MAP<Player*, UpdateData> UpdateDataMapType; @@ -117,7 +127,7 @@ class TRINITY_DLL_SPEC Object public: virtual ~Object ( ); - const bool& IsInWorld() const { return m_inWorld; } + const bool IsInWorld() const { return m_inWorld; } virtual void AddToWorld() { if(m_inWorld) @@ -210,6 +220,9 @@ class TRINITY_DLL_SPEC Object void SetStatFloatValue( uint16 index, float value); void SetStatInt32Value( uint16 index, int32 value); + bool AddUInt64Value( uint16 index, const uint64 &value ); + bool RemoveUInt64Value( uint16 index, const uint64 &value ); + void ApplyModUInt32Value(uint16 index, int32 val, bool apply); void ApplyModInt32Value(uint16 index, int32 val, bool apply); void ApplyModUInt64Value(uint16 index, int32 val, bool apply); @@ -357,7 +370,7 @@ class TRINITY_DLL_SPEC WorldObject : public Object virtual void Update ( uint32 /*time_diff*/ ) { } - void _Create( uint32 guidlow, HighGuid guidhigh, uint32 mapid ); + void _Create( uint32 guidlow, HighGuid guidhigh, uint32 mapid, uint32 phaseMask); void Relocate(float x, float y, float z, float orientation) { @@ -421,8 +434,14 @@ class TRINITY_DLL_SPEC WorldObject : public Object void SetMapId(uint32 newMap) { m_mapId = newMap; } uint32 GetMapId() const { return m_mapId; } + virtual void SetPhaseMask(uint32 newPhaseMask, bool update); + uint32 GetPhaseMask() const { return m_phaseMask; } + bool InSamePhase(WorldObject const* obj) const { return InSamePhase(obj->GetPhaseMask()); } + bool InSamePhase(uint32 phasemask) const { return (GetPhaseMask() & phasemask); } + uint32 GetZoneId() const; uint32 GetAreaId() const; + void GetZoneAndAreaId(uint32& zoneid, uint32& areaid) const; InstanceData* GetInstanceData(); @@ -436,22 +455,26 @@ class TRINITY_DLL_SPEC WorldObject : public Object float GetDistanceSq(const float &x, const float &y, const float &z) const; float GetDistance2d(const WorldObject* obj) const; float GetDistance2d(const float x, const float y) const; + float GetExactDistance2d(const float x, const float y) const; float GetDistanceZ(const WorldObject* obj) const; - bool IsInMap(const WorldObject* obj) const { return GetMapId()==obj->GetMapId() && GetInstanceId()==obj->GetInstanceId(); } + bool IsInMap(const WorldObject* obj) const + { + return IsInWorld() && obj->IsInWorld() && GetMapId()==obj->GetMapId() && + GetInstanceId()==obj->GetInstanceId() && InSamePhase(obj); + } bool IsWithinDistInMap(const WorldObject* obj, const float dist2compare, const bool is3D = true) const; bool IsWithinLOS(const float x, const float y, const float z ) const; bool IsWithinLOSInMap(const WorldObject* obj) const; float GetAngle( const WorldObject* obj ) const; float GetAngle( const float x, const float y ) const; + void GetSinCos(const float x, const float y, float &vsin, float &vcos); bool HasInArc( const float arcangle, const WorldObject* obj ) const; virtual void SendMessageToSet(WorldPacket *data, bool self, bool to_possessor = true); virtual void SendMessageToSetInRange(WorldPacket *data, float dist, bool self, bool to_possessor = true); void BuildHeartBeatMsg( WorldPacket *data ) const; void BuildTeleportAckMsg( WorldPacket *data, float x, float y, float z, float ang) const; - bool IsBeingTeleported() { return mSemaphoreTeleport; } - void SetSemaphoreTeleport(bool semphsetting) { mSemaphoreTeleport = semphsetting; } void MonsterSay(const char* text, uint32 language, uint64 TargetGuid); void MonsterYell(const char* text, uint32 language, uint64 TargetGuid); @@ -461,8 +484,12 @@ class TRINITY_DLL_SPEC WorldObject : public Object void MonsterYell(int32 textId, uint32 language, uint64 TargetGuid); void MonsterTextEmote(int32 textId, uint64 TargetGuid, bool IsBossEmote = false); void MonsterWhisper(int32 textId, uint64 receiver, bool IsBossWhisper = false); + void MonsterYellToZone(int32 textId, uint32 language, uint64 TargetGuid); void BuildMonsterChat(WorldPacket *data, uint8 msgtype, char const* text, uint32 language, char const* name, uint64 TargetGuid) const; + void PlayDistanceSound(uint32 sound_id, Player* target = NULL); + void PlayDirectSound(uint32 sound_id, Player* target = NULL); + void SendObjectDeSpawnAnim(uint64 guid); virtual void SaveRespawnTime() {} @@ -484,9 +511,10 @@ class TRINITY_DLL_SPEC WorldObject : public Object Map * GetMap() const; Map * FindMap() const; Map const* GetBaseMap() const; - Creature* SummonCreature(uint32 id, float x, float y, float z, float ang,TempSummonType spwtype,uint32 despwtime); + TempSummon* SummonCreature(uint32 id, float x, float y, float z, float ang,TempSummonType spwtype,uint32 despwtime); + Vehicle* SummonVehicle(uint32 entry, float x, float y, float z, float ang); GameObject* SummonGameObject(uint32 entry, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime); - Creature* SummonTrigger(float x, float y, float z, float ang, uint32 dur, CreatureAI* (*GetAI)(Creature*) = NULL); + Creature* SummonTrigger(float x, float y, float z, float ang, uint32 dur, CreatureAI* (*GetAI)(Creature*) = NULL); bool isActiveObject() const { return m_isActive; } void setActive(bool isActiveObject); void SetWorldObject(bool apply); @@ -501,16 +529,14 @@ class TRINITY_DLL_SPEC WorldObject : public Object bool m_isActive; private: - uint32 m_mapId; + uint32 m_mapId; // object at map with map_id + uint32 m_InstanceId; // in map copy with instance id + uint32 m_phaseMask; // in area phase state float m_positionX; float m_positionY; float m_positionZ; float m_orientation; - - bool mSemaphoreTeleport; - - uint32 m_InstanceId; }; #endif diff --git a/src/game/ObjectAccessor.cpp b/src/game/ObjectAccessor.cpp index 5ad5f036813..7daf7e5395e 100644 --- a/src/game/ObjectAccessor.cpp +++ b/src/game/ObjectAccessor.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,8 +25,7 @@ #include "Creature.h" #include "GameObject.h" #include "DynamicObject.h" -#include "Corpse.h" -#include "WorldSession.h" +#include "Vehicle.h" #include "WorldPacket.h" #include "Item.h" #include "Corpse.h" @@ -38,6 +37,7 @@ #include "Opcodes.h" #include "ObjectDefines.h" #include "MapInstanced.h" +#include "World.h" #include <cmath> @@ -45,38 +45,6 @@ INSTANTIATE_SINGLETON_2(ObjectAccessor, CLASS_LOCK); INSTANTIATE_CLASS_MUTEX(ObjectAccessor, ZThread::FastMutex); -namespace Trinity -{ - struct TRINITY_DLL_DECL BuildUpdateForPlayer - { - Player &i_player; - UpdateDataMapType &i_updatePlayers; - - BuildUpdateForPlayer(Player &player, UpdateDataMapType &data_map) : i_player(player), i_updatePlayers(data_map) {} - - void Visit(PlayerMapType &m) - { - for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter) - { - if( iter->getSource() == &i_player ) - continue; - - UpdateDataMapType::iterator iter2 = i_updatePlayers.find(iter->getSource()); - if( iter2 == i_updatePlayers.end() ) - { - std::pair<UpdateDataMapType::iterator, bool> p = i_updatePlayers.insert( ObjectAccessor::UpdateDataValueType(iter->getSource(), UpdateData()) ); - assert(p.second); - iter2 = p.first; - } - - i_player.BuildValuesUpdateBlockForPlayer(&iter2->second, iter2->first); - } - } - - template<class SKIP> void Visit(GridRefManager<SKIP> &) {} - }; -} - ObjectAccessor::ObjectAccessor() {} ObjectAccessor::~ObjectAccessor() {} @@ -113,13 +81,11 @@ ObjectAccessor::GetNPCIfCanInteractWith(Player const &player, uint64 guid, uint3 return NULL; // not unfriendly - FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(unit->getFaction()); - if(factionTemplate) - { - FactionEntry const* faction = sFactionStore.LookupEntry(factionTemplate->faction); - if( faction->reputationListID >= 0 && player.GetReputationRank(faction) <= REP_UNFRIENDLY) - return NULL; - } + if(FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(unit->getFaction())) + if(factionTemplate->faction) + if(FactionEntry const* faction = sFactionStore.LookupEntry(factionTemplate->faction)) + if(faction->reputationListID >= 0 && player.GetReputationMgr().GetRank(faction) <= REP_UNFRIENDLY) + return NULL; // not too far if(!unit->IsWithinDistInMap(&player,INTERACTION_DISTANCE)) @@ -129,11 +95,14 @@ ObjectAccessor::GetNPCIfCanInteractWith(Player const &player, uint64 guid, uint3 } Creature* -ObjectAccessor::GetCreatureOrPet(WorldObject const &u, uint64 guid) +ObjectAccessor::GetCreatureOrPetOrVehicle(WorldObject const &u, uint64 guid) { if(Creature *unit = GetPet(guid)) return unit; + if(Creature *unit = GetVehicle(guid)) + return unit; + return GetCreature(u, guid); } @@ -162,18 +131,23 @@ ObjectAccessor::GetUnit(WorldObject const &u, uint64 guid) if(IS_PLAYER_GUID(guid)) return FindPlayer(guid); - return GetCreatureOrPet(u, guid); + return GetCreatureOrPetOrVehicle(u, guid); } 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()) + return 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; @@ -185,7 +159,7 @@ Object* ObjectAccessor::GetObjectByTypeMask(Player const &p, uint64 guid, uint32 if(typemask & TYPEMASK_UNIT) { - obj = GetCreatureOrPet(p,guid); + obj = GetCreatureOrPetOrVehicle(p,guid); if(obj) return obj; } @@ -201,9 +175,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; } @@ -214,15 +188,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()) + return 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()) + return NULL; + if(ret->GetInstanceId() != u.GetInstanceId()) + return NULL; return ret; } @@ -273,48 +257,15 @@ ObjectAccessor::UpdateObject(Object* obj, Player* exceptPlayer) } void -ObjectAccessor::AddUpdateObject(Object *obj) -{ - Guard guard(i_updateGuard); - i_objects.insert(obj); -} - -void -ObjectAccessor::RemoveUpdateObject(Object *obj) -{ - Guard guard(i_updateGuard); - std::set<Object *>::iterator iter = i_objects.find(obj); - if( iter != i_objects.end() ) - i_objects.erase( iter ); -} - -void ObjectAccessor::_buildUpdateObject(Object *obj, UpdateDataMapType &update_players) { - bool build_for_all = true; - Player *pl = NULL; - if( obj->isType(TYPEMASK_ITEM) ) - { - Item *item = static_cast<Item *>(obj); - pl = item->GetOwner(); - build_for_all = false; - } - - if( pl != NULL ) - _buildPacket(pl, obj, update_players); - - // Capt: okey for all those fools who think its a real fix - // THIS IS A TEMP FIX - if( build_for_all ) + if(obj->isType(TYPEMASK_ITEM)) { - WorldObject * temp = dynamic_cast<WorldObject*>(obj); - - //assert(dynamic_cast<WorldObject*>(obj)!=NULL); - if (temp) - _buildChangeObjectForPlayer(temp, update_players); - else - sLog.outDebug("ObjectAccessor: Ln 405 Temp bug fix"); + if(Player *owner = ((Item*)obj)->GetOwner()) + _buildPacket(owner, obj, update_players); } + else + _buildChangeObjectForPlayer((WorldObject*)obj, update_players); } void @@ -351,6 +302,12 @@ ObjectAccessor::GetPet(uint64 guid) return GetObjectInWorld(guid, (Pet*)NULL); } +Vehicle* +ObjectAccessor::GetVehicle(uint64 guid) +{ + return GetObjectInWorld(guid, (Vehicle*)NULL); +} + Corpse* ObjectAccessor::GetCorpseForPlayerGUID(uint64 guid) { @@ -423,18 +380,18 @@ ObjectAccessor::AddCorpsesToGrid(GridPair const& gridpair,GridType& grid,Map* ma } Corpse* -ObjectAccessor::ConvertCorpseForPlayer(uint64 player_guid) +ObjectAccessor::ConvertCorpseForPlayer(uint64 player_guid, bool insignia) { Corpse *corpse = GetCorpseForPlayerGUID(player_guid); if(!corpse) { //in fact this function is called from several places //even when player doesn't have a corpse, not an error - //sLog.outError("ERROR: Try remove corpse that not in map for GUID %ul", player_guid); + //sLog.outError("Try remove corpse that not in map for GUID %ul", player_guid); return NULL; } - DEBUG_LOG("Deleting Corpse and spawning bones.\n"); + DEBUG_LOG("Deleting Corpse and spawning bones."); // remove corpse from player_guid -> corpse map RemoveCorpse(corpse); @@ -449,7 +406,10 @@ ObjectAccessor::ConvertCorpseForPlayer(uint64 player_guid) Corpse *bones = NULL; // create the bones only if the map and the grid is loaded at the corpse's location - if(map && !map->IsRemovalGrid(corpse->GetPositionX(), corpse->GetPositionY())) + // ignore bones creating option in case insignia + if (map && (insignia || + (map->IsBattleGroundOrArena() ? sWorld.getConfig(CONFIG_DEATH_BONES_BG_OR_ARENA) : sWorld.getConfig(CONFIG_DEATH_BONES_WORLD))) && + !map->IsRemovalGrid(corpse->GetPositionX(), corpse->GetPositionY())) { // Create bones, don't change Corpse bones = new Corpse; @@ -465,6 +425,7 @@ ObjectAccessor::ConvertCorpseForPlayer(uint64 player_guid) bones->Relocate(corpse->GetPositionX(), corpse->GetPositionY(), corpse->GetPositionZ(), corpse->GetOrientation()); bones->SetMapId(corpse->GetMapId()); bones->SetInstanceId(corpse->GetInstanceId()); + bones->SetPhaseMask(corpse->GetPhaseMask(),false); bones->SetUInt32Value(CORPSE_FIELD_FLAGS, CORPSE_FLAG_UNK2 | CORPSE_FLAG_BONES); bones->SetUInt64Value(CORPSE_FIELD_OWNER, 0); @@ -494,9 +455,8 @@ ObjectAccessor::Update(uint32 diff) while(!i_objects.empty()) { Object* obj = *i_objects.begin(); + assert(obj && obj->IsInWorld()); i_objects.erase(i_objects.begin()); - if (!obj || !obj->IsInWorld()) - continue; _buildUpdateObject(obj, update_players); obj->ClearUpdateMask(false); } @@ -602,6 +562,7 @@ template <class T> ZThread::FastMutex HashMapHolder<T>::i_lock; template class HashMapHolder<Player>; template class HashMapHolder<Pet>; +template class HashMapHolder<Vehicle>; template class HashMapHolder<GameObject>; template class HashMapHolder<DynamicObject>; template class HashMapHolder<Creature>; @@ -609,6 +570,7 @@ template class HashMapHolder<Corpse>; template Player* ObjectAccessor::GetObjectInWorld<Player>(uint32 mapid, float x, float y, uint64 guid, Player* /*fake*/); template Pet* ObjectAccessor::GetObjectInWorld<Pet>(uint32 mapid, float x, float y, uint64 guid, Pet* /*fake*/); +template Vehicle* ObjectAccessor::GetObjectInWorld<Vehicle>(uint32 mapid, float x, float y, uint64 guid, Vehicle* /*fake*/); template Creature* ObjectAccessor::GetObjectInWorld<Creature>(uint32 mapid, float x, float y, uint64 guid, Creature* /*fake*/); template Corpse* ObjectAccessor::GetObjectInWorld<Corpse>(uint32 mapid, float x, float y, uint64 guid, Corpse* /*fake*/); template GameObject* ObjectAccessor::GetObjectInWorld<GameObject>(uint32 mapid, float x, float y, uint64 guid, GameObject* /*fake*/); diff --git a/src/game/ObjectAccessor.h b/src/game/ObjectAccessor.h index bbfc06b78b6..1f413e2db40 100644 --- a/src/game/ObjectAccessor.h +++ b/src/game/ObjectAccessor.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -41,6 +41,7 @@ class Corpse; class Unit; class GameObject; class DynamicObject; +class Vehicle; class WorldObject; class Map; @@ -58,9 +59,7 @@ class HashMapHolder static void Remove(T* o) { Guard guard(i_lock); - typename MapType::iterator itr = m_objectMap.find(o->GetGUID()); - if (itr != m_objectMap.end()) - m_objectMap.erase(itr); + m_objectMap.erase(o->GetGUID()); } static T* Find(uint64 guid) @@ -139,17 +138,18 @@ 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* GetCreatureOrPet(WorldObject const &, uint64); + static Creature* GetCreatureOrPetOrVehicle(WorldObject const &, uint64); static Unit* GetUnit(WorldObject const &, uint64); 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); static Player* FindPlayer(uint64); Player* FindPlayerByName(const char *name) ; @@ -159,6 +159,16 @@ class TRINITY_DLL_DECL ObjectAccessor : public Trinity::Singleton<ObjectAccessor return HashMapHolder<Player>::GetContainer(); } + HashMapHolder<Creature>::MapType& GetCreatures() + { + return HashMapHolder<Creature>::GetContainer(); + } + + HashMapHolder<GameObject>::MapType& GetGameObjects() + { + return HashMapHolder<GameObject>::GetContainer(); + } + template<class T> void AddObject(T *object) { HashMapHolder<T>::Insert(object); @@ -174,16 +184,22 @@ class TRINITY_DLL_DECL ObjectAccessor : public Trinity::Singleton<ObjectAccessor HashMapHolder<Player>::Remove(pl); Guard guard(i_updateGuard); - - std::set<Object *>::iterator iter2 = std::find(i_objects.begin(), i_objects.end(), (Object *)pl); - if( iter2 != i_objects.end() ) - i_objects.erase(iter2); + i_objects.erase((Object *)pl); } void SaveAllPlayers(); - void AddUpdateObject(Object *obj); - void RemoveUpdateObject(Object *obj); + void AddUpdateObject(Object *obj) + { + Guard guard(i_updateGuard); + i_objects.insert(obj); + } + + void RemoveUpdateObject(Object *obj) + { + Guard guard(i_updateGuard); + i_objects.erase( obj ); + } void Update(uint32 diff); void UpdatePlayers(uint32 diff); @@ -192,7 +208,7 @@ class TRINITY_DLL_DECL ObjectAccessor : public Trinity::Singleton<ObjectAccessor void RemoveCorpse(Corpse *corpse); void AddCorpse(Corpse* corpse); void AddCorpsesToGrid(GridPair const& gridpair,GridType& grid,Map* map); - Corpse* ConvertCorpseForPlayer(uint64 player_guid); + Corpse* ConvertCorpseForPlayer(uint64 player_guid, bool insignia = false); static void UpdateObject(Object* obj, Player* exceptPlayer); static void _buildUpdateObject(Object* obj, UpdateDataMapType &); diff --git a/src/game/ObjectDefines.h b/src/game/ObjectDefines.h index 53a0174ba2f..ee58e8a5f34 100644 --- a/src/game/ObjectDefines.h +++ b/src/game/ObjectDefines.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -41,6 +41,7 @@ enum HighGuid HIGHGUID_TRANSPORT = 0xF120, // blizz F120 (for GAMEOBJECT_TYPE_TRANSPORT) HIGHGUID_UNIT = 0xF130, // blizz F130 HIGHGUID_PET = 0xF140, // blizz F140 + HIGHGUID_VEHICLE = 0xF150, // blizz F550 HIGHGUID_DYNAMICOBJECT = 0xF100, // blizz F100 HIGHGUID_CORPSE = 0xF101, // blizz F100 HIGHGUID_MO_TRANSPORT = 0x1FC0, // blizz 1FC0 (for GAMEOBJECT_TYPE_MO_TRANSPORT) @@ -50,6 +51,7 @@ enum HighGuid #define IS_CREATURE_GUID(Guid) ( GUID_HIPART(Guid) == HIGHGUID_UNIT ) #define IS_PET_GUID(Guid) ( GUID_HIPART(Guid) == HIGHGUID_PET ) +#define IS_VEHICLE_GUID(Guid) ( GUID_HIPART(Guid) == HIGHGUID_VEHICLE ) #define IS_CREATURE_OR_PET_GUID(Guid)( IS_CREATURE_GUID(Guid) || IS_PET_GUID(Guid) ) #define IS_PLAYER_GUID(Guid) ( GUID_HIPART(Guid) == HIGHGUID_PLAYER && Guid!=0 ) #define IS_UNIT_GUID(Guid) ( IS_CREATURE_OR_PET_GUID(Guid) || IS_PLAYER_GUID(Guid) ) @@ -87,6 +89,7 @@ inline bool IsGuidHaveEnPart(uint64 const& guid) case HIGHGUID_TRANSPORT: case HIGHGUID_UNIT: case HIGHGUID_PET: + case HIGHGUID_VEHICLE: case HIGHGUID_MO_TRANSPORT: default: return true; @@ -106,6 +109,7 @@ inline char const* GetLogNameForGuid(uint64 guid) case HIGHGUID_TRANSPORT: return "transport"; case HIGHGUID_UNIT: return "creature"; case HIGHGUID_PET: return "pet"; + case HIGHGUID_VEHICLE: return "vehicle"; case HIGHGUID_DYNAMICOBJECT:return "dynobject"; case HIGHGUID_CORPSE: return "corpse"; case HIGHGUID_MO_TRANSPORT: return "mo_transport"; diff --git a/src/game/ObjectGridLoader.cpp b/src/game/ObjectGridLoader.cpp index b819182a50d..368edce0a53 100644 --- a/src/game/ObjectGridLoader.cpp +++ b/src/game/ObjectGridLoader.cpp @@ -22,11 +22,13 @@ #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "Creature.h" +#include "Vehicle.h" #include "GameObject.h" #include "DynamicObject.h" #include "Corpse.h" #include "World.h" #include "CellImpl.h" +#include "CreatureAI.h" class TRINITY_DLL_DECL ObjectGridRespawnMover { @@ -57,7 +59,7 @@ ObjectGridRespawnMover::Visit(CreatureMapType &m) Creature * c = iter->getSource(); ++iter; - assert(!c->isPet() && "ObjectGridRespawnMover don't must be called for pets"); + assert(!c->isWorldCreature() && "ObjectGridRespawnMover don't must be called for pets"); Cell const& cur_cell = c->GetCurrentCell(); @@ -133,6 +135,36 @@ void LoadHelper(CellGuidSet const& guid_set, CellPair &cell, GridRefManager<T> & } } +void LoadHelper(CellGuidSet const& guid_set, CellPair &cell, CreatureMapType &m, uint32 &count, Map* map) +{ + for(CellGuidSet::const_iterator i_guid = guid_set.begin(); i_guid != guid_set.end(); ++i_guid) + { + Creature* obj = new Creature; + uint32 guid = *i_guid; + //sLog.outString("DEBUG: LoadHelper from table: %s for (guid: %u) Loading",table,guid); + if(!obj->LoadFromDB(guid, map)) + { + delete obj; + obj = new Vehicle; + if(!((Vehicle*)obj)->LoadFromDB(guid, map)) + { + delete (Vehicle*)obj; + continue; + } + } + + obj->GetGridRef().link(&m, obj); + + addUnitState(obj,cell); + obj->AddToWorld(); + if(obj->isActiveObject()) + map->AddToActive(obj); + + ++count; + + } +} + void LoadHelper(CellCorpseSet const& cell_corpses, CellPair &cell, CorpseMapType &m, uint32 &count, Map* map) { if(cell_corpses.empty()) diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index 9d4e3020cfc..7096fa03dfa 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,14 +30,13 @@ #include "SpellMgr.h" #include "UpdateMask.h" #include "World.h" -#include "WorldSession.h" #include "Group.h" #include "Guild.h" #include "ArenaTeam.h" #include "Transports.h" #include "ProgressBar.h" #include "Language.h" -#include "GameEvent.h" +#include "GameEventMgr.h" #include "Spell.h" #include "Chat.h" #include "AccountMgr.h" @@ -116,6 +115,7 @@ ObjectMgr::ObjectMgr() m_hiCharGuid = 1; m_hiCreatureGuid = 1; m_hiPetGuid = 1; + m_hiVehicleGuid = 1; m_hiItemGuid = 1; m_hiGoGuid = 1; m_hiDoGuid = 1; @@ -142,24 +142,10 @@ ObjectMgr::ObjectMgr() ObjectMgr::~ObjectMgr() { for( QuestMap::iterator i = mQuestTemplates.begin( ); i != mQuestTemplates.end( ); ++i ) - { - delete i->second; - } - mQuestTemplates.clear( ); - - for( GossipTextMap::iterator i = mGossipText.begin( ); i != mGossipText.end( ); ++i ) - { delete i->second; - } - mGossipText.clear( ); - - mAreaTriggers.clear(); for(PetLevelInfoMap::iterator i = petInfo.begin( ); i != petInfo.end( ); ++i ) - { delete[] i->second; - } - petInfo.clear(); // free only if loaded for (int class_ = 0; class_ < MAX_CLASSES; ++class_) @@ -251,7 +237,7 @@ Group * ObjectMgr::GetGroupByLeader(const uint64 &guid) const return NULL; } -Guild * ObjectMgr::GetGuildById(const uint32 GuildId) const +Guild * ObjectMgr::GetGuildById(uint32 GuildId) const { GuildMap::const_iterator itr = mGuildMap.find(GuildId); if (itr != mGuildMap.end()) @@ -269,7 +255,7 @@ Guild * ObjectMgr::GetGuildByName(const std::string& guildname) const return NULL; } -std::string ObjectMgr::GetGuildNameById(const uint32 GuildId) const +std::string ObjectMgr::GetGuildNameById(uint32 GuildId) const { GuildMap::const_iterator itr = mGuildMap.find(GuildId); if (itr != mGuildMap.end()) @@ -296,7 +282,8 @@ void ObjectMgr::RemoveGuild(uint32 Id) { mGuildMap.erase(Id); } -ArenaTeam* ObjectMgr::GetArenaTeamById(const uint32 arenateamid) const + +ArenaTeam* ObjectMgr::GetArenaTeamById(uint32 arenateamid) const { ArenaTeamMap::const_iterator itr = mArenaTeamMap.find(arenateamid); if (itr != mArenaTeamMap.end()) @@ -350,7 +337,7 @@ void ObjectMgr::LoadCreatureLocales() bar.step(); - sLog.outString(""); + sLog.outString(); sLog.outString(">> Loaded 0 creature locale strings. DB table `locales_creature` is empty."); return; } @@ -398,7 +385,7 @@ void ObjectMgr::LoadCreatureLocales() delete result; sLog.outString(); - sLog.outString( ">> Loaded %u creature locale strings", mCreatureLocaleMap.size() ); + sLog.outString( ">> Loaded %lu creature locale strings", (unsigned long)mCreatureLocaleMap.size() ); } void ObjectMgr::LoadNpcOptionLocales() @@ -418,7 +405,7 @@ void ObjectMgr::LoadNpcOptionLocales() bar.step(); - sLog.outString(""); + sLog.outString(); sLog.outString(">> Loaded 0 npc_option locale strings. DB table `locales_npc_option` is empty."); return; } @@ -466,7 +453,58 @@ void ObjectMgr::LoadNpcOptionLocales() delete result; sLog.outString(); - sLog.outString( ">> Loaded %u npc_option locale strings", mNpcOptionLocaleMap.size() ); + sLog.outString( ">> Loaded %lu npc_option locale strings", (unsigned long)mNpcOptionLocaleMap.size() ); +} + +void ObjectMgr::LoadPointOfInterestLocales() +{ + mPointOfInterestLocaleMap.clear(); // need for reload case + + QueryResult *result = WorldDatabase.Query("SELECT entry,icon_name_loc1,icon_name_loc2,icon_name_loc3,icon_name_loc4,icon_name_loc5,icon_name_loc6,icon_name_loc7,icon_name_loc8 FROM locales_points_of_interest"); + + if(!result) + { + barGoLink bar(1); + + bar.step(); + + sLog.outString(); + sLog.outString(">> Loaded 0 points_of_interest locale strings. DB table `locales_points_of_interest` is empty."); + return; + } + + barGoLink bar(result->GetRowCount()); + + do + { + Field *fields = result->Fetch(); + bar.step(); + + uint32 entry = fields[0].GetUInt32(); + + PointOfInterestLocale& data = mPointOfInterestLocaleMap[entry]; + + for(int i = 1; i < MAX_LOCALE; ++i) + { + std::string str = fields[i].GetCppString(); + if(str.empty()) + continue; + + int idx = GetOrNewIndexForLocale(LocaleConstant(i)); + if(idx >= 0) + { + if(data.IconName.size() <= idx) + data.IconName.resize(idx+1); + + data.IconName[idx] = str; + } + } + } while (result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %lu points_of_interest locale strings", (unsigned long)mPointOfInterestLocaleMap.size() ); } struct SQLCreatureLoader : public SQLStorageLoaderBase<SQLCreatureLoader> @@ -553,6 +591,18 @@ void ObjectMgr::LoadCreatureTemplates() continue; } + if(heroicInfo->AIName && *heroicInfo->AIName) + { + sLog.outErrorDb("Heroic mode creature (Entry: %u) has `AIName`, but in any case will used normal mode creature (Entry: %u) AIName.",cInfo->HeroicEntry,i); + continue; + } + + if(heroicInfo->ScriptID) + { + sLog.outErrorDb("Heroic mode creature (Entry: %u) has `ScriptName`, but in any case will used normal mode creature (Entry: %u) ScriptName.",cInfo->HeroicEntry,i); + continue; + } + hasHeroicEntries.insert(i); heroicEntries.insert(cInfo->HeroicEntry); } @@ -602,6 +652,19 @@ void ObjectMgr::LoadCreatureTemplates() if((cInfo->npcflag & UNIT_NPC_FLAG_TRAINER) && cInfo->trainer_type >= MAX_TRAINER_TYPE) sLog.outErrorDb("Creature (Entry: %u) has wrong trainer type %u",cInfo->Entry,cInfo->trainer_type); + if(cInfo->type && !sCreatureTypeStore.LookupEntry(cInfo->type)) + { + sLog.outErrorDb("Creature (Entry: %u) has invalid creature type (%u) in `type`",cInfo->Entry,cInfo->type); + const_cast<CreatureInfo*>(cInfo)->type = CREATURE_TYPE_HUMANOID; + } + + // must exist or used hidden but used in data horse case + if(cInfo->family && !sCreatureFamilyStore.LookupEntry(cInfo->family) && cInfo->family != CREATURE_FAMILY_HORSE_CUSTOM ) + { + sLog.outErrorDb("Creature (Entry: %u) has invalid creature family (%u) in `family`",cInfo->Entry,cInfo->family); + const_cast<CreatureInfo*>(cInfo)->family = 0; + } + if(cInfo->InhabitType <= 0 || cInfo->InhabitType > INHABIT_ANYWHERE) { sLog.outErrorDb("Creature (Entry: %u) has wrong value (%u) in `InhabitType`, creature will not correctly walk/swim/fly",cInfo->Entry,cInfo->InhabitType); @@ -615,6 +678,15 @@ void ObjectMgr::LoadCreatureTemplates() sLog.outErrorDb("Creature (Entry: %u) has non-existing PetSpellDataId (%u)", cInfo->Entry, cInfo->PetSpellDataId); } + for(int i = 0; i < CREATURE_MAX_SPELLS; ++i) + { + if(cInfo->spells[i] && !sSpellStore.LookupEntry(cInfo->spells[i])) + { + sLog.outErrorDb("Creature (Entry: %u) has non-existing Spell%d (%u), set to 0", cInfo->Entry, i+1,cInfo->spells[i]); + const_cast<CreatureInfo*>(cInfo)->spells[i] = 0; + } + } + if(cInfo->MovementType >= MAX_DB_MOTION_TYPE) { sLog.outErrorDb("Creature (Entry: %u) has wrong movement generator type (%u), ignore and set to IDLE.",cInfo->Entry,cInfo->MovementType); @@ -763,8 +835,47 @@ void ObjectMgr::LoadEquipmentTemplates() { sEquipmentStorage.Load(); + for(uint32 i=0; i< sEquipmentStorage.MaxEntry; ++i) + { + EquipmentInfo const* eqInfo = sEquipmentStorage.LookupEntry<EquipmentInfo>(i); + + if(!eqInfo) + continue; + + for(uint8 j=0; j<3; j++) + { + if(!eqInfo->equipentry[j]) + continue; + + ItemEntry const *dbcitem = sItemStore.LookupEntry(eqInfo->equipentry[j]); + + if(!dbcitem) + { + sLog.outErrorDb("Unknown item (entry=%u) in creature_equip_template.equipentry%u for entry = %u, forced to 0.", eqInfo->equipentry[j], j+1, i); + const_cast<EquipmentInfo*>(eqInfo)->equipentry[j] = 0; + continue; + } + + if(dbcitem->InventoryType != INVTYPE_WEAPON && + dbcitem->InventoryType != INVTYPE_SHIELD && + dbcitem->InventoryType != INVTYPE_RANGED && + dbcitem->InventoryType != INVTYPE_2HWEAPON && + dbcitem->InventoryType != INVTYPE_WEAPONMAINHAND && + dbcitem->InventoryType != INVTYPE_WEAPONOFFHAND && + dbcitem->InventoryType != INVTYPE_HOLDABLE && + dbcitem->InventoryType != INVTYPE_THROWN && + dbcitem->InventoryType != INVTYPE_RANGEDRIGHT) + { + sLog.outErrorDb("Item (entry=%u) in creature_equip_template.equipentry%u for entry = %u is not equipable in a hand, forced to 0.", eqInfo->equipentry[j], j+1, i); + const_cast<EquipmentInfo*>(eqInfo)->equipentry[j] = 0; + } + } + } sLog.outString( ">> Loaded %u equipment template", sEquipmentStorage.RecordCount ); sLog.outString(); + + // This DBC is currently only used for item templates and creature equipments checks. + sItemStore.Clear(); } CreatureModelInfo const* ObjectMgr::GetCreatureModelInfo(uint32 modelid) @@ -836,9 +947,10 @@ void ObjectMgr::LoadCreatures() QueryResult *result = WorldDatabase.Query("SELECT creature.guid, id, map, modelid," // 4 5 6 7 8 9 10 11 "equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, spawndist, currentwaypoint," - // 12 13 14 15 16 17 - "curhealth, curmana, DeathState, MovementType, spawnMask, event " - "FROM creature LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid"); + // 12 13 14 15 16 17 18 19 + "curhealth, curmana, DeathState, MovementType, spawnMask, phaseMask, event, pool_entry " + "FROM creature LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid " + "LEFT OUTER JOIN pool_creature ON creature.guid = pool_creature.guid"); if(!result) { @@ -846,7 +958,7 @@ void ObjectMgr::LoadCreatures() bar.step(); - sLog.outString(""); + sLog.outString(); sLog.outErrorDb(">> Loaded 0 creature. DB table `creature` is empty."); return; } @@ -865,11 +977,19 @@ void ObjectMgr::LoadCreatures() Field *fields = result->Fetch(); bar.step(); - uint32 guid = fields[0].GetUInt32(); + uint32 guid = fields[ 0].GetUInt32(); + uint32 entry = fields[ 1].GetUInt32(); + + CreatureInfo const* cInfo = GetCreatureTemplate(entry); + if(!cInfo) + { + sLog.outErrorDb("Table `creature` has creature (GUID: %u) with non existing creature entry %u, skipped.", guid, entry); + continue; + } CreatureData& data = mCreatureDataMap[guid]; - data.id = fields[ 1].GetUInt32(); + data.id = entry; data.mapid = fields[ 2].GetUInt32(); data.displayid = fields[ 3].GetUInt32(); data.equipmentId = fields[ 4].GetUInt32(); @@ -885,14 +1005,9 @@ void ObjectMgr::LoadCreatures() data.is_dead = fields[14].GetBool(); data.movementType = fields[15].GetUInt8(); data.spawnMask = fields[16].GetUInt8(); - int16 gameEvent = fields[17].GetInt16(); - - CreatureInfo const* cInfo = GetCreatureTemplate(data.id); - if(!cInfo) - { - sLog.outErrorDb("Table `creature` have creature (GUID: %u) with not existed creature entry %u, skipped.",guid,data.id ); - continue; - } + data.phaseMask = fields[17].GetUInt16(); + int16 gameEvent = fields[18].GetInt16(); + int16 PoolId = fields[19].GetInt16(); if(heroicCreatures.find(data.id)!=heroicCreatures.end()) { @@ -915,6 +1030,13 @@ void ObjectMgr::LoadCreatures() data.curhealth = cInfo->minhealth; } + if(cInfo->flags_extra & CREATURE_FLAG_EXTRA_INSTANCE_BIND) + { + MapEntry const* map = sMapStore.LookupEntry(data.mapid); + if(!map || !map->IsDungeon()) + sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `creature_template`.`flags_extra` including CREATURE_FLAG_EXTRA_INSTANCE_BIND but creature are not in instance.",guid,data.id); + } + if(data.curmana < cInfo->minmana) { sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with low current mana (%u), `creature_template`.`minmana`=%u.",guid,data.id,data.curmana, cInfo->minmana ); @@ -943,8 +1065,15 @@ void ObjectMgr::LoadCreatures() } } - if (gameEvent==0) // if not this is to be managed by GameEvent System + if(data.phaseMask==0) + { + sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `phaseMask`=0 (not visible for anyone), set to 1.",guid,data.id ); + data.phaseMask = 1; + } + + if (gameEvent==0 && PoolId==0) // if not this is to be managed by GameEvent System or Pool system AddCreatureToGrid(guid, &data); + ++count; } while (result->NextRow()); @@ -952,7 +1081,7 @@ void ObjectMgr::LoadCreatures() delete result; sLog.outString(); - sLog.outString( ">> Loaded %u creatures", mCreatureDataMap.size() ); + sLog.outString( ">> Loaded %lu creatures", (unsigned long)mCreatureDataMap.size() ); } void ObjectMgr::AddCreatureToGrid(uint32 guid, CreatureData const* data) @@ -993,9 +1122,10 @@ void ObjectMgr::LoadGameobjects() // 0 1 2 3 4 5 6 QueryResult *result = WorldDatabase.Query("SELECT gameobject.guid, id, map, position_x, position_y, position_z, orientation," - // 7 8 9 10 11 12 13 14 15 - "rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, spawnMask, event " - "FROM gameobject LEFT OUTER JOIN game_event_gameobject ON gameobject.guid = game_event_gameobject.guid"); + // 7 8 9 10 11 12 13 14 15 16 17 + "rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, spawnMask, phaseMask, event, pool_entry " + "FROM gameobject LEFT OUTER JOIN game_event_gameobject ON gameobject.guid = game_event_gameobject.guid " + "LEFT OUTER JOIN pool_gameobject ON gameobject.guid = pool_gameobject.guid"); if(!result) { @@ -1015,11 +1145,19 @@ void ObjectMgr::LoadGameobjects() Field *fields = result->Fetch(); bar.step(); - uint32 guid = fields[0].GetUInt32(); + uint32 guid = fields[ 0].GetUInt32(); + uint32 entry = fields[ 1].GetUInt32(); + + GameObjectInfo const* gInfo = GetGameObjectInfo(entry); + if(!gInfo) + { + sLog.outErrorDb("Table `gameobject` has gameobject (GUID: %u) with non existing gameobject entry %u, skipped.", guid, entry); + continue; + } GameObjectData& data = mGameObjectDataMap[guid]; - data.id = fields[ 1].GetUInt32(); + data.id = entry; data.mapid = fields[ 2].GetUInt32(); data.posX = fields[ 3].GetFloat(); data.posY = fields[ 4].GetFloat(); @@ -1034,16 +1172,35 @@ void ObjectMgr::LoadGameobjects() data.go_state = fields[13].GetUInt32(); data.ArtKit = 0; data.spawnMask = fields[14].GetUInt8(); - int16 gameEvent = fields[15].GetInt16(); + data.phaseMask = fields[15].GetUInt16(); + int16 gameEvent = fields[16].GetInt16(); + int16 PoolId = fields[17].GetInt16(); - GameObjectInfo const* gInfo = GetGameObjectInfo(data.id); - if(!gInfo) + if(data.rotation2 < -1.0f || data.rotation2 > 1.0f) + { + sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid rotation2 (%f) value, skip",guid,data.id,data.rotation2 ); + continue; + } + + if(data.rotation3 < -1.0f || data.rotation3 > 1.0f) { - sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u) with not existed gameobject entry %u, skipped.",guid,data.id ); + sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid rotation3 (%f) value, skip",guid,data.id,data.rotation3 ); continue; } - if (gameEvent==0) // if not this is to be managed by GameEvent System + if(!MapManager::IsValidMapCoord(data.mapid,data.posX,data.posY,data.posZ,data.orientation)) + { + sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid coordinates, skip",guid,data.id ); + continue; + } + + if(data.phaseMask==0) + { + sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with `phaseMask`=0 (not visible for anyone), set to 1.",guid,data.id ); + data.phaseMask = 1; + } + + if (gameEvent==0 && PoolId==0) // if not this is to be managed by GameEvent System or Pool system AddGameobjectToGrid(guid, &data); ++count; @@ -1052,7 +1209,7 @@ void ObjectMgr::LoadGameobjects() delete result; sLog.outString(); - sLog.outString( ">> Loaded %u gameobjects", mGameObjectDataMap.size()); + sLog.outString( ">> Loaded %lu gameobjects", (unsigned long)mGameObjectDataMap.size()); } void ObjectMgr::AddGameobjectToGrid(uint32 guid, GameObjectData const* data) @@ -1125,7 +1282,7 @@ void ObjectMgr::LoadCreatureRespawnTimes() delete result; - sLog.outString( ">> Loaded %u creature respawn times", mCreatureRespawnTimes.size() ); + sLog.outString( ">> Loaded %lu creature respawn times", (unsigned long)mCreatureRespawnTimes.size() ); sLog.outString(); } @@ -1167,7 +1324,7 @@ void ObjectMgr::LoadGameobjectRespawnTimes() delete result; - sLog.outString( ">> Loaded %u gameobject respawn times", mGORespawnTimes.size() ); + sLog.outString( ">> Loaded %lu gameobject respawn times", (unsigned long)mGORespawnTimes.size() ); sLog.outString(); } @@ -1270,7 +1427,7 @@ void ObjectMgr::LoadItemLocales() bar.step(); - sLog.outString(""); + sLog.outString(); sLog.outString(">> Loaded 0 Item locale strings. DB table `locales_item` is empty."); return; } @@ -1319,7 +1476,7 @@ void ObjectMgr::LoadItemLocales() delete result; sLog.outString(); - sLog.outString( ">> Loaded %u Item locale strings", mItemLocaleMap.size() ); + sLog.outString( ">> Loaded %lu Item locale strings", (unsigned long)mItemLocaleMap.size() ); } struct SQLItemLoader : public SQLStorageLoaderBase<SQLItemLoader> @@ -1354,6 +1511,32 @@ void ObjectMgr::LoadItemPrototypes() if(dbcitem) { + if(proto->Class != dbcitem->Class) + { + sLog.outErrorDb("Item (Entry: %u) not correct class %u, must be %u (still using DB value).",i,proto->Class,dbcitem->Class); + // It safe let use Class from DB + } + /* disabled: have some strange wrong cases for Subclass values. + for enable also uncomment Subclass field in ItemEntry structure and in Itemfmt[] + if(proto->SubClass != dbcitem->SubClass) + { + sLog.outErrorDb("Item (Entry: %u) not correct (Class: %u, Sub: %u) pair, must be (Class: %u, Sub: %u) (still using DB value).",i,proto->Class,proto->SubClass,dbcitem->Class,dbcitem->SubClass); + // It safe let use Subclass from DB + } + */ + + if(proto->Unk0 != dbcitem->Unk0) + { + sLog.outErrorDb("Item (Entry: %u) not correct %i Unk0, must be %i (still using DB value).",i,proto->Unk0,dbcitem->Unk0); + // It safe let use Unk0 from DB + } + + if(proto->Material != dbcitem->Material) + { + sLog.outErrorDb("Item (Entry: %u) not correct %i material, must be %i (still using DB value).",i,proto->Material,dbcitem->Material); + // It safe let use Material from DB + } + if(proto->InventoryType != dbcitem->InventoryType) { sLog.outErrorDb("Item (Entry: %u) not correct %u inventory type, must be %u (still using DB value).",i,proto->InventoryType,dbcitem->InventoryType); @@ -1379,7 +1562,7 @@ void ObjectMgr::LoadItemPrototypes() if(proto->Class >= MAX_ITEM_CLASS) { sLog.outErrorDb("Item (Entry: %u) has wrong Class value (%u)",i,proto->Class); - const_cast<ItemPrototype*>(proto)->Class = ITEM_CLASS_JUNK; + const_cast<ItemPrototype*>(proto)->Class = ITEM_CLASS_MISC; } if(proto->SubClass >= MaxItemSubclassValues[proto->Class]) @@ -1412,14 +1595,30 @@ void ObjectMgr::LoadItemPrototypes() const_cast<ItemPrototype*>(proto)->RequiredSkill = 0; } - if(!(proto->AllowableClass & CLASSMASK_ALL_PLAYABLE)) { - sLog.outErrorDb("Item (Entry: %u) not have in `AllowableClass` any playable classes (%u) and can't be equipped.",i,proto->AllowableClass); - } - if(!(proto->AllowableRace & RACEMASK_ALL_PLAYABLE)) - { - sLog.outErrorDb("Item (Entry: %u) not have in `AllowableRace` any playable races (%u) and can't be equipped.",i,proto->AllowableRace); + // can be used in equip slot, as page read use in inventory, or spell casting at use + bool req = proto->InventoryType!=INVTYPE_NON_EQUIP || proto->PageText; + if(!req) + { + for (int j = 0; j < MAX_ITEM_PROTO_SPELLS; ++j) + { + if(proto->Spells[j].SpellId) + { + req = true; + break; + } + } + } + + if(req) + { + if(!(proto->AllowableClass & CLASSMASK_ALL_PLAYABLE)) + sLog.outErrorDb("Item (Entry: %u) not have in `AllowableClass` any playable classes (%u) and can't be equipped or use.",i,proto->AllowableClass); + + if(!(proto->AllowableRace & RACEMASK_ALL_PLAYABLE)) + sLog.outErrorDb("Item (Entry: %u) not have in `AllowableRace` any playable races (%u) and can't be equipped or use.",i,proto->AllowableRace); + } } if(proto->RequiredSpell && !sSpellStore.LookupEntry(proto->RequiredSpell)) @@ -1445,18 +1644,35 @@ void ObjectMgr::LoadItemPrototypes() else if(proto->RequiredReputationRank > MIN_REPUTATION_RANK) sLog.outErrorDb("Item (Entry: %u) has RequiredReputationFaction ==0 but RequiredReputationRank > 0, rank setting is useless.",i); + if(proto->MaxCount < -1) + { + sLog.outErrorDb("Item (Entry: %u) has too large negative in maxcount (%i), replace by value (-1) no storing limits.",i,proto->MaxCount); + const_cast<ItemPrototype*>(proto)->MaxCount = -1; + } + if(proto->Stackable==0) { - sLog.outErrorDb("Item (Entry: %u) has wrong value in stackable (%u), replace by default 1.",i,proto->Stackable); + sLog.outErrorDb("Item (Entry: %u) has wrong value in stackable (%i), replace by default 1.",i,proto->Stackable); const_cast<ItemPrototype*>(proto)->Stackable = 1; } + else if(proto->Stackable < -1) + { + sLog.outErrorDb("Item (Entry: %u) has too large negative in stackable (%i), replace by value (-1) no stacking limits.",i,proto->Stackable); + const_cast<ItemPrototype*>(proto)->Stackable = -1; + } else if(proto->Stackable > 255) { sLog.outErrorDb("Item (Entry: %u) has too large value in stackable (%u), replace by hardcoded upper limit (255).",i,proto->Stackable); const_cast<ItemPrototype*>(proto)->Stackable = 255; } - for (int j = 0; j < 10; j++) + if(proto->StatsCount > MAX_ITEM_PROTO_STATS) + { + sLog.outErrorDb("Item (Entry: %u) has too large value in statscount (%u), replace by hardcoded limit (%u).",i,proto->StatsCount,MAX_ITEM_PROTO_STATS); + const_cast<ItemPrototype*>(proto)->StatsCount = MAX_ITEM_PROTO_STATS; + } + + for (int j = 0; j < MAX_ITEM_PROTO_STATS; ++j) { // for ItemStatValue != 0 if(proto->ItemStat[j].ItemStatValue && proto->ItemStat[j].ItemStatType >= MAX_ITEM_MOD) @@ -1466,7 +1682,7 @@ void ObjectMgr::LoadItemPrototypes() } } - for (int j = 0; j < 5; j++) + for (int j = 0; j < MAX_ITEM_PROTO_DAMAGES; ++j) { if(proto->Damage[j].DamageType >= MAX_SPELL_SCHOOL) { @@ -1476,7 +1692,7 @@ void ObjectMgr::LoadItemPrototypes() } // special format - if(proto->Spells[0].SpellId == SPELL_ID_GENERIC_LEARN) + if((proto->Spells[0].SpellId == SPELL_ID_GENERIC_LEARN) || (proto->Spells[0].SpellId == SPELL_ID_GENERIC_LEARN_PET)) { // spell_1 if(proto->Spells[0].SpellTrigger != ITEM_SPELLTRIGGER_ON_USE) @@ -1513,7 +1729,7 @@ void ObjectMgr::LoadItemPrototypes() const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE; } // allowed only in special format - else if(proto->Spells[1].SpellId==SPELL_ID_GENERIC_LEARN) + else if((proto->Spells[1].SpellId==SPELL_ID_GENERIC_LEARN) || (proto->Spells[1].SpellId==SPELL_ID_GENERIC_LEARN_PET)) { sLog.outErrorDb("Item (Entry: %u) has broken spell in spellid_%d (%u)",i,1+1,proto->Spells[1].SpellId); const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0; @@ -1523,7 +1739,7 @@ void ObjectMgr::LoadItemPrototypes() } // spell_3*,spell_4*,spell_5* is empty - for (int j = 2; j < 5; j++) + for (int j = 2; j < MAX_ITEM_PROTO_SPELLS; ++j) { if(proto->Spells[j].SpellTrigger != ITEM_SPELLTRIGGER_ON_USE) { @@ -1541,7 +1757,7 @@ void ObjectMgr::LoadItemPrototypes() // normal spell list else { - for (int j = 0; j < 5; j++) + for (int j = 0; j < MAX_ITEM_PROTO_SPELLS; ++j) { if(proto->Spells[j].SpellTrigger >= MAX_ITEM_SPELLTRIGGER || proto->Spells[j].SpellTrigger == ITEM_SPELLTRIGGER_LEARN_SPELL_ID) { @@ -1559,7 +1775,7 @@ void ObjectMgr::LoadItemPrototypes() const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0; } // allowed only in special format - else if(proto->Spells[j].SpellId==SPELL_ID_GENERIC_LEARN) + else if((proto->Spells[j].SpellId==SPELL_ID_GENERIC_LEARN) || (proto->Spells[j].SpellId==SPELL_ID_GENERIC_LEARN_PET)) { sLog.outErrorDb("Item (Entry: %u) has broken spell in spellid_%d (%u)",i,j+1,proto->Spells[j].SpellId); const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0; @@ -1607,10 +1823,39 @@ void ObjectMgr::LoadItemPrototypes() if(proto->Map && !sMapStore.LookupEntry(proto->Map)) sLog.outErrorDb("Item (Entry: %u) has wrong Map (%u)",i,proto->Map); + if(proto->BagFamily) + { + // check bits + for(uint32 j = 0; j < sizeof(proto->BagFamily)*8; ++j) + { + uint32 mask = 1 << j; + if((proto->BagFamily & mask)==0) + continue; + + ItemBagFamilyEntry const* bf = sItemBagFamilyStore.LookupEntry(j+1); + if(!bf) + { + sLog.outErrorDb("Item (Entry: %u) has bag family bit set not listed in ItemBagFamily.dbc, remove bit",i); + const_cast<ItemPrototype*>(proto)->BagFamily &= ~mask; + continue; + } + + if(BAG_FAMILY_MASK_CURRENCY_TOKENS & mask) + { + CurrencyTypesEntry const* ctEntry = sCurrencyTypesStore.LookupEntry(proto->ItemId); + if(!ctEntry) + { + sLog.outErrorDb("Item (Entry: %u) has currency bag family bit set in BagFamily but not listed in CurrencyTypes.dbc, remove bit",i); + const_cast<ItemPrototype*>(proto)->BagFamily &= ~mask; + } + } + } + } + if(proto->TotemCategory && !sTotemCategoryStore.LookupEntry(proto->TotemCategory)) sLog.outErrorDb("Item (Entry: %u) has wrong TotemCategory (%u)",i,proto->TotemCategory); - for (int j = 0; j < 3; j++) + for (int j = 0; j < MAX_ITEM_PROTO_SOCKETS; j++) { if(proto->Socket[j].Color && (proto->Socket[j].Color & SOCKET_COLOR_ALL) != proto->Socket[j].Color) { @@ -1627,10 +1872,13 @@ void ObjectMgr::LoadItemPrototypes() sLog.outErrorDb("Item (Entry: %u) has wrong FoodType value (%u)",i,proto->FoodType); const_cast<ItemPrototype*>(proto)->FoodType = 0; } - } - // this DBC used currently only for check item templates in DB. - sItemStore.Clear(); + if(proto->ItemLimitCategory && !sItemLimitCategoryStore.LookupEntry(proto->ItemLimitCategory)) + { + sLog.outErrorDb("Item (Entry: %u) has wrong LimitCategory value (%u)",i,proto->ItemLimitCategory); + const_cast<ItemPrototype*>(proto)->ItemLimitCategory = 0; + } + } } void ObjectMgr::LoadPetLevelInfo() @@ -1671,7 +1919,10 @@ void ObjectMgr::LoadPetLevelInfo() if(current_level > STRONG_MAX_LEVEL) // hardcoded level maximum sLog.outErrorDb("Wrong (> %u) level %u in `pet_levelstats` table, ignoring.",STRONG_MAX_LEVEL,current_level); else + { sLog.outDetail("Unused (> MaxPlayerLevel in Trinityd.conf) level %u in `pet_levelstats` table, ignoring.",current_level); + ++count; // make result loading percent "expected" correct in case disabled detail mode for example. + } continue; } else if(current_level < 1) @@ -1914,7 +2165,7 @@ void ObjectMgr::LoadPlayerInfo() if(sWorld.getConfig(CONFIG_START_ALL_SPELLS)) result = WorldDatabase.Query("SELECT race, class, Spell, Active FROM playercreateinfo_spell_custom"); else - result = WorldDatabase.Query("SELECT race, class, Spell, Active FROM playercreateinfo_spell"); + result = WorldDatabase.Query("SELECT race, class, Spell FROM playercreateinfo_spell"); uint32 count = 0; @@ -1949,7 +2200,7 @@ void ObjectMgr::LoadPlayerInfo() } PlayerInfo* pInfo = &playerInfo[current_race][current_class]; - pInfo->spell.push_back(CreateSpellPair(fields[2].GetUInt16(), fields[3].GetUInt8())); + pInfo->spell.push_back(fields[2].GetUInt32()); bar.step(); ++count; @@ -2054,7 +2305,10 @@ void ObjectMgr::LoadPlayerInfo() if(current_level > STRONG_MAX_LEVEL) // hardcoded level maximum sLog.outErrorDb("Wrong (> %u) level %u in `player_classlevelstats` table, ignoring.",STRONG_MAX_LEVEL,current_level); else + { sLog.outDetail("Unused (> MaxPlayerLevel in Trinityd.conf) level %u in `player_classlevelstats` table, ignoring.",current_level); + ++count; // make result loading percent "expected" correct in case disabled detail mode for example. + } continue; } @@ -2149,7 +2403,10 @@ void ObjectMgr::LoadPlayerInfo() if(current_level > STRONG_MAX_LEVEL) // hardcoded level maximum sLog.outErrorDb("Wrong (> %u) level %u in `player_levelstats` table, ignoring.",STRONG_MAX_LEVEL,current_level); else + { sLog.outDetail("Unused (> MaxPlayerLevel in Trinityd.conf) level %u in `player_levelstats` table, ignoring.",current_level); + ++count; // make result loading percent "expected" correct in case disabled detail mode for example. + } continue; } @@ -2221,6 +2478,70 @@ void ObjectMgr::LoadPlayerInfo() } } } + + // Loading xp per level data + { + mPlayerXPperLevel.resize(sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)); + for (uint32 level = 0; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level) + mPlayerXPperLevel[level] = 0; + + // 0 1 + QueryResult *result = WorldDatabase.Query("SELECT lvl, xp_for_next_level FROM player_xp_for_level"); + + uint32 count = 0; + + if (!result) + { + barGoLink bar( 1 ); + + sLog.outString(); + sLog.outString( ">> Loaded %u xp for level definitions", count ); + sLog.outErrorDb( "Error loading `player_xp_for_level` table or empty table."); + exit(1); + } + + barGoLink bar( result->GetRowCount() ); + + do + { + Field* fields = result->Fetch(); + + uint32 current_level = fields[0].GetUInt32(); + uint32 current_xp = fields[1].GetUInt32(); + + if(current_level >= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)) + { + if(current_level > STRONG_MAX_LEVEL) // hardcoded level maximum + sLog.outErrorDb("Wrong (> %u) level %u in `player_xp_for_level` table, ignoring.", STRONG_MAX_LEVEL,current_level); + else + { + sLog.outDetail("Unused (> MaxPlayerLevel in mangosd.conf) level %u in `player_xp_for_levels` table, ignoring.",current_level); + ++count; // make result loading percent "expected" correct in case disabled detail mode for example. + } + continue; + } + //PlayerXPperLevel + mPlayerXPperLevel[current_level] = current_xp; + bar.step(); + ++count; + } + while (result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u xp for level definitions", count ); + } + + // fill level gaps + for (uint32 level = 1; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level) + { + if( mPlayerXPperLevel[level] == 0) + { + sLog.outErrorDb("Level %i does not have XP for level data. Using data of level [%i] + 100.",level+1, level); + mPlayerXPperLevel[level] = mPlayerXPperLevel[level-1]+100; + } + } } void ObjectMgr::GetPlayerClassLevelInfo(uint32 class_, uint32 level, PlayerClassLevelInfo* info) const @@ -2547,7 +2868,14 @@ void ObjectMgr::LoadGroups() } } - InstanceSave *save = sInstanceSaveManager.AddInstanceSave(fields[1].GetUInt32(), fields[2].GetUInt32(), fields[4].GetUInt8(), (time_t)fields[5].GetUInt64(), (fields[6].GetUInt32() == 0), true); + MapEntry const* mapEntry = sMapStore.LookupEntry(fields[1].GetUInt32()); + if(!mapEntry || !mapEntry->IsDungeon()) + { + sLog.outErrorDb("Incorrect entry in group_instance table : no dungeon map %d", fields[1].GetUInt32()); + continue; + } + + InstanceSave *save = sInstanceSaveManager.AddInstanceSave(mapEntry->MapID, fields[2].GetUInt32(), fields[4].GetUInt8(), (time_t)fields[5].GetUInt64(), (fields[6].GetUInt32() == 0), true); group->BindToInstance(save, fields[3].GetBool(), true); }while( result->NextRow() ); delete result; @@ -2573,31 +2901,31 @@ void ObjectMgr::LoadQuests() QueryResult *result = WorldDatabase.Query("SELECT entry, Method, ZoneOrSort, SkillOrClass, MinLevel, QuestLevel, Type, RequiredRaces, RequiredSkillValue," // 9 10 11 12 13 14 15 16 "RepObjectiveFaction, RepObjectiveValue, RequiredMinRepFaction, RequiredMinRepValue, RequiredMaxRepFaction, RequiredMaxRepValue, SuggestedPlayers, LimitTime," - // 17 18 19 20 21 22 23 24 25 26 - "QuestFlags, SpecialFlags, CharTitleId, PrevQuestId, NextQuestId, ExclusiveGroup, NextQuestInChain, SrcItemId, SrcItemCount, SrcSpell," - // 27 28 29 30 31 32 33 34 35 36 + // 17 18 19 20 21 22 23 24 25 26 27 28 + "QuestFlags, SpecialFlags, CharTitleId, PlayersSlain, BonusTalents, PrevQuestId, NextQuestId, ExclusiveGroup, NextQuestInChain, SrcItemId, SrcItemCount, SrcSpell," + // 29 30 31 32 33 34 35 36 37 38 "Title, Details, Objectives, OfferRewardText, RequestItemsText, EndText, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4," - // 37 38 39 40 41 42 43 44 + // 39 40 41 42 43 44 45 46 "ReqItemId1, ReqItemId2, ReqItemId3, ReqItemId4, ReqItemCount1, ReqItemCount2, ReqItemCount3, ReqItemCount4," - // 45 46 47 48 49 50 51 52 53 54 54 55 - "ReqSourceId1, ReqSourceId2, ReqSourceId3, ReqSourceId4, ReqSourceCount1, ReqSourceCount2, ReqSourceCount3, ReqSourceCount4, ReqSourceRef1, ReqSourceRef2, ReqSourceRef3, ReqSourceRef4," - // 57 58 59 60 61 62 63 64 + // 47 48 49 50 51 52 53 54 + "ReqSourceId1, ReqSourceId2, ReqSourceId3, ReqSourceId4, ReqSourceCount1, ReqSourceCount2, ReqSourceCount3, ReqSourceCount4," + // 55 56 57 58 59 60 61 62 "ReqCreatureOrGOId1, ReqCreatureOrGOId2, ReqCreatureOrGOId3, ReqCreatureOrGOId4, ReqCreatureOrGOCount1, ReqCreatureOrGOCount2, ReqCreatureOrGOCount3, ReqCreatureOrGOCount4," - // 65 66 67 68 + // 63 64 65 66 "ReqSpellCast1, ReqSpellCast2, ReqSpellCast3, ReqSpellCast4," - // 69 70 71 72 73 74 + // 67 68 69 70 71 72 "RewChoiceItemId1, RewChoiceItemId2, RewChoiceItemId3, RewChoiceItemId4, RewChoiceItemId5, RewChoiceItemId6," - // 75 76 77 78 79 80 + // 73 74 75 76 77 78 "RewChoiceItemCount1, RewChoiceItemCount2, RewChoiceItemCount3, RewChoiceItemCount4, RewChoiceItemCount5, RewChoiceItemCount6," - // 81 82 83 84 85 86 87 88 + // 79 80 81 82 83 84 85 86 "RewItemId1, RewItemId2, RewItemId3, RewItemId4, RewItemCount1, RewItemCount2, RewItemCount3, RewItemCount4," - // 89 90 91 92 93 94 95 96 97 98 + // 87 88 89 90 91 92 93 94 95 96 "RewRepFaction1, RewRepFaction2, RewRepFaction3, RewRepFaction4, RewRepFaction5, RewRepValue1, RewRepValue2, RewRepValue3, RewRepValue4, RewRepValue5," - // 99 100 101 102 103 104 105 106 107 108 109 + // 97 98 99 100 101 102 103 104 105 106 107 "RewHonorableKills, RewOrReqMoney, RewMoneyMaxLevel, RewSpell, RewSpellCast, RewMailTemplateId, RewMailDelaySecs, PointMapId, PointX, PointY, PointOpt," - // 110 111 112 113 114 115 116 117 118 119 - "DetailsEmote1, DetailsEmote2, DetailsEmote3, DetailsEmote4,IncompleteEmote, CompleteEmote, OfferRewardEmote1, OfferRewardEmote2, OfferRewardEmote3, OfferRewardEmote4," - // 120 121 + // 108 109 110 111 112 113 114 115 116 117 + "DetailsEmote1, DetailsEmote2, DetailsEmote3, DetailsEmote4, IncompleteEmote, CompleteEmote, OfferRewardEmote1, OfferRewardEmote2, OfferRewardEmote3, OfferRewardEmote4," + // 118 119 "StartScript, CompleteScript" " FROM quest_template"); if(result == NULL) @@ -2769,10 +3097,10 @@ void ObjectMgr::LoadQuests() // no changes, quest can't be done for this requirement } - if(qinfo->RequiredMinRepValue && qinfo->RequiredMinRepValue > Player::Reputation_Cap) + if(qinfo->RequiredMinRepValue && qinfo->RequiredMinRepValue > ReputationMgr::Reputation_Cap) { sLog.outErrorDb("Quest %u has `RequiredMinRepValue` = %d but max reputation is %u, quest can't be done.", - qinfo->GetQuestId(),qinfo->RequiredMinRepValue,Player::Reputation_Cap); + qinfo->GetQuestId(),qinfo->RequiredMinRepValue,ReputationMgr::Reputation_Cap); // no changes, quest can't be done for this requirement } @@ -2891,20 +3219,6 @@ void ObjectMgr::LoadQuests() qinfo->GetQuestId(),j+1,id,id); // no changes, quest can't be done for this requirement } - - if(!qinfo->ReqSourceCount[j]) - { - sLog.outErrorDb("Quest %u has `ReqSourceId%d` = %u but `ReqSourceCount%d` = 0, quest can't be done.", - qinfo->GetQuestId(),j+1,id,j+1); - qinfo->ReqSourceId[j] = 0; // prevent incorrect work of quest - } - - if(!qinfo->ReqSourceRef[j]) - { - sLog.outErrorDb("Quest %u has `ReqSourceId%d` = %u but `ReqSourceRef%d` = 0, quest can't be done.", - qinfo->GetQuestId(),j+1,id,j+1); - qinfo->ReqSourceId[j] = 0; // prevent incorrect work of quest - } } else { @@ -2914,41 +3228,6 @@ void ObjectMgr::LoadQuests() qinfo->GetQuestId(),j+1,j+1,qinfo->ReqSourceCount[j]); // no changes, quest ignore this data } - - if(qinfo->ReqSourceRef[j]>0) - { - sLog.outErrorDb("Quest %u has `ReqSourceId%d` = 0 but `ReqSourceRef%d` = %u.", - qinfo->GetQuestId(),j+1,j+1,qinfo->ReqSourceRef[j]); - // no changes, quest ignore this data - } - } - } - - for(int j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j ) - { - uint32 ref = qinfo->ReqSourceRef[j]; - if(ref) - { - if(ref > QUEST_OBJECTIVES_COUNT) - { - sLog.outErrorDb("Quest %u has `ReqSourceRef%d` = %u but max value in `ReqSourceRef%d` is %u, quest can't be done.", - qinfo->GetQuestId(),j+1,ref,j+1,QUEST_OBJECTIVES_COUNT); - // no changes, quest can't be done for this requirement - } - else - if(!qinfo->ReqItemId[ref-1] && !qinfo->ReqSpell[ref-1]) - { - sLog.outErrorDb("Quest %u has `ReqSourceRef%d` = %u but `ReqItemId%u` = 0 and `ReqSpellCast%u` = 0, quest can't be done.", - qinfo->GetQuestId(),j+1,ref,ref,ref); - // no changes, quest can't be done for this requirement - } - else if(qinfo->ReqItemId[ref-1] && qinfo->ReqSpell[ref-1]) - { - sLog.outErrorDb("Quest %u has `ReqItemId%u` = %u and `ReqSpellCast%u` = %u, quest can't have both fields <> 0, then can't be done.", - qinfo->GetQuestId(),ref,qinfo->ReqItemId[ref-1],ref,qinfo->ReqSpell[ref-1]); - // no changes, quest can't be done for this requirement - qinfo->ReqSourceId[j] = 0; // prevent incorrect work of quest - } } } @@ -3169,14 +3448,15 @@ void ObjectMgr::LoadQuests() if(qinfo->NextQuestInChain) { - if(mQuestTemplates.find(qinfo->NextQuestInChain) == mQuestTemplates.end()) + QuestMap::iterator qNextItr = mQuestTemplates.find(qinfo->NextQuestInChain); + if(qNextItr == mQuestTemplates.end()) { sLog.outErrorDb("Quest %u has `NextQuestInChain` = %u but quest %u does not exist, quest chain will not work.", qinfo->GetQuestId(),qinfo->NextQuestInChain ,qinfo->NextQuestInChain ); qinfo->NextQuestInChain = 0; } else - mQuestTemplates[qinfo->NextQuestInChain]->prevChainQuests.push_back(qinfo->GetQuestId()); + qNextItr->second->prevChainQuests.push_back(qinfo->GetQuestId()); } // fill additional data stores @@ -3194,14 +3474,15 @@ void ObjectMgr::LoadQuests() if(qinfo->NextQuestId) { - if (mQuestTemplates.find(abs(qinfo->GetNextQuestId())) == mQuestTemplates.end()) + QuestMap::iterator qNextItr = mQuestTemplates.find(abs(qinfo->GetNextQuestId())); + if (qNextItr == mQuestTemplates.end()) { sLog.outErrorDb("Quest %d has NextQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->GetNextQuestId()); } else { int32 signedQuestId = qinfo->NextQuestId < 0 ? -int32(qinfo->GetQuestId()) : int32(qinfo->GetQuestId()); - mQuestTemplates[abs(qinfo->GetNextQuestId())]->prevQuests.push_back(signedQuestId); + qNextItr->second->prevQuests.push_back(signedQuestId); } } @@ -3242,7 +3523,7 @@ void ObjectMgr::LoadQuests() } sLog.outString(); - sLog.outString( ">> Loaded %u quests definitions", mQuestTemplates.size() ); + sLog.outString( ">> Loaded %lu quests definitions", (unsigned long)mQuestTemplates.size() ); } void ObjectMgr::LoadQuestLocales() @@ -3267,7 +3548,7 @@ void ObjectMgr::LoadQuestLocales() bar.step(); - sLog.outString(""); + sLog.outString(); sLog.outString(">> Loaded 0 Quest locale strings. DB table `locales_quest` is empty."); return; } @@ -3378,7 +3659,7 @@ void ObjectMgr::LoadQuestLocales() delete result; sLog.outString(); - sLog.outString( ">> Loaded %u Quest locale strings", mQuestLocaleMap.size() ); + sLog.outString( ">> Loaded %lu Quest locale strings", (unsigned long)mQuestLocaleMap.size() ); } void ObjectMgr::LoadPetCreateSpells() @@ -3627,6 +3908,21 @@ void ObjectMgr::LoadScripts(ScriptMapMap& scripts, char const* tablename) } case SCRIPT_COMMAND_REMOVE_AURA: + { + if(!sSpellStore.LookupEntry(tmp.datalong)) + { + sLog.outErrorDb("Table `%s` using non-existent spell (id: %u) in SCRIPT_COMMAND_REMOVE_AURA or SCRIPT_COMMAND_CAST_SPELL for script id %u", + tablename,tmp.datalong,tmp.id); + continue; + } + if(tmp.datalong2 & ~0x1) // 1 bits (0,1) + { + sLog.outErrorDb("Table `%s` using unknown flags in datalong2 (%u)i n SCRIPT_COMMAND_CAST_SPELL for script id %u", + tablename,tmp.datalong2,tmp.id); + continue; + } + break; + } case SCRIPT_COMMAND_CAST_SPELL: { if(!sSpellStore.LookupEntry(tmp.datalong)) @@ -3635,6 +3931,12 @@ void ObjectMgr::LoadScripts(ScriptMapMap& scripts, char const* tablename) tablename,tmp.datalong,tmp.id); continue; } + if(tmp.datalong2 & ~0x3) // 2 bits + { + sLog.outErrorDb("Table `%s` using unknown flags in datalong2 (%u)i n SCRIPT_COMMAND_CAST_SPELL for script id %u", + tablename,tmp.datalong2,tmp.id); + continue; + } break; } } @@ -3883,7 +4185,7 @@ void ObjectMgr::LoadPageTextLocales() bar.step(); - sLog.outString(""); + sLog.outString(); sLog.outString(">> Loaded 0 PageText locale strings. DB table `locales_page_text` is empty."); return; } @@ -3920,7 +4222,7 @@ void ObjectMgr::LoadPageTextLocales() delete result; sLog.outString(); - sLog.outString( ">> Loaded %u PageText locale strings", mPageTextLocaleMap.size() ); + sLog.outString( ">> Loaded %lu PageText locale strings", (unsigned long)mPageTextLocaleMap.size() ); } struct SQLInstanceLoader : public SQLStorageLoaderBase<SQLInstanceLoader> @@ -3950,14 +4252,20 @@ void ObjectMgr::LoadInstanceTemplate() else if(!entry->HasResetTime()) continue; + //FIXME: now exist heroic instance, normal/heroic raid instances + // entry->resetTimeHeroic store reset time for both heroic mode instance (raid and non-raid) + // entry->resetTimeRaid store reset time for normal raid only + // for current state entry->resetTimeRaid == entry->resetTimeHeroic in case raid instances with heroic mode. + // but at some point wee need implement reset time dependent from raid instance mode if(temp->reset_delay == 0) { // use defaults from the DBC - if(entry->SupportsHeroicMode()) + if(entry->resetTimeHeroic) // for both raid and non raids, read above { temp->reset_delay = entry->resetTimeHeroic / DAY; } else if (entry->resetTimeRaid && entry->map_type == MAP_RAID) + // for normal raid only { temp->reset_delay = entry->resetTimeRaid / DAY; } @@ -3971,27 +4279,33 @@ void ObjectMgr::LoadInstanceTemplate() sLog.outString(); } -void ObjectMgr::AddGossipText(GossipText *pGText) +bool ObjectMgr::IsGameObjectOfTypeInRange(Player *player, uint64 guid, GameobjectTypes type) const { - ASSERT( pGText->Text_ID ); - ASSERT( mGossipText.find(pGText->Text_ID) == mGossipText.end() ); - mGossipText[pGText->Text_ID] = pGText; + if(GameObject *go = ObjectAccessor::GetGameObject(*player, guid)) + { + if(go->GetGoType() == type) + { + // TODO: find out how the client calculates the maximal usage distance to spellless working + // gameobjects like guildbanks and mailboxes - 10.0 is a just an abitrary choosen number + if (go->IsWithinDistInMap(player, 10.0f)) + return true; + sLog.outError("IsGameObjectOfTypeInRange: GameObject '%s' [GUID: %u] is too far away from player %s [GUID: %u] to be used by him (distance=%f, maximal 10 is allowed)", go->GetGOInfo()->name, + go->GetGUIDLow(), player->GetName(), player->GetGUIDLow(), go->GetDistance(player)); + } + } + return false; } -GossipText *ObjectMgr::GetGossipText(uint32 Text_ID) +GossipText const *ObjectMgr::GetGossipText(uint32 Text_ID) const { - GossipTextMap::const_iterator itr; - for (itr = mGossipText.begin(); itr != mGossipText.end(); ++itr) - { - if(itr->second->Text_ID == Text_ID) - return itr->second; - } + GossipTextMap::const_iterator itr = mGossipText.find(Text_ID); + if(itr != mGossipText.end()) + return &itr->second; return NULL; } void ObjectMgr::LoadGossipText() { - GossipText *pGText; QueryResult *result = WorldDatabase.Query( "SELECT * FROM npc_text" ); int count = 0; @@ -4018,30 +4332,29 @@ void ObjectMgr::LoadGossipText() bar.step(); - pGText = new GossipText; - pGText->Text_ID = fields[cic++].GetUInt32(); - - for (int i=0; i< 8; i++) + uint32 Text_ID = fields[cic++].GetUInt32(); + if(!Text_ID) { - pGText->Options[i].Text_0 = fields[cic++].GetCppString(); - pGText->Options[i].Text_1 = fields[cic++].GetCppString(); + sLog.outErrorDb("Table `npc_text` has record wit reserved id 0, ignore."); + continue; + } - pGText->Options[i].Language = fields[cic++].GetUInt32(); - pGText->Options[i].Probability = fields[cic++].GetFloat(); + GossipText& gText = mGossipText[Text_ID]; - pGText->Options[i].Emotes[0]._Delay = fields[cic++].GetUInt32(); - pGText->Options[i].Emotes[0]._Emote = fields[cic++].GetUInt32(); + for (int i=0; i< 8; i++) + { + gText.Options[i].Text_0 = fields[cic++].GetCppString(); + gText.Options[i].Text_1 = fields[cic++].GetCppString(); - pGText->Options[i].Emotes[1]._Delay = fields[cic++].GetUInt32(); - pGText->Options[i].Emotes[1]._Emote = fields[cic++].GetUInt32(); + gText.Options[i].Language = fields[cic++].GetUInt32(); + gText.Options[i].Probability = fields[cic++].GetFloat(); - pGText->Options[i].Emotes[2]._Delay = fields[cic++].GetUInt32(); - pGText->Options[i].Emotes[2]._Emote = fields[cic++].GetUInt32(); + for(int j=0; j < 3; ++j) + { + gText.Options[i].Emotes[j]._Delay = fields[cic++].GetUInt32(); + gText.Options[i].Emotes[j]._Emote = fields[cic++].GetUInt32(); + } } - - if ( !pGText->Text_ID ) continue; - AddGossipText( pGText ); - } while( result->NextRow() ); sLog.outString(); @@ -4070,8 +4383,8 @@ void ObjectMgr::LoadNpcTextLocales() bar.step(); - sLog.outString(""); - sLog.outString(">> Loaded 0 Quest locale strings. DB table `locales_npc_text` is empty."); + sLog.outString(); + sLog.outString(">> Loaded 0 NpcText locale strings. DB table `locales_npc_text` is empty."); return; } @@ -4121,7 +4434,7 @@ void ObjectMgr::LoadNpcTextLocales() delete result; sLog.outString(); - sLog.outString( ">> Loaded %u NpcText locale strings", mNpcTextLocaleMap.size() ); + sLog.outString( ">> Loaded %lu NpcText locale strings", (unsigned long)mNpcTextLocaleMap.size() ); } //not very fast function but it is called only once a day, or on starting-up @@ -4135,14 +4448,27 @@ void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp) // 0 1 2 3 4 5 6 7 8 9 QueryResult* result = CharacterDatabase.PQuery("SELECT id,messageType,sender,receiver,itemTextId,has_items,expire_time,cod,checked,mailTemplateId FROM mail WHERE expire_time < '" I64FMTD "'", (uint64)basetime); if ( !result ) + { + barGoLink bar(1); + bar.step(); + sLog.outString(); + sLog.outString(">> Only expired mails (need to be return or delete) or DB table `mail` is empty."); return; // any mails need to be returned or deleted - Field *fields; + } + //std::ostringstream delitems, delmails; //will be here for optimization //bool deletemail = false, deleteitem = false; //delitems << "DELETE FROM item_instance WHERE guid IN ( "; //delmails << "DELETE FROM mail WHERE id IN ( " + + barGoLink bar( result->GetRowCount() ); + uint32 count = 0; + Field *fields; + do { + bar.step(); + fields = result->Fetch(); Mail *m = new Mail; m->messageID = fields[0].GetUInt32(); @@ -4208,8 +4534,12 @@ void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp) //delmails << m->messageID << ", "; CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", m->messageID); delete m; + ++count; } while (result->NextRow()); delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u mails", count ); } void ObjectMgr::LoadQuestAreaTriggers() @@ -4366,7 +4696,7 @@ void ObjectMgr::LoadAreaTriggerScripts() sLog.outString( ">> Loaded %u areatrigger scripts", count ); } -uint32 ObjectMgr::GetNearestTaxiNode( float x, float y, float z, uint32 mapid ) +uint32 ObjectMgr::GetNearestTaxiNode( float x, float y, float z, uint32 mapid, uint32 team ) { bool found = false; float dist; @@ -4375,24 +4705,31 @@ uint32 ObjectMgr::GetNearestTaxiNode( float x, float y, float z, uint32 mapid ) for(uint32 i = 1; i < sTaxiNodesStore.GetNumRows(); ++i) { TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(i); - if(node && node->map_id == mapid) + if(!node || node->map_id != mapid || !node->MountCreatureID[team == ALLIANCE ? 1 : 0]) + continue; + + uint8 field = (uint8)((i - 1) / 32); + uint32 submask = 1<<((i-1)%32); + + // skip not taxi network nodes + if((sTaxiNodesMask[field] & submask)==0) + continue; + + float dist2 = (node->x - x)*(node->x - x)+(node->y - y)*(node->y - y)+(node->z - z)*(node->z - z); + if(found) { - float dist2 = (node->x - x)*(node->x - x)+(node->y - y)*(node->y - y)+(node->z - z)*(node->z - z); - if(found) - { - if(dist2 < dist) - { - dist = dist2; - id = i; - } - } - else + if(dist2 < dist) { - found = true; dist = dist2; id = i; } } + else + { + found = true; + dist = dist2; + id = i; + } } return id; @@ -4430,17 +4767,19 @@ uint16 ObjectMgr::GetTaxiMount( uint32 id, uint32 team ) TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(id); if(node) { - if (team == ALLIANCE) mount_entry = node->alliance_mount_type; - else mount_entry = node->horde_mount_type; - - CreatureInfo const *cinfo = GetCreatureTemplate(mount_entry); - if (cinfo) + if (team == ALLIANCE) { - if(! (mount_id = cinfo->GetRandomValidModelId())) - { - sLog.outErrorDb("No displayid found for the taxi mount with the entry %u! Can't load it!", mount_entry); - return false; - } + mount_entry = node->MountCreatureID[1]; + CreatureInfo const *ci = GetCreatureTemplate(mount_entry); + if(ci) + mount_id = ci->Modelid1; + } + if (team == HORDE) + { + mount_entry = node->MountCreatureID[0]; + CreatureInfo const *ci = GetCreatureTemplate(mount_entry); + if(ci) + mount_id = ci->Modelid3; } } @@ -4568,7 +4907,7 @@ void ObjectMgr::LoadGraveyardZones() WorldSafeLocsEntry const *ObjectMgr::GetClosestGraveYard(float x, float y, float z, uint32 MapId, uint32 team) { // search for zone associated closest graveyard - uint32 zoneId = MapManager::Instance().GetZoneId(MapId,x,y); + uint32 zoneId = MapManager::Instance().GetZoneId(MapId,x,y,z); // Simulate std. algorithm: // found some graveyard associated to (ghost_zone,ghost_map) @@ -4579,7 +4918,10 @@ WorldSafeLocsEntry const *ObjectMgr::GetClosestGraveYard(float x, float y, float // then check faction GraveYardMap::const_iterator graveLow = mGraveYardMap.lower_bound(zoneId); GraveYardMap::const_iterator graveUp = mGraveYardMap.upper_bound(zoneId); - if(graveLow==graveUp) + MapEntry const* map = sMapStore.LookupEntry(MapId); + // not need to check validity of map object; MapId _MUST_ be valid here + + if(graveLow==graveUp && !map->IsBattleArena()) { sLog.outErrorDb("Table `game_graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.",zoneId,team); return NULL; @@ -4766,7 +5108,7 @@ void ObjectMgr::LoadAreaTriggerTeleports() uint32 count = 0; - // 0 1 2 3 4 5 6 + // 0 1 2 3 4 5 6 QueryResult *result = WorldDatabase.Query("SELECT id, access_id, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM areatrigger_teleport"); if( !result ) { @@ -4807,7 +5149,7 @@ void ObjectMgr::LoadAreaTriggerTeleports() sLog.outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.",Trigger_ID); continue; } - + MapEntry const* mapEntry = sMapStore.LookupEntry(at.target_mapId); if(!mapEntry) { @@ -4837,9 +5179,9 @@ void ObjectMgr::LoadAccessRequirements() uint32 count = 0; - // 0 1 2 3 4 5 6 7 8 9 10 + // 0 1 2 3 4 5 6 7 8 9 10 QueryResult *result = WorldDatabase.Query("SELECT id, level_min, level_max, item, item2, heroic_key, heroic_key2, quest_done, quest_failed_text, heroic_quest_done, heroic_quest_failed_text FROM access_requirement"); - if( !result ) + if( !result ) { barGoLink bar( 1 ); @@ -4916,18 +5258,20 @@ void ObjectMgr::LoadAccessRequirements() } } - if(ar.heroicQuest) - { - if(!mQuestTemplates[ar.heroicQuest]) - { - sLog.outErrorDb("Required Heroic Quest %u not exist for trigger %u, remove heroic quest done requirement.",ar.heroicQuest,requiremt_ID); - ar.heroicQuest = 0; - } - } - + if(ar.heroicQuest) + { + QuestMap::iterator qReqItr = mQuestTemplates.find(ar.heroicQuest); + if(qReqItr == mQuestTemplates.end()) + { + sLog.outErrorDb("Required Heroic Quest %u not exist for trigger %u, remove heroic quest done requirement.",ar.heroicQuest,requiremt_ID); + ar.heroicQuest = 0; + } + } + if(ar.quest) { - if(!mQuestTemplates[ar.quest]) + QuestMap::iterator qReqItr = mQuestTemplates.find(ar.quest); + if(qReqItr == mQuestTemplates.end()) { sLog.outErrorDb("Required Quest %u not exist for trigger %u, remove quest done requirement.",ar.quest,requiremt_ID); ar.quest = 0; @@ -4944,6 +5288,9 @@ void ObjectMgr::LoadAccessRequirements() sLog.outString( ">> Loaded %u access requirement definitions", count ); } +/* + * Searches for the areatrigger which teleports players out of the given map + */ AreaTrigger const* ObjectMgr::GetGoBackTrigger(uint32 Map) const { const MapEntry *mapEntry = sMapStore.LookupEntry(Map); @@ -4960,6 +5307,23 @@ AreaTrigger const* ObjectMgr::GetGoBackTrigger(uint32 Map) const return NULL; } +/** + * Searches for the areatrigger which teleports players to the given map + */ +AreaTrigger const* ObjectMgr::GetMapEntranceTrigger(uint32 Map) const +{ + for (AreaTriggerMap::const_iterator itr = mAreaTriggers.begin(); itr != mAreaTriggers.end(); ++itr) + { + if(itr->second.target_mapId == Map) + { + AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first); + if(atEntry) + return &itr->second; + } + } + return NULL; +} + void ObjectMgr::SetHighestGuids() { QueryResult *result = CharacterDatabase.Query( "SELECT MAX(guid) FROM characters" ); @@ -4976,9 +5340,6 @@ void ObjectMgr::SetHighestGuids() delete result; } - // pet guids are not saved to DB, set to 0 (pet guid != pet id) - m_hiPetGuid = 0; - result = CharacterDatabase.Query( "SELECT MAX(guid) FROM item_instance" ); if( result ) { @@ -5052,24 +5413,24 @@ uint32 ObjectMgr::GenerateArenaTeamId() return m_arenaTeamId++; } -uint32 ObjectMgr::GenerateGuildId() +uint32 ObjectMgr::GenerateAuctionID() { - if(m_guildId>=0xFFFFFFFE) + if(m_auctionid>=0xFFFFFFFE) { - sLog.outError("Guild ids overflow!! Can't continue, shutting down server. "); + sLog.outError("Auctions ids overflow!! Can't continue, shutting down server. "); World::StopNow(ERROR_EXIT_CODE); } - return m_guildId++; + return m_auctionid++; } -uint32 ObjectMgr::GenerateAuctionID() +uint32 ObjectMgr::GenerateGuildId() { - if(m_auctionid>=0xFFFFFFFE) + if(m_guildId>=0xFFFFFFFE) { - sLog.outError("Auctions ids overflow!! Can't continue, shutting down server. "); + sLog.outError("Guild ids overflow!! Can't continue, shutting down server. "); World::StopNow(ERROR_EXIT_CODE); } - return m_auctionid++; + return m_guildId++; } uint32 ObjectMgr::GenerateMailID() @@ -5131,6 +5492,13 @@ uint32 ObjectMgr::GenerateLowGuid(HighGuid guidhigh) World::StopNow(ERROR_EXIT_CODE); } return m_hiPetGuid++; + case HIGHGUID_VEHICLE: + if(m_hiVehicleGuid>=0x00FFFFFF) + { + sLog.outError("Vehicle guid overflow!! Can't continue, shutting down server. "); + World::StopNow(ERROR_EXIT_CODE); + } + return m_hiVehicleGuid++; case HIGHGUID_PLAYER: if(m_hiCharGuid>=0xFFFFFFFE) { @@ -5182,7 +5550,7 @@ void ObjectMgr::LoadGameObjectLocales() bar.step(); - sLog.outString(""); + sLog.outString(); sLog.outString(">> Loaded 0 gameobject locale strings. DB table `locales_gameobject` is empty."); return; } @@ -5214,9 +5582,9 @@ void ObjectMgr::LoadGameObjectLocales() } } - for(int i = MAX_LOCALE; i < MAX_LOCALE*2-1; ++i) + for(int i = 1; i < MAX_LOCALE; ++i) { - std::string str = fields[i].GetCppString(); + std::string str = fields[i+(MAX_LOCALE-1)].GetCppString(); if(!str.empty()) { int idx = GetOrNewIndexForLocale(LocaleConstant(i)); @@ -5235,7 +5603,7 @@ void ObjectMgr::LoadGameObjectLocales() delete result; sLog.outString(); - sLog.outString( ">> Loaded %u gameobject locale strings", mGameObjectLocaleMap.size() ); + sLog.outString( ">> Loaded %lu gameobject locale strings", (unsigned long)mGameObjectLocaleMap.size() ); } struct SQLGameObjectLoader : public SQLStorageLoaderBase<SQLGameObjectLoader> @@ -5247,6 +5615,61 @@ struct SQLGameObjectLoader : public SQLStorageLoaderBase<SQLGameObjectLoader> } }; +inline void CheckGOLockId(GameObjectInfo const* goInfo,uint32 dataN,uint32 N) +{ + if (sLockStore.LookupEntry(dataN)) + return; + + sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but lock (Id: %u) not found.", + goInfo->id,goInfo->type,N,goInfo->door.lockId,goInfo->door.lockId); +} + +inline void CheckGOLinkedTrapId(GameObjectInfo const* goInfo,uint32 dataN,uint32 N) +{ + if (GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(dataN)) + { + if (trapInfo->type!=GAMEOBJECT_TYPE_TRAP) + sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.", + goInfo->id,goInfo->type,N,dataN,dataN,GAMEOBJECT_TYPE_TRAP); + } + /* disable check for while (too many error reports baout not existed in trap templates + else + sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but trap GO (Entry %u) not exist in `gameobject_template`.", + goInfo->id,goInfo->type,N,dataN,dataN); + */ +} + +inline void CheckGOSpellId(GameObjectInfo const* goInfo,uint32 dataN,uint32 N) +{ + if (sSpellStore.LookupEntry(dataN)) + return; + + sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but Spell (Entry %u) not exist.", + goInfo->id,goInfo->type,N,dataN,dataN); +} + +inline void CheckAndFixGOChairHeightId(GameObjectInfo const* goInfo,uint32 const& dataN,uint32 N) +{ + if (dataN <= (UNIT_STAND_STATE_SIT_HIGH_CHAIR-UNIT_STAND_STATE_SIT_LOW_CHAIR) ) + return; + + sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but correct chair height in range 0..%i.", + goInfo->id,goInfo->type,N,dataN,UNIT_STAND_STATE_SIT_HIGH_CHAIR-UNIT_STAND_STATE_SIT_LOW_CHAIR); + + // prevent client and server unexpected work + const_cast<uint32&>(dataN) = 0; +} + +inline void CheckGONoDamageImmuneId(GameObjectInfo const* goInfo,uint32 dataN,uint32 N) +{ + // 0/1 correct values + if (dataN <= 1) + return; + + sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but expected boolean (0/1) noDamageImmune field value.", + goInfo->id,goInfo->type,N,dataN); +} + void ObjectMgr::LoadGameobjectInfo() { SQLGameObjectLoader loader; @@ -5256,139 +5679,104 @@ void ObjectMgr::LoadGameobjectInfo() for(uint32 id = 1; id < sGOStorage.MaxEntry; id++) { GameObjectInfo const* goInfo = sGOStorage.LookupEntry<GameObjectInfo>(id); - if(!goInfo) + if (!goInfo) continue; switch(goInfo->type) { case GAMEOBJECT_TYPE_DOOR: //0 { - if(goInfo->door.lockId) - { - if(!sLockStore.LookupEntry(goInfo->door.lockId)) - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data1=%u but lock (Id: %u) not found.", - id,goInfo->type,goInfo->door.lockId,goInfo->door.lockId); - } + if (goInfo->door.lockId) + CheckGOLockId(goInfo,goInfo->door.lockId,1); + CheckGONoDamageImmuneId(goInfo,goInfo->door.noDamageImmune,3); break; } case GAMEOBJECT_TYPE_BUTTON: //1 { - if(goInfo->button.lockId) - { - if(!sLockStore.LookupEntry(goInfo->button.lockId)) - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data1=%u but lock (Id: %u) not found.", - id,goInfo->type,goInfo->button.lockId,goInfo->button.lockId); - } + if (goInfo->button.lockId) + CheckGOLockId(goInfo,goInfo->button.lockId,1); + CheckGONoDamageImmuneId(goInfo,goInfo->button.noDamageImmune,4); + break; + } + case GAMEOBJECT_TYPE_QUESTGIVER: //2 + { + if (goInfo->questgiver.lockId) + CheckGOLockId(goInfo,goInfo->questgiver.lockId,0); + CheckGONoDamageImmuneId(goInfo,goInfo->questgiver.noDamageImmune,5); break; } case GAMEOBJECT_TYPE_CHEST: //3 { - if(goInfo->chest.lockId) - { - if(!sLockStore.LookupEntry(goInfo->chest.lockId)) - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but lock (Id: %u) not found.", - id,goInfo->type,goInfo->chest.lockId,goInfo->chest.lockId); - } - if(goInfo->chest.linkedTrapId) // linked trap - { - if(GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(goInfo->chest.linkedTrapId)) - { - if(trapInfo->type!=GAMEOBJECT_TYPE_TRAP) - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data7=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.", - id,goInfo->type,goInfo->chest.linkedTrapId,goInfo->chest.linkedTrapId,GAMEOBJECT_TYPE_TRAP); - } - /* disable check for while - else - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data2=%u but trap GO (Entry %u) not exist in `gameobject_template`.", - id,goInfo->type,goInfo->chest.linkedTrapId,goInfo->chest.linkedTrapId); - */ - } + if (goInfo->chest.lockId) + CheckGOLockId(goInfo,goInfo->chest.lockId,0); + + if (goInfo->chest.linkedTrapId) // linked trap + CheckGOLinkedTrapId(goInfo,goInfo->chest.linkedTrapId,7); break; } case GAMEOBJECT_TYPE_TRAP: //6 { - /* disable check for while - if(goInfo->trap.spellId) // spell - { - if(!sSpellStore.LookupEntry(goInfo->trap.spellId)) - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data3=%u but Spell (Entry %u) not exist.", - id,goInfo->type,goInfo->trap.spellId,goInfo->trap.spellId); - } + if (goInfo->trap.lockId) + CheckGOLockId(goInfo,goInfo->trap.lockId,0); + /* disable check for while, too many not existed spells + if (goInfo->trap.spellId) // spell + CheckGOSpellId(goInfo,goInfo->trap.spellId,3); */ break; } case GAMEOBJECT_TYPE_CHAIR: //7 - if(goInfo->chair.height > 2) - { - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data1=%u but correct chair height in range 0..2.", - id,goInfo->type,goInfo->chair.height); - - // prevent client and server unexpected work - const_cast<GameObjectInfo*>(goInfo)->chair.height = 0; - } + CheckAndFixGOChairHeightId(goInfo,goInfo->chair.height,1); break; case GAMEOBJECT_TYPE_SPELL_FOCUS: //8 { - if(goInfo->spellFocus.focusId) + if (goInfo->spellFocus.focusId) { - if(!sSpellFocusObjectStore.LookupEntry(goInfo->spellFocus.focusId)) + if (!sSpellFocusObjectStore.LookupEntry(goInfo->spellFocus.focusId)) sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but SpellFocus (Id: %u) not exist.", id,goInfo->type,goInfo->spellFocus.focusId,goInfo->spellFocus.focusId); } - if(goInfo->spellFocus.linkedTrapId) // linked trap - { - if(GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(goInfo->spellFocus.linkedTrapId)) - { - if(trapInfo->type!=GAMEOBJECT_TYPE_TRAP) - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data2=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.", - id,goInfo->type,goInfo->spellFocus.linkedTrapId,goInfo->spellFocus.linkedTrapId,GAMEOBJECT_TYPE_TRAP); - } - /* disable check for while - else - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data2=%u but trap GO (Entry %u) not exist in `gameobject_template`.", - id,goInfo->type,goInfo->spellFocus.linkedTrapId,goInfo->spellFocus.linkedTrapId); - */ - } + if (goInfo->spellFocus.linkedTrapId) // linked trap + CheckGOLinkedTrapId(goInfo,goInfo->spellFocus.linkedTrapId,2); break; } case GAMEOBJECT_TYPE_GOOBER: //10 { - if(goInfo->goober.pageId) // pageId + if (goInfo->goober.lockId) + CheckGOLockId(goInfo,goInfo->goober.lockId,0); + + if (goInfo->goober.pageId) // pageId { - if(!sPageTextStore.LookupEntry<PageText>(goInfo->goober.pageId)) + if (!sPageTextStore.LookupEntry<PageText>(goInfo->goober.pageId)) sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data7=%u but PageText (Entry %u) not exist.", id,goInfo->type,goInfo->goober.pageId,goInfo->goober.pageId); } - /* disable check for while - if(goInfo->goober.spellId) // spell - { - if(!sSpellStore.LookupEntry(goInfo->goober.spellId)) - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data2=%u but Spell (Entry %u) not exist.", - id,goInfo->type,goInfo->goober.spellId,goInfo->goober.spellId); - } + /* disable check for while, too many not existed spells + if (goInfo->goober.spellId) // spell + CheckGOSpellId(goInfo,goInfo->goober.spellId,10); */ - if(goInfo->goober.linkedTrapId) // linked trap - { - if(GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(goInfo->goober.linkedTrapId)) - { - if(trapInfo->type!=GAMEOBJECT_TYPE_TRAP) - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data12=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.", - id,goInfo->type,goInfo->goober.linkedTrapId,goInfo->goober.linkedTrapId,GAMEOBJECT_TYPE_TRAP); - } - /* disable check for while - else - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data12=%u but trap GO (Entry %u) not exist in `gameobject_template`.", - id,goInfo->type,goInfo->goober.linkedTrapId,goInfo->goober.linkedTrapId); - */ - } + CheckGONoDamageImmuneId(goInfo,goInfo->goober.noDamageImmune,11); + if (goInfo->goober.linkedTrapId) // linked trap + CheckGOLinkedTrapId(goInfo,goInfo->goober.linkedTrapId,12); + break; + } + case GAMEOBJECT_TYPE_AREADAMAGE: //12 + { + if (goInfo->areadamage.lockId) + CheckGOLockId(goInfo,goInfo->areadamage.lockId,0); + break; + } + case GAMEOBJECT_TYPE_CAMERA: //13 + { + if (goInfo->camera.lockId) + CheckGOLockId(goInfo,goInfo->camera.lockId,0); break; } case GAMEOBJECT_TYPE_MO_TRANSPORT: //15 { - if(goInfo->moTransport.taxiPathId) + if (goInfo->moTransport.taxiPathId) { - if(goInfo->moTransport.taxiPathId >= sTaxiPathNodesByPath.size() || sTaxiPathNodesByPath[goInfo->moTransport.taxiPathId].empty()) + if (goInfo->moTransport.taxiPathId >= sTaxiPathNodesByPath.size() || sTaxiPathNodesByPath[goInfo->moTransport.taxiPathId].empty()) sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but TaxiPath (Id: %u) not exist.", id,goInfo->type,goInfo->moTransport.taxiPathId,goInfo->moTransport.taxiPathId); } @@ -5396,26 +5784,41 @@ void ObjectMgr::LoadGameobjectInfo() } case GAMEOBJECT_TYPE_SUMMONING_RITUAL: //18 { - /* disabled - if(goInfo->summoningRitual.spellId) - { - if(!sSpellStore.LookupEntry(goInfo->summoningRitual.spellId)) - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data1=%u but Spell (Entry %u) not exist.", - id,goInfo->type,goInfo->summoningRitual.spellId,goInfo->summoningRitual.spellId); - } + /* disable check for while, too many not existed spells + // always must have spell + CheckGOSpellId(goInfo,goInfo->summoningRitual.spellId,1); */ break; } case GAMEOBJECT_TYPE_SPELLCASTER: //22 { - if(goInfo->spellcaster.spellId) // spell - { - if(!sSpellStore.LookupEntry(goInfo->spellcaster.spellId)) - sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data3=%u but Spell (Entry %u) not exist.", - id,goInfo->type,goInfo->spellcaster.spellId,goInfo->spellcaster.spellId); - } + // always must have spell + CheckGOSpellId(goInfo,goInfo->spellcaster.spellId,0); + break; + } + case GAMEOBJECT_TYPE_FLAGSTAND: //24 + { + if (goInfo->flagstand.lockId) + CheckGOLockId(goInfo,goInfo->flagstand.lockId,0); + CheckGONoDamageImmuneId(goInfo,goInfo->flagstand.noDamageImmune,5); + break; + } + case GAMEOBJECT_TYPE_FISHINGHOLE: //25 + { + if (goInfo->fishinghole.lockId) + CheckGOLockId(goInfo,goInfo->fishinghole.lockId,4); break; } + case GAMEOBJECT_TYPE_FLAGDROP: //26 + { + if (goInfo->flagdrop.lockId) + CheckGOLockId(goInfo,goInfo->flagdrop.lockId,0); + CheckGONoDamageImmuneId(goInfo,goInfo->flagdrop.noDamageImmune,3); + break; + } + case GAMEOBJECT_TYPE_BARBER_CHAIR: //32 + CheckAndFixGOChairHeightId(goInfo,goInfo->barberChair.chairheight,0); + break; } } @@ -5464,6 +5867,13 @@ uint32 ObjectMgr::GetBaseXP(uint32 level) return mBaseXPTable[level] ? mBaseXPTable[level] : 0; } +uint32 ObjectMgr::GetXPForLevel(uint32 level) +{ + if (level < mPlayerXPperLevel.size()) + return mPlayerXPperLevel[level]; + return 0; +} + void ObjectMgr::LoadPetNames() { uint32 count = 0; @@ -5665,6 +6075,58 @@ void ObjectMgr::LoadReputationOnKill() sLog.outString(">> Loaded %u creature award reputation definitions", count); } +void ObjectMgr::LoadPointsOfInterest() +{ + uint32 count = 0; + + // 0 1 2 3 4 5 + QueryResult *result = WorldDatabase.Query("SELECT entry, x, y, icon, flags, data, icon_name FROM points_of_interest"); + + if(!result) + { + barGoLink bar(1); + + bar.step(); + + sLog.outString(); + sLog.outErrorDb(">> Loaded 0 Points of Interest definitions. DB table `points_of_interest` is empty."); + return; + } + + barGoLink bar(result->GetRowCount()); + + do + { + Field *fields = result->Fetch(); + bar.step(); + + uint32 point_id = fields[0].GetUInt32(); + + PointOfInterest POI; + POI.x = fields[1].GetFloat(); + POI.y = fields[2].GetFloat(); + POI.icon = fields[3].GetUInt32(); + POI.flags = fields[4].GetUInt32(); + POI.data = fields[5].GetUInt32(); + POI.icon_name = fields[6].GetCppString(); + + if(!MaNGOS::IsValidMapCoord(POI.x,POI.y)) + { + sLog.outErrorDb("Table `points_of_interest` (Entry: %u) have invalid coordinates (X: %f Y: %f), ignored.",point_id,POI.x,POI.y); + continue; + } + + mPointsOfInterest[point_id] = POI; + + ++count; + } while (result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString(">> Loaded %u Points of Interest definitions", count); +} + void ObjectMgr::LoadWeatherZoneChances() { uint32 count = 0; @@ -5703,19 +6165,19 @@ void ObjectMgr::LoadWeatherZoneChances() if(wzc.data[season].rainChance > 100) { wzc.data[season].rainChance = 25; - sLog.outErrorDb("Weather for zone %u season %u has wrong rain chance > 100%",zone_id,season); + sLog.outErrorDb("Weather for zone %u season %u has wrong rain chance > 100%%",zone_id,season); } if(wzc.data[season].snowChance > 100) { wzc.data[season].snowChance = 25; - sLog.outErrorDb("Weather for zone %u season %u has wrong snow chance > 100%",zone_id,season); + sLog.outErrorDb("Weather for zone %u season %u has wrong snow chance > 100%%",zone_id,season); } if(wzc.data[season].stormChance > 100) { wzc.data[season].stormChance = 25; - sLog.outErrorDb("Weather for zone %u season %u has wrong storm chance > 100%",zone_id,season); + sLog.outErrorDb("Weather for zone %u season %u has wrong storm chance > 100%%",zone_id,season); } } @@ -5932,11 +6394,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; @@ -5945,6 +6414,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, @@ -6093,55 +6573,26 @@ int ObjectMgr::GetOrNewIndexForLocale( LocaleConstant loc ) return m_LocalForIndex.size()-1; } -void ObjectMgr::LoadBattleMastersEntry() +void ObjectMgr::LoadGameObjectForQuests() { - mBattleMastersMap.clear(); // need for reload case - - QueryResult *result = WorldDatabase.Query( "SELECT entry,bg_template FROM battlemaster_entry" ); - - uint32 count = 0; + mGameObjectForQuestSet.clear(); // need for reload case - if( !result ) + if( !sGOStorage.MaxEntry ) { barGoLink bar( 1 ); bar.step(); - sLog.outString(); - sLog.outString( ">> Loaded 0 battlemaster entries - table is empty!" ); + sLog.outString( ">> Loaded 0 GameObjects for quests" ); return; } - barGoLink bar( result->GetRowCount() ); - - do - { - ++count; - bar.step(); - - Field *fields = result->Fetch(); - - uint32 entry = fields[0].GetUInt32(); - uint32 bgTypeId = fields[1].GetUInt32(); - - mBattleMastersMap[entry] = bgTypeId; - - } while( result->NextRow() ); - - delete result; - - sLog.outString(); - sLog.outString( ">> Loaded %u battlemaster entries", count ); -} - -void ObjectMgr::LoadGameObjectForQuests() -{ - mGameObjectForQuestSet.clear(); // need for reload case - + barGoLink bar( sGOStorage.MaxEntry - 1 ); uint32 count = 0; // collect GO entries for GO that must activated for(uint32 go_entry = 1; go_entry < sGOStorage.MaxEntry; ++go_entry) { + bar.step(); GameObjectInfo const* goInfo = sGOStorage.LookupEntry<GameObjectInfo>(go_entry); if(!goInfo) continue; @@ -6176,7 +6627,7 @@ void ObjectMgr::LoadGameObjectForQuests() } sLog.outString(); - sLog.outString( ">> Loaded %u GameObject for quests", count ); + sLog.outString( ">> Loaded %u GameObjects for quests", count ); } bool ObjectMgr::LoadTrinityStrings(DatabaseType& db, char const* table, int32 min_value, int32 max_value) @@ -6202,7 +6653,7 @@ bool ObjectMgr::LoadTrinityStrings(DatabaseType& db, char const* table, int32 mi bar.step(); - sLog.outString(""); + sLog.outString(); if(min_value == MIN_TRINITY_STRING_ID) // error only in case internal strings sLog.outErrorDb(">> Loaded 0 trinity strings. DB table `%s` is empty. Cannot continue.",table); else @@ -6449,13 +6900,13 @@ bool PlayerCondition::Meets(Player const * player) const case CONDITION_ITEM: return player->HasItemCount(value1, value2); case CONDITION_ITEM_EQUIPPED: - return player->GetItemOrItemWithGemEquipped(value1) != NULL; + return player->HasItemOrGemWithIdEquipped(value1,1); case CONDITION_ZONEID: return player->GetZoneId() == value1; case CONDITION_REPUTATION_RANK: { FactionEntry const* faction = sFactionStore.LookupEntry(value1); - return faction && player->GetReputationRank(faction) >= value2; + return faction && player->GetReputationMgr().GetRank(faction) >= value2; } case CONDITION_TEAM: return player->GetTeam() == value1; @@ -6472,7 +6923,7 @@ bool PlayerCondition::Meets(Player const * player) const { Unit::AuraMap const& auras = player->GetAuras(); for(Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - if((itr->second->GetSpellProto()->Attributes & 0x1000010) && itr->second->GetSpellProto()->SpellVisual==3580) + if((itr->second->GetSpellProto()->Attributes & 0x1000010) && itr->second->GetSpellProto()->SpellVisual[0]==3580) return true; return false; } @@ -6622,7 +7073,7 @@ bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 val } case CONDITION_ACTIVE_EVENT: { - GameEvent::GameEventDataMap const& events = gameeventmgr.GetEventMap(); + GameEventMgr::GameEventDataMap const& events = gameeventmgr.GetEventMap(); if(value1 >=events.size() || !events[value1].isValid()) { sLog.outErrorDb("Active event condition requires existed event id (%u), skipped", value1); @@ -6649,7 +7100,7 @@ SkillRangeType GetSkillRangeType(SkillLineEntry const *pSkill, bool racial) return SKILL_RANGE_MONO; case SKILL_CATEGORY_ARMOR: case SKILL_CATEGORY_CLASS: - if(pSkill->id != SKILL_POISONS && pSkill->id != SKILL_LOCKPICKING) + if(pSkill->id != SKILL_LOCKPICKING) return SKILL_RANGE_MONO; else return SKILL_RANGE_LEVEL; @@ -6664,7 +7115,7 @@ SkillRangeType GetSkillRangeType(SkillLineEntry const *pSkill, bool racial) return SKILL_RANGE_MONO; default: case SKILL_CATEGORY_ATTRIBUTES: //not found in dbc - case SKILL_CATEGORY_NOT_DISPLAYED: //only GENEREC(DND) + case SKILL_CATEGORY_GENERIC: //only GENERIC(DND) return SKILL_RANGE_NONE; } } @@ -6725,11 +7176,10 @@ void ObjectMgr::LoadGameTele() ++count; } while (result->NextRow()); - delete result; sLog.outString(); - sLog.outString( ">> Loaded %u game tele's", count ); + sLog.outString( ">> Loaded %u GameTeleports", count ); } GameTele const* ObjectMgr::GetGameTele(const std::string& name) const @@ -6865,30 +7315,41 @@ void ObjectMgr::LoadTrainerSpell() continue; } - TrainerSpell* pTrainerSpell = new TrainerSpell(); - pTrainerSpell->spell = spell; - pTrainerSpell->spellcost = fields[2].GetUInt32(); - pTrainerSpell->reqskill = fields[3].GetUInt32(); - pTrainerSpell->reqskillvalue = fields[4].GetUInt32(); - pTrainerSpell->reqlevel = fields[5].GetUInt32(); + TrainerSpellData& data = m_mCacheTrainerSpellMap[entry]; - if(!pTrainerSpell->reqlevel) - pTrainerSpell->reqlevel = spellinfo->spellLevel; + TrainerSpell& trainerSpell = data.spellList[spell]; + trainerSpell.spell = spell; + trainerSpell.spellCost = fields[2].GetUInt32(); + trainerSpell.reqSkill = fields[3].GetUInt32(); + trainerSpell.reqSkillValue = fields[4].GetUInt32(); + trainerSpell.reqLevel = fields[5].GetUInt32(); + if(!trainerSpell.reqLevel) + trainerSpell.reqLevel = spellinfo->spellLevel; - TrainerSpellData& data = m_mCacheTrainerSpellMap[entry]; + // calculate learned spell for profession case when stored cast-spell + trainerSpell.learnedSpell = spell; + for(int i = 0; i <3; ++i) + { + if(spellinfo->Effect[i] != SPELL_EFFECT_LEARN_SPELL) + continue; + if(SpellMgr::IsProfessionOrRidingSpell(spellinfo->EffectTriggerSpell[i])) + { + trainerSpell.learnedSpell = spellinfo->EffectTriggerSpell[i]; + break; + } + } - if(SpellMgr::IsProfessionSpell(spell)) + if(SpellMgr::IsProfessionSpell(trainerSpell.learnedSpell)) data.trainerType = 2; - data.spellList.push_back(pTrainerSpell); ++count; } while (result->NextRow()); delete result; sLog.outString(); - sLog.outString( ">> Loaded Trainers %d", count ); + sLog.outString( ">> Loaded %d Trainers", count ); } void ObjectMgr::LoadVendors() @@ -7166,16 +7627,30 @@ void ObjectMgr::LoadScriptNames() "SELECT DISTINCT(ScriptName) FROM areatrigger_scripts WHERE ScriptName <> '' " "UNION " "SELECT DISTINCT(script) FROM instance_template WHERE script <> ''"); - if(result) + + if( !result ) { - do - { - m_scriptNames.push_back((*result)[0].GetString()); - } while (result->NextRow()); - delete result; + barGoLink bar( 1 ); + bar.step(); + sLog.outString(); + sLog.outErrorDb(">> Loaded empty set of Script Names!"); + return; } + barGoLink bar( result->GetRowCount() ); + uint32 count = 0; + + do + { + bar.step(); + m_scriptNames.push_back((*result)[0].GetString()); + ++count; + } while (result->NextRow()); + delete result; + std::sort(m_scriptNames.begin(), m_scriptNames.end()); + sLog.outString(); + sLog.outString( ">> Loaded %d Script Names", count ); } uint32 ObjectMgr::GetScriptId(const char *name) @@ -7268,16 +7743,6 @@ CreatureInfo const *GetCreatureInfo(uint32 id) return objmgr.GetCreatureTemplate(id); } -CreatureInfo const* GetCreatureTemplateStore(uint32 entry) -{ - return sCreatureStorage.LookupEntry<CreatureInfo>(entry); -} - -Quest const* GetQuestTemplateStore(uint32 entry) -{ - return objmgr.GetQuestTemplate(entry); -} - void ObjectMgr::LoadTransportEvents() { @@ -7314,3 +7779,12 @@ void ObjectMgr::LoadTransportEvents() delete result; } +CreatureInfo const* GetCreatureTemplateStore(uint32 entry) +{ + return sCreatureStorage.LookupEntry<CreatureInfo>(entry); +} + +Quest const* GetQuestTemplateStore(uint32 entry) +{ + return objmgr.GetQuestTemplate(entry); +} diff --git a/src/game/ObjectMgr.h b/src/game/ObjectMgr.h index 954bb6dbe61..56e1cbc2366 100644 --- a/src/game/ObjectMgr.h +++ b/src/game/ObjectMgr.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -144,6 +144,7 @@ typedef UNORDERED_MAP<uint32,NpcTextLocale> NpcTextLocaleMap; typedef UNORDERED_MAP<uint32,PageTextLocale> PageTextLocaleMap; typedef UNORDERED_MAP<uint32,TrinityStringLocale> TrinityStringLocaleMap; typedef UNORDERED_MAP<uint32,NpcOptionLocale> NpcOptionLocaleMap; +typedef UNORDERED_MAP<uint32,PointOfInterestLocale> PointOfInterestLocaleMap; typedef std::multimap<uint32,uint32> QuestRelations; @@ -170,6 +171,17 @@ struct ReputationOnKillEntry bool team_dependent; }; +struct PointOfInterest +{ + uint32 entry; + float x; + float y; + uint32 icon; + uint32 flags; + uint32 data; + std::string icon_name; +}; + struct PetCreateSpellEntry { uint32 spellid[4]; @@ -310,6 +322,7 @@ class ObjectMgr typedef UNORDERED_MAP<uint32, AccessRequirement> AccessRequirementMap; typedef UNORDERED_MAP<uint32, ReputationOnKillEntry> RepOnKillMap; + typedef UNORDERED_MAP<uint32, PointOfInterest> PointOfInterestMap; typedef UNORDERED_MAP<uint32, WeatherZoneChances> WeatherZoneMap; @@ -332,13 +345,13 @@ class ObjectMgr void RemoveGroup(Group* group) { mGroupSet.erase( group ); } Guild* GetGuildByLeader(uint64 const&guid) const; - Guild* GetGuildById(const uint32 GuildId) const; + Guild* GetGuildById(uint32 GuildId) const; Guild* GetGuildByName(const std::string& guildname) const; - std::string GetGuildNameById(const uint32 GuildId) const; + std::string GetGuildNameById(uint32 GuildId) const; void AddGuild(Guild* guild); void RemoveGuild(uint32 Id); - ArenaTeam* GetArenaTeamById(const uint32 arenateamid) const; + ArenaTeam* GetArenaTeamById(uint32 arenateamid) const; ArenaTeam* GetArenaTeamByName(const std::string& arenateamname) const; ArenaTeam* GetArenaTeamByCaptain(uint64 const& guid) const; void AddArenaTeam(ArenaTeam* arenaTeam); @@ -393,7 +406,7 @@ class ObjectMgr uint32 GetPlayerAccountIdByGUID(const uint64 &guid) const; uint32 GetPlayerAccountIdByPlayerName(const std::string& name) const; - uint32 GetNearestTaxiNode( float x, float y, float z, uint32 mapid ); + uint32 GetNearestTaxiNode( float x, float y, float z, uint32 mapid, uint32 team ); void GetTaxiPath( uint32 source, uint32 destination, uint32 &path, uint32 &cost); uint16 GetTaxiMount( uint32 id, uint32 team ); void GetTaxiPathNodes( uint32 path, Path &pathnodes, std::vector<uint32>& mapIds ); @@ -413,26 +426,19 @@ class ObjectMgr return itr->second; return 0; } - bool IsTavernAreaTrigger(uint32 Trigger_ID) const { return mTavernAreaTriggerSet.count(Trigger_ID) != 0; } - bool IsGameObjectForQuests(uint32 entry) const { return mGameObjectForQuestSet.count(entry) != 0; } - bool IsGuildVaultGameObject(Player *player, uint64 guid) const + bool IsTavernAreaTrigger(uint32 Trigger_ID) const { - if(GameObject *go = ObjectAccessor::GetGameObject(*player, guid)) - if(go->GetGoType() == GAMEOBJECT_TYPE_GUILD_BANK) - return true; - return false; + return mTavernAreaTriggerSet.find(Trigger_ID) != mTavernAreaTriggerSet.end(); } - uint32 GetBattleMasterBG(uint32 entry) const + bool IsGameObjectForQuests(uint32 entry) const { - BattleMastersMap::const_iterator itr = mBattleMastersMap.find(entry); - if(itr != mBattleMastersMap.end()) - return itr->second; - return 2; //BATTLEGROUND_WS - i will not add include only for constant usage! + return mGameObjectForQuestSet.find(entry) != mGameObjectForQuestSet.end(); } - void AddGossipText(GossipText *pGText); - GossipText *GetGossipText(uint32 Text_ID); + bool IsGameObjectOfTypeInRange(Player *player, uint64 guid, GameobjectTypes type) const; + + GossipText const* GetGossipText(uint32 Text_ID) const; WorldSafeLocsEntry const *GetClosestGraveYard(float x, float y, float z, uint32 MapId, uint32 team); bool AddGraveYardLink(uint32 id, uint32 zone, uint32 team, bool inDB = true); @@ -457,6 +463,7 @@ class ObjectMgr } AreaTrigger const* GetGoBackTrigger(uint32 Map) const; + AreaTrigger const* GetMapEntranceTrigger(uint32 Map) const; uint32 GetAreaTriggerScriptId(uint32 trigger_id); @@ -468,6 +475,14 @@ class ObjectMgr return NULL; } + PointOfInterest const* GetPointOfInterest(uint32 id) const + { + PointOfInterestMap::const_iterator itr = mPointsOfInterest.find(id); + if(itr != mPointsOfInterest.end()) + return &itr->second; + return NULL; + } + PetCreateSpellEntry const* GetPetCreateSpellEntry(uint32 id) const { PetCreateSpellMap::const_iterator itr = mPetCreateSpell.find(id); @@ -526,6 +541,7 @@ class ObjectMgr void LoadNpcTextLocales(); void LoadPageTextLocales(); void LoadNpcOptionLocales(); + void LoadPointOfInterestLocales(); void LoadInstanceTemplate(); void LoadGossipText(); @@ -535,7 +551,6 @@ class ObjectMgr void LoadQuestAreaTriggers(); void LoadAreaTriggerScripts(); void LoadTavernAreaTriggers(); - void LoadBattleMastersEntry(); void LoadGameObjectForQuests(); void LoadItemTexts(); @@ -550,6 +565,7 @@ class ObjectMgr void LoadFishingBaseSkillLevel(); void LoadReputationOnKill(); + void LoadPointsOfInterest(); void LoadWeatherZoneChances(); void LoadGameTele(); @@ -561,6 +577,7 @@ class ObjectMgr std::string GeneratePetName(uint32 entry); uint32 GetBaseXP(uint32 level); + uint32 GetXPForLevel(uint32 level); int32 GetFishingBaseSkillLevel(uint32 entry) const { @@ -572,12 +589,12 @@ class ObjectMgr void SetHighestGuids(); uint32 GenerateLowGuid(HighGuid guidhigh); + uint32 GenerateArenaTeamId(); uint32 GenerateAuctionID(); - uint32 GenerateMailID(); + uint32 GenerateGuildId(); uint32 GenerateItemTextID(); + uint32 GenerateMailID(); uint32 GeneratePetNumber(); - uint32 GenerateArenaTeamId(); - uint32 GenerateGuildId(); void LoadPlayerInfoInCache(); PCachePlayerInfo GetPlayerInfoFromCache(uint32 unPlayerGuid) const; @@ -660,6 +677,12 @@ class ObjectMgr if(itr==mNpcOptionLocaleMap.end()) return NULL; return &itr->second; } + PointOfInterestLocale const* GetPointOfInterestLocale(uint32 poi_id) const + { + PointOfInterestLocaleMap::const_iterator itr = mPointOfInterestLocaleMap.find(poi_id); + if(itr==mPointOfInterestLocaleMap.end()) return NULL; + return &itr->second; + } GameObjectData const* GetGOData(uint32 guid) const { @@ -698,10 +721,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 ); @@ -776,20 +796,23 @@ class ObjectMgr ScriptNameMap &GetScriptNames() { return m_scriptNames; } const char * GetScriptName(uint32 id) { return id < m_scriptNames.size() ? m_scriptNames[id].c_str() : ""; } uint32 GetScriptId(const char *name); + + int GetOrNewIndexForLocale(LocaleConstant loc); protected: // first free id for selected id type - uint32 m_auctionid; - uint32 m_mailid; - uint32 m_ItemTextId; uint32 m_arenaTeamId; + uint32 m_auctionid; uint32 m_guildId; + uint32 m_ItemTextId; + uint32 m_mailid; uint32 m_hiPetNumber; // first free low guid for seelcted guid type uint32 m_hiCharGuid; uint32 m_hiCreatureGuid; uint32 m_hiPetGuid; + uint32 m_hiVehicleGuid; uint32 m_hiItemGuid; uint32 m_hiGoGuid; uint32 m_hiDoGuid; @@ -797,9 +820,8 @@ class ObjectMgr QuestMap mQuestTemplates; - typedef UNORDERED_MAP<uint32, GossipText*> GossipTextMap; + typedef UNORDERED_MAP<uint32, GossipText> GossipTextMap; typedef UNORDERED_MAP<uint32, uint32> QuestAreaTriggerMap; - typedef UNORDERED_MAP<uint32, uint32> BattleMastersMap; typedef UNORDERED_MAP<uint32, std::string> ItemTextMap; typedef std::set<uint32> TavernAreaTriggerSet; typedef std::set<uint32> GameObjectForQuestSet; @@ -808,12 +830,9 @@ class ObjectMgr GuildMap mGuildMap; ArenaTeamMap mArenaTeamMap; - ItemMap mItems; - ItemTextMap mItemTexts; QuestAreaTriggerMap mQuestAreaTriggerMap; - BattleMastersMap mBattleMastersMap; TavernAreaTriggerSet mTavernAreaTriggerSet; GameObjectForQuestSet mGameObjectForQuestSet; GossipTextMap mGossipText; @@ -823,12 +842,14 @@ class ObjectMgr RepOnKillMap mRepOnKill; + PointOfInterestMap mPointsOfInterest; + WeatherZoneMap mWeatherZoneMap; 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; @@ -843,7 +864,6 @@ class ObjectMgr typedef std::vector<LocaleConstant> LocalForIndex; LocalForIndex m_LocalForIndex; - int GetOrNewIndexForLocale(LocaleConstant loc); int DBCLocaleIndex; @@ -862,6 +882,9 @@ class ObjectMgr void BuildPlayerLevelInfo(uint8 race, uint8 class_, uint8 level, PlayerLevelInfo* plinfo) const; PlayerInfo playerInfo[MAX_RACES][MAX_CLASSES]; + typedef std::vector<uint32> PlayerXPperLevel; // [level] + PlayerXPperLevel mPlayerXPperLevel; + typedef std::map<uint32,uint32> BaseXPMap; // [area level][base xp] BaseXPMap mBaseXPTable; @@ -883,6 +906,7 @@ class ObjectMgr PageTextLocaleMap mPageTextLocaleMap; TrinityStringLocaleMap mTrinityStringLocaleMap; NpcOptionLocaleMap mNpcOptionLocaleMap; + PointOfInterestLocaleMap mPointOfInterestLocaleMap; RespawnTimes mCreatureRespawnTimes; RespawnTimes mGORespawnTimes; diff --git a/src/game/ObjectPosSelector.cpp b/src/game/ObjectPosSelector.cpp new file mode 100644 index 00000000000..899dfec3fdb --- /dev/null +++ b/src/game/ObjectPosSelector.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ObjectPosSelector.h" + +ObjectPosSelector::ObjectPosSelector(float x,float y,float size,float dist) +: m_center_x(x),m_center_y(y),m_size(size),m_dist(dist) +{ + m_anglestep = acos(m_dist/(m_dist+2*m_size)); + + m_nextUsedPos[USED_POS_PLUS] = m_UsedPosLists[USED_POS_PLUS].end(); + m_nextUsedPos[USED_POS_MINUS] = m_UsedPosLists[USED_POS_MINUS].end(); + + m_smallStepAngle[USED_POS_PLUS] = 0; + m_smallStepAngle[USED_POS_MINUS] = 0; + + m_smallStepOk[USED_POS_PLUS] = false; + m_smallStepOk[USED_POS_MINUS] = false; + + m_smallStepNextUsedPos[USED_POS_PLUS] = NULL; + m_smallStepNextUsedPos[USED_POS_MINUS] = NULL; +} + +ObjectPosSelector::UsedPosList::value_type const* ObjectPosSelector::nextUsedPos(UsedPosType uptype) +{ + UsedPosList::const_iterator itr = m_nextUsedPos[uptype]; + if(itr!=m_UsedPosLists[uptype].end()) + ++itr; + + if(itr==m_UsedPosLists[uptype].end()) + { + if(!m_UsedPosLists[~uptype].empty()) + return &*m_UsedPosLists[~uptype].rbegin(); + else + return NULL; + } + else + return &*itr; +} + +void ObjectPosSelector::AddUsedPos(float size,float angle,float dist) +{ + if(angle>=0) + m_UsedPosLists[USED_POS_PLUS].insert(UsedPosList::value_type(angle,UsedPos(1.0,size,dist))); + else + m_UsedPosLists[USED_POS_MINUS].insert(UsedPosList::value_type(-angle,UsedPos(-1.0,size,dist))); +} + +void ObjectPosSelector::InitializeAngle() +{ + m_nextUsedPos[USED_POS_PLUS] = m_UsedPosLists[USED_POS_PLUS].begin(); + m_nextUsedPos[USED_POS_MINUS] = m_UsedPosLists[USED_POS_MINUS].begin(); + + m_smallStepAngle[USED_POS_PLUS] = 0; + m_smallStepAngle[USED_POS_MINUS] = 0; + + m_smallStepOk[USED_POS_PLUS] = true; + m_smallStepOk[USED_POS_MINUS] = true; +} + +bool ObjectPosSelector::FirstAngle(float& angle) +{ + if(m_UsedPosLists[USED_POS_PLUS].empty() && !m_UsedPosLists[USED_POS_MINUS].empty() ) + return NextAngleFor(*m_UsedPosLists[USED_POS_MINUS].begin(),1.0,USED_POS_PLUS,angle); + else if(m_UsedPosLists[USED_POS_MINUS].empty() && !m_UsedPosLists[USED_POS_PLUS].empty() ) + return NextAngleFor(*m_UsedPosLists[USED_POS_PLUS].begin(),-1.0,USED_POS_MINUS,angle); + + return false; +} + +bool ObjectPosSelector::NextAngle(float& angle) +{ + while(m_nextUsedPos[USED_POS_PLUS]!=m_UsedPosLists[USED_POS_PLUS].end() || + m_nextUsedPos[USED_POS_MINUS]!=m_UsedPosLists[USED_POS_MINUS].end() || + m_smallStepOk[USED_POS_PLUS] || m_smallStepOk[USED_POS_MINUS] ) + { + // calculate next possible angle + if(NextPosibleAngle(angle)) + return true; + } + + return false; +} + +bool ObjectPosSelector::NextUsedAngle(float& angle) +{ + while(m_nextUsedPos[USED_POS_PLUS]!=m_UsedPosLists[USED_POS_PLUS].end() || + m_nextUsedPos[USED_POS_MINUS]!=m_UsedPosLists[USED_POS_MINUS].end() ) + { + // calculate next possible angle + if(!NextPosibleAngle(angle)) + return true; + } + + return false; +} + +bool ObjectPosSelector::NextPosibleAngle( float& angle ) +{ + // ++ direction less updated + if( m_nextUsedPos[USED_POS_PLUS]!=m_UsedPosLists[USED_POS_PLUS].end() && + (m_nextUsedPos[USED_POS_MINUS]==m_UsedPosLists[USED_POS_MINUS].end() || m_nextUsedPos[USED_POS_PLUS]->first <= m_nextUsedPos[USED_POS_MINUS]->first) ) + { + bool ok; + if(m_smallStepOk[USED_POS_PLUS]) + ok = NextSmallStepAngle(1.0,USED_POS_PLUS,angle); + else + ok = NextAngleFor(*m_nextUsedPos[USED_POS_PLUS],1.0,USED_POS_PLUS,angle); + + if(!ok) + ++m_nextUsedPos[USED_POS_PLUS]; // increase. only at fail (original or checked) + return ok; + } + // -- direction less updated + else if( m_nextUsedPos[USED_POS_MINUS]!=m_UsedPosLists[USED_POS_MINUS].end()) + { + bool ok; + if(m_smallStepOk[USED_POS_MINUS]) + ok = NextSmallStepAngle(-1.0,USED_POS_MINUS,angle); + else + ok = NextAngleFor(*m_nextUsedPos[USED_POS_MINUS],-1.0,USED_POS_MINUS,angle); + + if(!ok) + ++m_nextUsedPos[USED_POS_MINUS]; + return ok; + } + else // both list empty + { + if( m_smallStepOk[USED_POS_PLUS] && (!m_smallStepOk[USED_POS_MINUS] || m_smallStepAngle[USED_POS_PLUS] <= m_smallStepAngle[USED_POS_MINUS]) ) + { + return NextSmallStepAngle(1.0,USED_POS_PLUS,angle); + } + // -- direction less updated + else if( m_smallStepOk[USED_POS_MINUS] ) + { + return NextSmallStepAngle(-1.0,USED_POS_MINUS,angle); + } + } + + // no angles + return false; +} diff --git a/src/game/ObjectPosSelector.h b/src/game/ObjectPosSelector.h new file mode 100644 index 00000000000..84050611121 --- /dev/null +++ b/src/game/ObjectPosSelector.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _OBJECT_POS_SELECTOR_H +#define _OBJECT_POS_SELECTOR_H + +#include<Common.h> + +#include<map> + +enum UsedPosType { USED_POS_PLUS, USED_POS_MINUS }; + +inline UsedPosType operator ~(UsedPosType uptype) +{ + return uptype==USED_POS_PLUS ? USED_POS_MINUS : USED_POS_PLUS; +} + +struct ObjectPosSelector +{ + struct UsedPos + { + UsedPos(float sign_, float size_,float dist_) : sign(sign_), size(size_),dist(dist_) {} + + float sign; + + float size; // size of point + float dist; // dist to central point (including central point size) + }; + + typedef std::multimap<float,UsedPos> UsedPosList; // abs(angle)->Node + + ObjectPosSelector(float x,float y,float size,float dist); + + void AddUsedPos(float size,float angle,float dist); + void InitializeAngle(); + + bool FirstAngle(float& angle); + bool NextAngle(float& angle); + bool NextUsedAngle(float& angle); + + bool NextPosibleAngle( float& angle ); + + bool CheckAngle(UsedPosList::value_type const& nextUsedPos, float sign, float angle ) const + { + float angle_step2 = GetAngle(nextUsedPos.second); + + float next_angle = nextUsedPos.first; + if(nextUsedPos.second.sign * sign < 0) // last node from diff. list (-pi+alpha) + next_angle = 2*M_PI-next_angle; // move to positive + + return fabs(angle)+angle_step2 <= next_angle; + } + + bool CheckOriginal() const + { + return (m_UsedPosLists[USED_POS_PLUS].empty() || CheckAngle( *m_UsedPosLists[USED_POS_PLUS].begin(),1.0,0)) && + (m_UsedPosLists[USED_POS_MINUS].empty() || CheckAngle( *m_UsedPosLists[USED_POS_MINUS].begin(),-1.0,0)); + } + + bool IsNonBalanced() const { return m_UsedPosLists[USED_POS_PLUS].empty() != m_UsedPosLists[USED_POS_MINUS].empty(); } + + bool NextAngleFor( UsedPosList::value_type const& usedPos, float sign, UsedPosType uptype, float &angle ) + { + float angle_step = GetAngle(usedPos.second); + + // next possible angle + angle = usedPos.first * usedPos.second.sign + angle_step * sign; + + UsedPosList::value_type const* nextNode = nextUsedPos(uptype); + if(nextNode) + { + // if next node permit use selected angle, then do it + if(!CheckAngle(*nextNode, sign, angle)) + { + m_smallStepOk[uptype] = false; + return false; + } + } + + // possible more points + m_smallStepOk[uptype] = true; + m_smallStepAngle[uptype] = angle; + m_smallStepNextUsedPos[uptype] = nextNode; + + return true; + } + + bool NextSmallStepAngle( float sign, UsedPosType uptype, float &angle ) + { + // next possible angle + angle = m_smallStepAngle[uptype] + m_anglestep * sign; + + if(fabs(angle) > M_PI) + { + m_smallStepOk[uptype] = false; + return false; + } + + if(m_smallStepNextUsedPos[uptype]) + { + if(fabs(angle) >= m_smallStepNextUsedPos[uptype]->first) + { + m_smallStepOk[uptype] = false; + return false; + } + + // if next node permit use selected angle, then do it + if(!CheckAngle(*m_smallStepNextUsedPos[uptype], sign, angle)) + { + m_smallStepOk[uptype] = false; + return false; + } + } + + // possible more points + m_smallStepAngle[uptype] = angle; + return true; + } + + // next used post for m_nextUsedPos[uptype] + UsedPosList::value_type const* nextUsedPos(UsedPosType uptype); + + // angle from used pos to next possible free pos + float GetAngle(UsedPos const& usedPos) const { return acos(m_dist/(usedPos.dist+usedPos.size+m_size)); } + + float m_center_x; + float m_center_y; + float m_size; // size of object in center + float m_dist; // distance for searching pos (including central object size) + float m_anglestep; + + UsedPosList m_UsedPosLists[2]; + UsedPosList::const_iterator m_nextUsedPos[2]; + + // field for small step from first after next used pos until next pos + float m_smallStepAngle[2]; + bool m_smallStepOk[2]; + UsedPosList::value_type const* m_smallStepNextUsedPos[2]; +}; +#endif diff --git a/src/game/Opcodes.cpp b/src/game/Opcodes.cpp index eebe4343797..638d54236d1 100644 --- a/src/game/Opcodes.cpp +++ b/src/game/Opcodes.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,1065 +28,1202 @@ /// Correspondence between opcodes and their names OpcodeHandler opcodeTable[NUM_MSG_TYPES] = { - /*0x000*/ { "MSG_NULL_ACTION", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x001*/ { "CMSG_BOOTME", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x002*/ { "CMSG_DBLOOKUP", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x003*/ { "SMSG_DBLOOKUP", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x004*/ { "CMSG_QUERY_OBJECT_POSITION", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x005*/ { "SMSG_QUERY_OBJECT_POSITION", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x006*/ { "CMSG_QUERY_OBJECT_ROTATION", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x007*/ { "SMSG_QUERY_OBJECT_ROTATION", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x008*/ { "CMSG_WORLD_TELEPORT", STATUS_LOGGEDIN, &WorldSession::HandleWorldTeleportOpcode }, - /*0x009*/ { "CMSG_TELEPORT_TO_UNIT", STATUS_LOGGEDIN, &WorldSession::Handle_NULL }, - /*0x00A*/ { "CMSG_ZONE_MAP", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x00B*/ { "SMSG_ZONE_MAP", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x00C*/ { "CMSG_DEBUG_CHANGECELLZONE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x00D*/ { "CMSG_EMBLAZON_TABARD_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x00E*/ { "CMSG_UNEMBLAZON_TABARD_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x00F*/ { "CMSG_RECHARGE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x010*/ { "CMSG_LEARN_SPELL", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x011*/ { "CMSG_CREATEMONSTER", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x012*/ { "CMSG_DESTROYMONSTER", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x013*/ { "CMSG_CREATEITEM", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x014*/ { "CMSG_CREATEGAMEOBJECT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x015*/ { "SMSG_CHECK_FOR_BOTS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x016*/ { "CMSG_MAKEMONSTERATTACKGUID", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x017*/ { "CMSG_BOT_DETECTED2", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x018*/ { "CMSG_FORCEACTION", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x019*/ { "CMSG_FORCEACTIONONOTHER", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x01A*/ { "CMSG_FORCEACTIONSHOW", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x01B*/ { "SMSG_FORCEACTIONSHOW", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x01C*/ { "CMSG_PETGODMODE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x01D*/ { "SMSG_PETGODMODE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x01E*/ { "SMSG_DEBUGINFOSPELLMISS_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x01F*/ { "CMSG_WEATHER_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x020*/ { "CMSG_UNDRESSPLAYER", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x021*/ { "CMSG_BEASTMASTER", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x022*/ { "CMSG_GODMODE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x023*/ { "SMSG_GODMODE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x024*/ { "CMSG_CHEAT_SETMONEY", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x025*/ { "CMSG_LEVEL_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x026*/ { "CMSG_PET_LEVEL_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x027*/ { "CMSG_SET_WORLDSTATE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x028*/ { "CMSG_COOLDOWN_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x029*/ { "CMSG_USE_SKILL_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x02A*/ { "CMSG_FLAG_QUEST", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x02B*/ { "CMSG_FLAG_QUEST_FINISH", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x02C*/ { "CMSG_CLEAR_QUEST", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x02D*/ { "CMSG_SEND_EVENT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x02E*/ { "CMSG_DEBUG_AISTATE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x02F*/ { "SMSG_DEBUG_AISTATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x030*/ { "CMSG_DISABLE_PVP_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x031*/ { "CMSG_ADVANCE_SPAWN_TIME", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x032*/ { "CMSG_PVP_PORT_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x033*/ { "CMSG_AUTH_SRP6_BEGIN", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x034*/ { "CMSG_AUTH_SRP6_PROOF", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x035*/ { "CMSG_AUTH_SRP6_RECODE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x036*/ { "CMSG_CHAR_CREATE", STATUS_AUTHED, &WorldSession::HandleCharCreateOpcode }, - /*0x037*/ { "CMSG_CHAR_ENUM", STATUS_AUTHED, &WorldSession::HandleCharEnumOpcode }, - /*0x038*/ { "CMSG_CHAR_DELETE", STATUS_AUTHED, &WorldSession::HandleCharDeleteOpcode }, - /*0x039*/ { "SMSG_AUTH_SRP6_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x03A*/ { "SMSG_CHAR_CREATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x03B*/ { "SMSG_CHAR_ENUM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x03C*/ { "SMSG_CHAR_DELETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x03D*/ { "CMSG_PLAYER_LOGIN", STATUS_AUTHED, &WorldSession::HandlePlayerLoginOpcode }, - /*0x03E*/ { "SMSG_NEW_WORLD", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x03F*/ { "SMSG_TRANSFER_PENDING", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x040*/ { "SMSG_TRANSFER_ABORTED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x041*/ { "SMSG_CHARACTER_LOGIN_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x042*/ { "SMSG_LOGIN_SETTIMESPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x043*/ { "SMSG_GAMETIME_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x044*/ { "CMSG_GAMETIME_SET", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x045*/ { "SMSG_GAMETIME_SET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x046*/ { "CMSG_GAMESPEED_SET", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x047*/ { "SMSG_GAMESPEED_SET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x048*/ { "CMSG_SERVERTIME", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x049*/ { "SMSG_SERVERTIME", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x04A*/ { "CMSG_PLAYER_LOGOUT", STATUS_LOGGEDIN, &WorldSession::HandlePlayerLogoutOpcode }, - /*0x04B*/ { "CMSG_LOGOUT_REQUEST", STATUS_LOGGEDIN, &WorldSession::HandleLogoutRequestOpcode }, - /*0x04C*/ { "SMSG_LOGOUT_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x04D*/ { "SMSG_LOGOUT_COMPLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x04E*/ { "CMSG_LOGOUT_CANCEL", STATUS_LOGGEDIN, &WorldSession::HandleLogoutCancelOpcode }, - /*0x04F*/ { "SMSG_LOGOUT_CANCEL_ACK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x050*/ { "CMSG_NAME_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleNameQueryOpcode }, - /*0x051*/ { "SMSG_NAME_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x052*/ { "CMSG_PET_NAME_QUERY", STATUS_LOGGEDIN, &WorldSession::HandlePetNameQuery }, - /*0x053*/ { "SMSG_PET_NAME_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x054*/ { "CMSG_GUILD_QUERY", STATUS_AUTHED, &WorldSession::HandleGuildQueryOpcode }, - /*0x055*/ { "SMSG_GUILD_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x056*/ { "CMSG_ITEM_QUERY_SINGLE", STATUS_LOGGEDIN, &WorldSession::HandleItemQuerySingleOpcode }, - /*0x057*/ { "CMSG_ITEM_QUERY_MULTIPLE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x058*/ { "SMSG_ITEM_QUERY_SINGLE_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x059*/ { "SMSG_ITEM_QUERY_MULTIPLE_RESPONSE",STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x05A*/ { "CMSG_PAGE_TEXT_QUERY", STATUS_LOGGEDIN, &WorldSession::HandlePageQueryOpcode }, - /*0x05B*/ { "SMSG_PAGE_TEXT_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x05C*/ { "CMSG_QUEST_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleQuestQueryOpcode }, - /*0x05D*/ { "SMSG_QUEST_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x05E*/ { "CMSG_GAMEOBJECT_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleGameObjectQueryOpcode }, - /*0x05F*/ { "SMSG_GAMEOBJECT_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x060*/ { "CMSG_CREATURE_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleCreatureQueryOpcode }, - /*0x061*/ { "SMSG_CREATURE_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x062*/ { "CMSG_WHO", STATUS_LOGGEDIN, &WorldSession::HandleWhoOpcode }, - /*0x063*/ { "SMSG_WHO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x064*/ { "CMSG_WHOIS", STATUS_LOGGEDIN, &WorldSession::HandleWhoisOpcode }, - /*0x065*/ { "SMSG_WHOIS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x066*/ { "CMSG_CONTACT_LIST", STATUS_LOGGEDIN, &WorldSession::HandleFriendListOpcode }, - /*0x067*/ { "SMSG_CONTACT_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x068*/ { "SMSG_FRIEND_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x069*/ { "CMSG_ADD_FRIEND", STATUS_LOGGEDIN, &WorldSession::HandleAddFriendOpcode }, - /*0x06A*/ { "CMSG_DEL_FRIEND", STATUS_LOGGEDIN, &WorldSession::HandleDelFriendOpcode }, - /*0x06B*/ { "CMSG_SET_CONTACT_NOTES", STATUS_LOGGEDIN, &WorldSession::HandleSetFriendNoteOpcode }, - /*0x06C*/ { "CMSG_ADD_IGNORE", STATUS_LOGGEDIN, &WorldSession::HandleAddIgnoreOpcode }, - /*0x06D*/ { "CMSG_DEL_IGNORE", STATUS_LOGGEDIN, &WorldSession::HandleDelIgnoreOpcode }, - /*0x06E*/ { "CMSG_GROUP_INVITE", STATUS_LOGGEDIN, &WorldSession::HandleGroupInviteOpcode }, - /*0x06F*/ { "SMSG_GROUP_INVITE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x070*/ { "CMSG_GROUP_CANCEL", STATUS_LOGGEDIN, &WorldSession::Handle_Deprecated }, - /*0x071*/ { "SMSG_GROUP_CANCEL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x072*/ { "CMSG_GROUP_ACCEPT", STATUS_LOGGEDIN, &WorldSession::HandleGroupAcceptOpcode }, - /*0x073*/ { "CMSG_GROUP_DECLINE", STATUS_LOGGEDIN, &WorldSession::HandleGroupDeclineOpcode }, - /*0x074*/ { "SMSG_GROUP_DECLINE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x075*/ { "CMSG_GROUP_UNINVITE", STATUS_LOGGEDIN, &WorldSession::HandleGroupUninviteNameOpcode }, - /*0x076*/ { "CMSG_GROUP_UNINVITE_GUID", STATUS_LOGGEDIN, &WorldSession::HandleGroupUninviteGuidOpcode }, - /*0x077*/ { "SMSG_GROUP_UNINVITE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x078*/ { "CMSG_GROUP_SET_LEADER", STATUS_LOGGEDIN, &WorldSession::HandleGroupSetLeaderOpcode }, - /*0x079*/ { "SMSG_GROUP_SET_LEADER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x07A*/ { "CMSG_LOOT_METHOD", STATUS_LOGGEDIN, &WorldSession::HandleLootMethodOpcode }, - /*0x07B*/ { "CMSG_GROUP_DISBAND", STATUS_LOGGEDIN, &WorldSession::HandleGroupLeaveOpcode }, - /*0x07C*/ { "SMSG_GROUP_DESTROYED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x07D*/ { "SMSG_GROUP_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x07E*/ { "SMSG_PARTY_MEMBER_STATS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x07F*/ { "SMSG_PARTY_COMMAND_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x080*/ { "UMSG_UPDATE_GROUP_MEMBERS", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x081*/ { "CMSG_GUILD_CREATE", STATUS_LOGGEDIN, &WorldSession::HandleGuildCreateOpcode }, - /*0x082*/ { "CMSG_GUILD_INVITE", STATUS_LOGGEDIN, &WorldSession::HandleGuildInviteOpcode }, - /*0x083*/ { "SMSG_GUILD_INVITE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x084*/ { "CMSG_GUILD_ACCEPT", STATUS_LOGGEDIN, &WorldSession::HandleGuildAcceptOpcode }, - /*0x085*/ { "CMSG_GUILD_DECLINE", STATUS_LOGGEDIN, &WorldSession::HandleGuildDeclineOpcode }, - /*0x086*/ { "SMSG_GUILD_DECLINE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x087*/ { "CMSG_GUILD_INFO", STATUS_LOGGEDIN, &WorldSession::HandleGuildInfoOpcode }, - /*0x088*/ { "SMSG_GUILD_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x089*/ { "CMSG_GUILD_ROSTER", STATUS_LOGGEDIN, &WorldSession::HandleGuildRosterOpcode }, - /*0x08A*/ { "SMSG_GUILD_ROSTER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x08B*/ { "CMSG_GUILD_PROMOTE", STATUS_LOGGEDIN, &WorldSession::HandleGuildPromoteOpcode }, - /*0x08C*/ { "CMSG_GUILD_DEMOTE", STATUS_LOGGEDIN, &WorldSession::HandleGuildDemoteOpcode }, - /*0x08D*/ { "CMSG_GUILD_LEAVE", STATUS_LOGGEDIN, &WorldSession::HandleGuildLeaveOpcode }, - /*0x08E*/ { "CMSG_GUILD_REMOVE", STATUS_LOGGEDIN, &WorldSession::HandleGuildRemoveOpcode }, - /*0x08F*/ { "CMSG_GUILD_DISBAND", STATUS_LOGGEDIN, &WorldSession::HandleGuildDisbandOpcode }, - /*0x090*/ { "CMSG_GUILD_LEADER", STATUS_LOGGEDIN, &WorldSession::HandleGuildLeaderOpcode }, - /*0x091*/ { "CMSG_GUILD_MOTD", STATUS_LOGGEDIN, &WorldSession::HandleGuildMOTDOpcode }, - /*0x092*/ { "SMSG_GUILD_EVENT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x093*/ { "SMSG_GUILD_COMMAND_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x094*/ { "UMSG_UPDATE_GUILD", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x095*/ { "CMSG_MESSAGECHAT", STATUS_LOGGEDIN, &WorldSession::HandleMessagechatOpcode }, - /*0x096*/ { "SMSG_MESSAGECHAT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x097*/ { "CMSG_JOIN_CHANNEL", STATUS_LOGGEDIN, &WorldSession::HandleChannelJoin }, - /*0x098*/ { "CMSG_LEAVE_CHANNEL", STATUS_LOGGEDIN, &WorldSession::HandleChannelLeave }, - /*0x099*/ { "SMSG_CHANNEL_NOTIFY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x09A*/ { "CMSG_CHANNEL_LIST", STATUS_LOGGEDIN, &WorldSession::HandleChannelList }, - /*0x09B*/ { "SMSG_CHANNEL_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x09C*/ { "CMSG_CHANNEL_PASSWORD", STATUS_LOGGEDIN, &WorldSession::HandleChannelPassword }, - /*0x09D*/ { "CMSG_CHANNEL_SET_OWNER", STATUS_LOGGEDIN, &WorldSession::HandleChannelSetOwner }, - /*0x09E*/ { "CMSG_CHANNEL_OWNER", STATUS_LOGGEDIN, &WorldSession::HandleChannelOwner }, - /*0x09F*/ { "CMSG_CHANNEL_MODERATOR", STATUS_LOGGEDIN, &WorldSession::HandleChannelModerator }, - /*0x0A0*/ { "CMSG_CHANNEL_UNMODERATOR", STATUS_LOGGEDIN, &WorldSession::HandleChannelUnmoderator }, - /*0x0A1*/ { "CMSG_CHANNEL_MUTE", STATUS_LOGGEDIN, &WorldSession::HandleChannelMute }, - /*0x0A2*/ { "CMSG_CHANNEL_UNMUTE", STATUS_LOGGEDIN, &WorldSession::HandleChannelUnmute }, - /*0x0A3*/ { "CMSG_CHANNEL_INVITE", STATUS_LOGGEDIN, &WorldSession::HandleChannelInvite }, - /*0x0A4*/ { "CMSG_CHANNEL_KICK", STATUS_LOGGEDIN, &WorldSession::HandleChannelKick }, - /*0x0A5*/ { "CMSG_CHANNEL_BAN", STATUS_LOGGEDIN, &WorldSession::HandleChannelBan }, - /*0x0A6*/ { "CMSG_CHANNEL_UNBAN", STATUS_LOGGEDIN, &WorldSession::HandleChannelUnban }, - /*0x0A7*/ { "CMSG_CHANNEL_ANNOUNCEMENTS", STATUS_LOGGEDIN, &WorldSession::HandleChannelAnnounce }, - /*0x0A8*/ { "CMSG_CHANNEL_MODERATE", STATUS_LOGGEDIN, &WorldSession::HandleChannelModerate }, - /*0x0A9*/ { "SMSG_UPDATE_OBJECT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x0AA*/ { "SMSG_DESTROY_OBJECT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x0AB*/ { "CMSG_USE_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleUseItemOpcode }, - /*0x0AC*/ { "CMSG_OPEN_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleOpenItemOpcode }, - /*0x0AD*/ { "CMSG_READ_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleReadItem }, - /*0x0AE*/ { "SMSG_READ_ITEM_OK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x0AF*/ { "SMSG_READ_ITEM_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x0B0*/ { "SMSG_ITEM_COOLDOWN", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x0B1*/ { "CMSG_GAMEOBJ_USE", STATUS_LOGGEDIN, &WorldSession::HandleGameObjectUseOpcode }, - /*0x0B2*/ { "CMSG_GAMEOBJ_CHAIR_USE_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0B3*/ { "SMSG_GAMEOBJECT_CUSTOM_ANIM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x0B4*/ { "CMSG_AREATRIGGER", STATUS_LOGGEDIN, &WorldSession::HandleAreaTriggerOpcode }, - /*0x0B5*/ { "MSG_MOVE_START_FORWARD", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x0B6*/ { "MSG_MOVE_START_BACKWARD", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x0B7*/ { "MSG_MOVE_STOP", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x0B8*/ { "MSG_MOVE_START_STRAFE_LEFT", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x0B9*/ { "MSG_MOVE_START_STRAFE_RIGHT", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x0BA*/ { "MSG_MOVE_STOP_STRAFE", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x0BB*/ { "MSG_MOVE_JUMP", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x0BC*/ { "MSG_MOVE_START_TURN_LEFT", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x0BD*/ { "MSG_MOVE_START_TURN_RIGHT", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x0BE*/ { "MSG_MOVE_STOP_TURN", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x0BF*/ { "MSG_MOVE_START_PITCH_UP", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x0C0*/ { "MSG_MOVE_START_PITCH_DOWN", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x0C1*/ { "MSG_MOVE_STOP_PITCH", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x0C2*/ { "MSG_MOVE_SET_RUN_MODE", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x0C3*/ { "MSG_MOVE_SET_WALK_MODE", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x0C4*/ { "MSG_MOVE_TOGGLE_LOGGING", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0C5*/ { "MSG_MOVE_TELEPORT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0C6*/ { "MSG_MOVE_TELEPORT_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0C7*/ { "MSG_MOVE_TELEPORT_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveTeleportAck }, - /*0x0C8*/ { "MSG_MOVE_TOGGLE_FALL_LOGGING", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0C9*/ { "MSG_MOVE_FALL_LAND", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x0CA*/ { "MSG_MOVE_START_SWIM", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x0CB*/ { "MSG_MOVE_STOP_SWIM", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x0CC*/ { "MSG_MOVE_SET_RUN_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0CD*/ { "MSG_MOVE_SET_RUN_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0CE*/ { "MSG_MOVE_SET_RUN_BACK_SPEED_CHEAT",STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0CF*/ { "MSG_MOVE_SET_RUN_BACK_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0D0*/ { "MSG_MOVE_SET_WALK_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0D1*/ { "MSG_MOVE_SET_WALK_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0D2*/ { "MSG_MOVE_SET_SWIM_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0D3*/ { "MSG_MOVE_SET_SWIM_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0D4*/ { "MSG_MOVE_SET_SWIM_BACK_SPEED_CHEAT",STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0D5*/ { "MSG_MOVE_SET_SWIM_BACK_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0D6*/ { "MSG_MOVE_SET_ALL_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0D7*/ { "MSG_MOVE_SET_TURN_RATE_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0D8*/ { "MSG_MOVE_SET_TURN_RATE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0D9*/ { "MSG_MOVE_TOGGLE_COLLISION_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0DA*/ { "MSG_MOVE_SET_FACING", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x0DB*/ { "MSG_MOVE_SET_PITCH", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x0DC*/ { "MSG_MOVE_WORLDPORT_ACK", STATUS_TRANSFER_PENDING, &WorldSession::HandleMoveWorldportAckOpcode}, - /*0x0DD*/ { "SMSG_MONSTER_MOVE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x0DE*/ { "SMSG_MOVE_WATER_WALK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x0DF*/ { "SMSG_MOVE_LAND_WALK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x0E0*/ { "MSG_MOVE_SET_RAW_POSITION_ACK", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0E1*/ { "CMSG_MOVE_SET_RAW_POSITION", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0E2*/ { "SMSG_FORCE_RUN_SPEED_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x0E3*/ { "CMSG_FORCE_RUN_SPEED_CHANGE_ACK", STATUS_LOGGEDIN, &WorldSession::HandleForceSpeedChangeAck }, - /*0x0E4*/ { "SMSG_FORCE_RUN_BACK_SPEED_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x0E5*/ { "CMSG_FORCE_RUN_BACK_SPEED_CHANGE_ACK",STATUS_LOGGEDIN,&WorldSession::HandleForceSpeedChangeAck }, - /*0x0E6*/ { "SMSG_FORCE_SWIM_SPEED_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x0E7*/ { "CMSG_FORCE_SWIM_SPEED_CHANGE_ACK", STATUS_LOGGEDIN, &WorldSession::HandleForceSpeedChangeAck }, - /*0x0E8*/ { "SMSG_FORCE_MOVE_ROOT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x0E9*/ { "CMSG_FORCE_MOVE_ROOT_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveRootAck }, - /*0x0EA*/ { "SMSG_FORCE_MOVE_UNROOT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x0EB*/ { "CMSG_FORCE_MOVE_UNROOT_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveUnRootAck }, - /*0x0EC*/ { "MSG_MOVE_ROOT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0ED*/ { "MSG_MOVE_UNROOT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0EE*/ { "MSG_MOVE_HEARTBEAT", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x0EF*/ { "SMSG_MOVE_KNOCK_BACK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x0F0*/ { "CMSG_MOVE_KNOCK_BACK_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveKnockBackAck }, - /*0x0F1*/ { "MSG_MOVE_KNOCK_BACK", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0F2*/ { "SMSG_MOVE_FEATHER_FALL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x0F3*/ { "SMSG_MOVE_NORMAL_FALL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x0F4*/ { "SMSG_MOVE_SET_HOVER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x0F5*/ { "SMSG_MOVE_UNSET_HOVER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x0F6*/ { "CMSG_MOVE_HOVER_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveHoverAck }, - /*0x0F7*/ { "MSG_MOVE_HOVER", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0F8*/ { "CMSG_TRIGGER_CINEMATIC_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0F9*/ { "CMSG_OPENING_CINEMATIC", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x0FA*/ { "SMSG_TRIGGER_CINEMATIC", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x0FB*/ { "CMSG_NEXT_CINEMATIC_CAMERA", STATUS_LOGGEDIN, &WorldSession::HandleNextCinematicCamera }, - /*0x0FC*/ { "CMSG_COMPLETE_CINEMATIC", STATUS_LOGGEDIN, &WorldSession::HandleCompleteCinema }, - /*0x0FD*/ { "SMSG_TUTORIAL_FLAGS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x0FE*/ { "CMSG_TUTORIAL_FLAG", STATUS_LOGGEDIN, &WorldSession::HandleTutorialFlag }, - /*0x0FF*/ { "CMSG_TUTORIAL_CLEAR", STATUS_LOGGEDIN, &WorldSession::HandleTutorialClear }, - /*0x100*/ { "CMSG_TUTORIAL_RESET", STATUS_LOGGEDIN, &WorldSession::HandleTutorialReset }, - /*0x101*/ { "CMSG_STANDSTATECHANGE", STATUS_LOGGEDIN, &WorldSession::HandleStandStateChangeOpcode }, - /*0x102*/ { "CMSG_EMOTE", STATUS_LOGGEDIN, &WorldSession::HandleEmoteOpcode }, - /*0x103*/ { "SMSG_EMOTE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x104*/ { "CMSG_TEXT_EMOTE", STATUS_LOGGEDIN, &WorldSession::HandleTextEmoteOpcode }, - /*0x105*/ { "SMSG_TEXT_EMOTE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x106*/ { "CMSG_AUTOEQUIP_GROUND_ITEM", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x107*/ { "CMSG_AUTOSTORE_GROUND_ITEM", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x108*/ { "CMSG_AUTOSTORE_LOOT_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAutostoreLootItemOpcode }, - /*0x109*/ { "CMSG_STORE_LOOT_IN_SLOT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x10A*/ { "CMSG_AUTOEQUIP_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAutoEquipItemOpcode }, - /*0x10B*/ { "CMSG_AUTOSTORE_BAG_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAutoStoreBagItemOpcode }, - /*0x10C*/ { "CMSG_SWAP_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleSwapItem }, - /*0x10D*/ { "CMSG_SWAP_INV_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleSwapInvItemOpcode }, - /*0x10E*/ { "CMSG_SPLIT_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleSplitItemOpcode }, - /*0x10F*/ { "CMSG_AUTOEQUIP_ITEM_SLOT", STATUS_LOGGEDIN, &WorldSession::HandleAutoEquipItemSlotOpcode }, - /*0x110*/ { "OBSOLETE_DROP_ITEM", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x111*/ { "CMSG_DESTROYITEM", STATUS_LOGGEDIN, &WorldSession::HandleDestroyItemOpcode }, - /*0x112*/ { "SMSG_INVENTORY_CHANGE_FAILURE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x113*/ { "SMSG_OPEN_CONTAINER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x114*/ { "CMSG_INSPECT", STATUS_LOGGEDIN, &WorldSession::HandleInspectOpcode }, - /*0x115*/ { "SMSG_INSPECT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x116*/ { "CMSG_INITIATE_TRADE", STATUS_LOGGEDIN, &WorldSession::HandleInitiateTradeOpcode }, - /*0x117*/ { "CMSG_BEGIN_TRADE", STATUS_LOGGEDIN, &WorldSession::HandleBeginTradeOpcode }, - /*0x118*/ { "CMSG_BUSY_TRADE", STATUS_LOGGEDIN, &WorldSession::HandleBusyTradeOpcode }, - /*0x119*/ { "CMSG_IGNORE_TRADE", STATUS_LOGGEDIN, &WorldSession::HandleIgnoreTradeOpcode }, - /*0x11A*/ { "CMSG_ACCEPT_TRADE", STATUS_LOGGEDIN, &WorldSession::HandleAcceptTradeOpcode }, - /*0x11B*/ { "CMSG_UNACCEPT_TRADE", STATUS_LOGGEDIN, &WorldSession::HandleUnacceptTradeOpcode }, - /*0x11C*/ { "CMSG_CANCEL_TRADE", STATUS_AUTHED, &WorldSession::HandleCancelTradeOpcode }, - /*0x11D*/ { "CMSG_SET_TRADE_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleSetTradeItemOpcode }, - /*0x11E*/ { "CMSG_CLEAR_TRADE_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleClearTradeItemOpcode }, - /*0x11F*/ { "CMSG_SET_TRADE_GOLD", STATUS_LOGGEDIN, &WorldSession::HandleSetTradeGoldOpcode }, - /*0x120*/ { "SMSG_TRADE_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x121*/ { "SMSG_TRADE_STATUS_EXTENDED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x122*/ { "SMSG_INITIALIZE_FACTIONS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x123*/ { "SMSG_SET_FACTION_VISIBLE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x124*/ { "SMSG_SET_FACTION_STANDING", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x125*/ { "CMSG_SET_FACTION_ATWAR", STATUS_LOGGEDIN, &WorldSession::HandleSetFactionAtWar }, - /*0x126*/ { "CMSG_SET_FACTION_CHEAT", STATUS_LOGGEDIN, &WorldSession::HandleSetFactionCheat }, - /*0x127*/ { "SMSG_SET_PROFICIENCY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x128*/ { "CMSG_SET_ACTION_BUTTON", STATUS_LOGGEDIN, &WorldSession::HandleSetActionButtonOpcode }, - /*0x129*/ { "SMSG_ACTION_BUTTONS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x12A*/ { "SMSG_INITIAL_SPELLS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x12B*/ { "SMSG_LEARNED_SPELL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x12C*/ { "SMSG_SUPERCEDED_SPELL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x12D*/ { "CMSG_NEW_SPELL_SLOT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x12E*/ { "CMSG_CAST_SPELL", STATUS_LOGGEDIN, &WorldSession::HandleCastSpellOpcode }, - /*0x12F*/ { "CMSG_CANCEL_CAST", STATUS_LOGGEDIN, &WorldSession::HandleCancelCastOpcode }, - /*0x130*/ { "SMSG_CAST_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x131*/ { "SMSG_SPELL_START", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x132*/ { "SMSG_SPELL_GO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x133*/ { "SMSG_SPELL_FAILURE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x134*/ { "SMSG_SPELL_COOLDOWN", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x135*/ { "SMSG_COOLDOWN_EVENT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x136*/ { "CMSG_CANCEL_AURA", STATUS_LOGGEDIN, &WorldSession::HandleCancelAuraOpcode }, - /*0x137*/ { "SMSG_UPDATE_AURA_DURATION", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x138*/ { "SMSG_PET_CAST_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x139*/ { "MSG_CHANNEL_START", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x13A*/ { "MSG_CHANNEL_UPDATE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x13B*/ { "CMSG_CANCEL_CHANNELLING", STATUS_LOGGEDIN, &WorldSession::HandleCancelChanneling }, - /*0x13C*/ { "SMSG_AI_REACTION", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x13D*/ { "CMSG_SET_SELECTION", STATUS_LOGGEDIN, &WorldSession::HandleSetSelectionOpcode }, - /*0x13E*/ { "CMSG_SET_TARGET_OBSOLETE", STATUS_LOGGEDIN, &WorldSession::HandleSetTargetOpcode }, - /*0x13F*/ { "CMSG_UNUSED", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x140*/ { "CMSG_UNUSED2", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x141*/ { "CMSG_ATTACKSWING", STATUS_LOGGEDIN, &WorldSession::HandleAttackSwingOpcode }, - /*0x142*/ { "CMSG_ATTACKSTOP", STATUS_LOGGEDIN, &WorldSession::HandleAttackStopOpcode }, - /*0x143*/ { "SMSG_ATTACKSTART", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x144*/ { "SMSG_ATTACKSTOP", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x145*/ { "SMSG_ATTACKSWING_NOTINRANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x146*/ { "SMSG_ATTACKSWING_BADFACING", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x147*/ { "SMSG_ATTACKSWING_NOTSTANDING", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x148*/ { "SMSG_ATTACKSWING_DEADTARGET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x149*/ { "SMSG_ATTACKSWING_CANT_ATTACK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x14A*/ { "SMSG_ATTACKERSTATEUPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x14B*/ { "SMSG_VICTIMSTATEUPDATE_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x14C*/ { "SMSG_DAMAGE_DONE_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x14D*/ { "SMSG_DAMAGE_TAKEN_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x14E*/ { "SMSG_CANCEL_COMBAT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x14F*/ { "SMSG_PLAYER_COMBAT_XP_GAIN_OBSOLETE",STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x150*/ { "SMSG_SPELLHEALLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x151*/ { "SMSG_SPELLENERGIZELOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x152*/ { "CMSG_SHEATHE_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x153*/ { "CMSG_SAVE_PLAYER", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x154*/ { "CMSG_SETDEATHBINDPOINT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x155*/ { "SMSG_BINDPOINTUPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x156*/ { "CMSG_GETDEATHBINDZONE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x157*/ { "SMSG_BINDZONEREPLY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x158*/ { "SMSG_PLAYERBOUND", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x159*/ { "SMSG_CLIENT_CONTROL_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x15A*/ { "CMSG_REPOP_REQUEST", STATUS_LOGGEDIN, &WorldSession::HandleRepopRequestOpcode }, - /*0x15B*/ { "SMSG_RESURRECT_REQUEST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x15C*/ { "CMSG_RESURRECT_RESPONSE", STATUS_LOGGEDIN, &WorldSession::HandleResurrectResponseOpcode }, - /*0x15D*/ { "CMSG_LOOT", STATUS_LOGGEDIN, &WorldSession::HandleLootOpcode }, - /*0x15E*/ { "CMSG_LOOT_MONEY", STATUS_LOGGEDIN, &WorldSession::HandleLootMoneyOpcode }, - /*0x15F*/ { "CMSG_LOOT_RELEASE", STATUS_LOGGEDIN, &WorldSession::HandleLootReleaseOpcode }, - /*0x160*/ { "SMSG_LOOT_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x161*/ { "SMSG_LOOT_RELEASE_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x162*/ { "SMSG_LOOT_REMOVED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x163*/ { "SMSG_LOOT_MONEY_NOTIFY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x164*/ { "SMSG_LOOT_ITEM_NOTIFY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x165*/ { "SMSG_LOOT_CLEAR_MONEY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x166*/ { "SMSG_ITEM_PUSH_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x167*/ { "SMSG_DUEL_REQUESTED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x168*/ { "SMSG_DUEL_OUTOFBOUNDS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x169*/ { "SMSG_DUEL_INBOUNDS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x16A*/ { "SMSG_DUEL_COMPLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x16B*/ { "SMSG_DUEL_WINNER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x16C*/ { "CMSG_DUEL_ACCEPTED", STATUS_LOGGEDIN, &WorldSession::HandleDuelAcceptedOpcode }, - /*0x16D*/ { "CMSG_DUEL_CANCELLED", STATUS_LOGGEDIN, &WorldSession::HandleDuelCancelledOpcode }, - /*0x16E*/ { "SMSG_MOUNTRESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x16F*/ { "SMSG_DISMOUNTRESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x170*/ { "SMSG_PUREMOUNT_CANCELLED_OBSOLETE",STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x171*/ { "CMSG_MOUNTSPECIAL_ANIM", STATUS_LOGGEDIN, &WorldSession::HandleMountSpecialAnimOpcode }, - /*0x172*/ { "SMSG_MOUNTSPECIAL_ANIM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x173*/ { "SMSG_PET_TAME_FAILURE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x174*/ { "CMSG_PET_SET_ACTION", STATUS_LOGGEDIN, &WorldSession::HandlePetSetAction }, - /*0x175*/ { "CMSG_PET_ACTION", STATUS_LOGGEDIN, &WorldSession::HandlePetAction }, - /*0x176*/ { "CMSG_PET_ABANDON", STATUS_LOGGEDIN, &WorldSession::HandlePetAbandon }, - /*0x177*/ { "CMSG_PET_RENAME", STATUS_LOGGEDIN, &WorldSession::HandlePetRename }, - /*0x178*/ { "SMSG_PET_NAME_INVALID", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x179*/ { "SMSG_PET_SPELLS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x17A*/ { "SMSG_PET_MODE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x17B*/ { "CMSG_GOSSIP_HELLO", STATUS_LOGGEDIN, &WorldSession::HandleGossipHelloOpcode }, - /*0x17C*/ { "CMSG_GOSSIP_SELECT_OPTION", STATUS_LOGGEDIN, &WorldSession::HandleGossipSelectOptionOpcode }, - /*0x17D*/ { "SMSG_GOSSIP_MESSAGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x17E*/ { "SMSG_GOSSIP_COMPLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x17F*/ { "CMSG_NPC_TEXT_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleNpcTextQueryOpcode }, - /*0x180*/ { "SMSG_NPC_TEXT_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x181*/ { "SMSG_NPC_WONT_TALK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x182*/ { "CMSG_QUESTGIVER_STATUS_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverStatusQueryOpcode}, - /*0x183*/ { "SMSG_QUESTGIVER_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x184*/ { "CMSG_QUESTGIVER_HELLO", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverHelloOpcode }, - /*0x185*/ { "SMSG_QUESTGIVER_QUEST_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x186*/ { "CMSG_QUESTGIVER_QUERY_QUEST", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverQuestQueryOpcode}, - /*0x187*/ { "CMSG_QUESTGIVER_QUEST_AUTOLAUNCH", STATUS_LOGGEDIN, &WorldSession::HandleQuestAutoLaunch }, - /*0x188*/ { "SMSG_QUESTGIVER_QUEST_DETAILS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x189*/ { "CMSG_QUESTGIVER_ACCEPT_QUEST", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverAcceptQuestOpcode}, - /*0x18A*/ { "CMSG_QUESTGIVER_COMPLETE_QUEST", STATUS_LOGGEDIN, &WorldSession::HandleQuestComplete }, - /*0x18B*/ { "SMSG_QUESTGIVER_REQUEST_ITEMS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x18C*/ { "CMSG_QUESTGIVER_REQUEST_REWARD", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverRequestRewardOpcode}, - /*0x18D*/ { "SMSG_QUESTGIVER_OFFER_REWARD", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x18E*/ { "CMSG_QUESTGIVER_CHOOSE_REWARD", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverChooseRewardOpcode}, - /*0x18F*/ { "SMSG_QUESTGIVER_QUEST_INVALID", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x190*/ { "CMSG_QUESTGIVER_CANCEL", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverCancel }, - /*0x191*/ { "SMSG_QUESTGIVER_QUEST_COMPLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x192*/ { "SMSG_QUESTGIVER_QUEST_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x193*/ { "CMSG_QUESTLOG_SWAP_QUEST", STATUS_LOGGEDIN, &WorldSession::HandleQuestLogSwapQuest }, - /*0x194*/ { "CMSG_QUESTLOG_REMOVE_QUEST", STATUS_LOGGEDIN, &WorldSession::HandleQuestLogRemoveQuest }, - /*0x195*/ { "SMSG_QUESTLOG_FULL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x196*/ { "SMSG_QUESTUPDATE_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x197*/ { "SMSG_QUESTUPDATE_FAILEDTIMER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x198*/ { "SMSG_QUESTUPDATE_COMPLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x199*/ { "SMSG_QUESTUPDATE_ADD_KILL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x19A*/ { "SMSG_QUESTUPDATE_ADD_ITEM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x19B*/ { "CMSG_QUEST_CONFIRM_ACCEPT", STATUS_LOGGEDIN, &WorldSession::HandleQuestConfirmAccept }, - /*0x19C*/ { "SMSG_QUEST_CONFIRM_ACCEPT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x19D*/ { "CMSG_PUSHQUESTTOPARTY", STATUS_LOGGEDIN, &WorldSession::HandleQuestPushToParty }, - /*0x19E*/ { "CMSG_LIST_INVENTORY", STATUS_LOGGEDIN, &WorldSession::HandleListInventoryOpcode }, - /*0x19F*/ { "SMSG_LIST_INVENTORY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1A0*/ { "CMSG_SELL_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleSellItemOpcode }, - /*0x1A1*/ { "SMSG_SELL_ITEM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1A2*/ { "CMSG_BUY_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleBuyItemOpcode }, - /*0x1A3*/ { "CMSG_BUY_ITEM_IN_SLOT", STATUS_LOGGEDIN, &WorldSession::HandleBuyItemInSlotOpcode }, - /*0x1A4*/ { "SMSG_BUY_ITEM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1A5*/ { "SMSG_BUY_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1A6*/ { "CMSG_TAXICLEARALLNODES", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x1A7*/ { "CMSG_TAXIENABLEALLNODES", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x1A8*/ { "CMSG_TAXISHOWNODES", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x1A9*/ { "SMSG_SHOWTAXINODES", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1AA*/ { "CMSG_TAXINODE_STATUS_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleTaxiNodeStatusQueryOpcode }, - /*0x1AB*/ { "SMSG_TAXINODE_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1AC*/ { "CMSG_TAXIQUERYAVAILABLENODES", STATUS_LOGGEDIN, &WorldSession::HandleTaxiQueryAvailableNodesOpcode}, - /*0x1AD*/ { "CMSG_ACTIVATETAXI", STATUS_LOGGEDIN, &WorldSession::HandleActivateTaxiOpcode }, - /*0x1AE*/ { "SMSG_ACTIVATETAXIREPLY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1AF*/ { "SMSG_NEW_TAXI_PATH", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1B0*/ { "CMSG_TRAINER_LIST", STATUS_LOGGEDIN, &WorldSession::HandleTrainerListOpcode }, - /*0x1B1*/ { "SMSG_TRAINER_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1B2*/ { "CMSG_TRAINER_BUY_SPELL", STATUS_LOGGEDIN, &WorldSession::HandleTrainerBuySpellOpcode }, - /*0x1B3*/ { "SMSG_TRAINER_BUY_SUCCEEDED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1B4*/ { "SMSG_TRAINER_BUY_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1B5*/ { "CMSG_BINDER_ACTIVATE", STATUS_LOGGEDIN, &WorldSession::HandleBinderActivateOpcode }, - /*0x1B6*/ { "SMSG_PLAYERBINDERROR", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1B7*/ { "CMSG_BANKER_ACTIVATE", STATUS_LOGGEDIN, &WorldSession::HandleBankerActivateOpcode }, - /*0x1B8*/ { "SMSG_SHOW_BANK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1B9*/ { "CMSG_BUY_BANK_SLOT", STATUS_LOGGEDIN, &WorldSession::HandleBuyBankSlotOpcode }, - /*0x1BA*/ { "SMSG_BUY_BANK_SLOT_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1BB*/ { "CMSG_PETITION_SHOWLIST", STATUS_LOGGEDIN, &WorldSession::HandlePetitionShowListOpcode }, - /*0x1BC*/ { "SMSG_PETITION_SHOWLIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1BD*/ { "CMSG_PETITION_BUY", STATUS_LOGGEDIN, &WorldSession::HandlePetitionBuyOpcode }, - /*0x1BE*/ { "CMSG_PETITION_SHOW_SIGNATURES", STATUS_LOGGEDIN, &WorldSession::HandlePetitionShowSignOpcode }, - /*0x1BF*/ { "SMSG_PETITION_SHOW_SIGNATURES", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1C0*/ { "CMSG_PETITION_SIGN", STATUS_LOGGEDIN, &WorldSession::HandlePetitionSignOpcode }, - /*0x1C1*/ { "SMSG_PETITION_SIGN_RESULTS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1C2*/ { "MSG_PETITION_DECLINE", STATUS_LOGGEDIN, &WorldSession::HandlePetitionDeclineOpcode }, - /*0x1C3*/ { "CMSG_OFFER_PETITION", STATUS_LOGGEDIN, &WorldSession::HandleOfferPetitionOpcode }, - /*0x1C4*/ { "CMSG_TURN_IN_PETITION", STATUS_LOGGEDIN, &WorldSession::HandleTurnInPetitionOpcode }, - /*0x1C5*/ { "SMSG_TURN_IN_PETITION_RESULTS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1C6*/ { "CMSG_PETITION_QUERY", STATUS_LOGGEDIN, &WorldSession::HandlePetitionQueryOpcode }, - /*0x1C7*/ { "SMSG_PETITION_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1C8*/ { "SMSG_FISH_NOT_HOOKED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1C9*/ { "SMSG_FISH_ESCAPED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1CA*/ { "CMSG_BUG", STATUS_LOGGEDIN, &WorldSession::HandleBugOpcode }, - /*0x1CB*/ { "SMSG_NOTIFICATION", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1CC*/ { "CMSG_PLAYED_TIME", STATUS_LOGGEDIN, &WorldSession::HandlePlayedTime }, - /*0x1CD*/ { "SMSG_PLAYED_TIME", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1CE*/ { "CMSG_QUERY_TIME", STATUS_LOGGEDIN, &WorldSession::HandleQueryTimeOpcode }, - /*0x1CF*/ { "SMSG_QUERY_TIME_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1D0*/ { "SMSG_LOG_XPGAIN", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1D1*/ { "SMSG_AURACASTLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1D2*/ { "CMSG_RECLAIM_CORPSE", STATUS_LOGGEDIN, &WorldSession::HandleCorpseReclaimOpcode }, - /*0x1D3*/ { "CMSG_WRAP_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleWrapItemOpcode }, - /*0x1D4*/ { "SMSG_LEVELUP_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1D5*/ { "MSG_MINIMAP_PING", STATUS_LOGGEDIN, &WorldSession::HandleMinimapPingOpcode }, - /*0x1D6*/ { "SMSG_RESISTLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1D7*/ { "SMSG_ENCHANTMENTLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1D8*/ { "CMSG_SET_SKILL_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x1D9*/ { "SMSG_START_MIRROR_TIMER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1DA*/ { "SMSG_PAUSE_MIRROR_TIMER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1DB*/ { "SMSG_STOP_MIRROR_TIMER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1DC*/ { "CMSG_PING", STATUS_NEVER, &WorldSession::Handle_EarlyProccess }, - /*0x1DD*/ { "SMSG_PONG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1DE*/ { "SMSG_CLEAR_COOLDOWN", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1DF*/ { "SMSG_GAMEOBJECT_PAGETEXT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1E0*/ { "CMSG_SETSHEATHED", STATUS_LOGGEDIN, &WorldSession::HandleSetSheathedOpcode }, - /*0x1E1*/ { "SMSG_COOLDOWN_CHEAT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1E2*/ { "SMSG_SPELL_DELAYED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1E3*/ { "CMSG_PLAYER_MACRO_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x1E4*/ { "SMSG_PLAYER_MACRO_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1E5*/ { "CMSG_GHOST", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x1E6*/ { "CMSG_GM_INVIS", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x1E7*/ { "SMSG_INVALID_PROMOTION_CODE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1E8*/ { "MSG_GM_BIND_OTHER", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x1E9*/ { "MSG_GM_SUMMON", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x1EA*/ { "SMSG_ITEM_TIME_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1EB*/ { "SMSG_ITEM_ENCHANT_TIME_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1EC*/ { "SMSG_AUTH_CHALLENGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1ED*/ { "CMSG_AUTH_SESSION", STATUS_NEVER, &WorldSession::Handle_EarlyProccess }, - /*0x1EE*/ { "SMSG_AUTH_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1EF*/ { "MSG_GM_SHOWLABEL", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x1F0*/ { "CMSG_PET_CAST_SPELL", STATUS_LOGGEDIN, &WorldSession::HandlePetCastSpellOpcode }, - /*0x1F1*/ { "MSG_SAVE_GUILD_EMBLEM", STATUS_LOGGEDIN, &WorldSession::HandleGuildSaveEmblemOpcode }, - /*0x1F2*/ { "MSG_TABARDVENDOR_ACTIVATE", STATUS_LOGGEDIN, &WorldSession::HandleTabardVendorActivateOpcode}, - /*0x1F3*/ { "SMSG_PLAY_SPELL_VISUAL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1F4*/ { "CMSG_ZONEUPDATE", STATUS_LOGGEDIN, &WorldSession::HandleZoneUpdateOpcode }, - /*0x1F5*/ { "SMSG_PARTYKILLLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1F6*/ { "SMSG_COMPRESSED_UPDATE_OBJECT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1F7*/ { "SMSG_PLAY_SPELL_IMPACT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1F8*/ { "SMSG_EXPLORATION_EXPERIENCE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1F9*/ { "CMSG_GM_SET_SECURITY_GROUP", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x1FA*/ { "CMSG_GM_NUKE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x1FB*/ { "MSG_RANDOM_ROLL", STATUS_LOGGEDIN, &WorldSession::HandleRandomRollOpcode }, - /*0x1FC*/ { "SMSG_ENVIRONMENTALDAMAGELOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1FD*/ { "CMSG_RWHOIS_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x1FE*/ { "SMSG_RWHOIS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x1FF*/ { "MSG_LOOKING_FOR_GROUP", STATUS_LOGGEDIN, &WorldSession::HandleLookingForGroup }, - /*0x200*/ { "CMSG_SET_LOOKING_FOR_GROUP", STATUS_LOGGEDIN, &WorldSession::HandleSetLfgOpcode }, - /*0x201*/ { "CMSG_UNLEARN_SPELL", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x202*/ { "CMSG_UNLEARN_SKILL", STATUS_LOGGEDIN, &WorldSession::HandleUnlearnSkillOpcode }, - /*0x203*/ { "SMSG_REMOVED_SPELL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x204*/ { "CMSG_DECHARGE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x205*/ { "CMSG_GMTICKET_CREATE", STATUS_LOGGEDIN, &WorldSession::HandleGMTicketCreateOpcode }, - /*0x206*/ { "SMSG_GMTICKET_CREATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x207*/ { "CMSG_GMTICKET_UPDATETEXT", STATUS_LOGGEDIN, &WorldSession::HandleGMTicketUpdateOpcode }, - /*0x208*/ { "SMSG_GMTICKET_UPDATETEXT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x209*/ { "SMSG_ACCOUNT_DATA_TIMES", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x20A*/ { "CMSG_REQUEST_ACCOUNT_DATA", STATUS_LOGGEDIN, &WorldSession::HandleRequestAccountData }, - /*0x20B*/ { "CMSG_UPDATE_ACCOUNT_DATA", STATUS_LOGGEDIN, &WorldSession::HandleUpdateAccountData }, - /*0x20C*/ { "SMSG_UPDATE_ACCOUNT_DATA", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x20D*/ { "SMSG_CLEAR_FAR_SIGHT_IMMEDIATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x20E*/ { "SMSG_POWERGAINLOG_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x20F*/ { "CMSG_GM_TEACH", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x210*/ { "CMSG_GM_CREATE_ITEM_TARGET", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x211*/ { "CMSG_GMTICKET_GETTICKET", STATUS_LOGGEDIN, &WorldSession::HandleGMTicketGetTicketOpcode }, - /*0x212*/ { "SMSG_GMTICKET_GETTICKET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x213*/ { "CMSG_UNLEARN_TALENTS", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x214*/ { "SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE",STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x215*/ { "SMSG_GAMEOBJECT_DESPAWN_ANIM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x216*/ { "MSG_CORPSE_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleCorpseQueryOpcode }, - /*0x217*/ { "CMSG_GMTICKET_DELETETICKET", STATUS_LOGGEDIN, &WorldSession::HandleGMTicketDeleteOpcode }, - /*0x218*/ { "SMSG_GMTICKET_DELETETICKET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x219*/ { "SMSG_CHAT_WRONG_FACTION", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x21A*/ { "CMSG_GMTICKET_SYSTEMSTATUS", STATUS_LOGGEDIN, &WorldSession::HandleGMTicketSystemStatusOpcode}, - /*0x21B*/ { "SMSG_GMTICKET_SYSTEMSTATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x21C*/ { "CMSG_SPIRIT_HEALER_ACTIVATE", STATUS_LOGGEDIN, &WorldSession::HandleSpiritHealerActivateOpcode}, - /*0x21D*/ { "CMSG_SET_STAT_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x21E*/ { "SMSG_SET_REST_START", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x21F*/ { "CMSG_SKILL_BUY_STEP", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x220*/ { "CMSG_SKILL_BUY_RANK", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x221*/ { "CMSG_XP_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x222*/ { "SMSG_SPIRIT_HEALER_CONFIRM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x223*/ { "CMSG_CHARACTER_POINT_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x224*/ { "SMSG_GOSSIP_POI", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x225*/ { "CMSG_CHAT_IGNORED", STATUS_LOGGEDIN, &WorldSession::HandleChatIgnoredOpcode }, - /*0x226*/ { "CMSG_GM_VISION", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x227*/ { "CMSG_SERVER_COMMAND", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x228*/ { "CMSG_GM_SILENCE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x229*/ { "CMSG_GM_REVEALTO", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x22A*/ { "CMSG_GM_RESURRECT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x22B*/ { "CMSG_GM_SUMMONMOB", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x22C*/ { "CMSG_GM_MOVECORPSE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x22D*/ { "CMSG_GM_FREEZE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x22E*/ { "CMSG_GM_UBERINVIS", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x22F*/ { "CMSG_GM_REQUEST_PLAYER_INFO", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x230*/ { "SMSG_GM_PLAYER_INFO", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x231*/ { "CMSG_GUILD_RANK", STATUS_LOGGEDIN, &WorldSession::HandleGuildRankOpcode }, - /*0x232*/ { "CMSG_GUILD_ADD_RANK", STATUS_LOGGEDIN, &WorldSession::HandleGuildAddRankOpcode }, - /*0x233*/ { "CMSG_GUILD_DEL_RANK", STATUS_LOGGEDIN, &WorldSession::HandleGuildDelRankOpcode }, - /*0x234*/ { "CMSG_GUILD_SET_PUBLIC_NOTE", STATUS_LOGGEDIN, &WorldSession::HandleGuildSetPublicNoteOpcode }, - /*0x235*/ { "CMSG_GUILD_SET_OFFICER_NOTE", STATUS_LOGGEDIN, &WorldSession::HandleGuildSetOfficerNoteOpcode }, - /*0x236*/ { "SMSG_LOGIN_VERIFY_WORLD", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x237*/ { "CMSG_CLEAR_EXPLORATION", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x238*/ { "CMSG_SEND_MAIL", STATUS_LOGGEDIN, &WorldSession::HandleSendMail }, - /*0x239*/ { "SMSG_SEND_MAIL_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x23A*/ { "CMSG_GET_MAIL_LIST", STATUS_LOGGEDIN, &WorldSession::HandleGetMail }, - /*0x23B*/ { "SMSG_MAIL_LIST_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x23C*/ { "CMSG_BATTLEFIELD_LIST", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundListOpcode }, - /*0x23D*/ { "SMSG_BATTLEFIELD_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x23E*/ { "CMSG_BATTLEFIELD_JOIN", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x23F*/ { "SMSG_BATTLEFIELD_WIN_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x240*/ { "SMSG_BATTLEFIELD_LOSE_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x241*/ { "CMSG_TAXICLEARNODE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x242*/ { "CMSG_TAXIENABLENODE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x243*/ { "CMSG_ITEM_TEXT_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleItemTextQuery }, - /*0x244*/ { "SMSG_ITEM_TEXT_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x245*/ { "CMSG_MAIL_TAKE_MONEY", STATUS_LOGGEDIN, &WorldSession::HandleTakeMoney }, - /*0x246*/ { "CMSG_MAIL_TAKE_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleTakeItem }, - /*0x247*/ { "CMSG_MAIL_MARK_AS_READ", STATUS_LOGGEDIN, &WorldSession::HandleMarkAsRead }, - /*0x248*/ { "CMSG_MAIL_RETURN_TO_SENDER", STATUS_LOGGEDIN, &WorldSession::HandleReturnToSender }, - /*0x249*/ { "CMSG_MAIL_DELETE", STATUS_LOGGEDIN, &WorldSession::HandleMailDelete }, - /*0x24A*/ { "CMSG_MAIL_CREATE_TEXT_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleMailCreateTextItem }, - /*0x24B*/ { "SMSG_SPELLLOGMISS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x24C*/ { "SMSG_SPELLLOGEXECUTE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x24D*/ { "SMSG_DEBUGAURAPROC", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x24E*/ { "SMSG_PERIODICAURALOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x24F*/ { "SMSG_SPELLDAMAGESHIELD", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x250*/ { "SMSG_SPELLNONMELEEDAMAGELOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x251*/ { "CMSG_LEARN_TALENT", STATUS_LOGGEDIN, &WorldSession::HandleLearnTalentOpcode }, - /*0x252*/ { "SMSG_RESURRECT_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x253*/ { "CMSG_TOGGLE_PVP", STATUS_LOGGEDIN, &WorldSession::HandleTogglePvP }, - /*0x254*/ { "SMSG_ZONE_UNDER_ATTACK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x255*/ { "MSG_AUCTION_HELLO", STATUS_LOGGEDIN, &WorldSession::HandleAuctionHelloOpcode }, - /*0x256*/ { "CMSG_AUCTION_SELL_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAuctionSellItem }, - /*0x257*/ { "CMSG_AUCTION_REMOVE_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAuctionRemoveItem }, - /*0x258*/ { "CMSG_AUCTION_LIST_ITEMS", STATUS_LOGGEDIN, &WorldSession::HandleAuctionListItems }, - /*0x259*/ { "CMSG_AUCTION_LIST_OWNER_ITEMS", STATUS_LOGGEDIN, &WorldSession::HandleAuctionListOwnerItems }, - /*0x25A*/ { "CMSG_AUCTION_PLACE_BID", STATUS_LOGGEDIN, &WorldSession::HandleAuctionPlaceBid }, - /*0x25B*/ { "SMSG_AUCTION_COMMAND_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x25C*/ { "SMSG_AUCTION_LIST_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x25D*/ { "SMSG_AUCTION_OWNER_LIST_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x25E*/ { "SMSG_AUCTION_BIDDER_NOTIFICATION", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x25F*/ { "SMSG_AUCTION_OWNER_NOTIFICATION", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x260*/ { "SMSG_PROCRESIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x261*/ { "SMSG_STANDSTATE_CHANGE_FAILURE_OBSOLETE",STATUS_NEVER,&WorldSession::Handle_ServerSide }, - /*0x262*/ { "SMSG_DISPEL_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x263*/ { "SMSG_SPELLORDAMAGE_IMMUNE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x264*/ { "CMSG_AUCTION_LIST_BIDDER_ITEMS", STATUS_LOGGEDIN, &WorldSession::HandleAuctionListBidderItems }, - /*0x265*/ { "SMSG_AUCTION_BIDDER_LIST_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x266*/ { "SMSG_SET_FLAT_SPELL_MODIFIER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x267*/ { "SMSG_SET_PCT_SPELL_MODIFIER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x268*/ { "CMSG_SET_AMMO", STATUS_LOGGEDIN, &WorldSession::HandleSetAmmoOpcode }, - /*0x269*/ { "SMSG_CORPSE_RECLAIM_DELAY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x26A*/ { "CMSG_SET_ACTIVE_MOVER", STATUS_LOGGEDIN, &WorldSession::HandleSetActiveMoverOpcode }, - /*0x26B*/ { "CMSG_PET_CANCEL_AURA", STATUS_LOGGEDIN, &WorldSession::HandlePetCancelAuraOpcode }, - /*0x26C*/ { "CMSG_PLAYER_AI_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x26D*/ { "CMSG_CANCEL_AUTO_REPEAT_SPELL", STATUS_LOGGEDIN, &WorldSession::HandleCancelAutoRepeatSpellOpcode}, - /*0x26E*/ { "MSG_GM_ACCOUNT_ONLINE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x26F*/ { "MSG_LIST_STABLED_PETS", STATUS_LOGGEDIN, &WorldSession::HandleListStabledPetsOpcode }, - /*0x270*/ { "CMSG_STABLE_PET", STATUS_LOGGEDIN, &WorldSession::HandleStablePet }, - /*0x271*/ { "CMSG_UNSTABLE_PET", STATUS_LOGGEDIN, &WorldSession::HandleUnstablePet }, - /*0x272*/ { "CMSG_BUY_STABLE_SLOT", STATUS_LOGGEDIN, &WorldSession::HandleBuyStableSlot }, - /*0x273*/ { "SMSG_STABLE_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x274*/ { "CMSG_STABLE_REVIVE_PET", STATUS_LOGGEDIN, &WorldSession::HandleStableRevivePet }, - /*0x275*/ { "CMSG_STABLE_SWAP_PET", STATUS_LOGGEDIN, &WorldSession::HandleStableSwapPet }, - /*0x276*/ { "MSG_QUEST_PUSH_RESULT", STATUS_LOGGEDIN, &WorldSession::HandleQuestPushResult }, - /*0x277*/ { "SMSG_PLAY_MUSIC", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x278*/ { "SMSG_PLAY_OBJECT_SOUND", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x279*/ { "CMSG_REQUEST_PET_INFO", STATUS_LOGGEDIN, &WorldSession::HandleRequestPetInfoOpcode }, - /*0x27A*/ { "CMSG_FAR_SIGHT", STATUS_LOGGEDIN, &WorldSession::HandleFarSightOpcode }, - /*0x27B*/ { "SMSG_SPELLDISPELLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x27C*/ { "SMSG_DAMAGE_CALC_LOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x27D*/ { "CMSG_ENABLE_DAMAGE_LOG", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x27E*/ { "CMSG_GROUP_CHANGE_SUB_GROUP", STATUS_LOGGEDIN, &WorldSession::HandleGroupChangeSubGroupOpcode }, - /*0x27F*/ { "CMSG_REQUEST_PARTY_MEMBER_STATS", STATUS_LOGGEDIN, &WorldSession::HandleRequestPartyMemberStatsOpcode}, - /*0x280*/ { "CMSG_GROUP_SWAP_SUB_GROUP", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x281*/ { "CMSG_RESET_FACTION_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x282*/ { "CMSG_AUTOSTORE_BANK_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAutoStoreBankItemOpcode }, - /*0x283*/ { "CMSG_AUTOBANK_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAutoBankItemOpcode }, - /*0x284*/ { "MSG_QUERY_NEXT_MAIL_TIME", STATUS_LOGGEDIN, &WorldSession::HandleMsgQueryNextMailtime }, - /*0x285*/ { "SMSG_RECEIVED_MAIL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x286*/ { "SMSG_RAID_GROUP_ONLY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x287*/ { "CMSG_SET_DURABILITY_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x288*/ { "CMSG_SET_PVP_RANK_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x289*/ { "CMSG_ADD_PVP_MEDAL_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x28A*/ { "CMSG_DEL_PVP_MEDAL_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x28B*/ { "CMSG_SET_PVP_TITLE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x28C*/ { "SMSG_PVP_CREDIT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x28D*/ { "SMSG_AUCTION_REMOVED_NOTIFICATION",STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x28E*/ { "CMSG_GROUP_RAID_CONVERT", STATUS_LOGGEDIN, &WorldSession::HandleRaidConvertOpcode }, - /*0x28F*/ { "CMSG_GROUP_ASSISTANT_LEADER", STATUS_LOGGEDIN, &WorldSession::HandleGroupAssistantOpcode }, - /*0x290*/ { "CMSG_BUYBACK_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleBuybackItem }, - /*0x291*/ { "SMSG_SERVER_MESSAGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x292*/ { "CMSG_MEETINGSTONE_JOIN", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x293*/ { "CMSG_MEETINGSTONE_LEAVE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x294*/ { "CMSG_MEETINGSTONE_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x295*/ { "SMSG_MEETINGSTONE_SETQUEUE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x296*/ { "CMSG_MEETINGSTONE_INFO", STATUS_LOGGEDIN, &WorldSession::HandleMeetingStoneInfo }, - /*0x297*/ { "SMSG_MEETINGSTONE_COMPLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x298*/ { "SMSG_MEETINGSTONE_IN_PROGRESS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x299*/ { "SMSG_MEETINGSTONE_MEMBER_ADDED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x29A*/ { "CMSG_GMTICKETSYSTEM_TOGGLE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x29B*/ { "CMSG_CANCEL_GROWTH_AURA", STATUS_LOGGEDIN, &WorldSession::HandleCancelGrowthAuraOpcode }, - /*0x29C*/ { "SMSG_CANCEL_AUTO_REPEAT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x29D*/ { "SMSG_STANDSTATE_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x29E*/ { "SMSG_LOOT_ALL_PASSED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x29F*/ { "SMSG_LOOT_ROLL_WON", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2A0*/ { "CMSG_LOOT_ROLL", STATUS_LOGGEDIN, &WorldSession::HandleLootRoll }, - /*0x2A1*/ { "SMSG_LOOT_START_ROLL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2A2*/ { "SMSG_LOOT_ROLL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2A3*/ { "CMSG_LOOT_MASTER_GIVE", STATUS_LOGGEDIN, &WorldSession::HandleLootMasterGiveOpcode }, - /*0x2A4*/ { "SMSG_LOOT_MASTER_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2A5*/ { "SMSG_SET_FORCED_REACTIONS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2A6*/ { "SMSG_SPELL_FAILED_OTHER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2A7*/ { "SMSG_GAMEOBJECT_RESET_STATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2A8*/ { "CMSG_REPAIR_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleRepairItemOpcode }, - /*0x2A9*/ { "SMSG_CHAT_PLAYER_NOT_FOUND", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2AA*/ { "MSG_TALENT_WIPE_CONFIRM", STATUS_LOGGEDIN, &WorldSession::HandleTalentWipeOpcode }, - /*0x2AB*/ { "SMSG_SUMMON_REQUEST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2AC*/ { "CMSG_SUMMON_RESPONSE", STATUS_LOGGEDIN, &WorldSession::HandleSummonResponseOpcode }, - /*0x2AD*/ { "MSG_MOVE_TOGGLE_GRAVITY_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x2AE*/ { "SMSG_MONSTER_MOVE_TRANSPORT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2AF*/ { "SMSG_PET_BROKEN", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2B0*/ { "MSG_MOVE_FEATHER_FALL", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x2B1*/ { "MSG_MOVE_WATER_WALK", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x2B2*/ { "CMSG_SERVER_BROADCAST", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x2B3*/ { "CMSG_SELF_RES", STATUS_LOGGEDIN, &WorldSession::HandleSelfResOpcode }, - /*0x2B4*/ { "SMSG_FEIGN_DEATH_RESISTED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2B5*/ { "CMSG_RUN_SCRIPT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x2B6*/ { "SMSG_SCRIPT_MESSAGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2B7*/ { "SMSG_DUEL_COUNTDOWN", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2B8*/ { "SMSG_AREA_TRIGGER_MESSAGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2B9*/ { "CMSG_TOGGLE_HELM", STATUS_LOGGEDIN, &WorldSession::HandleToggleHelmOpcode }, - /*0x2BA*/ { "CMSG_TOGGLE_CLOAK", STATUS_LOGGEDIN, &WorldSession::HandleToggleCloakOpcode }, - /*0x2BB*/ { "SMSG_MEETINGSTONE_JOINFAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2BC*/ { "SMSG_PLAYER_SKINNED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2BD*/ { "SMSG_DURABILITY_DAMAGE_DEATH", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2BE*/ { "CMSG_SET_EXPLORATION", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x2BF*/ { "CMSG_SET_ACTIONBAR_TOGGLES", STATUS_AUTHED, &WorldSession::HandleSetActionBar }, - /*0x2C0*/ { "UMSG_DELETE_GUILD_CHARTER", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x2C1*/ { "MSG_PETITION_RENAME", STATUS_LOGGEDIN, &WorldSession::HandlePetitionRenameOpcode }, - /*0x2C2*/ { "SMSG_INIT_WORLD_STATES", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2C3*/ { "SMSG_UPDATE_WORLD_STATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2C4*/ { "CMSG_ITEM_NAME_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleItemNameQueryOpcode }, - /*0x2C5*/ { "SMSG_ITEM_NAME_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2C6*/ { "SMSG_PET_ACTION_FEEDBACK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2C7*/ { "CMSG_CHAR_RENAME", STATUS_AUTHED, &WorldSession::HandleChangePlayerNameOpcode }, - /*0x2C8*/ { "SMSG_CHAR_RENAME", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2C9*/ { "CMSG_MOVE_SPLINE_DONE", STATUS_LOGGEDIN, &WorldSession::HandleTaxiNextDestinationOpcode }, - /*0x2CA*/ { "CMSG_MOVE_FALL_RESET", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x2CB*/ { "SMSG_INSTANCE_SAVE_CREATED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2CC*/ { "SMSG_RAID_INSTANCE_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2CD*/ { "CMSG_REQUEST_RAID_INFO", STATUS_LOGGEDIN, &WorldSession::HandleRequestRaidInfoOpcode }, - /*0x2CE*/ { "CMSG_MOVE_TIME_SKIPPED", STATUS_LOGGEDIN, &WorldSession::HandleMoveTimeSkippedOpcode }, - /*0x2CF*/ { "CMSG_MOVE_FEATHER_FALL_ACK", STATUS_LOGGEDIN, &WorldSession::HandleFeatherFallAck }, - /*0x2D0*/ { "CMSG_MOVE_WATER_WALK_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveWaterWalkAck }, - /*0x2D1*/ { "CMSG_MOVE_NOT_ACTIVE_MOVER", STATUS_LOGGEDIN, &WorldSession::HandleNotActiveMoverOpcode }, - /*0x2D2*/ { "SMSG_PLAY_SOUND", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2D3*/ { "CMSG_BATTLEFIELD_STATUS", STATUS_LOGGEDIN, &WorldSession::HandleBattlefieldStatusOpcode }, - /*0x2D4*/ { "SMSG_BATTLEFIELD_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2D5*/ { "CMSG_BATTLEFIELD_PORT", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundPlayerPortOpcode}, - /*0x2D6*/ { "MSG_INSPECT_HONOR_STATS", STATUS_LOGGEDIN, &WorldSession::HandleInspectHonorStatsOpcode }, - /*0x2D7*/ { "CMSG_BATTLEMASTER_HELLO", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundHelloOpcode }, - /*0x2D8*/ { "CMSG_MOVE_START_SWIM_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x2D9*/ { "CMSG_MOVE_STOP_SWIM_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x2DA*/ { "SMSG_FORCE_WALK_SPEED_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2DB*/ { "CMSG_FORCE_WALK_SPEED_CHANGE_ACK", STATUS_LOGGEDIN, &WorldSession::HandleForceSpeedChangeAck }, - /*0x2DC*/ { "SMSG_FORCE_SWIM_BACK_SPEED_CHANGE",STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2DD*/ { "CMSG_FORCE_SWIM_BACK_SPEED_CHANGE_ACK",STATUS_LOGGEDIN,&WorldSession::HandleForceSpeedChangeAck }, - /*0x2DE*/ { "SMSG_FORCE_TURN_RATE_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2DF*/ { "CMSG_FORCE_TURN_RATE_CHANGE_ACK", STATUS_LOGGEDIN, &WorldSession::HandleForceSpeedChangeAck }, - /*0x2E0*/ { "MSG_PVP_LOG_DATA", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundPVPlogdataOpcode}, - /*0x2E1*/ { "CMSG_LEAVE_BATTLEFIELD", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundLeaveOpcode }, - /*0x2E2*/ { "CMSG_AREA_SPIRIT_HEALER_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleAreaSpiritHealerQueryOpcode}, - /*0x2E3*/ { "CMSG_AREA_SPIRIT_HEALER_QUEUE", STATUS_LOGGEDIN, &WorldSession::HandleAreaSpiritHealerQueueOpcode}, - /*0x2E4*/ { "SMSG_AREA_SPIRIT_HEALER_TIME", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2E5*/ { "CMSG_GM_UNTEACH", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x2E6*/ { "SMSG_WARDEN_DATA", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2E7*/ { "CMSG_WARDEN_DATA", STATUS_LOGGEDIN, &WorldSession::HandleWardenDataOpcode }, - /*0x2E8*/ { "SMSG_GROUP_JOINED_BATTLEGROUND", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2E9*/ { "MSG_BATTLEGROUND_PLAYER_POSITIONS",STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundPlayerPositionsOpcode}, - /*0x2EA*/ { "CMSG_PET_STOP_ATTACK", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x2EB*/ { "SMSG_BINDER_CONFIRM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2EC*/ { "SMSG_BATTLEGROUND_PLAYER_JOINED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2ED*/ { "SMSG_BATTLEGROUND_PLAYER_LEFT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2EE*/ { "CMSG_BATTLEMASTER_JOIN", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundJoinOpcode }, - /*0x2EF*/ { "SMSG_ADDON_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2F0*/ { "CMSG_PET_UNLEARN", STATUS_LOGGEDIN, &WorldSession::HandlePetUnlearnOpcode }, - /*0x2F1*/ { "SMSG_PET_UNLEARN_CONFIRM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2F2*/ { "SMSG_PARTY_MEMBER_STATS_FULL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2F3*/ { "CMSG_PET_SPELL_AUTOCAST", STATUS_LOGGEDIN, &WorldSession::HandlePetSpellAutocastOpcode }, - /*0x2F4*/ { "SMSG_WEATHER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2F5*/ { "SMSG_PLAY_TIME_WARNING", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2F6*/ { "SMSG_MINIGAME_SETUP", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2F7*/ { "SMSG_MINIGAME_STATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2F8*/ { "CMSG_MINIGAME_MOVE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x2F9*/ { "SMSG_MINIGAME_MOVE_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2FA*/ { "SMSG_RAID_INSTANCE_MESSAGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2FB*/ { "SMSG_COMPRESSED_MOVES", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2FC*/ { "CMSG_GUILD_INFO_TEXT", STATUS_LOGGEDIN, &WorldSession::HandleGuildChangeInfoOpcode }, - /*0x2FD*/ { "SMSG_CHAT_RESTRICTED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2FE*/ { "SMSG_SPLINE_SET_RUN_SPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x2FF*/ { "SMSG_SPLINE_SET_RUN_BACK_SPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x300*/ { "SMSG_SPLINE_SET_SWIM_SPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x301*/ { "SMSG_SPLINE_SET_WALK_SPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x302*/ { "SMSG_SPLINE_SET_SWIM_BACK_SPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x303*/ { "SMSG_SPLINE_SET_TURN_RATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x304*/ { "SMSG_SPLINE_MOVE_UNROOT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x305*/ { "SMSG_SPLINE_MOVE_FEATHER_FALL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x306*/ { "SMSG_SPLINE_MOVE_NORMAL_FALL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x307*/ { "SMSG_SPLINE_MOVE_SET_HOVER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x308*/ { "SMSG_SPLINE_MOVE_UNSET_HOVER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x309*/ { "SMSG_SPLINE_MOVE_WATER_WALK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x30A*/ { "SMSG_SPLINE_MOVE_LAND_WALK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x30B*/ { "SMSG_SPLINE_MOVE_START_SWIM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x30C*/ { "SMSG_SPLINE_MOVE_STOP_SWIM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x30D*/ { "SMSG_SPLINE_MOVE_SET_RUN_MODE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x30E*/ { "SMSG_SPLINE_MOVE_SET_WALK_MODE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x30F*/ { "CMSG_GM_NUKE_ACCOUNT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x310*/ { "MSG_GM_DESTROY_CORPSE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x311*/ { "CMSG_GM_DESTROY_ONLINE_CORPSE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x312*/ { "CMSG_ACTIVATETAXIEXPRESS", STATUS_LOGGEDIN, &WorldSession::HandleActivateTaxiFarOpcode }, - /*0x313*/ { "SMSG_SET_FACTION_ATWAR", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x314*/ { "SMSG_GAMETIMEBIAS_SET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x315*/ { "CMSG_DEBUG_ACTIONS_START", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x316*/ { "CMSG_DEBUG_ACTIONS_STOP", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x317*/ { "CMSG_SET_FACTION_INACTIVE", STATUS_LOGGEDIN, &WorldSession::HandleSetWatchedFactionInactiveOpcode}, - /*0x318*/ { "CMSG_SET_WATCHED_FACTION", STATUS_LOGGEDIN, &WorldSession::HandleSetWatchedFactionIndexOpcode}, - /*0x319*/ { "MSG_MOVE_TIME_SKIPPED", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x31A*/ { "SMSG_SPLINE_MOVE_ROOT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x31B*/ { "CMSG_SET_EXPLORATION_ALL", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x31C*/ { "SMSG_INVALIDATE_PLAYER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x31D*/ { "CMSG_RESET_INSTANCES", STATUS_LOGGEDIN, &WorldSession::HandleResetInstancesOpcode }, - /*0x31E*/ { "SMSG_INSTANCE_RESET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x31F*/ { "SMSG_INSTANCE_RESET_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x320*/ { "SMSG_UPDATE_LAST_INSTANCE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x321*/ { "MSG_RAID_TARGET_UPDATE", STATUS_LOGGEDIN, &WorldSession::HandleRaidIconTargetOpcode }, - /*0x322*/ { "MSG_RAID_READY_CHECK", STATUS_LOGGEDIN, &WorldSession::HandleRaidReadyCheckOpcode }, - /*0x323*/ { "CMSG_LUA_USAGE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x324*/ { "SMSG_PET_ACTION_SOUND", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x325*/ { "SMSG_PET_DISMISS_SOUND", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x326*/ { "SMSG_GHOSTEE_GONE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x327*/ { "CMSG_GM_UPDATE_TICKET_STATUS", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x328*/ { "SMSG_GM_TICKET_STATUS_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x329*/ { "MSG_SET_DUNGEON_DIFFICULTY", STATUS_LOGGEDIN, &WorldSession::HandleDungeonDifficultyOpcode }, - /*0x32A*/ { "CMSG_GMSURVEY_SUBMIT", STATUS_LOGGEDIN, &WorldSession::Handle_NULL },//&WorldSession::HandleGMSurveySubmit - /*0x32B*/ { "SMSG_UPDATE_INSTANCE_OWNERSHIP", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x32C*/ { "CMSG_IGNORE_KNOCKBACK_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x32D*/ { "SMSG_CHAT_PLAYER_AMBIGUOUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x32E*/ { "MSG_DELAY_GHOST_TELEPORT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x32F*/ { "SMSG_SPELLINSTAKILLLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x330*/ { "SMSG_SPELL_UPDATE_CHAIN_TARGETS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x331*/ { "CMSG_CHAT_FILTERED", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x332*/ { "SMSG_EXPECTED_SPAM_RECORDS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x333*/ { "SMSG_SPELLSTEALLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x334*/ { "CMSG_LOTTERY_QUERY_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x335*/ { "SMSG_LOTTERY_QUERY_RESULT_OBSOLETE",STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x336*/ { "CMSG_BUY_LOTTERY_TICKET_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x337*/ { "SMSG_LOTTERY_RESULT_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x338*/ { "SMSG_CHARACTER_PROFILE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x339*/ { "SMSG_CHARACTER_PROFILE_REALM_CONNECTED",STATUS_NEVER,&WorldSession::Handle_ServerSide }, - /*0x33A*/ { "SMSG_DEFENSE_MESSAGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x33B*/ { "SMSG_INSTANCE_DIFFICULTY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x33C*/ { "MSG_GM_RESETINSTANCELIMIT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x33D*/ { "SMSG_MOTD", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x33E*/ { "SMSG_MOVE_SET_FLIGHT_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x33F*/ { "SMSG_MOVE_UNSET_FLIGHT_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x340*/ { "CMSG_MOVE_FLIGHT_ACK_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x341*/ { "MSG_MOVE_START_SWIM_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x342*/ { "MSG_MOVE_STOP_SWIM_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x343*/ { "SMSG_MOVE_SET_CAN_FLY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x344*/ { "SMSG_MOVE_UNSET_CAN_FLY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x345*/ { "CMSG_MOVE_SET_CAN_FLY_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveFlyModeChangeAckOpcode}, - /*0x346*/ { "CMSG_MOVE_SET_FLY", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x347*/ { "CMSG_SOCKET_GEMS", STATUS_LOGGEDIN, &WorldSession::HandleSocketOpcode }, - /*0x348*/ { "CMSG_ARENA_TEAM_CREATE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x349*/ { "SMSG_ARENA_TEAM_COMMAND_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x34A*/ { "UMSG_UPDATE_ARENA_TEAM_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x34B*/ { "CMSG_ARENA_TEAM_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamQueryOpcode }, - /*0x34C*/ { "SMSG_ARENA_TEAM_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x34D*/ { "CMSG_ARENA_TEAM_ROSTER", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamRosterOpcode }, - /*0x34E*/ { "SMSG_ARENA_TEAM_ROSTER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x34F*/ { "CMSG_ARENA_TEAM_INVITE", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamAddMemberOpcode }, - /*0x350*/ { "SMSG_ARENA_TEAM_INVITE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x351*/ { "CMSG_ARENA_TEAM_ACCEPT", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamInviteAcceptOpcode}, - /*0x352*/ { "CMSG_ARENA_TEAM_DECLINE", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamInviteDeclineOpcode}, - /*0x353*/ { "CMSG_ARENA_TEAM_LEAVE", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamLeaveOpcode }, - /*0x354*/ { "CMSG_ARENA_TEAM_REMOVE", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamRemoveFromTeamOpcode}, - /*0x355*/ { "CMSG_ARENA_TEAM_DISBAND", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamDisbandOpcode }, - /*0x356*/ { "CMSG_ARENA_TEAM_LEADER", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamPromoteToCaptainOpcode}, - /*0x357*/ { "SMSG_ARENA_TEAM_EVENT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x358*/ { "CMSG_BATTLEMASTER_JOIN_ARENA", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundArenaJoin }, - /*0x359*/ { "MSG_MOVE_START_ASCEND", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x35A*/ { "MSG_MOVE_STOP_ASCEND", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x35B*/ { "SMSG_ARENA_TEAM_STATS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x35C*/ { "CMSG_LFG_SET_AUTOJOIN", STATUS_AUTHED, &WorldSession::HandleLfgAutoJoinOpcode }, - /*0x35D*/ { "CMSG_LFG_CLEAR_AUTOJOIN", STATUS_LOGGEDIN, &WorldSession::HandleLfgCancelAutoJoinOpcode }, - /*0x35E*/ { "CMSG_LFM_SET_AUTOFILL", STATUS_AUTHED, &WorldSession::HandleLfmAutoAddMembersOpcode }, - /*0x35F*/ { "CMSG_LFM_CLEAR_AUTOFILL", STATUS_LOGGEDIN, &WorldSession::HandleLfmCancelAutoAddmembersOpcode}, - /*0x360*/ { "CMSG_ACCEPT_LFG_MATCH", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x361*/ { "CMSG_DECLINE_LFG_MATCH", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x362*/ { "CMSG_CANCEL_PENDING_LFG", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x363*/ { "CMSG_CLEAR_LOOKING_FOR_GROUP", STATUS_LOGGEDIN, &WorldSession::HandleLfgClearOpcode }, - /*0x364*/ { "CMSG_CLEAR_LOOKING_FOR_MORE", STATUS_LOGGEDIN, &WorldSession::HandleLfmSetNoneOpcode }, - /*0x365*/ { "CMSG_SET_LOOKING_FOR_MORE", STATUS_LOGGEDIN, &WorldSession::HandleLfmSetOpcode }, - /*0x366*/ { "CMSG_SET_LFG_COMMENT", STATUS_LOGGEDIN, &WorldSession::HandleLfgSetCommentOpcode }, - /*0x367*/ { "SMSG_LFG_TIMEDOUT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x368*/ { "SMSG_LFG_OTHER_TIMEDOUT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x369*/ { "SMSG_LFG_AUTOJOIN_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x36A*/ { "SMSG_LFG_AUTOJOIN_FAILED_NO_PLAYER",STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x36B*/ { "SMSG_LFG_LEADER_IS_LFM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x36C*/ { "SMSG_LFG_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x36D*/ { "SMSG_LFG_UPDATE_LFM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x36E*/ { "SMSG_LFG_UPDATE_LFG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x36F*/ { "SMSG_LFG_UPDATE_QUEUED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x370*/ { "SMSG_LFG_PENDING_INVITE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x371*/ { "SMSG_LFG_PENDING_MATCH", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x372*/ { "SMSG_LFG_PENDING_MATCH_DONE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x373*/ { "SMSG_TITLE_EARNED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x374*/ { "CMSG_SET_TITLE", STATUS_LOGGEDIN, &WorldSession::HandleChooseTitleOpcode }, - /*0x375*/ { "CMSG_CANCEL_MOUNT_AURA", STATUS_LOGGEDIN, &WorldSession::HandleDismountOpcode }, - /*0x376*/ { "SMSG_ARENA_ERROR", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x377*/ { "MSG_INSPECT_ARENA_TEAMS", STATUS_LOGGEDIN, &WorldSession::HandleInspectArenaStatsOpcode }, - /*0x378*/ { "SMSG_DEATH_RELEASE_LOC", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x379*/ { "CMSG_CANCEL_TEMP_ENCHANTMENT", STATUS_LOGGEDIN, &WorldSession::HandleCancelTempItemEnchantmentOpcode}, - /*0x37A*/ { "SMSG_FORCED_DEATH_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x37B*/ { "CMSG_CHEAT_SET_HONOR_CURRENCY", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x37C*/ { "CMSG_CHEAT_SET_ARENA_CURRENCY", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x37D*/ { "MSG_MOVE_SET_FLIGHT_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x37E*/ { "MSG_MOVE_SET_FLIGHT_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x37F*/ { "MSG_MOVE_SET_FLIGHT_BACK_SPEED_CHEAT",STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x380*/ { "MSG_MOVE_SET_FLIGHT_BACK_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x381*/ { "SMSG_FORCE_FLIGHT_SPEED_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x382*/ { "CMSG_FORCE_FLIGHT_SPEED_CHANGE_ACK",STATUS_LOGGEDIN,&WorldSession::HandleForceSpeedChangeAck }, - /*0x383*/ { "SMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE",STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x384*/ { "CMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE_ACK",STATUS_LOGGEDIN,&WorldSession::HandleForceSpeedChangeAck }, - /*0x385*/ { "SMSG_SPLINE_SET_FLIGHT_SPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x386*/ { "SMSG_SPLINE_SET_FLIGHT_BACK_SPEED",STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x387*/ { "CMSG_MAELSTROM_INVALIDATE_CACHE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x388*/ { "SMSG_FLIGHT_SPLINE_SYNC", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x389*/ { "CMSG_SET_TAXI_BENCHMARK_MODE", STATUS_AUTHED, &WorldSession::HandleSetTaxiBenchmarkOpcode }, - /*0x38A*/ { "SMSG_JOINED_BATTLEGROUND_QUEUE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x38B*/ { "SMSG_REALM_SPLIT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x38C*/ { "CMSG_REALM_SPLIT", STATUS_AUTHED, &WorldSession::HandleRealmStateRequestOpcode }, - /*0x38D*/ { "CMSG_MOVE_CHNG_TRANSPORT", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x38E*/ { "MSG_PARTY_ASSIGNMENT", STATUS_LOGGEDIN, &WorldSession::HandleGroupPromoteOpcode }, - /*0x38F*/ { "SMSG_OFFER_PETITION_ERROR", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x390*/ { "SMSG_TIME_SYNC_REQ", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x391*/ { "CMSG_TIME_SYNC_RESP", STATUS_LOGGEDIN, &WorldSession::HandleAllowMoveAckOpcode }, - /*0x392*/ { "CMSG_SEND_LOCAL_EVENT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x393*/ { "CMSG_SEND_GENERAL_TRIGGER", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x394*/ { "CMSG_SEND_COMBAT_TRIGGER", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x395*/ { "CMSG_MAELSTROM_GM_SENT_MAIL", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x396*/ { "SMSG_RESET_FAILED_NOTIFY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x397*/ { "SMSG_REAL_GROUP_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x398*/ { "SMSG_LFG_DISABLED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x399*/ { "CMSG_ACTIVE_PVP_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x39A*/ { "CMSG_CHEAT_DUMP_ITEMS_DEBUG_ONLY", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x39B*/ { "SMSG_CHEAT_DUMP_ITEMS_DEBUG_ONLY_RESPONSE",STATUS_NEVER,&WorldSession::Handle_ServerSide }, - /*0x39C*/ { "SMSG_CHEAT_DUMP_ITEMS_DEBUG_ONLY_RESPONSE_WRITE_FILE",STATUS_NEVER,&WorldSession::Handle_ServerSide}, - /*0x39D*/ { "SMSG_UPDATE_COMBO_POINTS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x39E*/ { "SMSG_VOICE_SESSION_ROSTER_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x39F*/ { "SMSG_VOICE_SESSION_LEAVE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3A0*/ { "SMSG_VOICE_SESSION_ADJUST_PRIORITY",STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3A1*/ { "CMSG_VOICE_SET_TALKER_MUTED_REQUEST",STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3A2*/ { "SMSG_VOICE_SET_TALKER_MUTED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3A3*/ { "SMSG_INIT_EXTRA_AURA_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3A4*/ { "SMSG_SET_EXTRA_AURA_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3A5*/ { "SMSG_SET_EXTRA_AURA_INFO_NEED_UPDATE",STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3A6*/ { "SMSG_CLEAR_EXTRA_AURA_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3A7*/ { "MSG_MOVE_START_DESCEND", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, - /*0x3A8*/ { "CMSG_IGNORE_REQUIREMENTS_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3A9*/ { "SMSG_IGNORE_REQUIREMENTS_CHEAT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3AA*/ { "SMSG_SPELL_CHANCE_PROC_LOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3AB*/ { "CMSG_MOVE_SET_RUN_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3AC*/ { "SMSG_DISMOUNT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3AD*/ { "MSG_MOVE_UPDATE_CAN_FLY", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3AE*/ { "MSG_RAID_READY_CHECK_CONFIRM", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3AF*/ { "CMSG_VOICE_SESSION_ENABLE", STATUS_AUTHED, &WorldSession::HandleVoiceSettingsOpcode }, - /*0x3B0*/ { "SMSG_VOICE_PARENTAL_CONTROLS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3B1*/ { "CMSG_GM_WHISPER", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3B2*/ { "SMSG_GM_MESSAGECHAT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3B3*/ { "MSG_GM_GEARRATING", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3B4*/ { "CMSG_COMMENTATOR_ENABLE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3B5*/ { "SMSG_COMMENTATOR_STATE_CHANGED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3B6*/ { "CMSG_COMMENTATOR_GET_MAP_INFO", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3B7*/ { "SMSG_COMMENTATOR_MAP_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3B8*/ { "CMSG_COMMENTATOR_GET_PLAYER_INFO", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3B9*/ { "SMSG_COMMENTATOR_GET_PLAYER_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3BA*/ { "SMSG_COMMENTATOR_PLAYER_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3BB*/ { "CMSG_COMMENTATOR_ENTER_INSTANCE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3BC*/ { "CMSG_COMMENTATOR_EXIT_INSTANCE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3BD*/ { "CMSG_COMMENTATOR_INSTANCE_COMMAND",STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3BE*/ { "SMSG_CLEAR_TARGET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3BF*/ { "CMSG_BOT_DETECTED", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3C0*/ { "SMSG_CROSSED_INEBRIATION_THRESHOLD",STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3C1*/ { "CMSG_CHEAT_PLAYER_LOGIN", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3C2*/ { "CMSG_CHEAT_PLAYER_LOOKUP", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3C3*/ { "SMSG_CHEAT_PLAYER_LOOKUP", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3C4*/ { "SMSG_KICK_REASON", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3C5*/ { "MSG_RAID_READY_CHECK_FINISHED", STATUS_LOGGEDIN, &WorldSession::HandleRaidReadyCheckFinishOpcode}, - /*0x3C6*/ { "CMSG_COMPLAIN", STATUS_LOGGEDIN, &WorldSession::HandleReportSpamOpcode }, - /*0x3C7*/ { "SMSG_COMPLAIN_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3C8*/ { "SMSG_FEATURE_SYSTEM_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3C9*/ { "CMSG_GM_SHOW_COMPLAINTS", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3CA*/ { "CMSG_GM_UNSQUELCH", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3CB*/ { "CMSG_CHANNEL_SILENCE_VOICE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3CC*/ { "CMSG_CHANNEL_SILENCE_ALL", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3CD*/ { "CMSG_CHANNEL_UNSILENCE_VOICE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3CE*/ { "CMSG_CHANNEL_UNSILENCE_ALL", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3CF*/ { "CMSG_TARGET_CAST", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3D0*/ { "CMSG_TARGET_SCRIPT_CAST", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3D1*/ { "CMSG_CHANNEL_DISPLAY_LIST", STATUS_LOGGEDIN, &WorldSession::HandleChannelRosterQuery }, - /*0x3D2*/ { "CMSG_SET_ACTIVE_VOICE_CHANNEL", STATUS_AUTHED, &WorldSession::HandleChannelVoiceChatQuery }, - /*0x3D3*/ { "CMSG_GET_CHANNEL_MEMBER_COUNT", STATUS_LOGGEDIN, &WorldSession::HandleChannelInfoQuery }, - /*0x3D4*/ { "SMSG_CHANNEL_MEMBER_COUNT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3D5*/ { "CMSG_CHANNEL_VOICE_ON", STATUS_LOGGEDIN, &WorldSession::HandleChannelEnableVoiceOpcode }, - /*0x3D6*/ { "CMSG_CHANNEL_VOICE_OFF", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3D7*/ { "CMSG_DEBUG_LIST_TARGETS", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3D8*/ { "SMSG_DEBUG_LIST_TARGETS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3D9*/ { "SMSG_AVAILABLE_VOICE_CHANNEL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3DA*/ { "CMSG_ADD_VOICE_IGNORE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3DB*/ { "CMSG_DEL_VOICE_IGNORE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3DC*/ { "CMSG_PARTY_SILENCE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3DD*/ { "CMSG_PARTY_UNSILENCE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3DE*/ { "MSG_NOTIFY_PARTY_SQUELCH", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3DF*/ { "SMSG_COMSAT_RECONNECT_TRY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3E0*/ { "SMSG_COMSAT_DISCONNECT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3E1*/ { "SMSG_COMSAT_CONNECT_FAIL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3E2*/ { "SMSG_VOICE_CHAT_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3E3*/ { "CMSG_REPORT_PVP_AFK", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundReportAFK }, - /*0x3E4*/ { "CMSG_REPORT_PVP_AFK_RESULT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3E5*/ { "CMSG_GUILD_BANKER_ACTIVATE", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankQuery }, - /*0x3E6*/ { "CMSG_GUILD_BANK_QUERY_TAB", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankTabColon }, - /*0x3E7*/ { "SMSG_GUILD_BANK_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3E8*/ { "CMSG_GUILD_BANK_SWAP_ITEMS", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankDepositItem }, - /*0x3E9*/ { "CMSG_GUILD_BANK_BUY_TAB", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankBuyTab }, - /*0x3EA*/ { "CMSG_GUILD_BANK_UPDATE_TAB", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankModifyTab }, - /*0x3EB*/ { "CMSG_GUILD_BANK_DEPOSIT_MONEY", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankDeposit }, - /*0x3EC*/ { "CMSG_GUILD_BANK_WITHDRAW_MONEY", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankWithdraw }, - /*0x3ED*/ { "MSG_GUILD_BANK_LOG_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankLog }, - /*0x3EE*/ { "CMSG_SET_CHANNEL_WATCH", STATUS_LOGGEDIN, &WorldSession::HandleChannelJoinNotify }, - /*0x3EF*/ { "SMSG_USERLIST_ADD", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3F0*/ { "SMSG_USERLIST_REMOVE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3F1*/ { "SMSG_USERLIST_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3F2*/ { "CMSG_CLEAR_CHANNEL_WATCH", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3F3*/ { "SMSG_INSPECT_TALENT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3F4*/ { "SMSG_GOGOGO_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3F5*/ { "SMSG_ECHO_PARTY_SQUELCH", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3F6*/ { "CMSG_SET_TITLE_SUFFIX", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3F7*/ { "CMSG_SPELLCLICK", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3F8*/ { "SMSG_LOOT_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3F9*/ { "CMSG_GM_CHARACTER_RESTORE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3FA*/ { "CMSG_GM_CHARACTER_SAVE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x3FB*/ { "SMSG_VOICESESSION_FULL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x3FC*/ { "MSG_GUILD_PERMISSIONS", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankGetRights }, - /*0x3FD*/ { "MSG_GUILD_BANK_MONEY_WITHDRAWN", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankGetMoneyAmount }, - /*0x3FE*/ { "MSG_GUILD_EVENT_LOG_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleGuildEventLogOpcode }, - /*0x3FF*/ { "CMSG_MAELSTROM_RENAME_GUILD", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x400*/ { "CMSG_GET_MIRRORIMAGE_DATA", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x401*/ { "SMSG_MIRRORIMAGE_DATA", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x402*/ { "SMSG_FORCE_DISPLAY_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x403*/ { "SMSG_SPELL_CHANCE_RESIST_PUSHBACK",STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x404*/ { "CMSG_IGNORE_DIMINISHING_RETURNS_CHEAT",STATUS_NEVER,&WorldSession::Handle_NULL }, - /*0x405*/ { "SMSG_IGNORE_DIMINISHING_RETURNS_CHEAT",STATUS_NEVER,&WorldSession::Handle_ServerSide }, - /*0x406*/ { "CMSG_KEEP_ALIVE", STATUS_NEVER, &WorldSession::Handle_EarlyProccess }, - /*0x407*/ { "SMSG_RAID_READY_CHECK_ERROR", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x408*/ { "CMSG_OPT_OUT_OF_LOOT", STATUS_AUTHED, &WorldSession::HandleGroupPassOnLootOpcode }, - /*0x409*/ { "MSG_QUERY_GUILD_BANK_TEXT", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankTabText }, - /*0x40A*/ { "CMSG_SET_GUILD_BANK_TEXT", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankSetTabText }, - /*0x40B*/ { "CMSG_SET_GRANTABLE_LEVELS", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x40C*/ { "CMSG_GRANT_LEVEL", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x40D*/ { "CMSG_REFER_A_FRIEND", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x40E*/ { "MSG_GM_CHANGE_ARENA_RATING", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x40F*/ { "CMSG_DECLINE_CHANNEL_INVITE", STATUS_LOGGEDIN, &WorldSession::HandleChannelDeclineInvite }, - /*0x410*/ { "CMSG_GROUPACTION_THROTTLED", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x411*/ { "SMSG_OVERRIDE_LIGHT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x412*/ { "SMSG_TOTEM_CREATED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x413*/ { "CMSG_TOTEM_DESTROYED", STATUS_LOGGEDIN, &WorldSession::HandleTotemDestroy }, - /*0x414*/ { "CMSG_EXPIRE_RAID_INSTANCE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x415*/ { "CMSG_NO_SPELL_VARIANCE", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x416*/ { "CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY",STATUS_LOGGEDIN,&WorldSession::HandleQuestgiverStatusQueryMultipleOpcode}, - /*0x417*/ { "SMSG_QUESTGIVER_STATUS_MULTIPLE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x418*/ { "CMSG_SET_PLAYER_DECLINED_NAMES", STATUS_AUTHED, &WorldSession::HandleDeclinedPlayerNameOpcode }, - /*0x419*/ { "SMSG_SET_PLAYER_DECLINED_NAMES_RESULT",STATUS_NEVER,&WorldSession::Handle_ServerSide }, - /*0x41A*/ { "CMSG_QUERY_SERVER_BUCK_DATA", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x41B*/ { "CMSG_CLEAR_SERVER_BUCK_DATA", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x41C*/ { "SMSG_SERVER_BUCK_DATA", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x41D*/ { "SMSG_SEND_UNLEARN_SPELLS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x41E*/ { "SMSG_PROPOSE_LEVEL_GRANT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x41F*/ { "CMSG_ACCEPT_LEVEL_GRANT", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x420*/ { "SMSG_REFER_A_FRIEND_FAILURE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x421*/ { "SMSG_SPLINE_MOVE_SET_FLYING", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x422*/ { "SMSG_SPLINE_MOVE_UNSET_FLYING", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x423*/ { "SMSG_SUMMON_CANCEL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x000*/ { "MSG_NULL_ACTION", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x001*/ { "CMSG_BOOTME", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x002*/ { "CMSG_DBLOOKUP", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x003*/ { "SMSG_DBLOOKUP", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x004*/ { "CMSG_QUERY_OBJECT_POSITION", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x005*/ { "SMSG_QUERY_OBJECT_POSITION", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x006*/ { "CMSG_QUERY_OBJECT_ROTATION", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x007*/ { "SMSG_QUERY_OBJECT_ROTATION", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x008*/ { "CMSG_WORLD_TELEPORT", STATUS_LOGGEDIN, &WorldSession::HandleWorldTeleportOpcode }, + /*0x009*/ { "CMSG_TELEPORT_TO_UNIT", STATUS_LOGGEDIN, &WorldSession::Handle_NULL }, + /*0x00A*/ { "CMSG_ZONE_MAP", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x00B*/ { "SMSG_ZONE_MAP", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x00C*/ { "CMSG_DEBUG_CHANGECELLZONE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x00D*/ { "CMSG_MOVE_CHARACTER_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x00E*/ { "SMSG_MOVE_CHARACTER_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x00F*/ { "CMSG_RECHARGE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x010*/ { "CMSG_LEARN_SPELL", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x011*/ { "CMSG_CREATEMONSTER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x012*/ { "CMSG_DESTROYMONSTER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x013*/ { "CMSG_CREATEITEM", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x014*/ { "CMSG_CREATEGAMEOBJECT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x015*/ { "SMSG_CHECK_FOR_BOTS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x016*/ { "CMSG_MAKEMONSTERATTACKGUID", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x017*/ { "CMSG_BOT_DETECTED2", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x018*/ { "CMSG_FORCEACTION", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x019*/ { "CMSG_FORCEACTIONONOTHER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x01A*/ { "CMSG_FORCEACTIONSHOW", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x01B*/ { "SMSG_FORCEACTIONSHOW", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x01C*/ { "CMSG_PETGODMODE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x01D*/ { "SMSG_PETGODMODE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x01E*/ { "SMSG_REFER_A_FRIEND_EXPIRED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x01F*/ { "CMSG_WEATHER_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x020*/ { "CMSG_UNDRESSPLAYER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x021*/ { "CMSG_BEASTMASTER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x022*/ { "CMSG_GODMODE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x023*/ { "SMSG_GODMODE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x024*/ { "CMSG_CHEAT_SETMONEY", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x025*/ { "CMSG_LEVEL_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x026*/ { "CMSG_PET_LEVEL_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x027*/ { "CMSG_SET_WORLDSTATE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x028*/ { "CMSG_COOLDOWN_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x029*/ { "CMSG_USE_SKILL_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x02A*/ { "CMSG_FLAG_QUEST", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x02B*/ { "CMSG_FLAG_QUEST_FINISH", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x02C*/ { "CMSG_CLEAR_QUEST", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x02D*/ { "CMSG_SEND_EVENT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x02E*/ { "CMSG_DEBUG_AISTATE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x02F*/ { "SMSG_DEBUG_AISTATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x030*/ { "CMSG_DISABLE_PVP_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x031*/ { "CMSG_ADVANCE_SPAWN_TIME", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x032*/ { "SMSG_DESTRUCTIBLE_BUILDING_DAMAGE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x033*/ { "CMSG_AUTH_SRP6_BEGIN", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x034*/ { "CMSG_AUTH_SRP6_PROOF", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x035*/ { "CMSG_AUTH_SRP6_RECODE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x036*/ { "CMSG_CHAR_CREATE", STATUS_AUTHED, &WorldSession::HandleCharCreateOpcode }, + /*0x037*/ { "CMSG_CHAR_ENUM", STATUS_AUTHED, &WorldSession::HandleCharEnumOpcode }, + /*0x038*/ { "CMSG_CHAR_DELETE", STATUS_AUTHED, &WorldSession::HandleCharDeleteOpcode }, + /*0x039*/ { "SMSG_AUTH_SRP6_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x03A*/ { "SMSG_CHAR_CREATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x03B*/ { "SMSG_CHAR_ENUM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x03C*/ { "SMSG_CHAR_DELETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x03D*/ { "CMSG_PLAYER_LOGIN", STATUS_AUTHED, &WorldSession::HandlePlayerLoginOpcode }, + /*0x03E*/ { "SMSG_NEW_WORLD", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x03F*/ { "SMSG_TRANSFER_PENDING", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x040*/ { "SMSG_TRANSFER_ABORTED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x041*/ { "SMSG_CHARACTER_LOGIN_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x042*/ { "SMSG_LOGIN_SETTIMESPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x043*/ { "SMSG_GAMETIME_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x044*/ { "CMSG_GAMETIME_SET", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x045*/ { "SMSG_GAMETIME_SET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x046*/ { "CMSG_GAMESPEED_SET", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x047*/ { "SMSG_GAMESPEED_SET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x048*/ { "CMSG_SERVERTIME", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x049*/ { "SMSG_SERVERTIME", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x04A*/ { "CMSG_PLAYER_LOGOUT", STATUS_LOGGEDIN, &WorldSession::HandlePlayerLogoutOpcode }, + /*0x04B*/ { "CMSG_LOGOUT_REQUEST", STATUS_LOGGEDIN, &WorldSession::HandleLogoutRequestOpcode }, + /*0x04C*/ { "SMSG_LOGOUT_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x04D*/ { "SMSG_LOGOUT_COMPLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x04E*/ { "CMSG_LOGOUT_CANCEL", STATUS_LOGGEDIN, &WorldSession::HandleLogoutCancelOpcode }, + /*0x04F*/ { "SMSG_LOGOUT_CANCEL_ACK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x050*/ { "CMSG_NAME_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleNameQueryOpcode }, + /*0x051*/ { "SMSG_NAME_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x052*/ { "CMSG_PET_NAME_QUERY", STATUS_LOGGEDIN, &WorldSession::HandlePetNameQuery }, + /*0x053*/ { "SMSG_PET_NAME_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x054*/ { "CMSG_GUILD_QUERY", STATUS_AUTHED, &WorldSession::HandleGuildQueryOpcode }, + /*0x055*/ { "SMSG_GUILD_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x056*/ { "CMSG_ITEM_QUERY_SINGLE", STATUS_LOGGEDIN, &WorldSession::HandleItemQuerySingleOpcode }, + /*0x057*/ { "CMSG_ITEM_QUERY_MULTIPLE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x058*/ { "SMSG_ITEM_QUERY_SINGLE_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x059*/ { "SMSG_ITEM_QUERY_MULTIPLE_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x05A*/ { "CMSG_PAGE_TEXT_QUERY", STATUS_LOGGEDIN, &WorldSession::HandlePageQueryOpcode }, + /*0x05B*/ { "SMSG_PAGE_TEXT_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x05C*/ { "CMSG_QUEST_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleQuestQueryOpcode }, + /*0x05D*/ { "SMSG_QUEST_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x05E*/ { "CMSG_GAMEOBJECT_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleGameObjectQueryOpcode }, + /*0x05F*/ { "SMSG_GAMEOBJECT_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x060*/ { "CMSG_CREATURE_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleCreatureQueryOpcode }, + /*0x061*/ { "SMSG_CREATURE_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x062*/ { "CMSG_WHO", STATUS_LOGGEDIN, &WorldSession::HandleWhoOpcode }, + /*0x063*/ { "SMSG_WHO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x064*/ { "CMSG_WHOIS", STATUS_LOGGEDIN, &WorldSession::HandleWhoisOpcode }, + /*0x065*/ { "SMSG_WHOIS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x066*/ { "CMSG_CONTACT_LIST", STATUS_LOGGEDIN, &WorldSession::HandleFriendListOpcode }, + /*0x067*/ { "SMSG_CONTACT_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x068*/ { "SMSG_FRIEND_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x069*/ { "CMSG_ADD_FRIEND", STATUS_LOGGEDIN, &WorldSession::HandleAddFriendOpcode }, + /*0x06A*/ { "CMSG_DEL_FRIEND", STATUS_LOGGEDIN, &WorldSession::HandleDelFriendOpcode }, + /*0x06B*/ { "CMSG_SET_CONTACT_NOTES", STATUS_LOGGEDIN, &WorldSession::HandleSetFriendNoteOpcode }, + /*0x06C*/ { "CMSG_ADD_IGNORE", STATUS_LOGGEDIN, &WorldSession::HandleAddIgnoreOpcode }, + /*0x06D*/ { "CMSG_DEL_IGNORE", STATUS_LOGGEDIN, &WorldSession::HandleDelIgnoreOpcode }, + /*0x06E*/ { "CMSG_GROUP_INVITE", STATUS_LOGGEDIN, &WorldSession::HandleGroupInviteOpcode }, + /*0x06F*/ { "SMSG_GROUP_INVITE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x070*/ { "CMSG_GROUP_CANCEL", STATUS_LOGGEDIN, &WorldSession::Handle_Deprecated }, + /*0x071*/ { "SMSG_GROUP_CANCEL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x072*/ { "CMSG_GROUP_ACCEPT", STATUS_LOGGEDIN, &WorldSession::HandleGroupAcceptOpcode }, + /*0x073*/ { "CMSG_GROUP_DECLINE", STATUS_LOGGEDIN, &WorldSession::HandleGroupDeclineOpcode }, + /*0x074*/ { "SMSG_GROUP_DECLINE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x075*/ { "CMSG_GROUP_UNINVITE", STATUS_LOGGEDIN, &WorldSession::HandleGroupUninviteNameOpcode }, + /*0x076*/ { "CMSG_GROUP_UNINVITE_GUID", STATUS_LOGGEDIN, &WorldSession::HandleGroupUninviteGuidOpcode }, + /*0x077*/ { "SMSG_GROUP_UNINVITE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x078*/ { "CMSG_GROUP_SET_LEADER", STATUS_LOGGEDIN, &WorldSession::HandleGroupSetLeaderOpcode }, + /*0x079*/ { "SMSG_GROUP_SET_LEADER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x07A*/ { "CMSG_LOOT_METHOD", STATUS_LOGGEDIN, &WorldSession::HandleLootMethodOpcode }, + /*0x07B*/ { "CMSG_GROUP_DISBAND", STATUS_LOGGEDIN, &WorldSession::HandleGroupLeaveOpcode }, + /*0x07C*/ { "SMSG_GROUP_DESTROYED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x07D*/ { "SMSG_GROUP_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x07E*/ { "SMSG_PARTY_MEMBER_STATS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x07F*/ { "SMSG_PARTY_COMMAND_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x080*/ { "UMSG_UPDATE_GROUP_MEMBERS", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x081*/ { "CMSG_GUILD_CREATE", STATUS_LOGGEDIN, &WorldSession::HandleGuildCreateOpcode }, + /*0x082*/ { "CMSG_GUILD_INVITE", STATUS_LOGGEDIN, &WorldSession::HandleGuildInviteOpcode }, + /*0x083*/ { "SMSG_GUILD_INVITE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x084*/ { "CMSG_GUILD_ACCEPT", STATUS_LOGGEDIN, &WorldSession::HandleGuildAcceptOpcode }, + /*0x085*/ { "CMSG_GUILD_DECLINE", STATUS_LOGGEDIN, &WorldSession::HandleGuildDeclineOpcode }, + /*0x086*/ { "SMSG_GUILD_DECLINE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x087*/ { "CMSG_GUILD_INFO", STATUS_LOGGEDIN, &WorldSession::HandleGuildInfoOpcode }, + /*0x088*/ { "SMSG_GUILD_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x089*/ { "CMSG_GUILD_ROSTER", STATUS_LOGGEDIN, &WorldSession::HandleGuildRosterOpcode }, + /*0x08A*/ { "SMSG_GUILD_ROSTER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x08B*/ { "CMSG_GUILD_PROMOTE", STATUS_LOGGEDIN, &WorldSession::HandleGuildPromoteOpcode }, + /*0x08C*/ { "CMSG_GUILD_DEMOTE", STATUS_LOGGEDIN, &WorldSession::HandleGuildDemoteOpcode }, + /*0x08D*/ { "CMSG_GUILD_LEAVE", STATUS_LOGGEDIN, &WorldSession::HandleGuildLeaveOpcode }, + /*0x08E*/ { "CMSG_GUILD_REMOVE", STATUS_LOGGEDIN, &WorldSession::HandleGuildRemoveOpcode }, + /*0x08F*/ { "CMSG_GUILD_DISBAND", STATUS_LOGGEDIN, &WorldSession::HandleGuildDisbandOpcode }, + /*0x090*/ { "CMSG_GUILD_LEADER", STATUS_LOGGEDIN, &WorldSession::HandleGuildLeaderOpcode }, + /*0x091*/ { "CMSG_GUILD_MOTD", STATUS_LOGGEDIN, &WorldSession::HandleGuildMOTDOpcode }, + /*0x092*/ { "SMSG_GUILD_EVENT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x093*/ { "SMSG_GUILD_COMMAND_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x094*/ { "UMSG_UPDATE_GUILD", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x095*/ { "CMSG_MESSAGECHAT", STATUS_LOGGEDIN, &WorldSession::HandleMessagechatOpcode }, + /*0x096*/ { "SMSG_MESSAGECHAT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x097*/ { "CMSG_JOIN_CHANNEL", STATUS_LOGGEDIN, &WorldSession::HandleChannelJoin }, + /*0x098*/ { "CMSG_LEAVE_CHANNEL", STATUS_LOGGEDIN, &WorldSession::HandleChannelLeave }, + /*0x099*/ { "SMSG_CHANNEL_NOTIFY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x09A*/ { "CMSG_CHANNEL_LIST", STATUS_LOGGEDIN, &WorldSession::HandleChannelList }, + /*0x09B*/ { "SMSG_CHANNEL_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x09C*/ { "CMSG_CHANNEL_PASSWORD", STATUS_LOGGEDIN, &WorldSession::HandleChannelPassword }, + /*0x09D*/ { "CMSG_CHANNEL_SET_OWNER", STATUS_LOGGEDIN, &WorldSession::HandleChannelSetOwner }, + /*0x09E*/ { "CMSG_CHANNEL_OWNER", STATUS_LOGGEDIN, &WorldSession::HandleChannelOwner }, + /*0x09F*/ { "CMSG_CHANNEL_MODERATOR", STATUS_LOGGEDIN, &WorldSession::HandleChannelModerator }, + /*0x0A0*/ { "CMSG_CHANNEL_UNMODERATOR", STATUS_LOGGEDIN, &WorldSession::HandleChannelUnmoderator }, + /*0x0A1*/ { "CMSG_CHANNEL_MUTE", STATUS_LOGGEDIN, &WorldSession::HandleChannelMute }, + /*0x0A2*/ { "CMSG_CHANNEL_UNMUTE", STATUS_LOGGEDIN, &WorldSession::HandleChannelUnmute }, + /*0x0A3*/ { "CMSG_CHANNEL_INVITE", STATUS_LOGGEDIN, &WorldSession::HandleChannelInvite }, + /*0x0A4*/ { "CMSG_CHANNEL_KICK", STATUS_LOGGEDIN, &WorldSession::HandleChannelKick }, + /*0x0A5*/ { "CMSG_CHANNEL_BAN", STATUS_LOGGEDIN, &WorldSession::HandleChannelBan }, + /*0x0A6*/ { "CMSG_CHANNEL_UNBAN", STATUS_LOGGEDIN, &WorldSession::HandleChannelUnban }, + /*0x0A7*/ { "CMSG_CHANNEL_ANNOUNCEMENTS", STATUS_LOGGEDIN, &WorldSession::HandleChannelAnnounce }, + /*0x0A8*/ { "CMSG_CHANNEL_MODERATE", STATUS_LOGGEDIN, &WorldSession::HandleChannelModerate }, + /*0x0A9*/ { "SMSG_UPDATE_OBJECT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0AA*/ { "SMSG_DESTROY_OBJECT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0AB*/ { "CMSG_USE_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleUseItemOpcode }, + /*0x0AC*/ { "CMSG_OPEN_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleOpenItemOpcode }, + /*0x0AD*/ { "CMSG_READ_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleReadItem }, + /*0x0AE*/ { "SMSG_READ_ITEM_OK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0AF*/ { "SMSG_READ_ITEM_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0B0*/ { "SMSG_ITEM_COOLDOWN", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0B1*/ { "CMSG_GAMEOBJ_USE", STATUS_LOGGEDIN, &WorldSession::HandleGameObjectUseOpcode }, + /*0x0B2*/ { "CMSG_DESTROY_ITEMS", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0B3*/ { "SMSG_GAMEOBJECT_CUSTOM_ANIM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0B4*/ { "CMSG_AREATRIGGER", STATUS_LOGGEDIN, &WorldSession::HandleAreaTriggerOpcode }, + /*0x0B5*/ { "MSG_MOVE_START_FORWARD", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0B6*/ { "MSG_MOVE_START_BACKWARD", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0B7*/ { "MSG_MOVE_STOP", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0B8*/ { "MSG_MOVE_START_STRAFE_LEFT", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0B9*/ { "MSG_MOVE_START_STRAFE_RIGHT", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0BA*/ { "MSG_MOVE_STOP_STRAFE", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0BB*/ { "MSG_MOVE_JUMP", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0BC*/ { "MSG_MOVE_START_TURN_LEFT", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0BD*/ { "MSG_MOVE_START_TURN_RIGHT", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0BE*/ { "MSG_MOVE_STOP_TURN", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0BF*/ { "MSG_MOVE_START_PITCH_UP", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0C0*/ { "MSG_MOVE_START_PITCH_DOWN", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0C1*/ { "MSG_MOVE_STOP_PITCH", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0C2*/ { "MSG_MOVE_SET_RUN_MODE", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0C3*/ { "MSG_MOVE_SET_WALK_MODE", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0C4*/ { "MSG_MOVE_TOGGLE_LOGGING", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0C5*/ { "MSG_MOVE_TELEPORT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0C6*/ { "MSG_MOVE_TELEPORT_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0C7*/ { "MSG_MOVE_TELEPORT_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveTeleportAck }, + /*0x0C8*/ { "MSG_MOVE_TOGGLE_FALL_LOGGING", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0C9*/ { "MSG_MOVE_FALL_LAND", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0CA*/ { "MSG_MOVE_START_SWIM", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0CB*/ { "MSG_MOVE_STOP_SWIM", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0CC*/ { "MSG_MOVE_SET_RUN_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0CD*/ { "MSG_MOVE_SET_RUN_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0CE*/ { "MSG_MOVE_SET_RUN_BACK_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0CF*/ { "MSG_MOVE_SET_RUN_BACK_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0D0*/ { "MSG_MOVE_SET_WALK_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0D1*/ { "MSG_MOVE_SET_WALK_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0D2*/ { "MSG_MOVE_SET_SWIM_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0D3*/ { "MSG_MOVE_SET_SWIM_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0D4*/ { "MSG_MOVE_SET_SWIM_BACK_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0D5*/ { "MSG_MOVE_SET_SWIM_BACK_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0D6*/ { "MSG_MOVE_SET_ALL_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0D7*/ { "MSG_MOVE_SET_TURN_RATE_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0D8*/ { "MSG_MOVE_SET_TURN_RATE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0D9*/ { "MSG_MOVE_TOGGLE_COLLISION_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0DA*/ { "MSG_MOVE_SET_FACING", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0DB*/ { "MSG_MOVE_SET_PITCH", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0DC*/ { "MSG_MOVE_WORLDPORT_ACK", STATUS_TRANSFER_PENDING,&WorldSession::HandleMoveWorldportAckOpcode}, + /*0x0DD*/ { "SMSG_MONSTER_MOVE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0DE*/ { "SMSG_MOVE_WATER_WALK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0DF*/ { "SMSG_MOVE_LAND_WALK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0E0*/ { "MSG_MOVE_SET_RAW_POSITION_ACK", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0E1*/ { "CMSG_MOVE_SET_RAW_POSITION", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0E2*/ { "SMSG_FORCE_RUN_SPEED_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0E3*/ { "CMSG_FORCE_RUN_SPEED_CHANGE_ACK", STATUS_LOGGEDIN, &WorldSession::HandleForceSpeedChangeAck }, + /*0x0E4*/ { "SMSG_FORCE_RUN_BACK_SPEED_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0E5*/ { "CMSG_FORCE_RUN_BACK_SPEED_CHANGE_ACK", STATUS_LOGGEDIN, &WorldSession::HandleForceSpeedChangeAck }, + /*0x0E6*/ { "SMSG_FORCE_SWIM_SPEED_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0E7*/ { "CMSG_FORCE_SWIM_SPEED_CHANGE_ACK", STATUS_LOGGEDIN, &WorldSession::HandleForceSpeedChangeAck }, + /*0x0E8*/ { "SMSG_FORCE_MOVE_ROOT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0E9*/ { "CMSG_FORCE_MOVE_ROOT_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveRootAck }, + /*0x0EA*/ { "SMSG_FORCE_MOVE_UNROOT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0EB*/ { "CMSG_FORCE_MOVE_UNROOT_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveUnRootAck }, + /*0x0EC*/ { "MSG_MOVE_ROOT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0ED*/ { "MSG_MOVE_UNROOT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0EE*/ { "MSG_MOVE_HEARTBEAT", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0EF*/ { "SMSG_MOVE_KNOCK_BACK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0F0*/ { "CMSG_MOVE_KNOCK_BACK_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveKnockBackAck }, + /*0x0F1*/ { "MSG_MOVE_KNOCK_BACK", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0F2*/ { "SMSG_MOVE_FEATHER_FALL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0F3*/ { "SMSG_MOVE_NORMAL_FALL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0F4*/ { "SMSG_MOVE_SET_HOVER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0F5*/ { "SMSG_MOVE_UNSET_HOVER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0F6*/ { "CMSG_MOVE_HOVER_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveHoverAck }, + /*0x0F7*/ { "MSG_MOVE_HOVER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0F8*/ { "CMSG_TRIGGER_CINEMATIC_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0F9*/ { "CMSG_OPENING_CINEMATIC", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0FA*/ { "SMSG_TRIGGER_CINEMATIC", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0FB*/ { "CMSG_NEXT_CINEMATIC_CAMERA", STATUS_LOGGEDIN, &WorldSession::HandleNextCinematicCamera }, + /*0x0FC*/ { "CMSG_COMPLETE_CINEMATIC", STATUS_LOGGEDIN, &WorldSession::HandleCompleteCinema }, + /*0x0FD*/ { "SMSG_TUTORIAL_FLAGS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0FE*/ { "CMSG_TUTORIAL_FLAG", STATUS_LOGGEDIN, &WorldSession::HandleTutorialFlag }, + /*0x0FF*/ { "CMSG_TUTORIAL_CLEAR", STATUS_LOGGEDIN, &WorldSession::HandleTutorialClear }, + /*0x100*/ { "CMSG_TUTORIAL_RESET", STATUS_LOGGEDIN, &WorldSession::HandleTutorialReset }, + /*0x101*/ { "CMSG_STANDSTATECHANGE", STATUS_LOGGEDIN, &WorldSession::HandleStandStateChangeOpcode }, + /*0x102*/ { "CMSG_EMOTE", STATUS_LOGGEDIN, &WorldSession::HandleEmoteOpcode }, + /*0x103*/ { "SMSG_EMOTE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x104*/ { "CMSG_TEXT_EMOTE", STATUS_LOGGEDIN, &WorldSession::HandleTextEmoteOpcode }, + /*0x105*/ { "SMSG_TEXT_EMOTE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x106*/ { "CMSG_AUTOEQUIP_GROUND_ITEM", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x107*/ { "CMSG_AUTOSTORE_GROUND_ITEM", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x108*/ { "CMSG_AUTOSTORE_LOOT_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAutostoreLootItemOpcode }, + /*0x109*/ { "CMSG_STORE_LOOT_IN_SLOT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x10A*/ { "CMSG_AUTOEQUIP_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAutoEquipItemOpcode }, + /*0x10B*/ { "CMSG_AUTOSTORE_BAG_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAutoStoreBagItemOpcode }, + /*0x10C*/ { "CMSG_SWAP_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleSwapItem }, + /*0x10D*/ { "CMSG_SWAP_INV_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleSwapInvItemOpcode }, + /*0x10E*/ { "CMSG_SPLIT_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleSplitItemOpcode }, + /*0x10F*/ { "CMSG_AUTOEQUIP_ITEM_SLOT", STATUS_LOGGEDIN, &WorldSession::HandleAutoEquipItemSlotOpcode }, + /*0x110*/ { "OBSOLETE_DROP_ITEM", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x111*/ { "CMSG_DESTROYITEM", STATUS_LOGGEDIN, &WorldSession::HandleDestroyItemOpcode }, + /*0x112*/ { "SMSG_INVENTORY_CHANGE_FAILURE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x113*/ { "SMSG_OPEN_CONTAINER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x114*/ { "CMSG_INSPECT", STATUS_LOGGEDIN, &WorldSession::HandleInspectOpcode }, + /*0x115*/ { "SMSG_INSPECT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x116*/ { "CMSG_INITIATE_TRADE", STATUS_LOGGEDIN, &WorldSession::HandleInitiateTradeOpcode }, + /*0x117*/ { "CMSG_BEGIN_TRADE", STATUS_LOGGEDIN, &WorldSession::HandleBeginTradeOpcode }, + /*0x118*/ { "CMSG_BUSY_TRADE", STATUS_LOGGEDIN, &WorldSession::HandleBusyTradeOpcode }, + /*0x119*/ { "CMSG_IGNORE_TRADE", STATUS_LOGGEDIN, &WorldSession::HandleIgnoreTradeOpcode }, + /*0x11A*/ { "CMSG_ACCEPT_TRADE", STATUS_LOGGEDIN, &WorldSession::HandleAcceptTradeOpcode }, + /*0x11B*/ { "CMSG_UNACCEPT_TRADE", STATUS_LOGGEDIN, &WorldSession::HandleUnacceptTradeOpcode }, + /*0x11C*/ { "CMSG_CANCEL_TRADE", STATUS_AUTHED, &WorldSession::HandleCancelTradeOpcode }, + /*0x11D*/ { "CMSG_SET_TRADE_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleSetTradeItemOpcode }, + /*0x11E*/ { "CMSG_CLEAR_TRADE_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleClearTradeItemOpcode }, + /*0x11F*/ { "CMSG_SET_TRADE_GOLD", STATUS_LOGGEDIN, &WorldSession::HandleSetTradeGoldOpcode }, + /*0x120*/ { "SMSG_TRADE_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x121*/ { "SMSG_TRADE_STATUS_EXTENDED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x122*/ { "SMSG_INITIALIZE_FACTIONS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x123*/ { "SMSG_SET_FACTION_VISIBLE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x124*/ { "SMSG_SET_FACTION_STANDING", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x125*/ { "CMSG_SET_FACTION_ATWAR", STATUS_LOGGEDIN, &WorldSession::HandleSetFactionAtWar }, + /*0x126*/ { "CMSG_SET_FACTION_CHEAT", STATUS_LOGGEDIN, &WorldSession::HandleSetFactionCheat }, + /*0x127*/ { "SMSG_SET_PROFICIENCY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x128*/ { "CMSG_SET_ACTION_BUTTON", STATUS_LOGGEDIN, &WorldSession::HandleSetActionButtonOpcode }, + /*0x129*/ { "SMSG_ACTION_BUTTONS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x12A*/ { "SMSG_INITIAL_SPELLS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x12B*/ { "SMSG_LEARNED_SPELL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x12C*/ { "SMSG_SUPERCEDED_SPELL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x12D*/ { "CMSG_NEW_SPELL_SLOT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x12E*/ { "CMSG_CAST_SPELL", STATUS_LOGGEDIN, &WorldSession::HandleCastSpellOpcode }, + /*0x12F*/ { "CMSG_CANCEL_CAST", STATUS_LOGGEDIN, &WorldSession::HandleCancelCastOpcode }, + /*0x130*/ { "SMSG_CAST_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x131*/ { "SMSG_SPELL_START", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x132*/ { "SMSG_SPELL_GO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x133*/ { "SMSG_SPELL_FAILURE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x134*/ { "SMSG_SPELL_COOLDOWN", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x135*/ { "SMSG_COOLDOWN_EVENT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x136*/ { "CMSG_CANCEL_AURA", STATUS_LOGGEDIN, &WorldSession::HandleCancelAuraOpcode }, + /*0x137*/ { "SMSG_UPDATE_AURA_DURATION_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x138*/ { "SMSG_PET_CAST_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x139*/ { "MSG_CHANNEL_START", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x13A*/ { "MSG_CHANNEL_UPDATE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x13B*/ { "CMSG_CANCEL_CHANNELLING", STATUS_LOGGEDIN, &WorldSession::HandleCancelChanneling }, + /*0x13C*/ { "SMSG_AI_REACTION", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x13D*/ { "CMSG_SET_SELECTION", STATUS_LOGGEDIN, &WorldSession::HandleSetSelectionOpcode }, + /*0x13E*/ { "CMSG_SET_TARGET_OBSOLETE", STATUS_LOGGEDIN, &WorldSession::HandleSetTargetOpcode }, + /*0x13F*/ { "CMSG_UNUSED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x140*/ { "CMSG_UNUSED2", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x141*/ { "CMSG_ATTACKSWING", STATUS_LOGGEDIN, &WorldSession::HandleAttackSwingOpcode }, + /*0x142*/ { "CMSG_ATTACKSTOP", STATUS_LOGGEDIN, &WorldSession::HandleAttackStopOpcode }, + /*0x143*/ { "SMSG_ATTACKSTART", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x144*/ { "SMSG_ATTACKSTOP", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x145*/ { "SMSG_ATTACKSWING_NOTINRANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x146*/ { "SMSG_ATTACKSWING_BADFACING", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x147*/ { "SMSG_ATTACKSWING_NOTSTANDING", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x148*/ { "SMSG_ATTACKSWING_DEADTARGET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x149*/ { "SMSG_ATTACKSWING_CANT_ATTACK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x14A*/ { "SMSG_ATTACKERSTATEUPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x14B*/ { "SMSG_VICTIMSTATEUPDATE_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x14C*/ { "SMSG_DAMAGE_DONE_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x14D*/ { "SMSG_DAMAGE_TAKEN_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x14E*/ { "SMSG_CANCEL_COMBAT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x14F*/ { "SMSG_SPELLBREAKLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x150*/ { "SMSG_SPELLHEALLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x151*/ { "SMSG_SPELLENERGIZELOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x152*/ { "SMSG_BREAK_TARGET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x153*/ { "CMSG_SAVE_PLAYER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x154*/ { "CMSG_SETDEATHBINDPOINT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x155*/ { "SMSG_BINDPOINTUPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x156*/ { "CMSG_GETDEATHBINDZONE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x157*/ { "SMSG_BINDZONEREPLY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x158*/ { "SMSG_PLAYERBOUND", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x159*/ { "SMSG_CLIENT_CONTROL_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x15A*/ { "CMSG_REPOP_REQUEST", STATUS_LOGGEDIN, &WorldSession::HandleRepopRequestOpcode }, + /*0x15B*/ { "SMSG_RESURRECT_REQUEST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x15C*/ { "CMSG_RESURRECT_RESPONSE", STATUS_LOGGEDIN, &WorldSession::HandleResurrectResponseOpcode }, + /*0x15D*/ { "CMSG_LOOT", STATUS_LOGGEDIN, &WorldSession::HandleLootOpcode }, + /*0x15E*/ { "CMSG_LOOT_MONEY", STATUS_LOGGEDIN, &WorldSession::HandleLootMoneyOpcode }, + /*0x15F*/ { "CMSG_LOOT_RELEASE", STATUS_LOGGEDIN, &WorldSession::HandleLootReleaseOpcode }, + /*0x160*/ { "SMSG_LOOT_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x161*/ { "SMSG_LOOT_RELEASE_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x162*/ { "SMSG_LOOT_REMOVED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x163*/ { "SMSG_LOOT_MONEY_NOTIFY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x164*/ { "SMSG_LOOT_ITEM_NOTIFY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x165*/ { "SMSG_LOOT_CLEAR_MONEY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x166*/ { "SMSG_ITEM_PUSH_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x167*/ { "SMSG_DUEL_REQUESTED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x168*/ { "SMSG_DUEL_OUTOFBOUNDS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x169*/ { "SMSG_DUEL_INBOUNDS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x16A*/ { "SMSG_DUEL_COMPLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x16B*/ { "SMSG_DUEL_WINNER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x16C*/ { "CMSG_DUEL_ACCEPTED", STATUS_LOGGEDIN, &WorldSession::HandleDuelAcceptedOpcode }, + /*0x16D*/ { "CMSG_DUEL_CANCELLED", STATUS_LOGGEDIN, &WorldSession::HandleDuelCancelledOpcode }, + /*0x16E*/ { "SMSG_MOUNTRESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x16F*/ { "SMSG_DISMOUNTRESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x170*/ { "SMSG_PUREMOUNT_CANCELLED_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x171*/ { "CMSG_MOUNTSPECIAL_ANIM", STATUS_LOGGEDIN, &WorldSession::HandleMountSpecialAnimOpcode }, + /*0x172*/ { "SMSG_MOUNTSPECIAL_ANIM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x173*/ { "SMSG_PET_TAME_FAILURE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x174*/ { "CMSG_PET_SET_ACTION", STATUS_LOGGEDIN, &WorldSession::HandlePetSetAction }, + /*0x175*/ { "CMSG_PET_ACTION", STATUS_LOGGEDIN, &WorldSession::HandlePetAction }, + /*0x176*/ { "CMSG_PET_ABANDON", STATUS_LOGGEDIN, &WorldSession::HandlePetAbandon }, + /*0x177*/ { "CMSG_PET_RENAME", STATUS_LOGGEDIN, &WorldSession::HandlePetRename }, + /*0x178*/ { "SMSG_PET_NAME_INVALID", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x179*/ { "SMSG_PET_SPELLS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x17A*/ { "SMSG_PET_MODE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x17B*/ { "CMSG_GOSSIP_HELLO", STATUS_LOGGEDIN, &WorldSession::HandleGossipHelloOpcode }, + /*0x17C*/ { "CMSG_GOSSIP_SELECT_OPTION", STATUS_LOGGEDIN, &WorldSession::HandleGossipSelectOptionOpcode }, + /*0x17D*/ { "SMSG_GOSSIP_MESSAGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x17E*/ { "SMSG_GOSSIP_COMPLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x17F*/ { "CMSG_NPC_TEXT_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleNpcTextQueryOpcode }, + /*0x180*/ { "SMSG_NPC_TEXT_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x181*/ { "SMSG_NPC_WONT_TALK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x182*/ { "CMSG_QUESTGIVER_STATUS_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverStatusQueryOpcode}, + /*0x183*/ { "SMSG_QUESTGIVER_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x184*/ { "CMSG_QUESTGIVER_HELLO", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverHelloOpcode }, + /*0x185*/ { "SMSG_QUESTGIVER_QUEST_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x186*/ { "CMSG_QUESTGIVER_QUERY_QUEST", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverQuestQueryOpcode}, + /*0x187*/ { "CMSG_QUESTGIVER_QUEST_AUTOLAUNCH", STATUS_LOGGEDIN, &WorldSession::HandleQuestAutoLaunch }, + /*0x188*/ { "SMSG_QUESTGIVER_QUEST_DETAILS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x189*/ { "CMSG_QUESTGIVER_ACCEPT_QUEST", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverAcceptQuestOpcode}, + /*0x18A*/ { "CMSG_QUESTGIVER_COMPLETE_QUEST", STATUS_LOGGEDIN, &WorldSession::HandleQuestComplete }, + /*0x18B*/ { "SMSG_QUESTGIVER_REQUEST_ITEMS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x18C*/ { "CMSG_QUESTGIVER_REQUEST_REWARD", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverRequestRewardOpcode}, + /*0x18D*/ { "SMSG_QUESTGIVER_OFFER_REWARD", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x18E*/ { "CMSG_QUESTGIVER_CHOOSE_REWARD", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverChooseRewardOpcode}, + /*0x18F*/ { "SMSG_QUESTGIVER_QUEST_INVALID", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x190*/ { "CMSG_QUESTGIVER_CANCEL", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverCancel }, + /*0x191*/ { "SMSG_QUESTGIVER_QUEST_COMPLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x192*/ { "SMSG_QUESTGIVER_QUEST_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x193*/ { "CMSG_QUESTLOG_SWAP_QUEST", STATUS_LOGGEDIN, &WorldSession::HandleQuestLogSwapQuest }, + /*0x194*/ { "CMSG_QUESTLOG_REMOVE_QUEST", STATUS_LOGGEDIN, &WorldSession::HandleQuestLogRemoveQuest }, + /*0x195*/ { "SMSG_QUESTLOG_FULL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x196*/ { "SMSG_QUESTUPDATE_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x197*/ { "SMSG_QUESTUPDATE_FAILEDTIMER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x198*/ { "SMSG_QUESTUPDATE_COMPLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x199*/ { "SMSG_QUESTUPDATE_ADD_KILL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x19A*/ { "SMSG_QUESTUPDATE_ADD_ITEM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x19B*/ { "CMSG_QUEST_CONFIRM_ACCEPT", STATUS_LOGGEDIN, &WorldSession::HandleQuestConfirmAccept }, + /*0x19C*/ { "SMSG_QUEST_CONFIRM_ACCEPT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x19D*/ { "CMSG_PUSHQUESTTOPARTY", STATUS_LOGGEDIN, &WorldSession::HandleQuestPushToParty }, + /*0x19E*/ { "CMSG_LIST_INVENTORY", STATUS_LOGGEDIN, &WorldSession::HandleListInventoryOpcode }, + /*0x19F*/ { "SMSG_LIST_INVENTORY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1A0*/ { "CMSG_SELL_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleSellItemOpcode }, + /*0x1A1*/ { "SMSG_SELL_ITEM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1A2*/ { "CMSG_BUY_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleBuyItemOpcode }, + /*0x1A3*/ { "CMSG_BUY_ITEM_IN_SLOT", STATUS_LOGGEDIN, &WorldSession::HandleBuyItemInSlotOpcode }, + /*0x1A4*/ { "SMSG_BUY_ITEM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1A5*/ { "SMSG_BUY_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1A6*/ { "CMSG_TAXICLEARALLNODES", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1A7*/ { "CMSG_TAXIENABLEALLNODES", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1A8*/ { "CMSG_TAXISHOWNODES", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1A9*/ { "SMSG_SHOWTAXINODES", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1AA*/ { "CMSG_TAXINODE_STATUS_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleTaxiNodeStatusQueryOpcode }, + /*0x1AB*/ { "SMSG_TAXINODE_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1AC*/ { "CMSG_TAXIQUERYAVAILABLENODES", STATUS_LOGGEDIN, &WorldSession::HandleTaxiQueryAvailableNodes }, + /*0x1AD*/ { "CMSG_ACTIVATETAXI", STATUS_LOGGEDIN, &WorldSession::HandleActivateTaxiOpcode }, + /*0x1AE*/ { "SMSG_ACTIVATETAXIREPLY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1AF*/ { "SMSG_NEW_TAXI_PATH", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1B0*/ { "CMSG_TRAINER_LIST", STATUS_LOGGEDIN, &WorldSession::HandleTrainerListOpcode }, + /*0x1B1*/ { "SMSG_TRAINER_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1B2*/ { "CMSG_TRAINER_BUY_SPELL", STATUS_LOGGEDIN, &WorldSession::HandleTrainerBuySpellOpcode }, + /*0x1B3*/ { "SMSG_TRAINER_BUY_SUCCEEDED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1B4*/ { "SMSG_TRAINER_BUY_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1B5*/ { "CMSG_BINDER_ACTIVATE", STATUS_LOGGEDIN, &WorldSession::HandleBinderActivateOpcode }, + /*0x1B6*/ { "SMSG_PLAYERBINDERROR", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1B7*/ { "CMSG_BANKER_ACTIVATE", STATUS_LOGGEDIN, &WorldSession::HandleBankerActivateOpcode }, + /*0x1B8*/ { "SMSG_SHOW_BANK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1B9*/ { "CMSG_BUY_BANK_SLOT", STATUS_LOGGEDIN, &WorldSession::HandleBuyBankSlotOpcode }, + /*0x1BA*/ { "SMSG_BUY_BANK_SLOT_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1BB*/ { "CMSG_PETITION_SHOWLIST", STATUS_LOGGEDIN, &WorldSession::HandlePetitionShowListOpcode }, + /*0x1BC*/ { "SMSG_PETITION_SHOWLIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1BD*/ { "CMSG_PETITION_BUY", STATUS_LOGGEDIN, &WorldSession::HandlePetitionBuyOpcode }, + /*0x1BE*/ { "CMSG_PETITION_SHOW_SIGNATURES", STATUS_LOGGEDIN, &WorldSession::HandlePetitionShowSignOpcode }, + /*0x1BF*/ { "SMSG_PETITION_SHOW_SIGNATURES", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1C0*/ { "CMSG_PETITION_SIGN", STATUS_LOGGEDIN, &WorldSession::HandlePetitionSignOpcode }, + /*0x1C1*/ { "SMSG_PETITION_SIGN_RESULTS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1C2*/ { "MSG_PETITION_DECLINE", STATUS_LOGGEDIN, &WorldSession::HandlePetitionDeclineOpcode }, + /*0x1C3*/ { "CMSG_OFFER_PETITION", STATUS_LOGGEDIN, &WorldSession::HandleOfferPetitionOpcode }, + /*0x1C4*/ { "CMSG_TURN_IN_PETITION", STATUS_LOGGEDIN, &WorldSession::HandleTurnInPetitionOpcode }, + /*0x1C5*/ { "SMSG_TURN_IN_PETITION_RESULTS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1C6*/ { "CMSG_PETITION_QUERY", STATUS_LOGGEDIN, &WorldSession::HandlePetitionQueryOpcode }, + /*0x1C7*/ { "SMSG_PETITION_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1C8*/ { "SMSG_FISH_NOT_HOOKED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1C9*/ { "SMSG_FISH_ESCAPED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1CA*/ { "CMSG_BUG", STATUS_LOGGEDIN, &WorldSession::HandleBugOpcode }, + /*0x1CB*/ { "SMSG_NOTIFICATION", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1CC*/ { "CMSG_PLAYED_TIME", STATUS_LOGGEDIN, &WorldSession::HandlePlayedTime }, + /*0x1CD*/ { "SMSG_PLAYED_TIME", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1CE*/ { "CMSG_QUERY_TIME", STATUS_LOGGEDIN, &WorldSession::HandleQueryTimeOpcode }, + /*0x1CF*/ { "SMSG_QUERY_TIME_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1D0*/ { "SMSG_LOG_XPGAIN", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1D1*/ { "SMSG_AURACASTLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1D2*/ { "CMSG_RECLAIM_CORPSE", STATUS_LOGGEDIN, &WorldSession::HandleCorpseReclaimOpcode }, + /*0x1D3*/ { "CMSG_WRAP_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleWrapItemOpcode }, + /*0x1D4*/ { "SMSG_LEVELUP_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1D5*/ { "MSG_MINIMAP_PING", STATUS_LOGGEDIN, &WorldSession::HandleMinimapPingOpcode }, + /*0x1D6*/ { "SMSG_RESISTLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1D7*/ { "SMSG_ENCHANTMENTLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1D8*/ { "CMSG_SET_SKILL_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1D9*/ { "SMSG_START_MIRROR_TIMER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1DA*/ { "SMSG_PAUSE_MIRROR_TIMER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1DB*/ { "SMSG_STOP_MIRROR_TIMER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1DC*/ { "CMSG_PING", STATUS_NEVER, &WorldSession::Handle_EarlyProccess }, + /*0x1DD*/ { "SMSG_PONG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1DE*/ { "SMSG_CLEAR_COOLDOWN", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1DF*/ { "SMSG_GAMEOBJECT_PAGETEXT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1E0*/ { "CMSG_SETSHEATHED", STATUS_LOGGEDIN, &WorldSession::HandleSetSheathedOpcode }, + /*0x1E1*/ { "SMSG_COOLDOWN_CHEAT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1E2*/ { "SMSG_SPELL_DELAYED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1E3*/ { "CMSG_PLAYER_MACRO_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1E4*/ { "SMSG_PLAYER_MACRO_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1E5*/ { "CMSG_GHOST", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1E6*/ { "CMSG_GM_INVIS", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1E7*/ { "SMSG_INVALID_PROMOTION_CODE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1E8*/ { "MSG_GM_BIND_OTHER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1E9*/ { "MSG_GM_SUMMON", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1EA*/ { "SMSG_ITEM_TIME_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1EB*/ { "SMSG_ITEM_ENCHANT_TIME_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1EC*/ { "SMSG_AUTH_CHALLENGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1ED*/ { "CMSG_AUTH_SESSION", STATUS_NEVER, &WorldSession::Handle_EarlyProccess }, + /*0x1EE*/ { "SMSG_AUTH_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1EF*/ { "MSG_GM_SHOWLABEL", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1F0*/ { "CMSG_PET_CAST_SPELL", STATUS_LOGGEDIN, &WorldSession::HandlePetCastSpellOpcode }, + /*0x1F1*/ { "MSG_SAVE_GUILD_EMBLEM", STATUS_LOGGEDIN, &WorldSession::HandleGuildSaveEmblemOpcode }, + /*0x1F2*/ { "MSG_TABARDVENDOR_ACTIVATE", STATUS_LOGGEDIN, &WorldSession::HandleTabardVendorActivateOpcode}, + /*0x1F3*/ { "SMSG_PLAY_SPELL_VISUAL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1F4*/ { "CMSG_ZONEUPDATE", STATUS_LOGGEDIN, &WorldSession::HandleZoneUpdateOpcode }, + /*0x1F5*/ { "SMSG_PARTYKILLLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1F6*/ { "SMSG_COMPRESSED_UPDATE_OBJECT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1F7*/ { "SMSG_PLAY_SPELL_IMPACT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1F8*/ { "SMSG_EXPLORATION_EXPERIENCE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1F9*/ { "CMSG_GM_SET_SECURITY_GROUP", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1FA*/ { "CMSG_GM_NUKE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1FB*/ { "MSG_RANDOM_ROLL", STATUS_LOGGEDIN, &WorldSession::HandleRandomRollOpcode }, + /*0x1FC*/ { "SMSG_ENVIRONMENTALDAMAGELOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1FD*/ { "CMSG_RWHOIS_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1FE*/ { "SMSG_RWHOIS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1FF*/ { "MSG_LOOKING_FOR_GROUP", STATUS_LOGGEDIN, &WorldSession::HandleLookingForGroup }, + /*0x200*/ { "CMSG_SET_LOOKING_FOR_GROUP", STATUS_LOGGEDIN, &WorldSession::HandleSetLfgOpcode }, + /*0x201*/ { "CMSG_UNLEARN_SPELL", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x202*/ { "CMSG_UNLEARN_SKILL", STATUS_LOGGEDIN, &WorldSession::HandleUnlearnSkillOpcode }, + /*0x203*/ { "SMSG_REMOVED_SPELL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x204*/ { "CMSG_DECHARGE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x205*/ { "CMSG_GMTICKET_CREATE", STATUS_LOGGEDIN, &WorldSession::HandleGMTicketCreateOpcode }, + /*0x206*/ { "SMSG_GMTICKET_CREATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x207*/ { "CMSG_GMTICKET_UPDATETEXT", STATUS_LOGGEDIN, &WorldSession::HandleGMTicketUpdateOpcode }, + /*0x208*/ { "SMSG_GMTICKET_UPDATETEXT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x209*/ { "SMSG_ACCOUNT_DATA_TIMES", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x20A*/ { "CMSG_REQUEST_ACCOUNT_DATA", STATUS_LOGGEDIN, &WorldSession::HandleRequestAccountData }, + /*0x20B*/ { "CMSG_UPDATE_ACCOUNT_DATA", STATUS_AUTHED, &WorldSession::HandleUpdateAccountData }, + /*0x20C*/ { "SMSG_UPDATE_ACCOUNT_DATA", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x20D*/ { "SMSG_CLEAR_FAR_SIGHT_IMMEDIATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x20E*/ { "SMSG_POWERGAINLOG_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x20F*/ { "CMSG_GM_TEACH", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x210*/ { "CMSG_GM_CREATE_ITEM_TARGET", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x211*/ { "CMSG_GMTICKET_GETTICKET", STATUS_LOGGEDIN, &WorldSession::HandleGMTicketGetTicketOpcode }, + /*0x212*/ { "SMSG_GMTICKET_GETTICKET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x213*/ { "CMSG_UNLEARN_TALENTS", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x214*/ { "SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x215*/ { "SMSG_GAMEOBJECT_DESPAWN_ANIM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x216*/ { "MSG_CORPSE_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleCorpseQueryOpcode }, + /*0x217*/ { "CMSG_GMTICKET_DELETETICKET", STATUS_LOGGEDIN, &WorldSession::HandleGMTicketDeleteOpcode }, + /*0x218*/ { "SMSG_GMTICKET_DELETETICKET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x219*/ { "SMSG_CHAT_WRONG_FACTION", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x21A*/ { "CMSG_GMTICKET_SYSTEMSTATUS", STATUS_LOGGEDIN, &WorldSession::HandleGMTicketSystemStatusOpcode}, + /*0x21B*/ { "SMSG_GMTICKET_SYSTEMSTATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x21C*/ { "CMSG_SPIRIT_HEALER_ACTIVATE", STATUS_LOGGEDIN, &WorldSession::HandleSpiritHealerActivateOpcode}, + /*0x21D*/ { "CMSG_SET_STAT_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x21E*/ { "SMSG_SET_REST_START_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x21F*/ { "CMSG_SKILL_BUY_STEP", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x220*/ { "CMSG_SKILL_BUY_RANK", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x221*/ { "CMSG_XP_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x222*/ { "SMSG_SPIRIT_HEALER_CONFIRM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x223*/ { "CMSG_CHARACTER_POINT_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x224*/ { "SMSG_GOSSIP_POI", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x225*/ { "CMSG_CHAT_IGNORED", STATUS_LOGGEDIN, &WorldSession::HandleChatIgnoredOpcode }, + /*0x226*/ { "CMSG_GM_VISION", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x227*/ { "CMSG_SERVER_COMMAND", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x228*/ { "CMSG_GM_SILENCE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x229*/ { "CMSG_GM_REVEALTO", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x22A*/ { "CMSG_GM_RESURRECT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x22B*/ { "CMSG_GM_SUMMONMOB", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x22C*/ { "CMSG_GM_MOVECORPSE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x22D*/ { "CMSG_GM_FREEZE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x22E*/ { "CMSG_GM_UBERINVIS", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x22F*/ { "CMSG_GM_REQUEST_PLAYER_INFO", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x230*/ { "SMSG_GM_PLAYER_INFO", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x231*/ { "CMSG_GUILD_RANK", STATUS_LOGGEDIN, &WorldSession::HandleGuildRankOpcode }, + /*0x232*/ { "CMSG_GUILD_ADD_RANK", STATUS_LOGGEDIN, &WorldSession::HandleGuildAddRankOpcode }, + /*0x233*/ { "CMSG_GUILD_DEL_RANK", STATUS_LOGGEDIN, &WorldSession::HandleGuildDelRankOpcode }, + /*0x234*/ { "CMSG_GUILD_SET_PUBLIC_NOTE", STATUS_LOGGEDIN, &WorldSession::HandleGuildSetPublicNoteOpcode }, + /*0x235*/ { "CMSG_GUILD_SET_OFFICER_NOTE", STATUS_LOGGEDIN, &WorldSession::HandleGuildSetOfficerNoteOpcode }, + /*0x236*/ { "SMSG_LOGIN_VERIFY_WORLD", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x237*/ { "CMSG_CLEAR_EXPLORATION", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x238*/ { "CMSG_SEND_MAIL", STATUS_LOGGEDIN, &WorldSession::HandleSendMail }, + /*0x239*/ { "SMSG_SEND_MAIL_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x23A*/ { "CMSG_GET_MAIL_LIST", STATUS_LOGGEDIN, &WorldSession::HandleGetMail }, + /*0x23B*/ { "SMSG_MAIL_LIST_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x23C*/ { "CMSG_BATTLEFIELD_LIST", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundListOpcode }, + /*0x23D*/ { "SMSG_BATTLEFIELD_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x23E*/ { "CMSG_BATTLEFIELD_JOIN", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x23F*/ { "SMSG_BATTLEFIELD_WIN_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x240*/ { "SMSG_BATTLEFIELD_LOSE_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x241*/ { "CMSG_TAXICLEARNODE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x242*/ { "CMSG_TAXIENABLENODE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x243*/ { "CMSG_ITEM_TEXT_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleItemTextQuery }, + /*0x244*/ { "SMSG_ITEM_TEXT_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x245*/ { "CMSG_MAIL_TAKE_MONEY", STATUS_LOGGEDIN, &WorldSession::HandleTakeMoney }, + /*0x246*/ { "CMSG_MAIL_TAKE_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleTakeItem }, + /*0x247*/ { "CMSG_MAIL_MARK_AS_READ", STATUS_LOGGEDIN, &WorldSession::HandleMarkAsRead }, + /*0x248*/ { "CMSG_MAIL_RETURN_TO_SENDER", STATUS_LOGGEDIN, &WorldSession::HandleReturnToSender }, + /*0x249*/ { "CMSG_MAIL_DELETE", STATUS_LOGGEDIN, &WorldSession::HandleMailDelete }, + /*0x24A*/ { "CMSG_MAIL_CREATE_TEXT_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleMailCreateTextItem }, + /*0x24B*/ { "SMSG_SPELLLOGMISS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x24C*/ { "SMSG_SPELLLOGEXECUTE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x24D*/ { "SMSG_DEBUGAURAPROC", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x24E*/ { "SMSG_PERIODICAURALOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x24F*/ { "SMSG_SPELLDAMAGESHIELD", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x250*/ { "SMSG_SPELLNONMELEEDAMAGELOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x251*/ { "CMSG_LEARN_TALENT", STATUS_LOGGEDIN, &WorldSession::HandleLearnTalentOpcode }, + /*0x252*/ { "SMSG_RESURRECT_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x253*/ { "CMSG_TOGGLE_PVP", STATUS_LOGGEDIN, &WorldSession::HandleTogglePvP }, + /*0x254*/ { "SMSG_ZONE_UNDER_ATTACK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x255*/ { "MSG_AUCTION_HELLO", STATUS_LOGGEDIN, &WorldSession::HandleAuctionHelloOpcode }, + /*0x256*/ { "CMSG_AUCTION_SELL_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAuctionSellItem }, + /*0x257*/ { "CMSG_AUCTION_REMOVE_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAuctionRemoveItem }, + /*0x258*/ { "CMSG_AUCTION_LIST_ITEMS", STATUS_LOGGEDIN, &WorldSession::HandleAuctionListItems }, + /*0x259*/ { "CMSG_AUCTION_LIST_OWNER_ITEMS", STATUS_LOGGEDIN, &WorldSession::HandleAuctionListOwnerItems }, + /*0x25A*/ { "CMSG_AUCTION_PLACE_BID", STATUS_LOGGEDIN, &WorldSession::HandleAuctionPlaceBid }, + /*0x25B*/ { "SMSG_AUCTION_COMMAND_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x25C*/ { "SMSG_AUCTION_LIST_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x25D*/ { "SMSG_AUCTION_OWNER_LIST_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x25E*/ { "SMSG_AUCTION_BIDDER_NOTIFICATION", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x25F*/ { "SMSG_AUCTION_OWNER_NOTIFICATION", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x260*/ { "SMSG_PROCRESIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x261*/ { "SMSG_STANDSTATE_CHANGE_FAILURE_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x262*/ { "SMSG_DISPEL_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x263*/ { "SMSG_SPELLORDAMAGE_IMMUNE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x264*/ { "CMSG_AUCTION_LIST_BIDDER_ITEMS", STATUS_LOGGEDIN, &WorldSession::HandleAuctionListBidderItems }, + /*0x265*/ { "SMSG_AUCTION_BIDDER_LIST_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x266*/ { "SMSG_SET_FLAT_SPELL_MODIFIER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x267*/ { "SMSG_SET_PCT_SPELL_MODIFIER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x268*/ { "CMSG_SET_AMMO", STATUS_LOGGEDIN, &WorldSession::HandleSetAmmoOpcode }, + /*0x269*/ { "SMSG_CORPSE_RECLAIM_DELAY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x26A*/ { "CMSG_SET_ACTIVE_MOVER", STATUS_LOGGEDIN, &WorldSession::HandleSetActiveMoverOpcode }, + /*0x26B*/ { "CMSG_PET_CANCEL_AURA", STATUS_LOGGEDIN, &WorldSession::HandlePetCancelAuraOpcode }, + /*0x26C*/ { "CMSG_PLAYER_AI_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x26D*/ { "CMSG_CANCEL_AUTO_REPEAT_SPELL", STATUS_LOGGEDIN, &WorldSession::HandleCancelAutoRepeatSpellOpcode}, + /*0x26E*/ { "MSG_GM_ACCOUNT_ONLINE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x26F*/ { "MSG_LIST_STABLED_PETS", STATUS_LOGGEDIN, &WorldSession::HandleListStabledPetsOpcode }, + /*0x270*/ { "CMSG_STABLE_PET", STATUS_LOGGEDIN, &WorldSession::HandleStablePet }, + /*0x271*/ { "CMSG_UNSTABLE_PET", STATUS_LOGGEDIN, &WorldSession::HandleUnstablePet }, + /*0x272*/ { "CMSG_BUY_STABLE_SLOT", STATUS_LOGGEDIN, &WorldSession::HandleBuyStableSlot }, + /*0x273*/ { "SMSG_STABLE_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x274*/ { "CMSG_STABLE_REVIVE_PET", STATUS_LOGGEDIN, &WorldSession::HandleStableRevivePet }, + /*0x275*/ { "CMSG_STABLE_SWAP_PET", STATUS_LOGGEDIN, &WorldSession::HandleStableSwapPet }, + /*0x276*/ { "MSG_QUEST_PUSH_RESULT", STATUS_LOGGEDIN, &WorldSession::HandleQuestPushResult }, + /*0x277*/ { "SMSG_PLAY_MUSIC", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x278*/ { "SMSG_PLAY_OBJECT_SOUND", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x279*/ { "CMSG_REQUEST_PET_INFO", STATUS_LOGGEDIN, &WorldSession::HandleRequestPetInfoOpcode }, + /*0x27A*/ { "CMSG_FAR_SIGHT", STATUS_LOGGEDIN, &WorldSession::HandleFarSightOpcode }, + /*0x27B*/ { "SMSG_SPELLDISPELLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x27C*/ { "SMSG_DAMAGE_CALC_LOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x27D*/ { "CMSG_ENABLE_DAMAGE_LOG", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x27E*/ { "CMSG_GROUP_CHANGE_SUB_GROUP", STATUS_LOGGEDIN, &WorldSession::HandleGroupChangeSubGroupOpcode }, + /*0x27F*/ { "CMSG_REQUEST_PARTY_MEMBER_STATS", STATUS_LOGGEDIN, &WorldSession::HandleRequestPartyMemberStatsOpcode}, + /*0x280*/ { "CMSG_GROUP_SWAP_SUB_GROUP", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x281*/ { "CMSG_RESET_FACTION_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x282*/ { "CMSG_AUTOSTORE_BANK_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAutoStoreBankItemOpcode }, + /*0x283*/ { "CMSG_AUTOBANK_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAutoBankItemOpcode }, + /*0x284*/ { "MSG_QUERY_NEXT_MAIL_TIME", STATUS_LOGGEDIN, &WorldSession::HandleMsgQueryNextMailtime }, + /*0x285*/ { "SMSG_RECEIVED_MAIL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x286*/ { "SMSG_RAID_GROUP_ONLY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x287*/ { "CMSG_SET_DURABILITY_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x288*/ { "CMSG_SET_PVP_RANK_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x289*/ { "CMSG_ADD_PVP_MEDAL_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x28A*/ { "CMSG_DEL_PVP_MEDAL_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x28B*/ { "CMSG_SET_PVP_TITLE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x28C*/ { "SMSG_PVP_CREDIT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x28D*/ { "SMSG_AUCTION_REMOVED_NOTIFICATION", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x28E*/ { "CMSG_GROUP_RAID_CONVERT", STATUS_LOGGEDIN, &WorldSession::HandleRaidConvertOpcode }, + /*0x28F*/ { "CMSG_GROUP_ASSISTANT_LEADER", STATUS_LOGGEDIN, &WorldSession::HandleGroupAssistantOpcode }, + /*0x290*/ { "CMSG_BUYBACK_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleBuybackItem }, + /*0x291*/ { "SMSG_SERVER_MESSAGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x292*/ { "CMSG_MEETINGSTONE_JOIN", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x293*/ { "CMSG_MEETINGSTONE_LEAVE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x294*/ { "CMSG_MEETINGSTONE_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x295*/ { "SMSG_MEETINGSTONE_SETQUEUE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x296*/ { "CMSG_MEETINGSTONE_INFO", STATUS_LOGGEDIN, &WorldSession::HandleMeetingStoneInfo }, + /*0x297*/ { "SMSG_MEETINGSTONE_COMPLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x298*/ { "SMSG_MEETINGSTONE_IN_PROGRESS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x299*/ { "SMSG_MEETINGSTONE_MEMBER_ADDED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x29A*/ { "CMSG_GMTICKETSYSTEM_TOGGLE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x29B*/ { "CMSG_CANCEL_GROWTH_AURA", STATUS_LOGGEDIN, &WorldSession::HandleCancelGrowthAuraOpcode }, + /*0x29C*/ { "SMSG_CANCEL_AUTO_REPEAT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x29D*/ { "SMSG_STANDSTATE_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x29E*/ { "SMSG_LOOT_ALL_PASSED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x29F*/ { "SMSG_LOOT_ROLL_WON", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2A0*/ { "CMSG_LOOT_ROLL", STATUS_LOGGEDIN, &WorldSession::HandleLootRoll }, + /*0x2A1*/ { "SMSG_LOOT_START_ROLL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2A2*/ { "SMSG_LOOT_ROLL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2A3*/ { "CMSG_LOOT_MASTER_GIVE", STATUS_LOGGEDIN, &WorldSession::HandleLootMasterGiveOpcode }, + /*0x2A4*/ { "SMSG_LOOT_MASTER_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2A5*/ { "SMSG_SET_FORCED_REACTIONS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2A6*/ { "SMSG_SPELL_FAILED_OTHER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2A7*/ { "SMSG_GAMEOBJECT_RESET_STATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2A8*/ { "CMSG_REPAIR_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleRepairItemOpcode }, + /*0x2A9*/ { "SMSG_CHAT_PLAYER_NOT_FOUND", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2AA*/ { "MSG_TALENT_WIPE_CONFIRM", STATUS_LOGGEDIN, &WorldSession::HandleTalentWipeOpcode }, + /*0x2AB*/ { "SMSG_SUMMON_REQUEST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2AC*/ { "CMSG_SUMMON_RESPONSE", STATUS_LOGGEDIN, &WorldSession::HandleSummonResponseOpcode }, + /*0x2AD*/ { "MSG_MOVE_TOGGLE_GRAVITY_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x2AE*/ { "SMSG_MONSTER_MOVE_TRANSPORT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2AF*/ { "SMSG_PET_BROKEN", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2B0*/ { "MSG_MOVE_FEATHER_FALL", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x2B1*/ { "MSG_MOVE_WATER_WALK", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x2B2*/ { "CMSG_SERVER_BROADCAST", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x2B3*/ { "CMSG_SELF_RES", STATUS_LOGGEDIN, &WorldSession::HandleSelfResOpcode }, + /*0x2B4*/ { "SMSG_FEIGN_DEATH_RESISTED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2B5*/ { "CMSG_RUN_SCRIPT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x2B6*/ { "SMSG_SCRIPT_MESSAGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2B7*/ { "SMSG_DUEL_COUNTDOWN", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2B8*/ { "SMSG_AREA_TRIGGER_MESSAGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2B9*/ { "CMSG_SHOWING_HELM", STATUS_LOGGEDIN, &WorldSession::HandleToggleHelmOpcode }, + /*0x2BA*/ { "CMSG_SHOWING_CLOAK", STATUS_LOGGEDIN, &WorldSession::HandleToggleCloakOpcode }, + /*0x2BB*/ { "SMSG_MEETINGSTONE_JOINFAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2BC*/ { "SMSG_PLAYER_SKINNED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2BD*/ { "SMSG_DURABILITY_DAMAGE_DEATH", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2BE*/ { "CMSG_SET_EXPLORATION", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x2BF*/ { "CMSG_SET_ACTIONBAR_TOGGLES", STATUS_AUTHED, &WorldSession::HandleSetActionBar }, + /*0x2C0*/ { "UMSG_DELETE_GUILD_CHARTER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x2C1*/ { "MSG_PETITION_RENAME", STATUS_LOGGEDIN, &WorldSession::HandlePetitionRenameOpcode }, + /*0x2C2*/ { "SMSG_INIT_WORLD_STATES", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2C3*/ { "SMSG_UPDATE_WORLD_STATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2C4*/ { "CMSG_ITEM_NAME_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleItemNameQueryOpcode }, + /*0x2C5*/ { "SMSG_ITEM_NAME_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2C6*/ { "SMSG_PET_ACTION_FEEDBACK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2C7*/ { "CMSG_CHAR_RENAME", STATUS_AUTHED, &WorldSession::HandleChangePlayerNameOpcode }, + /*0x2C8*/ { "SMSG_CHAR_RENAME", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2C9*/ { "CMSG_MOVE_SPLINE_DONE", STATUS_LOGGEDIN, &WorldSession::HandleTaxiNextDestinationOpcode }, + /*0x2CA*/ { "CMSG_MOVE_FALL_RESET", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x2CB*/ { "SMSG_INSTANCE_SAVE_CREATED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2CC*/ { "SMSG_RAID_INSTANCE_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2CD*/ { "CMSG_REQUEST_RAID_INFO", STATUS_LOGGEDIN, &WorldSession::HandleRequestRaidInfoOpcode }, + /*0x2CE*/ { "CMSG_MOVE_TIME_SKIPPED", STATUS_LOGGEDIN, &WorldSession::HandleMoveTimeSkippedOpcode }, + /*0x2CF*/ { "CMSG_MOVE_FEATHER_FALL_ACK", STATUS_LOGGEDIN, &WorldSession::HandleFeatherFallAck }, + /*0x2D0*/ { "CMSG_MOVE_WATER_WALK_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveWaterWalkAck }, + /*0x2D1*/ { "CMSG_MOVE_NOT_ACTIVE_MOVER", STATUS_LOGGEDIN, &WorldSession::HandleMoveNotActiveMover }, + /*0x2D2*/ { "SMSG_PLAY_SOUND", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2D3*/ { "CMSG_BATTLEFIELD_STATUS", STATUS_LOGGEDIN, &WorldSession::HandleBattlefieldStatusOpcode }, + /*0x2D4*/ { "SMSG_BATTLEFIELD_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2D5*/ { "CMSG_BATTLEFIELD_PORT", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundPlayerPortOpcode}, + /*0x2D6*/ { "MSG_INSPECT_HONOR_STATS", STATUS_LOGGEDIN, &WorldSession::HandleInspectHonorStatsOpcode }, + /*0x2D7*/ { "CMSG_BATTLEMASTER_HELLO", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundHelloOpcode }, + /*0x2D8*/ { "CMSG_MOVE_START_SWIM_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x2D9*/ { "CMSG_MOVE_STOP_SWIM_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x2DA*/ { "SMSG_FORCE_WALK_SPEED_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2DB*/ { "CMSG_FORCE_WALK_SPEED_CHANGE_ACK", STATUS_LOGGEDIN, &WorldSession::HandleForceSpeedChangeAck }, + /*0x2DC*/ { "SMSG_FORCE_SWIM_BACK_SPEED_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2DD*/ { "CMSG_FORCE_SWIM_BACK_SPEED_CHANGE_ACK", STATUS_LOGGEDIN, &WorldSession::HandleForceSpeedChangeAck }, + /*0x2DE*/ { "SMSG_FORCE_TURN_RATE_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2DF*/ { "CMSG_FORCE_TURN_RATE_CHANGE_ACK", STATUS_LOGGEDIN, &WorldSession::HandleForceSpeedChangeAck }, + /*0x2E0*/ { "MSG_PVP_LOG_DATA", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundPVPlogdataOpcode}, + /*0x2E1*/ { "CMSG_LEAVE_BATTLEFIELD", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundLeaveOpcode }, + /*0x2E2*/ { "CMSG_AREA_SPIRIT_HEALER_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleAreaSpiritHealerQueryOpcode}, + /*0x2E3*/ { "CMSG_AREA_SPIRIT_HEALER_QUEUE", STATUS_LOGGEDIN, &WorldSession::HandleAreaSpiritHealerQueueOpcode}, + /*0x2E4*/ { "SMSG_AREA_SPIRIT_HEALER_TIME", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2E5*/ { "CMSG_GM_UNTEACH", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x2E6*/ { "SMSG_WARDEN_DATA", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2E7*/ { "CMSG_WARDEN_DATA", STATUS_LOGGEDIN, &WorldSession::HandleWardenDataOpcode }, + /*0x2E8*/ { "SMSG_GROUP_JOINED_BATTLEGROUND", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2E9*/ { "MSG_BATTLEGROUND_PLAYER_POSITIONS", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundPlayerPositionsOpcode}, + /*0x2EA*/ { "CMSG_PET_STOP_ATTACK", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x2EB*/ { "SMSG_BINDER_CONFIRM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2EC*/ { "SMSG_BATTLEGROUND_PLAYER_JOINED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2ED*/ { "SMSG_BATTLEGROUND_PLAYER_LEFT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2EE*/ { "CMSG_BATTLEMASTER_JOIN", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundJoinOpcode }, + /*0x2EF*/ { "SMSG_ADDON_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2F0*/ { "CMSG_PET_UNLEARN", STATUS_LOGGEDIN, &WorldSession::HandlePetUnlearnOpcode }, + /*0x2F1*/ { "SMSG_PET_UNLEARN_CONFIRM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2F2*/ { "SMSG_PARTY_MEMBER_STATS_FULL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2F3*/ { "CMSG_PET_SPELL_AUTOCAST", STATUS_LOGGEDIN, &WorldSession::HandlePetSpellAutocastOpcode }, + /*0x2F4*/ { "SMSG_WEATHER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2F5*/ { "SMSG_PLAY_TIME_WARNING", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2F6*/ { "SMSG_MINIGAME_SETUP", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2F7*/ { "SMSG_MINIGAME_STATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2F8*/ { "CMSG_MINIGAME_MOVE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x2F9*/ { "SMSG_MINIGAME_MOVE_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2FA*/ { "SMSG_RAID_INSTANCE_MESSAGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2FB*/ { "SMSG_COMPRESSED_MOVES", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2FC*/ { "CMSG_GUILD_INFO_TEXT", STATUS_LOGGEDIN, &WorldSession::HandleGuildChangeInfoOpcode }, + /*0x2FD*/ { "SMSG_CHAT_RESTRICTED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2FE*/ { "SMSG_SPLINE_SET_RUN_SPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2FF*/ { "SMSG_SPLINE_SET_RUN_BACK_SPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x300*/ { "SMSG_SPLINE_SET_SWIM_SPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x301*/ { "SMSG_SPLINE_SET_WALK_SPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x302*/ { "SMSG_SPLINE_SET_SWIM_BACK_SPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x303*/ { "SMSG_SPLINE_SET_TURN_RATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x304*/ { "SMSG_SPLINE_MOVE_UNROOT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x305*/ { "SMSG_SPLINE_MOVE_FEATHER_FALL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x306*/ { "SMSG_SPLINE_MOVE_NORMAL_FALL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x307*/ { "SMSG_SPLINE_MOVE_SET_HOVER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x308*/ { "SMSG_SPLINE_MOVE_UNSET_HOVER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x309*/ { "SMSG_SPLINE_MOVE_WATER_WALK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x30A*/ { "SMSG_SPLINE_MOVE_LAND_WALK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x30B*/ { "SMSG_SPLINE_MOVE_START_SWIM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x30C*/ { "SMSG_SPLINE_MOVE_STOP_SWIM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x30D*/ { "SMSG_SPLINE_MOVE_SET_RUN_MODE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x30E*/ { "SMSG_SPLINE_MOVE_SET_WALK_MODE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x30F*/ { "CMSG_GM_NUKE_ACCOUNT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x310*/ { "MSG_GM_DESTROY_CORPSE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x311*/ { "CMSG_GM_DESTROY_ONLINE_CORPSE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x312*/ { "CMSG_ACTIVATETAXIEXPRESS", STATUS_LOGGEDIN, &WorldSession::HandleActivateTaxiFarOpcode }, + /*0x313*/ { "SMSG_SET_FACTION_ATWAR", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x314*/ { "SMSG_GAMETIMEBIAS_SET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x315*/ { "CMSG_DEBUG_ACTIONS_START", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x316*/ { "CMSG_DEBUG_ACTIONS_STOP", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x317*/ { "CMSG_SET_FACTION_INACTIVE", STATUS_LOGGEDIN, &WorldSession::HandleSetWatchedFactionInactiveOpcode}, + /*0x318*/ { "CMSG_SET_WATCHED_FACTION", STATUS_LOGGEDIN, &WorldSession::HandleSetWatchedFactionIndexOpcode}, + /*0x319*/ { "MSG_MOVE_TIME_SKIPPED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x31A*/ { "SMSG_SPLINE_MOVE_ROOT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x31B*/ { "CMSG_SET_EXPLORATION_ALL", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x31C*/ { "SMSG_INVALIDATE_PLAYER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x31D*/ { "CMSG_RESET_INSTANCES", STATUS_LOGGEDIN, &WorldSession::HandleResetInstancesOpcode }, + /*0x31E*/ { "SMSG_INSTANCE_RESET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x31F*/ { "SMSG_INSTANCE_RESET_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x320*/ { "SMSG_UPDATE_LAST_INSTANCE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x321*/ { "MSG_RAID_TARGET_UPDATE", STATUS_LOGGEDIN, &WorldSession::HandleRaidIconTargetOpcode }, + /*0x322*/ { "MSG_RAID_READY_CHECK", STATUS_LOGGEDIN, &WorldSession::HandleRaidReadyCheckOpcode }, + /*0x323*/ { "CMSG_LUA_USAGE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x324*/ { "SMSG_PET_ACTION_SOUND", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x325*/ { "SMSG_PET_DISMISS_SOUND", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x326*/ { "SMSG_GHOSTEE_GONE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x327*/ { "CMSG_GM_UPDATE_TICKET_STATUS", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x328*/ { "SMSG_GM_TICKET_STATUS_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x329*/ { "MSG_SET_DUNGEON_DIFFICULTY", STATUS_LOGGEDIN, &WorldSession::HandleDungeonDifficultyOpcode }, + /*0x32A*/ { "CMSG_GMSURVEY_SUBMIT", STATUS_NEVER, &WorldSession::Handle_NULL },//LOGGEDIN, &WorldSession::HandleGMSurveySubmit }, + /*0x32B*/ { "SMSG_UPDATE_INSTANCE_OWNERSHIP", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x32C*/ { "CMSG_IGNORE_KNOCKBACK_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x32D*/ { "SMSG_CHAT_PLAYER_AMBIGUOUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x32E*/ { "MSG_DELAY_GHOST_TELEPORT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x32F*/ { "SMSG_SPELLINSTAKILLLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x330*/ { "SMSG_SPELL_UPDATE_CHAIN_TARGETS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x331*/ { "CMSG_CHAT_FILTERED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x332*/ { "SMSG_EXPECTED_SPAM_RECORDS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x333*/ { "SMSG_SPELLSTEALLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x334*/ { "CMSG_LOTTERY_QUERY_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x335*/ { "SMSG_LOTTERY_QUERY_RESULT_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x336*/ { "CMSG_BUY_LOTTERY_TICKET_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x337*/ { "SMSG_LOTTERY_RESULT_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x338*/ { "SMSG_CHARACTER_PROFILE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x339*/ { "SMSG_CHARACTER_PROFILE_REALM_CONNECTED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x33A*/ { "SMSG_DEFENSE_MESSAGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x33B*/ { "SMSG_INSTANCE_DIFFICULTY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x33C*/ { "MSG_GM_RESETINSTANCELIMIT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x33D*/ { "SMSG_MOTD", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x33E*/ { "SMSG_MOVE_SET_FLIGHT_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x33F*/ { "SMSG_MOVE_UNSET_FLIGHT_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x340*/ { "CMSG_MOVE_FLIGHT_ACK_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x341*/ { "MSG_MOVE_START_SWIM_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x342*/ { "MSG_MOVE_STOP_SWIM_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x343*/ { "SMSG_MOVE_SET_CAN_FLY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x344*/ { "SMSG_MOVE_UNSET_CAN_FLY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x345*/ { "CMSG_MOVE_SET_CAN_FLY_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveFlyModeChangeAckOpcode}, + /*0x346*/ { "CMSG_MOVE_SET_FLY", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x347*/ { "CMSG_SOCKET_GEMS", STATUS_LOGGEDIN, &WorldSession::HandleSocketOpcode }, + /*0x348*/ { "CMSG_ARENA_TEAM_CREATE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x349*/ { "SMSG_ARENA_TEAM_COMMAND_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x34A*/ { "UMSG_UPDATE_ARENA_TEAM_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x34B*/ { "CMSG_ARENA_TEAM_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamQueryOpcode }, + /*0x34C*/ { "SMSG_ARENA_TEAM_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x34D*/ { "CMSG_ARENA_TEAM_ROSTER", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamRosterOpcode }, + /*0x34E*/ { "SMSG_ARENA_TEAM_ROSTER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x34F*/ { "CMSG_ARENA_TEAM_INVITE", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamAddMemberOpcode }, + /*0x350*/ { "SMSG_ARENA_TEAM_INVITE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x351*/ { "CMSG_ARENA_TEAM_ACCEPT", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamInviteAcceptOpcode}, + /*0x352*/ { "CMSG_ARENA_TEAM_DECLINE", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamInviteDeclineOpcode}, + /*0x353*/ { "CMSG_ARENA_TEAM_LEAVE", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamLeaveOpcode }, + /*0x354*/ { "CMSG_ARENA_TEAM_REMOVE", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamRemoveFromTeamOpcode}, + /*0x355*/ { "CMSG_ARENA_TEAM_DISBAND", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamDisbandOpcode }, + /*0x356*/ { "CMSG_ARENA_TEAM_LEADER", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamPromoteToCaptainOpcode}, + /*0x357*/ { "SMSG_ARENA_TEAM_EVENT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x358*/ { "CMSG_BATTLEMASTER_JOIN_ARENA", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundArenaJoin }, + /*0x359*/ { "MSG_MOVE_START_ASCEND", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x35A*/ { "MSG_MOVE_STOP_ASCEND", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x35B*/ { "SMSG_ARENA_TEAM_STATS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x35C*/ { "CMSG_LFG_SET_AUTOJOIN", STATUS_AUTHED, &WorldSession::HandleLfgAutoJoinOpcode }, + /*0x35D*/ { "CMSG_LFG_CLEAR_AUTOJOIN", STATUS_LOGGEDIN, &WorldSession::HandleLfgCancelAutoJoinOpcode }, + /*0x35E*/ { "CMSG_LFM_SET_AUTOFILL", STATUS_AUTHED, &WorldSession::HandleLfmAutoAddMembersOpcode }, + /*0x35F*/ { "CMSG_LFM_CLEAR_AUTOFILL", STATUS_LOGGEDIN, &WorldSession::HandleLfmCancelAutoAddmembersOpcode}, + /*0x360*/ { "CMSG_ACCEPT_LFG_MATCH", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x361*/ { "CMSG_DECLINE_LFG_MATCH", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x362*/ { "CMSG_CANCEL_PENDING_LFG", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x363*/ { "CMSG_CLEAR_LOOKING_FOR_GROUP", STATUS_LOGGEDIN, &WorldSession::HandleLfgClearOpcode }, + /*0x364*/ { "CMSG_CLEAR_LOOKING_FOR_MORE", STATUS_LOGGEDIN, &WorldSession::HandleLfmSetNoneOpcode }, + /*0x365*/ { "CMSG_SET_LOOKING_FOR_MORE", STATUS_LOGGEDIN, &WorldSession::HandleLfmSetOpcode }, + /*0x366*/ { "CMSG_SET_LFG_COMMENT", STATUS_LOGGEDIN, &WorldSession::HandleLfgSetCommentOpcode }, + /*0x367*/ { "SMSG_LFG_TIMEDOUT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x368*/ { "SMSG_LFG_OTHER_TIMEDOUT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x369*/ { "SMSG_LFG_AUTOJOIN_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x36A*/ { "SMSG_LFG_AUTOJOIN_FAILED_NO_PLAYER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x36B*/ { "SMSG_LFG_LEADER_IS_LFM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x36C*/ { "SMSG_LFG_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x36D*/ { "SMSG_LFG_UPDATE_LFM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x36E*/ { "SMSG_LFG_UPDATE_LFG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x36F*/ { "SMSG_LFG_UPDATE_QUEUED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x370*/ { "SMSG_LFG_PENDING_INVITE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x371*/ { "SMSG_LFG_PENDING_MATCH", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x372*/ { "SMSG_LFG_PENDING_MATCH_DONE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x373*/ { "SMSG_TITLE_EARNED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x374*/ { "CMSG_SET_TITLE", STATUS_LOGGEDIN, &WorldSession::HandleChooseTitleOpcode }, + /*0x375*/ { "CMSG_CANCEL_MOUNT_AURA", STATUS_LOGGEDIN, &WorldSession::HandleDismountOpcode }, + /*0x376*/ { "SMSG_ARENA_ERROR", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x377*/ { "MSG_INSPECT_ARENA_TEAMS", STATUS_LOGGEDIN, &WorldSession::HandleInspectArenaStatsOpcode }, + /*0x378*/ { "SMSG_DEATH_RELEASE_LOC", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x379*/ { "CMSG_CANCEL_TEMP_ENCHANTMENT", STATUS_LOGGEDIN, &WorldSession::HandleCancelTempItemEnchantmentOpcode}, + /*0x37A*/ { "SMSG_FORCED_DEATH_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x37B*/ { "CMSG_CHEAT_SET_HONOR_CURRENCY", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x37C*/ { "CMSG_CHEAT_SET_ARENA_CURRENCY", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x37D*/ { "MSG_MOVE_SET_FLIGHT_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x37E*/ { "MSG_MOVE_SET_FLIGHT_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x37F*/ { "MSG_MOVE_SET_FLIGHT_BACK_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x380*/ { "MSG_MOVE_SET_FLIGHT_BACK_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x381*/ { "SMSG_FORCE_FLIGHT_SPEED_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x382*/ { "CMSG_FORCE_FLIGHT_SPEED_CHANGE_ACK", STATUS_LOGGEDIN, &WorldSession::HandleForceSpeedChangeAck }, + /*0x383*/ { "SMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x384*/ { "CMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE_ACK", STATUS_LOGGEDIN, &WorldSession::HandleForceSpeedChangeAck }, + /*0x385*/ { "SMSG_SPLINE_SET_FLIGHT_SPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x386*/ { "SMSG_SPLINE_SET_FLIGHT_BACK_SPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x387*/ { "CMSG_MAELSTROM_INVALIDATE_CACHE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x388*/ { "SMSG_FLIGHT_SPLINE_SYNC", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x389*/ { "CMSG_SET_TAXI_BENCHMARK_MODE", STATUS_AUTHED, &WorldSession::HandleSetTaxiBenchmarkOpcode }, + /*0x38A*/ { "SMSG_JOINED_BATTLEGROUND_QUEUE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x38B*/ { "SMSG_REALM_SPLIT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x38C*/ { "CMSG_REALM_SPLIT", STATUS_AUTHED, &WorldSession::HandleRealmStateRequestOpcode }, + /*0x38D*/ { "CMSG_MOVE_CHNG_TRANSPORT", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x38E*/ { "MSG_PARTY_ASSIGNMENT", STATUS_LOGGEDIN, &WorldSession::HandleGroupPromoteOpcode }, + /*0x38F*/ { "SMSG_OFFER_PETITION_ERROR", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x390*/ { "SMSG_TIME_SYNC_REQ", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x391*/ { "CMSG_TIME_SYNC_RESP", STATUS_LOGGEDIN, &WorldSession::HandleTimeSyncResp }, + /*0x392*/ { "CMSG_SEND_LOCAL_EVENT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x393*/ { "CMSG_SEND_GENERAL_TRIGGER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x394*/ { "CMSG_SEND_COMBAT_TRIGGER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x395*/ { "CMSG_MAELSTROM_GM_SENT_MAIL", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x396*/ { "SMSG_RESET_FAILED_NOTIFY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x397*/ { "SMSG_REAL_GROUP_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x398*/ { "SMSG_LFG_DISABLED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x399*/ { "CMSG_ACTIVE_PVP_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x39A*/ { "CMSG_CHEAT_DUMP_ITEMS_DEBUG_ONLY", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x39B*/ { "SMSG_CHEAT_DUMP_ITEMS_DEBUG_ONLY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x39C*/ { "SMSG_CHEAT_DUMP_ITEMS_DEBUG_ONLY_RESPONSE_WRITE_FILE",STATUS_NEVER,&WorldSession::Handle_ServerSide }, + /*0x39D*/ { "SMSG_UPDATE_COMBO_POINTS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x39E*/ { "SMSG_VOICE_SESSION_ROSTER_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x39F*/ { "SMSG_VOICE_SESSION_LEAVE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3A0*/ { "SMSG_VOICE_SESSION_ADJUST_PRIORITY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3A1*/ { "CMSG_VOICE_SET_TALKER_MUTED_REQUEST", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3A2*/ { "SMSG_VOICE_SET_TALKER_MUTED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3A3*/ { "SMSG_INIT_EXTRA_AURA_INFO_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3A4*/ { "SMSG_SET_EXTRA_AURA_INFO_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3A5*/ { "SMSG_SET_EXTRA_AURA_INFO_NEED_UPDATE_OBSOLETE",STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3A6*/ { "SMSG_CLEAR_EXTRA_AURA_INFO_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3A7*/ { "MSG_MOVE_START_DESCEND", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x3A8*/ { "CMSG_IGNORE_REQUIREMENTS_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3A9*/ { "SMSG_IGNORE_REQUIREMENTS_CHEAT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3AA*/ { "SMSG_SPELL_CHANCE_PROC_LOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3AB*/ { "CMSG_MOVE_SET_RUN_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3AC*/ { "SMSG_DISMOUNT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3AD*/ { "MSG_MOVE_UPDATE_CAN_FLY", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3AE*/ { "MSG_RAID_READY_CHECK_CONFIRM", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3AF*/ { "CMSG_VOICE_SESSION_ENABLE", STATUS_AUTHED, &WorldSession::HandleVoiceSettingsOpcode }, + /*0x3B0*/ { "SMSG_VOICE_SESSION_ENABLE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3B1*/ { "SMSG_VOICE_PARENTAL_CONTROLS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3B2*/ { "CMSG_GM_WHISPER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3B3*/ { "SMSG_GM_MESSAGECHAT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3B4*/ { "MSG_GM_GEARRATING", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3B5*/ { "CMSG_COMMENTATOR_ENABLE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3B6*/ { "SMSG_COMMENTATOR_STATE_CHANGED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3B7*/ { "CMSG_COMMENTATOR_GET_MAP_INFO", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3B8*/ { "SMSG_COMMENTATOR_MAP_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3B9*/ { "CMSG_COMMENTATOR_GET_PLAYER_INFO", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3BA*/ { "SMSG_COMMENTATOR_GET_PLAYER_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3BB*/ { "SMSG_COMMENTATOR_PLAYER_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3BC*/ { "CMSG_COMMENTATOR_ENTER_INSTANCE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3BD*/ { "CMSG_COMMENTATOR_EXIT_INSTANCE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3BE*/ { "CMSG_COMMENTATOR_INSTANCE_COMMAND", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3BF*/ { "SMSG_CLEAR_TARGET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3C0*/ { "CMSG_BOT_DETECTED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3C1*/ { "SMSG_CROSSED_INEBRIATION_THRESHOLD", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3C2*/ { "CMSG_CHEAT_PLAYER_LOGIN", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3C3*/ { "CMSG_CHEAT_PLAYER_LOOKUP", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3C4*/ { "SMSG_CHEAT_PLAYER_LOOKUP", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3C5*/ { "SMSG_KICK_REASON", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3C6*/ { "MSG_RAID_READY_CHECK_FINISHED", STATUS_LOGGEDIN, &WorldSession::HandleRaidReadyCheckFinishOpcode}, + /*0x3C7*/ { "CMSG_COMPLAIN", STATUS_LOGGEDIN, &WorldSession::HandleReportSpamOpcode }, + /*0x3C8*/ { "SMSG_COMPLAIN_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3C9*/ { "SMSG_FEATURE_SYSTEM_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3CA*/ { "CMSG_GM_SHOW_COMPLAINTS", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3CB*/ { "CMSG_GM_UNSQUELCH", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3CC*/ { "CMSG_CHANNEL_SILENCE_VOICE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3CD*/ { "CMSG_CHANNEL_SILENCE_ALL", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3CE*/ { "CMSG_CHANNEL_UNSILENCE_VOICE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3CF*/ { "CMSG_CHANNEL_UNSILENCE_ALL", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3D0*/ { "CMSG_TARGET_CAST", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3D1*/ { "CMSG_TARGET_SCRIPT_CAST", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3D2*/ { "CMSG_CHANNEL_DISPLAY_LIST", STATUS_LOGGEDIN, &WorldSession::HandleChannelRosterQuery }, + /*0x3D3*/ { "CMSG_SET_ACTIVE_VOICE_CHANNEL", STATUS_AUTHED, &WorldSession::HandleChannelVoiceChatQuery }, + /*0x3D4*/ { "CMSG_GET_CHANNEL_MEMBER_COUNT", STATUS_LOGGEDIN, &WorldSession::HandleChannelInfoQuery }, + /*0x3D5*/ { "SMSG_CHANNEL_MEMBER_COUNT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3D6*/ { "CMSG_CHANNEL_VOICE_ON", STATUS_LOGGEDIN, &WorldSession::HandleChannelEnableVoiceOpcode }, + /*0x3D7*/ { "CMSG_CHANNEL_VOICE_OFF", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3D8*/ { "CMSG_DEBUG_LIST_TARGETS", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3D9*/ { "SMSG_DEBUG_LIST_TARGETS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3DA*/ { "SMSG_AVAILABLE_VOICE_CHANNEL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3DB*/ { "CMSG_ADD_VOICE_IGNORE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3DC*/ { "CMSG_DEL_VOICE_IGNORE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3DD*/ { "CMSG_PARTY_SILENCE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3DE*/ { "CMSG_PARTY_UNSILENCE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3DF*/ { "MSG_NOTIFY_PARTY_SQUELCH", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3E0*/ { "SMSG_COMSAT_RECONNECT_TRY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3E1*/ { "SMSG_COMSAT_DISCONNECT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3E2*/ { "SMSG_COMSAT_CONNECT_FAIL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3E3*/ { "SMSG_VOICE_CHAT_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3E4*/ { "CMSG_REPORT_PVP_AFK", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundReportAFK }, + /*0x3E5*/ { "CMSG_REPORT_PVP_AFK_RESULT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3E6*/ { "CMSG_GUILD_BANKER_ACTIVATE", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankQuery }, + /*0x3E7*/ { "CMSG_GUILD_BANK_QUERY_TAB", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankTabColon }, + /*0x3E8*/ { "SMSG_GUILD_BANK_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3E9*/ { "CMSG_GUILD_BANK_SWAP_ITEMS", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankDepositItem }, + /*0x3EA*/ { "CMSG_GUILD_BANK_BUY_TAB", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankBuyTab }, + /*0x3EB*/ { "CMSG_GUILD_BANK_UPDATE_TAB", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankModifyTab }, + /*0x3EC*/ { "CMSG_GUILD_BANK_DEPOSIT_MONEY", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankDeposit }, + /*0x3ED*/ { "CMSG_GUILD_BANK_WITHDRAW_MONEY", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankWithdraw }, + /*0x3EE*/ { "MSG_GUILD_BANK_LOG_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankLog }, + /*0x3EF*/ { "CMSG_SET_CHANNEL_WATCH", STATUS_LOGGEDIN, &WorldSession::HandleChannelJoinNotify }, + /*0x3F0*/ { "SMSG_USERLIST_ADD", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3F1*/ { "SMSG_USERLIST_REMOVE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3F2*/ { "SMSG_USERLIST_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3F3*/ { "CMSG_CLEAR_CHANNEL_WATCH", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3F4*/ { "SMSG_INSPECT_TALENT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3F5*/ { "SMSG_GOGOGO_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3F6*/ { "SMSG_ECHO_PARTY_SQUELCH", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3F7*/ { "CMSG_SET_TITLE_SUFFIX", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3F8*/ { "CMSG_SPELLCLICK", STATUS_LOGGEDIN, &WorldSession::HandleSpellClick }, + /*0x3F9*/ { "SMSG_LOOT_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3FA*/ { "CMSG_GM_CHARACTER_RESTORE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3FB*/ { "CMSG_GM_CHARACTER_SAVE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3FC*/ { "SMSG_VOICESESSION_FULL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3FD*/ { "MSG_GUILD_PERMISSIONS", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankGetRights }, + /*0x3FE*/ { "MSG_GUILD_BANK_MONEY_WITHDRAWN", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankGetMoneyAmount }, + /*0x3FF*/ { "MSG_GUILD_EVENT_LOG_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleGuildEventLogOpcode }, + /*0x400*/ { "CMSG_MAELSTROM_RENAME_GUILD", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x401*/ { "CMSG_GET_MIRRORIMAGE_DATA", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x402*/ { "SMSG_MIRRORIMAGE_DATA", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x403*/ { "SMSG_FORCE_DISPLAY_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x404*/ { "SMSG_SPELL_CHANCE_RESIST_PUSHBACK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x405*/ { "CMSG_IGNORE_DIMINISHING_RETURNS_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x406*/ { "SMSG_IGNORE_DIMINISHING_RETURNS_CHEAT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x407*/ { "CMSG_KEEP_ALIVE", STATUS_NEVER, &WorldSession::Handle_EarlyProccess }, + /*0x408*/ { "SMSG_RAID_READY_CHECK_ERROR", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x409*/ { "CMSG_OPT_OUT_OF_LOOT", STATUS_AUTHED, &WorldSession::HandleGroupPassOnLootOpcode }, + /*0x40A*/ { "MSG_QUERY_GUILD_BANK_TEXT", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankTabText }, + /*0x40B*/ { "CMSG_SET_GUILD_BANK_TEXT", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankSetTabText }, + /*0x40C*/ { "CMSG_SET_GRANTABLE_LEVELS", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x40D*/ { "CMSG_GRANT_LEVEL", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x40E*/ { "CMSG_REFER_A_FRIEND", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x40F*/ { "CMSG_DECLINE_CHANNEL_INVITE", STATUS_LOGGEDIN, &WorldSession::HandleChannelDeclineInvite }, + /*0x410*/ { "CMSG_DECLINE_CHANNEL_INVITE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x411*/ { "CMSG_GROUPACTION_THROTTLED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x412*/ { "SMSG_OVERRIDE_LIGHT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x413*/ { "SMSG_TOTEM_CREATED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x414*/ { "CMSG_TOTEM_DESTROYED", STATUS_LOGGEDIN, &WorldSession::HandleTotemDestroy }, + /*0x415*/ { "CMSG_EXPIRE_RAID_INSTANCE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x416*/ { "CMSG_NO_SPELL_VARIANCE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x417*/ { "CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverStatusQueryMultipleOpcode}, + /*0x418*/ { "SMSG_QUESTGIVER_STATUS_MULTIPLE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x419*/ { "CMSG_SET_PLAYER_DECLINED_NAMES", STATUS_AUTHED, &WorldSession::HandleDeclinedPlayerNameOpcode }, + /*0x41A*/ { "SMSG_SET_PLAYER_DECLINED_NAMES_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x41B*/ { "CMSG_QUERY_SERVER_BUCK_DATA", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x41C*/ { "CMSG_CLEAR_SERVER_BUCK_DATA", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x41D*/ { "SMSG_SERVER_BUCK_DATA", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x41E*/ { "SMSG_SEND_UNLEARN_SPELLS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x41F*/ { "SMSG_PROPOSE_LEVEL_GRANT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x420*/ { "CMSG_ACCEPT_LEVEL_GRANT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x421*/ { "SMSG_REFER_A_FRIEND_FAILURE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x422*/ { "SMSG_SPLINE_MOVE_SET_FLYING", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x423*/ { "SMSG_SPLINE_MOVE_UNSET_FLYING", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x424*/ { "SMSG_SUMMON_CANCEL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x425*/ { "CMSG_CHANGE_PERSONAL_ARENA_RATING", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x426*/ { "CMSG_ALTER_APPEARANCE", STATUS_LOGGEDIN, &WorldSession::HandleAlterAppearance }, + /*0x427*/ { "SMSG_ENABLE_BARBER_SHOP", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x428*/ { "SMSG_BARBER_SHOP_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x429*/ { "CMSG_CALENDAR_GET_CALENDAR", STATUS_LOGGEDIN, &WorldSession::HandleCalendarGetCalendar }, + /*0x42A*/ { "CMSG_CALENDAR_GET_EVENT", STATUS_LOGGEDIN, &WorldSession::HandleCalendarGetEvent }, + /*0x42B*/ { "CMSG_CALENDAR_GUILD_FILTER", STATUS_LOGGEDIN, &WorldSession::HandleCalendarGuildFilter }, + /*0x42C*/ { "CMSG_CALENDAR_ARENA_TEAM", STATUS_LOGGEDIN, &WorldSession::HandleCalendarArenaTeam }, + /*0x42D*/ { "CMSG_CALENDAR_ADD_EVENT", STATUS_LOGGEDIN, &WorldSession::HandleCalendarAddEvent }, + /*0x42E*/ { "CMSG_CALENDAR_UPDATE_EVENT", STATUS_LOGGEDIN, &WorldSession::HandleCalendarUpdateEvent }, + /*0x42F*/ { "CMSG_CALENDAR_REMOVE_EVENT", STATUS_LOGGEDIN, &WorldSession::HandleCalendarRemoveEvent }, + /*0x430*/ { "CMSG_CALENDAR_COPY_EVENT", STATUS_LOGGEDIN, &WorldSession::HandleCalendarCopyEvent }, + /*0x431*/ { "CMSG_CALENDAR_EVENT_INVITE", STATUS_LOGGEDIN, &WorldSession::HandleCalendarEventInvite }, + /*0x432*/ { "CMSG_CALENDAR_EVENT_RSVP", STATUS_LOGGEDIN, &WorldSession::HandleCalendarEventRsvp }, + /*0x433*/ { "CMSG_CALENDAR_EVENT_REMOVE_INVITE", STATUS_LOGGEDIN, &WorldSession::HandleCalendarEventRemoveInvite }, + /*0x434*/ { "CMSG_CALENDAR_EVENT_STATUS", STATUS_LOGGEDIN, &WorldSession::HandleCalendarEventStatus }, + /*0x435*/ { "CMSG_CALENDAR_EVENT_MODERATOR_STATUS", STATUS_LOGGEDIN, &WorldSession::HandleCalendarEventModeratorStatus}, + /*0x436*/ { "SMSG_CALENDAR_SEND_CALENDAR", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x437*/ { "SMSG_CALENDAR_SEND_EVENT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x438*/ { "SMSG_CALENDAR_FILTER_GUILD", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x439*/ { "SMSG_CALENDAR_ARENA_TEAM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x43A*/ { "SMSG_CALENDAR_EVENT_INVITE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x43B*/ { "SMSG_CALENDAR_EVENT_INVITE_REMOVED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x43C*/ { "SMSG_CALENDAR_EVENT_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x43D*/ { "SMSG_CALENDAR_COMMAND_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x43E*/ { "SMSG_CALENDAR_RAID_LOCKOUT_ADDED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x43F*/ { "SMSG_CALENDAR_RAID_LOCKOUT_REMOVED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x440*/ { "SMSG_CALENDAR_EVENT_INVITE_ALERT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x441*/ { "SMSG_CALENDAR_EVENT_INVITE_REMOVED_ALERT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x442*/ { "SMSG_CALENDAR_EVENT_INVITE_STATUS_ALERT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x443*/ { "SMSG_CALENDAR_EVENT_REMOVED_ALERT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x444*/ { "SMSG_CALENDAR_EVENT_UPDATED_ALERT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x445*/ { "SMSG_CALENDAR_EVENT_MODERATOR_STATUS_ALERT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x446*/ { "CMSG_CALENDAR_COMPLAIN", STATUS_LOGGEDIN, &WorldSession::HandleCalendarComplain }, + /*0x447*/ { "CMSG_CALENDAR_GET_NUM_PENDING", STATUS_LOGGEDIN, &WorldSession::HandleCalendarGetNumPending }, + /*0x448*/ { "SMSG_CALENDAR_SEND_NUM_PENDING", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x449*/ { "CMSG_SAVE_DANCE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x44A*/ { "SMSG_NOTIFY_DANCE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x44B*/ { "CMSG_PLAY_DANCE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x44C*/ { "SMSG_PLAY_DANCE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x44D*/ { "CMSG_LOAD_DANCES", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x44E*/ { "CMSG_STOP_DANCE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x44F*/ { "SMSG_STOP_DANCE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x450*/ { "CMSG_SYNC_DANCE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x451*/ { "CMSG_DANCE_QUERY", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x452*/ { "SMSG_DANCE_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x453*/ { "SMSG_INVALIDATE_DANCE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x454*/ { "CMSG_DELETE_DANCE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x455*/ { "SMSG_LEARNED_DANCE_MOVES", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x456*/ { "CMSG_LEARN_DANCE_MOVE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x457*/ { "CMSG_UNLEARN_DANCE_MOVE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x458*/ { "CMSG_SET_RUNE_COUNT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x459*/ { "CMSG_SET_RUNE_COOLDOWN", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x45A*/ { "MSG_MOVE_SET_PITCH_RATE_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x45B*/ { "MSG_MOVE_SET_PITCH_RATE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x45C*/ { "SMSG_FORCE_PITCH_RATE_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x45D*/ { "CMSG_FORCE_PITCH_RATE_CHANGE_ACK", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x45E*/ { "SMSG_SPLINE_SET_PITCH_RATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x45F*/ { "SMSG_MOVE_ABANDON_TRANSPORT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x460*/ { "MSG_MOVE_ABANDON_TRANSPORT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x461*/ { "CMSG_MOVE_ABANDON_TRANSPORT_ACK", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x462*/ { "CMSG_UPDATE_MISSILE_TRAJECTORY", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x463*/ { "SMSG_UPDATE_ACCOUNT_DATA_COMPLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x464*/ { "SMSG_TRIGGER_MOVIE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x465*/ { "CMSG_COMPLETE_MOVIE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x466*/ { "CMSG_SET_GLYPH_SLOT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x467*/ { "CMSG_SET_GLYPH", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x468*/ { "SMSG_ACHIEVEMENT_EARNED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x469*/ { "SMSG_DYNAMIC_DROP_ROLL_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x46A*/ { "SMSG_CRITERIA_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x46B*/ { "CMSG_QUERY_INSPECT_ACHIEVEMENTS", STATUS_LOGGEDIN, &WorldSession::HandleInspectAchievements }, + /*0x46C*/ { "SMSG_RESPOND_INSPECT_ACHIEVEMENTS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x46D*/ { "CMSG_DISMISS_CONTROLLED_VEHICLE", STATUS_LOGGEDIN, &WorldSession::HandleDismissControlledVehicle }, + /*0x46E*/ { "CMSG_COMPLETE_ACHIEVEMENT_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x46F*/ { "SMSG_QUESTUPDATE_ADD_PVP_KILL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x470*/ { "CMSG_SET_CRITERIA_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x471*/ { "SMSG_GROUP_SWAP_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x472*/ { "CMSG_UNITANIMTIER_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x473*/ { "CMSG_CHAR_CUSTOMIZE", STATUS_AUTHED, &WorldSession::HandleCharCustomize }, + /*0x474*/ { "SMSG_CHAR_CUSTOMIZE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x475*/ { "SMSG_PET_RENAMEABLE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x476*/ { "CMSG_REQUEST_VEHICLE_EXIT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x477*/ { "CMSG_REQUEST_VEHICLE_PREV_SEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x478*/ { "CMSG_REQUEST_VEHICLE_NEXT_SEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x479*/ { "CMSG_REQUEST_VEHICLE_SWITCH_SEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x47A*/ { "CMSG_PET_LEARN_TALENT", STATUS_LOGGEDIN, &WorldSession::HandlePetLearnTalent }, + /*0x47B*/ { "CMSG_PET_UNLEARN_TALENTS", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x47C*/ { "SMSG_SET_PHASE_SHIFT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x47D*/ { "SMSG_ALL_ACHIEVEMENT_DATA", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x47E*/ { "CMSG_FORCE_SAY_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x47F*/ { "SMSG_HEALTH_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x480*/ { "SMSG_POWER_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x481*/ { "CMSG_GAMEOBJ_REPORT_USE", STATUS_LOGGEDIN, &WorldSession::HandleGameobjectReportUse }, + /*0x482*/ { "SMSG_HIGHEST_THREAT_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x483*/ { "SMSG_THREAT_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x484*/ { "SMSG_THREAT_REMOVE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x485*/ { "SMSG_THREAT_CLEAR", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x486*/ { "SMSG_CONVERT_RUNE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x487*/ { "SMSG_RESYNC_RUNES", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x488*/ { "SMSG_ADD_RUNE_POWER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x489*/ { "CMSG_START_QUEST", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x48A*/ { "CMSG_REMOVE_GLYPH", STATUS_LOGGEDIN, &WorldSession::HandleRemoveGlyph }, + /*0x48B*/ { "CMSG_DUMP_OBJECTS", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x48C*/ { "SMSG_DUMP_OBJECTS_DATA", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x48D*/ { "CMSG_DISMISS_CRITTER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x48E*/ { "SMSG_NOTIFY_DEST_LOC_SPELL_CAST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x48F*/ { "CMSG_AUCTION_LIST_PENDING_SALES", STATUS_LOGGEDIN, &WorldSession::HandleAuctionListPendingSales }, + /*0x490*/ { "SMSG_AUCTION_LIST_PENDING_SALES", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x491*/ { "SMSG_MODIFY_COOLDOWN", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x492*/ { "SMSG_PET_UPDATE_COMBO_POINTS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x493*/ { "CMSG_ENABLETAXI", STATUS_LOGGEDIN, &WorldSession::HandleTaxiQueryAvailableNodes }, + /*0x494*/ { "SMSG_PRE_RESURRECT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x495*/ { "SMSG_AURA_UPDATE_ALL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x496*/ { "SMSG_AURA_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x497*/ { "CMSG_FLOOD_GRACE_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x498*/ { "SMSG_SERVER_FIRST_ACHIEVEMENT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x499*/ { "SMSG_PET_LEARNED_SPELL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x49A*/ { "SMSG_PET_REMOVED_SPELL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x49B*/ { "CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x49C*/ { "CMSG_HEARTH_AND_RESURRECT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x49D*/ { "SMSG_ON_CANCEL_EXPECTED_RIDE_VEHICLE_AURA", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x49E*/ { "SMSG_CRITERIA_DELETED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x49F*/ { "SMSG_ACHIEVEMENT_DELETED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x4A0*/ { "CMSG_SERVER_INFO_QUERY", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x4A1*/ { "SMSG_SERVER_INFO_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x4A2*/ { "CMSG_CHECK_LOGIN_CRITERIA", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x4A3*/ { "SMSG_SERVER_BUCK_DATA_START", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x4A4*/ { "CMSG_QUERY_VEHICLE_STATUS", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x4A5*/ { "SMSG_PET_GUIDS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x4A6*/ { "SMSG_CLIENTCACHE_VERSION", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x4A7*/ { "UMSG_UNKNOWN_1191", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x4A8*/ { "UMSG_UNKNOWN_1192", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x4A9*/ { "UMSG_UNKNOWN_1193", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x4AA*/ { "UMSG_UNKNOWN_1194", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x4AB*/ { "UMSG_UNKNOWN_1195", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x4AC*/ { "UMSG_UNKNOWN_1196", STATUS_NEVER, &WorldSession::Handle_NULL }, }; diff --git a/src/game/Opcodes.h b/src/game/Opcodes.h index 12f9c9459fa..d57cfd62852 100644 --- a/src/game/Opcodes.h +++ b/src/game/Opcodes.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -49,8 +49,8 @@ enum Opcodes CMSG_ZONE_MAP = 0x00A, SMSG_ZONE_MAP = 0x00B, CMSG_DEBUG_CHANGECELLZONE = 0x00C, - CMSG_EMBLAZON_TABARD_OBSOLETE = 0x00D, - CMSG_UNEMBLAZON_TABARD_OBSOLETE = 0x00E, + CMSG_MOVE_CHARACTER_CHEAT = 0x00D, + SMSG_MOVE_CHARACTER_CHEAT = 0x00E, CMSG_RECHARGE = 0x00F, CMSG_LEARN_SPELL = 0x010, CMSG_CREATEMONSTER = 0x011, @@ -66,7 +66,7 @@ enum Opcodes SMSG_FORCEACTIONSHOW = 0x01B, CMSG_PETGODMODE = 0x01C, SMSG_PETGODMODE = 0x01D, - SMSG_DEBUGINFOSPELLMISS_OBSOLETE = 0x01E, + SMSG_REFER_A_FRIEND_EXPIRED = 0x01E, CMSG_WEATHER_SPEED_CHEAT = 0x01F, CMSG_UNDRESSPLAYER = 0x020, CMSG_BEASTMASTER = 0x021, @@ -86,7 +86,7 @@ enum Opcodes SMSG_DEBUG_AISTATE = 0x02F, CMSG_DISABLE_PVP_CHEAT = 0x030, CMSG_ADVANCE_SPAWN_TIME = 0x031, - CMSG_PVP_PORT_OBSOLETE = 0x032, + SMSG_DESTRUCTIBLE_BUILDING_DAMAGE = 0x032, CMSG_AUTH_SRP6_BEGIN = 0x033, CMSG_AUTH_SRP6_PROOF = 0x034, CMSG_AUTH_SRP6_RECODE = 0x035, @@ -214,7 +214,7 @@ enum Opcodes SMSG_READ_ITEM_FAILED = 0x0AF, SMSG_ITEM_COOLDOWN = 0x0B0, CMSG_GAMEOBJ_USE = 0x0B1, - CMSG_GAMEOBJ_CHAIR_USE_OBSOLETE = 0x0B2, + CMSG_DESTROY_ITEMS = 0x0B2, SMSG_GAMEOBJECT_CUSTOM_ANIM = 0x0B3, CMSG_AREATRIGGER = 0x0B4, MSG_MOVE_START_FORWARD = 0x0B5, @@ -347,7 +347,7 @@ enum Opcodes SMSG_SPELL_COOLDOWN = 0x134, SMSG_COOLDOWN_EVENT = 0x135, CMSG_CANCEL_AURA = 0x136, - SMSG_UPDATE_AURA_DURATION = 0x137, + SMSG_UPDATE_AURA_DURATION_OBSOLETE = 0x137, SMSG_PET_CAST_FAILED = 0x138, MSG_CHANNEL_START = 0x139, MSG_CHANNEL_UPDATE = 0x13A, @@ -371,10 +371,10 @@ enum Opcodes SMSG_DAMAGE_DONE_OBSOLETE = 0x14C, SMSG_DAMAGE_TAKEN_OBSOLETE = 0x14D, SMSG_CANCEL_COMBAT = 0x14E, - SMSG_PLAYER_COMBAT_XP_GAIN_OBSOLETE = 0x14F, + SMSG_SPELLBREAKLOG = 0x14F, SMSG_SPELLHEALLOG = 0x150, SMSG_SPELLENERGIZELOG = 0x151, - CMSG_SHEATHE_OBSOLETE = 0x152, + SMSG_BREAK_TARGET = 0x152, CMSG_SAVE_PLAYER = 0x153, CMSG_SETDEATHBINDPOINT = 0x154, SMSG_BINDPOINTUPDATE = 0x155, @@ -578,7 +578,7 @@ enum Opcodes SMSG_GMTICKET_SYSTEMSTATUS = 0x21B, CMSG_SPIRIT_HEALER_ACTIVATE = 0x21C, CMSG_SET_STAT_CHEAT = 0x21D, - SMSG_SET_REST_START = 0x21E, + SMSG_SET_REST_START_OBSOLETE = 0x21E, CMSG_SKILL_BUY_STEP = 0x21F, CMSG_SKILL_BUY_RANK = 0x220, CMSG_XP_CHEAT = 0x221, @@ -733,8 +733,8 @@ enum Opcodes SMSG_SCRIPT_MESSAGE = 0x2B6, SMSG_DUEL_COUNTDOWN = 0x2B7, SMSG_AREA_TRIGGER_MESSAGE = 0x2B8, - CMSG_TOGGLE_HELM = 0x2B9, - CMSG_TOGGLE_CLOAK = 0x2BA, + CMSG_SHOWING_HELM = 0x2B9, + CMSG_SHOWING_CLOAK = 0x2BA, SMSG_MEETINGSTONE_JOINFAILED = 0x2BB, SMSG_PLAYER_SKINNED = 0x2BC, SMSG_DURABILITY_DAMAGE_DEATH = 0x2BD, @@ -967,10 +967,10 @@ enum Opcodes SMSG_VOICE_SESSION_ADJUST_PRIORITY = 0x3A0, CMSG_VOICE_SET_TALKER_MUTED_REQUEST = 0x3A1, SMSG_VOICE_SET_TALKER_MUTED = 0x3A2, - SMSG_INIT_EXTRA_AURA_INFO = 0x3A3, - SMSG_SET_EXTRA_AURA_INFO = 0x3A4, - SMSG_SET_EXTRA_AURA_INFO_NEED_UPDATE = 0x3A5, - SMSG_CLEAR_EXTRA_AURA_INFO = 0x3A6, + SMSG_INIT_EXTRA_AURA_INFO_OBSOLETE = 0x3A3, + SMSG_SET_EXTRA_AURA_INFO_OBSOLETE = 0x3A4, + SMSG_SET_EXTRA_AURA_INFO_NEED_UPDATE_OBSOLETE = 0x3A5, + SMSG_CLEAR_EXTRA_AURA_INFO_OBSOLETE = 0x3A6, MSG_MOVE_START_DESCEND = 0x3A7, CMSG_IGNORE_REQUIREMENTS_CHEAT = 0x3A8, SMSG_IGNORE_REQUIREMENTS_CHEAT = 0x3A9, @@ -980,127 +980,262 @@ enum Opcodes MSG_MOVE_UPDATE_CAN_FLY = 0x3AD, MSG_RAID_READY_CHECK_CONFIRM = 0x3AE, CMSG_VOICE_SESSION_ENABLE = 0x3AF, - SMSG_VOICE_PARENTAL_CONTROLS = 0x3B0, - CMSG_GM_WHISPER = 0x3B1, - SMSG_GM_MESSAGECHAT = 0x3B2, - MSG_GM_GEARRATING = 0x3B3, - CMSG_COMMENTATOR_ENABLE = 0x3B4, - SMSG_COMMENTATOR_STATE_CHANGED = 0x3B5, - CMSG_COMMENTATOR_GET_MAP_INFO = 0x3B6, - SMSG_COMMENTATOR_MAP_INFO = 0x3B7, - CMSG_COMMENTATOR_GET_PLAYER_INFO = 0x3B8, - SMSG_COMMENTATOR_GET_PLAYER_INFO = 0x3B9, - SMSG_COMMENTATOR_PLAYER_INFO = 0x3BA, - CMSG_COMMENTATOR_ENTER_INSTANCE = 0x3BB, - CMSG_COMMENTATOR_EXIT_INSTANCE = 0x3BC, - CMSG_COMMENTATOR_INSTANCE_COMMAND = 0x3BD, - SMSG_CLEAR_TARGET = 0x3BE, - CMSG_BOT_DETECTED = 0x3BF, - SMSG_CROSSED_INEBRIATION_THRESHOLD = 0x3C0, - CMSG_CHEAT_PLAYER_LOGIN = 0x3C1, - CMSG_CHEAT_PLAYER_LOOKUP = 0x3C2, - SMSG_CHEAT_PLAYER_LOOKUP = 0x3C3, - SMSG_KICK_REASON = 0x3C4, - MSG_RAID_READY_CHECK_FINISHED = 0x3C5, - CMSG_COMPLAIN = 0x3C6, - SMSG_COMPLAIN_RESULT = 0x3C7, - SMSG_FEATURE_SYSTEM_STATUS = 0x3C8, - CMSG_GM_SHOW_COMPLAINTS = 0x3C9, - CMSG_GM_UNSQUELCH = 0x3CA, - CMSG_CHANNEL_SILENCE_VOICE = 0x3CB, - CMSG_CHANNEL_SILENCE_ALL = 0x3CC, - CMSG_CHANNEL_UNSILENCE_VOICE = 0x3CD, - CMSG_CHANNEL_UNSILENCE_ALL = 0x3CE, - CMSG_TARGET_CAST = 0x3CF, - CMSG_TARGET_SCRIPT_CAST = 0x3D0, - CMSG_CHANNEL_DISPLAY_LIST = 0x3D1, - CMSG_SET_ACTIVE_VOICE_CHANNEL = 0x3D2, - CMSG_GET_CHANNEL_MEMBER_COUNT = 0x3D3, - SMSG_CHANNEL_MEMBER_COUNT = 0x3D4, - CMSG_CHANNEL_VOICE_ON = 0x3D5, - CMSG_CHANNEL_VOICE_OFF = 0x3D6, - CMSG_DEBUG_LIST_TARGETS = 0x3D7, - SMSG_DEBUG_LIST_TARGETS = 0x3D8, - SMSG_AVAILABLE_VOICE_CHANNEL = 0x3D9, - CMSG_ADD_VOICE_IGNORE = 0x3DA, - CMSG_DEL_VOICE_IGNORE = 0x3DB, - CMSG_PARTY_SILENCE = 0x3DC, - CMSG_PARTY_UNSILENCE = 0x3DD, - MSG_NOTIFY_PARTY_SQUELCH = 0x3DE, - SMSG_COMSAT_RECONNECT_TRY = 0x3DF, - SMSG_COMSAT_DISCONNECT = 0x3E0, - SMSG_COMSAT_CONNECT_FAIL = 0x3E1, - SMSG_VOICE_CHAT_STATUS = 0x3E2, - CMSG_REPORT_PVP_AFK = 0x3E3, - CMSG_REPORT_PVP_AFK_RESULT = 0x3E4, - CMSG_GUILD_BANKER_ACTIVATE = 0x3E5, - CMSG_GUILD_BANK_QUERY_TAB = 0x3E6, - SMSG_GUILD_BANK_LIST = 0x3E7, - CMSG_GUILD_BANK_SWAP_ITEMS = 0x3E8, - CMSG_GUILD_BANK_BUY_TAB = 0x3E9, - CMSG_GUILD_BANK_UPDATE_TAB = 0x3EA, - CMSG_GUILD_BANK_DEPOSIT_MONEY = 0x3EB, - CMSG_GUILD_BANK_WITHDRAW_MONEY = 0x3EC, - MSG_GUILD_BANK_LOG_QUERY = 0x3ED, - CMSG_SET_CHANNEL_WATCH = 0x3EE, - SMSG_USERLIST_ADD = 0x3EF, - SMSG_USERLIST_REMOVE = 0x3F0, - SMSG_USERLIST_UPDATE = 0x3F1, - CMSG_CLEAR_CHANNEL_WATCH = 0x3F2, - SMSG_INSPECT_TALENT = 0x3F3, - SMSG_GOGOGO_OBSOLETE = 0x3F4, - SMSG_ECHO_PARTY_SQUELCH = 0x3F5, - CMSG_SET_TITLE_SUFFIX = 0x3F6, - CMSG_SPELLCLICK = 0x3F7, - SMSG_LOOT_LIST = 0x3F8, - CMSG_GM_CHARACTER_RESTORE = 0x3F9, - CMSG_GM_CHARACTER_SAVE = 0x3FA, - SMSG_VOICESESSION_FULL = 0x3FB, - MSG_GUILD_PERMISSIONS = 0x3FC, - MSG_GUILD_BANK_MONEY_WITHDRAWN = 0x3FD, - MSG_GUILD_EVENT_LOG_QUERY = 0x3FE, - CMSG_MAELSTROM_RENAME_GUILD = 0x3FF, - CMSG_GET_MIRRORIMAGE_DATA = 0x400, - SMSG_MIRRORIMAGE_DATA = 0x401, - SMSG_FORCE_DISPLAY_UPDATE = 0x402, - SMSG_SPELL_CHANCE_RESIST_PUSHBACK = 0x403, - CMSG_IGNORE_DIMINISHING_RETURNS_CHEAT = 0x404, - SMSG_IGNORE_DIMINISHING_RETURNS_CHEAT = 0x405, - CMSG_KEEP_ALIVE = 0x406, - SMSG_RAID_READY_CHECK_ERROR = 0x407, - CMSG_OPT_OUT_OF_LOOT = 0x408, - MSG_QUERY_GUILD_BANK_TEXT = 0x409, - CMSG_SET_GUILD_BANK_TEXT = 0x40A, - CMSG_SET_GRANTABLE_LEVELS = 0x40B, - CMSG_GRANT_LEVEL = 0x40C, - CMSG_REFER_A_FRIEND = 0x40D, - MSG_GM_CHANGE_ARENA_RATING = 0x40E, - CMSG_DECLINE_CHANNEL_INVITE = 0x40F, - CMSG_GROUPACTION_THROTTLED = 0x410, - SMSG_OVERRIDE_LIGHT = 0x411, - SMSG_TOTEM_CREATED = 0x412, - CMSG_TOTEM_DESTROYED = 0x413, - CMSG_EXPIRE_RAID_INSTANCE = 0x414, - CMSG_NO_SPELL_VARIANCE = 0x415, - CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY = 0x416, - SMSG_QUESTGIVER_STATUS_MULTIPLE = 0x417, - CMSG_SET_PLAYER_DECLINED_NAMES = 0x418, - SMSG_SET_PLAYER_DECLINED_NAMES_RESULT = 0x419, - CMSG_QUERY_SERVER_BUCK_DATA = 0x41A, - CMSG_CLEAR_SERVER_BUCK_DATA = 0x41B, - SMSG_SERVER_BUCK_DATA = 0x41C, - SMSG_SEND_UNLEARN_SPELLS = 0x41D, - SMSG_PROPOSE_LEVEL_GRANT = 0x41E, - CMSG_ACCEPT_LEVEL_GRANT = 0x41F, - SMSG_REFER_A_FRIEND_FAILURE = 0x420, - SMSG_SPLINE_MOVE_SET_FLYING = 0x421, - SMSG_SPLINE_MOVE_UNSET_FLYING = 0x422, - SMSG_SUMMON_CANCEL = 0x423 + SMSG_VOICE_SESSION_ENABLE = 0x3B0, + SMSG_VOICE_PARENTAL_CONTROLS = 0x3B1, + CMSG_GM_WHISPER = 0x3B2, + SMSG_GM_MESSAGECHAT = 0x3B3, + MSG_GM_GEARRATING = 0x3B4, + CMSG_COMMENTATOR_ENABLE = 0x3B5, + SMSG_COMMENTATOR_STATE_CHANGED = 0x3B6, + CMSG_COMMENTATOR_GET_MAP_INFO = 0x3B7, + SMSG_COMMENTATOR_MAP_INFO = 0x3B8, + CMSG_COMMENTATOR_GET_PLAYER_INFO = 0x3B9, + SMSG_COMMENTATOR_GET_PLAYER_INFO = 0x3BA, + SMSG_COMMENTATOR_PLAYER_INFO = 0x3BB, + CMSG_COMMENTATOR_ENTER_INSTANCE = 0x3BC, + CMSG_COMMENTATOR_EXIT_INSTANCE = 0x3BD, + CMSG_COMMENTATOR_INSTANCE_COMMAND = 0x3BE, + SMSG_CLEAR_TARGET = 0x3BF, + CMSG_BOT_DETECTED = 0x3C0, + SMSG_CROSSED_INEBRIATION_THRESHOLD = 0x3C1, + CMSG_CHEAT_PLAYER_LOGIN = 0x3C2, + CMSG_CHEAT_PLAYER_LOOKUP = 0x3C3, + SMSG_CHEAT_PLAYER_LOOKUP = 0x3C4, + SMSG_KICK_REASON = 0x3C5, + MSG_RAID_READY_CHECK_FINISHED = 0x3C6, + CMSG_COMPLAIN = 0x3C7, + SMSG_COMPLAIN_RESULT = 0x3C8, + SMSG_FEATURE_SYSTEM_STATUS = 0x3C9, + CMSG_GM_SHOW_COMPLAINTS = 0x3CA, + CMSG_GM_UNSQUELCH = 0x3CB, + CMSG_CHANNEL_SILENCE_VOICE = 0x3CC, + CMSG_CHANNEL_SILENCE_ALL = 0x3CD, + CMSG_CHANNEL_UNSILENCE_VOICE = 0x3CE, + CMSG_CHANNEL_UNSILENCE_ALL = 0x3CF, + CMSG_TARGET_CAST = 0x3D0, + CMSG_TARGET_SCRIPT_CAST = 0x3D1, + CMSG_CHANNEL_DISPLAY_LIST = 0x3D2, + CMSG_SET_ACTIVE_VOICE_CHANNEL = 0x3D3, + CMSG_GET_CHANNEL_MEMBER_COUNT = 0x3D4, + SMSG_CHANNEL_MEMBER_COUNT = 0x3D5, + CMSG_CHANNEL_VOICE_ON = 0x3D6, + CMSG_CHANNEL_VOICE_OFF = 0x3D7, + CMSG_DEBUG_LIST_TARGETS = 0x3D8, + SMSG_DEBUG_LIST_TARGETS = 0x3D9, + SMSG_AVAILABLE_VOICE_CHANNEL = 0x3DA, + CMSG_ADD_VOICE_IGNORE = 0x3DB, + CMSG_DEL_VOICE_IGNORE = 0x3DC, + CMSG_PARTY_SILENCE = 0x3DD, + CMSG_PARTY_UNSILENCE = 0x3DE, + MSG_NOTIFY_PARTY_SQUELCH = 0x3DF, + SMSG_COMSAT_RECONNECT_TRY = 0x3E0, + SMSG_COMSAT_DISCONNECT = 0x3E1, + SMSG_COMSAT_CONNECT_FAIL = 0x3E2, + SMSG_VOICE_CHAT_STATUS = 0x3E3, + CMSG_REPORT_PVP_AFK = 0x3E4, + CMSG_REPORT_PVP_AFK_RESULT = 0x3E5, + CMSG_GUILD_BANKER_ACTIVATE = 0x3E6, + CMSG_GUILD_BANK_QUERY_TAB = 0x3E7, + SMSG_GUILD_BANK_LIST = 0x3E8, + CMSG_GUILD_BANK_SWAP_ITEMS = 0x3E9, + CMSG_GUILD_BANK_BUY_TAB = 0x3EA, + CMSG_GUILD_BANK_UPDATE_TAB = 0x3EB, + CMSG_GUILD_BANK_DEPOSIT_MONEY = 0x3EC, + CMSG_GUILD_BANK_WITHDRAW_MONEY = 0x3ED, + MSG_GUILD_BANK_LOG_QUERY = 0x3EE, + CMSG_SET_CHANNEL_WATCH = 0x3EF, + SMSG_USERLIST_ADD = 0x3F0, + SMSG_USERLIST_REMOVE = 0x3F1, + SMSG_USERLIST_UPDATE = 0x3F2, + CMSG_CLEAR_CHANNEL_WATCH = 0x3F3, + SMSG_INSPECT_TALENT = 0x3F4, + SMSG_GOGOGO_OBSOLETE = 0x3F5, + SMSG_ECHO_PARTY_SQUELCH = 0x3F6, + CMSG_SET_TITLE_SUFFIX = 0x3F7, + CMSG_SPELLCLICK = 0x3F8, + SMSG_LOOT_LIST = 0x3F9, + CMSG_GM_CHARACTER_RESTORE = 0x3FA, + CMSG_GM_CHARACTER_SAVE = 0x3FB, + SMSG_VOICESESSION_FULL = 0x3FC, + MSG_GUILD_PERMISSIONS = 0x3FD, + MSG_GUILD_BANK_MONEY_WITHDRAWN = 0x3FE, + MSG_GUILD_EVENT_LOG_QUERY = 0x3FF, + CMSG_MAELSTROM_RENAME_GUILD = 0x400, + CMSG_GET_MIRRORIMAGE_DATA = 0x401, + SMSG_MIRRORIMAGE_DATA = 0x402, + SMSG_FORCE_DISPLAY_UPDATE = 0x403, + SMSG_SPELL_CHANCE_RESIST_PUSHBACK = 0x404, + CMSG_IGNORE_DIMINISHING_RETURNS_CHEAT = 0x405, + SMSG_IGNORE_DIMINISHING_RETURNS_CHEAT = 0x406, + CMSG_KEEP_ALIVE = 0x407, + SMSG_RAID_READY_CHECK_ERROR = 0x408, + CMSG_OPT_OUT_OF_LOOT = 0x409, + MSG_QUERY_GUILD_BANK_TEXT = 0x40A, + CMSG_SET_GUILD_BANK_TEXT = 0x40B, + CMSG_SET_GRANTABLE_LEVELS = 0x40C, + CMSG_GRANT_LEVEL = 0x40D, + CMSG_REFER_A_FRIEND = 0x40E, + MSG_GM_CHANGE_ARENA_RATING = 0x40F, + CMSG_DECLINE_CHANNEL_INVITE = 0x410, + CMSG_GROUPACTION_THROTTLED = 0x411, + SMSG_OVERRIDE_LIGHT = 0x412, + SMSG_TOTEM_CREATED = 0x413, + CMSG_TOTEM_DESTROYED = 0x414, + CMSG_EXPIRE_RAID_INSTANCE = 0x415, + CMSG_NO_SPELL_VARIANCE = 0x416, + CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY = 0x417, + SMSG_QUESTGIVER_STATUS_MULTIPLE = 0x418, + CMSG_SET_PLAYER_DECLINED_NAMES = 0x419, + SMSG_SET_PLAYER_DECLINED_NAMES_RESULT = 0x41A, + CMSG_QUERY_SERVER_BUCK_DATA = 0x41B, + CMSG_CLEAR_SERVER_BUCK_DATA = 0x41C, + SMSG_SERVER_BUCK_DATA = 0x41D, + SMSG_SEND_UNLEARN_SPELLS = 0x41E, + SMSG_PROPOSE_LEVEL_GRANT = 0x41F, + CMSG_ACCEPT_LEVEL_GRANT = 0x420, + SMSG_REFER_A_FRIEND_FAILURE = 0x421, + SMSG_SPLINE_MOVE_SET_FLYING = 0x422, + SMSG_SPLINE_MOVE_UNSET_FLYING = 0x423, + SMSG_SUMMON_CANCEL = 0x424, + CMSG_CHANGE_PERSONAL_ARENA_RATING = 0x425, + CMSG_ALTER_APPEARANCE = 0x426, + SMSG_ENABLE_BARBER_SHOP = 0x427, + SMSG_BARBER_SHOP_RESULT = 0x428, + CMSG_CALENDAR_GET_CALENDAR = 0x429, + CMSG_CALENDAR_GET_EVENT = 0x42A, + CMSG_CALENDAR_GUILD_FILTER = 0x42B, + CMSG_CALENDAR_ARENA_TEAM = 0x42C, + CMSG_CALENDAR_ADD_EVENT = 0x42D, + CMSG_CALENDAR_UPDATE_EVENT = 0x42E, + CMSG_CALENDAR_REMOVE_EVENT = 0x42F, + CMSG_CALENDAR_COPY_EVENT = 0x430, + CMSG_CALENDAR_EVENT_INVITE = 0x431, + CMSG_CALENDAR_EVENT_RSVP = 0x432, + CMSG_CALENDAR_EVENT_REMOVE_INVITE = 0x433, + CMSG_CALENDAR_EVENT_STATUS = 0x434, + CMSG_CALENDAR_EVENT_MODERATOR_STATUS = 0x435, + SMSG_CALENDAR_SEND_CALENDAR = 0x436, + SMSG_CALENDAR_SEND_EVENT = 0x437, + SMSG_CALENDAR_FILTER_GUILD = 0x438, + SMSG_CALENDAR_ARENA_TEAM = 0x439, + SMSG_CALENDAR_EVENT_INVITE = 0x43A, + SMSG_CALENDAR_EVENT_INVITE_REMOVED = 0x43B, + SMSG_CALENDAR_EVENT_STATUS = 0x43C, + SMSG_CALENDAR_COMMAND_RESULT = 0x43D, + SMSG_CALENDAR_RAID_LOCKOUT_ADDED = 0x43E, + SMSG_CALENDAR_RAID_LOCKOUT_REMOVED = 0x43F, + SMSG_CALENDAR_EVENT_INVITE_ALERT = 0x440, + SMSG_CALENDAR_EVENT_INVITE_REMOVED_ALERT = 0x441, + SMSG_CALENDAR_EVENT_INVITE_STATUS_ALERT = 0x442, + SMSG_CALENDAR_EVENT_REMOVED_ALERT = 0x443, + SMSG_CALENDAR_EVENT_UPDATED_ALERT = 0x444, + SMSG_CALENDAR_EVENT_MODERATOR_STATUS_ALERT = 0x445, + CMSG_CALENDAR_COMPLAIN = 0x446, + CMSG_CALENDAR_GET_NUM_PENDING = 0x447, + SMSG_CALENDAR_SEND_NUM_PENDING = 0x448, + CMSG_SAVE_DANCE = 0x449, + SMSG_NOTIFY_DANCE = 0x44A, + CMSG_PLAY_DANCE = 0x44B, + SMSG_PLAY_DANCE = 0x44C, + CMSG_LOAD_DANCES = 0x44D, + CMSG_STOP_DANCE = 0x44E, + SMSG_STOP_DANCE = 0x44F, + CMSG_SYNC_DANCE = 0x450, + CMSG_DANCE_QUERY = 0x451, + SMSG_DANCE_QUERY_RESPONSE = 0x452, + SMSG_INVALIDATE_DANCE = 0x453, + CMSG_DELETE_DANCE = 0x454, + SMSG_LEARNED_DANCE_MOVES = 0x455, + CMSG_LEARN_DANCE_MOVE = 0x456, + CMSG_UNLEARN_DANCE_MOVE = 0x457, + CMSG_SET_RUNE_COUNT = 0x458, + CMSG_SET_RUNE_COOLDOWN = 0x459, + MSG_MOVE_SET_PITCH_RATE_CHEAT = 0x45A, + MSG_MOVE_SET_PITCH_RATE = 0x45B, + SMSG_FORCE_PITCH_RATE_CHANGE = 0x45C, + CMSG_FORCE_PITCH_RATE_CHANGE_ACK = 0x45D, + SMSG_SPLINE_SET_PITCH_RATE = 0x45E, + SMSG_MOVE_ABANDON_TRANSPORT = 0x45F, + MSG_MOVE_ABANDON_TRANSPORT = 0x460, + CMSG_MOVE_ABANDON_TRANSPORT_ACK = 0x461, + CMSG_UPDATE_MISSILE_TRAJECTORY = 0x462, + SMSG_UPDATE_ACCOUNT_DATA_COMPLETE = 0x463, + SMSG_TRIGGER_MOVIE = 0x464, + CMSG_COMPLETE_MOVIE = 0x465, + CMSG_SET_GLYPH_SLOT = 0x466, + CMSG_SET_GLYPH = 0x467, + SMSG_ACHIEVEMENT_EARNED = 0x468, + SMSG_DYNAMIC_DROP_ROLL_RESULT = 0x469, + SMSG_CRITERIA_UPDATE = 0x46A, + CMSG_QUERY_INSPECT_ACHIEVEMENTS = 0x46B, + SMSG_RESPOND_INSPECT_ACHIEVEMENTS = 0x46C, + CMSG_DISMISS_CONTROLLED_VEHICLE = 0x46D, + CMSG_COMPLETE_ACHIEVEMENT_CHEAT = 0x46E, + SMSG_QUESTUPDATE_ADD_PVP_KILL = 0x46F, + CMSG_SET_CRITERIA_CHEAT = 0x470, + SMSG_GROUP_SWAP_FAILED = 0x471, + CMSG_UNITANIMTIER_CHEAT = 0x472, + CMSG_CHAR_CUSTOMIZE = 0x473, + SMSG_CHAR_CUSTOMIZE = 0x474, + SMSG_PET_RENAMEABLE = 0x475, + CMSG_REQUEST_VEHICLE_EXIT = 0x476, + CMSG_REQUEST_VEHICLE_PREV_SEAT = 0x477, + CMSG_REQUEST_VEHICLE_NEXT_SEAT = 0x478, + CMSG_REQUEST_VEHICLE_SWITCH_SEAT = 0x479, + CMSG_PET_LEARN_TALENT = 0x47A, + CMSG_PET_UNLEARN_TALENTS = 0x47B, + SMSG_SET_PHASE_SHIFT = 0x47C, + SMSG_ALL_ACHIEVEMENT_DATA = 0x47D, + CMSG_FORCE_SAY_CHEAT = 0x47E, + SMSG_HEALTH_UPDATE = 0x47F, + SMSG_POWER_UPDATE = 0x480, + CMSG_GAMEOBJ_REPORT_USE = 0x481, + SMSG_HIGHEST_THREAT_UPDATE = 0x482, + SMSG_THREAT_UPDATE = 0x483, + SMSG_THREAT_REMOVE = 0x484, + SMSG_THREAT_CLEAR = 0x485, + SMSG_CONVERT_RUNE = 0x486, + SMSG_RESYNC_RUNES = 0x487, + SMSG_ADD_RUNE_POWER = 0x488, + CMSG_START_QUEST = 0x489, + CMSG_REMOVE_GLYPH = 0x48A, + CMSG_DUMP_OBJECTS = 0x48B, + SMSG_DUMP_OBJECTS_DATA = 0x48C, + CMSG_DISMISS_CRITTER = 0x48D, + SMSG_NOTIFY_DEST_LOC_SPELL_CAST = 0x48E, + CMSG_AUCTION_LIST_PENDING_SALES = 0x48F, + SMSG_AUCTION_LIST_PENDING_SALES = 0x490, + SMSG_MODIFY_COOLDOWN = 0x491, + SMSG_PET_UPDATE_COMBO_POINTS = 0x492, + CMSG_ENABLETAXI = 0x493, + SMSG_PRE_RESURRECT = 0x494, + SMSG_AURA_UPDATE_ALL = 0x495, + SMSG_AURA_UPDATE = 0x496, + CMSG_FLOOD_GRACE_CHEAT = 0x497, + SMSG_SERVER_FIRST_ACHIEVEMENT = 0x498, + SMSG_PET_LEARNED_SPELL = 0x499, + SMSG_PET_REMOVED_SPELL = 0x49A, + CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE = 0x49B, + CMSG_HEARTH_AND_RESURRECT = 0x49C, + SMSG_ON_CANCEL_EXPECTED_RIDE_VEHICLE_AURA = 0x49D, + SMSG_CRITERIA_DELETED = 0x49E, + SMSG_ACHIEVEMENT_DELETED = 0x49F, + CMSG_SERVER_INFO_QUERY = 0x4A0, + SMSG_SERVER_INFO_RESPONSE = 0x4A1, + CMSG_CHECK_LOGIN_CRITERIA = 0x4A2, + SMSG_SERVER_BUCK_DATA_START = 0x4A3, + CMSG_QUERY_VEHICLE_STATUS = 0x4A4, + SMSG_PET_GUIDS = 0x4A5, + SMSG_CLIENTCACHE_VERSION = 0x4A6, + UMSG_UNKNOWN_1191 = 0x4A7, + UMSG_UNKNOWN_1192 = 0x4A8, + UMSG_UNKNOWN_1193 = 0x4A9, + UMSG_UNKNOWN_1194 = 0x4AA, + UMSG_UNKNOWN_1195 = 0x4AB, + UMSG_UNKNOWN_1196 = 0x4AC, + NUM_MSG_TYPES = 0x4AD }; -// Don't forget to change this value and add opcode name to Opcodes.cpp when you add new opcode! -#define NUM_MSG_TYPES 0x424 - /// Player state enum SessionStatus { diff --git a/src/game/OutdoorPvP.cpp b/src/game/OutdoorPvP.cpp index 99b1c9a55f0..d702ea6d047 100644 --- a/src/game/OutdoorPvP.cpp +++ b/src/game/OutdoorPvP.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -95,7 +95,7 @@ bool OutdoorPvPObjective::AddObject(uint32 type, uint32 entry, uint32 map, float if(!pMap) return true; GameObject * go = new GameObject; - if(!go->Create(guid,entry, pMap,x,y,z,o,rotation0,rotation1,rotation2,rotation3,100,1)) + if(!go->Create(guid,entry, pMap,PHASEMASK_NORMAL,x,y,z,o,rotation0,rotation1,rotation2,rotation3,100,1)) { sLog.outError("Gameobject template %u not found in database.", entry); delete go; @@ -156,20 +156,19 @@ bool OutdoorPvPObjective::AddCreature(uint32 type, uint32 entry, uint32 teamval, if(!pMap) return true; Creature* pCreature = new Creature; - if (!pCreature->Create(guid, pMap, entry, teamval)) + if (!pCreature->Create(guid, pMap, PHASEMASK_NORMAL, entry, teamval)) { sLog.outError("Can't create creature entry: %u",entry); delete pCreature; - return true; + return false; } - pCreature->AIM_Initialize(); - pCreature->Relocate(x, y, z, o); if(!pCreature->IsPositionValid()) { sLog.outError("ERROR: Creature (guidlow %d, entry %d) not added to opvp. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY()); + delete pCreature; return false; } @@ -256,7 +255,7 @@ bool OutdoorPvPObjective::AddCapturePoint(uint32 entry, uint32 map, float x, flo return true; // add GO... GameObject * go = new GameObject; - if(!go->Create(guid,entry, pMap,x,y,z,o,rotation0,rotation1,rotation2,rotation3,100,1)) + if(!go->Create(guid,entry, pMap,PHASEMASK_NORMAL,x,y,z,o,rotation0,rotation1,rotation2,rotation3,100,1)) { sLog.outError("Gameobject template %u not found in database.", entry); delete go; @@ -269,20 +268,20 @@ bool OutdoorPvPObjective::AddCapturePoint(uint32 entry, uint32 map, float x, flo } // add creature... Creature* pCreature = new Creature; - if (!pCreature->Create(creature_guid, pMap, OPVP_TRIGGER_CREATURE_ENTRY, 0)) + if (!pCreature->Create(creature_guid, pMap, PHASEMASK_NORMAL, OPVP_TRIGGER_CREATURE_ENTRY, 0)) { sLog.outError("Can't create creature entry: %u",entry); delete pCreature; + return false; } else { - pCreature->AIM_Initialize(); - pCreature->Relocate(x, y, z, o); if(!pCreature->IsPositionValid()) { sLog.outError("ERROR: Creature (guidlow %d, entry %d) not added to opvp. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY()); + delete pCreature; return false; } @@ -315,8 +314,8 @@ bool OutdoorPvPObjective::DelCreature(uint32 type) // explicit removal from map // beats me why this is needed, but with the recent removal "cleanup" some creatures stay in the map if "properly" deleted // so this is a big fat workaround, if AddObjectToRemoveList and DoDelayedMovesAndRemoves worked correctly, this wouldn't be needed - if(Map * map = MapManager::Instance().FindMap(cr->GetMapId())) - map->Remove(cr,false); + //if(Map * map = MapManager::Instance().FindMap(cr->GetMapId())) + // map->Remove(cr,false); // delete respawn time for this creature WorldDatabase.PExecute("DELETE FROM creature_respawn WHERE guid = '%u'", guid); cr->AddObjectToRemoveList(); @@ -373,8 +372,8 @@ bool OutdoorPvPObjective::DelCapturePoint() // explicit removal from map // beats me why this is needed, but with the recent removal "cleanup" some creatures stay in the map if "properly" deleted // so this is a big fat workaround, if AddObjectToRemoveList and DoDelayedMovesAndRemoves worked correctly, this wouldn't be needed - if(Map * map = MapManager::Instance().FindMap(cr->GetMapId())) - map->Remove(cr,false); + //if(Map * map = MapManager::Instance().FindMap(cr->GetMapId())) + // map->Remove(cr,false); // delete respawn time for this creature WorldDatabase.PExecute("DELETE FROM creature_respawn WHERE guid = '%u'", guid); cr->AddObjectToRemoveList(); diff --git a/src/game/OutdoorPvP.h b/src/game/OutdoorPvP.h index e7bd21059a7..c1c39c1ff65 100644 --- a/src/game/OutdoorPvP.h +++ b/src/game/OutdoorPvP.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/OutdoorPvPEP.cpp b/src/game/OutdoorPvPEP.cpp index 41747dba100..f0ff85574c0 100644 --- a/src/game/OutdoorPvPEP.cpp +++ b/src/game/OutdoorPvPEP.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/OutdoorPvPEP.h b/src/game/OutdoorPvPEP.h index ae242a1da16..08d31de5cf5 100644 --- a/src/game/OutdoorPvPEP.h +++ b/src/game/OutdoorPvPEP.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,7 +21,7 @@ #include "OutdoorPvP.h" -#include "Database/DBCStructure.h" +#include "DBCStructure.h" const uint32 EP_AllianceBuffs[4] = {11413, 11414, 11415, 1386}; diff --git a/src/game/OutdoorPvPHP.cpp b/src/game/OutdoorPvPHP.cpp index b31f79a6288..6058b82c102 100644 --- a/src/game/OutdoorPvPHP.cpp +++ b/src/game/OutdoorPvPHP.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/OutdoorPvPHP.h b/src/game/OutdoorPvPHP.h index da41761d572..a538f82b216 100644 --- a/src/game/OutdoorPvPHP.h +++ b/src/game/OutdoorPvPHP.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/OutdoorPvPMgr.cpp b/src/game/OutdoorPvPMgr.cpp index 5c53bf4eaba..2050a6c11f7 100644 --- a/src/game/OutdoorPvPMgr.cpp +++ b/src/game/OutdoorPvPMgr.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -143,7 +143,6 @@ void OutdoorPvPMgr::HandlePlayerEnterZone(Player *plr, uint32 zoneid) } // add possibly beneficial buffs to plr for zone itr->second->HandlePlayerEnterZone(plr, zoneid); - plr->SendInitWorldStates(); sLog.outDebug("Player %u entered outdoorpvp id %u",plr->GetGUIDLow(), itr->second->GetTypeId()); } diff --git a/src/game/OutdoorPvPMgr.h b/src/game/OutdoorPvPMgr.h index 7426876b42b..28b01c586b7 100644 --- a/src/game/OutdoorPvPMgr.h +++ b/src/game/OutdoorPvPMgr.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/OutdoorPvPNA.cpp b/src/game/OutdoorPvPNA.cpp index 9b59347dd2d..9030337f2cd 100644 --- a/src/game/OutdoorPvPNA.cpp +++ b/src/game/OutdoorPvPNA.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/OutdoorPvPNA.h b/src/game/OutdoorPvPNA.h index b6be2270cda..92e0b9dcbbb 100644 --- a/src/game/OutdoorPvPNA.h +++ b/src/game/OutdoorPvPNA.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/OutdoorPvPObjectiveAI.cpp b/src/game/OutdoorPvPObjectiveAI.cpp index 7c4fd5e6de8..233fffd6b2f 100644 --- a/src/game/OutdoorPvPObjectiveAI.cpp +++ b/src/game/OutdoorPvPObjectiveAI.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/OutdoorPvPObjectiveAI.h b/src/game/OutdoorPvPObjectiveAI.h index d21655795e9..dffd8885243 100644 --- a/src/game/OutdoorPvPObjectiveAI.h +++ b/src/game/OutdoorPvPObjectiveAI.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,7 +26,7 @@ class Creature; class TRINITY_DLL_DECL OutdoorPvPObjectiveAI : public NullCreatureAI { public: - OutdoorPvPObjectiveAI(Creature *c); + explicit OutdoorPvPObjectiveAI(Creature *c); void MoveInLineOfSight(Unit *); diff --git a/src/game/OutdoorPvPSI.cpp b/src/game/OutdoorPvPSI.cpp index 378543da48e..2bd35ef5a54 100644 --- a/src/game/OutdoorPvPSI.cpp +++ b/src/game/OutdoorPvPSI.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -129,7 +129,7 @@ bool OutdoorPvPSI::HandleAreaTrigger(Player *plr, uint32 trigger) switch(trigger) { case SI_AREATRIGGER_A: - if(plr->GetTeam() == ALLIANCE && plr->HasAura(SI_SILITHYST_FLAG,0)) + if(plr->GetTeam() == ALLIANCE && plr->HasAura(SI_SILITHYST_FLAG)) { // remove aura plr->RemoveAurasDueToSpell(SI_SILITHYST_FLAG); @@ -148,13 +148,13 @@ bool OutdoorPvPSI::HandleAreaTrigger(Player *plr, uint32 trigger) // add 19 honor plr->RewardHonor(NULL,1,19); // add 20 cenarion circle repu - plr->ModifyFactionReputation(609,20); + plr->GetReputationMgr().ModifyReputation(sFactionStore.LookupEntry(609),20); // complete quest plr->KilledMonster(SI_TURNIN_QUEST_CM_A,0); } return true; case SI_AREATRIGGER_H: - if(plr->GetTeam() == HORDE && plr->HasAura(SI_SILITHYST_FLAG,0)) + if(plr->GetTeam() == HORDE && plr->HasAura(SI_SILITHYST_FLAG)) { // remove aura plr->RemoveAurasDueToSpell(SI_SILITHYST_FLAG); @@ -173,7 +173,7 @@ bool OutdoorPvPSI::HandleAreaTrigger(Player *plr, uint32 trigger) // add 19 honor plr->RewardHonor(NULL,1,19); // add 20 cenarion circle repu - plr->ModifyFactionReputation(609,20); + plr->GetReputationMgr().ModifyReputation(sFactionStore.LookupEntry(609),20); // complete quest plr->KilledMonster(SI_TURNIN_QUEST_CM_H,0); } @@ -202,7 +202,7 @@ bool OutdoorPvPSI::HandleDropFlag(Player *plr, uint32 spellId) Map * map = MapManager::Instance().GetMap(plr->GetMapId(), plr); if(!map) return true; - if(!go->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT),SI_SILITHYST_MOUND, map,plr->GetPositionX(),plr->GetPositionY(),plr->GetPositionZ(),plr->GetOrientation(),0,0,0,0,100,1)) + if(!go->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT),SI_SILITHYST_MOUND, map, plr->GetPhaseMask(), plr->GetPositionX(),plr->GetPositionY(),plr->GetPositionZ(),plr->GetOrientation(),0,0,0,0,100,1)) { delete go; } @@ -228,7 +228,7 @@ bool OutdoorPvPSI::HandleDropFlag(Player *plr, uint32 spellId) Map * map = MapManager::Instance().GetMap(plr->GetMapId(), plr); if(!map) return true; - if(!go->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT),SI_SILITHYST_MOUND, map ,plr->GetPositionX(),plr->GetPositionY(),plr->GetPositionZ(),plr->GetOrientation(),0,0,0,0,100,1)) + if(!go->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT),SI_SILITHYST_MOUND, map, plr->GetPhaseMask() ,plr->GetPositionX(),plr->GetPositionY(),plr->GetPositionZ(),plr->GetOrientation(),0,0,0,0,100,1)) { delete go; } diff --git a/src/game/OutdoorPvPSI.h b/src/game/OutdoorPvPSI.h index 4b0b5da357e..1f2331b4251 100644 --- a/src/game/OutdoorPvPSI.h +++ b/src/game/OutdoorPvPSI.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/OutdoorPvPTF.cpp b/src/game/OutdoorPvPTF.cpp index b94bc9246ee..01b224cffa9 100644 --- a/src/game/OutdoorPvPTF.cpp +++ b/src/game/OutdoorPvPTF.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/OutdoorPvPZM.cpp b/src/game/OutdoorPvPZM.cpp index b220f6a778f..29997eec920 100644 --- a/src/game/OutdoorPvPZM.cpp +++ b/src/game/OutdoorPvPZM.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/OutdoorPvPZM.h b/src/game/OutdoorPvPZM.h index b9d5ae7bc2e..405c0133b5c 100644 --- a/src/game/OutdoorPvPZM.h +++ b/src/game/OutdoorPvPZM.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/Path.h b/src/game/Path.h index 2b793aed892..2fd6723b01c 100644 --- a/src/game/Path.h +++ b/src/game/Path.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/Pet.cpp b/src/game/Pet.cpp index 6b5ff6c1543..d1345b7079d 100644 --- a/src/game/Pet.cpp +++ b/src/game/Pet.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,12 +21,10 @@ #include "Common.h" #include "Database/DatabaseEnv.h" #include "Log.h" -#include "WorldSession.h" #include "WorldPacket.h" #include "ObjectMgr.h" #include "SpellMgr.h" #include "Pet.h" -#include "MapManager.h" #include "Formulas.h" #include "SpellAuras.h" #include "CreatureAI.h" @@ -41,71 +39,30 @@ char const* petTypeSuffix[MAX_PET_TYPE] = "'s Companion" // MINI_PET }; -//numbers represent minutes * 100 while happy (you get 100 loyalty points per min while happy) -uint32 const LevelUpLoyalty[6] = -{ - 5500, - 11500, - 17000, - 23500, - 31000, - 39500, -}; +#define PET_XP_FACTOR 0.1f -uint32 const LevelStartLoyalty[6] = +Pet::Pet(Player *owner, PetType type) : Guardian(NULL, owner), +m_petType(type), m_removed(false), m_happinessTimer(7500), m_duration(0), +m_resetTalentsCost(0), m_resetTalentsTime(0), m_usedTalentCount(0), m_auraRaidUpdateMask(0), m_loading(false), +m_declinedname(NULL), m_owner(owner) { - 2000, - 4500, - 7000, - 10000, - 13500, - 17500, -}; - -Pet::Pet(PetType type) : Creature() -{ - m_isPet = true; + m_summonMask |= SUMMON_MASK_PET; m_name = "Pet"; - m_petType = type; - - m_removed = false; m_regenTimer = 4000; - m_happinessTimer = 7500; - m_loyaltyTimer = 12000; - m_duration = 0; - m_bonusdamage = 0; - m_loyaltyPoints = 0; - m_TrainingPoints = 0; - m_resetTalentsCost = 0; - m_resetTalentsTime = 0; - - m_auraUpdateMask = 0; - - // pets always have a charminfo, even if they are not actually charmed - CharmInfo* charmInfo = InitCharmInfo(); - - if(type == MINI_PET || type == POSSESSED_PET) // always passive + if(type == POSSESSED_PET) // always passive SetReactState(REACT_PASSIVE); - else if(type == GUARDIAN_PET) // always aggressive - SetReactState(REACT_AGGRESSIVE); - - m_spells.clear(); - m_Auras.clear(); - m_CreatureSpellCooldowns.clear(); - m_CreatureCategoryCooldowns.clear(); - m_autospells.clear(); - m_declinedname = NULL; - //m_isActive = true; } Pet::~Pet() { if(m_uint32Values) // only for fully created Object { + if(GetOwnerGUID()) + sLog.outCrash("Pet %u is deconstructed but it still has owner guid", GetEntry()); + for (PetSpellMap::iterator i = m_spells.begin(); i != m_spells.end(); ++i) delete i->second; - ObjectAccessor::Instance().RemoveObject(this); } delete m_declinedname; @@ -115,9 +72,11 @@ void Pet::AddToWorld() { ///- Register the pet for guid lookup if(!IsInWorld()) - { + { + ///- Register the pet for guid lookup ObjectAccessor::Instance().AddObject(this); Unit::AddToWorld(); + AIM_Initialize(); } } @@ -126,32 +85,42 @@ void Pet::RemoveFromWorld() ///- Remove the pet from the accessor if(IsInWorld()) { - ObjectAccessor::Instance().RemoveObject(this); ///- Don't call the function for Creature, normal mobs + totems go in a different storage Unit::RemoveFromWorld(); + ObjectAccessor::Instance().RemoveObject(this); } } -bool Pet::LoadPetFromDB( Unit* owner, uint32 petentry, uint32 petnumber, bool current ) +bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool current ) { + m_loading = true; + uint32 ownerid = owner->GetGUIDLow(); QueryResult *result; - if(petnumber) - // known petnumber entry 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 - result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND id = '%u'",ownerid, petnumber); - else if(current) - // current pet (slot 0) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 - result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND slot = '0'",ownerid ); - else if(petentry) + if (petnumber) + // known petnumber entry 0 1 2(?) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 + result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, talentpoints, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType " + "FROM character_pet WHERE owner = '%u' AND id = '%u'", + ownerid, petnumber); + else if (current) + // current pet (slot 0) 0 1 2(?) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 + result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, talentpoints, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType " + "FROM character_pet WHERE owner = '%u' AND slot = '%u'", + ownerid, PET_SAVE_AS_CURRENT ); + else if (petentry) // known petentry entry (unique for summoned pet, but non unique for hunter pet (only from current or not stabled pets) - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 - result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND entry = '%u' AND (slot = '0' OR slot = '3') ",ownerid, petentry ); + // 0 1 2(?) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 + result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, talentpoints, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType " + "FROM character_pet WHERE owner = '%u' AND entry = '%u' AND (slot = '%u' OR slot > '%u') ", + ownerid, petentry,PET_SAVE_AS_CURRENT,PET_SAVE_LAST_STABLE_SLOT); else // any current or other non-stabled pet (for hunter "call pet") - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 - result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND (slot = '0' OR slot = '3') ",ownerid); + // 0 1 2(?) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 + result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, talentpoints, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType " + "FROM character_pet WHERE owner = '%u' AND (slot = '%u' OR slot > '%u') ", + ownerid,PET_SAVE_AS_CURRENT,PET_SAVE_LAST_STABLE_SLOT); if(!result) return false; @@ -160,28 +129,36 @@ bool Pet::LoadPetFromDB( Unit* owner, uint32 petentry, uint32 petnumber, bool cu // update for case of current pet "slot = 0" petentry = fields[1].GetUInt32(); - if(!petentry) + if (!petentry) { delete result; return false; } - uint32 summon_spell_id = fields[21].GetUInt32(); + uint32 summon_spell_id = fields[19].GetUInt32(); SpellEntry const* spellInfo = sSpellStore.LookupEntry(summon_spell_id); bool is_temporary_summoned = spellInfo && GetSpellDuration(spellInfo) > 0; // check temporary summoned pets like mage water elemental - if(current && is_temporary_summoned) + if (current && is_temporary_summoned) + { + delete result; + return false; + } + + uint32 pet_number = fields[0].GetUInt32(); + + if (current && owner->IsPetNeedBeTemporaryUnsummoned()) { + owner->SetTemporaryUnsummonedPetNumber(pet_number); delete result; return false; } Map *map = owner->GetMap(); uint32 guid = objmgr.GenerateLowGuid(HIGHGUID_PET); - uint32 pet_number = fields[0].GetUInt32(); - if(!Create(guid, map, petentry, pet_number)) + if (!Create(guid, map, owner->GetPhaseMask(), petentry, pet_number)) { delete result; return false; @@ -189,97 +166,115 @@ bool Pet::LoadPetFromDB( Unit* owner, uint32 petentry, uint32 petnumber, bool cu float px, py, pz; owner->GetClosePoint(px, py, pz, GetObjectSize(), PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); - Relocate(px, py, pz, owner->GetOrientation()); - if(!IsPositionValid()) + if (!IsPositionValid()) { - sLog.outError("ERROR: Pet (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %f Y: %f)", + sLog.outError("Pet (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %f Y: %f)", GetGUIDLow(), GetEntry(), GetPositionX(), GetPositionY()); delete result; return false; } - setPetType(PetType(fields[22].GetUInt8())); - SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,owner->getFaction()); + setPetType(PetType(fields[20].GetUInt8())); + SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, owner->getFaction()); SetUInt32Value(UNIT_CREATED_BY_SPELL, summon_spell_id); CreatureInfo const *cinfo = GetCreatureInfo(); - if(cinfo->type == CREATURE_TYPE_CRITTER) + if (cinfo->type == CREATURE_TYPE_CRITTER) { - AIM_Initialize(); map->Add((Creature*)this); delete result; return true; } - if(getPetType()==HUNTER_PET || (getPetType()==SUMMON_PET && cinfo->type == CREATURE_TYPE_DEMON && owner->getClass() == CLASS_WARLOCK)) + + if (getPetType() == HUNTER_PET || (getPetType() == SUMMON_PET && cinfo->type == CREATURE_TYPE_DEMON && owner->getClass() == CLASS_WARLOCK)) m_charmInfo->SetPetNumber(pet_number, true); else m_charmInfo->SetPetNumber(pet_number, false); - SetUInt64Value(UNIT_FIELD_SUMMONEDBY, owner->GetGUID()); + + // set current pet as current + // 0=current + // 1..MAX_PET_STABLES in stable slot + // PET_SAVE_NOT_IN_SLOT(100) = not stable slot (summoning)) + if(fields[8].GetUInt32() != 0) + { + CharacterDatabase.BeginTransaction(); + CharacterDatabase.PExecute("UPDATE character_pet SET slot = '%u' WHERE owner = '%u' AND slot = '%u' AND id <> '%u'", + PET_SAVE_NOT_IN_SLOT, ownerid, PET_SAVE_AS_CURRENT, m_charmInfo->GetPetNumber()); + CharacterDatabase.PExecute("UPDATE character_pet SET slot = '%u' WHERE owner = '%u' AND id = '%u'", + PET_SAVE_AS_CURRENT, ownerid, m_charmInfo->GetPetNumber()); + CharacterDatabase.CommitTransaction(); + } + SetDisplayId(fields[3].GetUInt32()); SetNativeDisplayId(fields[3].GetUInt32()); - uint32 petlevel=fields[4].GetUInt32(); - SetUInt32Value(UNIT_NPC_FLAGS , 0); - SetName(fields[11].GetString()); + uint32 petlevel = fields[4].GetUInt32(); + SetUInt32Value(UNIT_NPC_FLAGS, 0); + SetName(fields[9].GetString()); - switch(getPetType()) + switch (getPetType()) { - case SUMMON_PET: petlevel=owner->getLevel(); - SetUInt32Value(UNIT_FIELD_BYTES_0,2048); + SetUInt32Value(UNIT_FIELD_BYTES_0, 2048); SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); // this enables popup window (pet dismiss, cancel) break; case HUNTER_PET: SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100); - SetByteValue(UNIT_FIELD_BYTES_1, 1, fields[8].GetUInt32()); - SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE ); - SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5 ); - - if(fields[12].GetBool()) - SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_NOT_ALLOWED); - else - SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_ALLOWED); + SetByteValue(UNIT_FIELD_BYTES_1, 1, fields[7].GetUInt32()); + SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE); + SetByteValue(UNIT_FIELD_BYTES_2, 2, fields[10].GetBool() ? UNIT_RENAME_NOT_ALLOWED : UNIT_RENAME_ALLOWED); SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); // this enables popup window (pet abandon, cancel) - SetTP(fields[9].GetInt32()); - SetMaxPower(POWER_HAPPINESS,GetCreatePowers(POWER_HAPPINESS)); - SetPower( POWER_HAPPINESS,fields[15].GetUInt32()); + SetMaxPower(POWER_HAPPINESS, GetCreatePowers(POWER_HAPPINESS)); + SetPower(POWER_HAPPINESS, fields[13].GetUInt32()); setPowerType(POWER_FOCUS); break; default: - sLog.outError("Pet have incorrect type (%u) for pet loading.",getPetType()); + sLog.outError("Pet have incorrect type (%u) for pet loading.", getPetType()); } - InitStatsForLevel( petlevel); + SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL)); SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, fields[5].GetUInt32()); - SetUInt64Value(UNIT_FIELD_CREATEDBY, owner->GetGUID()); + SetCreatorGUID(owner->GetGUID()); SetReactState( ReactStates( fields[6].GetUInt8() )); - m_loyaltyPoints = fields[7].GetInt32(); - - uint32 savedhealth = fields[13].GetUInt32(); - uint32 savedmana = fields[14].GetUInt32(); - // set current pet as current - if(fields[10].GetUInt32() != 0) + SetCanModifyStats(true); + InitStatsForLevel(petlevel); + uint32 savedhealth = fields[11].GetUInt32(); + uint32 savedmana = fields[12].GetUInt32(); + if(getPetType() == SUMMON_PET && !current) //all (?) summon pets come with full health when called, but not when they are current { - CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("UPDATE character_pet SET slot = '3' WHERE owner = '%u' AND slot = '0' AND id <> '%u'",ownerid, m_charmInfo->GetPetNumber()); - CharacterDatabase.PExecute("UPDATE character_pet SET slot = '0' WHERE owner = '%u' AND id = '%u'",ownerid, m_charmInfo->GetPetNumber()); - CharacterDatabase.CommitTransaction(); + SetHealth(GetMaxHealth()); + SetPower(POWER_MANA, GetMaxPower(POWER_MANA)); } + else + { + SetHealth(savedhealth > GetMaxHealth() ? GetMaxHealth() : savedhealth); + SetPower(POWER_MANA, savedmana > GetMaxPower(POWER_MANA) ? GetMaxPower(POWER_MANA) : savedmana); + } + + map->Add((Creature*)this); + owner->SetGuardian(this, true); - if(!is_temporary_summoned) + m_resetTalentsCost = fields[17].GetUInt32(); + m_resetTalentsTime = fields[18].GetUInt64(); + learnLevelupSpells(); + LearnPetPassives(); + _LoadSpells(); + _LoadSpellCooldowns(); + + if (!is_temporary_summoned) { // permanent controlled pets store state in DB - Tokens tokens = StrSplit(fields[16].GetString(), " "); + Tokens tokens = StrSplit(fields[14].GetString(), " "); - if(tokens.size() != 20) + if (tokens.size() != 20) { delete result; return false; @@ -295,12 +290,18 @@ bool Pet::LoadPetFromDB( Unit* owner, uint32 petentry, uint32 petnumber, bool cu // patch for old data where some spells have ACT_DECIDE but should have ACT_CAST // so overwrite old state - SpellEntry const *spellInfo = sSpellStore.LookupEntry(m_charmInfo->GetActionBarEntry(index)->SpellOrAction); - if (spellInfo && spellInfo->AttributesEx & SPELL_ATTR_EX_UNAUTOCASTABLE_BY_PET) m_charmInfo->GetActionBarEntry(index)->Type = ACT_CAST; + if(SpellEntry const *spellInfo = sSpellStore.LookupEntry(m_charmInfo->GetActionBarEntry(index)->SpellOrAction)) + { + if (spellInfo && spellInfo->AttributesEx & SPELL_ATTR_EX_UNAUTOCASTABLE_BY_PET) + m_charmInfo->GetActionBarEntry(index)->Type = ACT_DISABLED; + + if(m_charmInfo->GetActionBarEntry(index)->Type == ACT_ENABLED) + ToggleAutocast(spellInfo->Id, true); + } } //init teach spells - tokens = StrSplit(fields[17].GetString(), " "); + tokens = StrSplit(fields[15].GetString(), " "); for (iter = tokens.begin(), index = 0; index < 4; ++iter, ++index) { uint32 tmp = atol((*iter).c_str()); @@ -314,56 +315,22 @@ bool Pet::LoadPetFromDB( Unit* owner, uint32 petentry, uint32 petnumber, bool cu } } - // since last save (in seconds) - uint32 timediff = (time(NULL) - fields[18].GetUInt32()); - - delete result; - //load spells/cooldowns/auras - SetCanModifyStats(true); + // Spells should be loaded after pet is added to map, because in CheckCast is check on it + // since last save (in seconds) + uint32 timediff = (time(NULL) - fields[16].GetUInt32()); _LoadAuras(timediff); + CastPetAuras(current); - //init AB - if(is_temporary_summoned) - { - // Temporary summoned pets always have initial spell list at load - InitPetCreateSpells(); - } - else - { - LearnPetPassives(); - CastPetAuras(current); - } - - if(getPetType() == SUMMON_PET && !current) //all (?) summon pets come with full health when called, but not when they are current - { - SetHealth(GetMaxHealth()); - SetPower(POWER_MANA, GetMaxPower(POWER_MANA)); - } - else - { - SetHealth(savedhealth > GetMaxHealth() ? GetMaxHealth() : savedhealth); - SetPower(POWER_MANA, savedmana > GetMaxPower(POWER_MANA) ? GetMaxPower(POWER_MANA) : savedmana); - } - - AIM_Initialize(); - map->Add((Creature*)this); - - // Spells should be loaded after pet is added to map, because in CanCast is check on it - _LoadSpells(); - _LoadSpellCooldowns(); - - owner->SetPet(this); // in DB stored only full controlled creature + delete result; sLog.outDebug("New Pet has guid %u", GetGUIDLow()); - if(owner->GetTypeId() == TYPEID_PLAYER) - { - ((Player*)owner)->PetSpellInitialize(); - if(((Player*)owner)->GetGroup()) - ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_PET); - } + owner->PetSpellInitialize(); + + if(owner->GetGroup()) + owner->SetGroupUpdateFlag(GROUP_UPDATE_PET); - if(owner->GetTypeId() == TYPEID_PLAYER && getPetType() == HUNTER_PET) + if(getPetType() == HUNTER_PET) { result = CharacterDatabase.PQuery("SELECT genitive, dative, accusative, instrumental, prepositional FROM character_pet_declinedname WHERE owner = '%u' AND id = '%u'", owner->GetGUIDLow(), GetCharmInfo()->GetPetNumber()); @@ -381,120 +348,118 @@ bool Pet::LoadPetFromDB( Unit* owner, uint32 petentry, uint32 petnumber, bool cu } } + m_loading = false; return true; } void Pet::SavePetToDB(PetSaveMode mode) { - if(!GetEntry()) + if (!GetEntry()) return; // save only fully controlled creature - if(!isControlled()) + if (!isControlled()) + return; + + // not save not player pets + if(!IS_PLAYER_GUID(GetOwnerGUID())) return; + Player* pOwner = (Player*)GetOwner(); + if (!pOwner) + return; + + // not save pet as current if another pet temporary unsummoned + if (mode == PET_SAVE_AS_CURRENT && pOwner->GetTemporaryUnsummonedPetNumber() && + pOwner->GetTemporaryUnsummonedPetNumber() != m_charmInfo->GetPetNumber()) + { + // pet will lost anyway at restore temporary unsummoned + if(getPetType()==HUNTER_PET) + return; + + // for warlock case + mode = PET_SAVE_NOT_IN_SLOT; + } + uint32 curhealth = GetHealth(); uint32 curmana = GetPower(POWER_MANA); - switch(mode) + // stable and not in slot saves + if(mode > PET_SAVE_AS_CURRENT) { - case PET_SAVE_IN_STABLE_SLOT_1: - case PET_SAVE_IN_STABLE_SLOT_2: - case PET_SAVE_NOT_IN_SLOT: - { - RemoveAllAuras(); - - //only alive hunter pets get auras saved, the others don't - if(!(getPetType() == HUNTER_PET && isAlive())) - m_Auras.clear(); - } - default: - break; + RemoveAllAuras(); } _SaveSpells(); _SaveSpellCooldowns(); _SaveAuras(); - switch(mode) + // current/stable/not_in_slot + if(mode >= PET_SAVE_AS_CURRENT) { - case PET_SAVE_AS_CURRENT: - case PET_SAVE_IN_STABLE_SLOT_1: - case PET_SAVE_IN_STABLE_SLOT_2: - case PET_SAVE_NOT_IN_SLOT: + uint32 owner = GUID_LOPART(GetOwnerGUID()); + std::string name = m_name; + CharacterDatabase.escape_string(name); + CharacterDatabase.BeginTransaction(); + // remove current data + CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND id = '%u'", owner,m_charmInfo->GetPetNumber() ); + + // prevent duplicate using slot (except PET_SAVE_NOT_IN_SLOT) + if(mode <= PET_SAVE_LAST_STABLE_SLOT) + CharacterDatabase.PExecute("UPDATE character_pet SET slot = '%u' WHERE owner = '%u' AND slot = '%u'", + PET_SAVE_NOT_IN_SLOT, owner, uint32(mode) ); + + // prevent existence another hunter pet in PET_SAVE_AS_CURRENT and PET_SAVE_NOT_IN_SLOT + if(getPetType()==HUNTER_PET && (mode==PET_SAVE_AS_CURRENT||mode > PET_SAVE_LAST_STABLE_SLOT)) + CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND (slot = '%u' OR slot > '%u')", + owner,PET_SAVE_AS_CURRENT,PET_SAVE_LAST_STABLE_SLOT); + // save pet + std::ostringstream ss; + ss << "INSERT INTO character_pet ( id, entry, owner, modelid, level, exp, Reactstate, talentpoints, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType) " + << "VALUES (" + << m_charmInfo->GetPetNumber() << ", " + << GetEntry() << ", " + << owner << ", " + << GetNativeDisplayId() << ", " + << getLevel() << ", " + << GetUInt32Value(UNIT_FIELD_PETEXPERIENCE) << ", " + << uint32(GetReactState()) << ", " + << uint32(GetFreeTalentPoints()) << ", " + << uint32(mode) << ", '" + << name.c_str() << "', " + << uint32((GetByteValue(UNIT_FIELD_BYTES_2, 2) == UNIT_RENAME_ALLOWED)?0:1) << ", " + << (curhealth<1?1:curhealth) << ", " + << curmana << ", " + << GetPower(POWER_HAPPINESS) << ", '"; + + for(uint32 i = 0; i < 10; i++) + ss << uint32(m_charmInfo->GetActionBarEntry(i)->Type) << " " << uint32(m_charmInfo->GetActionBarEntry(i)->SpellOrAction) << " "; + ss << "', '"; + + //save spells the pet can teach to it's Master { - uint32 loyalty =1; - if(getPetType()!=HUNTER_PET) - loyalty = GetLoyaltyLevel(); - - uint32 owner = GUID_LOPART(GetOwnerGUID()); - std::string name = m_name; - CharacterDatabase.escape_string(name); - CharacterDatabase.BeginTransaction(); - // remove current data - CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND id = '%u'", owner,m_charmInfo->GetPetNumber() ); - - // prevent duplicate using slot (except PET_SAVE_NOT_IN_SLOT) - if(mode!=PET_SAVE_NOT_IN_SLOT) - CharacterDatabase.PExecute("UPDATE character_pet SET slot = 3 WHERE owner = '%u' AND slot = '%u'", owner, uint32(mode) ); - - // prevent existence another hunter pet in PET_SAVE_AS_CURRENT and PET_SAVE_NOT_IN_SLOT - if(getPetType()==HUNTER_PET && (mode==PET_SAVE_AS_CURRENT||mode==PET_SAVE_NOT_IN_SLOT)) - CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND (slot = '0' OR slot = '3')", owner ); - // save pet - std::ostringstream ss; - ss << "INSERT INTO character_pet ( id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata,TeachSpelldata,savetime,resettalents_cost,resettalents_time,CreatedBySpell,PetType) " - << "VALUES (" - << m_charmInfo->GetPetNumber() << ", " - << GetEntry() << ", " - << owner << ", " - << GetNativeDisplayId() << ", " - << getLevel() << ", " - << GetUInt32Value(UNIT_FIELD_PETEXPERIENCE) << ", " - << uint32(GetReactState()) << ", " - << m_loyaltyPoints << ", " - << GetLoyaltyLevel() << ", " - << m_TrainingPoints << ", " - << uint32(mode) << ", '" - << name.c_str() << "', " - << uint32((GetByteValue(UNIT_FIELD_BYTES_2, 2) == UNIT_RENAME_ALLOWED)?0:1) << ", " - << (curhealth<1?1:curhealth) << ", " - << curmana << ", " - << GetPower(POWER_HAPPINESS) << ", '"; - - for(uint32 i = 0; i < 10; i++) - ss << uint32(m_charmInfo->GetActionBarEntry(i)->Type) << " " << uint32(m_charmInfo->GetActionBarEntry(i)->SpellOrAction) << " "; - ss << "', '"; - - //save spells the pet can teach to it's Master - { - int i = 0; - for(TeachSpellMap::iterator itr = m_teachspells.begin(); i < 4 && itr != m_teachspells.end(); ++i, ++itr) - ss << itr->first << " " << itr->second << " "; - for(; i < 4; ++i) - ss << uint32(0) << " " << uint32(0) << " "; - } - - ss << "', " - << time(NULL) << ", " - << uint32(m_resetTalentsCost) << ", " - << uint64(m_resetTalentsTime) << ", " - << GetUInt32Value(UNIT_CREATED_BY_SPELL) << ", " - << uint32(getPetType()) << ")"; + int i = 0; + for(TeachSpellMap::iterator itr = m_teachspells.begin(); i < 4 && itr != m_teachspells.end(); ++i, ++itr) + ss << itr->first << " " << itr->second << " "; + for(; i < 4; ++i) + ss << uint32(0) << " " << uint32(0) << " "; + } - CharacterDatabase.Execute( ss.str().c_str() ); + ss << "', " + << time(NULL) << ", " + << uint32(m_resetTalentsCost) << ", " + << uint64(m_resetTalentsTime) << ", " + << GetUInt32Value(UNIT_CREATED_BY_SPELL) << ", " + << uint32(getPetType()) << ")"; - CharacterDatabase.CommitTransaction(); - break; - } - case PET_SAVE_AS_DELETED: - { - RemoveAllAuras(); - DeleteFromDB(m_charmInfo->GetPetNumber()); - break; - } - default: - sLog.outError("Unknown pet save/remove mode: %d",mode); + CharacterDatabase.Execute( ss.str().c_str() ); + CharacterDatabase.CommitTransaction(); + } + // delete + else + { + RemoveAllAuras(); + DeleteFromDB(m_charmInfo->GetPetNumber()); } } @@ -512,11 +477,7 @@ void Pet::setDeathState(DeathState s) // overwrite virtual Creature::setDeathState(s); if(getDeathState()==CORPSE) { - //remove summoned pet (no corpse) - if(getPetType()==SUMMON_PET) - Remove(PET_SAVE_NOT_IN_SLOT); - // other will despawn at corpse desppawning (Pet::Update code) - else + if(getPetType() == HUNTER_PET) { // pet corpse non lootable and non skinnable SetUInt32Value( UNIT_DYNAMIC_FLAGS, 0x00 ); @@ -527,12 +488,12 @@ void Pet::setDeathState(DeathState s) // overwrite virtual if(!mapEntry || (mapEntry->map_type != MAP_ARENA && mapEntry->map_type != MAP_BATTLEGROUND)) ModifyPower(POWER_HAPPINESS, -HAPPINESS_LEVEL_SIZE); - SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE); + SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); } } else if(getDeathState()==ALIVE) { - RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE); + RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); CastPetAuras(true); } } @@ -546,9 +507,8 @@ void Pet::Update(uint32 diff) { case CORPSE: { - if( m_deathTimer <= diff ) + if(getPetType() != HUNTER_PET || m_deathTimer <= diff ) { - assert(getPetType()!=SUMMON_PET && "Must be already removed."); Remove(PET_SAVE_NOT_IN_SLOT); //hunters' pets never get removed because of death, NEVER! return; } @@ -557,8 +517,9 @@ void Pet::Update(uint32 diff) case ALIVE: { // unsummon pet that lost owner - Unit* owner = GetOwner(); + Player* owner = GetOwner(); if(!owner || (!IsWithinDistInMap(owner, OWNER_MAX_DISTANCE) && !isPossessed()) || isControlled() && !owner->GetPetGUID()) + //if(!owner || (!IsWithinDistInMap(owner, OWNER_MAX_DISTANCE) && (owner->GetCharmGUID() && (owner->GetCharmGUID() != GetGUID()))) || (isControlled() && !owner->GetPetGUID())) { Remove(PET_SAVE_NOT_IN_SLOT, true); return; @@ -568,6 +529,7 @@ void Pet::Update(uint32 diff) { if( owner->GetPetGUID() != GetGUID() ) { + sLog.outError("Pet %u is not pet of owner %u, removed", GetEntry(), m_owner->GetName()); Remove(getPetType()==HUNTER_PET?PET_SAVE_AS_DELETED:PET_SAVE_NOT_IN_SLOT); return; } @@ -604,14 +566,6 @@ void Pet::Update(uint32 diff) else m_happinessTimer -= diff; - if(m_loyaltyTimer <= diff) - { - TickLoyaltyChange(); - m_loyaltyTimer = 12000; - } - else - m_loyaltyTimer -= diff; - break; } default: @@ -630,10 +584,10 @@ void Pet::RegenerateFocus() float addvalue = 24 * sWorld.getRate(RATE_POWER_FOCUS); - AuraList const& ModPowerRegenPCTAuras = GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT); - for(AuraList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i) - if ((*i)->GetModifier()->m_miscvalue == POWER_FOCUS) - addvalue *= ((*i)->GetModifierValue() + 100) / 100.0f; + AuraEffectList const& ModPowerRegenPCTAuras = GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT); + for(AuraEffectList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i) + if ((*i)->GetMiscValue() == POWER_FOCUS) + addvalue *= ((*i)->GetAmount() + 100) / 100.0f; ModifyPower(POWER_FOCUS, (int32)addvalue); } @@ -643,83 +597,12 @@ void Pet::LooseHappiness() uint32 curValue = GetPower(POWER_HAPPINESS); if (curValue <= 0) return; - int32 addvalue = (140 >> GetLoyaltyLevel()) * 125; //value is 70/35/17/8/4 (per min) * 1000 / 8 (timer 7.5 secs) + int32 addvalue = 670; //value is 70/35/17/8/4 (per min) * 1000 / 8 (timer 7.5 secs) if(isInCombat()) //we know in combat happiness fades faster, multiplier guess addvalue = int32(addvalue * 1.5); ModifyPower(POWER_HAPPINESS, -addvalue); } -void Pet::ModifyLoyalty(int32 addvalue) -{ - uint32 loyaltylevel = GetLoyaltyLevel(); - - if(addvalue > 0) //only gain influenced, not loss - addvalue = int32((float)addvalue * sWorld.getRate(RATE_LOYALTY)); - - if(loyaltylevel >= BEST_FRIEND && (addvalue + m_loyaltyPoints) > int32(GetMaxLoyaltyPoints(loyaltylevel))) - return; - - m_loyaltyPoints += addvalue; - - if(m_loyaltyPoints < 0) - { - if(loyaltylevel > REBELLIOUS) - { - //level down - --loyaltylevel; - SetLoyaltyLevel(LoyaltyLevel(loyaltylevel)); - m_loyaltyPoints = GetStartLoyaltyPoints(loyaltylevel); - SetTP(m_TrainingPoints - int32(getLevel())); - } - else - { - m_loyaltyPoints = 0; - Unit* owner = GetOwner(); - if(owner && owner->GetTypeId() == TYPEID_PLAYER) - { - WorldPacket data(SMSG_PET_BROKEN, 0); - ((Player*)owner)->GetSession()->SendPacket(&data); - - //run away - ((Player*)owner)->RemovePet(this,PET_SAVE_AS_DELETED); - } - } - } - //level up - else if(m_loyaltyPoints > int32(GetMaxLoyaltyPoints(loyaltylevel))) - { - ++loyaltylevel; - SetLoyaltyLevel(LoyaltyLevel(loyaltylevel)); - m_loyaltyPoints = GetStartLoyaltyPoints(loyaltylevel); - SetTP(m_TrainingPoints + getLevel()); - } -} - -void Pet::TickLoyaltyChange() -{ - int32 addvalue; - - switch(GetHappinessState()) - { - case HAPPY: addvalue = 20; break; - case CONTENT: addvalue = 10; break; - case UNHAPPY: addvalue = -20; break; - default: - return; - } - ModifyLoyalty(addvalue); -} - -void Pet::KillLoyaltyBonus(uint32 level) -{ - if(level > 100) - return; - - //at lower levels gain is faster | the lower loyalty the more loyalty is gained - uint32 bonus = uint32(((100 - level) / 10) + (6 - GetLoyaltyLevel())); - ModifyLoyalty(bonus); -} - HappinessState Pet::GetHappinessState() { if(GetPower(POWER_HAPPINESS) < HAPPINESS_LEVEL_SIZE) @@ -730,11 +613,6 @@ HappinessState Pet::GetHappinessState() return CONTENT; } -void Pet::SetLoyaltyLevel(LoyaltyLevel level) -{ - SetByteValue(UNIT_FIELD_BYTES_1, 1, level); -} - bool Pet::CanTakeMoreActiveSpells(uint32 spellid) { uint8 activecount = 1; @@ -771,102 +649,9 @@ bool Pet::CanTakeMoreActiveSpells(uint32 spellid) return true; } -bool Pet::HasTPForSpell(uint32 spellid) -{ - int32 neededtrainp = GetTPForSpell(spellid); - if((m_TrainingPoints - neededtrainp < 0 || neededtrainp < 0) && neededtrainp != 0) - return false; - return true; -} - -int32 Pet::GetTPForSpell(uint32 spellid) -{ - uint32 basetrainp = 0; - - SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spellid); - SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spellid); - for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx) - { - if(!_spell_idx->second->reqtrainpoints) - return 0; - - basetrainp = _spell_idx->second->reqtrainpoints; - break; - } - - uint32 spenttrainp = 0; - uint32 chainstart = spellmgr.GetFirstSpellInChain(spellid); - - for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr) - { - if(itr->second->state == PETSPELL_REMOVED) - continue; - - if(spellmgr.GetFirstSpellInChain(itr->first) == chainstart) - { - SkillLineAbilityMap::const_iterator _lower = spellmgr.GetBeginSkillLineAbilityMap(itr->first); - SkillLineAbilityMap::const_iterator _upper = spellmgr.GetEndSkillLineAbilityMap(itr->first); - - for(SkillLineAbilityMap::const_iterator _spell_idx2 = _lower; _spell_idx2 != _upper; ++_spell_idx2) - { - if(_spell_idx2->second->reqtrainpoints > spenttrainp) - { - spenttrainp = _spell_idx2->second->reqtrainpoints; - break; - } - } - } - } - - return int32(basetrainp) - int32(spenttrainp); -} - -uint32 Pet::GetMaxLoyaltyPoints(uint32 level) -{ - return LevelUpLoyalty[level - 1]; -} - -uint32 Pet::GetStartLoyaltyPoints(uint32 level) -{ - return LevelStartLoyalty[level - 1]; -} - -void Pet::SetTP(int32 TP) -{ - m_TrainingPoints = TP; - SetUInt32Value(UNIT_TRAINING_POINTS, (uint32)GetDispTP()); -} - -int32 Pet::GetDispTP() -{ - if(getPetType()!= HUNTER_PET) - return(0); - if(m_TrainingPoints < 0) - return -m_TrainingPoints; - else - return -(m_TrainingPoints + 1); -} - void Pet::Remove(PetSaveMode mode, bool returnreagent) { - Unit* owner = GetOwner(); - - if(owner) - { - if(owner->GetTypeId()==TYPEID_PLAYER) - { - ((Player*)owner)->RemovePet(this,mode,returnreagent); - return; - } - - // only if current pet in slot - if(owner->GetPetGUID()==GetGUID()) - owner->SetPet(0); - } - - CleanupsBeforeDelete(); - AddObjectToRemoveList(); - m_removed = true; + m_owner->RemovePet(this,mode,returnreagent); } void Pet::GivePetXP(uint32 xp) @@ -901,7 +686,7 @@ void Pet::GivePetXP(uint32 xp) newXP -= nextLvlXP; SetLevel( level + 1 ); - SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32((Trinity::XP::xp_to_level(level+1))/4)); + SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, objmgr.GetXPForLevel(level+1)*PET_XP_FACTOR); level = getLevel(); nextLvlXP = GetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP); @@ -909,9 +694,6 @@ void Pet::GivePetXP(uint32 xp) } SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, newXP); - - if(getPetType() == HUNTER_PET) - KillLoyaltyBonus(level); } void Pet::GivePetLevel(uint32 level) @@ -919,16 +701,15 @@ void Pet::GivePetLevel(uint32 level) if(!level) return; - InitStatsForLevel( level); - - SetTP(m_TrainingPoints + (GetLoyaltyLevel() - 1)); + InitStatsForLevel(level); + InitTalentForLevel(); } bool Pet::CreateBaseAtCreature(Creature* creature) { if(!creature) { - sLog.outError("CRITICAL ERROR: NULL pointer parsed into CreateBaseAtCreature()"); + sLog.outError("CRITICAL: NULL pointer parsed into CreateBaseAtCreature()"); return false; } uint32 guid=objmgr.GenerateLowGuid(HIGHGUID_PET); @@ -938,14 +719,14 @@ bool Pet::CreateBaseAtCreature(Creature* creature) sLog.outDebug("Create pet"); uint32 pet_number = objmgr.GeneratePetNumber(); - if(!Create(guid, creature->GetMap(), creature->GetEntry(), pet_number)) + if(!Create(guid, creature->GetMap(), creature->GetPhaseMask(), creature->GetEntry(), pet_number)) return false; Relocate(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ(), creature->GetOrientation()); if(!IsPositionValid()) { - sLog.outError("ERROR: Pet (guidlow %d, entry %d) not created base at creature. Suggested coordinates isn't valid (X: %f Y: %f)", + sLog.outError("Pet (guidlow %d, entry %d) not created base at creature. Suggested coordinates isn't valid (X: %f Y: %f)", GetGUIDLow(), GetEntry(), GetPositionX(), GetPositionY()); return false; } @@ -953,15 +734,10 @@ bool Pet::CreateBaseAtCreature(Creature* creature) CreatureInfo const *cinfo = GetCreatureInfo(); if(!cinfo) { - sLog.outError("ERROR: CreateBaseAtCreature() failed, creatureInfo is missing!"); + sLog.outError("CreateBaseAtCreature() failed, creatureInfo is missing!"); return false; } - if(cinfo->type == CREATURE_TYPE_CRITTER) - { - setPetType(MINI_PET); - return true; - } SetDisplayId(creature->GetDisplayId()); SetNativeDisplayId(creature->GetNativeDisplayId()); SetMaxPower(POWER_HAPPINESS, GetCreatePowers(POWER_HAPPINESS)); @@ -969,45 +745,43 @@ bool Pet::CreateBaseAtCreature(Creature* creature) setPowerType(POWER_FOCUS); SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, 0); SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0); - SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32((Trinity::XP::xp_to_level(creature->getLevel()))/4)); - SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); + SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, objmgr.GetXPForLevel(creature->getLevel())*PET_XP_FACTOR); SetUInt32Value(UNIT_NPC_FLAGS, 0); - CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(creature->GetCreatureInfo()->family); - if( char* familyname = cFamily->Name[sWorld.GetDefaultDbcLocale()] ) - SetName(familyname); + if(CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cinfo->family)) + SetName(cFamily->Name[sWorld.GetDefaultDbcLocale()]); else - SetName(creature->GetName()); + SetName(creature->GetNameForLocaleIdx(objmgr.GetDBCLocaleIndex())); - m_loyaltyPoints = 1000; if(cinfo->type == CREATURE_TYPE_BEAST) { SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100); SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE ); - SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5 ); SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_ALLOWED); - - SetUInt32Value(UNIT_MOD_CAST_SPEED, creature->GetUInt32Value(UNIT_MOD_CAST_SPEED) ); - SetLoyaltyLevel(REBELLIOUS); + SetUInt32Value(UNIT_MOD_CAST_SPEED, creature->GetUInt32Value(UNIT_MOD_CAST_SPEED)); } return true; } -bool Pet::InitStatsForLevel(uint32 petlevel) +bool Guardian::InitStatsForLevel(uint32 petlevel) { CreatureInfo const *cinfo = GetCreatureInfo(); assert(cinfo); - Unit* owner = GetOwner(); - if(!owner) + SetLevel(petlevel); + + PetType petType = MAX_PET_TYPE; + if(HasSummonMask(SUMMON_MASK_PET) && m_owner->GetTypeId() == TYPEID_PLAYER) { - sLog.outError("ERROR: attempt to summon pet (Entry %u) without owner! Attempt terminated.", cinfo->Entry); - return false; + if(m_owner->getClass() == CLASS_WARLOCK) + petType = SUMMON_PET; + else if(m_owner->getClass() == CLASS_HUNTER) + petType = HUNTER_PET; + else + sLog.outError("Unknown type pet %u is summoned by player class %u", GetEntry(), m_owner->getClass()); } - uint32 creature_ID = (getPetType() == HUNTER_PET) ? 1 : cinfo->Entry; - - SetLevel(petlevel); + uint32 creature_ID = (petType == HUNTER_PET) ? 1 : cinfo->Entry; SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool)); @@ -1020,7 +794,7 @@ bool Pet::InitStatsForLevel(uint32 petlevel) SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0); CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cinfo->family); - if(cFamily && cFamily->minScale > 0.0f && getPetType()==HUNTER_PET) + if(cFamily && cFamily->minScale > 0.0f && petType==HUNTER_PET) { float scale; if (getLevel() >= cFamily->maxScaleLevel) @@ -1036,7 +810,7 @@ bool Pet::InitStatsForLevel(uint32 petlevel) int32 createResistance[MAX_SPELL_SCHOOL] = {0,0,0,0,0,0,0}; - if(cinfo && getPetType() != HUNTER_PET) + if(cinfo && petType != HUNTER_PET) { createResistance[SPELL_SCHOOL_HOLY] = cinfo->resistance1; createResistance[SPELL_SCHOOL_FIRE] = cinfo->resistance2; @@ -1046,39 +820,16 @@ bool Pet::InitStatsForLevel(uint32 petlevel) createResistance[SPELL_SCHOOL_ARCANE] = cinfo->resistance6; } - switch(getPetType()) + switch(petType) { case SUMMON_PET: { - if(owner->GetTypeId() == TYPEID_PLAYER) - { - switch(owner->getClass()) - { - case CLASS_WARLOCK: - { - - //the damage bonus used for pets is either fire or shadow damage, whatever is higher - uint32 fire = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FIRE); - uint32 shadow = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_SHADOW); - uint32 val = (fire > shadow) ? fire : shadow; - - SetBonusDamage(int32 (val * 0.15f)); - //bonusAP += val * 0.57; - break; - } - case CLASS_MAGE: - { - //40% damage bonus of mage's frost damage - float val = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FROST) * 0.4; - if(val < 0) - val = 0; - SetBonusDamage( int32(val)); - break; - } - default: - break; - } - } + //the damage bonus used for pets is either fire or shadow damage, whatever is higher + uint32 fire = m_owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FIRE); + uint32 shadow = m_owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_SHADOW); + uint32 val = (fire > shadow) ? fire : shadow; + SetBonusDamage(int32 (val * 0.15f)); + //bonusAP += val * 0.57; SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4)) ); SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4)) ); @@ -1117,8 +868,7 @@ bool Pet::InitStatsForLevel(uint32 petlevel) } case HUNTER_PET: { - SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32((Trinity::XP::xp_to_level(petlevel))/4)); - + SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, objmgr.GetXPForLevel(petlevel)*PET_XP_FACTOR); //these formula may not be correct; however, it is designed to be close to what it should be //this makes dps 0.5 of pets level SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4)) ); @@ -1154,12 +904,18 @@ bool Pet::InitStatsForLevel(uint32 petlevel) } break; } - case GUARDIAN_PET: - SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0); - SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, 1000); - + default: switch(GetEntry()) { + case 510: // mage Water Elemental + { + //40% damage bonus of mage's frost damage + float val = m_owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FROST) * 0.4; + if(val < 0) + val = 0; + SetBonusDamage( int32(val)); + break; + } case 1964: //force of nature SetCreateHealth(30 + 30*petlevel); SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel * 2.5f - (petlevel / 2))); @@ -1177,20 +933,17 @@ bool Pet::InitStatsForLevel(uint32 petlevel) SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel * 4 + petlevel)); break; default: - SetCreateMana(28 + 10*petlevel); - SetCreateHealth(28 + 30*petlevel); - - // FIXME: this is wrong formula, possible each guardian pet have own damage formula - //these formula may not be correct; however, it is designed to be close to what it should be - //this makes dps 0.5 of pets level - SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4))); - //damage range is then petlevel / 2 - SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4))); + SetCreateMana(28 + 10*petlevel); + SetCreateHealth(28 + 30*petlevel); + // FIXME: this is wrong formula, possible each guardian pet have own damage formula + //these formula may not be correct; however, it is designed to be close to what it should be + //this makes dps 0.5 of pets level + SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4))); + //damage range is then petlevel / 2 + SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4))); break; } - break; - default: - sLog.outError("Pet have incorrect type (%u) for levelup.", getPetType()); + //sLog.outError("Pet have incorrect type (%u) for levelup.", petType); break; } @@ -1272,7 +1025,7 @@ void Pet::_LoadSpellCooldowns() continue; data << uint32(spell_id); - data << uint32(uint32(db_time-curTime)*1000); // in m.secs + data << uint32(uint32(db_time-curTime)*IN_MILISECONDS); _AddCreatureSpellCooldown(spell_id,db_time); @@ -1310,7 +1063,7 @@ void Pet::_SaveSpellCooldowns() void Pet::_LoadSpells() { - QueryResult *result = CharacterDatabase.PQuery("SELECT spell,slot,active FROM pet_spell WHERE guid = '%u'",m_charmInfo->GetPetNumber()); + QueryResult *result = CharacterDatabase.PQuery("SELECT spell,active FROM pet_spell WHERE guid = '%u'",m_charmInfo->GetPetNumber()); if(result) { @@ -1318,7 +1071,7 @@ void Pet::_LoadSpells() { Field *fields = result->Fetch(); - addSpell(fields[0].GetUInt16(), fields[2].GetUInt16(), PETSPELL_UNCHANGED, fields[1].GetUInt16()); + addSpell(fields[0].GetUInt32(), fields[1].GetUInt16(), PETSPELL_UNCHANGED); } while( result->NextRow() ); @@ -1335,7 +1088,7 @@ void Pet::_SaveSpells() if (itr->second->state == PETSPELL_REMOVED || itr->second->state == PETSPELL_CHANGED) CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE guid = '%u' and spell = '%u'", m_charmInfo->GetPetNumber(), itr->first); if (itr->second->state == PETSPELL_NEW || itr->second->state == PETSPELL_CHANGED) - CharacterDatabase.PExecute("INSERT INTO pet_spell (guid,spell,slot,active) VALUES ('%u', '%u', '%u','%u')", m_charmInfo->GetPetNumber(), itr->first, itr->second->slotId,itr->second->active); + CharacterDatabase.PExecute("INSERT INTO pet_spell (guid,spell,active) VALUES ('%u', '%u', '%u')", m_charmInfo->GetPetNumber(), itr->first, itr->second->active); if (itr->second->state == PETSPELL_REMOVED) _removeSpell(itr->first); @@ -1346,45 +1099,39 @@ void Pet::_SaveSpells() void Pet::_LoadAuras(uint32 timediff) { + sLog.outDebug("Loading auras for pet %u",GetGUIDLow()); m_Auras.clear(); for (int i = 0; i < TOTAL_AURAS; i++) m_modAuras[i].clear(); - // all aura related fields - for(int i = UNIT_FIELD_AURA; i <= UNIT_FIELD_AURASTATE; ++i) - SetUInt32Value(i, 0); - - QueryResult *result = CharacterDatabase.PQuery("SELECT caster_guid,spell,effect_index,stackcount,amount,maxduration,remaintime,remaincharges FROM pet_aura WHERE guid = '%u'",m_charmInfo->GetPetNumber()); + QueryResult *result = CharacterDatabase.PQuery("SELECT caster_guid,spell,effect_mask,stackcount,amount0, amount1, amount2 ,maxduration,remaintime,remaincharges FROM pet_aura WHERE guid = '%u'",m_charmInfo->GetPetNumber()); if(result) { do { + int32 damage[3]; Field *fields = result->Fetch(); uint64 caster_guid = fields[0].GetUInt64(); uint32 spellid = fields[1].GetUInt32(); - uint32 effindex = fields[2].GetUInt32(); - uint32 stackcount= fields[3].GetUInt32(); - int32 damage = (int32)fields[4].GetUInt32(); - int32 maxduration = (int32)fields[5].GetUInt32(); - int32 remaintime = (int32)fields[6].GetUInt32(); - int32 remaincharges = (int32)fields[7].GetUInt32(); + uint32 effmask = fields[2].GetUInt32(); + uint32 stackcount = fields[3].GetUInt32(); + damage[0] = int32(fields[4].GetUInt32()); + damage[1] = int32(fields[5].GetUInt32()); + damage[2] = int32(fields[6].GetUInt32()); + int32 maxduration = (int32)fields[7].GetUInt32(); + int32 remaintime = (int32)fields[8].GetUInt32(); + int32 remaincharges = (int32)fields[9].GetUInt32(); SpellEntry const* spellproto = sSpellStore.LookupEntry(spellid); if(!spellproto) { - sLog.outError("Unknown aura (spellid %u, effindex %u), ignore.",spellid,effindex); - continue; - } - - if(effindex >= 3) - { - sLog.outError("Invalid effect index (spellid %u, effindex %u), ignore.",spellid,effindex); + sLog.outError("Unknown aura (spellid %u), ignore.",spellid); continue; } // negative effects should continue counting down after logout - if (remaintime != -1 && !IsPositiveEffect(spellid, effindex)) + if (remaintime != -1 && !IsPositiveSpell(spellid)) { if(remaintime <= int32(timediff)) continue; @@ -1399,21 +1146,12 @@ void Pet::_LoadAuras(uint32 timediff) remaincharges = spellproto->procCharges; } else - remaincharges = -1; + remaincharges = 0; - /// do not load single target auras (unless they were cast by the player) - if (caster_guid != GetGUID() && IsSingleTargetSpell(spellproto)) - continue; - - for(uint32 i=0; i<stackcount; i++) - { - Aura* aura = CreateAura(spellproto, effindex, NULL, this, NULL); - - if(!damage) - damage = aura->GetModifier()->m_amount; - aura->SetLoadedState(caster_guid,damage,maxduration,remaintime,remaincharges); - AddAura(aura); - } + Aura* aura = new Aura(spellproto, effmask, NULL, this, NULL, NULL); + aura->SetLoadedState(caster_guid,maxduration,remaintime,remaincharges, stackcount, &damage[0]); + AddAura(aura); + sLog.outDetail("Added aura spellid %u, effectmask %u", spellproto->Id, effmask); } while( result->NextRow() ); @@ -1426,56 +1164,36 @@ void Pet::_SaveAuras() CharacterDatabase.PExecute("DELETE FROM pet_aura WHERE guid = '%u'", m_charmInfo->GetPetNumber()); AuraMap const& auras = GetAuras(); - if (auras.empty()) - return; - - spellEffectPair lastEffectPair = auras.begin()->first; - uint32 stackCounter = 1; - - for(AuraMap::const_iterator itr = auras.begin(); ; ++itr) + for(AuraMap::const_iterator itr = auras.begin(); itr !=auras.end() ; ++itr) { - if(itr == auras.end() || lastEffectPair != itr->first) - { - AuraMap::const_iterator itr2 = itr; - // save previous spellEffectPair to db - itr2--; - SpellEntry const *spellInfo = itr2->second->GetSpellProto(); - /// do not save single target auras (unless they were cast by the player) - if (!(itr2->second->GetCasterGUID() != GetGUID() && IsSingleTargetSpell(spellInfo))) - { - if(!itr2->second->IsPassive()) - { - // skip all auras from spell that apply at cast SPELL_AURA_MOD_SHAPESHIFT or pet area auras. - uint8 i; - for (i = 0; i < 3; i++) - if (spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_STEALTH || - spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_OWNER || - spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_PET ) - break; - - if (i == 3) - { - CharacterDatabase.PExecute("INSERT INTO pet_aura (guid,caster_guid,spell,effect_index,stackcount,amount,maxduration,remaintime,remaincharges) " - "VALUES ('%u', '" I64FMTD "', '%u', '%u', '%u', '%d', '%d', '%d', '%d')", - m_charmInfo->GetPetNumber(), itr2->second->GetCasterGUID(),(uint32)itr2->second->GetId(), (uint32)itr2->second->GetEffIndex(), (uint32)itr2->second->GetStackAmount(), itr2->second->GetModifier()->m_amount,int(itr2->second->GetAuraMaxDuration()),int(itr2->second->GetAuraDuration()),int(itr2->second->m_procCharges)); - } - } - } - if(itr == auras.end()) - break; - } + // skip all auras from spell that apply at cast SPELL_AURA_MOD_SHAPESHIFT or pet area auras. + // do not save single target auras (unless they were cast by the player) + if (itr->second->IsPassive() || itr->second->IsAuraType(SPELL_AURA_MOD_STEALTH)) + continue; + bool isCaster = itr->second->GetCasterGUID() == GetGUID(); + if (!isCaster) + if (itr->second->IsSingleTarget() + || itr->second->IsAreaAura()) + continue; - if (lastEffectPair == itr->first) - stackCounter++; - else + uint32 amounts[MAX_SPELL_EFFECTS]; + for (uint8 i=0;i<MAX_SPELL_EFFECTS;++i) { - lastEffectPair = itr->first; - stackCounter = 1; + if (AuraEffect * partAura = itr->second->GetPartAura(i)) + amounts[i]=partAura->GetAmount(); + else + amounts[i]=0; } + + CharacterDatabase.PExecute("INSERT INTO pet_aura (guid,caster_guid,spell,effect_mask,stackcount,amount0, amount1, amount2,maxduration,remaintime,remaincharges) " + "VALUES ('%u', '" I64FMTD "', '%u', '%u', '%u', '%u', '%u', '%d', '%d', '%d', '%d')", + m_charmInfo->GetPetNumber(), itr->second->GetCasterGUID(),(uint32)itr->second->GetId(), (uint32)itr->second->GetEffectMask(), + (uint32)itr->second->GetStackAmount(), amounts[0], amounts[1], amounts[2] + ,int(itr->second->GetAuraMaxDuration()),int(itr->second->GetAuraDuration()),int(itr->second->GetAuraCharges())); } } -bool Pet::addSpell(uint16 spell_id, uint16 active, PetSpellState state, uint16 slot_id, PetSpellType type) +bool Pet::addSpell(uint32 spell_id, uint16 active, PetSpellState state, PetSpellType type) { SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id); if (!spellInfo) @@ -1494,7 +1212,7 @@ bool Pet::addSpell(uint16 spell_id, uint16 active, PetSpellState state, uint16 s // same spells don't have autocast option if (spellInfo->AttributesEx & SPELL_ATTR_EX_UNAUTOCASTABLE_BY_PET) - active = ACT_CAST; + active = ACT_DISABLED; PetSpellMap::iterator itr = m_spells.find(spell_id); if (itr != m_spells.end()) @@ -1531,40 +1249,45 @@ bool Pet::addSpell(uint16 spell_id, uint16 active, PetSpellState state, uint16 s else newspell->active = active; - uint32 chainstart = spellmgr.GetFirstSpellInChain(spell_id); - - for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr) + // talent: unlearn all other talent ranks (high and low) + if(TalentSpellPos const* talentPos = GetTalentSpellPos(spell_id)) { - if(itr->second->state == PETSPELL_REMOVED) continue; - - if(spellmgr.GetFirstSpellInChain(itr->first) == chainstart) + if(TalentEntry const *talentInfo = sTalentStore.LookupEntry( talentPos->talent_id )) { - slot_id = itr->second->slotId; - newspell->active = itr->second->active; - - if(newspell->active == ACT_ENABLED) - ToggleAutocast(itr->first, false); + for(int i=0; i < MAX_TALENT_RANK; ++i) + { + // skip learning spell and no rank spell case + uint32 rankSpellId = talentInfo->RankID[i]; + if(!rankSpellId || rankSpellId==spell_id) + continue; - oldspell_id = itr->first; - removeSpell(itr->first); + // skip unknown ranks + if(!HasSpell(rankSpellId)) + continue; + removeSpell(rankSpellId); + } } } - - uint16 tmpslot=slot_id; - - if (tmpslot == 0xffff) + else if(uint32 chainstart = spellmgr.GetFirstSpellInChain(spell_id)) { - uint16 maxid = 0; - PetSpellMap::iterator itr; - for (itr = m_spells.begin(); itr != m_spells.end(); ++itr) + for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr) { if(itr->second->state == PETSPELL_REMOVED) continue; - if (itr->second->slotId > maxid) maxid = itr->second->slotId; + + if(spellmgr.GetFirstSpellInChain(itr->first) == chainstart) + { + newspell->active = itr->second->active; + + if(newspell->active == ACT_ENABLED) + ToggleAutocast(itr->first, false); + + oldspell_id = itr->first; + unlearnSpell(itr->first); + break; + } } - tmpslot = maxid + 1; } - newspell->slotId = tmpslot; m_spells[spell_id] = newspell; if (IsPassiveSpell(spell_id)) @@ -1575,29 +1298,70 @@ bool Pet::addSpell(uint16 spell_id, uint16 active, PetSpellState state, uint16 s if(newspell->active == ACT_ENABLED) ToggleAutocast(spell_id, true); + uint32 talentCost = GetTalentSpellCost(spell_id); + if (talentCost) + { + int32 free_points = GetMaxTalentPointsForLevel(getLevel()); + m_usedTalentCount+=talentCost; + // update free talent points + free_points-=m_usedTalentCount; + SetFreeTalentPoints(free_points > 0 ? free_points : 0); + } return true; } -bool Pet::learnSpell(uint16 spell_id) +bool Pet::learnSpell(uint32 spell_id) { // prevent duplicated entires in spell book if (!addSpell(spell_id)) return false; - Unit* owner = GetOwner(); - if(owner->GetTypeId()==TYPEID_PLAYER) - ((Player*)owner)->PetSpellInitialize(); + if(!m_loading) + { + WorldPacket data(SMSG_PET_LEARNED_SPELL, 2); + data << uint16(spell_id); + m_owner->GetSession()->SendPacket(&data); + m_owner->PetSpellInitialize(); + } return true; } -void Pet::removeSpell(uint16 spell_id) +void Pet::learnLevelupSpells() +{ + PetLevelupSpellMap const * spell_map = spellmgr.GetPetLevelupSpellMap(); + int8 level = getLevel(); + for(PetLevelupSpellMap::const_iterator itr = spell_map->lower_bound(GetCreatureInfo()->family); itr != spell_map->upper_bound(GetCreatureInfo()->family); ++itr) + { + if(itr->second.first <= level) + learnSpell(itr->second.second); + else + unlearnSpell(itr->second.second); + } +} + +bool Pet::unlearnSpell(uint32 spell_id) +{ + if(removeSpell(spell_id)) + { + if(!m_loading) + { + WorldPacket data(SMSG_PET_REMOVED_SPELL, 2); + data << uint16(spell_id); + m_owner->GetSession()->SendPacket(&data); + } + return true; + } + return false; +} + +bool Pet::removeSpell(uint32 spell_id) { PetSpellMap::iterator itr = m_spells.find(spell_id); if (itr == m_spells.end()) - return; + return false; if(itr->second->state == PETSPELL_REMOVED) - return; + return false; if(itr->second->state == PETSPELL_NEW) { @@ -1608,9 +1372,23 @@ void Pet::removeSpell(uint16 spell_id) itr->second->state = PETSPELL_REMOVED; RemoveAurasDueToSpell(spell_id); + + uint32 talentCost = GetTalentSpellCost(spell_id); + if (talentCost > 0) + { + if (m_usedTalentCount > talentCost) + m_usedTalentCount-=talentCost; + else + m_usedTalentCount = 0; + // update free talent points + int32 free_points = GetMaxTalentPointsForLevel(getLevel()) - m_usedTalentCount; + SetFreeTalentPoints(free_points > 0 ? free_points : 0); + } + + 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()) @@ -1626,8 +1404,11 @@ void Pet::InitPetCreateSpells() { m_charmInfo->InitPetActionBar(); + for (PetSpellMap::iterator i = m_spells.begin(); i != m_spells.end(); ++i) + delete i->second; m_spells.clear(); - int32 usedtrainpoints = 0, petspellid; + + uint32 petspellid; PetCreateSpellEntry const* CreateSpells = objmgr.GetPetCreateSpellEntry(GetEntry()); if(CreateSpells) { @@ -1647,7 +1428,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); } @@ -1656,23 +1437,12 @@ void Pet::InitPetCreateSpells() petspellid = learn_spellproto->Id; addSpell(petspellid); - - SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(learn_spellproto->EffectTriggerSpell[0]); - SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(learn_spellproto->EffectTriggerSpell[0]); - - for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx) - { - usedtrainpoints += _spell_idx->second->reqtrainpoints; - break; - } } } LearnPetPassives(); CastPetAuras(false); - - SetTP(-usedtrainpoints); } void Pet::CheckLearning(uint32 spellid) @@ -1692,11 +1462,116 @@ 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); } } +bool Pet::resetTalents(bool no_cost) +{ + Unit *owner = GetOwner(); + if (!owner || owner->GetTypeId()!=TYPEID_PLAYER) + return false; + + CreatureInfo const * ci = GetCreatureInfo(); + if(!ci) + return false; + // Check pet talent type + CreatureFamilyEntry const *pet_family = sCreatureFamilyStore.LookupEntry(ci->family); + if(!pet_family || pet_family->petTalentType < 0) + return false; + + Player *player = (Player *)owner; + + uint32 level = getLevel(); + uint32 talentPointsForLevel = GetMaxTalentPointsForLevel(level); + + if (m_usedTalentCount == 0) + { + SetFreeTalentPoints(talentPointsForLevel); + return false; + } + + uint32 cost = 0; + + if(!no_cost) + { + cost = resetTalentsCost(); + + if (player->GetMoney() < cost) + { + player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0); + return false; + } + } + + for (unsigned int i = 0; i < sTalentStore.GetNumRows(); i++) + { + TalentEntry const *talentInfo = sTalentStore.LookupEntry(i); + + if (!talentInfo) continue; + + TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab ); + + if(!talentTabInfo) + continue; + + // unlearn only talents for pets family talent type + if(!((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask)) + continue; + + for (int j = 0; j < MAX_TALENT_RANK; j++) + { + for(PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end();) + { + if(itr->second->state == PETSPELL_REMOVED) + { + ++itr; + continue; + } + // remove learned spells (all ranks) + uint32 itrFirstId = spellmgr.GetFirstSpellInChain(itr->first); + + // unlearn if first rank is talent or learned by talent + if (itrFirstId == talentInfo->RankID[j] || spellmgr.IsSpellLearnToSpell(talentInfo->RankID[j],itrFirstId)) + { + removeSpell(itr->first); + itr = m_spells.begin(); + continue; + } + else + ++itr; + } + } + } + + SetFreeTalentPoints(talentPointsForLevel); + + if(!no_cost) + { + player->ModifyMoney(-(int32)cost); + + m_resetTalentsCost = cost; + m_resetTalentsTime = time(NULL); + } + if(!m_loading) + player->PetSpellInitialize(); + return true; +} + +void Pet::InitTalentForLevel() +{ + uint32 level = getLevel(); + uint32 talentPointsForLevel = GetMaxTalentPointsForLevel(level); + // Reset talents in case low level (on level down) or wrong points for level (hunter can unlearn TP increase talent) + if(talentPointsForLevel == 0 || m_usedTalentCount > talentPointsForLevel) + { + // Remove all talent points + resetTalents(true); + } + SetFreeTalentPoints(talentPointsForLevel - m_usedTalentCount); +} + uint32 Pet::resetTalentsCost() const { uint32 days = (sWorld.GetGameTime() - m_resetTalentsTime)/DAY; @@ -1715,23 +1590,31 @@ uint32 Pet::resetTalentsCost() const return (m_resetTalentsCost + 1*GOLD > 10*GOLD ? 10*GOLD : m_resetTalentsCost + 1*GOLD); } +uint8 Pet::GetMaxTalentPointsForLevel(uint32 level) +{ + uint8 points = (level >= 20) ? ((level - 16) / 4) : 0; + // Mod points from owner SPELL_AURA_MOD_PET_TALENT_POINTS + if (Unit *owner = GetOwner()) + points+=owner->GetTotalAuraModifier(SPELL_AURA_MOD_PET_TALENT_POINTS); + return points; +} + void Pet::ToggleAutocast(uint32 spellid, bool apply) { if(IsPassiveSpell(spellid)) return; - //if(const SpellEntry *tempSpell = GetSpellStore()->LookupEntry(spellid)) - // if(tempSpell->EffectImplicitTargetA[0] != TARGET_ALL_AROUND_CASTER - // && tempSpell->EffectImplicitTargetA[0] != TARGET_CHAIN_DAMAGE) - // return; - - PetSpellMap::const_iterator itr = m_spells.find((uint16)spellid); + PetSpellMap::const_iterator itr = m_spells.find(spellid); + if(itr == m_spells.end()) + return; int i; if(apply) { - for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; i++); + for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; i++) + ; // just search + if (i == m_autospells.size()) { m_autospells.push_back(spellid); @@ -1742,7 +1625,9 @@ void Pet::ToggleAutocast(uint32 spellid, bool apply) else { AutoSpellList::iterator itr2 = m_autospells.begin(); - for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; i++, itr2++); + for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; i++, itr2++) + ; // just search + if (i < m_autospells.size()) { m_autospells.erase(itr2); @@ -1752,10 +1637,11 @@ void Pet::ToggleAutocast(uint32 spellid, bool apply) } } -bool Pet::Create(uint32 guidlow, Map *map, uint32 Entry, uint32 pet_number) +bool Pet::Create(uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, uint32 pet_number) { SetMapId(map->GetId()); SetInstanceId(map->GetInstanceId()); + SetPhaseMask(phaseMask,false); Object::_Create(guidlow, pet_number, HIGHGUID_PET); @@ -1765,18 +1651,15 @@ bool Pet::Create(uint32 guidlow, Map *map, uint32 Entry, uint32 pet_number) if(!InitEntry(Entry)) return false; - SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE ); - SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5 ); - - if(getPetType() == MINI_PET) // always non-attackable - SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE); return true; } bool Pet::HasSpell(uint32 spell) const { - return (m_spells.find(spell) != m_spells.end()); + PetSpellMap::const_iterator itr = m_spells.find(spell); + return (itr != m_spells.end() && itr->second->state != PETSPELL_REMOVED ); } // Get all passive spells in our skill line @@ -1797,7 +1680,7 @@ void Pet::LearnPetPassives() // Passive 01~10, Passive 00 (20782, not used), Ferocious Inspiration (34457) // Scale 01~03 (34902~34904, bonus from owner, not used) for(PetFamilySpellsSet::const_iterator petSet = petStore->second.begin(); petSet != petStore->second.end(); ++petSet) - addSpell(*petSet, ACT_DECIDE, PETSPELL_NEW, 0xffff, PETSPELL_FAMILY); + addSpell(*petSet, ACT_DECIDE, PETSPELL_NEW, PETSPELL_FAMILY); } } @@ -1837,3 +1720,10 @@ void Pet::CastPetAura(PetAura const* aura) CastSpell(this, auraId, true); } +void Pet::learnSpellHighRank(uint32 spellid) +{ + learnSpell(spellid); + + if(uint32 next = spellmgr.GetNextSpellInChain(spellid)) + learnSpellHighRank(next); +} diff --git a/src/game/Pet.h b/src/game/Pet.h index d55c467826a..de998c20aac 100644 --- a/src/game/Pet.h +++ b/src/game/Pet.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,28 +22,29 @@ #define TRINITYCORE_PET_H #include "ObjectDefines.h" -#include "Creature.h" #include "Unit.h" +#include "TemporarySummon.h" enum PetType { SUMMON_PET = 0, HUNTER_PET = 1, - GUARDIAN_PET = 2, - MINI_PET = 3, - POSSESSED_PET = 4, - MAX_PET_TYPE = 5 + POSSESSED_PET = 2, + MAX_PET_TYPE = 4, }; extern char const* petTypeSuffix[MAX_PET_TYPE]; +#define MAX_PET_STABLES 4 + +// stored in character_pet.slot enum PetSaveMode { - PET_SAVE_AS_DELETED =-1, - PET_SAVE_AS_CURRENT = 0, - PET_SAVE_IN_STABLE_SLOT_1 = 1, - PET_SAVE_IN_STABLE_SLOT_2 = 2, - PET_SAVE_NOT_IN_SLOT = 3 + PET_SAVE_AS_DELETED = -1, // not saved in fact + PET_SAVE_AS_CURRENT = 0, // in current slot (with player) + PET_SAVE_FIRST_STABLE_SLOT = 1, + PET_SAVE_LAST_STABLE_SLOT = MAX_PET_STABLES, // last in DB stable slot index (including), all higher have same meaning as PET_SAVE_NOT_IN_SLOT + PET_SAVE_NOT_IN_SLOT = 100 // for avoid conflict with stable size grow will use 100 }; enum HappinessState @@ -53,16 +54,6 @@ enum HappinessState HAPPY = 3 }; -enum LoyaltyLevel -{ - REBELLIOUS = 1, - UNRULY = 2, - SUBMISSIVE = 3, - DEPENDABLE = 4, - FAITHFUL = 5, - BEST_FRIEND = 6 -}; - enum PetSpellState { PETSPELL_UNCHANGED = 0, @@ -79,7 +70,6 @@ enum PetSpellType struct PetSpell { - uint16 slotId; uint16 active; PetSpellState state : 16; @@ -117,15 +107,12 @@ 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; #define HAPPINESS_LEVEL_SIZE 333000 -extern const uint32 LevelUpLoyalty[6]; -extern const uint32 LevelStartLoyalty[6]; - #define ACTIVE_SPELLS_MAX 4 #define OWNER_MAX_DISTANCE 100 @@ -133,10 +120,10 @@ extern const uint32 LevelStartLoyalty[6]; #define PET_FOLLOW_DIST 1 #define PET_FOLLOW_ANGLE (M_PI/2) -class Pet : public Creature +class Pet : public Guardian { public: - explicit Pet(PetType type = MAX_PET_TYPE); + explicit Pet(Player *owner, PetType type = MAX_PET_TYPE); virtual ~Pet(); void AddToWorld(); @@ -147,9 +134,9 @@ class Pet : public Creature bool isControlled() const { return getPetType()==SUMMON_PET || getPetType()==HUNTER_PET; } bool isTemporarySummoned() const { return m_duration > 0; } - bool Create (uint32 guidlow, Map *map, uint32 Entry, uint32 pet_number); + bool Create (uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, uint32 pet_number); bool CreateBaseAtCreature(Creature* creature); - bool LoadPetFromDB( Unit* owner,uint32 petentry = 0,uint32 petnumber = 0, bool current = false ); + bool LoadPetFromDB( Player* owner,uint32 petentry = 0,uint32 petnumber = 0, bool current = false ); void SavePetToDB(PetSaveMode mode); void Remove(PetSaveMode mode, bool returnreagent = false); static void DeleteFromDB(uint32 guidlow); @@ -168,24 +155,13 @@ class Pet : public Creature void RegenerateFocus(); void LooseHappiness(); - void TickLoyaltyChange(); - void ModifyLoyalty(int32 addvalue); HappinessState GetHappinessState(); - uint32 GetMaxLoyaltyPoints(uint32 level); - uint32 GetStartLoyaltyPoints(uint32 level); - void KillLoyaltyBonus(uint32 level); - uint32 GetLoyaltyLevel() { return GetByteValue(UNIT_FIELD_BYTES_1, 1); } - void SetLoyaltyLevel(LoyaltyLevel level); void GivePetXP(uint32 xp); void GivePetLevel(uint32 level); - bool InitStatsForLevel(uint32 level); bool HaveInDiet(ItemPrototype const* item) const; uint32 GetCurrentFoodBenefitLevel(uint32 itemlevel); void SetDuration(int32 dur) { m_duration = dur; } - int32 GetBonusDamage() { return m_bonusdamage; } - void SetBonusDamage(int32 damage) { m_bonusdamage = damage; } - bool UpdateStats(Stats stat); bool UpdateAllStats(); void UpdateResistances(uint32 school); @@ -195,10 +171,8 @@ class Pet : public Creature void UpdateAttackPowerAndDamage(bool ranged = false); void UpdateDamagePhysical(WeaponAttackType attType); - bool CanTakeMoreActiveSpells(uint32 SpellIconID); - void ToggleAutocast(uint32 spellid, bool apply); - bool HasTPForSpell(uint32 spellid); - int32 GetTPForSpell(uint32 spellid); + bool CanTakeMoreActiveSpells(uint32 SpellIconID); + void ToggleAutocast(uint32 spellid, bool apply); bool HasSpell(uint32 spell) const; void AddTeachSpell(uint32 learned_id, uint32 source_id) { m_teachspells[learned_id] = source_id; } @@ -214,10 +188,13 @@ class Pet : public Creature void _LoadSpells(); void _SaveSpells(); - bool addSpell(uint16 spell_id,uint16 active = ACT_DECIDE, PetSpellState state = PETSPELL_NEW, uint16 slot_id=0xffff, PetSpellType type = PETSPELL_NORMAL); - bool learnSpell(uint16 spell_id); - void removeSpell(uint16 spell_id); - bool _removeSpell(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 learnSpellHighRank(uint32 spellid); + void learnLevelupSpells(); + bool unlearnSpell(uint32 spell_id); + bool removeSpell(uint32 spell_id); + bool _removeSpell(uint32 spell_id); PetSpellMap m_spells; TeachSpellMap m_teachspells; @@ -225,32 +202,35 @@ class Pet : public Creature void InitPetCreateSpells(); void CheckLearning(uint32 spellid); + + bool resetTalents(bool no_cost = false); uint32 resetTalentsCost() const; + void InitTalentForLevel(); - void SetTP(int32 TP); - int32 GetDispTP(); + uint8 GetMaxTalentPointsForLevel(uint32 level); + uint8 GetFreeTalentPoints() { return GetByteValue(UNIT_FIELD_BYTES_1, 1); } + void SetFreeTalentPoints(uint8 points) { SetByteValue(UNIT_FIELD_BYTES_1, 1, points); } - int32 m_TrainingPoints; uint32 m_resetTalentsCost; time_t m_resetTalentsTime; + uint32 m_usedTalentCount; - uint64 GetAuraUpdateMask() { return m_auraUpdateMask; } - void SetAuraUpdateMask(uint8 slot) { m_auraUpdateMask |= (uint64(1) << slot); } - void UnsetAuraUpdateMask(uint8 slot) { m_auraUpdateMask &= ~(uint64(1) << slot); } - void ResetAuraUpdateMask() { m_auraUpdateMask = 0; } + const uint64& GetAuraUpdateMaskForRaid() const { return m_auraRaidUpdateMask; } + void SetAuraUpdateMaskForRaid(uint8 slot) { m_auraRaidUpdateMask |= (uint64(1) << slot); } + void ResetAuraUpdateMaskForRaid() { m_auraRaidUpdateMask = 0; } DeclinedName const* GetDeclinedNames() const { return m_declinedname; } bool m_removed; // prevent overwrite pet state in DB at next Pet::Update if pet already removed(saved) + + Player *GetOwner() { return m_owner; } protected: - uint32 m_regenTimer; + Player *m_owner; uint32 m_happinessTimer; - uint32 m_loyaltyTimer; PetType m_petType; int32 m_duration; // time until unsummon (used mostly for summoned guardians and not used for controlled pets) - int32 m_loyaltyPoints; - int32 m_bonusdamage; - uint64 m_auraUpdateMask; + uint64 m_auraRaidUpdateMask; + bool m_loading; DeclinedName *m_declinedname; diff --git a/src/game/PetAI.cpp b/src/game/PetAI.cpp index 75c1aa06c20..88327978de9 100644 --- a/src/game/PetAI.cpp +++ b/src/game/PetAI.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,7 +22,7 @@ #include "Errors.h" #include "Pet.h" #include "Player.h" -#include "Database/DBCStores.h" +#include "DBCStores.h" #include "Spell.h" #include "ObjectAccessor.h" #include "SpellMgr.h" @@ -38,7 +38,7 @@ int PetAI::Permissible(const Creature *creature) return PERMIT_BASE_NO; } -PetAI::PetAI(Creature *c) : CreatureAI(c), i_pet(*c), i_tracker(TIME_INTERVAL_LOOK) +PetAI::PetAI(Creature *c) : CreatureAI(c), i_tracker(TIME_INTERVAL_LOOK) { m_AllySet.clear(); UpdateAllies(); @@ -51,46 +51,46 @@ void PetAI::EnterEvadeMode() bool PetAI::_needToStop() const { // This is needed for charmed creatures, as once their target was reset other effects can trigger threat - if(i_pet.isCharmed() && i_pet.getVictim() == i_pet.GetCharmer()) + if(m_creature->isCharmed() && m_creature->getVictim() == m_creature->GetCharmer()) return true; - return !i_pet.canAttack(i_pet.getVictim()); + return !m_creature->canAttack(m_creature->getVictim()); } void PetAI::_stopAttack() { - if( !i_pet.isAlive() ) + if( !m_creature->isAlive() ) { - DEBUG_LOG("Creature stoped attacking cuz his dead [guid=%u]", i_pet.GetGUIDLow()); - i_pet.GetMotionMaster()->Clear(); - i_pet.GetMotionMaster()->MoveIdle(); - i_pet.CombatStop(); - i_pet.getHostilRefManager().deleteReferences(); + DEBUG_LOG("Creature stoped attacking cuz his dead [guid=%u]", m_creature->GetGUIDLow()); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->CombatStop(); + m_creature->getHostilRefManager().deleteReferences(); return; } - Unit* owner = i_pet.GetCharmerOrOwner(); + Unit* owner = m_creature->GetCharmerOrOwner(); - if(owner && i_pet.GetCharmInfo() && i_pet.GetCharmInfo()->HasCommandState(COMMAND_FOLLOW)) + if(owner && m_creature->GetCharmInfo() && m_creature->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW)) { - i_pet.GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE); + m_creature->GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE); } else { - i_pet.clearUnitState(UNIT_STAT_FOLLOW); - i_pet.GetMotionMaster()->Clear(); - i_pet.GetMotionMaster()->MoveIdle(); + m_creature->clearUnitState(UNIT_STAT_FOLLOW); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); } - i_pet.AttackStop(); + m_creature->AttackStop(); } void PetAI::UpdateAI(const uint32 diff) { - if (!i_pet.isAlive()) + if (!m_creature->isAlive()) return; - Unit* owner = i_pet.GetCharmerOrOwner(); + Unit* owner = m_creature->GetCharmerOrOwner(); if(m_updateAlliesTimer <= diff) // UpdateAllies self set update timer @@ -98,12 +98,12 @@ void PetAI::UpdateAI(const uint32 diff) else m_updateAlliesTimer -= diff; - // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc. - if( i_pet.getVictim() ) + // m_creature->getVictim() can't be used for check in case stop fighting, m_creature->getVictim() clear at Unit death etc. + if( m_creature->getVictim() ) { if( _needToStop() ) { - DEBUG_LOG("Pet AI stoped attacking [guid=%u]", i_pet.GetGUIDLow()); + DEBUG_LOG("Pet AI stoped attacking [guid=%u]", m_creature->GetGUIDLow()); _stopAttack(); return; } @@ -114,26 +114,26 @@ void PetAI::UpdateAI(const uint32 diff) { if(me->isInCombat()) _stopAttack(); - else if(owner && i_pet.GetCharmInfo()) //no victim + else if(owner && m_creature->GetCharmInfo()) //no victim { - if(owner->isInCombat() && !(i_pet.HasReactState(REACT_PASSIVE) || i_pet.GetCharmInfo()->HasCommandState(COMMAND_STAY))) + if(owner->isInCombat() && !(m_creature->HasReactState(REACT_PASSIVE) || m_creature->GetCharmInfo()->HasCommandState(COMMAND_STAY))) AttackStart(owner->getAttackerForHelper()); - else if(i_pet.GetCharmInfo()->HasCommandState(COMMAND_FOLLOW) && !i_pet.hasUnitState(UNIT_STAT_FOLLOW)) - i_pet.GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE); + else if(m_creature->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW) && !m_creature->hasUnitState(UNIT_STAT_FOLLOW)) + m_creature->GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE); } } if(!me->GetCharmInfo()) return; - if (i_pet.GetGlobalCooldown() == 0 && !i_pet.hasUnitState(UNIT_STAT_CASTING)) + if (m_creature->GetGlobalCooldown() == 0 && !m_creature->hasUnitState(UNIT_STAT_CASTING)) { bool inCombat = me->getVictim(); //Autocast - for (uint8 i = 0; i < i_pet.GetPetAutoSpellSize(); i++) + for (uint8 i = 0; i < m_creature->GetPetAutoSpellSize(); i++) { - uint32 spellID = i_pet.GetPetAutoSpellOnPos(i); + uint32 spellID = m_creature->GetPetAutoSpellOnPos(i); if (!spellID) continue; @@ -153,11 +153,11 @@ void PetAI::UpdateAI(const uint32 diff) continue; } - Spell *spell = new Spell(&i_pet, spellInfo, false, 0); + Spell *spell = new Spell(m_creature, spellInfo, false, 0); - if(inCombat && !i_pet.hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(i_pet.getVictim())) + if(inCombat && !m_creature->hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(m_creature->getVictim())) { - m_targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(i_pet.getVictim(), spell)); + m_targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(m_creature->getVictim(), spell)); continue; } else @@ -165,7 +165,7 @@ void PetAI::UpdateAI(const uint32 diff) bool spellUsed = false; for(std::set<uint64>::iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar) { - Unit* Target = ObjectAccessor::GetUnit(i_pet,*tar); + Unit* Target = ObjectAccessor::GetUnit(*m_creature,*tar); //only buff targets that are in combat, unless the spell can only be cast while out of combat if(!Target) @@ -196,19 +196,19 @@ void PetAI::UpdateAI(const uint32 diff) SpellCastTargets targets; targets.setUnitTarget( target ); - if( !i_pet.HasInArc(M_PI, target) ) + if( !m_creature->HasInArc(M_PI, target) ) { - i_pet.SetInFront(target); + m_creature->SetInFront(target); if( target->GetTypeId() == TYPEID_PLAYER ) - i_pet.SendUpdateToPlayer( (Player*)target ); + m_creature->SendUpdateToPlayer( (Player*)target ); if(owner && owner->GetTypeId() == TYPEID_PLAYER) - i_pet.SendUpdateToPlayer( (Player*)owner ); + m_creature->SendUpdateToPlayer( (Player*)owner ); } - i_pet.AddCreatureSpellCooldown(spell->m_spellInfo->Id); - if(i_pet.isPet()) - ((Pet*)&i_pet)->CheckLearning(spell->m_spellInfo->Id); + m_creature->AddCreatureSpellCooldown(spell->m_spellInfo->Id); + if(m_creature->isPet()) + ((Pet*)m_creature)->CheckLearning(spell->m_spellInfo->Id); spell->prepare(&targets); } @@ -222,10 +222,10 @@ void PetAI::UpdateAI(const uint32 diff) void PetAI::UpdateAllies() { - Unit* owner = i_pet.GetCharmerOrOwner(); + Unit* owner = m_creature->GetCharmerOrOwner(); Group *pGroup = NULL; - m_updateAlliesTimer = 10000; //update friendly targets every 10 seconds, lesser checks increase performance + m_updateAlliesTimer = 10*IN_MILISECONDS; //update friendly targets every 10 seconds, lesser checks increase performance if(!owner) return; @@ -240,7 +240,7 @@ void PetAI::UpdateAllies() return; m_AllySet.clear(); - m_AllySet.insert(i_pet.GetGUID()); + m_AllySet.insert(m_creature->GetGUID()); if(pGroup) //add group { for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) diff --git a/src/game/PetAI.h b/src/game/PetAI.h index 864195701c2..a346908ee0e 100644 --- a/src/game/PetAI.h +++ b/src/game/PetAI.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,7 +31,7 @@ class TRINITY_DLL_DECL PetAI : public CreatureAI { public: - PetAI(Creature *c); + explicit PetAI(Creature *c); void EnterEvadeMode(); void JustDied(Unit* who) { _stopAttack(); } @@ -46,7 +46,6 @@ class TRINITY_DLL_DECL PetAI : public CreatureAI void UpdateAllies(); - Creature &i_pet; TimeTracker i_tracker; std::set<uint64> m_AllySet; uint32 m_updateAlliesTimer; diff --git a/src/game/PetHandler.cpp b/src/game/PetHandler.cpp index 456d68ed54d..56845eaa656 100644 --- a/src/game/PetHandler.cpp +++ b/src/game/PetHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,18 +21,15 @@ #include "Common.h" #include "WorldPacket.h" #include "WorldSession.h" -#include "World.h" #include "ObjectMgr.h" #include "SpellMgr.h" #include "Log.h" #include "Opcodes.h" #include "Spell.h" #include "ObjectAccessor.h" -#include "MapManager.h" #include "CreatureAI.h" #include "Util.h" #include "Pet.h" -#include "Language.h" void WorldSession::HandlePetAction( WorldPacket & recv_data ) { @@ -49,25 +46,39 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) // used also for charmed creature Unit* pet= ObjectAccessor::GetUnit(*_player, guid1); - sLog.outDetail("HandlePetAction.Pet %u flag is %u, spellid is %u, target %u.\n", uint32(GUID_LOPART(guid1)), flag, spellid, uint32(GUID_LOPART(guid2)) ); + sLog.outDetail("HandlePetAction.Pet %u flag is %u, spellid is %u, target %u.", uint32(GUID_LOPART(guid1)), flag, spellid, uint32(GUID_LOPART(guid2)) ); if(!pet) { - sLog.outError( "Pet %u not exist.\n", uint32(GUID_LOPART(guid1)) ); + sLog.outError( "Pet %u not exist.", uint32(GUID_LOPART(guid1)) ); return; } - if(pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharm()) + if(pet != GetPlayer()->GetFirstControlled()) { - sLog.outError("HandlePetAction.Pet %u isn't pet of player %s.\n", uint32(GUID_LOPART(guid1)), GetPlayer()->GetName() ); + sLog.outError("HandlePetAction.Pet %u isn't pet of player %s.", uint32(GUID_LOPART(guid1)), GetPlayer()->GetName() ); return; } - if(!pet->isAlive()) - return; - + //TODO: allow control charmed player? if(pet->GetTypeId() == TYPEID_PLAYER && !(flag == ACT_COMMAND && spellid == COMMAND_ATTACK)) return; + if(GetPlayer()->m_Controlled.size() == 1) + HandlePetActionHelper(pet, guid1, spellid, flag, guid2); + else + { + //If a pet is dismissed, m_Controlled will change + std::vector<Unit*> controlled; + for(Unit::ControlList::iterator itr = GetPlayer()->m_Controlled.begin(); itr != GetPlayer()->m_Controlled.end(); ++itr) + if((*itr)->GetEntry() == pet->GetEntry() && (*itr)->isAlive()) + controlled.push_back(*itr); + for(std::vector<Unit*>::iterator itr = controlled.begin(); itr != controlled.end(); ++itr) + HandlePetActionHelper(*itr, guid1, spellid, flag, guid2); + } +} + +void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid, uint16 flag, uint64 guid2) +{ CharmInfo *charmInfo = pet->GetCharmInfo(); if(!charmInfo) { @@ -107,7 +118,7 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) } // only place where pet can be player pet->clearUnitState(UNIT_STAT_FOLLOW); - uint64 selguid = _player->GetSelection(); + const uint64& selguid = _player->GetSelection(); Unit *TargetUnit = ObjectAccessor::GetUnit(*_player, selguid); if(!TargetUnit) return; @@ -119,26 +130,33 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) //if(!pet->IsWithinLOSInMap(TargetUnit)) // return; - if(pet->GetTypeId() != TYPEID_PLAYER && ((Creature*)pet)->IsAIEnabled) + // This is true if pet has no target or has target but targets differs. + if(pet->getVictim() != TargetUnit) { - ((Creature*)pet)->AI()->AttackStart(TargetUnit); + if (pet->getVictim()) + pet->AttackStop(); - //10% chance to play special pet attack talk, else growl - if(((Creature*)pet)->isPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != TargetUnit && urand(0, 100) < 10) - pet->SendPetTalk((uint32)PET_TALK_ATTACK); - else + if(pet->GetTypeId() != TYPEID_PLAYER && ((Creature*)pet)->IsAIEnabled) { - // 90% chance for pet and 100% chance for charmed creature - pet->SendPetAIReaction(guid1); + ((Creature*)pet)->AI()->AttackStart(TargetUnit); + + //10% chance to play special pet attack talk, else growl + if(((Creature*)pet)->isPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != TargetUnit && urand(0, 100) < 10) + pet->SendPetTalk((uint32)PET_TALK_ATTACK); + else + { + // 90% chance for pet and 100% chance for charmed creature + pet->SendPetAIReaction(guid1); + } } - } - else // charmed player - { - if(pet->getVictim() && pet->getVictim() != TargetUnit) - pet->AttackStop(); + else // charmed player + { + if(pet->getVictim() && pet->getVictim() != TargetUnit) + pet->AttackStop(); - pet->Attack(TargetUnit,true); - pet->SendPetAIReaction(guid1); + pet->Attack(TargetUnit,true); + pet->SendPetAIReaction(guid1); + } } break; } @@ -153,10 +171,10 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) p->setDeathState(CORPSE); } else // charmed or possessed - _player->Uncharm(); + _player->StopCastingCharm(); break; default: - sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.\n", flag, spellid); + sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", flag, spellid); } break; case ACT_REACTION: // 0x600 @@ -170,24 +188,22 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) break; } break; - case ACT_DISABLED: //0x8100 spell (disabled), ignore - case ACT_CAST: //0x0100 - case ACT_ENABLED: //0xc100 spell + case ACT_DISABLED: // 0x8100 spell (disabled), ignore + case ACT_PASSIVE: // 0x0100 + case ACT_ENABLED: // 0xC100 spell { - Unit* unit_target; - if(guid2) - unit_target = ObjectAccessor::GetUnit(*_player,guid2); - else - unit_target = NULL; - + Unit* unit_target = NULL; if (((Creature*)pet)->GetGlobalCooldown() > 0) return; + if(guid2) + unit_target = ObjectAccessor::GetUnit(*_player,guid2); + // do not cast unknown spells SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid ); if(!spellInfo) { - sLog.outError("WORLD: unknown PET spell id %i\n", spellid); + sLog.outError("WORLD: unknown PET spell id %i", spellid); return; } @@ -205,9 +221,9 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) Spell *spell = new Spell(pet, spellInfo, false); - int16 result = spell->PetCanCast(unit_target); + SpellCastResult result = spell->CheckPetCast(unit_target); - //auto turn to target unless possessed + //auto turn to target unless possessed if(result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->isPossessed()) { pet->SetInFront(unit_target); @@ -216,10 +232,10 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) if(Unit* powner = pet->GetCharmerOrOwner()) if(powner->GetTypeId() == TYPEID_PLAYER) pet->SendUpdateToPlayer((Player*)powner); - result = -1; + result = SPELL_CAST_OK; } - if(result == -1) + if(result == SPELL_CAST_OK) { ((Creature*)pet)->AddCreatureSpellCooldown(spellid); if (((Creature*)pet)->isPet()) @@ -238,12 +254,15 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) if( unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->isPossessed()) { - pet->clearUnitState(UNIT_STAT_FOLLOW); - if(pet->getVictim()) - pet->AttackStop(); - pet->GetMotionMaster()->Clear(); - if (((Creature*)pet)->IsAIEnabled) - ((Creature*)pet)->AI()->AttackStart(unit_target); + // This is true if pet has no target or has target but targets differs. + if (pet->getVictim() != unit_target) + { + if (pet->getVictim()) + pet->AttackStop(); + pet->GetMotionMaster()->Clear(); + if (((Creature*)pet)->IsAIEnabled) + ((Creature*)pet)->AI()->AttackStart(unit_target); + } } spell->prepare(&(spell->m_targets)); @@ -253,15 +272,12 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) if(pet->isPossessed()) { WorldPacket data(SMSG_CAST_FAILED, (4+1+1)); - data << uint32(spellid) << uint8(2) << uint8(result); + data << uint8(0) << uint32(spellid) << uint8(result); switch (result) { case SPELL_FAILED_REQUIRES_SPELL_FOCUS: data << uint32(spellInfo->RequiresSpellFocus); break; - case SPELL_FAILED_REQUIRES_AREA: - data << uint32(spellInfo->AreaId); - break; } SendPacket(&data); } @@ -277,7 +293,7 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) break; } default: - sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.\n", flag, spellid); + sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", flag, spellid); } } @@ -285,7 +301,7 @@ void WorldSession::HandlePetNameQuery( WorldPacket & recv_data ) { CHECK_PACKET_SIZE(recv_data,4+8); - sLog.outDetail( "HandlePetNameQuery. CMSG_PET_NAME_QUERY\n" ); + sLog.outDetail( "HandlePetNameQuery. CMSG_PET_NAME_QUERY" ); uint32 petnumber; uint64 petguid; @@ -298,7 +314,7 @@ void WorldSession::HandlePetNameQuery( WorldPacket & recv_data ) void WorldSession::SendPetNameQuery( uint64 petguid, uint32 petnumber) { - Creature* pet = ObjectAccessor::GetCreatureOrPet(*_player, petguid); + Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, petguid); if(!pet || !pet->GetCharmInfo() || pet->GetCharmInfo()->GetPetNumber() != petnumber) return; @@ -325,7 +341,7 @@ void WorldSession::HandlePetSetAction( WorldPacket & recv_data ) { CHECK_PACKET_SIZE(recv_data, 8+4+2+2); - sLog.outDetail( "HandlePetSetAction. CMSG_PET_SET_ACTION\n" ); + sLog.outDetail( "HandlePetSetAction. CMSG_PET_SET_ACTION" ); uint64 petguid; uint32 position; @@ -335,16 +351,11 @@ void WorldSession::HandlePetSetAction( WorldPacket & recv_data ) recv_data >> petguid; - // FIXME: charmed case - //Pet* pet = ObjectAccessor::Instance().GetPet(petguid); - if(ObjectAccessor::FindPlayer(petguid)) - return; - - Creature* pet = ObjectAccessor::GetCreatureOrPet(*_player, petguid); + Unit* pet = ObjectAccessor::GetUnit(*_player, petguid); - if(!pet || (pet != _player->GetPet() && pet != _player->GetCharm())) + if(!pet || pet != _player->GetFirstControlled()) { - sLog.outError( "HandlePetSetAction: Unknown pet or pet owner.\n" ); + sLog.outError( "HandlePetSetAction: Unknown pet or pet owner." ); return; } @@ -362,26 +373,27 @@ void WorldSession::HandlePetSetAction( WorldPacket & recv_data ) recv_data >> spell_id; recv_data >> act_state; - sLog.outDetail( "Player %s has changed pet spell action. Position: %u, Spell: %u, State: 0x%X\n", _player->GetName(), position, spell_id, act_state); + sLog.outDetail( "Player %s has changed pet spell action. Position: %u, Spell: %u, State: 0x%X", _player->GetName(), position, spell_id, act_state); //if it's act for spell (en/disable/cast) and there is a spell given (0 = remove spell) which pet doesn't know, don't add - if(!((act_state == ACT_ENABLED || act_state == ACT_DISABLED || act_state == ACT_CAST) && spell_id && !pet->HasSpell(spell_id))) + if(!((act_state == ACT_ENABLED || act_state == ACT_DISABLED || act_state == ACT_PASSIVE) && spell_id && !pet->HasSpell(spell_id))) { //sign for autocast if(act_state == ACT_ENABLED && spell_id) { - if(pet->isCharmed()) - charmInfo->ToggleCreatureAutocast(spell_id, true); - else + if(pet->GetTypeId() == TYPEID_UNIT && ((Creature*)pet)->isPet()) ((Pet*)pet)->ToggleAutocast(spell_id, true); + else + charmInfo->ToggleCreatureAutocast(spell_id, true); } //sign for no/turn off autocast else if(act_state == ACT_DISABLED && spell_id) { - if(pet->isCharmed()) - charmInfo->ToggleCreatureAutocast(spell_id, false); - else + if(pet->GetTypeId() == TYPEID_UNIT && ((Creature*)pet)->isPet()) ((Pet*)pet)->ToggleAutocast(spell_id, false); + else + charmInfo->ToggleCreatureAutocast(spell_id, false); + } charmInfo->GetActionBarEntry(position)->Type = act_state; @@ -394,7 +406,7 @@ void WorldSession::HandlePetRename( WorldPacket & recv_data ) { CHECK_PACKET_SIZE(recv_data, 8+1); - sLog.outDetail( "HandlePetRename. CMSG_PET_RENAME\n" ); + sLog.outDetail( "HandlePetRename. CMSG_PET_RENAME" ); uint64 petguid; uint8 isdeclined; @@ -477,7 +489,7 @@ void WorldSession::HandlePetAbandon( WorldPacket & recv_data ) sLog.outDetail( "HandlePetAbandon. CMSG_PET_ABANDON pet guid is %u", GUID_LOPART(guid) ); // pet/charmed - Creature* pet = ObjectAccessor::GetCreatureOrPet(*_player, guid); + Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid); if(pet) { if(pet->isPet()) @@ -491,7 +503,7 @@ void WorldSession::HandlePetAbandon( WorldPacket & recv_data ) _player->RemovePet((Pet*)pet,PET_SAVE_AS_DELETED); } else if(pet->GetGUID() == _player->GetCharmGUID()) - _player->Uncharm(); + _player->StopCastingCharm(); } } @@ -501,16 +513,16 @@ void WorldSession::HandlePetUnlearnOpcode(WorldPacket& recvPacket) sLog.outDetail("CMSG_PET_UNLEARN"); uint64 guid; - recvPacket >> guid; + recvPacket >> guid; // Pet guid Pet* pet = _player->GetPet(); - if(!pet || pet->getPetType() != HUNTER_PET || pet->m_spells.size() <= 1) + if(!pet || pet->getPetType() != HUNTER_PET || pet->m_usedTalentCount == 0) return; if(guid != pet->GetGUID()) { - sLog.outError( "HandlePetUnlearnOpcode.Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); + sLog.outError( "HandlePetUnlearnOpcode.Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); return; } @@ -520,38 +532,7 @@ void WorldSession::HandlePetUnlearnOpcode(WorldPacket& recvPacket) sLog.outError("WorldSession::HandlePetUnlearnOpcode: object "I64FMTD" is considered pet-like but doesn't have a charminfo!", pet->GetGUID()); return; } - - uint32 cost = pet->resetTalentsCost(); - - if (GetPlayer()->GetMoney() < cost) - { - GetPlayer()->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0); - return; - } - - for(PetSpellMap::iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end();) - { - uint32 spell_id = itr->first; // Pet::removeSpell can invalidate iterator at erase NEW spell - ++itr; - pet->removeSpell(spell_id); - } - - pet->SetTP(pet->getLevel() * (pet->GetLoyaltyLevel() - 1)); - - for(uint8 i = 0; i < 10; i++) - { - if(charmInfo->GetActionBarEntry(i)->SpellOrAction && charmInfo->GetActionBarEntry(i)->Type == ACT_ENABLED || charmInfo->GetActionBarEntry(i)->Type == ACT_DISABLED) - charmInfo->GetActionBarEntry(i)->SpellOrAction = 0; - } - - // relearn pet passives - pet->LearnPetPassives(); - - pet->m_resetTalentsTime = time(NULL); - pet->m_resetTalentsCost = cost; - GetPlayer()->ModifyMoney(-(int32)cost); - - GetPlayer()->PetSpellInitialize(); + pet->resetTalents(); } void WorldSession::HandlePetSpellAutocastOpcode( WorldPacket& recvPacket ) @@ -565,22 +546,22 @@ void WorldSession::HandlePetSpellAutocastOpcode( WorldPacket& recvPacket ) uint8 state; //1 for on, 0 for off recvPacket >> guid >> spellid >> spellid2 >> state; - if(!_player->GetPet() && !_player->GetCharm()) + if(!_player->GetGuardianPet() && !_player->GetCharm()) return; if(ObjectAccessor::FindPlayer(guid)) return; - Creature* pet=ObjectAccessor::GetCreatureOrPet(*_player,guid); + Creature* pet=ObjectAccessor::GetCreatureOrPetOrVehicle(*_player,guid); - if(!pet || (pet != _player->GetPet() && pet != _player->GetCharm())) + if(!pet || (pet != _player->GetGuardianPet() && pet != _player->GetCharm())) { - sLog.outError( "HandlePetSpellAutocastOpcode.Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); + sLog.outError( "HandlePetSpellAutocastOpcode.Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); return; } // do not add not learned spells/ passive spells - if(!pet->HasSpell(spellid) || IsPassiveSpell(spellid)) + if(!pet->HasSpell(spellid) || IsAutocastableSpell(spellid)) return; CharmInfo *charmInfo = pet->GetCharmInfo(); @@ -590,11 +571,10 @@ void WorldSession::HandlePetSpellAutocastOpcode( WorldPacket& recvPacket ) return; } - if(pet->isCharmed()) - //state can be used as boolean - pet->GetCharmInfo()->ToggleCreatureAutocast(spellid, state); - else + if(pet->isPet()) ((Pet*)pet)->ToggleAutocast(spellid, state); + else + pet->GetCharmInfo()->ToggleCreatureAutocast(spellid, state); for(uint8 i = 0; i < 10; ++i) { @@ -607,21 +587,25 @@ void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) { sLog.outDetail("WORLD: CMSG_PET_CAST_SPELL"); - CHECK_PACKET_SIZE(recvPacket,8+4); + CHECK_PACKET_SIZE(recvPacket,8+1+4+1); uint64 guid; uint32 spellid; + uint8 cast_count; + uint8 unk_flags; // flags (if 0x02 - some additional data are received) + + recvPacket >> guid >> cast_count >> spellid >> unk_flags; - recvPacket >> guid >> spellid; + sLog.outDebug("WORLD: CMSG_PET_CAST_SPELL, cast_count: %u, spellid %u, unk_flags %u", cast_count, spellid, unk_flags); // This opcode is also sent from charmed and possessed units (players and creatures) - if(!_player->GetPet() && !_player->GetCharm()) + if(!_player->GetGuardianPet() && !_player->GetCharm()) return; Unit* caster = ObjectAccessor::GetUnit(*_player, guid); - if(!caster || (caster != _player->GetPet() && caster != _player->GetCharm())) + if(!caster || (caster != _player->GetGuardianPet() && caster != _player->GetCharm())) { - sLog.outError( "HandlePetCastSpellOpcode: Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); + sLog.outError( "HandlePetCastSpellOpcode: Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); return; } @@ -631,7 +615,7 @@ void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid); if(!spellInfo) { - sLog.outError("WORLD: unknown PET spell id %i\n", spellid); + sLog.outError("WORLD: unknown PET spell id %i", spellid); return; } @@ -646,10 +630,11 @@ void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) caster->clearUnitState(UNIT_STAT_FOLLOW); Spell *spell = new Spell(caster, spellInfo, false); + spell->m_cast_count = cast_count; // probably pending spell cast spell->m_targets = targets; - int16 result = spell->PetCanCast(NULL); - if(result == -1) + SpellCastResult result = spell->CheckPetCast(NULL); + if(result == SPELL_CAST_OK) { if(caster->GetTypeId() == TYPEID_UNIT) { @@ -705,3 +690,15 @@ void WorldSession::SendPetNameInvalid(uint32 error, const std::string& name, Dec SendPacket(&data); } +void WorldSession::HandlePetLearnTalent( WorldPacket & recv_data ) +{ + sLog.outDebug("WORLD: CMSG_PET_LEARN_TALENT"); + + CHECK_PACKET_SIZE(recv_data, 8+4+4); + + uint64 guid; + uint32 talent_id, requested_rank; + recv_data >> guid >> talent_id >> requested_rank; + + _player->LearnPetTalent(guid, talent_id, requested_rank); +} diff --git a/src/game/PetitionsHandler.cpp b/src/game/PetitionsHandler.cpp index 7ae368140c7..61485c3eb04 100644 --- a/src/game/PetitionsHandler.cpp +++ b/src/game/PetitionsHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,7 +28,6 @@ #include "Opcodes.h" #include "Guild.h" #include "ArenaTeam.h" -#include "MapManager.h" #include "GossipDef.h" #include "SocialMgr.h" @@ -92,7 +91,7 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket & recv_data) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); uint32 charterid = 0; uint32 cost = 0; @@ -199,9 +198,9 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket & recv_data) if(!charter) return; - charter->SetUInt32Value(ITEM_FIELD_ENCHANTMENT, charter->GetGUIDLow()); - // ITEM_FIELD_ENCHANTMENT is guild/arenateam id - // ITEM_FIELD_ENCHANTMENT+1 is current signatures count (showed on item) + charter->SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1, charter->GetGUIDLow()); + // ITEM_FIELD_ENCHANTMENT_1_1 is guild/arenateam id + // ITEM_FIELD_ENCHANTMENT_1_1+1 is current signatures count (showed on item) charter->SetState(ITEM_CHANGED, _player); _player->SendNewItem(charter, 1, true, false); @@ -255,7 +254,7 @@ void WorldSession::HandlePetitionShowSignOpcode(WorldPacket & recv_data) QueryResult *result = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", petitionguid_low); if(!result) { - sLog.outError("any petition on server..."); + sLog.outError("Petition %u is not found for player %u %s", GUID_LOPART(petitionguid), GetPlayer()->GetGUIDLow(), GetPlayer()->GetName()); return; } Field *fields = result->Fetch(); @@ -463,7 +462,7 @@ void WorldSession::HandlePetitionSignOpcode(WorldPacket & recv_data) if(!result) { - sLog.outError("any petition on server..."); + sLog.outError("Petition %u is not found for player %u %s", GUID_LOPART(petitionguid), GetPlayer()->GetGUIDLow(), GetPlayer()->GetName()); return; } @@ -565,7 +564,7 @@ void WorldSession::HandlePetitionSignOpcode(WorldPacket & recv_data) // update signs count on charter, required testing... //Item *item = _player->GetItemByGuid(petitionguid)); //if(item) - // item->SetUInt32Value(ITEM_FIELD_ENCHANTMENT+1, signs); + // item->SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1+1, signs); // update for owner if online if(Player *owner = objmgr.GetPlayer(ownerguid)) @@ -827,7 +826,7 @@ void WorldSession::HandleTurnInPetitionOpcode(WorldPacket & recv_data) if(type == 9) // create guild { Guild* guild = new Guild; - if(!guild->create(_player->GetGUID(), name)) + if(!guild->create(_player, name)) { delete guild; delete result; @@ -916,7 +915,7 @@ void WorldSession::SendPetitionShowList(uint64 guid) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); uint8 count = 0; if(pCreature->isTabardDesigner()) diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 0078ed535d3..65a3adae6b8 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,13 +23,13 @@ #include "Database/DatabaseEnv.h" #include "Log.h" #include "Opcodes.h" -#include "ObjectMgr.h" #include "SpellMgr.h" #include "World.h" #include "WorldPacket.h" #include "WorldSession.h" #include "UpdateMask.h" #include "Player.h" +#include "Vehicle.h" #include "SkillDiscovery.h" #include "QuestDef.h" #include "GossipDef.h" @@ -49,7 +49,6 @@ #include "Group.h" #include "Guild.h" #include "Pet.h" -#include "SpellAuras.h" #include "Util.h" #include "Transports.h" #include "Weather.h" @@ -63,11 +62,13 @@ #include "Database/DatabaseImpl.h" #include "Spell.h" #include "SocialMgr.h" -#include "GameEvent.h" +#include "GameEventMgr.h" +#include "AchievementMgr.h" +#include "SpellAuras.h" #include <cmath> -#define ZONE_UPDATE_INTERVAL 1000 +#define ZONE_UPDATE_INTERVAL (1*IN_MILISECONDS) #define PLAYER_SKILL_INDEX(x) (PLAYER_SKILL_INFO_1_1 + ((x)*3)) #define PLAYER_SKILL_VALUE_INDEX(x) (PLAYER_SKILL_INDEX(x)+1) @@ -132,9 +133,20 @@ PlayerTaxi::PlayerTaxi() memset(m_taximask, 0, sizeof(m_taximask)); } -void PlayerTaxi::InitTaxiNodesForLevel(uint32 race, uint32 level) +void PlayerTaxi::InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint32 level) { - // capital and taxi hub masks + // class specific initial known nodes + switch(chrClass) + { + case CLASS_DEATH_KNIGHT: + { + for(int i = 0; i < TaxiMaskSize; ++i) + m_taximask[i] |= sOldContinentsNodesMask[i]; + break; + } + } + + // race specific initial known nodes: capital and taxi hub masks switch(race) { case RACE_HUMAN: SetTaximaskNode(2); break; // Human @@ -149,6 +161,7 @@ void PlayerTaxi::InitTaxiNodesForLevel(uint32 race, uint32 level) case RACE_BLOODELF: SetTaximaskNode(82); break; // Blood Elf case RACE_DRAENEI: SetTaximaskNode(94); break; // Draenei } + // new continent starting masks (It will be accessible only at new map) switch(Player::TeamForRace(race)) { @@ -188,7 +201,7 @@ void PlayerTaxi::AppendTaximaskTo( ByteBuffer& data, bool all ) } } -bool PlayerTaxi::LoadTaxiDestinationsFromString( const std::string& values ) +bool PlayerTaxi::LoadTaxiDestinationsFromString( const std::string& values, uint32 team ) { ClearTaxiDestinations(); @@ -216,6 +229,10 @@ bool PlayerTaxi::LoadTaxiDestinationsFromString( const std::string& values ) return false; } + // can't load taxi path without mount set (quest taxi path?) + if(!objmgr.GetTaxiMount(GetTaxiSource(),team)) + return false; + return true; } @@ -245,13 +262,20 @@ uint32 PlayerTaxi::GetCurrentTaxiPath() const return path; } -//== Player ==================================================== +std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi) +{ + ss << "'"; + for(int i = 0; i < TaxiMaskSize; ++i) + ss << taxi.m_taximask[i] << " "; + ss << "'"; + return ss; +} -const int32 Player::ReputationRank_Length[MAX_REPUTATION_RANK] = {36000, 3000, 3000, 3000, 6000, 12000, 21000, 1000}; +//== Player ==================================================== UpdateMask Player::updateVisualBits; -Player::Player (WorldSession *session): Unit() +Player::Player (WorldSession *session): Unit(), m_achievementMgr(this), m_reputationMgr(this) { m_transport = 0; @@ -280,6 +304,7 @@ Player::Player (WorldSession *session): Unit() m_comboPoints = 0; m_usedTalentCount = 0; + m_questRewardTalentCount = 0; m_regenTimer = 0; m_weaponChangeTimer = 0; @@ -306,7 +331,7 @@ Player::Player (WorldSession *session): Unit() // group is initialized in the reference constructor SetGroupInvite(NULL); m_groupUpdateMask = 0; - m_auraUpdateMask = 0; + m_auraRaidUpdateMask = 0; duel = NULL; @@ -315,7 +340,8 @@ Player::Player (WorldSession *session): Unit() m_atLoginFlags = AT_LOGIN_NONE; - m_dontMove = false; + mSemaphoreTeleport_Near = false; + mSemaphoreTeleport_Far = false; pTrader = 0; ClearTrade(); @@ -332,10 +358,11 @@ Player::Player (WorldSession *session): Unit() m_DailyQuestChanged = false; m_lastDailyQuestTime = 0; - m_regenTimer = 0; - m_weaponChangeTimer = 0; - m_breathTimer = 0; - m_isunderwater = 0; + for (int i=0; i<MAX_TIMERS; i++) + m_MirrorTimer[i] = DISABLED_MIRROR_TIMER; + + m_MirrorTimerFlags = UNDERWATER_NONE; + m_MirrorTimerFlagsLast = UNDERWATER_NONE; m_isInWater = false; m_drunkTimer = 0; m_drunk = 0; @@ -345,12 +372,13 @@ Player::Player (WorldSession *session): Unit() m_swingErrorMsg = 0; - m_DetectInvTimer = 1000; + m_DetectInvTimer = 1*IN_MILISECONDS; m_bgBattleGroundID = 0; + m_bgTypeID = BATTLEGROUND_TYPE_NONE; for (int j=0; j < PLAYER_MAX_BATTLEGROUND_QUEUES; j++) { - m_bgBattleGroundQueueID[j].bgQueueType = 0; + m_bgBattleGroundQueueID[j].bgQueueTypeId = BATTLEGROUND_QUEUE_NONE; m_bgBattleGroundQueueID[j].invitedToInstance = 0; } m_bgTeam = 0; @@ -362,6 +390,7 @@ Player::Player (WorldSession *session): Unit() m_canParry = false; m_canBlock = false; m_canDualWield = false; + m_canTitanGrip = false; m_ammoDPS = 0.0f; m_temporaryUnsummonedPetNumber = 0; @@ -400,12 +429,22 @@ Player::Player (WorldSession *session): Unit() m_InstanceValid = true; m_dungeonDifficulty = DIFFICULTY_NORMAL; + m_lastPotionId = 0; + for (int i = 0; i < BASEMOD_END; i++) { m_auraBaseMod[i][FLAT_MOD] = 0.0f; m_auraBaseMod[i][PCT_MOD] = 1.0f; } + for (int i = 0; i < MAX_COMBAT_RATING; i++) + m_baseRatingValue[i] = 0; + + m_baseSpellDamage = 0; + m_baseSpellHealing = 0; + m_baseFeralAP = 0; + m_baseManaRegen = 0; + // Honor System m_lastHonorUpdateTime = time(NULL); @@ -419,7 +458,9 @@ Player::Player (WorldSession *session): Unit() //Default movement to run mode m_unit_movement_flags = 0; - m_miniPet = 0; + m_mover = this; + m_seer = this; + m_bgAfkReportedTimer = 0; m_contestedPvPTimer = 0; @@ -427,13 +468,14 @@ Player::Player (WorldSession *session): Unit() m_isActive = true; - m_farsightVision = false; + m_runes = NULL; + + m_lastFallTime = 0; + m_lastFallZ = 0; } Player::~Player () { - CleanupsBeforeDelete(); - // it must be unloaded already in PlayerLogout and accessed only for loggined player //m_social = NULL; @@ -443,7 +485,6 @@ Player::~Player () if(m_items[i]) delete m_items[i]; } - CleanupChannels(); for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr) delete itr->second; @@ -457,31 +498,28 @@ Player::~Player () delete PlayerTalkClass; - if (m_transport) - { - m_transport->RemovePassenger(this); - } - for(size_t x = 0; x < ItemSetEff.size(); x++) if(ItemSetEff[x]) delete ItemSetEff[x]; - // clean up player-instance binds, may unload some instance saves - for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++) - for(BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr) - itr->second.save->RemovePlayer(this); - delete m_declinedname; + delete m_runes; } void Player::CleanupsBeforeDelete() { - if(m_uint32Values) // only for fully created Object - { - TradeCancel(false); - DuelComplete(DUEL_INTERUPTED); - } + TradeCancel(false); + DuelComplete(DUEL_INTERUPTED); + Unit::CleanupsBeforeDelete(); + + if (m_transport) + m_transport->RemovePassenger(this); + + // clean up player-instance binds, may unload some instance saves + for(uint8 i = 0; i < TOTAL_DIFFICULTIES; ++i) + for(BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr) + itr->second.save->RemovePlayer(this); } bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 class_, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair, uint8 outfitId ) @@ -517,24 +555,8 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 uint8 powertype = cEntry->powerType; - uint32 unitfield; - - switch(powertype) - { - case POWER_ENERGY: - case POWER_MANA: - unitfield = 0x00000000; - break; - case POWER_RAGE: - unitfield = 0x00110000; - break; - default: - sLog.outError("Invalid default powertype %u for player (class %u)",powertype,class_); - return false; - } - - SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE ); - SetFloatValue(UNIT_FIELD_COMBATREACH, DEFAULT_COMBAT_REACH ); + SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE); + SetFloatValue(UNIT_FIELD_COMBATREACH, 1.5f); switch(gender) { @@ -557,12 +579,13 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 uint32 RaceClassGender = ( race ) | ( class_ << 8 ) | ( gender << 16 ); SetUInt32Value(UNIT_FIELD_BYTES_0, ( RaceClassGender | ( powertype << 24 ) ) ); - SetUInt32Value(UNIT_FIELD_BYTES_1, unitfield); - SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY | UNIT_BYTE2_FLAG_UNK5 ); - SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE ); + 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); SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f); // fix cast time showed in spell tooltip on client + SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f); // default for players in 3.0.3 - //-1 is default value + // -1 is default value SetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, uint32(-1)); SetUInt32Value(PLAYER_BYTES, (skin | (face << 8) | (hairStyle << 16) | (hairColor << 24))); @@ -574,6 +597,7 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 SetUInt32Value( PLAYER_GUILD_TIMESTAMP, 0 ); SetUInt64Value( PLAYER__FIELD_KNOWN_TITLES, 0 ); // 0=disabled + SetUInt64Value( PLAYER__FIELD_KNOWN_TITLES1, 0 ); // 0=disabled SetUInt32Value( PLAYER_CHOSEN_TITLE, 0 ); SetUInt32Value( PLAYER_FIELD_KILLS, 0 ); SetUInt32Value( PLAYER_FIELD_LIFETIME_HONORBALE_KILLS, 0 ); @@ -581,10 +605,20 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 SetUInt32Value( PLAYER_FIELD_YESTERDAY_CONTRIBUTION, 0 ); // set starting level + uint32 start_level = getClass() != CLASS_DEATH_KNIGHT + ? sWorld.getConfig(CONFIG_START_PLAYER_LEVEL) + : sWorld.getConfig(CONFIG_START_HEROIC_PLAYER_LEVEL); + if (GetSession()->GetSecurity() >= SEC_MODERATOR) - SetUInt32Value (UNIT_FIELD_LEVEL, sWorld.getConfig(CONFIG_START_GM_LEVEL)); - else - SetUInt32Value (UNIT_FIELD_LEVEL, sWorld.getConfig(CONFIG_START_PLAYER_LEVEL)); + { + uint32 gm_level = sWorld.getConfig(CONFIG_START_GM_LEVEL); + if(gm_level > start_level) + start_level = gm_level; + } + + SetUInt32Value(UNIT_FIELD_LEVEL, start_level); + + InitRunes(); SetUInt32Value (PLAYER_FIELD_COINAGE, sWorld.getConfig(CONFIG_START_PLAYER_MONEY)); SetUInt32Value (PLAYER_FIELD_HONOR_CURRENCY, sWorld.getConfig(CONFIG_START_HONOR_POINTS)); @@ -600,40 +634,40 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 //Reputations if "StartAllReputation" is enabled, -- TODO: Fix this in a better way if(sWorld.getConfig(CONFIG_START_ALL_REP)) { - SetFactionReputation(sFactionStore.LookupEntry(942),42999); - SetFactionReputation(sFactionStore.LookupEntry(935),42999); - SetFactionReputation(sFactionStore.LookupEntry(936),42999); - SetFactionReputation(sFactionStore.LookupEntry(1011),42999); - SetFactionReputation(sFactionStore.LookupEntry(970),42999); - SetFactionReputation(sFactionStore.LookupEntry(967),42999); - SetFactionReputation(sFactionStore.LookupEntry(989),42999); - SetFactionReputation(sFactionStore.LookupEntry(932),42999); - SetFactionReputation(sFactionStore.LookupEntry(934),42999); - SetFactionReputation(sFactionStore.LookupEntry(1038),42999); - SetFactionReputation(sFactionStore.LookupEntry(1077),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(942),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(935),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(936),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(1011),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(970),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(967),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(989),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(932),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(934),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(1038),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(1077),42999); // Factions depending on team, like cities and some more stuff switch(GetTeam()) { case ALLIANCE: - SetFactionReputation(sFactionStore.LookupEntry(72),42999); - SetFactionReputation(sFactionStore.LookupEntry(47),42999); - SetFactionReputation(sFactionStore.LookupEntry(69),42999); - SetFactionReputation(sFactionStore.LookupEntry(930),42999); - SetFactionReputation(sFactionStore.LookupEntry(730),42999); - SetFactionReputation(sFactionStore.LookupEntry(978),42999); - SetFactionReputation(sFactionStore.LookupEntry(54),42999); - SetFactionReputation(sFactionStore.LookupEntry(946),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(72),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(47),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(69),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(930),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(730),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(978),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(54),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(946),42999); break; case HORDE: - SetFactionReputation(sFactionStore.LookupEntry(76),42999); - SetFactionReputation(sFactionStore.LookupEntry(68),42999); - SetFactionReputation(sFactionStore.LookupEntry(81),42999); - SetFactionReputation(sFactionStore.LookupEntry(911),42999); - SetFactionReputation(sFactionStore.LookupEntry(729),42999); - SetFactionReputation(sFactionStore.LookupEntry(941),42999); - SetFactionReputation(sFactionStore.LookupEntry(530),42999); - SetFactionReputation(sFactionStore.LookupEntry(947),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(76),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(68),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(81),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(911),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(729),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(941),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(530),42999); + GetReputationMgr().SetReputation(sFactionStore.LookupEntry(947),42999); break; default: break; @@ -648,6 +682,7 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 // base stats and related field values InitStatsForLevel(); InitTaxiNodesForLevel(); + InitGlyphsForLevel(); InitTalentForLevel(); InitPrimaryProffesions(); // to max set before any spell added @@ -660,8 +695,16 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 SetPower(POWER_MANA,GetMaxPower(POWER_MANA)); } + if(getPowerType() == POWER_RUNIC_POWER) + { + SetPower(POWER_RUNE, 8); + SetMaxPower(POWER_RUNE, 8); + SetPower(POWER_RUNIC_POWER, 0); + SetMaxPower(POWER_RUNIC_POWER, 1000); + } + // original spells - learnDefaultSpells(true); + learnDefaultSpells(); // original action bar std::list<uint16>::const_iterator action_itr[4]; @@ -703,6 +746,11 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 uint32 item_id = oEntry->ItemId[j]; + + // Hack for not existed item id in dbc 3.0.3 + if(item_id==40582) + continue; + ItemPrototype const* iProto = objmgr.GetItemPrototype(item_id); if(!iProto) { @@ -710,7 +758,9 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 continue; } - uint32 count = iProto->Stackable; // max stack by default (mostly 1) + // max stack by default (mostly 1), 1 for infinity stackable + uint32 count = iProto->Stackable > 0 ? uint32(iProto->Stackable) : 1; + if(iProto->Class==ITEM_CLASS_CONSUMABLE && iProto->SubClass==ITEM_SUBCLASS_FOOD) { switch(iProto->Spells[0].SpellCategory) @@ -760,9 +810,9 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 } // if this is ammo then use it - uint8 msg = CanUseAmmo( pItem->GetProto()->ItemId ); + uint8 msg = CanUseAmmo( pItem->GetEntry() ); if( msg == EQUIP_ERR_OK ) - SetAmmo( pItem->GetProto()->ItemId ); + SetAmmo( pItem->GetEntry() ); } } } @@ -806,25 +856,14 @@ bool Player::StoreNewItemInBestSlots(uint32 titem_id, uint32 titem_amount) return false; } -void Player::StartMirrorTimer(MirrorTimerType Type, uint32 MaxValue) +void Player::SendMirrorTimer(MirrorTimerType Type, uint32 MaxValue, uint32 CurrentValue, int32 Regen) { - uint32 BreathRegen = (uint32)-1; - - WorldPacket data(SMSG_START_MIRROR_TIMER, (21)); - data << (uint32)Type; - data << MaxValue; - data << MaxValue; - data << BreathRegen; - data << (uint8)0; - data << (uint32)0; // spell id - GetSession()->SendPacket(&data); -} - -void Player::ModifyMirrorTimer(MirrorTimerType Type, uint32 MaxValue, uint32 CurrentValue, uint32 Regen) -{ - if(Type==BREATH_TIMER) - m_breathTimer = ((MaxValue + 1000) - CurrentValue) / Regen; - + if (MaxValue == DISABLED_MIRROR_TIMER) + { + if (CurrentValue!=DISABLED_MIRROR_TIMER) + StopMirrorTimer(Type); + return; + } WorldPacket data(SMSG_START_MIRROR_TIMER, (21)); data << (uint32)Type; data << CurrentValue; @@ -837,150 +876,199 @@ void Player::ModifyMirrorTimer(MirrorTimerType Type, uint32 MaxValue, uint32 Cur void Player::StopMirrorTimer(MirrorTimerType Type) { - if(Type==BREATH_TIMER) - m_breathTimer = 0; - + m_MirrorTimer[Type] = DISABLED_MIRROR_TIMER; WorldPacket data(SMSG_STOP_MIRROR_TIMER, 4); data << (uint32)Type; GetSession()->SendPacket( &data ); } -void Player::EnvironmentalDamage(uint64 guid, EnviromentalDamage type, uint32 damage) +void Player::EnvironmentalDamage(EnviromentalDamage type, uint32 damage) { + if(!isAlive() || isGameMaster()) + return; + + // Absorb, resist some environmental damage type + uint32 absorb = 0; + uint32 resist = 0; + if (type == DAMAGE_LAVA) + CalcAbsorbResist(this, SPELL_SCHOOL_MASK_FIRE, DIRECT_DAMAGE, damage, &absorb, &resist); + else if (type == DAMAGE_SLIME) + CalcAbsorbResist(this, SPELL_SCHOOL_MASK_NATURE, DIRECT_DAMAGE, damage, &absorb, &resist); + + damage-=absorb+resist; + WorldPacket data(SMSG_ENVIRONMENTALDAMAGELOG, (21)); - data << (uint64)guid; - data << (uint8)(type!=DAMAGE_FALL_TO_VOID ? type : DAMAGE_FALL); - data << (uint32)damage; - data << (uint32)0; - data << (uint32)0; + data << uint64(GetGUID()); + data << uint8(type!=DAMAGE_FALL_TO_VOID ? type : DAMAGE_FALL); + data << uint32(damage); + data << uint32(absorb); + data << uint32(resist); SendMessageToSet(&data, true); DealDamage(this, damage, NULL, SELF_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); - if(type==DAMAGE_FALL && !isAlive()) // DealDamage not apply item durability loss at self damage + if(!isAlive()) { - DEBUG_LOG("We are fall to death, loosing 10 percents durability"); - DurabilityLossAll(0.10f,false); - // durability lost message - WorldPacket data(SMSG_DURABILITY_DAMAGE_DEATH, 0); - GetSession()->SendPacket(&data); + if(type==DAMAGE_FALL) // DealDamage not apply item durability loss at self damage + { + DEBUG_LOG("We are fall to death, loosing 10 percents durability"); + DurabilityLossAll(0.10f,false); + // durability lost message + WorldPacket data(SMSG_DURABILITY_DAMAGE_DEATH, 0); + GetSession()->SendPacket(&data); + } + + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM, 1, type); } } -void Player::HandleDrowning() +int32 Player::getMaxTimer(MirrorTimerType timer) { - if(!m_isunderwater) - return; - - //if player is GM, have waterbreath, is dead or if breathing is disabled then return - if(waterbreath || isGameMaster() || !isAlive() || GetSession()->GetSecurity() >= sWorld.getConfig(CONFIG_DISABLE_BREATHING)) + switch (timer) { - StopMirrorTimer(BREATH_TIMER); - m_isunderwater = 0; - return; + case FATIGUE_TIMER: + return MINUTE*IN_MILISECONDS; + case BREATH_TIMER: + { + if (!isAlive() || HasAuraType(SPELL_AURA_WATER_BREATHING) || GetSession()->GetSecurity() >= sWorld.getConfig(CONFIG_DISABLE_BREATHING)) + return DISABLED_MIRROR_TIMER; + int32 UnderWaterTime = 3*MINUTE*IN_MILISECONDS; + AuraEffectList const& mModWaterBreathing = GetAurasByType(SPELL_AURA_MOD_WATER_BREATHING); + for(AuraEffectList::const_iterator i = mModWaterBreathing.begin(); i != mModWaterBreathing.end(); ++i) + UnderWaterTime = uint32(UnderWaterTime * (100.0f + (*i)->GetAmount()) / 100.0f); + return UnderWaterTime; + } + case FIRE_TIMER: + { + if (!isAlive()) + return DISABLED_MIRROR_TIMER; + return 1*IN_MILISECONDS; + } + default: + return 0; } + return 0; +} - uint32 UnderWaterTime = 1*MINUTE*1000; // default length 1 min +void Player::UpdateMirrorTimers() +{ + // Desync flags for update on next HandleDrowning + if (m_MirrorTimerFlags) + m_MirrorTimerFlagsLast = ~m_MirrorTimerFlags; +} - AuraList const& mModWaterBreathing = GetAurasByType(SPELL_AURA_MOD_WATER_BREATHING); - for(AuraList::const_iterator i = mModWaterBreathing.begin(); i != mModWaterBreathing.end(); ++i) - UnderWaterTime = uint32(UnderWaterTime * (100.0f + (*i)->GetModifierValue()) / 100.0f); +void Player::HandleDrowning(uint32 time_diff) +{ + if (!m_MirrorTimerFlags) + return; - if ((m_isunderwater & 0x01) && !(m_isunderwater & 0x80) && isAlive()) + // In water + if (m_MirrorTimerFlags & UNDERWATER_INWATER) { - //single trigger timer - if (!(m_isunderwater & 0x02)) - { - m_isunderwater|= 0x02; - m_breathTimer = UnderWaterTime + 1000; - } - //single trigger "Breathbar" - if ( m_breathTimer <= UnderWaterTime && !(m_isunderwater & 0x04)) + // Breath timer not activated - activate it + if (m_MirrorTimer[BREATH_TIMER] == DISABLED_MIRROR_TIMER) { - m_isunderwater|= 0x04; - StartMirrorTimer(BREATH_TIMER, UnderWaterTime); + m_MirrorTimer[BREATH_TIMER] = getMaxTimer(BREATH_TIMER); + SendMirrorTimer(BREATH_TIMER, m_MirrorTimer[BREATH_TIMER], m_MirrorTimer[BREATH_TIMER], -1); } - //continuous trigger drowning "Damage" - if ((m_breathTimer == 0) && (m_isunderwater & 0x01)) + else // If activated - do tick { - //TODO: Check this formula - uint64 guid = GetGUID(); - uint32 damage = GetMaxHealth() / 5 + urand(0, getLevel()-1); - - EnvironmentalDamage(guid, DAMAGE_DROWNING,damage); - m_breathTimer = 2000; + m_MirrorTimer[BREATH_TIMER]-=time_diff; + // Timer limit - need deal damage + if (m_MirrorTimer[BREATH_TIMER] < 0) + { + m_MirrorTimer[BREATH_TIMER]+= 1*IN_MILISECONDS; + // Calculate and deal damage + // TODO: Check this formula + uint32 damage = GetMaxHealth() / 5 + urand(0, getLevel()-1); + EnvironmentalDamage(DAMAGE_DROWNING, damage); + } + else if (!(m_MirrorTimerFlagsLast & UNDERWATER_INWATER)) // Update time in client if need + SendMirrorTimer(BREATH_TIMER, getMaxTimer(BREATH_TIMER), m_MirrorTimer[BREATH_TIMER], -1); } } - //single trigger retract bar - else if (!(m_isunderwater & 0x01) && !(m_isunderwater & 0x08) && (m_isunderwater & 0x02) && (m_breathTimer > 0) && isAlive()) - { - m_isunderwater = 0x08; - - uint32 BreathRegen = 10; - ModifyMirrorTimer(BREATH_TIMER, UnderWaterTime, m_breathTimer,BreathRegen); - m_isunderwater = 0x10; - } - //remove bar - else if ((m_breathTimer < 50) && !(m_isunderwater & 0x01) && (m_isunderwater == 0x10)) + else if (m_MirrorTimer[BREATH_TIMER] != DISABLED_MIRROR_TIMER) // Regen timer { - StopMirrorTimer(BREATH_TIMER); - m_isunderwater = 0; + int32 UnderWaterTime = getMaxTimer(BREATH_TIMER); + // Need breath regen + m_MirrorTimer[BREATH_TIMER]+=10*time_diff; + if (m_MirrorTimer[BREATH_TIMER] >= UnderWaterTime || !isAlive()) + StopMirrorTimer(BREATH_TIMER); + else if (m_MirrorTimerFlagsLast & UNDERWATER_INWATER) + SendMirrorTimer(BREATH_TIMER, UnderWaterTime, m_MirrorTimer[BREATH_TIMER], 10); } -} - -void Player::HandleLava() -{ - bool ValidArea = false; - if ((m_isunderwater & 0x80) && isAlive()) + // In dark water + if (m_MirrorTimerFlags & UNDERWARER_INDARKWATER) { - //Single trigger Set BreathTimer - if (!(m_isunderwater & 0x80)) + // Fatigue timer not activated - activate it + if (m_MirrorTimer[FATIGUE_TIMER] == DISABLED_MIRROR_TIMER) { - m_isunderwater|= 0x04; - m_breathTimer = 1000; + m_MirrorTimer[FATIGUE_TIMER] = getMaxTimer(FATIGUE_TIMER); + SendMirrorTimer(FATIGUE_TIMER, m_MirrorTimer[FATIGUE_TIMER], m_MirrorTimer[FATIGUE_TIMER], -1); } - //Reset BreathTimer and still in the lava - if (!m_breathTimer) + else { - uint64 guid = GetGUID(); - uint32 damage = urand(600, 700); // TODO: Get more detailed information about lava damage - uint32 dmgZone = GetZoneId(); // TODO: Find correct "lava dealing zone" flag in Area Table - - // Deal lava damage only in lava zones. - switch(dmgZone) + m_MirrorTimer[FATIGUE_TIMER]-=time_diff; + // Timer limit - need deal damage or teleport ghost to graveyard + if (m_MirrorTimer[FATIGUE_TIMER] < 0) { - case 0x8D: - ValidArea = false; - break; - case 0x94: - ValidArea = false; - break; - case 0x2CE: - ValidArea = false; - break; - case 0x2CF: - ValidArea = false; - break; - default: - if (dmgZone / 5 & 0x408) - ValidArea = true; + m_MirrorTimer[FATIGUE_TIMER]+= 1*IN_MILISECONDS; + if (isAlive()) // Calculate and deal damage + { + uint32 damage = GetMaxHealth() / 5 + urand(0, getLevel()-1); + EnvironmentalDamage(DAMAGE_EXHAUSTED, damage); + } + else if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) // Teleport ghost to graveyard + RepopAtGraveyard(); } - - // if is valid area and is not gamemaster then deal damage - if ( ValidArea && !isGameMaster() ) - EnvironmentalDamage(guid, DAMAGE_LAVA, damage); - - m_breathTimer = 1000; + else if (!(m_MirrorTimerFlagsLast & UNDERWARER_INDARKWATER)) + SendMirrorTimer(FATIGUE_TIMER, getMaxTimer(FATIGUE_TIMER), m_MirrorTimer[FATIGUE_TIMER], -1); } - } - //Death timer disabled and WaterFlags reset - else if (m_deathState == DEAD) + else if (m_MirrorTimer[FATIGUE_TIMER] != DISABLED_MIRROR_TIMER) // Regen timer { - m_breathTimer = 0; - m_isunderwater = 0; + int32 DarkWaterTime = getMaxTimer(FATIGUE_TIMER); + m_MirrorTimer[FATIGUE_TIMER]+=10*time_diff; + if (m_MirrorTimer[FATIGUE_TIMER] >= DarkWaterTime || !isAlive()) + StopMirrorTimer(FATIGUE_TIMER); + else if (m_MirrorTimerFlagsLast & UNDERWARER_INDARKWATER) + SendMirrorTimer(FATIGUE_TIMER, DarkWaterTime, m_MirrorTimer[FATIGUE_TIMER], 10); + } + + if (m_MirrorTimerFlags & (UNDERWATER_INLAVA|UNDERWATER_INSLIME)) + { + // Breath timer not activated - activate it + if (m_MirrorTimer[FIRE_TIMER] == DISABLED_MIRROR_TIMER) + m_MirrorTimer[FIRE_TIMER] = getMaxTimer(FIRE_TIMER); + else + { + m_MirrorTimer[FIRE_TIMER]-=time_diff; + if (m_MirrorTimer[FIRE_TIMER] < 0) + { + m_MirrorTimer[FIRE_TIMER]+= 1*IN_MILISECONDS; + // Calculate and deal damage + // TODO: Check this formula + uint32 damage = urand(600, 700); + if (m_MirrorTimerFlags&UNDERWATER_INLAVA) + EnvironmentalDamage(DAMAGE_LAVA, damage); + else + EnvironmentalDamage(DAMAGE_SLIME, damage); + } + } } + else + m_MirrorTimer[FIRE_TIMER] = DISABLED_MIRROR_TIMER; + + // Recheck timers flag + m_MirrorTimerFlags&=~UNDERWATER_EXIST_TIMERS; + for (int i = 0; i< MAX_TIMERS; ++i) + if (m_MirrorTimer[i]!=DISABLED_MIRROR_TIMER) + { + m_MirrorTimerFlags|=UNDERWATER_EXIST_TIMERS; + break; + } + m_MirrorTimerFlagsLast = m_MirrorTimerFlags; } ///The player sobers by 256 every 10 seconds @@ -1058,8 +1146,6 @@ void Player::Update( uint32 p_time ) UpdateAfkReport(now); - CheckExploreSystem(); - if(isCharmed()) { if(Unit *charmer = GetCharmer()) @@ -1203,14 +1289,15 @@ void Player::Update( uint32 p_time ) { if(p_time >= m_zoneUpdateTimer) { - uint32 newzone = GetZoneId(); + uint32 newzone, newarea; + GetZoneAndAreaId(newzone,newarea); + if( m_zoneUpdateId != newzone ) - UpdateZone(newzone); // also update area + UpdateZone(newzone,newarea); // also update area else { // use area updates as well // needed for free far all arenas for example - uint32 newarea = GetAreaId(); if( m_areaUpdateId != newarea ) UpdateArea(newarea); @@ -1245,21 +1332,8 @@ void Player::Update( uint32 p_time ) } } - //Breathtimer - if(m_breathTimer > 0) - { - if(p_time >= m_breathTimer) - m_breathTimer = 0; - else - m_breathTimer -= p_time; - - } - //Handle Water/drowning - HandleDrowning(); - - //Handle lava - HandleLava(); + HandleDrowning(p_time); //Handle detect stealth players if (m_DetectInvTimer > 0) @@ -1286,7 +1360,7 @@ void Player::Update( uint32 p_time ) { m_drunkTimer += p_time; - if (m_drunkTimer > 10000) + if (m_drunkTimer > 10*IN_MILISECONDS) HandleSobering(); } @@ -1311,6 +1385,7 @@ void Player::Update( uint32 p_time ) Pet* pet = GetPet(); if(pet && !IsWithinDistInMap(pet, OWNER_MAX_DISTANCE) && !pet->isPossessed()) + //if(pet && !IsWithinDistInMap(pet, OWNER_MAX_DISTANCE) && (GetCharmGUID() && (pet->GetGUID() != GetCharmGUID()))) { RemovePet(pet, PET_SAVE_NOT_IN_SLOT, true); return; @@ -1344,16 +1419,15 @@ void Player::setDeathState(DeathState s) //FIXME: is pet dismissed at dying or releasing spirit? if second, add setDeathState(DEAD) to HandleRepopRequestOpcode and define pet unsummon here with (s == DEAD) RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true); - // remove uncontrolled pets - RemoveMiniPet(); - RemoveGuardians(); - // save value before aura remove in Unit::setDeathState ressSpellId = GetUInt32Value(PLAYER_SELF_RES_SPELL); // passive spell if(!ressSpellId) ressSpellId = GetResurrectionSpellId(); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP, 1); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH, 1); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON, 1); } Unit::setDeathState(s); @@ -1374,13 +1448,15 @@ void Player::setDeathState(DeathState s) void Player::BuildEnumData( QueryResult * result, WorldPacket * p_data ) { - *p_data << GetGUID(); + Field *fields = result->Fetch(); + + *p_data << uint64(GetGUID()); *p_data << m_name; - *p_data << getRace(); + *p_data << uint8(getRace()); uint8 pClass = getClass(); - *p_data << pClass; - *p_data << getGender(); + *p_data << uint8(pClass); + *p_data << uint8(getGender()); uint32 bytes = GetUInt32Value(PLAYER_BYTES); *p_data << uint8(bytes); @@ -1393,16 +1469,17 @@ void Player::BuildEnumData( QueryResult * result, WorldPacket * p_data ) *p_data << uint8(getLevel()); // player level // do not use GetMap! it will spawn a new instance since the bound instances are not loaded - uint32 zoneId = MapManager::Instance().GetZoneId(GetMapId(), GetPositionX(),GetPositionY()); + uint32 zoneId = MapManager::Instance().GetZoneId(GetMapId(), GetPositionX(),GetPositionY(),GetPositionZ()); sLog.outDebug("Player::BuildEnumData: m:%u, x:%f, y:%f, z:%f zone:%u", GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), zoneId); - *p_data << zoneId; - *p_data << GetMapId(); + *p_data << uint32(zoneId); + *p_data << uint32(GetMapId()); *p_data << GetPositionX(); *p_data << GetPositionY(); *p_data << GetPositionZ(); - *p_data << (result ? result->Fetch()[13].GetUInt32() : 0); + // guild id + *p_data << (result ? fields[13].GetUInt32() : 0); uint32 char_flags = 0; if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM)) @@ -1413,14 +1490,13 @@ void Player::BuildEnumData( QueryResult * result, WorldPacket * p_data ) char_flags |= CHARACTER_FLAG_GHOST; if(HasAtLoginFlag(AT_LOGIN_RENAME)) char_flags |= CHARACTER_FLAG_RENAME; - // always send the flag if declined names aren't used - // to let the client select a default method of declining the name - if(!sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) || (result && result->Fetch()[14].GetCppString() != "")) + if(sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) && (fields[14].GetCppString() != "")) char_flags |= CHARACTER_FLAG_DECLINED; - *p_data << (uint32)char_flags; // character flags - - *p_data << (uint8)1; // unknown + *p_data << uint32(char_flags); // character flags + // character customize (flags?) + *p_data << uint32(HasAtLoginFlag(AT_LOGIN_CUSTOMIZE) ? 1 : 0); + *p_data << uint8(1); // unknown // Pets info { @@ -1431,8 +1507,6 @@ void Player::BuildEnumData( QueryResult * result, WorldPacket * p_data ) // show pet at selection character in character list only for non-ghost character if(result && isAlive() && (pClass == CLASS_WARLOCK || pClass == CLASS_HUNTER)) { - Field* fields = result->Fetch(); - uint32 entry = fields[10].GetUInt32(); CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(entry); if(cInfo) @@ -1443,36 +1517,11 @@ void Player::BuildEnumData( QueryResult * result, WorldPacket * p_data ) } } - *p_data << (uint32)petDisplayId; - *p_data << (uint32)petLevel; - *p_data << (uint32)petFamily; + *p_data << uint32(petDisplayId); + *p_data << uint32(petLevel); + *p_data << uint32(petFamily); } - /*ItemPrototype const *items[EQUIPMENT_SLOT_END]; - for (int i = 0; i < EQUIPMENT_SLOT_END; i++) - items[i] = NULL; - - QueryResult *result = CharacterDatabase.PQuery("SELECT slot,item_template FROM character_inventory WHERE guid = '%u' AND bag = 0",GetGUIDLow()); - if (result) - { - do - { - Field *fields = result->Fetch(); - uint8 slot = fields[0].GetUInt8() & 255; - uint32 item_id = fields[1].GetUInt32(); - if( slot >= EQUIPMENT_SLOT_END ) - continue; - - items[slot] = objmgr.GetItemPrototype(item_id); - if(!items[slot]) - { - sLog.outError( "Player::BuildEnumData: Player %s have unknown item (id: #%u) in inventory, skipped.", GetName(),item_id ); - continue; - } - } while (result->NextRow()); - delete result; - }*/ - for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; slot++) { uint32 visualbase = PLAYER_VISIBLE_ITEM_1_0 + (slot * MAX_VISIBLE_ITEM_OFFSET); @@ -1489,20 +1538,20 @@ void Player::BuildEnumData( QueryResult * result, WorldPacket * p_data ) if (proto != NULL) { - *p_data << (uint32)proto->DisplayInfoID; - *p_data << (uint8)proto->InventoryType; - *p_data << (uint32)(enchant?enchant->aura_id:0); + *p_data << uint32(proto->DisplayInfoID); + *p_data << uint8(proto->InventoryType); + *p_data << uint32(enchant ? enchant->aura_id : 0); } else { - *p_data << (uint32)0; - *p_data << (uint8)0; - *p_data << (uint32)0; // enchant? + *p_data << uint32(0); + *p_data << uint8(0); + *p_data << uint32(0); // enchant? } } - *p_data << (uint32)0; // first bag display id - *p_data << (uint8)0; // first bag inventory type - *p_data << (uint32)0; // enchant? + *p_data << uint32(0); // first bag display id + *p_data << uint8(0); // first bag inventory type + *p_data << uint32(0); // enchant? } bool Player::ToggleAFK() @@ -1566,19 +1615,6 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati if(!InBattleGround() && mEntry->IsBattleGroundOrArena()) return false; - // 449 - Champions' Hall (Alliance) // 450 - Hall of Legends (Horde) - if(mapid == 449 && GetTeam()==HORDE) - { - GetSession()->SendNotification(LANG_NO_ENTER_CHAMPIONS_HALL); - return false; - } - - if(mapid == 450 && GetTeam() == ALLIANCE) - { - GetSession()->SendNotification(LANG_NO_ENTER_HALL_OF_LEGENDS); - return false; - } - // client without expansion support if(GetSession()->Expansion() < mEntry->Expansion()) { @@ -1587,13 +1623,13 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati if(GetTransport()) RepopAtGraveyard(); // teleport to near graveyard if on transport, looks blizz like :) - SendTransferAborted(mapid, TRANSFER_ABORT_INSUF_EXPAN_LVL1); + SendTransferAborted(mapid, TRANSFER_ABORT_INSUF_EXPAN_LVL, mEntry->Expansion()); return false; // normal client can't teleport to this map... } else { - sLog.outDebug("Player %s will teleported to map %u", GetName(), mapid); + sLog.outDebug("Player %s is being teleported to map %u", GetName(), mapid); } // if we were on a transport, leave @@ -1608,9 +1644,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati m_movementInfo.t_time = 0; } - SetSemaphoreTeleport(true); - - // The player was ported to another map and looses the duel immediatly. + // The player was ported to another map and looses the duel immediately. // We have to perform this check before the teleport, otherwise the // ObjectAccessor won't find the flag. if (duel && GetMapId()!=mapid) @@ -1625,68 +1659,29 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati if ((GetMapId() == mapid) && (!m_transport)) { - // prepare zone change detect - uint32 old_zone = GetZoneId(); - - // near teleport - if(!GetSession()->PlayerLogout()) - { - WorldPacket data; - BuildTeleportAckMsg(&data, x, y, z, orientation); - GetSession()->SendPacket(&data); - SetPosition( x, y, z, orientation, true); - } - else - // this will be used instead of the current location in SaveToDB - m_teleport_dest = WorldLocation(mapid, x, y, z, orientation); - SetFallInformation(0, z); - - //BuildHeartBeatMsg(&data); - //SendMessageToSet(&data, true); if (!(options & TELE_TO_NOT_UNSUMMON_PET)) { - //same map, only remove pet if out of range - if(pet && !IsWithinDistInMap(pet, OWNER_MAX_DISTANCE)) - { - if(pet->isControlled() && !pet->isTemporarySummoned() ) - m_temporaryUnsummonedPetNumber = pet->GetCharmInfo()->GetPetNumber(); - else - m_temporaryUnsummonedPetNumber = 0; - - RemovePet(pet, PET_SAVE_NOT_IN_SLOT); - } + //same map, only remove pet if out of range for new position + if(pet && pet->GetDistance(x,y,z) >= OWNER_MAX_DISTANCE) + UnsummonPetTemporaryIfAny(); } if(!(options & TELE_TO_NOT_LEAVE_COMBAT)) CombatStop(); - if (!(options & TELE_TO_NOT_UNSUMMON_PET)) - { - // resummon pet - if(pet && m_temporaryUnsummonedPetNumber) - { - Pet* NewPet = new Pet; - if(!NewPet->LoadPetFromDB(this, 0, m_temporaryUnsummonedPetNumber, true)) - delete NewPet; - - m_temporaryUnsummonedPetNumber = 0; - } - } + // this will be used instead of the current location in SaveToDB + m_teleport_dest = WorldLocation(mapid, x, y, z, orientation); + SetFallInformation(0, z); + // code for finish transfer called in WorldSession::HandleMovementOpcodes() + // at client packet MSG_MOVE_TELEPORT_ACK + SetSemaphoreTeleportNear(true); + // near teleport, triggering send MSG_MOVE_TELEPORT_ACK from client at landing if(!GetSession()->PlayerLogout()) { - // don't reset teleport semaphore while logging out, otherwise m_teleport_dest won't be used in Player::SaveToDB - SetSemaphoreTeleport(false); - - UpdateZone(GetZoneId()); - } - - // new zone - if(old_zone != GetZoneId()) - { - // honorless target - if(pvpInfo.inHostileArea) - CastSpell(this, 2479, true); + WorldPacket data; + BuildTeleportAckMsg(&data, x, y, z, orientation); + GetSession()->SendPacket(&data); } } else @@ -1698,10 +1693,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati // Check enter rights before map getting to avoid creating instance copy for player // this check not dependent from map instance copy and same for all instance copies of selected map if (!MapManager::Instance().CanPlayerEnter(mapid, this)) - { - SetSemaphoreTeleport(false); return false; - } // If the map is not created, assume it is possible to enter it. // It will be created in the WorldPortAck. @@ -1726,15 +1718,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati // remove pet on map change if (pet) - { - //leaving map -> delete pet right away (doing this later will cause problems) - if(pet->isControlled() && !pet->isTemporarySummoned()) - m_temporaryUnsummonedPetNumber = pet->GetCharmInfo()->GetPetNumber(); - else - m_temporaryUnsummonedPetNumber = 0; - - RemovePet(pet, PET_SAVE_NOT_IN_SLOT); - } + UnsummonPetTemporaryIfAny(); // remove all dyn objects RemoveAllDynObjects(); @@ -1791,13 +1775,11 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati // if the player is saved before worldportack (at logout for example) // this will be used instead of the current location in SaveToDB - RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CHANGE_MAP); + RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CHANGE_MAP | AURA_INTERRUPT_FLAG_MOVE | AURA_INTERRUPT_FLAG_TURNING); // move packet sent by client always after far teleport - // SetPosition(final_x, final_y, final_z, final_o, true); - SetDontMove(true); - // code for finish transfer to new map called in WorldSession::HandleMoveWorldportAckOpcode at client packet + SetSemaphoreTeleportFar(true); } else return false; @@ -1827,9 +1809,6 @@ void Player::RemoveFromWorld() ///- Release charmed creatures, unsummon totems and remove pets/guardians StopCastingCharm(); StopCastingBindSight(); - UnsummonAllTotems(); - RemoveMiniPet(); - RemoveGuardians(); } for(int i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; i++) @@ -1842,6 +1821,15 @@ void Player::RemoveFromWorld() ///- It will crash when updating the ObjectAccessor ///- The player should only be removed when logging out Unit::RemoveFromWorld(); + + if(m_uint32Values) + { + if(WorldObject *viewpoint = GetViewpoint()) + { + sLog.outCrash("Player %s has viewpoint %u %u when removed from world", GetName(), viewpoint->GetEntry(), viewpoint->GetTypeId()); + SetViewpoint(viewpoint, false); + } + } } void Player::RewardRage( uint32 damage, uint32 weaponSpeedHitFactor, bool attacker ) @@ -1883,13 +1871,20 @@ void Player::RegenerateAll() { RegenerateHealth(); if (!isInCombat() && !HasAuraType(SPELL_AURA_INTERRUPT_REGEN)) + { Regenerate(POWER_RAGE); + if(getClass() == CLASS_DEATH_KNIGHT) + Regenerate(POWER_RUNIC_POWER); + } } Regenerate( POWER_ENERGY ); Regenerate( POWER_MANA ); + if(getClass() == CLASS_DEATH_KNIGHT) + Regenerate( POWER_RUNE ); + m_regenTimer = regenDelay; } @@ -1909,11 +1904,11 @@ void Player::Regenerate(Powers power) if (recentCast) { // Trinity Updates Mana in intervals of 2s, which is correct - addvalue = GetFloatValue(PLAYER_FIELD_MOD_MANA_REGEN_INTERRUPT) * ManaIncreaseRate * 2.00f; + addvalue = GetFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER) * ManaIncreaseRate * 2.00f; } else { - addvalue = GetFloatValue(PLAYER_FIELD_MOD_MANA_REGEN) * ManaIncreaseRate * 2.00f; + addvalue = GetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER) * ManaIncreaseRate * 2.00f; } } break; case POWER_RAGE: // Regenerate rage @@ -1924,6 +1919,17 @@ void Player::Regenerate(Powers power) case POWER_ENERGY: // Regenerate energy (rogue) addvalue = 20; break; + case POWER_RUNIC_POWER: + { + float RunicPowerDecreaseRate = sWorld.getRate(RATE_POWER_RUNICPOWER_LOSS); + addvalue = 30 * RunicPowerDecreaseRate; // 3 RunicPower by tick + } break; + case POWER_RUNE: + { + for(uint32 i = 0; i < MAX_RUNES; ++i) + if(uint8 cd = GetRuneCooldown(i)) // if we have cooldown, reduce it... + SetRuneCooldown(i, cd - 1); // ... by 2 sec (because update is every 2 sec) + } break; case POWER_FOCUS: case POWER_HAPPINESS: break; @@ -1933,13 +1939,13 @@ void Player::Regenerate(Powers power) // Exist only for POWER_MANA, POWER_ENERGY, POWER_FOCUS auras if(power != POWER_MANA) { - AuraList const& ModPowerRegenPCTAuras = GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT); - for(AuraList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i) - if ((*i)->GetModifier()->m_miscvalue == power) - addvalue *= ((*i)->GetModifierValue() + 100) / 100.0f; + AuraEffectList const& ModPowerRegenPCTAuras = GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT); + for(AuraEffectList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i) + if ((*i)->GetMiscValue() == power) + addvalue *= ((*i)->GetAmount() + 100) / 100.0f; } - if (power != POWER_RAGE) + if (power != POWER_RAGE && power != POWER_RUNIC_POWER) { curValue += uint32(addvalue); if (curValue > maxValue) @@ -1975,9 +1981,9 @@ void Player::RegenerateHealth() addvalue = OCTRegenHPPerSpirit()* HealthIncreaseRate; if (!isInCombat()) { - AuraList const& mModHealthRegenPct = GetAurasByType(SPELL_AURA_MOD_HEALTH_REGEN_PERCENT); - for(AuraList::const_iterator i = mModHealthRegenPct.begin(); i != mModHealthRegenPct.end(); ++i) - addvalue *= (100.0f + (*i)->GetModifierValue()) / 100.0f; + AuraEffectList const& mModHealthRegenPct = GetAurasByType(SPELL_AURA_MOD_HEALTH_REGEN_PERCENT); + for(AuraEffectList::const_iterator i = mModHealthRegenPct.begin(); i != mModHealthRegenPct.end(); ++i) + addvalue *= (100.0f + (*i)->GetAmount()) / 100.0f; } else if(HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT)) addvalue *= GetTotalAuraModifier(SPELL_AURA_MOD_REGEN_DURING_COMBAT) / 100.0f; @@ -2037,21 +2043,27 @@ void Player::SetGameMaster(bool on) setFaction(35); SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM); - RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP); + RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); ResetContestedPvP(); getHostilRefManager().setOnlineOfflineState(false); CombatStop(); + + SetPhaseMask(PHASEMASK_ANYWHERE,false); // see and visible in all phases } else { + // restore phase + AuraEffectList const& phases = GetAurasByType(SPELL_AURA_PHASE); + SetPhaseMask(!phases.empty() ? phases.front()->GetMiscValue() : PHASEMASK_NORMAL,false); + m_ExtraFlags &= ~ PLAYER_EXTRA_GM_ON; setFactionForRace(getRace()); RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM); // restore FFA PvP Server state if(sWorld.IsFFAPvPRealm()) - SetFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP); + SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); // restore FFA PvP area state, remove not allowed for GM mounts UpdateArea(m_areaUpdateId); @@ -2115,7 +2127,7 @@ void Player::UninviteFromGroup() group->RemoveInvite(this); - if(group->GetMembersCount() <= 1) // group has just 1 member => disband + if(group->GetMembersCount() <= 1) // group has just 1 member => disband { if(group->IsCreated()) { @@ -2173,9 +2185,9 @@ void Player::GiveXP(uint32 xp, Unit* victim) return; // handle SPELL_AURA_MOD_XP_PCT auras - Unit::AuraList const& ModXPPctAuras = GetAurasByType(SPELL_AURA_MOD_XP_PCT); - for(Unit::AuraList::const_iterator i = ModXPPctAuras.begin();i != ModXPPctAuras.end(); ++i) - xp = uint32(xp*(1.0f + (*i)->GetModifierValue() / 100.0f)); + Unit::AuraEffectList const& ModXPPctAuras = GetAurasByType(SPELL_AURA_MOD_XP_PCT); + for(Unit::AuraEffectList::const_iterator i = ModXPPctAuras.begin();i != ModXPPctAuras.end(); ++i) + xp = uint32(xp*(1.0f + (*i)->GetAmount() / 100.0f)); // XP resting bonus for kill uint32 rested_bonus_xp = victim ? GetXPRestBonus(xp) : 0; @@ -2223,13 +2235,15 @@ void Player::GiveLevel(uint32 level) data << uint32(0); data << uint32(0); data << uint32(0); + data << uint32(0); + data << uint32(0); // end for for(int i = STAT_STRENGTH; i < MAX_STATS; ++i) // Stats loop (0-4) data << uint32(int32(info.stats[i]) - GetCreateStat(Stats(i))); GetSession()->SendPacket(&data); - SetUInt32Value(PLAYER_NEXT_LEVEL_XP, Trinity::XP::xp_to_level(level)); + SetUInt32Value(PLAYER_NEXT_LEVEL_XP, objmgr.GetXPForLevel(level)); //update level, max level of skills if(getLevel()!= level) @@ -2246,6 +2260,7 @@ void Player::GiveLevel(uint32 level) InitTalentForLevel(); InitTaxiNodesForLevel(); + InitGlyphsForLevel(); UpdateAllStats(); @@ -2265,6 +2280,7 @@ void Player::GiveLevel(uint32 level) Pet* pet = GetPet(); if(pet && pet->getPetType()==SUMMON_PET) pet->GivePetLevel(level); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL); } void Player::InitTalentForLevel() @@ -2282,7 +2298,8 @@ void Player::InitTalentForLevel() } else { - uint32 talentPointsForLevel = uint32((level-9)*sWorld.getRate(RATE_TALENT)); + uint32 talentPointsForLevel = CalculateTalentsPoints(); + // if used more that have then reset if(m_usedTalentCount > talentPointsForLevel) { @@ -2309,7 +2326,7 @@ void Player::InitStatsForLevel(bool reapplyMods) objmgr.GetPlayerLevelInfo(getRace(),getClass(),getLevel(),&info); SetUInt32Value(PLAYER_FIELD_MAX_LEVEL, sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) ); - SetUInt32Value(PLAYER_NEXT_LEVEL_XP, Trinity::XP::xp_to_level(getLevel())); + SetUInt32Value(PLAYER_NEXT_LEVEL_XP, objmgr.GetXPForLevel(getLevel())); UpdateSkillsForLevel (); @@ -2359,11 +2376,11 @@ void Player::InitStatsForLevel(bool reapplyMods) SetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE, 0.0f ); SetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE, 0.0f ); - SetUInt32Value(UNIT_FIELD_ATTACK_POWER, 0 ); - SetUInt32Value(UNIT_FIELD_ATTACK_POWER_MODS, 0 ); + SetInt32Value(UNIT_FIELD_ATTACK_POWER, 0 ); + SetInt32Value(UNIT_FIELD_ATTACK_POWER_MODS, 0 ); SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER,0.0f); - SetUInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER, 0 ); - SetUInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER_MODS,0 ); + SetInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER, 0 ); + SetInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER_MODS,0 ); SetFloatValue(UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER,0.0f); // Base crit values (will be recalculated in UpdateAllStats() at loading and in _ApplyAllStatBonuses() at reset @@ -2398,9 +2415,12 @@ void Player::InitStatsForLevel(bool reapplyMods) SetUInt32Value(PLAYER_FIELD_MOD_TARGET_PHYSICAL_RESISTANCE,0); for(int i = 0; i < MAX_SPELL_SCHOOL; ++i) { - SetFloatValue(UNIT_FIELD_POWER_COST_MODIFIER+i,0.0f); + SetUInt32Value(UNIT_FIELD_POWER_COST_MODIFIER+i,0); SetFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER+i,0.0f); } + // Reset no reagent cost field + for(int i = 0; i < 3; i++) + SetUInt32Value(PLAYER_NO_REAGENT_COST_1 + i, 0); // Init data for form but skip reapply item mods for form InitDataForForm(reapplyMods); @@ -2417,15 +2437,18 @@ void Player::InitStatsForLevel(bool reapplyMods) RemoveFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_ATTACKABLE_1 | UNIT_FLAG_PET_IN_COMBAT | UNIT_FLAG_SILENCED | UNIT_FLAG_PACIFIED | - UNIT_FLAG_DISABLE_ROTATE | UNIT_FLAG_IN_COMBAT | UNIT_FLAG_DISARMED | + UNIT_FLAG_STUNNED | UNIT_FLAG_IN_COMBAT | UNIT_FLAG_DISARMED | UNIT_FLAG_CONFUSED | UNIT_FLAG_FLEEING | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_SKINNABLE | UNIT_FLAG_MOUNT | UNIT_FLAG_TAXI_FLIGHT ); SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE ); // must be set + SetFlag(UNIT_FIELD_FLAGS_2,UNIT_FLAG2_REGENERATE_POWER);// must be set + // cleanup player flags (will be re-applied if need at aura load), to avoid have ghost flag without ghost aura, for example. - RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK | PLAYER_FLAGS_DND | PLAYER_FLAGS_GM | PLAYER_FLAGS_GHOST | PLAYER_FLAGS_FFA_PVP); + RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK | PLAYER_FLAGS_DND | PLAYER_FLAGS_GM | PLAYER_FLAGS_GHOST | PLAYER_ALLOW_ONLY_ABILITY); - SetByteValue(UNIT_FIELD_BYTES_1, 2, 0x00); // one form stealth modified bytes + RemoveStandFlags(UNIT_STAND_FLAGS_ALL); // one form stealth modified bytes + RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP | UNIT_BYTE2_FLAG_SANCTUARY); // restore if need some important flags SetUInt32Value(PLAYER_FIELD_BYTES2, 0 ); // flags empty by default @@ -2441,10 +2464,14 @@ void Player::InitStatsForLevel(bool reapplyMods) SetPower(POWER_RAGE, GetMaxPower(POWER_RAGE)); SetPower(POWER_FOCUS, 0); SetPower(POWER_HAPPINESS, 0); + SetPower(POWER_RUNIC_POWER, 0); } void Player::SendInitialSpells() { + time_t curTime = time(NULL); + time_t infTime = curTime + MONTH/2; + uint16 spellCount = 0; WorldPacket data(SMSG_INITIAL_SPELLS, (1+2+4*m_spells.size()+2+m_spellCooldowns.size()*(2+2+2+4+4))); @@ -2477,12 +2504,15 @@ void Player::SendInitialSpells() if(!sEntry) continue; + // not send infinity cooldown + if(itr->second.end > infTime) + continue; + data << uint16(itr->first); time_t cooldown = 0; - time_t curTime = time(NULL); if(itr->second.end > curTime) - cooldown = (itr->second.end-curTime)*1000; + cooldown = (itr->second.end-curTime)*IN_MILISECONDS; data << uint16(itr->second.itemid); // cast item id data << uint16(sEntry->Category); // spell category @@ -2573,13 +2603,13 @@ void Player::AddNewMailDeliverTime(time_t deliver_time) } } -bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool loading, uint16 slot_id, 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) { // do character spell book cleanup (all characters) - if(loading && !learning) // spell load case + if(!IsInWorld() && !learning) // spell load case { sLog.outError("Player::addSpell: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.",spell_id); CharacterDatabase.PExecute("DELETE FROM character_spell WHERE spell = '%u'",spell_id); @@ -2593,7 +2623,7 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool loading, if(!SpellMgr::IsSpellValid(spellInfo,this,false)) { // do character spell book cleanup (all characters) - if(loading && !learning) // spell load case + if(!IsInWorld() && !learning) // spell load case { sLog.outError("Player::addSpell: Broken spell #%u learning not allowed, deleting for all characters in `character_spell`.",spell_id); CharacterDatabase.PExecute("DELETE FROM character_spell WHERE spell = '%u'",spell_id); @@ -2606,29 +2636,80 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool loading, 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; - // loading && !learning == explicitly load from DB and then exist in it already and set correctly - if(loading && !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) } @@ -2657,7 +2738,7 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool loading, default: // known not saved yet spell (new or modified) { // can be in case spell loading but learned at some previous spell loading - if(loading && !learning) + if(!IsInWorld() && !learning && !dependent_set) itr->second->state = PLAYERSPELL_UNCHANGED; return false; @@ -2672,17 +2753,13 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool loading, { if(TalentEntry const *talentInfo = sTalentStore.LookupEntry( talentPos->talent_id )) { - for(int i=0; i <5; ++i) + for(int i=0; i < MAX_TALENT_RANK; ++i) { // skip learning spell and no rank spell case uint32 rankSpellId = talentInfo->RankID[i]; if(!rankSpellId || rankSpellId==spell_id) continue; - // skip unknown ranks - if(!HasSpell(rankSpellId)) - continue; - removeSpell(rankSpellId); } } @@ -2690,16 +2767,17 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool loading, // non talent spell: learn low ranks (recursive call) else if(uint32 prev_spell = spellmgr.GetPrevSpellInChain(spell_id)) { - if(loading) // at spells loading, no output, but allow save - addSpell(prev_spell,active,true,loading,SPELL_WITHOUT_SLOT_ID,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) @@ -2716,7 +2794,7 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool loading, { if(spellmgr.IsHighRankOfSpell(spell_id,itr->first)) { - if(!loading) // not send spell (re-/over-)learn packets at loading + if(IsInWorld()) // not send spell (re-/over-)learn packets at loading { WorldPacket data(SMSG_SUPERCEDED_SPELL, (4)); data << uint16(itr->first); @@ -2726,12 +2804,13 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool loading, // 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)) { - if(!loading) // not send spell (re-/over-)learn packets at loading + if(IsInWorld()) // not send spell (re-/over-)learn packets at loading { WorldPacket data(SMSG_SUPERCEDED_SPELL, (4)); data << uint16(spell_id); @@ -2749,23 +2828,6 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool loading, } } - uint16 tmpslot=slot_id; - - if (tmpslot == SPELL_WITHOUT_SLOT_ID) - { - uint16 maxid = 0; - PlayerSpellMap::iterator itr; - for (itr = m_spells.begin(); itr != m_spells.end(); ++itr) - { - if(itr->second->state == PLAYERSPELL_REMOVED) - continue; - if (itr->second->slotId > maxid) - maxid = itr->second->slotId; - } - tmpslot = maxid + 1; - } - - newspell->slotId = tmpslot; m_spells[spell_id] = newspell; // return false if spell disabled @@ -2785,23 +2847,8 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool loading, // also cast passive spells (including all talents without SPELL_EFFECT_LEARN_SPELL) with additional checks else if (IsPassiveSpell(spell_id)) { - // if spell doesn't require a stance or the player is in the required stance - if( ( !spellInfo->Stances && - spell_id != 5420 && spell_id != 5419 && spell_id != 7376 && - spell_id != 7381 && spell_id != 21156 && spell_id != 21009 && - spell_id != 21178 && spell_id != 33948 && spell_id != 40121 ) || - m_form != 0 && (spellInfo->Stances & (1<<(m_form-1))) || - (spell_id == 5420 && m_form == FORM_TREE) || - (spell_id == 5419 && m_form == FORM_TRAVEL) || - (spell_id == 7376 && m_form == FORM_DEFENSIVESTANCE) || - (spell_id == 7381 && m_form == FORM_BERSERKERSTANCE) || - (spell_id == 21156 && m_form == FORM_BATTLESTANCE)|| - (spell_id == 21178 && (m_form == FORM_BEAR || m_form == FORM_DIREBEAR) ) || - (spell_id == 33948 && m_form == FORM_FLIGHT) || - (spell_id == 40121 && m_form == FORM_FLIGHT_EPIC) ) - //Check CasterAuraStates - if (!spellInfo->CasterAuraState || HasAuraState(AuraState(spellInfo->CasterAuraState))) - CastSpell(this, spell_id, true); + if(IsNeedCastPassiveSpellAtLearn(spellInfo)) + CastSpell(this, spell_id, true); } else if( IsSpellHaveEffect(spellInfo,SPELL_EFFECT_SKILL_STEP) ) { @@ -2855,10 +2902,8 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool loading, continue; if(_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL || - // poison special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL - pSkill->id==SKILL_POISONS && _spell_idx->second->max_value==0 || - // lockpicking special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL - pSkill->id==SKILL_LOCKPICKING && _spell_idx->second->max_value==0 ) + // lockpicking/runeforging special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL + (pSkill->id==SKILL_LOCKPICKING || pSkill->id==SKILL_RUNEFORGING) && _spell_idx->second->max_value==0 ) { switch(GetSkillRangeType(pSkill,_spell_idx->second->racemask!=0)) { @@ -2886,37 +2931,71 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool loading, { if(!itr->second.autoLearned) { - if(loading) // at spells loading, no output, but allow save - addSpell(itr->second.spell,true,true,loading); + if(!IsInWorld() || !itr->second.active) // at spells loading, no output, but allow save + addSpell(itr->second.spell,itr->second.active,true,true,false); else // at normal learning - learnSpell(itr->second.spell); + learnSpell(itr->second.spell,true); } } + if(IsInWorld()) + { + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL,spell_id); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS,spell_id); + } + // return true (for send learn packet) only if spell active (in case ranked spells) and not replace old spell 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); + bool learning = addSpell(spell_id,active,true,dependent,false); // learn all disabled higher ranks (recursive) - SpellChainNode const* node = spellmgr.GetSpellChainNode(spell_id); - if (node) + if(disabled) { - PlayerSpellMap::iterator iter = m_spells.find(node->next); - if (disabled && iter != m_spells.end() && iter->second->disabled ) - learnSpell(node->next); + SpellChainNode const* node = spellmgr.GetSpellChainNode(spell_id); + if(node) + { + PlayerSpellMap::iterator iter = m_spells.find(node->next); + if (iter != m_spells.end() && iter->second->disabled ) + learnSpell(node->next,false); + } } - // prevent duplicated entires in spell book - if(!learning) + // prevent duplicated entires in spell book, also not send if not in world (loading) + if(!learning || !IsInWorld ()) return; WorldPacket data(SMSG_LEARNED_SPELL, 4); @@ -2924,7 +3003,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()) @@ -2946,10 +3025,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) { @@ -3042,10 +3119,8 @@ void Player::removeSpell(uint32 spell_id, bool disabled) continue; if(_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL || - // poison special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL - pSkill->id==SKILL_POISONS && _spell_idx->second->max_value==0 || - // lockpicking special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL - pSkill->id==SKILL_LOCKPICKING && _spell_idx->second->max_value==0 ) + // lockpicking/runeforging special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL + (pSkill->id==SKILL_LOCKPICKING || pSkill->id==SKILL_RUNEFORGING) && _spell_idx->second->max_value==0 ) { // not reset skills for professions and racial abilities if( (pSkill->categoryId==SKILL_CATEGORY_SECONDARY || pSkill->categoryId==SKILL_CATEGORY_PROFESSION) && @@ -3063,6 +3138,60 @@ void Player::removeSpell(uint32 spell_id, bool disabled) for(SpellLearnSpellMap::const_iterator itr2 = spell_begin; itr2 != spell_end; ++itr2) removeSpell(itr2->second.spell, disabled); + + // activate lesser rank in spellbook/action bar, and cast it if need + bool prev_activate = false; + + if(uint32 prev_id = spellmgr.GetPrevSpellInChain (spell_id)) + { + 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() @@ -3077,13 +3206,13 @@ void Player::RemoveArenaSpellCooldowns() SpellEntry const * entry = sSpellStore.LookupEntry(itr->first); // check if spellentry is present and if the cooldown is less than 15 mins if( entry && - entry->RecoveryTime <= 15 * MINUTE * 1000 && - entry->CategoryRecoveryTime <= 15 * MINUTE * 1000 ) + entry->RecoveryTime <= 15 * MINUTE * IN_MILISECONDS && + entry->CategoryRecoveryTime <= 15 * MINUTE * IN_MILISECONDS ) { // notify player WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8)); data << uint32(itr->first); - data << GetGUID(); + data << uint64(GetGUID()); GetSession()->SendPacket(&data); // remove cooldown m_spellCooldowns.erase(itr); @@ -3108,10 +3237,9 @@ void Player::RemoveAllSpellCooldown() void Player::_LoadSpellCooldowns(QueryResult *result) { - m_spellCooldowns.clear(); + // some cooldowns can be already set at aura loading... //QueryResult *result = CharacterDatabase.PQuery("SELECT spell,item,time FROM character_spell_cooldown WHERE guid = '%u'",GetGUIDLow()); - if(result) { time_t curTime = time(NULL); @@ -3149,17 +3277,20 @@ void Player::_SaveSpellCooldowns() CharacterDatabase.PExecute("DELETE FROM character_spell_cooldown WHERE guid = '%u'", GetGUIDLow()); time_t curTime = time(NULL); + time_t infTime = curTime + MONTH/2; // remove outdated and save active for(SpellCooldowns::iterator itr = m_spellCooldowns.begin();itr != m_spellCooldowns.end();) { if(itr->second.end <= curTime) m_spellCooldowns.erase(itr++); - else + else if(itr->second.end <= infTime) // not save locked cooldowns, it will be reset or set at reload { CharacterDatabase.PExecute("INSERT INTO character_spell_cooldown (guid,spell,item,time) VALUES ('%u', '%u', '%u', '" I64FMTD "')", GetGUIDLow(), itr->first, itr->second.itemid, uint64(itr->second.end)); ++itr; } + else + ++itr; } } @@ -3205,8 +3336,7 @@ bool Player::resetTalents(bool no_cost) CharacterDatabase.PExecute("UPDATE characters set at_login = at_login & ~ %u WHERE guid ='%u'", uint32(AT_LOGIN_RESET_TALENTS), GetGUIDLow()); } - uint32 level = getLevel(); - uint32 talentPointsForLevel = level < 10 ? 0 : uint32((level-9)*sWorld.getRate(RATE_TALENT)); + uint32 talentPointsForLevel = CalculateTalentsPoints(); if (m_usedTalentCount == 0) { @@ -3244,7 +3374,7 @@ bool Player::resetTalents(bool no_cost) if( (getClassMask() & talentTabInfo->ClassMask) == 0 ) continue; - for (int j = 0; j < 5; j++) + for (int j = 0; j < MAX_TALENT_RANK; j++) { for(PlayerSpellMap::iterator itr = GetSpellMap().begin(); itr != GetSpellMap().end();) { @@ -3275,6 +3405,7 @@ bool Player::resetTalents(bool no_cost) if(!no_cost) { ModifyMoney(-(int32)cost); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS, cost); m_resetTalentsCost = cost; m_resetTalentsTime = time(NULL); @@ -3283,19 +3414,14 @@ bool Player::resetTalents(bool no_cost) //FIXME: remove pet before or after unlearn spells? for now after unlearn to allow removing of talent related, pet affecting auras RemovePet(NULL,PET_SAVE_NOT_IN_SLOT, true); - return true; -} - -bool Player::_removeSpell(uint16 spell_id) -{ - PlayerSpellMap::iterator itr = m_spells.find(spell_id); - if (itr != m_spells.end()) + if(m_canTitanGrip) { - delete itr->second; - m_spells.erase(itr); - return true; + m_canTitanGrip = false; + if(sWorld.getConfig(CONFIG_OFFHAND_CHECK_AT_TALENTS_RESET)) + AutoUnequipOffhandIfNeed(); } - return false; + + return true; } Mail* Player::GetMail(uint32 id) @@ -3343,50 +3469,46 @@ void Player::InitVisibleBits() { updateVisualBits.SetCount(PLAYER_END); - // TODO: really implement OWNER_ONLY and GROUP_ONLY. Flags can be found in UpdateFields.h - updateVisualBits.SetBit(OBJECT_FIELD_GUID); updateVisualBits.SetBit(OBJECT_FIELD_TYPE); + updateVisualBits.SetBit(OBJECT_FIELD_ENTRY); updateVisualBits.SetBit(OBJECT_FIELD_SCALE_X); - - updateVisualBits.SetBit(UNIT_FIELD_CHARM); - updateVisualBits.SetBit(UNIT_FIELD_CHARM+1); - - updateVisualBits.SetBit(UNIT_FIELD_SUMMON); - updateVisualBits.SetBit(UNIT_FIELD_SUMMON+1); - - updateVisualBits.SetBit(UNIT_FIELD_CHARMEDBY); - updateVisualBits.SetBit(UNIT_FIELD_CHARMEDBY+1); - - updateVisualBits.SetBit(UNIT_FIELD_TARGET); - updateVisualBits.SetBit(UNIT_FIELD_TARGET+1); - - updateVisualBits.SetBit(UNIT_FIELD_CHANNEL_OBJECT); - updateVisualBits.SetBit(UNIT_FIELD_CHANNEL_OBJECT+1); - + updateVisualBits.SetBit(UNIT_FIELD_CHARM + 0); + updateVisualBits.SetBit(UNIT_FIELD_CHARM + 1); + updateVisualBits.SetBit(UNIT_FIELD_SUMMON + 0); + updateVisualBits.SetBit(UNIT_FIELD_SUMMON + 1); + updateVisualBits.SetBit(UNIT_FIELD_CHARMEDBY + 0); + updateVisualBits.SetBit(UNIT_FIELD_CHARMEDBY + 1); + updateVisualBits.SetBit(UNIT_FIELD_TARGET + 0); + updateVisualBits.SetBit(UNIT_FIELD_TARGET + 1); + updateVisualBits.SetBit(UNIT_FIELD_CHANNEL_OBJECT + 0); + updateVisualBits.SetBit(UNIT_FIELD_CHANNEL_OBJECT + 1); + updateVisualBits.SetBit(UNIT_FIELD_BYTES_0); updateVisualBits.SetBit(UNIT_FIELD_HEALTH); updateVisualBits.SetBit(UNIT_FIELD_POWER1); updateVisualBits.SetBit(UNIT_FIELD_POWER2); updateVisualBits.SetBit(UNIT_FIELD_POWER3); updateVisualBits.SetBit(UNIT_FIELD_POWER4); updateVisualBits.SetBit(UNIT_FIELD_POWER5); - + updateVisualBits.SetBit(UNIT_FIELD_POWER6); + updateVisualBits.SetBit(UNIT_FIELD_POWER7); updateVisualBits.SetBit(UNIT_FIELD_MAXHEALTH); updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER1); updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER2); updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER3); updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER4); updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER5); - + updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER6); + updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER7); updateVisualBits.SetBit(UNIT_FIELD_LEVEL); updateVisualBits.SetBit(UNIT_FIELD_FACTIONTEMPLATE); - updateVisualBits.SetBit(UNIT_FIELD_BYTES_0); + updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_SLOT_ID + 0); + updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_SLOT_ID + 1); + updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_SLOT_ID + 2); updateVisualBits.SetBit(UNIT_FIELD_FLAGS); updateVisualBits.SetBit(UNIT_FIELD_FLAGS_2); - for(uint16 i = UNIT_FIELD_AURA; i < UNIT_FIELD_AURASTATE; ++i) - updateVisualBits.SetBit(i); updateVisualBits.SetBit(UNIT_FIELD_AURASTATE); - updateVisualBits.SetBit(UNIT_FIELD_BASEATTACKTIME); + updateVisualBits.SetBit(UNIT_FIELD_BASEATTACKTIME + 0); updateVisualBits.SetBit(UNIT_FIELD_BASEATTACKTIME + 1); updateVisualBits.SetBit(UNIT_FIELD_BOUNDINGRADIUS); updateVisualBits.SetBit(UNIT_FIELD_COMBATREACH); @@ -3399,10 +3521,12 @@ void Player::InitVisibleBits() updateVisualBits.SetBit(UNIT_DYNAMIC_FLAGS); updateVisualBits.SetBit(UNIT_CHANNEL_SPELL); updateVisualBits.SetBit(UNIT_MOD_CAST_SPEED); + updateVisualBits.SetBit(UNIT_FIELD_BASE_MANA); updateVisualBits.SetBit(UNIT_FIELD_BYTES_2); + updateVisualBits.SetBit(UNIT_FIELD_HOVERHEIGHT); - updateVisualBits.SetBit(PLAYER_DUEL_ARBITER); - updateVisualBits.SetBit(PLAYER_DUEL_ARBITER+1); + updateVisualBits.SetBit(PLAYER_DUEL_ARBITER + 0); + updateVisualBits.SetBit(PLAYER_DUEL_ARBITER + 1); updateVisualBits.SetBit(PLAYER_FLAGS); updateVisualBits.SetBit(PLAYER_GUILDID); updateVisualBits.SetBit(PLAYER_GUILDRANK); @@ -3413,29 +3537,29 @@ void Player::InitVisibleBits() updateVisualBits.SetBit(PLAYER_GUILD_TIMESTAMP); // PLAYER_QUEST_LOG_x also visible bit on official (but only on party/raid)... - for(uint16 i = PLAYER_QUEST_LOG_1_1; i < PLAYER_QUEST_LOG_25_2; i+=4) + for(uint16 i = PLAYER_QUEST_LOG_1_1; i < PLAYER_QUEST_LOG_25_2; i += 4) updateVisualBits.SetBit(i); - //Players visible items are not inventory stuff - //431) = 884 (0x374) = main weapon - for(uint16 i = 0; i < EQUIPMENT_SLOT_END; i++) + // Players visible items are not inventory stuff + for(uint16 i = 0; i < EQUIPMENT_SLOT_END; ++i) { - // item creator - updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_CREATOR + (i*MAX_VISIBLE_ITEM_OFFSET) + 0); - updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_CREATOR + (i*MAX_VISIBLE_ITEM_OFFSET) + 1); + uint32 offset = i * MAX_VISIBLE_ITEM_OFFSET; - uint16 visual_base = PLAYER_VISIBLE_ITEM_1_0 + (i*MAX_VISIBLE_ITEM_OFFSET); + // item creator + updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_CREATOR + 0 + offset); + updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_CREATOR + 1 + offset); // item entry - updateVisualBits.SetBit(visual_base + 0); + updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_0 + 0 + offset); - // item enchantment IDs - for(uint8 j = 0; j < MAX_INSPECTED_ENCHANTMENT_SLOT; ++j) - updateVisualBits.SetBit(visual_base + 1 + j); + // item enchantments + for(uint8 j = 0; j < MAX_ENCHANTMENT_SLOT; ++j) + updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_0 + 1 + j + offset); // random properties - updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_PROPERTIES + 0 + (i*MAX_VISIBLE_ITEM_OFFSET)); - updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_PROPERTIES + 1 + (i*MAX_VISIBLE_ITEM_OFFSET)); + updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_PROPERTIES + offset); + updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_SEED + offset); + updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_PAD + offset); } updateVisualBits.SetBit(PLAYER_CHOSEN_TITLE); @@ -3453,7 +3577,6 @@ void Player::BuildCreateUpdateBlockForPlayer( UpdateData *data, Player *target ) if(target == this) { - for(int i = INVENTORY_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++) { if(m_items[i] == NULL) @@ -3461,7 +3584,7 @@ void Player::BuildCreateUpdateBlockForPlayer( UpdateData *data, Player *target ) m_items[i]->BuildCreateUpdateBlockForPlayer( data, target ); } - for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++) + for(int i = KEYRING_SLOT_START; i < QUESTBAG_SLOT_END; i++) { if(m_items[i] == NULL) continue; @@ -3494,7 +3617,7 @@ void Player::DestroyForPlayer( Player *target ) const m_items[i]->DestroyForPlayer( target ); } - for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++) + for(int i = KEYRING_SLOT_START; i < QUESTBAG_SLOT_END; i++) { if(m_items[i] == NULL) continue; @@ -3506,8 +3629,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 @@ -3515,22 +3646,22 @@ TrainerSpellState Player::GetTrainerSpellState(TrainerSpell const* trainer_spell if (!trainer_spell) return TRAINER_SPELL_RED; - if (!trainer_spell->spell) + if (!trainer_spell->learnedSpell) return TRAINER_SPELL_RED; // known spell - if(HasSpell(trainer_spell->spell)) + if(HasSpell(trainer_spell->learnedSpell)) return TRAINER_SPELL_GRAY; // check race/class requirement - if(!IsSpellFitByClassAndRace(trainer_spell->spell)) + if(!IsSpellFitByClassAndRace(trainer_spell->learnedSpell)) return TRAINER_SPELL_RED; // check level requirement - if(getLevel() < trainer_spell->reqlevel) + if(getLevel() < trainer_spell->reqLevel) return TRAINER_SPELL_RED; - if(SpellChainNode const* spell_chain = spellmgr.GetSpellChainNode(trainer_spell->spell)) + if(SpellChainNode const* spell_chain = spellmgr.GetSpellChainNode(trainer_spell->learnedSpell)) { // check prev.rank requirement if(spell_chain->prev && !HasSpell(spell_chain->prev)) @@ -3545,11 +3676,11 @@ TrainerSpellState Player::GetTrainerSpellState(TrainerSpell const* trainer_spell } // check skill requirement - if(trainer_spell->reqskill && GetBaseSkillValue(trainer_spell->reqskill) < trainer_spell->reqskillvalue) + if(trainer_spell->reqSkill && GetBaseSkillValue(trainer_spell->reqSkill) < trainer_spell->reqSkillValue) return TRAINER_SPELL_RED; // exist, already checked at loading - SpellEntry const* spell = sSpellStore.LookupEntry(trainer_spell->spell); + SpellEntry const* spell = sSpellStore.LookupEntry(trainer_spell->learnedSpell); // secondary prof. or not prof. spell uint32 skill = spell->EffectMiscValue[1]; @@ -3582,27 +3713,7 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC } // remove from arena teams - uint32 at_id = GetArenaTeamIdFromDB(playerguid,ARENA_TEAM_2v2); - if(at_id != 0) - { - ArenaTeam * at = objmgr.GetArenaTeamById(at_id); - if(at) - at->DelMember(playerguid); - } - at_id = GetArenaTeamIdFromDB(playerguid,ARENA_TEAM_3v3); - if(at_id != 0) - { - ArenaTeam * at = objmgr.GetArenaTeamById(at_id); - if(at) - at->DelMember(playerguid); - } - at_id = GetArenaTeamIdFromDB(playerguid,ARENA_TEAM_5v5); - if(at_id != 0) - { - ArenaTeam * at = objmgr.GetArenaTeamById(at_id); - if(at) - at->DelMember(playerguid); - } + LeaveAllArenaTeams(playerguid); // the player was uninvited already on logout so just remove from group QueryResult *resultGroup = CharacterDatabase.PQuery("SELECT leaderGuid FROM group_member WHERE memberGuid='%u'", guid); @@ -3643,15 +3754,16 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC MailItemsInfo mi; if(has_items) { - QueryResult *resultItems = CharacterDatabase.PQuery("SELECT item_guid,item_template FROM mail_items WHERE mail_id='%u'", mail_id); + // data needs to be at first place for Item::LoadFromDB + QueryResult *resultItems = CharacterDatabase.PQuery("SELECT data,item_guid,item_template FROM mail_items JOIN item_instance ON item_guid = guid WHERE mail_id='%u'", mail_id); if(resultItems) { do { Field *fields2 = resultItems->Fetch(); - uint32 item_guidlow = fields2[0].GetUInt32(); - uint32 item_template = fields2[1].GetUInt32(); + uint32 item_guidlow = fields2[1].GetUInt32(); + uint32 item_template = fields2[2].GetUInt32(); ItemPrototype const* itemProto = objmgr.GetItemPrototype(item_template); if(!itemProto) @@ -3661,7 +3773,7 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC } Item *pItem = NewItemOrBag(itemProto); - if(!pItem->LoadFromDB(item_guidlow, MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER))) + if(!pItem->LoadFromDB(item_guidlow, MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER),resultItems)) { pItem->FSetState(ITEM_REMOVED); pItem->SaveToDB(); // it also deletes item object ! @@ -3724,6 +3836,8 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC CharacterDatabase.PExecute("DELETE FROM mail_items WHERE receiver = '%u'",guid); CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u'",guid); CharacterDatabase.PExecute("DELETE FROM character_pet_declinedname WHERE owner = '%u'",guid); + CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE guid = '%u'",guid); + CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE guid = '%u'",guid); CharacterDatabase.CommitTransaction(); //LoginDatabase.PExecute("UPDATE realmcharacters SET numchars = numchars - 1 WHERE acctid = %d AND realmid = %d", accountId, realmID); @@ -3754,6 +3868,10 @@ void Player::SetMovement(PlayerMovementType pType) */ void Player::BuildPlayerRepop() { + WorldPacket data(SMSG_PRE_RESURRECT, GetPackGUID().size()); + data.append(GetPackGUID()); + GetSession()->SendPacket(&data); + if(getRace() == RACE_NIGHTELF) CastSpell(this, 20584, true); // auras SPELL_AURA_INCREASE_SPEED(+speed in wisp form), SPELL_AURA_INCREASE_SWIM_SPEED(+swim speed in wisp form), SPELL_AURA_TRANSFORM (to wisp form) CastSpell(this, 8326, true); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?) @@ -3798,7 +3916,8 @@ void Player::BuildPlayerRepop() SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, (float)1.0); //see radius of death player? - SetByteValue(UNIT_FIELD_BYTES_1, 3, PLAYER_STATE_FLAG_ALWAYS_STAND); + // set and clear other + SetByteValue(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND); } void Player::SendDelayResponse(const uint32 ml_seconds) @@ -3843,13 +3962,15 @@ void Player::ResurrectPlayer(float restore_percent, bool applySickness) SetPower(POWER_ENERGY, uint32(GetMaxPower(POWER_ENERGY)*restore_percent)); } + // trigger update zone for alive state zone updates + uint32 newzone, newarea; + GetZoneAndAreaId(newzone,newarea); + UpdateZone(newzone,newarea); + // update visibility //ObjectAccessor::UpdateVisibilityForPlayer(this); SetToNotify(); - // some items limited to specific map - DestroyZoneLimitedItem( true, GetZoneId()); - if(!applySickness) return; @@ -3869,13 +3990,9 @@ void Player::ResurrectPlayer(float restore_percent, bool applySickness) { int32 delta = (int32(getLevel()) - startLevel + 1)*MINUTE; - for(int i =0; i < 3; ++i) + if(Aura* Aur = GetAura(SPELL_ID_PASSIVE_RESURRECTION_SICKNESS, GetGUID())) { - if(Aura* Aur = GetAura(SPELL_ID_PASSIVE_RESURRECTION_SICKNESS,i)) - { - Aur->SetAuraDuration(delta*1000); - Aur->UpdateAuraDuration(); - } + Aur->SetAuraDuration(delta*IN_MILISECONDS); } } } @@ -3894,7 +4011,7 @@ void Player::KillPlayer() ApplyModFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTE_RELEASE_TIMER, !sMapStore.LookupEntry(GetMapId())->Instanceable()); // 6 minutes until repop at graveyard - m_deathTimer = 6*MINUTE*1000; + m_deathTimer = 6*MINUTE*IN_MILISECONDS; UpdateCorpseReclaimDelay(); // dependent at use SetDeathPvP() call before kill SendCorpseReclaimDelay(); @@ -3915,8 +4032,7 @@ void Player::CreateCorpse() Corpse *corpse = new Corpse( (m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH) ? CORPSE_RESURRECTABLE_PVP : CORPSE_RESURRECTABLE_PVE ); SetPvPDeath(false); - if(!corpse->Create(objmgr.GenerateLowGuid(HIGHGUID_CORPSE), this, GetMapId(), GetPositionX(), - GetPositionY(), GetPositionZ(), GetOrientation())) + if(!corpse->Create(objmgr.GenerateLowGuid(HIGHGUID_CORPSE), this)) { delete corpse; return; @@ -3944,7 +4060,7 @@ void Player::CreateCorpse() flags |= CORPSE_FLAG_HIDE_HELM; if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK)) flags |= CORPSE_FLAG_HIDE_CLOAK; - if(InBattleGround()) + if(InBattleGround() && !InArena()) flags |= CORPSE_FLAG_LOOTABLE; // to be able to remove insignia corpse->SetUInt32Value( CORPSE_FIELD_FLAGS, flags ); @@ -4131,7 +4247,7 @@ uint32 Player::DurabilityRepair(uint16 pos, bool cost, float discountMod, bool g DurabilityCostsEntry const *dcost = sDurabilityCostsStore.LookupEntry(ditemProto->ItemLevel); if(!dcost) { - sLog.outError("ERROR: RepairDurability: Wrong item lvl %u", ditemProto->ItemLevel); + sLog.outError("RepairDurability: Wrong item lvl %u", ditemProto->ItemLevel); return TotalCost; } @@ -4139,7 +4255,7 @@ uint32 Player::DurabilityRepair(uint16 pos, bool cost, float discountMod, bool g DurabilityQualityEntry const *dQualitymodEntry = sDurabilityQualityStore.LookupEntry(dQualitymodEntryId); if(!dQualitymodEntry) { - sLog.outError("ERROR: RepairDurability: Wrong dQualityModEntry %u", dQualitymodEntryId); + sLog.outError("RepairDurability: Wrong dQualityModEntry %u", dQualitymodEntryId); return TotalCost; } @@ -4220,10 +4336,8 @@ void Player::RepopAtGraveyard() WorldSafeLocsEntry const *ClosestGrave = NULL; // Special handle for battleground maps - BattleGround *bg = sBattleGroundMgr.GetBattleGround(GetBattleGroundId()); - - if(bg && (bg->GetTypeID() == BATTLEGROUND_AB || bg->GetTypeID() == BATTLEGROUND_EY || bg->GetTypeID() == BATTLEGROUND_AV)) - ClosestGrave = bg->GetClosestGraveYard(GetPositionX(), GetPositionY(), GetPositionZ(), GetTeam()); + if( BattleGround *bg = GetBattleGround() ) + ClosestGrave = bg->GetClosestGraveYard(this); else ClosestGrave = objmgr.GetClosestGraveYard( GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId(), GetTeam() ); @@ -4384,7 +4498,7 @@ float Player::GetBaseModValue(BaseModGroup modGroup, BaseModType modType) const { if(modGroup >= BASEMOD_END || modType > MOD_END) { - sLog.outError("ERROR: trial to access non existed BaseModGroup or wrong BaseModType!"); + sLog.outError("trial to access non existed BaseModGroup or wrong BaseModType!"); return 0.0f; } @@ -4398,7 +4512,7 @@ float Player::GetTotalBaseModValue(BaseModGroup modGroup) const { if(modGroup >= BASEMOD_END) { - sLog.outError("ERROR: wrong BaseModGroup in GetTotalBaseModValue()!"); + sLog.outError("wrong BaseModGroup in GetTotalBaseModValue()!"); return 0.0f; } @@ -4410,9 +4524,7 @@ float Player::GetTotalBaseModValue(BaseModGroup modGroup) const uint32 Player::GetShieldBlockValue() const { - BaseModGroup modGroup = SHIELD_BLOCK_VALUE; - - float value = GetTotalBaseModValue(modGroup) + GetStat(STAT_STRENGTH)/20 - 1; + float value = (m_auraBaseMod[SHIELD_BLOCK_VALUE][FLAT_MOD] + GetStat(STAT_STRENGTH) * 0.5f - 10)*m_auraBaseMod[SHIELD_BLOCK_VALUE][PCT_MOD]; value = (value < 0) ? 0 : value; @@ -4516,24 +4628,24 @@ float Player::GetRatingBonusValue(CombatRating cr) const uint32 Player::GetMeleeCritDamageReduction(uint32 damage) const { - float melee = GetRatingBonusValue(CR_CRIT_TAKEN_MELEE)*2.0f; - if (melee>25.0f) melee = 25.0f; + float melee = GetRatingBonusValue(CR_CRIT_TAKEN_MELEE)*2.2f; + if (melee>33.0f) melee = 33.0f; return uint32 (melee * damage /100.0f); } uint32 Player::GetRangedCritDamageReduction(uint32 damage) const { - float ranged = GetRatingBonusValue(CR_CRIT_TAKEN_RANGED)*2.0f; - if (ranged>25.0f) ranged=25.0f; + float ranged = GetRatingBonusValue(CR_CRIT_TAKEN_RANGED)*2.2f; + if (ranged>33.0f) ranged=33.0f; return uint32 (ranged * damage /100.0f); } uint32 Player::GetSpellCritDamageReduction(uint32 damage) const { - float spell = GetRatingBonusValue(CR_CRIT_TAKEN_SPELL)*2.0f; - // In wow script resilience limited to 25% - if (spell>25.0f) - spell = 25.0f; + float spell = GetRatingBonusValue(CR_CRIT_TAKEN_SPELL)*2.2f; + // In wow script resilience limited to 33% + if (spell>33.0f) + spell = 33.0f; return uint32 (spell * damage / 100.0f); } @@ -4601,7 +4713,18 @@ float Player::OCTRegenMPPerSpirit() void Player::ApplyRatingMod(CombatRating cr, int32 value, bool apply) { - ApplyModUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + cr, value, apply); + m_baseRatingValue[cr]+=(apply ? value : -value); + + int32 amount = uint32(m_baseRatingValue[cr]); + // Apply bonus from SPELL_AURA_MOD_RATING_FROM_STAT + // stat used stored in miscValueB for this aura + AuraEffectList const& modRatingFromStat = GetAurasByType(SPELL_AURA_MOD_RATING_FROM_STAT); + for(AuraEffectList::const_iterator i = modRatingFromStat.begin();i != modRatingFromStat.end(); ++i) + if ((*i)->GetMiscValue() & (1<<cr)) + amount += int32(GetStat(Stats((*i)->GetMiscBValue())) * (*i)->GetAmount() / 100.0f); + if (amount < 0) + amount = 0; + SetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + cr, uint32(amount)); float RatingCoeffecient = GetRatingCoefficient(cr); float RatingChange = 0.0f; @@ -4624,16 +4747,13 @@ void Player::ApplyRatingMod(CombatRating cr, int32 value, bool apply) UpdateBlockPercentage(); break; case CR_HIT_MELEE: - RatingChange = value / RatingCoeffecient; - m_modMeleeHitChance += apply ? RatingChange : -RatingChange; + UpdateMeleeHitChances(); break; case CR_HIT_RANGED: - RatingChange = value / RatingCoeffecient; - m_modRangedHitChance += apply ? RatingChange : -RatingChange; + UpdateRangedHitChances(); break; case CR_HIT_SPELL: - RatingChange = value / RatingCoeffecient; - m_modSpellHitChance += apply ? RatingChange : -RatingChange; + UpdateSpellHitChances(); break; case CR_CRIT_MELEE: if(affectStats) @@ -4731,6 +4851,7 @@ bool Player::UpdateSkill(uint32 skill_id, uint32 step) new_value = max; SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(new_value,max)); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL,skill_id); return true; } @@ -4766,7 +4887,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); @@ -4793,6 +4914,7 @@ bool Player::UpdateGatherSkill(uint32 SkillId, uint32 SkillValue, uint32 RedLeve case SKILL_HERBALISM: case SKILL_LOCKPICKING: case SKILL_JEWELCRAFTING: + case SKILL_INSCRIPTION: return UpdateSkillPro(SkillId, SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator,gathering_skill_gain); case SKILL_SKINNING: if( sWorld.getConfig(CONFIG_SKILL_CHANCE_SKINNING_STEPS)==0) @@ -4821,6 +4943,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); @@ -4855,6 +4982,15 @@ 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,SkillId); sLog.outDebug("Player::UpdateSkillPro Chance=%3.1f%% taken", Chance/10.0); return true; } @@ -4902,22 +5038,8 @@ void Player::UpdateWeaponSkill (WeaponAttackType attType) UpdateAllCritPercentages(); } -void Player::UpdateCombatSkills(Unit *pVictim, WeaponAttackType attType, MeleeHitOutcome outcome, bool defence) +void Player::UpdateCombatSkills(Unit *pVictim, WeaponAttackType attType, bool defence) { -/* Not need, this checked on call this func from trigger system - switch(outcome) - { - case MELEE_HIT_CRIT: - case MELEE_HIT_DODGE: - case MELEE_HIT_PARRY: - case MELEE_HIT_BLOCK: - case MELEE_HIT_BLOCK_CRIT: - return; - - default: - break; - } -*/ uint32 plevel = getLevel(); // if defense than pVictim == attacker uint32 greylevel = Trinity::XP::GetGrayLevel(plevel); uint32 moblevel = pVictim->getLevelForTarget(this); @@ -5014,7 +5136,7 @@ void Player::UpdateSkillsToMaxSkillsForLevel() if (GetUInt32Value(PLAYER_SKILL_INDEX(i))) { uint32 pskill = GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF; - if( IsProfessionSkill(pskill) || pskill == SKILL_RIDING ) + if( IsProfessionOrRidingSkill(pskill)) continue; uint32 data = GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i)); @@ -5042,7 +5164,12 @@ void Player::SetSkill(uint32 id, uint16 currVal, uint16 maxVal) if(i<PLAYER_MAX_SKILLS) //has skill { if(currVal) + { SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(currVal,maxVal)); + learnSkillRewardedSpells(id, currVal); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL,id); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL,id); + } else //remove { // clear skill fields @@ -5050,27 +5177,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 @@ -5090,24 +5201,26 @@ void Player::SetSkill(uint32 id, uint16 currVal, uint16 maxVal) else SetUInt32Value(PLAYER_SKILL_INDEX(i), MAKE_PAIR32(id,0)); SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(currVal,maxVal)); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL,id); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL,id); // apply skill bonuses SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),0); // temporary bonuses - AuraList const& mModSkill = GetAurasByType(SPELL_AURA_MOD_SKILL); - for(AuraList::const_iterator i = mModSkill.begin(); i != mModSkill.end(); ++i) - if ((*i)->GetModifier()->m_miscvalue == int32(id)) + AuraEffectList const& mModSkill = GetAurasByType(SPELL_AURA_MOD_SKILL); + for(AuraEffectList::const_iterator i = mModSkill.begin(); i != mModSkill.end(); ++i) + if ((*i)->GetMiscValue() == int32(id)) (*i)->ApplyModifier(true); // permanent bonuses - AuraList const& mModSkillTalent = GetAurasByType(SPELL_AURA_MOD_SKILL_TALENT); - for(AuraList::const_iterator i = mModSkillTalent.begin(); i != mModSkillTalent.end(); ++i) - if ((*i)->GetModifier()->m_miscvalue == int32(id)) + AuraEffectList const& mModSkillTalent = GetAurasByType(SPELL_AURA_MOD_SKILL_TALENT); + for(AuraEffectList::const_iterator i = mModSkillTalent.begin(); i != mModSkillTalent.end(); ++i) + if ((*i)->GetMiscValue() == int32(id)) (*i)->ApplyModifier(true); // Learn all spells for skill - learnSkillRewardedSpells(id); + learnSkillRewardedSpells(id, currVal); return; } } @@ -5205,6 +5318,22 @@ uint16 Player::GetPureSkillValue(uint32 skill) const return 0; } +int16 Player::GetSkillPermBonusValue(uint32 skill) const +{ + if(!skill) + return 0; + + for (int i = 0; i < PLAYER_MAX_SKILLS; i++) + { + if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill) + { + return SKILL_PERM_BONUS(GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i))); + } + } + + return 0; +} + int16 Player::GetSkillTempBonusValue(uint32 skill) const { if(!skill) @@ -5245,12 +5374,12 @@ void Player::SendInitialActionButtons() sLog.outDetail( "Action Buttons for '%u' Initialized", GetGUIDLow() ); } -void Player::addActionButton(const uint8 button, const uint16 action, const uint8 type, const uint8 misc) +bool Player::addActionButton(const uint8 button, const uint16 action, const uint8 type, const uint8 misc) { if(button >= MAX_ACTION_BUTTONS) { sLog.outError( "Action %u not added into button %u for player %s: button must be < 132", action, button, GetName() ); - return; + return false; } // check cheating with adding non-known spells to action bar @@ -5259,13 +5388,13 @@ void Player::addActionButton(const uint8 button, const uint16 action, const uint if(!sSpellStore.LookupEntry(action)) { sLog.outError( "Action %u not added into button %u for player %s: spell not exist", action, button, GetName() ); - return; + return false; } if(!HasSpell(action)) { sLog.outError( "Action %u not added into button %u for player %s: player don't known this spell", action, button, GetName() ); - return; + return false; } } @@ -5283,6 +5412,7 @@ void Player::addActionButton(const uint8 button, const uint16 action, const uint }; sLog.outDetail( "Player '%u' Added Action '%u' to Button '%u'", GetGUIDLow(), action, button ); + return true; } void Player::removeActionButton(uint8 button) @@ -5299,11 +5429,6 @@ void Player::removeActionButton(uint8 button) sLog.outDetail( "Action Button '%u' Removed from Player '%u'", button, GetGUIDLow() ); } -void Player::SetDontMove(bool dontMove) -{ - m_dontMove = dontMove; -} - bool Player::SetPosition(float x, float y, float z, float orientation, bool teleport) { // prevent crash when a bad coord is sent by the client @@ -5360,17 +5485,17 @@ void Player::SaveRecallPosition() void Player::SendMessageToSet(WorldPacket *data, bool self, bool to_possessor) { - MapManager::Instance().GetMap(GetMapId(), this)->MessageBroadcast(this, data, self, to_possessor); + GetMap()->MessageBroadcast(this, data, self, to_possessor); } void Player::SendMessageToSetInRange(WorldPacket *data, float dist, bool self, bool to_possessor) { - MapManager::Instance().GetMap(GetMapId(), this)->MessageDistBroadcast(this, data, dist, self, to_possessor); + GetMap()->MessageDistBroadcast(this, data, dist, self, to_possessor); } void Player::SendMessageToSetInRange(WorldPacket *data, float dist, bool self, bool to_possessor, bool own_team_only) { - MapManager::Instance().GetMap(GetMapId(), this)->MessageDistBroadcast(this, data, dist, self, to_possessor, own_team_only); + GetMap()->MessageDistBroadcast(this, data, dist, self, to_possessor, own_team_only); } void Player::SendDirectMessage(WorldPacket *data) @@ -5378,6 +5503,20 @@ void Player::SendDirectMessage(WorldPacket *data) GetSession()->SendPacket(data); } +void Player::SendCinematicStart(uint32 CinematicSequenceId) +{ + WorldPacket data(SMSG_TRIGGER_CINEMATIC, 4); + data << uint32(CinematicSequenceId); + SendDirectMessage(&data); +} + +void Player::SendMovieStart(uint32 MovieId) +{ + WorldPacket data(SMSG_TRIGGER_MOVIE, 4); + data << uint32(MovieId); + SendDirectMessage(&data); +} + void Player::CheckExploreSystem() { if (!isAlive()) @@ -5386,14 +5525,14 @@ void Player::CheckExploreSystem() if (isInFlight()) return; - uint16 areaFlag=MapManager::Instance().GetBaseMap(GetMapId())->GetAreaFlag(GetPositionX(),GetPositionY()); + uint16 areaFlag=MapManager::Instance().GetBaseMap(GetMapId())->GetAreaFlag(GetPositionX(),GetPositionY(),GetPositionZ()); if(areaFlag==0xffff) return; int offset = areaFlag / 32; if(offset >= 128) { - sLog.outError("ERROR: Wrong area flag %u in map data for (X: %f Y: %f) point to field PLAYER_EXPLORED_ZONES_1 + %u ( %u must be < 64 ).",areaFlag,GetPositionX(),GetPositionY(),offset,offset); + sLog.outError("Wrong area flag %u in map data for (X: %f Y: %f) point to field PLAYER_EXPLORED_ZONES_1 + %u ( %u must be < 128 ).",areaFlag,GetPositionX(),GetPositionY(),offset,offset); return; } @@ -5404,6 +5543,8 @@ void Player::CheckExploreSystem() { SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, (uint32)(currFields | val)); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA); + AreaTableEntry const *p = GetAreaEntryByAreaFlagAndMap(areaFlag,GetMapId()); if(!p) { @@ -5484,435 +5625,27 @@ void Player::setFactionForRace(uint8 race) setFaction( getFactionForRace(race) ); } -void Player::UpdateReputation() const -{ - sLog.outDetail( "WORLD: Player::UpdateReputation" ); - - for(FactionStateList::const_iterator itr = m_factions.begin(); itr != m_factions.end(); ++itr) - { - SendFactionState(&(itr->second)); - } -} - -void Player::SendFactionState(FactionState const* faction) const -{ - if(faction->Flags & FACTION_FLAG_VISIBLE) //If faction is visible then update it - { - WorldPacket data(SMSG_SET_FACTION_STANDING, (16)); // last check 2.4.0 - data << (float) 0; // unk 2.4.0 - data << (uint32) 1; // count - // for - data << (uint32) faction->ReputationListID; - data << (uint32) faction->Standing; - // end for - GetSession()->SendPacket(&data); - } -} - -void Player::SendInitialReputations() -{ - WorldPacket data(SMSG_INITIALIZE_FACTIONS, (4+128*5)); - data << uint32 (0x00000080); - - RepListID a = 0; - - for (FactionStateList::const_iterator itr = m_factions.begin(); itr != m_factions.end(); ++itr) - { - // fill in absent fields - for (; a != itr->first; a++) - { - data << uint8 (0x00); - data << uint32 (0x00000000); - } - - // fill in encountered data - data << uint8 (itr->second.Flags); - data << uint32 (itr->second.Standing); - - ++a; - } - - // fill in absent fields - for (; a != 128; a++) - { - data << uint8 (0x00); - data << uint32 (0x00000000); - } - - GetSession()->SendPacket(&data); -} - -FactionState const* Player::GetFactionState( FactionEntry const* factionEntry) const -{ - FactionStateList::const_iterator itr = m_factions.find(factionEntry->reputationListID); - if (itr != m_factions.end()) - return &itr->second; - - return NULL; -} - -void Player::SetFactionAtWar(FactionState* faction, bool atWar) -{ - // not allow declare war to own faction - if(atWar && (faction->Flags & FACTION_FLAG_PEACE_FORCED) ) - return; - - // already set - if(((faction->Flags & FACTION_FLAG_AT_WAR) != 0) == atWar) - return; - - if( atWar ) - faction->Flags |= FACTION_FLAG_AT_WAR; - else - faction->Flags &= ~FACTION_FLAG_AT_WAR; - - faction->Changed = true; -} - -void Player::SetFactionInactive(FactionState* faction, bool inactive) -{ - // always invisible or hidden faction can't be inactive - if(inactive && ((faction->Flags & (FACTION_FLAG_INVISIBLE_FORCED|FACTION_FLAG_HIDDEN)) || !(faction->Flags & FACTION_FLAG_VISIBLE) ) ) - return; - - // already set - if(((faction->Flags & FACTION_FLAG_INACTIVE) != 0) == inactive) - return; - - if(inactive) - faction->Flags |= FACTION_FLAG_INACTIVE; - else - faction->Flags &= ~FACTION_FLAG_INACTIVE; - - faction->Changed = true; -} - -void Player::SetFactionVisibleForFactionTemplateId(uint32 FactionTemplateId) -{ - FactionTemplateEntry const*factionTemplateEntry = sFactionTemplateStore.LookupEntry(FactionTemplateId); - - if(!factionTemplateEntry) - return; - - SetFactionVisibleForFactionId(factionTemplateEntry->faction); -} - -void Player::SetFactionVisibleForFactionId(uint32 FactionId) -{ - FactionEntry const *factionEntry = sFactionStore.LookupEntry(FactionId); - if(!factionEntry) - return; - - if(factionEntry->reputationListID < 0) - return; - - FactionStateList::iterator itr = m_factions.find(factionEntry->reputationListID); - if (itr == m_factions.end()) - return; - - SetFactionVisible(&itr->second); -} - -void Player::SetFactionVisible(FactionState* faction) -{ - // always invisible or hidden faction can't be make visible - if(faction->Flags & (FACTION_FLAG_INVISIBLE_FORCED|FACTION_FLAG_HIDDEN)) - return; - - // already set - if(faction->Flags & FACTION_FLAG_VISIBLE) - return; - - faction->Flags |= FACTION_FLAG_VISIBLE; - faction->Changed = true; - - if(!m_session->PlayerLoading()) - { - // make faction visible in reputation list at client - WorldPacket data(SMSG_SET_FACTION_VISIBLE, 4); - data << faction->ReputationListID; - GetSession()->SendPacket(&data); - } -} - -void Player::SetInitialFactions() -{ - for(unsigned int i = 1; i < sFactionStore.GetNumRows(); i++) - { - FactionEntry const *factionEntry = sFactionStore.LookupEntry(i); - - if( factionEntry && (factionEntry->reputationListID >= 0)) - { - FactionState newFaction; - newFaction.ID = factionEntry->ID; - newFaction.ReputationListID = factionEntry->reputationListID; - newFaction.Standing = 0; - newFaction.Flags = GetDefaultReputationFlags(factionEntry); - newFaction.Changed = true; - - m_factions[newFaction.ReputationListID] = newFaction; - } - } -} - -uint32 Player::GetDefaultReputationFlags(const FactionEntry *factionEntry) const -{ - if (!factionEntry) - return 0; - - uint32 raceMask = getRaceMask(); - uint32 classMask = getClassMask(); - for (int i=0; i < 4; i++) - { - if( (factionEntry->BaseRepRaceMask[i] & raceMask) && - (factionEntry->BaseRepClassMask[i]==0 || - (factionEntry->BaseRepClassMask[i] & classMask) ) ) - return factionEntry->ReputationFlags[i]; - } - return 0; -} - -int32 Player::GetBaseReputation(const FactionEntry *factionEntry) const -{ - if (!factionEntry) - return 0; - - uint32 raceMask = getRaceMask(); - uint32 classMask = getClassMask(); - for (int i=0; i < 4; i++) - { - if( (factionEntry->BaseRepRaceMask[i] & raceMask) && - (factionEntry->BaseRepClassMask[i]==0 || - (factionEntry->BaseRepClassMask[i] & classMask) ) ) - return factionEntry->BaseRepValue[i]; - } - - // in faction.dbc exist factions with (RepListId >=0, listed in character reputation list) with all BaseRepRaceMask[i]==0 - return 0; -} - -int32 Player::GetReputation(uint32 faction_id) const -{ - FactionEntry const *factionEntry = sFactionStore.LookupEntry(faction_id); - - if (!factionEntry) - { - sLog.outError("Player::GetReputation: Can't get reputation of %s for unknown faction (faction template id) #%u.",GetName(), faction_id); - return 0; - } - - return GetReputation(factionEntry); -} - -int32 Player::GetReputation(const FactionEntry *factionEntry) const -{ - // Faction without recorded reputation. Just ignore. - if(!factionEntry) - return 0; - - FactionStateList::const_iterator itr = m_factions.find(factionEntry->reputationListID); - if (itr != m_factions.end()) - return GetBaseReputation(factionEntry) + itr->second.Standing; - - return 0; -} - ReputationRank Player::GetReputationRank(uint32 faction) const { - FactionEntry const*factionEntry = sFactionStore.LookupEntry(faction); - if(!factionEntry) - return MIN_REPUTATION_RANK; - - return GetReputationRank(factionEntry); -} - -ReputationRank Player::ReputationToRank(int32 standing) const -{ - int32 Limit = Reputation_Cap + 1; - for (int i = MAX_REPUTATION_RANK-1; i >= MIN_REPUTATION_RANK; --i) - { - Limit -= ReputationRank_Length[i]; - if (standing >= Limit ) - return ReputationRank(i); - } - return MIN_REPUTATION_RANK; -} - -ReputationRank Player::GetReputationRank(const FactionEntry *factionEntry) const -{ - int32 Reputation = GetReputation(factionEntry); - return ReputationToRank(Reputation); -} - -ReputationRank Player::GetBaseReputationRank(const FactionEntry *factionEntry) const -{ - int32 Reputation = GetBaseReputation(factionEntry); - return ReputationToRank(Reputation); -} - -bool Player::ModifyFactionReputation(uint32 FactionTemplateId, int32 DeltaReputation) -{ - FactionTemplateEntry const* factionTemplateEntry = sFactionTemplateStore.LookupEntry(FactionTemplateId); - - if(!factionTemplateEntry) - { - sLog.outError("Player::ModifyFactionReputation: Can't update reputation of %s for unknown faction (faction template id) #%u.", GetName(), FactionTemplateId); - return false; - } - - FactionEntry const *factionEntry = sFactionStore.LookupEntry(factionTemplateEntry->faction); - - // Faction without recorded reputation. Just ignore. - if(!factionEntry) - return false; - - return ModifyFactionReputation(factionEntry, DeltaReputation); -} - -bool Player::ModifyFactionReputation(FactionEntry const* factionEntry, int32 standing) -{ - SimpleFactionsList const* flist = GetFactionTeamList(factionEntry->ID); - if (flist) - { - bool res = false; - for (SimpleFactionsList::const_iterator itr = flist->begin();itr != flist->end();++itr) - { - FactionEntry const *factionEntryCalc = sFactionStore.LookupEntry(*itr); - if(factionEntryCalc) - res = ModifyOneFactionReputation(factionEntryCalc, standing); - } - return res; - } - else - return ModifyOneFactionReputation(factionEntry, standing); -} - -bool Player::ModifyOneFactionReputation(FactionEntry const* factionEntry, int32 standing) -{ - FactionStateList::iterator itr = m_factions.find(factionEntry->reputationListID); - if (itr != m_factions.end()) - { - int32 BaseRep = GetBaseReputation(factionEntry); - int32 new_rep = BaseRep + itr->second.Standing + standing; - - if (new_rep > Reputation_Cap) - new_rep = Reputation_Cap; - else - if (new_rep < Reputation_Bottom) - new_rep = Reputation_Bottom; - - if(ReputationToRank(new_rep) <= REP_HOSTILE) - SetFactionAtWar(&itr->second,true); - - itr->second.Standing = new_rep - BaseRep; - itr->second.Changed = true; - - SetFactionVisible(&itr->second); - - for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ ) - { - if(uint32 questid = GetQuestSlotQuestId(i)) - { - Quest const* qInfo = objmgr.GetQuestTemplate(questid); - if( qInfo && qInfo->GetRepObjectiveFaction() == factionEntry->ID ) - { - QuestStatusData& q_status = mQuestStatus[questid]; - if( q_status.m_status == QUEST_STATUS_INCOMPLETE ) - { - if(GetReputation(factionEntry) >= qInfo->GetRepObjectiveValue()) - if ( CanCompleteQuest( questid ) ) - CompleteQuest( questid ); - } - else if( q_status.m_status == QUEST_STATUS_COMPLETE ) - { - if(GetReputation(factionEntry) < qInfo->GetRepObjectiveValue()) - IncompleteQuest( questid ); - } - } - } - } - - SendFactionState(&(itr->second)); - - return true; - } - return false; -} - -bool Player::SetFactionReputation(uint32 FactionTemplateId, int32 standing) -{ - FactionTemplateEntry const* factionTemplateEntry = sFactionTemplateStore.LookupEntry(FactionTemplateId); - - if(!factionTemplateEntry) - { - sLog.outError("Player::SetFactionReputation: Can't set reputation of %s for unknown faction (faction template id) #%u.", GetName(), FactionTemplateId); - return false; - } - - FactionEntry const *factionEntry = sFactionStore.LookupEntry(factionTemplateEntry->faction); - - // Faction without recorded reputation. Just ignore. - if(!factionEntry) - return false; - - return SetFactionReputation(factionEntry, standing); -} - -bool Player::SetFactionReputation(FactionEntry const* factionEntry, int32 standing) -{ - SimpleFactionsList const* flist = GetFactionTeamList(factionEntry->ID); - if (flist) - { - bool res = false; - for (SimpleFactionsList::const_iterator itr = flist->begin();itr != flist->end();++itr) - { - FactionEntry const *factionEntryCalc = sFactionStore.LookupEntry(*itr); - if(factionEntryCalc) - res = SetOneFactionReputation(factionEntryCalc, standing); - } - return res; - } - else - return SetOneFactionReputation(factionEntry, standing); -} - -bool Player::SetOneFactionReputation(FactionEntry const* factionEntry, int32 standing) -{ - FactionStateList::iterator itr = m_factions.find(factionEntry->reputationListID); - if (itr != m_factions.end()) - { - if (standing > Reputation_Cap) - standing = Reputation_Cap; - else - if (standing < Reputation_Bottom) - standing = Reputation_Bottom; - - int32 BaseRep = GetBaseReputation(factionEntry); - itr->second.Standing = standing - BaseRep; - itr->second.Changed = true; - - SetFactionVisible(&itr->second); - - if(ReputationToRank(standing) <= REP_HOSTILE) - SetFactionAtWar(&itr->second,true); - - SendFactionState(&(itr->second)); - return true; - } - return false; + FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction); + return GetReputationMgr().GetRank(factionEntry); } //Calculate total reputation percent player gain with quest/creature level int32 Player::CalculateReputationGain(uint32 creatureOrQuestLevel, int32 rep, bool for_quest) { - // for grey creature kill received 20%, in other case 100. - int32 percent = (!for_quest && (creatureOrQuestLevel <= Trinity::XP::GetGrayLevel(getLevel()))) ? 20 : 100; + float percent = 100.0f; + + float rate = for_quest ? sWorld.getRate(RATE_REPUTATION_LOWLEVEL_QUEST) : sWorld.getRate(RATE_REPUTATION_LOWLEVEL_KILL); + + if(rate != 1.0f && creatureOrQuestLevel <= MaNGOS::XP::GetGrayLevel(getLevel())) + percent *= rate; int32 repMod = GetTotalAuraModifier(SPELL_AURA_MOD_REPUTATION_GAIN); percent += rep > 0 ? repMod : -repMod; - if(percent <=0) + if(percent <= 0.0f) return 0; return int32(sWorld.getRate(RATE_REPUTATION_GAIN)*rep*percent/100); @@ -5937,16 +5670,16 @@ void Player::RewardReputation(Unit *pVictim, float rate) int32 donerep1 = CalculateReputationGain(pVictim->getLevel(),Rep->repvalue1,false); donerep1 = int32(donerep1*rate); FactionEntry const *factionEntry1 = sFactionStore.LookupEntry(Rep->repfaction1); - uint32 current_reputation_rank1 = GetReputationRank(factionEntry1); + uint32 current_reputation_rank1 = GetReputationMgr().GetRank(factionEntry1); if(factionEntry1 && current_reputation_rank1 <= Rep->reputation_max_cap1) - ModifyFactionReputation(factionEntry1, donerep1); + GetReputationMgr().ModifyReputation(factionEntry1, donerep1); // Wiki: Team factions value divided by 2 if(Rep->is_teamaward1) { FactionEntry const *team1_factionEntry = sFactionStore.LookupEntry(factionEntry1->team); if(team1_factionEntry) - ModifyFactionReputation(team1_factionEntry, donerep1 / 2); + GetReputationMgr().ModifyReputation(team1_factionEntry, donerep1 / 2); } } @@ -5955,16 +5688,16 @@ void Player::RewardReputation(Unit *pVictim, float rate) int32 donerep2 = CalculateReputationGain(pVictim->getLevel(),Rep->repvalue2,false); donerep2 = int32(donerep2*rate); FactionEntry const *factionEntry2 = sFactionStore.LookupEntry(Rep->repfaction2); - uint32 current_reputation_rank2 = GetReputationRank(factionEntry2); + uint32 current_reputation_rank2 = GetReputationMgr().GetRank(factionEntry2); if(factionEntry2 && current_reputation_rank2 <= Rep->reputation_max_cap2) - ModifyFactionReputation(factionEntry2, donerep2); + GetReputationMgr().ModifyReputation(factionEntry2, donerep2); // Wiki: Team factions value divided by 2 if(Rep->is_teamaward2) { FactionEntry const *team2_factionEntry = sFactionStore.LookupEntry(factionEntry2->team); if(team2_factionEntry) - ModifyFactionReputation(team2_factionEntry, donerep2 / 2); + GetReputationMgr().ModifyReputation(team2_factionEntry, donerep2 / 2); } } } @@ -5977,10 +5710,10 @@ void Player::RewardReputation(Quest const *pQuest) { if(pQuest->RewRepFaction[i] && pQuest->RewRepValue[i] ) { - int32 rep = CalculateReputationGain(pQuest->GetQuestLevel(),pQuest->RewRepValue[i],true); + int32 rep = CalculateReputationGain(GetQuestLevel(pQuest),pQuest->RewRepValue[i],true); FactionEntry const* factionEntry = sFactionStore.LookupEntry(pQuest->RewRepFaction[i]); if(factionEntry) - ModifyFactionReputation(factionEntry, rep); + GetReputationMgr().ModifyReputation(factionEntry, rep); } } @@ -6098,12 +5831,7 @@ bool Player::RewardHonor(Unit *uVictim, uint32 groupsize, float honor, bool pvpt victim_guid = 0; // Don't show HK: <rank> message, only log. } - if(k_level <= 5) - k_grey = 0; - else if( k_level <= 39 ) - k_grey = k_level - 5 - k_level/10; - else - k_grey = k_level - 1 - k_level/5; + k_grey = Trinity::XP::GetGrayLevel(k_level); if(v_level<=k_grey) return false; @@ -6139,6 +5867,8 @@ bool Player::RewardHonor(Unit *uVictim, uint32 groupsize, float honor, bool pvpt if(groupsize > 1) honor /= groupsize; + // apply honor multiplier from aura (not stacking-get highest) + honor = int32(float(honor) * (float(GetMaxPositiveAuraModifier(SPELL_AURA_MOD_HONOR_GAIN_PCT))+100.0f)/100.0f); honor *= (((float)urand(8,12))/10); // approx honor: 80% - 120% of real honor } @@ -6168,8 +5898,8 @@ bool Player::RewardHonor(Unit *uVictim, uint32 groupsize, float honor, bool pvpt { // Check if allowed to receive it in current map uint8 MapType = sWorld.getConfig(CONFIG_PVP_TOKEN_MAP_TYPE); - if( (MapType == 1 && !InBattleGround() && !HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP)) - || (MapType == 2 && !HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP)) + if( (MapType == 1 && !InBattleGround() && !HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP)) + || (MapType == 2 && !HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP)) || (MapType == 3 && !InBattleGround()) ) return true; @@ -6227,24 +5957,18 @@ void Player::ModifyArenaPoints( int32 value ) uint32 Player::GetGuildIdFromDB(uint64 guid) { - std::ostringstream ss; - ss<<"SELECT guildid FROM guild_member WHERE guid='"<<guid<<"'"; - QueryResult *result = CharacterDatabase.Query( ss.str().c_str() ); - if( result ) - { - uint32 v = result->Fetch()[0].GetUInt32(); - delete result; - return v; - } - else + QueryResult* result = CharacterDatabase.PQuery("SELECT guildid FROM guild_member WHERE guid='%u'", GUID_LOPART(guid)); + if(!result) return 0; + + uint32 id = result->Fetch()[0].GetUInt32(); + delete result; + return id; } uint32 Player::GetRankFromDB(uint64 guid) { - std::ostringstream ss; - ss<<"SELECT rank FROM guild_member WHERE guid='"<<guid<<"'"; - QueryResult *result = CharacterDatabase.Query( ss.str().c_str() ); + QueryResult *result = CharacterDatabase.PQuery( "SELECT rank FROM guild_member WHERE guid='%u'", GUID_LOPART(guid) ); if( result ) { uint32 v = result->Fetch()[0].GetUInt32(); @@ -6268,10 +5992,8 @@ uint32 Player::GetArenaTeamIdFromDB(uint64 guid, uint8 type) uint32 Player::GetZoneIdFromDB(uint64 guid) { - std::ostringstream ss; - - ss<<"SELECT zone FROM characters WHERE guid='"<<GUID_LOPART(guid)<<"'"; - QueryResult *result = CharacterDatabase.Query( ss.str().c_str() ); + uint32 guidLow = GUID_LOPART(guid); + QueryResult *result = CharacterDatabase.PQuery( "SELECT zone FROM characters WHERE guid='%u'", guidLow ); if (!result) return 0; Field* fields = result->Fetch(); @@ -6281,22 +6003,19 @@ uint32 Player::GetZoneIdFromDB(uint64 guid) if (!zone) { // stored zone is zero, use generic and slow zone detection - ss.str(""); - ss<<"SELECT map,position_x,position_y FROM characters WHERE guid='"<<GUID_LOPART(guid)<<"'"; - result = CharacterDatabase.Query(ss.str().c_str()); + result = CharacterDatabase.PQuery("SELECT map,position_x,position_y,position_z FROM characters WHERE guid='%u'", guidLow); if( !result ) return 0; fields = result->Fetch(); - uint32 map = fields[0].GetUInt32(); + uint32 map = fields[0].GetUInt32(); float posx = fields[1].GetFloat(); float posy = fields[2].GetFloat(); + float posz = fields[3].GetFloat(); delete result; - zone = MapManager::Instance().GetZoneId(map,posx,posy); + zone = MapManager::Instance().GetZoneId(map,posx,posy,posz); - ss.str(""); - ss << "UPDATE characters SET zone='"<<zone<<"' WHERE guid='"<<GUID_LOPART(guid)<<"'"; - CharacterDatabase.Execute(ss.str().c_str()); + CharacterDatabase.PExecute("UPDATE characters SET zone='%u' WHERE guid='%u'", zone, guidLow); } return zone; @@ -6313,39 +6032,38 @@ void Player::UpdateArea(uint32 newArea) if(area && (area->flags & AREA_FLAG_ARENA)) { if(!isGameMaster()) - SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP); + SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); } else { // remove ffa flag only if not ffapvp realm // removal in sanctuaries and capitals is handled in zone update - if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP) && !sWorld.IsFFAPvPRealm()) - RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP); + if(HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP) && !sWorld.IsFFAPvPRealm()) + RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); } UpdateAreaDependentAuras(newArea); } -void Player::UpdateZone(uint32 newZone) +void Player::UpdateZone(uint32 newZone, uint32 newArea) { - uint32 oldZoneId = m_zoneUpdateId; + if(m_zoneUpdateId != newZone) + { + sOutdoorPvPMgr.HandlePlayerLeaveZone(this, m_zoneUpdateId); + sOutdoorPvPMgr.HandlePlayerEnterZone(this, newZone); + SendInitWorldStates(newZone, newArea); // only if really enters to new zone, not just area change, works strange... + } + m_zoneUpdateId = newZone; m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL; // zone changed, so area changed as well, update it - UpdateArea(GetAreaId()); + UpdateArea(newArea); AreaTableEntry const* zone = GetAreaEntryByAreaID(newZone); if(!zone) return; - // inform outdoor pvp - if(oldZoneId != m_zoneUpdateId) - { - sOutdoorPvPMgr.HandlePlayerLeaveZone(this, oldZoneId); - sOutdoorPvPMgr.HandlePlayerEnterZone(this, m_zoneUpdateId); - } - if (sWorld.getConfig(CONFIG_WEATHER)) { Weather *wth = sWorld.FindWeather(zone->ID); @@ -6380,15 +6098,15 @@ void Player::UpdateZone(uint32 newZone) pvpInfo.endTimer = time(0); // start toggle-off } - if(zone->flags & AREA_FLAG_SANCTUARY) // in sanctuary + if((zone->flags & AREA_FLAG_SANCTUARY) || zone->mapid == 609) // in sanctuary { - SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY); + SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY); if(sWorld.IsFFAPvPRealm()) - RemoveFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP); + RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); } else { - RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY); + RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY); } if(zone->flags & AREA_FLAG_CAPITAL) // in capital city @@ -6398,7 +6116,7 @@ void Player::UpdateZone(uint32 newZone) InnEnter(time(0),GetMapId(),0,0,0); if(sWorld.IsFFAPvPRealm()) - RemoveFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP); + RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); } else // anywhere else { @@ -6412,7 +6130,7 @@ void Player::UpdateZone(uint32 newZone) SetRestType(REST_TYPE_NO); if(sWorld.IsFFAPvPRealm()) - SetFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP); + SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); } } else // not in tavern (leave city then) @@ -6422,7 +6140,7 @@ void Player::UpdateZone(uint32 newZone) // Set player to FFA PVP when not in rested environment. if(sWorld.IsFFAPvPRealm()) - SetFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP); + SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); } } } @@ -6432,6 +6150,9 @@ void Player::UpdateZone(uint32 newZone) if(isAlive()) DestroyZoneLimitedItem( true, newZone ); + // check some item equip limitations (in result lost CanTitanGrip at talent reset, for example) + AutoUnequipOffhandIfNeed(); + // recent client version not send leave/join channel packets for built-in local channels UpdateLocalChannels( newZone ); @@ -6490,6 +6211,8 @@ void Player::DuelComplete(DuelCompleteType type) if(!duel) return; + sLog.outDebug("Duel Complete %s %s", GetName(), duel->opponent->GetName()); + WorldPacket data(SMSG_DUEL_COMPLETE, (1)); data << (uint8)((type != DUEL_INTERUPTED) ? 1 : 0); GetSession()->SendPacket(&data); @@ -6526,26 +6249,27 @@ void Player::DuelComplete(DuelCompleteType type) duel->initiator->RemoveGameObject(obj,true); /* remove auras */ - std::vector<uint32> auras2remove; - AuraMap const& vAuras = duel->opponent->GetAuras(); - for (AuraMap::const_iterator i = vAuras.begin(); i != vAuras.end(); ++i) + AuraMap &itsAuras = duel->opponent->GetAuras(); + for(AuraMap::iterator i = itsAuras.begin(); i != itsAuras.end();) { if (!i->second->IsPositive() && i->second->GetCasterGUID() == GetGUID() && i->second->GetAuraApplyTime() >= duel->startTime) - auras2remove.push_back(i->second->GetId()); + { + duel->opponent->RemoveAura(i); + } + else + ++i; } - for(size_t i=0; i<auras2remove.size(); i++) - duel->opponent->RemoveAurasDueToSpell(auras2remove[i]); - - auras2remove.clear(); - AuraMap const& auras = GetAuras(); - for (AuraMap::const_iterator i = auras.begin(); i != auras.end(); ++i) + AuraMap &myAuras = GetAuras(); + for(AuraMap::iterator i = myAuras.begin(); i != myAuras.end();) { if (!i->second->IsPositive() && i->second->GetCasterGUID() == duel->opponent->GetGUID() && i->second->GetAuraApplyTime() >= duel->startTime) - auras2remove.push_back(i->second->GetId()); + { + RemoveAura(i); + } + else + ++i; } - for(size_t i=0; i<auras2remove.size(); i++) - RemoveAurasDueToSpell(auras2remove[i]); // cleanup combo points if(GetComboTarget()==duel->opponent->GetGUID()) @@ -6592,7 +6316,12 @@ void Player::_ApplyItemMods(Item *item, uint8 slot,bool apply) sLog.outDetail("applying mods for item %u ",item->GetGUIDLow()); - uint32 attacktype = Player::GetAttackBySlot(slot); + uint8 attacktype = Player::GetAttackBySlot(slot); + + //check disarm only on mod apply to allow remove item mods + if (apply && !CanUseAttackType(attacktype) ) + return; + if(attacktype < MAX_ATTACK) _ApplyWeaponDependentAuraMods(item,WeaponAttackType(attacktype),apply); @@ -6610,19 +6339,43 @@ void Player::_ApplyItemMods(Item *item, uint8 slot,bool apply) sLog.outDebug("_ApplyItemMods complete."); } -void Player::_ApplyItemBonuses(ItemPrototype const *proto,uint8 slot,bool apply) +void Player::_ApplyItemBonuses(ItemPrototype const *proto, uint8 slot, bool apply) { if(slot >= INVENTORY_SLOT_BAG_END || !proto) return; - for (int i = 0; i < 10; i++) + for (int i = 0; i < MAX_ITEM_PROTO_STATS; ++i) { - float val = float (proto->ItemStat[i].ItemStatValue); + uint32 statType = 0; + int32 val = 0; + + if(proto->ScalingStatDistribution) + { + if(ScalingStatDistributionEntry const *ssd = sScalingStatDistributionStore.LookupEntry(proto->ScalingStatDistribution)) + { + statType = ssd->StatMod[i]; + + if(uint32 modifier = ssd->Modifier[i]) + { + uint32 level = ((getLevel() > ssd->MaxLevel) ? ssd->MaxLevel : getLevel()); + if(ScalingStatValuesEntry const *ssv = sScalingStatValuesStore.LookupEntry(level)) + { + uint32 multiplier = ssv->Multiplier[proto->GetScalingStatValuesColumn()]; + val = (multiplier * modifier) / 10000; + } + } + } + } + else + { + statType = proto->ItemStat[i].ItemStatType; + val = proto->ItemStat[i].ItemStatValue; + } - if(val==0) + if(val == 0) continue; - switch (proto->ItemStat[i].ItemStatType) + switch (statType) { case ITEM_MOD_MANA: HandleStatModifier(UNIT_MOD_MANA, BASE_VALUE, float(val), apply); @@ -6740,6 +6493,32 @@ void Player::_ApplyItemBonuses(ItemPrototype const *proto,uint8 slot,bool apply) case ITEM_MOD_EXPERTISE_RATING: ApplyRatingMod(CR_EXPERTISE, int32(val), apply); break; + case ITEM_MOD_ATTACK_POWER: + HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(val), apply); + HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(val), apply); + break; + case ITEM_MOD_RANGED_ATTACK_POWER: + HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(val), apply); + break; + case ITEM_MOD_FERAL_ATTACK_POWER: + ApplyFeralAPBonus(int32(val), apply); + break; + case ITEM_MOD_SPELL_HEALING_DONE: + ApplySpellHealingBonus(int32(val), apply); + break; + case ITEM_MOD_SPELL_DAMAGE_DONE: + ApplySpellDamageBonus(int32(val), apply); + break; + case ITEM_MOD_MANA_REGENERATION: + ApplyManaRegenBonus(int32(val), apply); + break; + case ITEM_MOD_ARMOR_PENETRATION_RATING: + ApplyRatingMod(CR_ARMOR_PENETRATION, int32(val), apply); + break; + case ITEM_MOD_SPELL_POWER: + ApplySpellHealingBonus(int32(val), apply); + ApplySpellDamageBonus(int32(val), apply); + break; } } @@ -6794,7 +6573,15 @@ void Player::_ApplyItemBonuses(ItemPrototype const *proto,uint8 slot,bool apply) SetBaseWeaponDamage(attType, MAXDAMAGE, damage); } - if(!IsUseEquipedWeapon(slot==EQUIPMENT_SLOT_MAINHAND)) + // Druids get feral AP bonus from weapon dps + if(getClass() == CLASS_DRUID) + { + int32 feral_bonus = proto->getFeralBonus(); + if (feral_bonus > 0) + ApplyFeralAPBonus(feral_bonus, apply); + } + + if(IsInFeralForm() || !CanUseAttackType(attType)) return; if (proto->Delay) @@ -6813,20 +6600,20 @@ void Player::_ApplyItemBonuses(ItemPrototype const *proto,uint8 slot,bool apply) void Player::_ApplyWeaponDependentAuraMods(Item *item,WeaponAttackType attackType,bool apply) { - AuraList const& auraCritList = GetAurasByType(SPELL_AURA_MOD_CRIT_PERCENT); - for(AuraList::const_iterator itr = auraCritList.begin(); itr!=auraCritList.end();++itr) + AuraEffectList const& auraCritList = GetAurasByType(SPELL_AURA_MOD_CRIT_PERCENT); + for(AuraEffectList::const_iterator itr = auraCritList.begin(); itr!=auraCritList.end();++itr) _ApplyWeaponDependentAuraCritMod(item,attackType,*itr,apply); - AuraList const& auraDamageFlatList = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE); - for(AuraList::const_iterator itr = auraDamageFlatList.begin(); itr!=auraDamageFlatList.end();++itr) + AuraEffectList const& auraDamageFlatList = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE); + for(AuraEffectList::const_iterator itr = auraDamageFlatList.begin(); itr!=auraDamageFlatList.end();++itr) _ApplyWeaponDependentAuraDamageMod(item,attackType,*itr,apply); - AuraList const& auraDamagePCTList = GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE); - for(AuraList::const_iterator itr = auraDamagePCTList.begin(); itr!=auraDamagePCTList.end();++itr) + AuraEffectList const& auraDamagePCTList = GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE); + for(AuraEffectList::const_iterator itr = auraDamagePCTList.begin(); itr!=auraDamagePCTList.end();++itr) _ApplyWeaponDependentAuraDamageMod(item,attackType,*itr,apply); } -void Player::_ApplyWeaponDependentAuraCritMod(Item *item, WeaponAttackType attackType, Aura* aura, bool apply) +void Player::_ApplyWeaponDependentAuraCritMod(Item *item, WeaponAttackType attackType, AuraEffect* aura, bool apply) { // generic not weapon specific case processes in aura code if(aura->GetSpellProto()->EquippedItemClass == -1) @@ -6843,15 +6630,14 @@ void Player::_ApplyWeaponDependentAuraCritMod(Item *item, WeaponAttackType attac if (item->IsFitToSpellRequirements(aura->GetSpellProto())) { - HandleBaseModValue(mod, FLAT_MOD, float (aura->GetModifierValue()), apply); + HandleBaseModValue(mod, FLAT_MOD, float (aura->GetAmount()), apply); } } -void Player::_ApplyWeaponDependentAuraDamageMod(Item *item, WeaponAttackType attackType, Aura* aura, bool apply) +void Player::_ApplyWeaponDependentAuraDamageMod(Item *item, WeaponAttackType attackType, AuraEffect* aura, bool apply) { // ignore spell mods for not wands - Modifier const* modifier = aura->GetModifier(); - if((modifier->m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)==0 && (getClassMask() & CLASSMASK_WAND_USERS)==0) + if((aura->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL)==0 && (getClassMask() & CLASSMASK_WAND_USERS)==0) return; // generic not weapon specific case processes in aura code @@ -6868,7 +6654,7 @@ void Player::_ApplyWeaponDependentAuraDamageMod(Item *item, WeaponAttackType att } UnitModifierType unitModType = TOTAL_VALUE; - switch(modifier->m_auraname) + switch(aura->GetAuraName()) { case SPELL_AURA_MOD_DAMAGE_DONE: unitModType = TOTAL_VALUE; break; case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE: unitModType = TOTAL_PCT; break; @@ -6877,7 +6663,7 @@ void Player::_ApplyWeaponDependentAuraDamageMod(Item *item, WeaponAttackType att if (item->IsFitToSpellRequirements(aura->GetSpellProto())) { - HandleStatModifier(unitMod, unitModType, float(aura->GetModifierValue()),apply); + HandleStatModifier(unitMod, unitModType, float(aura->GetAmount()),apply); } } @@ -6890,7 +6676,7 @@ void Player::ApplyItemEquipSpell(Item *item, bool apply, bool form_change) if(!proto) return; - for (int i = 0; i < 5; i++) + for (int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) { _Spell const& spellData = proto->Spells[i]; @@ -6916,29 +6702,15 @@ void Player::ApplyEquipSpell(SpellEntry const* spellInfo, Item* item, bool apply if(apply) { // Cannot be used in this stance/form - if(GetErrorAtShapeshiftedCast(spellInfo, m_form)!=0) + if(GetErrorAtShapeshiftedCast(spellInfo, m_form) != SPELL_CAST_OK) return; if(form_change) // check aura active state from other form { - bool found = false; - for (int k=0; k < 3; ++k) - { - spellEffectPair spair = spellEffectPair(spellInfo->Id, k); - for (AuraMap::iterator iter = m_Auras.lower_bound(spair); iter != m_Auras.upper_bound(spair); ++iter) - { - if(!item || iter->second->GetCastItemGUID() == item->GetGUID()) - { - found = true; - break; - } - } - if(found) - break; - } - - if(found) // and skip re-cast already active aura at form change - return; + AuraMap const& auras = GetAuras(); + for(AuraMap::const_iterator itr = auras.lower_bound(spellInfo->Id); itr != auras.upper_bound(spellInfo->Id); ++itr) + if(!item || itr->second->GetCastItemGUID()==item->GetGUID()) + return; } DEBUG_LOG("WORLD: cast %s Equip spellId - %i", (item ? "item" : "itemset"), spellInfo->Id); @@ -6950,7 +6722,7 @@ void Player::ApplyEquipSpell(SpellEntry const* spellInfo, Item* item, bool apply if(form_change) // check aura compatibility { // Cannot be used in this stance/form - if(GetErrorAtShapeshiftedCast(spellInfo, m_form)==0) + if(GetErrorAtShapeshiftedCast(spellInfo, m_form)==SPELL_CAST_OK) return; // and remove only not compatible at form change } @@ -7003,7 +6775,7 @@ void Player::CastItemCombatSpell(Item *item,Unit* Target, WeaponAttackType attTy if (!Target || Target == this ) return; - for (int i = 0; i < 5; i++) + for (int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) { _Spell const& spellData = proto->Spells[i]; @@ -7031,7 +6803,7 @@ void Player::CastItemCombatSpell(Item *item,Unit* Target, WeaponAttackType attTy if(spellData.SpellPPMRate) { uint32 WeaponSpeed = GetAttackTime(attType); - chance = GetPPMProcChance(WeaponSpeed, spellData.SpellPPMRate); + chance = GetPPMProcChance(WeaponSpeed, spellData.SpellPPMRate, spellInfo); } else if(chance > 100.0f) { @@ -7072,6 +6844,92 @@ void Player::CastItemCombatSpell(Item *item,Unit* Target, WeaponAttackType attTy } } +void Player::CastItemUseSpell(Item *item,SpellCastTargets const& targets,uint8 cast_count, uint32 glyphIndex) +{ + ItemPrototype const* proto = item->GetProto(); + // special learning case + if(proto->Spells[0].SpellId==SPELL_ID_GENERIC_LEARN || proto->Spells[0].SpellId==SPELL_ID_GENERIC_LEARN_PET) + { + uint32 learn_spell_id = proto->Spells[0].SpellId; + uint32 learning_spell_id = proto->Spells[1].SpellId; + + SpellEntry const *spellInfo = sSpellStore.LookupEntry(learn_spell_id); + if(!spellInfo) + { + sLog.outError("Player::CastItemUseSpell: Item (Entry: %u) in have wrong spell id %u, ignoring ",proto->ItemId, learn_spell_id); + SendEquipError(EQUIP_ERR_NONE,item,NULL); + return; + } + + Spell *spell = new Spell(this, spellInfo, false); + spell->m_CastItem = item; + spell->m_cast_count = cast_count; //set count of casts + spell->m_currentBasePoints[0] = learning_spell_id; + spell->prepare(&targets); + return; + } + + // use triggered flag only for items with many spell casts and for not first cast + int count = 0; + + // item spells casted at use + for(int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) + { + _Spell const& spellData = proto->Spells[i]; + + // no spell + if(!spellData.SpellId) + continue; + + // wrong triggering type + if( spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_USE && spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_NO_DELAY_USE) + continue; + + SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellData.SpellId); + if(!spellInfo) + { + sLog.outError("Player::CastItemUseSpell: Item (Entry: %u) in have wrong spell id %u, ignoring",proto->ItemId, spellData.SpellId); + continue; + } + + Spell *spell = new Spell(this, spellInfo, (count > 0)); + spell->m_CastItem = item; + spell->m_cast_count = cast_count; // set count of casts + spell->m_glyphIndex = glyphIndex; // glyph index + spell->prepare(&targets); + + ++count; + } + + // Item enchantments spells casted at use + for(int e_slot = 0; e_slot < MAX_ENCHANTMENT_SLOT; ++e_slot) + { + uint32 enchant_id = item->GetEnchantmentId(EnchantmentSlot(e_slot)); + SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); + if(!pEnchant) continue; + for (int s=0;s<3;s++) + { + if(pEnchant->type[s]!=ITEM_ENCHANTMENT_TYPE_USE_SPELL) + continue; + + SpellEntry const *spellInfo = sSpellStore.LookupEntry(pEnchant->spellid[s]); + if (!spellInfo) + { + sLog.outError("Player::CastItemUseSpell Enchant %i, cast unknown spell %i", pEnchant->ID, pEnchant->spellid[s]); + continue; + } + + Spell *spell = new Spell(this, spellInfo, (count > 0)); + spell->m_CastItem = item; + spell->m_cast_count = cast_count; // set count of casts + spell->m_glyphIndex = glyphIndex; // glyph index + spell->prepare(&targets); + + ++count; + } + } +} + void Player::_RemoveAllItemMods() { sLog.outDebug("_RemoveAllItemMods start."); @@ -7247,7 +7105,7 @@ void Player::RemovedInsignia(Player* looterPlr) // We have to convert player corpse to bones, not to be able to resurrect there // SpawnCorpseBones isn't handy, 'cos it saves player while he in BG - Corpse *bones = ObjectAccessor::Instance().ConvertCorpseForPlayer(GetGUID()); + Corpse *bones = ObjectAccessor::Instance().ConvertCorpseForPlayer(GetGUID(),true); if (!bones) return; @@ -7276,6 +7134,9 @@ void Player::SendLootRelease( uint64 guid ) void Player::SendLoot(uint64 guid, LootType loot_type) { + if (uint64 lguid = GetLootGUID()) + m_session->DoLootRelease(lguid); + Loot *loot = 0; PermissionTypes permission = ALL_PERMISSION; @@ -7314,11 +7175,11 @@ void Player::SendLoot(uint64 guid, LootType loot_type) { sLog.outDebug(" if(lootid)"); loot->clear(); - loot->FillLoot(lootid, LootTemplates_Gameobject, this); + loot->FillLoot(lootid, LootTemplates_Gameobject, this, false); } if(loot_type == LOOT_FISHING) - go->getFishLoot(loot); + go->getFishLoot(loot,this); go->SetLootState(GO_ACTIVATED); } @@ -7341,7 +7202,7 @@ void Player::SendLoot(uint64 guid, LootType loot_type) { item->m_lootGenerated = true; loot->clear(); - loot->FillLoot(item->GetProto()->DisenchantID, LootTemplates_Disenchant, this); + loot->FillLoot(item->GetProto()->DisenchantID, LootTemplates_Disenchant, this,true); } } else if(loot_type == LOOT_PROSPECTING) @@ -7352,7 +7213,18 @@ void Player::SendLoot(uint64 guid, LootType loot_type) { item->m_lootGenerated = true; loot->clear(); - loot->FillLoot(item->GetEntry(), LootTemplates_Prospecting, this); + loot->FillLoot(item->GetEntry(), LootTemplates_Prospecting, this,true); + } + } + else if(loot_type == LOOT_MILLING) + { + loot = &item->loot; + + if(!item->m_lootGenerated) + { + item->m_lootGenerated = true; + loot->clear(); + loot->FillLoot(item->GetEntry(), LootTemplates_Milling, this,true); } } else @@ -7363,7 +7235,7 @@ void Player::SendLoot(uint64 guid, LootType loot_type) { item->m_lootGenerated = true; loot->clear(); - loot->FillLoot(item->GetEntry(), LootTemplates_Item, this); + loot->FillLoot(item->GetEntry(), LootTemplates_Item, this,true); loot->generateMoneyLoot(item->GetProto()->MinMoneyLoot,item->GetProto()->MaxMoneyLoot); } @@ -7387,7 +7259,7 @@ void Player::SendLoot(uint64 guid, LootType loot_type) uint32 pLevel = bones->loot.gold; bones->loot.clear(); if(GetBattleGround()->GetTypeID() == BATTLEGROUND_AV) - loot->FillLoot(1, LootTemplates_Creature, this); + loot->FillLoot(1, LootTemplates_Creature, this, true); // It may need a better formula // Now it works like this: lvl10: ~6copper, lvl70: ~9silver bones->loot.gold = (uint32)( urand(50, 150) * 0.016f * pow( ((float)pLevel)/5.76f, 2.5f) * sWorld.getRate(RATE_DROP_MONEY) ); @@ -7423,7 +7295,7 @@ void Player::SendLoot(uint64 guid, LootType loot_type) loot->clear(); if (uint32 lootid = creature->GetCreatureInfo()->pickpocketLootId) - loot->FillLoot(lootid, LootTemplates_Pickpocketing, this); + loot->FillLoot(lootid, LootTemplates_Pickpocketing, this, false); // Generate extra money for pick pocket loot const uint32 a = urand(0, creature->getLevel()/2); @@ -7453,7 +7325,7 @@ void Player::SendLoot(uint64 guid, LootType loot_type) loot->clear(); if (uint32 lootid = creature->GetCreatureInfo()->lootid) - loot->FillLoot(lootid, LootTemplates_Creature, recipient); + loot->FillLoot(lootid, LootTemplates_Creature, recipient, false); loot->generateMoneyLoot(creature->GetCreatureInfo()->mingold,creature->GetCreatureInfo()->maxgold); @@ -7483,7 +7355,7 @@ void Player::SendLoot(uint64 guid, LootType loot_type) if (loot_type == LOOT_SKINNING) { loot->clear(); - loot->FillLoot(creature->GetCreatureInfo()->SkinLootId, LootTemplates_Skinning, this); + loot->FillLoot(creature->GetCreatureInfo()->SkinLootId, LootTemplates_Skinning, this, false); } // set group rights only for loot_type != LOOT_SKINNING else @@ -7517,41 +7389,8 @@ void Player::SendLoot(uint64 guid, LootType loot_type) SetLootGUID(guid); - QuestItemList *q_list = 0; - if (permission != NONE_PERMISSION) - { - QuestItemMap const& lootPlayerQuestItems = loot->GetPlayerQuestItems(); - QuestItemMap::const_iterator itr = lootPlayerQuestItems.find(GetGUIDLow()); - if (itr == lootPlayerQuestItems.end()) - q_list = loot->FillQuestLoot(this); - else - q_list = itr->second; - } - - QuestItemList *ffa_list = 0; - if (permission != NONE_PERMISSION) - { - QuestItemMap const& lootPlayerFFAItems = loot->GetPlayerFFAItems(); - QuestItemMap::const_iterator itr = lootPlayerFFAItems.find(GetGUIDLow()); - if (itr == lootPlayerFFAItems.end()) - ffa_list = loot->FillFFALoot(this); - else - ffa_list = itr->second; - } - - QuestItemList *conditional_list = 0; - if (permission != NONE_PERMISSION) - { - QuestItemMap const& lootPlayerNonQuestNonFFAConditionalItems = loot->GetPlayerNonQuestNonFFAConditionalItems(); - QuestItemMap::const_iterator itr = lootPlayerNonQuestNonFFAConditionalItems.find(GetGUIDLow()); - if (itr == lootPlayerNonQuestNonFFAConditionalItems.end()) - conditional_list = loot->FillNonQuestNonFFAConditionalLoot(this); - else - conditional_list = itr->second; - } - - // LOOT_PICKPOCKETING, LOOT_PROSPECTING, LOOT_DISENCHANTING and LOOT_INSIGNIA unsupported by client, sending LOOT_SKINNING instead - if(loot_type == LOOT_PICKPOCKETING || loot_type == LOOT_DISENCHANTING || loot_type == LOOT_PROSPECTING || loot_type == LOOT_INSIGNIA) + // LOOT_PICKPOCKETING, LOOT_PROSPECTING, LOOT_DISENCHANTING, LOOT_INSIGNIA and LOOT_MILLING unsupported by client, sending LOOT_SKINNING instead + if(loot_type == LOOT_PICKPOCKETING || loot_type == LOOT_DISENCHANTING || loot_type == LOOT_PROSPECTING || loot_type == LOOT_INSIGNIA || loot_type == LOOT_MILLING) loot_type = LOOT_SKINNING; if(loot_type == LOOT_FISHINGHOLE) @@ -7561,7 +7400,7 @@ void Player::SendLoot(uint64 guid, LootType loot_type) data << uint64(guid); data << uint8(loot_type); - data << LootView(*loot, q_list, ffa_list, conditional_list, this, permission); + data << LootView(*loot, this, permission); SendDirectMessage(&data); @@ -7594,20 +7433,16 @@ void Player::SendUpdateWorldState(uint32 Field, uint32 Value) GetSession()->SendPacket(&data); } -void Player::SendInitWorldStates(bool forceZone, uint32 forceZoneId) +void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid) { // data depends on zoneid/mapid... BattleGround* bg = GetBattleGround(); uint16 NumberOfFields = 0; uint32 mapid = GetMapId(); - uint32 zoneid; - if(forceZone) - zoneid = forceZoneId; - else - zoneid = GetZoneId(); OutdoorPvP * pvp = sOutdoorPvPMgr.GetOutdoorPvPToZoneId(zoneid); - uint32 areaid = GetAreaId(); + sLog.outDebug("Sending SMSG_INIT_WORLD_STATES to Map:%u, Zone: %u", mapid, zoneid); + // may be exist better way to do this... switch(zoneid) { @@ -7628,46 +7463,46 @@ void Player::SendInitWorldStates(bool forceZone, uint32 forceZoneId) case 1537: case 2257: case 2918: - NumberOfFields = 6; + NumberOfFields = 8; break; case 139: - NumberOfFields = 39; + NumberOfFields = 41; break; case 1377: - NumberOfFields = 13; + NumberOfFields = 15; break; case 2597: - NumberOfFields = 81; + NumberOfFields = 83; break; case 3277: - NumberOfFields = 14; + NumberOfFields = 16; break; case 3358: case 3820: - NumberOfFields = 38; + NumberOfFields = 40; break; case 3483: - NumberOfFields = 25; + NumberOfFields = 27; break; case 3518: - NumberOfFields = 37; + NumberOfFields = 39; break; case 3519: - NumberOfFields = 36; + NumberOfFields = 38; break; case 3521: - NumberOfFields = 35; + NumberOfFields = 37; break; case 3698: case 3702: case 3968: - NumberOfFields = 9; + NumberOfFields = 11; break; case 3703: - NumberOfFields = 9; + NumberOfFields = 11; break; default: - NumberOfFields = 10; + NumberOfFields = 12; break; } @@ -7682,6 +7517,10 @@ void Player::SendInitWorldStates(bool forceZone, uint32 forceZoneId) data << uint32(0x8d5) << uint32(0x0); // 4 data << uint32(0x8d4) << uint32(0x0); // 5 data << uint32(0x8d3) << uint32(0x0); // 6 + // 7 1 - Arena season in progress, 0 - end of season + data << uint32(0xC77) << uint32(sWorld.getConfig(CONFIG_ARENA_SEASON_IN_PROGRESS)); + // 8 Arena season id + data << uint32(0xF3D) << uint32(sWorld.getConfig(CONFIG_ARENA_SEASON_ID)); if(mapid == 530) // Outland { data << uint32(0x9bf) << uint32(0x0); // 7 @@ -8275,7 +8114,8 @@ uint8 Player::FindEquipSlot( ItemPrototype const* proto, uint32 slot, bool swap // (this will be replace mainhand weapon at auto equip instead unwonted "you don't known dual wielding" ... if(CanDualWield()) slots[1] = EQUIPMENT_SLOT_OFFHAND; - };break; + break; + }; case INVTYPE_SHIELD: slots[0] = EQUIPMENT_SLOT_OFFHAND; break; @@ -8284,6 +8124,8 @@ uint8 Player::FindEquipSlot( ItemPrototype const* proto, uint32 slot, bool swap break; case INVTYPE_2HWEAPON: slots[0] = EQUIPMENT_SLOT_MAINHAND; + if (CanDualWield() && CanTitanGrip()) + slots[1] = EQUIPMENT_SLOT_OFFHAND; break; case INVTYPE_TABARD: slots[0] = EQUIPMENT_SLOT_TABARD; @@ -8304,10 +8146,10 @@ uint8 Player::FindEquipSlot( ItemPrototype const* proto, uint32 slot, bool swap slots[0] = EQUIPMENT_SLOT_RANGED; break; case INVTYPE_BAG: - slots[0] = INVENTORY_SLOT_BAG_1; - slots[1] = INVENTORY_SLOT_BAG_2; - slots[2] = INVENTORY_SLOT_BAG_3; - slots[3] = INVENTORY_SLOT_BAG_4; + slots[0] = INVENTORY_SLOT_BAG_START + 0; + slots[1] = INVENTORY_SLOT_BAG_START + 1; + slots[2] = INVENTORY_SLOT_BAG_START + 2; + slots[3] = INVENTORY_SLOT_BAG_START + 3; break; case INVTYPE_RELIC: { @@ -8329,6 +8171,10 @@ uint8 Player::FindEquipSlot( ItemPrototype const* proto, uint32 slot, bool swap if (pClass == CLASS_WARLOCK) slots[0] = EQUIPMENT_SLOT_RANGED; break; + case ITEM_SUBCLASS_ARMOR_SIGIL: + if (pClass == CLASS_DEATH_KNIGHT) + slots[0] = EQUIPMENT_SLOT_RANGED; + break; } break; } @@ -8354,14 +8200,8 @@ uint8 Player::FindEquipSlot( ItemPrototype const* proto, uint32 slot, bool swap { if ( slots[i] != NULL_SLOT && !GetItemByPos( INVENTORY_SLOT_BAG_0, slots[i] ) ) { - // in case 2hand equipped weapon offhand slot empty but not free - if(slots[i]==EQUIPMENT_SLOT_OFFHAND) - { - Item* mainItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND ); - if(!mainItem || mainItem->GetProto()->InventoryType != INVTYPE_2HWEAPON) - return slots[i]; - } - else + // in case 2hand equipped weapon (without titan grip) offhand slot empty but not free + if(slots[i]!=EQUIPMENT_SLOT_OFFHAND || !IsTwoHandUsed()) return slots[i]; } } @@ -8411,7 +8251,7 @@ uint8 Player::CanUnequipItems( uint32 item, uint32 count ) const return EQUIP_ERR_OK; } } - for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++) + for(int i = KEYRING_SLOT_START; i < QUESTBAG_SLOT_END; i++) { pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); if( pItem && pItem->GetEntry() == item ) @@ -8453,7 +8293,7 @@ uint32 Player::GetItemCount( uint32 item, bool inBankAlso, Item* skipItem ) cons if( pItem && pItem != skipItem && pItem->GetEntry() == item ) count += pItem->GetCount(); } - for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++) + for(int i = KEYRING_SLOT_START; i < QUESTBAG_SLOT_END; i++) { Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); if( pItem && pItem != skipItem && pItem->GetEntry() == item ) @@ -8513,7 +8353,7 @@ Item* Player::GetItemByGuid( uint64 guid ) const if( pItem && pItem->GetGUID() == guid ) return pItem; } - for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++) + for(int i = KEYRING_SLOT_START; i < QUESTBAG_SLOT_END; i++) { Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); if( pItem && pItem->GetGUID() == guid ) @@ -8559,7 +8399,7 @@ Item* Player::GetItemByPos( uint16 pos ) const Item* Player::GetItemByPos( uint8 bag, uint8 slot ) const { - if( bag == INVENTORY_SLOT_BAG_0 && ( slot < BANK_SLOT_BAG_END || slot >= KEYRING_SLOT_START && slot < KEYRING_SLOT_END ) ) + if( bag == INVENTORY_SLOT_BAG_0 && ( slot < BANK_SLOT_BAG_END || slot >= KEYRING_SLOT_START && slot < QUESTBAG_SLOT_END ) ) return m_items[slot]; else if(bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END || bag >= BANK_SLOT_BAG_START && bag < BANK_SLOT_BAG_END ) @@ -8582,14 +8422,14 @@ Item* Player::GetWeaponForAttack(WeaponAttackType attackType, bool useable) cons default: return NULL; } - Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + Item* item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, slot); if (!item || item->GetProto()->Class != ITEM_CLASS_WEAPON) return NULL; if(!useable) return item; - if( item->IsBroken() || !IsUseEquipedWeapon(attackType==BASE_ATTACK) ) + if( item->IsBroken() || IsInFeralForm()) return NULL; return item; @@ -8597,7 +8437,7 @@ Item* Player::GetWeaponForAttack(WeaponAttackType attackType, bool useable) cons Item* Player::GetShield(bool useable) const { - Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); + Item* item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); if (!item || item->GetProto()->Class != ITEM_CLASS_ARMOR) return NULL; @@ -8610,7 +8450,7 @@ Item* Player::GetShield(bool useable) const return item; } -uint32 Player::GetAttackBySlot( uint8 slot ) +uint8 Player::GetAttackBySlot( uint8 slot ) { switch(slot) { @@ -8637,7 +8477,7 @@ bool Player::IsInventoryPos( uint8 bag, uint8 slot ) return true; if( bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END ) return true; - if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= KEYRING_SLOT_START && slot < KEYRING_SLOT_END ) ) + if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= KEYRING_SLOT_START && slot < QUESTBAG_SLOT_END ) ) return true; return false; } @@ -8758,7 +8598,7 @@ bool Player::HasItemCount( uint32 item, uint32 count, bool inBankAlso ) const return true; } } - for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++) + for(int i = KEYRING_SLOT_START; i < QUESTBAG_SLOT_END; i++) { Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); if( pItem && pItem->GetEntry() == item ) @@ -8818,31 +8658,76 @@ bool Player::HasItemCount( uint32 item, uint32 count, bool inBankAlso ) const return false; } -Item* Player::GetItemOrItemWithGemEquipped( uint32 item ) const +bool Player::HasItemOrGemWithIdEquipped( uint32 item, uint32 count, uint8 except_slot ) const { - Item *pItem; - for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++) + uint32 tempcount = 0; + for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i) { - pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); - if( pItem && pItem->GetEntry() == item ) - return pItem; + if(i==int(except_slot)) + continue; + + Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); + if( pItem && pItem->GetEntry() == item) + { + tempcount += pItem->GetCount(); + if( tempcount >= count ) + return true; + } } ItemPrototype const *pProto = objmgr.GetItemPrototype(item); if (pProto && pProto->GemProperties) { - for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++) + for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i) { - pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); - if( pItem && pItem->GetProto()->Socket[0].Color ) + if(i==int(except_slot)) + continue; + + Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); + if( pItem && pItem->GetProto()->Socket[0].Color) { - if (pItem->GetGemCountWithID(item) > 0 ) - return pItem; + tempcount += pItem->GetGemCountWithID(item); + if( tempcount >= count ) + return true; } } } - return NULL; + return false; +} + +bool Player::HasItemOrGemWithLimitCategoryEquipped( uint32 limitCategory, uint32 count, uint8 except_slot ) const +{ + uint32 tempcount = 0; + for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i) + { + if(i==int(except_slot)) + continue; + + Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); + if (!pItem) + continue; + + ItemPrototype const *pProto = pItem->GetProto(); + if (!pProto) + continue; + + if (pProto->ItemLimitCategory == limitCategory) + { + tempcount += pItem->GetCount(); + if( tempcount >= count ) + return true; + } + + if( pProto->Socket[0].Color) + { + tempcount += pItem->GetGemCountWithLimitCategory(limitCategory); + if( tempcount >= count ) + return true; + } + } + + return false; } uint8 Player::_CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count ) const @@ -8856,12 +8741,12 @@ uint8 Player::_CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, } // no maximum - if(pProto->MaxCount == 0) + if(pProto->MaxCount <= 0) return EQUIP_ERR_OK; uint32 curcount = GetItemCount(pProto->ItemId,true,pItem); - if( curcount + count > pProto->MaxCount ) + if (curcount + count > uint32(pProto->MaxCount)) { if(no_space_count) *no_space_count = count +curcount - pProto->MaxCount; @@ -8876,13 +8761,13 @@ bool Player::HasItemTotemCategory( uint32 TotemCategory ) const Item *pItem; for(uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i) { - pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); + pItem = GetUseableItemByPos( INVENTORY_SLOT_BAG_0, i ); if( pItem && IsTotemCategoryCompatiableWith(pItem->GetProto()->TotemCategory,TotemCategory )) return true; } - for(uint8 i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; ++i) + for(uint8 i = KEYRING_SLOT_START; i < QUESTBAG_SLOT_END; ++i) { - pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); + pItem = GetUseableItemByPos( INVENTORY_SLOT_BAG_0, i ); if( pItem && IsTotemCategoryCompatiableWith(pItem->GetProto()->TotemCategory,TotemCategory )) return true; } @@ -8892,7 +8777,7 @@ bool Player::HasItemTotemCategory( uint32 TotemCategory ) const { for(uint32 j = 0; j < pBag->GetBagSize(); ++j) { - pItem = GetItemByPos( i, j ); + pItem = GetUseableItemByPos( i, j ); if( pItem && IsTotemCategoryCompatiableWith(pItem->GetProto()->TotemCategory,TotemCategory )) return true; } @@ -8920,6 +8805,18 @@ 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 (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) + if(slot >= CURRENCYTOKEN_SLOT_START && slot < CURRENCYTOKEN_SLOT_END && !(pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS)) + return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; + + // guestbag case (not use) + if(slot >= QUESTBAG_SLOT_START && slot < QUESTBAG_SLOT_END) + return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; + // prevent cheating if(slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END || slot >= PLAYER_SLOT_END) return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; @@ -8939,7 +8836,7 @@ uint8 Player::_CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountV } // non empty stack with space - need_space = pProto->Stackable; + need_space = pProto->GetMaxStackSize(); } // non empty slot, check item type else @@ -8949,10 +8846,11 @@ uint8 Player::_CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountV return EQUIP_ERR_ITEM_CANT_STACK; // check free space - if(pItem2->GetCount() >= pProto->Stackable) + if(pItem2->GetCount() >= pProto->GetMaxStackSize()) return EQUIP_ERR_ITEM_CANT_STACK; - need_space = pProto->Stackable - pItem2->GetCount(); + // free stack space or infinity + need_space = pProto->GetMaxStackSize() - pItem2->GetCount(); } if(need_space > count) @@ -9006,9 +8904,9 @@ uint8 Player::_CanStoreItem_InBag( uint8 bag, ItemPosCountVec &dest, ItemPrototy if( pItem2 ) { - if(pItem2->GetEntry() == pProto->ItemId && pItem2->GetCount() < pProto->Stackable ) + if(pItem2->GetEntry() == pProto->ItemId && pItem2->GetCount() < pProto->GetMaxStackSize()) { - uint32 need_space = pProto->Stackable - pItem2->GetCount(); + uint32 need_space = pProto->GetMaxStackSize() - pItem2->GetCount(); if(need_space > count) need_space = count; @@ -9025,7 +8923,7 @@ uint8 Player::_CanStoreItem_InBag( uint8 bag, ItemPosCountVec &dest, ItemPrototy } else { - uint32 need_space = pProto->Stackable; + uint32 need_space = pProto->GetMaxStackSize(); if(need_space > count) need_space = count; @@ -9063,9 +8961,9 @@ uint8 Player::_CanStoreItem_InInventorySlots( uint8 slot_begin, uint8 slot_end, if( pItem2 ) { - if(pItem2->GetEntry() == pProto->ItemId && pItem2->GetCount() < pProto->Stackable ) + if(pItem2->GetEntry() == pProto->ItemId && pItem2->GetCount() < pProto->GetMaxStackSize()) { - uint32 need_space = pProto->Stackable - pItem2->GetCount(); + uint32 need_space = pProto->GetMaxStackSize() - pItem2->GetCount(); if(need_space > count) need_space = count; ItemPosCount newPosition = ItemPosCount((INVENTORY_SLOT_BAG_0 << 8) | j, need_space); @@ -9081,7 +8979,7 @@ uint8 Player::_CanStoreItem_InInventorySlots( uint8 slot_begin, uint8 slot_end, } else { - uint32 need_space = pProto->Stackable; + uint32 need_space = pProto->GetMaxStackSize(); if(need_space > count) need_space = count; @@ -9160,11 +9058,11 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 if( bag != NULL_BAG ) { // search stack in bag for merge to - if( pProto->Stackable > 1 ) + if( pProto->Stackable != 1 ) { if( bag == INVENTORY_SLOT_BAG_0 ) // inventory { - res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,KEYRING_SLOT_END,dest,pProto,count,true,pItem,bag,slot); + res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,QUESTBAG_SLOT_END,dest,pProto,count,true,pItem,bag,slot); if(res!=EQUIP_ERR_OK) { if(no_space_count) @@ -9250,6 +9148,44 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 *no_space_count = count + no_similar_count; return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; } + + res = _CanStoreItem_InInventorySlots(CURRENCYTOKEN_SLOT_START,CURRENCYTOKEN_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; + + if(no_space_count) + *no_space_count = count + no_similar_count; + return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; + } + } + else if(pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS) + { + res = _CanStoreItem_InInventorySlots(CURRENCYTOKEN_SLOT_START,CURRENCYTOKEN_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; + + if(no_space_count) + *no_space_count = count + no_similar_count; + return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; + } } res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,INVENTORY_SLOT_ITEM_END,dest,pProto,count,false,pItem,bag,slot); @@ -9298,9 +9234,9 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 // not specific bag or have space for partly store only in specific bag // search stack for merge to - if( pProto->Stackable > 1 ) + if( pProto->Stackable != 1 ) { - res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,KEYRING_SLOT_END,dest,pProto,count,true,pItem,bag,slot); + res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,QUESTBAG_SLOT_END,dest,pProto,count,true,pItem,bag,slot); if(res!=EQUIP_ERR_OK) { if(no_space_count) @@ -9398,6 +9334,26 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3 return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; } } + else if(pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS) + { + res = _CanStoreItem_InInventorySlots(CURRENCYTOKEN_SLOT_START,CURRENCYTOKEN_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; + + if(no_space_count) + *no_space_count = count + no_similar_count; + return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; + } + } for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++) { @@ -9468,10 +9424,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_tokens[CURRENCYTOKEN_SLOT_END-CURRENCYTOKEN_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_tokens,0,sizeof(int)*(CURRENCYTOKEN_SLOT_END-CURRENCYTOKEN_SLOT_START)); for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++) { @@ -9493,6 +9451,16 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const } } + for(int i = CURRENCYTOKEN_SLOT_START; i < CURRENCYTOKEN_SLOT_END; i++) + { + pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); + + if (pItem2 && !pItem2->IsInTrade()) + { + inv_tokens[i-CURRENCYTOKEN_SLOT_START] = pItem2->GetCount(); + } + } + for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++) { if(Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i )) @@ -9536,14 +9504,14 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const return res; // search stack for merge to - if( pProto->Stackable > 1 ) + if( pProto->Stackable != 1 ) { bool b_found = false; for(int t = KEYRING_SLOT_START; t < KEYRING_SLOT_END; t++) { pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, t ); - if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_keys[t-KEYRING_SLOT_START] + pItem->GetCount() <= pProto->Stackable ) + if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_keys[t-KEYRING_SLOT_START] + pItem->GetCount() <= pProto->GetMaxStackSize()) { inv_keys[t-KEYRING_SLOT_START] += pItem->GetCount(); b_found = true; @@ -9552,10 +9520,22 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const } if (b_found) continue; + for(int t = CURRENCYTOKEN_SLOT_START; t < CURRENCYTOKEN_SLOT_END; t++) + { + pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, t ); + if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_tokens[t-CURRENCYTOKEN_SLOT_START] + pItem->GetCount() <= pProto->GetMaxStackSize()) + { + inv_tokens[t-CURRENCYTOKEN_SLOT_START] += pItem->GetCount(); + b_found = true; + break; + } + } + if (b_found) continue; + for(int t = INVENTORY_SLOT_ITEM_START; t < INVENTORY_SLOT_ITEM_END; t++) { pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, t ); - if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_slot_items[t-INVENTORY_SLOT_ITEM_START] + pItem->GetCount() <= pProto->Stackable ) + if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_slot_items[t-INVENTORY_SLOT_ITEM_START] + pItem->GetCount() <= pProto->GetMaxStackSize()) { inv_slot_items[t-INVENTORY_SLOT_ITEM_START] += pItem->GetCount(); b_found = true; @@ -9567,12 +9547,12 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const for(int t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; t++) { pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, t ); - if( pBag ) + if( pBag && ItemCanGoIntoBag(pItem->GetProto(), pBag->GetProto())) { for(uint32 j = 0; j < pBag->GetBagSize(); j++) { pItem2 = GetItemByPos( t, j ); - if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_bags[t-INVENTORY_SLOT_BAG_START][j] + pItem->GetCount() <= pProto->Stackable ) + if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_bags[t-INVENTORY_SLOT_BAG_START][j] + pItem->GetCount() <= pProto->GetMaxStackSize()) { inv_bags[t-INVENTORY_SLOT_BAG_START][j] += pItem->GetCount(); b_found = true; @@ -9604,6 +9584,21 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const if (b_found) continue; + if(pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS) + { + for(uint32 t = CURRENCYTOKEN_SLOT_START; t < CURRENCYTOKEN_SLOT_END; ++t) + { + if( inv_tokens[t-CURRENCYTOKEN_SLOT_START] == 0 ) + { + inv_tokens[t-CURRENCYTOKEN_SLOT_START] = 1; + b_found = true; + break; + } + } + } + + if (b_found) continue; + for(int t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; t++) { pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, t ); @@ -9699,11 +9694,6 @@ uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bo ItemPrototype const *pProto = pItem->GetProto(); if( pProto ) { - // May be here should be more stronger checks; STUNNED checked - // ROOT, CONFUSED, DISTRACTED, FLEEING this needs to be checked. - if (not_loading && hasUnitState(UNIT_STAT_STUNNED)) - return EQUIP_ERR_YOU_ARE_STUNNED; - if(pItem->IsBindedNotWith(GetGUID())) return EQUIP_ERR_DONT_OWN_THAT_ITEM; @@ -9712,24 +9702,33 @@ uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bo if(res != EQUIP_ERR_OK) return res; - // do not allow equipping gear except weapons, offhands, projectiles, relics in - // - combat - // - in-progress arenas - if( !pProto->CanChangeEquipStateInCombat() ) + // check this only in game + if(not_loading) { - if( isInCombat() ) - return EQUIP_ERR_NOT_IN_COMBAT; + // May be here should be more stronger checks; STUNNED checked + // ROOT, CONFUSED, DISTRACTED, FLEEING this needs to be checked. + if (hasUnitState(UNIT_STAT_STUNNED)) + return EQUIP_ERR_YOU_ARE_STUNNED; + + // do not allow equipping gear except weapons, offhands, projectiles, relics in + // - combat + // - in-progress arenas + if( !pProto->CanChangeEquipStateInCombat() ) + { + if( isInCombat() ) + return EQUIP_ERR_NOT_IN_COMBAT; - if(BattleGround* bg = GetBattleGround()) - if( bg->isArena() && bg->GetStatus() == STATUS_IN_PROGRESS ) - return EQUIP_ERR_NOT_DURING_ARENA_MATCH; - } + if(BattleGround* bg = GetBattleGround()) + if( bg->isArena() && bg->GetStatus() == STATUS_IN_PROGRESS ) + return EQUIP_ERR_NOT_DURING_ARENA_MATCH; + } - if(isInCombat()&& pProto->Class == ITEM_CLASS_WEAPON && m_weaponChangeTimer != 0) - return EQUIP_ERR_CANT_DO_RIGHT_NOW; // maybe exist better err + if(isInCombat()&& pProto->Class == ITEM_CLASS_WEAPON && m_weaponChangeTimer != 0) + return EQUIP_ERR_CANT_DO_RIGHT_NOW; // maybe exist better err - if(IsNonMeleeSpellCasted(false)) - return EQUIP_ERR_CANT_DO_RIGHT_NOW; + if(IsNonMeleeSpellCasted(false)) + return EQUIP_ERR_CANT_DO_RIGHT_NOW; + } uint8 eslot = FindEquipSlot( pProto, slot, swap ); if( eslot == NULL_SLOT ) @@ -9741,33 +9740,9 @@ uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bo if( !swap && GetItemByPos( INVENTORY_SLOT_BAG_0, eslot ) ) return EQUIP_ERR_NO_EQUIPMENT_SLOT_AVAILABLE; - // check unique-equipped on item - if (pProto->Flags & ITEM_FLAGS_UNIQUE_EQUIPPED) - { - // there is an equip limit on this item - Item* tItem = GetItemOrItemWithGemEquipped(pProto->ItemId); - if (tItem && (!swap || tItem->GetSlot() != eslot ) ) - return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE; - } - - // check unique-equipped on gems - for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot) - { - uint32 enchant_id = pItem->GetEnchantmentId(EnchantmentSlot(enchant_slot)); - if(!enchant_id) - continue; - SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id); - if(!enchantEntry) - continue; - - ItemPrototype const* pGem = objmgr.GetItemPrototype(enchantEntry->GemID); - if(pGem && (pGem->Flags & ITEM_FLAGS_UNIQUE_EQUIPPED)) - { - Item* tItem = GetItemOrItemWithGemEquipped(enchantEntry->GemID); - if(tItem && (!swap || tItem->GetSlot() != eslot )) - return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE; - } - } + // if swap ignore item (equipped also) + if(uint8 res = CanEquipUniqueItem(pItem, swap ? eslot : NULL_SLOT)) + return res; // check unique-equipped special item classes if (pProto->Class == ITEM_CLASS_QUIVER) @@ -9794,33 +9769,42 @@ uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bo if(eslot == EQUIPMENT_SLOT_OFFHAND) { - if( type == INVTYPE_WEAPON || type == INVTYPE_WEAPONOFFHAND ) + if (type == INVTYPE_WEAPON || type == INVTYPE_WEAPONOFFHAND) { if(!CanDualWield()) return EQUIP_ERR_CANT_DUAL_WIELD; } - - Item *mainItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND ); - if(mainItem) + else if (type == INVTYPE_2HWEAPON) { - if(mainItem->GetProto()->InventoryType == INVTYPE_2HWEAPON) - return EQUIP_ERR_CANT_EQUIP_WITH_TWOHANDED; + if(!CanDualWield() || !CanTitanGrip()) + return EQUIP_ERR_CANT_DUAL_WIELD; } + + if(IsTwoHandUsed()) + return EQUIP_ERR_CANT_EQUIP_WITH_TWOHANDED; } // equip two-hand weapon case (with possible unequip 2 items) if( type == INVTYPE_2HWEAPON ) { - if(eslot != EQUIPMENT_SLOT_MAINHAND) + if (eslot == EQUIPMENT_SLOT_OFFHAND) + { + if (!CanTitanGrip()) + return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED; + } + else if (eslot != EQUIPMENT_SLOT_MAINHAND) return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED; - // offhand item must can be stored in inventory for offhand item and it also must be unequipped - Item *offItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND ); - ItemPosCountVec off_dest; - if( offItem && (!not_loading || - CanUnequipItem(uint16(INVENTORY_SLOT_BAG_0) << 8 | EQUIPMENT_SLOT_OFFHAND,false) != EQUIP_ERR_OK || - CanStoreItem( NULL_BAG, NULL_SLOT, off_dest, offItem, false ) != EQUIP_ERR_OK ) ) - return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_INVENTORY_FULL; + if (!CanTitanGrip()) + { + // offhand item must can be stored in inventory for offhand item and it also must be unequipped + Item *offItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND ); + ItemPosCountVec off_dest; + if( offItem && (!not_loading || + CanUnequipItem(uint16(INVENTORY_SLOT_BAG_0) << 8 | EQUIPMENT_SLOT_OFFHAND,false) != EQUIP_ERR_OK || + CanStoreItem( NULL_BAG, NULL_SLOT, off_dest, offItem, false ) != EQUIP_ERR_OK ) ) + return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_INVENTORY_FULL; + } } dest = ((INVENTORY_SLOT_BAG_0 << 8) | eslot); return EQUIP_ERR_OK; @@ -9892,29 +9876,17 @@ uint8 Player::CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *p // in specific slot if( bag != NULL_BAG && slot != NULL_SLOT ) { - if( pProto->InventoryType == INVTYPE_BAG ) + if( slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END ) { - Bag *pBag = (Bag*)pItem; - if( pBag ) - { - if( slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END ) - { - if( !HasBankBagSlot( slot ) ) - return EQUIP_ERR_MUST_PURCHASE_THAT_BAG_SLOT; - if( uint8 cantuse = CanUseItem( pItem, not_loading ) != EQUIP_ERR_OK ) - return cantuse; - } - else - { - if( !pBag->IsEmpty() ) - return EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG; - } - } - } - else - { - if( slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END ) + if (!pItem->IsBag()) return EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT; + + Bag *pBag = (Bag*)pItem; + if( !HasBankBagSlot( slot ) ) + return EQUIP_ERR_MUST_PURCHASE_THAT_BAG_SLOT; + + if( uint8 cantuse = CanUseItem( pItem, not_loading ) != EQUIP_ERR_OK ) + return cantuse; } res = _CanStoreItem_InSpecificSlot(bag,slot,dest,pProto,count,swap,pItem); @@ -9938,7 +9910,7 @@ uint8 Player::CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *p } // search stack in bag for merge to - if( pProto->Stackable > 1 ) + if( pProto->Stackable != 1 ) { if( bag == INVENTORY_SLOT_BAG_0 ) { @@ -9990,7 +9962,7 @@ uint8 Player::CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *p // not specific bag or have space for partly store only in specific bag // search stack for merge to - if( pProto->Stackable > 1 ) + if( pProto->Stackable != 1 ) { // in slots res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START,BANK_SLOT_ITEM_END,dest,pProto,count,true,pItem,bag,slot); @@ -10146,7 +10118,7 @@ uint8 Player::CanUseAmmo( uint32 item ) const } if( pProto->RequiredSpell != 0 && !HasSpell( pProto->RequiredSpell ) ) return EQUIP_ERR_NO_REQUIRED_PROFICIENCY; - /*if( GetReputation() < pProto->RequiredReputation ) + /*if( GetReputationMgr().GetReputation() < pProto->RequiredReputation ) return EQUIP_ERR_CANT_EQUIP_REPUTATION; */ if( getLevel() < pProto->RequiredLevel ) @@ -10220,7 +10192,7 @@ Item* Player::StoreItem( ItemPosCountVec const& dest, Item* pItem, bool update ) return NULL; Item* lastItem = pItem; - + uint32 entry = pItem->GetEntry(); for(ItemPosCountVec::const_iterator itr = dest.begin(); itr != dest.end(); ) { uint16 pos = itr->pos; @@ -10236,7 +10208,7 @@ Item* Player::StoreItem( ItemPosCountVec const& dest, Item* pItem, bool update ) lastItem = _StoreItem(pos,pItem,count,true,update); } - + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM, entry); return lastItem; } @@ -10278,6 +10250,10 @@ Item* Player::_StoreItem( uint16 pos, Item *pItem, uint32 count, bool clone, boo pItem->SetSlot( slot ); pItem->SetContainer( NULL ); + // need update known currency + if (slot >= CURRENCYTOKEN_SLOT_START && slot < CURRENCYTOKEN_SLOT_END) + UpdateKnownCurrencies(pItem->GetEntry(),true); + if( IsInWorld() && update ) { pItem->AddToWorld(); @@ -10446,6 +10422,8 @@ Item* Player::EquipItem( uint16 pos, Item *pItem, bool update ) } } + // only for full equip instead adding to stack + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM, pItem->GetEntry()); return pItem; } @@ -10560,23 +10538,32 @@ void Player::RemoveItem( uint8 bag, uint8 slot, bool update ) // remove item dependent auras and casts (only weapon and armor slots) if(slot < EQUIPMENT_SLOT_END) + { RemoveItemDependentAurasAndCasts(pItem); - // remove held enchantments - if ( slot == EQUIPMENT_SLOT_MAINHAND ) - { - if (pItem->GetItemSuffixFactor()) + // remove held enchantments, update expertise + if ( slot == EQUIPMENT_SLOT_MAINHAND ) { - pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_3); - pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_4); - } - else - { - pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_0); - pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_1); + if (pItem->GetItemSuffixFactor()) + { + pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_3); + pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_4); + } + else + { + pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_0); + pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_1); + } + + UpdateExpertise(BASE_ATTACK); } + else if( slot == EQUIPMENT_SLOT_OFFHAND ) + UpdateExpertise(OFF_ATTACK); } } + // need update known currency + else if (slot >= CURRENCYTOKEN_SLOT_START && slot < CURRENCYTOKEN_SLOT_END) + UpdateKnownCurrencies(pItem->GetEntry(),false); m_items[slot] = NULL; SetUInt64Value((uint16)(PLAYER_FIELD_INV_SLOT_HEAD + (slot*2)), 0); @@ -10595,11 +10582,6 @@ void Player::RemoveItem( uint8 bag, uint8 slot, bool update ) pItem->SetSlot( NULL_SLOT ); if( IsInWorld() && update ) pItem->SendUpdateToPlayer( this ); - - if( slot == EQUIPMENT_SLOT_MAINHAND ) - UpdateExpertise(BASE_ATTACK); - else if( slot == EQUIPMENT_SLOT_OFFHAND ) - UpdateExpertise(OFF_ATTACK); } } @@ -10684,9 +10666,18 @@ void Player::DestroyItem( uint8 bag, uint8 slot, bool update ) // remove item dependent auras and casts (only weapon and armor slots) RemoveItemDependentAurasAndCasts(pItem); + // update expertise + if ( slot == EQUIPMENT_SLOT_MAINHAND ) + UpdateExpertise(BASE_ATTACK); + else if( slot == EQUIPMENT_SLOT_OFFHAND ) + UpdateExpertise(OFF_ATTACK); + // equipment visual show SetVisibleItemSlot(slot,NULL); } + // need update known currency + else if (slot >= CURRENCYTOKEN_SLOT_START && slot < CURRENCYTOKEN_SLOT_END) + UpdateKnownCurrencies(pItem->GetEntry(),false); m_items[slot] = NULL; } @@ -10709,60 +10700,61 @@ void Player::DestroyItem( uint8 bag, uint8 slot, bool update ) void Player::DestroyItemCount( uint32 item, uint32 count, bool update, bool unequip_check) { sLog.outDebug( "STORAGE: DestroyItemCount item = %u, count = %u", item, count); - Item *pItem; - ItemPrototype const *pProto; uint32 remcount = 0; // in inventory for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++) { - pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); - if( pItem && pItem->GetEntry() == item ) + if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i )) { - if( pItem->GetCount() + remcount <= count ) + if (pItem->GetEntry() == item) { - // all items in inventory can unequipped - remcount += pItem->GetCount(); - DestroyItem( INVENTORY_SLOT_BAG_0, i, update); + if (pItem->GetCount() + remcount <= count) + { + // all items in inventory can unequipped + remcount += pItem->GetCount(); + DestroyItem( INVENTORY_SLOT_BAG_0, i, update); - if(remcount >=count) + if (remcount >=count) + return; + } + else + { + ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount ); + pItem->SetCount( pItem->GetCount() - count + remcount ); + if (IsInWorld() & update) + pItem->SendUpdateToPlayer( this ); + pItem->SetState(ITEM_CHANGED, this); return; - } - else - { - pProto = pItem->GetProto(); - ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount ); - pItem->SetCount( pItem->GetCount() - count + remcount ); - if( IsInWorld() & update ) - pItem->SendUpdateToPlayer( this ); - pItem->SetState(ITEM_CHANGED, this); - return; + } } } } - for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++) + + for(int i = KEYRING_SLOT_START; i < QUESTBAG_SLOT_END; i++) { - pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); - if( pItem && pItem->GetEntry() == item ) + if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i )) { - if( pItem->GetCount() + remcount <= count ) + if (pItem->GetEntry() == item) { - // all keys can be unequipped - remcount += pItem->GetCount(); - DestroyItem( INVENTORY_SLOT_BAG_0, i, update); + if (pItem->GetCount() + remcount <= count) + { + // all keys can be unequipped + remcount += pItem->GetCount(); + DestroyItem( INVENTORY_SLOT_BAG_0, i, update); - if(remcount >=count) + if (remcount >=count) + return; + } + else + { + ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount ); + pItem->SetCount( pItem->GetCount() - count + remcount ); + if (IsInWorld() & update) + pItem->SendUpdateToPlayer( this ); + pItem->SetState(ITEM_CHANGED, this); return; - } - else - { - pProto = pItem->GetProto(); - ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount ); - pItem->SetCount( pItem->GetCount() - count + remcount ); - if( IsInWorld() & update ) - pItem->SendUpdateToPlayer( this ); - pItem->SetState(ITEM_CHANGED, this); - return; + } } } } @@ -10774,27 +10766,28 @@ void Player::DestroyItemCount( uint32 item, uint32 count, bool update, bool uneq { for(uint32 j = 0; j < pBag->GetBagSize(); j++) { - pItem = pBag->GetItemByPos(j); - if( pItem && pItem->GetEntry() == item ) + if(Item* pItem = pBag->GetItemByPos(j)) { - // all items in bags can be unequipped - if( pItem->GetCount() + remcount <= count ) + if (pItem->GetEntry() == item) { - remcount += pItem->GetCount(); - DestroyItem( i, j, update ); + // all items in bags can be unequipped + if (pItem->GetCount() + remcount <= count) + { + remcount += pItem->GetCount(); + DestroyItem( i, j, update ); - if(remcount >=count) + if (remcount >=count) + return; + } + else + { + ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount ); + pItem->SetCount( pItem->GetCount() - count + remcount ); + if (IsInWorld() && update) + pItem->SendUpdateToPlayer( this ); + pItem->SetState(ITEM_CHANGED, this); return; - } - else - { - pProto = pItem->GetProto(); - ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount ); - pItem->SetCount( pItem->GetCount() - count + remcount ); - if( IsInWorld() && update ) - pItem->SendUpdateToPlayer( this ); - pItem->SetState(ITEM_CHANGED, this); - return; + } } } } @@ -10804,29 +10797,30 @@ void Player::DestroyItemCount( uint32 item, uint32 count, bool update, bool uneq // in equipment and bag list for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++) { - pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); - if( pItem && pItem->GetEntry() == item ) + if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i )) { - if( pItem->GetCount() + remcount <= count ) + if (pItem && pItem->GetEntry() == item) { - if(!unequip_check || CanUnequipItem(INVENTORY_SLOT_BAG_0 << 8 | i,false) == EQUIP_ERR_OK ) + if (pItem->GetCount() + remcount <= count) { - remcount += pItem->GetCount(); - DestroyItem( INVENTORY_SLOT_BAG_0, i, update); + if (!unequip_check || CanUnequipItem(INVENTORY_SLOT_BAG_0 << 8 | i,false) == EQUIP_ERR_OK ) + { + remcount += pItem->GetCount(); + DestroyItem( INVENTORY_SLOT_BAG_0, i, update); - if(remcount >=count) - return; + if (remcount >=count) + return; + } + } + else + { + ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount ); + pItem->SetCount( pItem->GetCount() - count + remcount ); + if (IsInWorld() & update) + pItem->SendUpdateToPlayer( this ); + pItem->SetState(ITEM_CHANGED, this); + return; } - } - else - { - pProto = pItem->GetProto(); - ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount ); - pItem->SetCount( pItem->GetCount() - count + remcount ); - if( IsInWorld() & update ) - pItem->SendUpdateToPlayer( this ); - pItem->SetState(ITEM_CHANGED, this); - return; } } } @@ -10838,40 +10832,28 @@ void Player::DestroyZoneLimitedItem( bool update, uint32 new_zone ) // in inventory for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++) - { - Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); - if( pItem && pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone) ) - DestroyItem( INVENTORY_SLOT_BAG_0, i, update); - } - for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++) - { - Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); - if( pItem && pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone) ) - DestroyItem( INVENTORY_SLOT_BAG_0, i, update); - } + if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i )) + if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone)) + DestroyItem( INVENTORY_SLOT_BAG_0, i, update); + + for(int i = KEYRING_SLOT_START; i < QUESTBAG_SLOT_END; i++) + if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i )) + if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone)) + DestroyItem( INVENTORY_SLOT_BAG_0, i, update); // in inventory bags for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++) - { - Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ); - if( pBag ) - { + if (Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i )) for(uint32 j = 0; j < pBag->GetBagSize(); j++) - { - Item* pItem = pBag->GetItemByPos(j); - if( pItem && pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone) ) - DestroyItem( i, j, update); - } - } - } + if (Item* pItem = pBag->GetItemByPos(j)) + if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone)) + DestroyItem( i, j, update); // in equipment and bag list for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++) - { - Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); - if( pItem && pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone) ) - DestroyItem( INVENTORY_SLOT_BAG_0, i, update); - } + if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i )) + if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone)) + DestroyItem( INVENTORY_SLOT_BAG_0, i, update); } void Player::DestroyConjuredItems( bool update ) @@ -10882,40 +10864,23 @@ void Player::DestroyConjuredItems( bool update ) // in inventory for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++) - { - Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); - if( pItem && pItem->GetProto() && - (pItem->GetProto()->Class == ITEM_CLASS_CONSUMABLE) && - (pItem->GetProto()->Flags & ITEM_FLAGS_CONJURED) ) - DestroyItem( INVENTORY_SLOT_BAG_0, i, update); - } + if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i )) + if (pItem->IsConjuredConsumable()) + DestroyItem( INVENTORY_SLOT_BAG_0, i, update); // in inventory bags for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++) - { - Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ); - if( pBag ) - { + if (Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i )) for(uint32 j = 0; j < pBag->GetBagSize(); j++) - { - Item* pItem = pBag->GetItemByPos(j); - if( pItem && pItem->GetProto() && - (pItem->GetProto()->Class == ITEM_CLASS_CONSUMABLE) && - (pItem->GetProto()->Flags & ITEM_FLAGS_CONJURED) ) - DestroyItem( i, j, update); - } - } - } + if (Item* pItem = pBag->GetItemByPos(j)) + if (pItem->IsConjuredConsumable()) + DestroyItem( i, j, update); // in equipment and bag list for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++) - { - Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); - if( pItem && pItem->GetProto() && - (pItem->GetProto()->Class == ITEM_CLASS_CONSUMABLE) && - (pItem->GetProto()->Flags & ITEM_FLAGS_CONJURED) ) - DestroyItem( INVENTORY_SLOT_BAG_0, i, update); - } + if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i )) + if (pItem->IsConjuredConsumable()) + DestroyItem( INVENTORY_SLOT_BAG_0, i, update); } void Player::DestroyItemCount( Item* pItem, uint32 &count, bool update ) @@ -11071,6 +11036,8 @@ void Player::SwapItem( uint16 src, uint16 dst ) return; } + // SRC checks + if(pSrcItem->m_lootGenerated) // prevent swap looting item { //best error message found for attempting to swap while looting @@ -11081,8 +11048,8 @@ void Player::SwapItem( uint16 src, uint16 dst ) // check unequip potability for equipped items and bank bags if(IsEquipmentPos ( src ) || IsBagPos ( src )) { - // bags can be swapped with empty bag slots - uint8 msg = CanUnequipItem( src, !IsBagPos ( src ) || IsBagPos ( dst )); + // bags can be swapped with empty bag slots, or with empty bag (items move possibility checked later) + uint8 msg = CanUnequipItem( src, !IsBagPos ( src ) || IsBagPos ( dst ) || pDstItem && pDstItem->IsBag() && ((Bag*)pDstItem)->IsEmpty()); if(msg != EQUIP_ERR_OK) { SendEquipError( msg, pSrcItem, pDstItem ); @@ -11097,6 +11064,34 @@ void Player::SwapItem( uint16 src, uint16 dst ) return; } + // DST checks + + if (pDstItem) + { + if(pDstItem->m_lootGenerated) // prevent swap looting item + { + //best error message found for attempting to swap while looting + SendEquipError( EQUIP_ERR_CANT_DO_RIGHT_NOW, pDstItem, NULL ); + return; + } + + // check unequip potability for equipped items and bank bags + if(IsEquipmentPos ( dst ) || IsBagPos ( dst )) + { + // bags can be swapped with empty bag slots, or with empty bag (items move possibility checked later) + uint8 msg = CanUnequipItem( dst, !IsBagPos ( dst ) || IsBagPos ( src ) || pSrcItem->IsBag() && ((Bag*)pSrcItem)->IsEmpty()); + if(msg != EQUIP_ERR_OK) + { + SendEquipError( msg, pSrcItem, pDstItem ); + return; + } + } + } + + // NOW this is or item move (swap with empty), or swap with another item (including bags in bag possitions) + // or swap empty bag with another empty or not empty bag (with items exchange) + + // Move case if( !pDstItem ) { if( IsInventoryPos( dst ) ) @@ -11139,140 +11134,187 @@ void Player::SwapItem( uint16 src, uint16 dst ) EquipItem( dest, pSrcItem, true); AutoUnequipOffhandIfNeed(); } + + return; } - else // if (!pDstItem) + + // attempt merge to / fill target item + if(!pSrcItem->IsBag() && !pDstItem->IsBag()) { - if(pDstItem->m_lootGenerated) // prevent swap looting item - { - //best error message found for attempting to swap while looting - SendEquipError( EQUIP_ERR_CANT_DO_RIGHT_NOW, pDstItem, NULL ); + uint8 msg; + ItemPosCountVec sDest; + uint16 eDest; + if( IsInventoryPos( dst ) ) + msg = CanStoreItem( dstbag, dstslot, sDest, pSrcItem, false ); + else if( IsBankPos ( dst ) ) + msg = CanBankItem( dstbag, dstslot, sDest, pSrcItem, false ); + else if( IsEquipmentPos ( dst ) ) + msg = CanEquipItem( dstslot, eDest, pSrcItem, false ); + else return; - } - // check unequip potability for equipped items and bank bags - if(IsEquipmentPos ( dst ) || IsBagPos ( dst )) + // can be merge/fill + if(msg == EQUIP_ERR_OK) { - // bags can be swapped with empty bag slots - uint8 msg = CanUnequipItem( dst, !IsBagPos ( dst ) || IsBagPos ( src ) ); - if(msg != EQUIP_ERR_OK) + if( pSrcItem->GetCount() + pDstItem->GetCount() <= pSrcItem->GetProto()->GetMaxStackSize()) { - SendEquipError( msg, pSrcItem, pDstItem ); - return; - } - } + RemoveItem(srcbag, srcslot, true); - // attempt merge to / fill target item - { - uint8 msg; - ItemPosCountVec sDest; - uint16 eDest; - if( IsInventoryPos( dst ) ) - msg = CanStoreItem( dstbag, dstslot, sDest, pSrcItem, false ); - else if( IsBankPos ( dst ) ) - msg = CanBankItem( dstbag, dstslot, sDest, pSrcItem, false ); - else if( IsEquipmentPos ( dst ) ) - msg = CanEquipItem( dstslot, eDest, pSrcItem, false ); - else - return; - - // can be merge/fill - if(msg == EQUIP_ERR_OK) - { - if( pSrcItem->GetCount() + pDstItem->GetCount() <= pSrcItem->GetProto()->Stackable ) + if( IsInventoryPos( dst ) ) + StoreItem( sDest, pSrcItem, true); + else if( IsBankPos ( dst ) ) + BankItem( sDest, pSrcItem, true); + else if( IsEquipmentPos ( dst ) ) { - RemoveItem(srcbag, srcslot, true); - - if( IsInventoryPos( dst ) ) - StoreItem( sDest, pSrcItem, true); - else if( IsBankPos ( dst ) ) - BankItem( sDest, pSrcItem, true); - else if( IsEquipmentPos ( dst ) ) - { - EquipItem( eDest, pSrcItem, true); - AutoUnequipOffhandIfNeed(); - } + EquipItem( eDest, pSrcItem, true); + AutoUnequipOffhandIfNeed(); } - else + } + else + { + pSrcItem->SetCount( pSrcItem->GetCount() + pDstItem->GetCount() - pSrcItem->GetProto()->GetMaxStackSize()); + pDstItem->SetCount( pSrcItem->GetProto()->GetMaxStackSize()); + pSrcItem->SetState(ITEM_CHANGED, this); + pDstItem->SetState(ITEM_CHANGED, this); + if( IsInWorld() ) { - pSrcItem->SetCount( pSrcItem->GetCount() + pDstItem->GetCount() - pSrcItem->GetProto()->Stackable ); - pDstItem->SetCount( pSrcItem->GetProto()->Stackable ); - pSrcItem->SetState(ITEM_CHANGED, this); - pDstItem->SetState(ITEM_CHANGED, this); - if( IsInWorld() ) - { - pSrcItem->SendUpdateToPlayer( this ); - pDstItem->SendUpdateToPlayer( this ); - } + pSrcItem->SendUpdateToPlayer( this ); + pDstItem->SendUpdateToPlayer( this ); } - return; } + return; } + } - // impossible merge/fill, do real swap - uint8 msg; + // impossible merge/fill, do real swap + uint8 msg; - // check src->dest move possibility - ItemPosCountVec sDest; - uint16 eDest; - if( IsInventoryPos( dst ) ) - msg = CanStoreItem( dstbag, dstslot, sDest, pSrcItem, true ); - else if( IsBankPos( dst ) ) - msg = CanBankItem( dstbag, dstslot, sDest, pSrcItem, true ); - else if( IsEquipmentPos( dst ) ) - { - msg = CanEquipItem( dstslot, eDest, pSrcItem, true ); - if( msg == EQUIP_ERR_OK ) - msg = CanUnequipItem( eDest, true ); - } + // check src->dest move possibility + ItemPosCountVec sDest; + uint16 eDest; + if( IsInventoryPos( dst ) ) + msg = CanStoreItem( dstbag, dstslot, sDest, pSrcItem, true ); + else if( IsBankPos( dst ) ) + msg = CanBankItem( dstbag, dstslot, sDest, pSrcItem, true ); + else if( IsEquipmentPos( dst ) ) + { + msg = CanEquipItem( dstslot, eDest, pSrcItem, true ); + if( msg == EQUIP_ERR_OK ) + msg = CanUnequipItem( eDest, true ); + } - if( msg != EQUIP_ERR_OK ) + if( msg != EQUIP_ERR_OK ) + { + SendEquipError( msg, pSrcItem, pDstItem ); + return; + } + + // check dest->src move possibility + ItemPosCountVec sDest2; + uint16 eDest2; + if( IsInventoryPos( src ) ) + msg = CanStoreItem( srcbag, srcslot, sDest2, pDstItem, true ); + else if( IsBankPos( src ) ) + msg = CanBankItem( srcbag, srcslot, sDest2, pDstItem, true ); + else if( IsEquipmentPos( src ) ) + { + msg = CanEquipItem( srcslot, eDest2, pDstItem, true); + if( msg == EQUIP_ERR_OK ) + msg = CanUnequipItem( eDest2, true); + } + + if( msg != EQUIP_ERR_OK ) + { + SendEquipError( msg, pDstItem, pSrcItem ); + return; + } + + // Check bag swap with item exchange (one from empty in not bag possition (equipped (not possible in fact) or store) + if(pSrcItem->IsBag() && pDstItem->IsBag()) + { + Bag* emptyBag = NULL; + Bag* fullBag = NULL; + if(((Bag*)pSrcItem)->IsEmpty() && !IsBagPos(src)) { - SendEquipError( msg, pSrcItem, pDstItem ); - return; + emptyBag = (Bag*)pSrcItem; + fullBag = (Bag*)pDstItem; } - - // check dest->src move possibility - ItemPosCountVec sDest2; - uint16 eDest2; - if( IsInventoryPos( src ) ) - msg = CanStoreItem( srcbag, srcslot, sDest2, pDstItem, true ); - else if( IsBankPos( src ) ) - msg = CanBankItem( srcbag, srcslot, sDest2, pDstItem, true ); - else if( IsEquipmentPos( src ) ) + else if(((Bag*)pDstItem)->IsEmpty() && !IsBagPos(dst)) { - msg = CanEquipItem( srcslot, eDest2, pDstItem, true); - if( msg == EQUIP_ERR_OK ) - msg = CanUnequipItem( eDest2, true); + emptyBag = (Bag*)pDstItem; + fullBag = (Bag*)pSrcItem; } - if( msg != EQUIP_ERR_OK ) + // bag swap (with items exchange) case + if(emptyBag && fullBag) { - SendEquipError( msg, pDstItem, pSrcItem ); - return; - } + ItemPrototype const* emotyProto = emptyBag->GetProto(); - // now do moves, remove... - RemoveItem(dstbag, dstslot, false); - RemoveItem(srcbag, srcslot, false); + uint32 count = 0; - // add to dest - if( IsInventoryPos( dst ) ) - StoreItem(sDest, pSrcItem, true); - else if( IsBankPos( dst ) ) - BankItem(sDest, pSrcItem, true); - else if( IsEquipmentPos( dst ) ) - EquipItem(eDest, pSrcItem, true); - - // add to src - if( IsInventoryPos( src ) ) - StoreItem(sDest2, pDstItem, true); - else if( IsBankPos( src ) ) - BankItem(sDest2, pDstItem, true); - else if( IsEquipmentPos( src ) ) - EquipItem(eDest2, pDstItem, true); + for(int i=0; i < fullBag->GetBagSize(); ++i) + { + Item *bagItem = fullBag->GetItemByPos(i); + if (!bagItem) + continue; - AutoUnequipOffhandIfNeed(); + ItemPrototype const* bagItemProto = bagItem->GetProto(); + if (!bagItemProto || !ItemCanGoIntoBag(bagItemProto, emotyProto)) + { + // one from items not go to empry target bag + SendEquipError( EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG, pSrcItem, pDstItem ); + return; + } + + ++count; + } + + + if (count > emptyBag->GetBagSize()) + { + // too small targeted bag + SendEquipError( EQUIP_ERR_ITEMS_CANT_BE_SWAPPED, pSrcItem, pDstItem ); + return; + } + + // Items swap + count = 0; // will pos in new bag + for(int i=0; i< fullBag->GetBagSize(); ++i) + { + Item *bagItem = fullBag->GetItemByPos(i); + if (!bagItem) + continue; + + fullBag->RemoveItem(i, true); + emptyBag->StoreItem(count, bagItem, true); + bagItem->SetState(ITEM_CHANGED, this); + + ++count; + } + } } + + // now do moves, remove... + RemoveItem(dstbag, dstslot, false); + RemoveItem(srcbag, srcslot, false); + + // add to dest + if( IsInventoryPos( dst ) ) + StoreItem(sDest, pSrcItem, true); + else if( IsBankPos( dst ) ) + BankItem(sDest, pSrcItem, true); + else if( IsEquipmentPos( dst ) ) + EquipItem(eDest, pSrcItem, true); + + // add to src + if( IsInventoryPos( src ) ) + StoreItem(sDest2, pDstItem, true); + else if( IsBankPos( src ) ) + BankItem(sDest2, pDstItem, true); + else if( IsEquipmentPos( src ) ) + EquipItem(eDest2, pDstItem, true); + + AutoUnequipOffhandIfNeed(); } void Player::AddItemToBuyBackSlot( Item *pItem ) @@ -11840,6 +11882,40 @@ void Player::ApplyEnchantment(Item *item,EnchantmentSlot slot,bool apply, bool a ((Player*)this)->ApplyRatingMod(CR_EXPERTISE, enchant_amount, apply); sLog.outDebug("+ %u EXPERTISE", enchant_amount); break; + case ITEM_MOD_ATTACK_POWER: + HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(enchant_amount), apply); + HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(enchant_amount), apply); + sLog.outDebug("+ %u ATTACK_POWER", enchant_amount); + break; + case ITEM_MOD_RANGED_ATTACK_POWER: + HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(enchant_amount), apply); + sLog.outDebug("+ %u RANGED_ATTACK_POWER", enchant_amount); + break; + case ITEM_MOD_FERAL_ATTACK_POWER: + ((Player*)this)->ApplyFeralAPBonus(enchant_amount, apply); + sLog.outDebug("+ %u FERAL_ATTACK_POWER", enchant_amount); + break; + case ITEM_MOD_SPELL_HEALING_DONE: + ((Player*)this)->ApplySpellHealingBonus(enchant_amount, apply); + sLog.outDebug("+ %u SPELL_HEALING_DONE", enchant_amount); + break; + case ITEM_MOD_SPELL_DAMAGE_DONE: + ((Player*)this)->ApplySpellDamageBonus(enchant_amount, apply); + sLog.outDebug("+ %u SPELL_DAMAGE_DONE", enchant_amount); + break; + case ITEM_MOD_MANA_REGENERATION: + ((Player*)this)->ApplyManaRegenBonus(enchant_amount, apply); + sLog.outDebug("+ %u MANA_REGENERATION", enchant_amount); + break; + case ITEM_MOD_ARMOR_PENETRATION_RATING: + ((Player*)this)->ApplyRatingMod(CR_ARMOR_PENETRATION, enchant_amount, apply); + sLog.outDebug("+ %u ARMOR PENETRATION", enchant_amount); + break; + case ITEM_MOD_SPELL_POWER: + ((Player*)this)->ApplySpellHealingBonus(enchant_amount, apply); + ((Player*)this)->ApplySpellDamageBonus(enchant_amount, apply); + sLog.outDebug("+ %u SPELL_POWER", enchant_amount); + break; default: break; } @@ -11863,8 +11939,14 @@ void Player::ApplyEnchantment(Item *item,EnchantmentSlot slot,bool apply, bool a } break; } + case ITEM_ENCHANTMENT_TYPE_USE_SPELL: + // processed in Player::CastItemUseSpell + break; + case ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET: + // nothing do.. + break; default: - sLog.outError("Unknown item enchantment display type: %d",enchant_display_type); + sLog.outError("Unknown item enchantment (id = %d) display type: %d", enchant_id, enchant_display_type); break; } /*switch(enchant_display_type)*/ } /*for*/ @@ -11930,7 +12012,7 @@ void Player::SendNewItem(Item *item, uint32 count, bool received, bool created, data << GetItemCount(item->GetEntry()); // count of items in inventory if (broadcast && GetGroup()) - GetGroup()->BroadcastPacket(&data); + GetGroup()->BroadcastPacket(&data, true); else GetSession()->SendPacket(&data); } @@ -12034,7 +12116,7 @@ void Player::SendPreparedQuest( uint64 guid ) if( pCreature ) { uint32 textid = pCreature->GetNpcTextId(); - GossipText * gossiptext = objmgr.GetGossipText(textid); + GossipText const* gossiptext = objmgr.GetGossipText(textid); if( !gossiptext ) { qe._Delay = 0; //TEXTEMOTE_MESSAGE; //zyg: player emote @@ -12225,7 +12307,7 @@ bool Player::CanCompleteQuest( uint32 quest_id ) } uint32 repFacId = qInfo->GetRepObjectiveFaction(); - if ( repFacId && GetReputation(repFacId) < qInfo->GetRepObjectiveValue() ) + if ( repFacId && GetReputationMgr().GetReputation(repFacId) < qInfo->GetRepObjectiveValue() ) return false; return true; @@ -12338,8 +12420,6 @@ void Player::AddQuest( Quest const *pQuest, Object *questGiver ) // if not exist then created with set uState==NEW and rewarded=false QuestStatusData& questStatusData = mQuestStatus[quest_id]; - if (questStatusData.uState != QUEST_NEW) - questStatusData.uState = QUEST_CHANGED; // check for repeatable quests status reset questStatusData.m_status = QUEST_STATUS_INCOMPLETE; @@ -12347,21 +12427,22 @@ void Player::AddQuest( Quest const *pQuest, Object *questGiver ) if ( pQuest->HasFlag( QUEST_TRINITY_FLAGS_DELIVER ) ) { - for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++) + for(int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) questStatusData.m_itemcount[i] = 0; } if ( pQuest->HasFlag(QUEST_TRINITY_FLAGS_KILL_OR_CAST | QUEST_TRINITY_FLAGS_SPEAKTO) ) { - for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++) + for(int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) questStatusData.m_creatureOrGOcount[i] = 0; } GiveQuestSourceItem( pQuest ); - AdjustQuestReqItemCount( pQuest ); + AdjustQuestReqItemCount( pQuest, questStatusData ); if( pQuest->GetRepObjectiveFaction() ) - SetFactionVisibleForFactionId(pQuest->GetRepObjectiveFaction()); + if(FactionEntry const* factionEntry = sFactionStore.LookupEntry(pQuest->GetRepObjectiveFaction())) + GetReputationMgr().SetVisible(factionEntry); uint32 qtime = 0; if( pQuest->HasFlag( QUEST_TRINITY_FLAGS_TIMED ) ) @@ -12370,10 +12451,10 @@ void Player::AddQuest( Quest const *pQuest, Object *questGiver ) // shared timed quest if(questGiver && questGiver->GetTypeId()==TYPEID_PLAYER) - limittime = ((Player*)questGiver)->getQuestStatusMap()[quest_id].m_timer / 1000; + limittime = ((Player*)questGiver)->getQuestStatusMap()[quest_id].m_timer / IN_MILISECONDS; AddTimedQuest( quest_id ); - questStatusData.m_timer = limittime * 1000; + questStatusData.m_timer = limittime * IN_MILISECONDS; qtime = static_cast<uint32>(time(NULL)) + limittime; } else @@ -12381,10 +12462,26 @@ void Player::AddQuest( Quest const *pQuest, Object *questGiver ) SetQuestSlot(log_slot, quest_id, qtime); + if (questStatusData.uState != QUEST_NEW) + questStatusData.uState = QUEST_CHANGED; + //starting initial quest script if(questGiver && pQuest->GetQuestStartScript()!=0) sWorld.ScriptsStart(sQuestStartScripts, pQuest->GetQuestStartScript(), questGiver, this); + // Some spells applied at quest activation + SpellAreaForQuestMapBounds saBounds = spellmgr.GetSpellAreaForQuestMapBounds(quest_id,true); + if(saBounds.first != saBounds.second) + { + uint32 zone, area; + GetZoneAndAreaId(zone,area); + + for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) + if(itr->second->autocast && itr->second->IsFitToRequirements(this,zone,area)) + if( !HasAura(itr->second->spellId) ) + CastSpell(this,itr->second->spellId,true); + } + UpdateForQuestsGO(); } @@ -12482,10 +12579,20 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver if ( getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) ) GiveXP( XP , NULL ); else - ModifyMoney( int32(pQuest->GetRewMoneyMaxLevel() * sWorld.getRate(RATE_DROP_MONEY)) ); + { + uint32 money = uint32(pQuest->GetRewMoneyMaxLevel() * sWorld.getRate(RATE_DROP_MONEY)); + ModifyMoney( money ); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD, money); + } // Give player extra money if GetRewOrReqMoney > 0 and get ReqMoney if negative - ModifyMoney( pQuest->GetRewOrReqMoney() ); + if(pQuest->GetRewOrReqMoney()) + { + ModifyMoney( pQuest->GetRewOrReqMoney() ); + + if(pQuest->GetRewOrReqMoney() > 0) + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD, pQuest->GetRewOrReqMoney()); + } // honor reward if(pQuest->GetRewHonorableKills()) @@ -12498,6 +12605,12 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver SetTitle(titleEntry); } + if(pQuest->GetBonusTalents()) + { + m_questRewardTalentCount+=pQuest->GetBonusTalents(); + InitTalentForLevel(); + } + // Send reward mail if(pQuest->GetRewMailTemplateId()) { @@ -12529,12 +12642,13 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver Loot questMailLoot; - questMailLoot.FillLoot(pQuest->GetQuestId(), LootTemplates_QuestMail, this); + questMailLoot.FillLoot(pQuest->GetQuestId(), LootTemplates_QuestMail, this,true); // fill mail MailItemsInfo mi; // item list preparing - for(size_t i = 0; mi.size() < MAX_MAIL_ITEMS && i < questMailLoot.items.size(); ++i) + 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)) { @@ -12546,23 +12660,14 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver } } - for(size_t i = 0; mi.size() < MAX_MAIL_ITEMS && i < questMailLoot.quest_items.size(); ++i) - { - if(LootItem* lootitem = questMailLoot.LootItemInSlot(i+questMailLoot.items.size(),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); - } - } - } - WorldSession::SendMailTo(this, mailType, MAIL_STATIONERY_NORMAL, senderGuidOrEntry, GetGUIDLow(), "", 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE,pQuest->GetRewMailDelaySecs(),pQuest->GetRewMailTemplateId()); } if(pQuest->IsDaily()) + { SetDailyQuestStatus(quest_id); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST, 1); + } if ( !pQuest->IsRepeatable() ) SetQuestStatus(quest_id, QUEST_STATUS_COMPLETE); @@ -12575,6 +12680,37 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver SendQuestReward( pQuest, XP, questGiver ); if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED; + if (pQuest->GetZoneOrSort() > 0) + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE, pQuest->GetZoneOrSort()); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST); + + uint32 zone = 0; + uint32 area = 0; + + // remove auras from spells with quest reward state limitations + SpellAreaForQuestMapBounds saEndBounds = spellmgr.GetSpellAreaForQuestEndMapBounds(quest_id); + if(saEndBounds.first != saEndBounds.second) + { + GetZoneAndAreaId(zone,area); + + for(SpellAreaForAreaMap::const_iterator itr = saEndBounds.first; itr != saEndBounds.second; ++itr) + if(!itr->second->IsFitToRequirements(this,zone,area)) + RemoveAurasDueToSpell(itr->second->spellId); + } + + // Some spells applied at quest reward + SpellAreaForQuestMapBounds saBounds = spellmgr.GetSpellAreaForQuestMapBounds(quest_id,false); + if(saBounds.first != saBounds.second) + { + if(!zone || !area) + GetZoneAndAreaId(zone,area); + + for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) + if(itr->second->autocast && itr->second->IsFitToRequirements(this,zone,area)) + if( !HasAura(itr->second->spellId) ) + CastSpell(this,itr->second->spellId,true); + } } void Player::FailQuest( uint32 quest_id ) @@ -12599,8 +12735,9 @@ void Player::FailTimedQuest( uint32 quest_id ) { QuestStatusData& q_status = mQuestStatus[quest_id]; - if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED; q_status.m_timer = 0; + if (q_status.uState != QUEST_NEW) + q_status.uState = QUEST_CHANGED; IncompleteQuest( quest_id ); @@ -12800,7 +12937,7 @@ bool Player::SatisfyQuestRace( Quest const* qInfo, bool msg ) bool Player::SatisfyQuestReputation( Quest const* qInfo, bool msg ) { uint32 fIdMin = qInfo->GetRequiredMinRepFaction(); //Min required rep - if(fIdMin && GetReputation(fIdMin) < qInfo->GetRequiredMinRepValue()) + if(fIdMin && GetReputationMgr().GetReputation(fIdMin) < qInfo->GetRequiredMinRepValue()) { if( msg ) SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ ); @@ -12808,7 +12945,7 @@ bool Player::SatisfyQuestReputation( Quest const* qInfo, bool msg ) } uint32 fIdMax = qInfo->GetRequiredMaxRepFaction(); //Max required rep - if(fIdMax && GetReputation(fIdMax) >= qInfo->GetRequiredMaxRepValue()) + if(fIdMax && GetReputationMgr().GetReputation(fIdMax) >= qInfo->GetRequiredMaxRepValue()) { if( msg ) SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ ); @@ -13086,18 +13223,18 @@ uint32 Player::GetReqKillOrCastCurrentCount(uint32 quest_id, int32 entry) if( !qInfo ) return 0; - for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++) + for (int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j) if ( qInfo->ReqCreatureOrGOId[j] == entry ) return mQuestStatus[quest_id].m_creatureOrGOcount[j]; return 0; } -void Player::AdjustQuestReqItemCount( Quest const* pQuest ) +void Player::AdjustQuestReqItemCount( Quest const* pQuest, QuestStatusData& questStatusData ) { if ( pQuest->HasFlag( QUEST_TRINITY_FLAGS_DELIVER ) ) { - for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++) + for(int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) { uint32 reqitemcount = pQuest->ReqItemCount[i]; if( reqitemcount != 0 ) @@ -13105,9 +13242,8 @@ void Player::AdjustQuestReqItemCount( Quest const* pQuest ) uint32 quest_id = pQuest->GetQuestId(); uint32 curitemcount = GetItemCount(pQuest->ReqItemId[i],true); - QuestStatusData& q_status = mQuestStatus[quest_id]; - q_status.m_itemcount[i] = std::min(curitemcount, reqitemcount); - if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED; + questStatusData.m_itemcount[i] = std::min(curitemcount, reqitemcount); + if (questStatusData.uState != QUEST_NEW) questStatusData.uState = QUEST_CHANGED; } } } @@ -13115,7 +13251,7 @@ void Player::AdjustQuestReqItemCount( Quest const* pQuest ) uint16 Player::FindQuestSlot( uint32 quest_id ) const { - for ( uint16 i = 0; i < MAX_QUEST_LOG_SIZE; i++ ) + for ( uint16 i = 0; i < MAX_QUEST_LOG_SIZE; ++i ) if ( GetQuestSlotQuestId(i) == quest_id ) return i; @@ -13163,7 +13299,7 @@ void Player::GroupEventHappens( uint32 questId, WorldObject const* pEventObject void Player::ItemAddedQuestCheck( uint32 entry, uint32 count ) { - for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ ) + for( int i = 0; i < MAX_QUEST_LOG_SIZE; ++i ) { uint32 questid = GetQuestSlotQuestId(i); if ( questid == 0 ) @@ -13204,7 +13340,7 @@ void Player::ItemAddedQuestCheck( uint32 entry, uint32 count ) void Player::ItemRemovedQuestCheck( uint32 entry, uint32 count ) { - for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ ) + for( int i = 0; i < MAX_QUEST_LOG_SIZE; ++i ) { uint32 questid = GetQuestSlotQuestId(i); if(!questid) @@ -13246,7 +13382,8 @@ void Player::ItemRemovedQuestCheck( uint32 entry, uint32 count ) void Player::KilledMonster( uint32 entry, uint64 guid ) { uint32 addkillcount = 1; - for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ ) + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, entry, addkillcount); + for( int i = 0; i < MAX_QUEST_LOG_SIZE; ++i ) { uint32 questid = GetQuestSlotQuestId(i); if(!questid) @@ -13301,7 +13438,7 @@ void Player::CastedCreatureOrGO( uint32 entry, uint64 guid, uint32 spell_id ) bool isCreature = IS_CREATURE_GUID(guid); uint32 addCastCount = 1; - for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ ) + for( int i = 0; i < MAX_QUEST_LOG_SIZE; ++i) { uint32 questid = GetQuestSlotQuestId(i); if(!questid) @@ -13368,7 +13505,7 @@ void Player::CastedCreatureOrGO( uint32 entry, uint64 guid, uint32 spell_id ) void Player::TalkedToCreature( uint32 entry, uint64 guid ) { uint32 addTalkCount = 1; - for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ ) + for( int i = 0; i < MAX_QUEST_LOG_SIZE; ++i ) { uint32 questid = GetQuestSlotQuestId(i); if(!questid) @@ -13423,7 +13560,7 @@ void Player::TalkedToCreature( uint32 entry, uint64 guid ) void Player::MoneyChanged( uint32 count ) { - for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ ) + for( int i = 0; i < MAX_QUEST_LOG_SIZE; ++i ) { uint32 questid = GetQuestSlotQuestId(i); if (!questid) @@ -13451,15 +13588,51 @@ void Player::MoneyChanged( uint32 count ) } } +void Player::ReputationChanged(FactionEntry const* factionEntry ) +{ + for( int i = 0; i < MAX_QUEST_LOG_SIZE; ++i ) + { + if(uint32 questid = GetQuestSlotQuestId(i)) + { + if(Quest const* qInfo = objmgr.GetQuestTemplate(questid)) + { + if(qInfo->GetRepObjectiveFaction() == factionEntry->ID ) + { + QuestStatusData& q_status = mQuestStatus[questid]; + if( q_status.m_status == QUEST_STATUS_INCOMPLETE ) + { + if(GetReputationMgr().GetReputation(factionEntry) >= qInfo->GetRepObjectiveValue()) + if ( CanCompleteQuest( questid ) ) + CompleteQuest( questid ); + } + else if( q_status.m_status == QUEST_STATUS_COMPLETE ) + { + if(GetReputationMgr().GetReputation(factionEntry) < qInfo->GetRepObjectiveValue()) + IncompleteQuest( questid ); + } + } + } + } + } +} + bool Player::HasQuestForItem( uint32 itemid ) const { - for( QuestStatusMap::const_iterator i = mQuestStatus.begin( ); i != mQuestStatus.end( ); ++i ) + for( int i = 0; i < MAX_QUEST_LOG_SIZE; ++i ) { - QuestStatusData const& q_status = i->second; + uint32 questid = GetQuestSlotQuestId(i); + if ( questid == 0 ) + continue; + + QuestStatusMap::const_iterator qs_itr = mQuestStatus.find(questid); + if(qs_itr == mQuestStatus.end()) + continue; + + QuestStatusData const& q_status = qs_itr->second; if (q_status.m_status == QUEST_STATUS_INCOMPLETE) { - Quest const* qinfo = objmgr.GetQuestTemplate(i->first); + Quest const* qinfo = objmgr.GetQuestTemplate(questid); if(!qinfo) continue; @@ -13479,28 +13652,21 @@ bool Player::HasQuestForItem( uint32 itemid ) const for (int j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; j++) { // examined item is a source item - if (qinfo->ReqSourceId[j] == itemid && qinfo->ReqSourceRef[j] > 0 && qinfo->ReqSourceRef[j] <= QUEST_OBJECTIVES_COUNT) + if (qinfo->ReqSourceId[j] == itemid) { - uint32 idx = qinfo->ReqSourceRef[j]-1; + ItemPrototype const *pProto = objmgr.GetItemPrototype(itemid); - // total count of created ReqItems and SourceItems is less than ReqItemCount - if(qinfo->ReqItemId[idx] != 0 && - q_status.m_itemcount[idx] * qinfo->ReqSourceCount[j] + GetItemCount(itemid,true) < qinfo->ReqItemCount[idx] * qinfo->ReqSourceCount[j]) + // 'unique' item + if (pProto->MaxCount && GetItemCount(itemid,true) < pProto->MaxCount) return true; - // total count of casted ReqCreatureOrGOs and SourceItems is less than ReqCreatureOrGOCount - if (qinfo->ReqCreatureOrGOId[idx] != 0) - { - if(q_status.m_creatureOrGOcount[idx] * qinfo->ReqSourceCount[j] + GetItemCount(itemid,true) < qinfo->ReqCreatureOrGOCount[idx] * qinfo->ReqSourceCount[j]) - return true; - } - // spell with SPELL_EFFECT_QUEST_COMPLETE or SPELL_EFFECT_SEND_EVENT (with script) case - else if(qinfo->ReqSpell[idx] != 0) + // allows custom amount drop when not 0 + if (qinfo->ReqSourceCount[j]) { - // not casted and need more reagents/item for use. - if(!q_status.m_explored && GetItemCount(itemid,true) < qinfo->ReqSourceCount[j]) + if (GetItemCount(itemid,true) < qinfo->ReqSourceCount[j]) return true; - } + } else if (GetItemCount(itemid,true) < pProto->Stackable) + return true; } } } @@ -13524,13 +13690,12 @@ void Player::SendQuestReward( Quest const *pQuest, uint32 XP, Object * questGive uint32 questid = pQuest->GetQuestId(); sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_QUEST_COMPLETE quest = %u", questid ); gameeventmgr.HandleQuestComplete(questid); - WorldPacket data( SMSG_QUESTGIVER_QUEST_COMPLETE, (4+4+4+4+4+4+pQuest->GetRewItemsCount()*8) ); - data << questid; - data << uint32(0x03); + WorldPacket data( SMSG_QUESTGIVER_QUEST_COMPLETE, (4+4+4+4+4) ); + data << uint32(questid); if ( getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) ) { - data << XP; + data << uint32(XP); data << uint32(pQuest->GetRewOrReqMoney()); } else @@ -13538,16 +13703,9 @@ void Player::SendQuestReward( Quest const *pQuest, uint32 XP, Object * questGive data << uint32(0); data << uint32(pQuest->GetRewOrReqMoney() + int32(pQuest->GetRewMoneyMaxLevel() * sWorld.getRate(RATE_DROP_MONEY))); } - data << uint32(0); // new 2.3.0, HonorPoints? - data << uint32( pQuest->GetRewItemsCount() ); // max is 5 - for (uint32 i = 0; i < pQuest->GetRewItemsCount(); ++i) - { - if ( pQuest->RewItemId[i] > 0 ) - data << pQuest->RewItemId[i] << pQuest->RewItemCount[i]; - else - data << uint32(0) << uint32(0); - } + data << uint32(10*Trinity::Honor::hk_honor_at_level(getLevel(), pQuest->GetRewHonorableKills())); + data << uint32(pQuest->GetBonusTalents()); // bonus talents GetSession()->SendPacket( &data ); if (pQuest->GetQuestCompleteScript() != 0) @@ -13558,8 +13716,9 @@ void Player::SendQuestFailed( uint32 quest_id ) { if( quest_id ) { - WorldPacket data( SMSG_QUESTGIVER_QUEST_FAILED, 4 ); + WorldPacket data( SMSG_QUESTGIVER_QUEST_FAILED, 4+4 ); data << quest_id; + data << uint32(0); // failed reason (4 for inventory is full) GetSession()->SendPacket( &data ); sLog.outDebug("WORLD: Sent SMSG_QUESTGIVER_QUEST_FAILED"); } @@ -13598,10 +13757,10 @@ void Player::SendPushToPartyResponse( Player *pPlayer, uint32 msg ) void Player::SendQuestUpdateAddItem( Quest const* pQuest, uint32 item_idx, uint32 count ) { - WorldPacket data( SMSG_QUESTUPDATE_ADD_ITEM, (4+4) ); + WorldPacket data( SMSG_QUESTUPDATE_ADD_ITEM, 0 ); sLog.outDebug( "WORLD: Sent SMSG_QUESTUPDATE_ADD_ITEM" ); - data << pQuest->ReqItemId[item_idx]; - data << count; + //data << pQuest->ReqItemId[item_idx]; + //data << count; GetSession()->SendPacket( &data ); } @@ -13647,7 +13806,7 @@ bool Player::MinimalLoadFromDB( QueryResult *result, uint32 guid ) if(!LoadValues( fields[1].GetString())) { - sLog.outError("ERROR: Player #%d have broken data in `data` field. Can't be loaded.",GUID_LOPART(guid)); + sLog.outError("Player #%d have broken data in `data` field. Can't be loaded for character list.",GUID_LOPART(guid)); if(delete_result) delete result; return false; } @@ -13717,7 +13876,7 @@ void Player::_LoadArenaTeamInfo(QueryResult *result) ArenaTeam* aTeam = objmgr.GetArenaTeamById(arenateamid); if(!aTeam) { - sLog.outError("FATAL: couldn't load arenateam %u", arenateamid); + sLog.outError("Player::_LoadArenaTeamInfo: couldn't load arenateam %u, week %u, season %u, rating %u", arenateamid, played_week, played_season, personal_rating); continue; } uint8 arenaSlot = aTeam->GetSlot(); @@ -13835,13 +13994,13 @@ float Player::GetFloatValueFromDB(uint16 index, uint64 guid) bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) { - //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 [28] [29] 30 31 32 33 - //QueryResult *result = CharacterDatabase.PQuery("SELECT guid, account, data, name, race, class, position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty, arena_pending_points FROM characters WHERE guid = '%u'", guid); + //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 [28] [29] 30 31 32 33 34 35 36 37 38 39 40 + //QueryResult *result = CharacterDatabase.PQuery("SELECT guid, account, data, name, race, class, position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty, arena_pending_points,bgid,bgteam,bgmap,bgx,bgy,bgz,bgo FROM characters WHERE guid = '%u'", guid); QueryResult *result = holder->GetResult(PLAYER_LOGIN_QUERY_LOADFROM); if(!result) { - sLog.outError("ERROR: Player (GUID: %u) not found in table `characters`, can't load. ",guid); + sLog.outError("Player (GUID: %u) not found in table `characters`, can't load. ",guid); return false; } @@ -13853,7 +14012,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) // player should be able to load/delete character only with correct account! if( dbAccountId != GetSession()->GetAccountId() ) { - sLog.outError("ERROR: Player (GUID: %u) loading from wrong account (is: %u, should be: %u)",guid,GetSession()->GetAccountId(),dbAccountId); + sLog.outError("Player (GUID: %u) loading from wrong account (is: %u, should be: %u)",guid,GetSession()->GetAccountId(),dbAccountId); delete result; return false; } @@ -13872,7 +14031,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) if(!LoadValues( fields[2].GetString())) { - sLog.outError("ERROR: Player #%d have broken data in `data` field. Can't be loaded.",GUID_LOPART(guid)); + sLog.outError("Player #%d have broken data in `data` field. Can't be loaded.",GUID_LOPART(guid)); delete result; return false; } @@ -13904,23 +14063,19 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) //Need to call it to initialize m_team (m_team can be calculated from m_race) //Other way is to saves m_team into characters table. setFactionForRace(m_race); - SetCharm(0); + SetMover(this); m_class = fields[5].GetUInt8(); - PlayerInfo const *info = objmgr.GetPlayerInfo(m_race, m_class); - if(!info) - { - sLog.outError("Player have incorrect race/class pair. Can't be loaded."); - delete result; + // load home bind and check in same time class/race pair, it used later for restore broken positions + if(!_LoadHomeBind(holder->GetResult(PLAYER_LOGIN_QUERY_LOADHOMEBIND))) return false; - } InitPrimaryProffesions(); // to max set before any spell loaded + // init saved position, and fix it later if problematic uint32 transGUID = fields[24].GetUInt32(); Relocate(fields[6].GetFloat(),fields[7].GetFloat(),fields[8].GetFloat(),fields[10].GetFloat()); - SetFallInformation(0, fields[8].GetFloat()); SetMapId(fields[9].GetUInt32()); SetDifficulty(fields[32].GetUInt32()); // may be changed in _LoadGroup @@ -13954,10 +14109,8 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) if(!IsPositionValid()) { - sLog.outError("ERROR: Player (guidlow %d) have invalid coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.",guid,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation()); - - SetMapId(info->mapId); - Relocate(info->positionX,info->positionY,info->positionZ,0.0f); + sLog.outError("Player (guidlow %d) have invalid coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.",guid,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation()); + RelocateToHomebind(); transGUID = 0; @@ -13967,49 +14120,57 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) m_movementInfo.t_o = 0.0f; } - if(!_LoadHomeBind(holder->GetResult(PLAYER_LOGIN_QUERY_LOADHOMEBIND))) - return false; + uint32 bgid = fields[34].GetUInt32(); + uint32 bgteam = fields[35].GetUInt32(); - // load the player's map here if it's not already loaded - Map *map = GetMap(); - if (!map) + if(bgid) //saved in BattleGround { - AreaTrigger const* at = objmgr.GetGoBackTrigger(GetMapId()); - if(at) + SetBattleGroundEntryPoint(fields[36].GetUInt32(),fields[37].GetFloat(),fields[38].GetFloat(),fields[39].GetFloat(),fields[40].GetFloat()); + + // check entry point and fix to homebind if need + MapEntry const* mapEntry = sMapStore.LookupEntry(m_bgEntryPoint.mapid); + if(!mapEntry || mapEntry->Instanceable() || !MapManager::IsValidMapCoord(m_bgEntryPoint)) + SetBattleGroundEntryPoint(m_homebindMapId,m_homebindX,m_homebindY,m_homebindZ,0.0f); + + BattleGround *currentBg = sBattleGroundMgr.GetBattleGround(bgid, BATTLEGROUND_TYPE_NONE); + + if(currentBg && currentBg->IsPlayerInBattleGround(GetGUID())) { - SetMapId(at->target_mapId); - Relocate(at->target_X, at->target_Y, at->target_Z, GetOrientation()); - sLog.outError("Player (guidlow %d) is teleported to gobacktrigger (Map: %u X: %f Y: %f Z: %f O: %f).",guid,GetMapId(),GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation()); + BattleGroundQueueTypeId bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(currentBg->GetTypeID(), currentBg->GetArenaType()); + uint32 queueSlot = AddBattleGroundQueueId(bgQueueTypeId); + + SetBattleGroundId(currentBg->GetInstanceID(), currentBg->GetTypeID()); + SetBGTeam(bgteam); + + //join player to battleground group + currentBg->EventPlayerLoggedIn(this, GetGUID()); + currentBg->AddOrSetPlayerToCorrectBgGroup(this, GetGUID(), bgteam); + + SetInviteForBattleGroundQueueType(bgQueueTypeId,currentBg->GetInstanceID()); } else { - SetMapId(m_homebindMapId); - Relocate(m_homebindX, m_homebindY, m_homebindZ, GetOrientation()); - sLog.outError("Player (guidlow %d) is teleported to home (Map: %u X: %f Y: %f Z: %f O: %f).",guid,GetMapId(),GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation()); + Relocate(GetBattleGroundEntryPoint()); + //RemoveArenaAuras(true); } - - map = GetMap(); - if(!map) + } + else + { + MapEntry const* mapEntry = sMapStore.LookupEntry(GetMapId()); + // if server restart after player save in BG or area + // player can have current coordinates in to BG/Arean map, fix this + if(!mapEntry || mapEntry->IsBattleGroundOrArena()) { - sLog.outError("ERROR: Player (guidlow %d) have invalid coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.",guid,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation()); - - SetMapId(info->mapId); - Relocate(info->positionX,info->positionY,info->positionZ,0.0f); + // return to BG master + SetMapId(fields[36].GetUInt32()); + Relocate(fields[37].GetFloat(),fields[38].GetFloat(),fields[39].GetFloat(),fields[40].GetFloat()); - map = GetMap(); - if(!map) - { - sLog.outError("ERROR: Player (guidlow %d) have invalid coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.",guid,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation()); - sLog.outError("CRASH."); - assert(false); - } + // check entry point and fix to homebind if need + mapEntry = sMapStore.LookupEntry(GetMapId()); + if(!mapEntry || mapEntry->IsBattleGroundOrArena() || !IsPositionValid()) + RelocateToHomebind(); } } - // since the player may not be bound to the map yet, make sure subsequent - // getmap calls won't create new maps - SetInstanceId(map->GetInstanceId()); - - SaveRecallPosition(); if (transGUID != 0) { @@ -14024,12 +14185,11 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) // transport size limited m_movementInfo.t_x > 50 || m_movementInfo.t_y > 50 || m_movementInfo.t_z > 50 ) { - sLog.outError("ERROR: Player (guidlow %d) have invalid transport coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.", + sLog.outError("Player (guidlow %d) have invalid transport coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.", guid,GetPositionX()+m_movementInfo.t_x,GetPositionY()+m_movementInfo.t_y, GetPositionZ()+m_movementInfo.t_z,GetOrientation()+m_movementInfo.t_o); - SetMapId(info->mapId); - Relocate(info->positionX,info->positionY,info->positionZ,0.0f); + RelocateToHomebind(); m_movementInfo.t_x = 0.0f; m_movementInfo.t_y = 0.0f; @@ -14046,6 +14206,14 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) { if( (*iter)->GetGUIDLow() == transGUID) { + MapEntry const* transMapEntry = sMapStore.LookupEntry((*iter)->GetMapId()); + // client without expansion support + if(GetSession()->Expansion() < transMapEntry->Expansion()) + { + sLog.outDebug("Player %s using client without required expansion tried login at transport at non accessible map %u", GetName(), (*iter)->GetMapId()); + break; + } + m_transport = *iter; m_transport->AddPassenger(this); SetMapId(m_transport->GetMapId()); @@ -14055,11 +14223,10 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) if(!m_transport) { - sLog.outError("ERROR: Player (guidlow %d) have invalid transport guid (%u). Teleport to default race/class locations.", + sLog.outError("Player (guidlow %d) have problems with transport guid (%u). Teleport to default race/class locations.", guid,transGUID); - SetMapId(info->mapId); - Relocate(info->positionX,info->positionY,info->positionZ,0.0f); + RelocateToHomebind(); m_movementInfo.t_x = 0.0f; m_movementInfo.t_y = 0.0f; @@ -14069,6 +14236,71 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) transGUID = 0; } } + else // not transport case + { + MapEntry const* mapEntry = sMapStore.LookupEntry(GetMapId()); + // client without expansion support + if(GetSession()->Expansion() < mapEntry->Expansion()) + { + sLog.outDebug("Player %s using client without required expansion tried login at non accessible map %u", GetName(), GetMapId()); + RelocateToHomebind(); + } + } + + // NOW player must have valid map + // load the player's map here if it's not already loaded + Map *map = GetMap(); + + if (!map) + { + AreaTrigger const* at = objmgr.GetGoBackTrigger(GetMapId()); + if(at) + { + SetMapId(at->target_mapId); + Relocate(at->target_X, at->target_Y, at->target_Z, GetOrientation()); + sLog.outError("Player (guidlow %d) is teleported to gobacktrigger (Map: %u X: %f Y: %f Z: %f O: %f).",guid,GetMapId(),GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation()); + } + else + { + RelocateToHomebind(); + sLog.outError("Player (guidlow %d) is teleported to home (Map: %u X: %f Y: %f Z: %f O: %f).",guid,GetMapId(),GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation()); + } + + map = GetMap(); + if(!map) + { + sLog.outError("ERROR: Player (guidlow %d) have invalid coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.",guid,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation()); + delete result; + return false; + + /*SetMapId(info->mapId); + Relocate(info->positionX,info->positionY,info->positionZ,0.0f); + + map = GetMap(); + if(!map) + { + sLog.outError("ERROR: Player (guidlow %d) have invalid coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.",guid,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation()); + sLog.outError("CRASH."); + assert(false); + }*/ + } + } + + // since the player may not be bound to the map yet, make sure subsequent + // getmap calls won't create new maps + SetInstanceId(map->GetInstanceId()); + + // if the player is in an instance and it has been reset in the meantime teleport him to the entrance + if(GetInstanceId() && !sInstanceSaveManager.GetInstanceSave(GetInstanceId())) + { + AreaTrigger const* at = objmgr.GetMapEntranceTrigger(GetMapId()); + if(at) + Relocate(at->target_X, at->target_Y, at->target_Z, at->target_Orientation); + else + sLog.outError("Player %s(GUID: %u) logged in to a reset instance (map: %u) and there is no aretrigger leading to this map. Thus he can't be ported back to the entrance. This _might_ be an exploit attempt.", GetName(), GetGUIDLow(), GetMapId()); + } + + SaveRecallPosition(); time_t now = time(NULL); time_t logoutTime = time_t(fields[16].GetUInt64()); @@ -14119,10 +14351,10 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) uint32 extraflags = fields[25].GetUInt32(); m_stableSlots = fields[26].GetUInt32(); - if(m_stableSlots > 2) + if(m_stableSlots > MAX_PET_STABLES) { - sLog.outError("Player can have not more 2 stable slots, but have in DB %u",uint32(m_stableSlots)); - m_stableSlots = 2; + sLog.outError("Player can have not more %u stable slots, but have in DB %u",MAX_PET_STABLES,uint32(m_stableSlots)); + m_stableSlots = MAX_PET_STABLES; } m_atLoginFlags = fields[27].GetUInt32(); @@ -14145,35 +14377,18 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) SetUInt32Value(UNIT_CHANNEL_SPELL,0); // clear charm/summon related fields - SetCharm(NULL); - SetPet(NULL); - SetCharmerGUID(NULL); - SetOwnerGUID(NULL); - SetCreatorGUID(NULL); + SetUInt64Value(UNIT_FIELD_CHARM, 0); + SetUInt64Value(UNIT_FIELD_SUMMON, 0); + SetUInt64Value(PLAYER_FARSIGHT, 0); + SetCharmerGUID(0); + SetOwnerGUID(0); + SetCreatorGUID(0); // reset some aura modifiers before aura apply - SetFarSight(NULL); 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(); @@ -14191,6 +14406,8 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) // reset stats before loading any modifiers InitStatsForLevel(); InitTaxiNodesForLevel(); + InitGlyphsForLevel(); + InitRunes(); // apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods() @@ -14198,25 +14415,25 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) //_LoadMail(); _LoadAuras(holder->GetResult(PLAYER_LOGIN_QUERY_LOADAURAS), time_diff); - + _LoadGlyphAuras(); // add ghost flag (must be after aura load: PLAYER_FLAGS_GHOST set in aura) if( HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST) ) m_deathState = DEAD; _LoadSpells(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSPELLS)); - // after spell load - InitTalentForLevel(); - learnSkillRewardedSpells(); - // after spell load, learn rewarded spell if need also _LoadQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS)); _LoadDailyQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS)); + // after spell and quest load + InitTalentForLevel(); + learnDefaultSpells(); + _LoadTutorials(holder->GetResult(PLAYER_LOGIN_QUERY_LOADTUTORIALS)); // must be before inventory (some items required reputation check) - _LoadReputation(holder->GetResult(PLAYER_LOGIN_QUERY_LOADREPUTATION)); + m_reputationMgr.LoadFromDB(holder->GetResult(PLAYER_LOGIN_QUERY_LOADREPUTATION)); _LoadInventory(holder->GetResult(PLAYER_LOGIN_QUERY_LOADINVENTORY), time_diff); @@ -14230,9 +14447,6 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) m_social = sSocialMgr.LoadFromDB(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSOCIALLIST), GetGUIDLow()); - //if(!_LoadHomeBind(holder->GetResult(PLAYER_LOGIN_QUERY_LOADHOMEBIND))) - // return false; - // check PLAYER_CHOSEN_TITLE compatibility with PLAYER__FIELD_KNOWN_TITLES // note: PLAYER__FIELD_KNOWN_TITLES updated at quest status loaded if(uint32 curTitle = GetUInt32Value(PLAYER_CHOSEN_TITLE)) @@ -14242,7 +14456,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) } // Not finish taxi flight path - if(!m_taxi.LoadTaxiDestinationsFromString(taxi_nodes)) + if(!m_taxi.LoadTaxiDestinationsFromString(taxi_nodes,GetTeam())) { // problems with taxi path loading TaxiNodesEntry const* nodeEntry = NULL; @@ -14252,8 +14466,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) if(!nodeEntry) // don't know taxi start node, to homebind { sLog.outError("Character %u have wrong data in taxi destination list, teleport to homebind.",GetGUIDLow()); - SetMapId(m_homebindMapId); - Relocate( m_homebindX, m_homebindY, m_homebindZ,0.0f); + RelocateToHomebind(); SaveRecallPosition(); // save as recall also to prevent recall and fall from sky } else // have start node, to it @@ -14278,6 +14491,9 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) // flight will started later } + // has to be called after last Relocate() in Player::LoadFromDB + SetFallInformation(0, GetPositionZ()); + _LoadSpellCooldowns(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS)); // Spell code allow apply any auras to dead character in load time in aura/spell/item loading @@ -14311,6 +14527,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: @@ -14336,6 +14574,8 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) _LoadDeclinedNames(holder->GetResult(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES)); + m_achievementMgr.LoadFromDB(holder->GetResult(PLAYER_LOGIN_QUERY_LOADACHIEVEMENTS), holder->GetResult(PLAYER_LOGIN_QUERY_LOADCRITERIAPROGRESS)); + m_achievementMgr.CheckAllAchievementCriteria(); return true; } @@ -14373,9 +14613,15 @@ void Player::_LoadActions(QueryResult *result) uint8 button = fields[0].GetUInt8(); - addActionButton(button, fields[1].GetUInt16(), fields[2].GetUInt8(), fields[3].GetUInt8()); + if(addActionButton(button, fields[1].GetUInt16(), fields[2].GetUInt8(), fields[3].GetUInt8())) + m_actionButtons[button].uState = ACTIONBUTTON_UNCHANGED; + else + { + sLog.outError( " ...at loading, and will deleted in DB also"); - m_actionButtons[button].uState = ACTIONBUTTON_UNCHANGED; + // Will deleted in DB at next save (it can create data until save but marked as deleted) + m_actionButtons[button].uState = ACTIONBUTTON_DELETED; + } } while( result->NextRow() ); @@ -14385,45 +14631,39 @@ void Player::_LoadActions(QueryResult *result) void Player::_LoadAuras(QueryResult *result, uint32 timediff) { + sLog.outDebug("Loading auras for player %u",GetGUIDLow()); m_Auras.clear(); for (int i = 0; i < TOTAL_AURAS; i++) m_modAuras[i].clear(); - // all aura related fields - for(int i = UNIT_FIELD_AURA; i <= UNIT_FIELD_AURASTATE; ++i) - SetUInt32Value(i, 0); - - //QueryResult *result = CharacterDatabase.PQuery("SELECT caster_guid,spell,effect_index,stackcount,amount,maxduration,remaintime,remaincharges FROM character_aura WHERE guid = '%u'",GetGUIDLow()); + //QueryResult *result = CharacterDatabase.PQuery("SELECT caster_guid,spell,effect_mask,stackcount,amount0,amount1,amount2,maxduration,remaintime,remaincharges FROM character_aura WHERE guid = '%u'",GetGUIDLow()); if(result) { do { + int32 damage[3]; Field *fields = result->Fetch(); uint64 caster_guid = fields[0].GetUInt64(); uint32 spellid = fields[1].GetUInt32(); - uint32 effindex = fields[2].GetUInt32(); + uint32 effmask = fields[2].GetUInt32(); uint32 stackcount = fields[3].GetUInt32(); - int32 damage = (int32)fields[4].GetUInt32(); - int32 maxduration = (int32)fields[5].GetUInt32(); - int32 remaintime = (int32)fields[6].GetUInt32(); - int32 remaincharges = (int32)fields[7].GetUInt32(); + damage[0] = int32(fields[4].GetUInt32()); + damage[1] = int32(fields[5].GetUInt32()); + damage[2] = int32(fields[6].GetUInt32()); + int32 maxduration = (int32)fields[7].GetUInt32(); + int32 remaintime = (int32)fields[8].GetUInt32(); + int32 remaincharges = (int32)fields[9].GetUInt32(); SpellEntry const* spellproto = sSpellStore.LookupEntry(spellid); if(!spellproto) { - sLog.outError("Unknown aura (spellid %u, effindex %u), ignore.",spellid,effindex); - continue; - } - - if(effindex >= 3) - { - sLog.outError("Invalid effect index (spellid %u, effindex %u), ignore.",spellid,effindex); + sLog.outError("Unknown aura (spellid %u), ignore.",spellid); continue; } // negative effects should continue counting down after logout - if (remaintime != -1 && !IsPositiveEffect(spellid, effindex)) + if (remaintime != -1 && !IsPositiveSpell(spellid)) { if(remaintime <= int32(timediff)) continue; @@ -14438,21 +14678,12 @@ void Player::_LoadAuras(QueryResult *result, uint32 timediff) remaincharges = spellproto->procCharges; } else - remaincharges = -1; - - //do not load single target auras (unless they were cast by the player) - if (caster_guid != GetGUID() && IsSingleTargetSpell(spellproto)) - continue; + remaincharges = 0; - for(uint32 i=0; i<stackcount; i++) - { - Aura* aura = CreateAura(spellproto, effindex, NULL, this, NULL); - if(!damage) - damage = aura->GetModifier()->m_amount; - aura->SetLoadedState(caster_guid,damage,maxduration,remaintime,remaincharges); - AddAura(aura); - sLog.outDetail("Added aura spellid %u, effect %u", spellproto->Id, effindex); - } + Aura* aura = new Aura(spellproto, effmask, NULL, this, NULL, NULL); + aura->SetLoadedState(caster_guid,maxduration,remaintime,remaincharges, stackcount, &damage[0]); + AddAura(aura); + sLog.outDetail("Added aura spellid %u, effectmask %u", spellproto->Id, effmask); } while( result->NextRow() ); @@ -14463,6 +14694,36 @@ void Player::_LoadAuras(QueryResult *result, uint32 timediff) CastSpell(this,SPELL_ID_PASSIVE_BATTLE_STANCE,true); } +void Player::_LoadGlyphAuras() +{ + for (uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i) + { + if (uint32 glyph = GetGlyph(i)) + { + if (GlyphPropertiesEntry const *gp = sGlyphPropertiesStore.LookupEntry(glyph)) + { + if (GlyphSlotEntry const *gs = sGlyphSlotStore.LookupEntry(GetGlyphSlot(i))) + { + if(gp->TypeFlags == gs->TypeFlags) + { + CastSpell(this, gp->SpellId, true); + continue; + } + else + sLog.outError("Player %s has glyph with typeflags %u in slot with typeflags %u, removing.", m_name.c_str(), gp->TypeFlags, gs->TypeFlags); + } + else + sLog.outError("Player %s has not existing glyph slot entry %u on index %u", m_name.c_str(), GetGlyphSlot(i), i); + } + else + sLog.outError("Player %s has not existing glyph entry %u on index %u", m_name.c_str(), glyph, i); + + // On any error remove glyph + SetGlyph(i, 0); + } + } +} + void Player::LoadCorpse() { if( isAlive() ) @@ -14638,15 +14899,16 @@ void Player::_LoadInventory(QueryResult *result, uint32 timediff) // load mailed item which should receive current player void Player::_LoadMailedItems(Mail *mail) { - QueryResult* result = CharacterDatabase.PQuery("SELECT item_guid, item_template FROM mail_items WHERE mail_id='%u'", mail->messageID); + // data needs to be at first place for Item::LoadFromDB + QueryResult* result = CharacterDatabase.PQuery("SELECT data, item_guid, item_template FROM mail_items JOIN item_instance ON item_guid = guid WHERE mail_id='%u'", mail->messageID); if(!result) return; do { Field *fields = result->Fetch(); - uint32 item_guid_low = fields[0].GetUInt32(); - uint32 item_template = fields[1].GetUInt32(); + uint32 item_guid_low = fields[1].GetUInt32(); + uint32 item_template = fields[2].GetUInt32(); mail->AddItem(item_guid_low, item_template); @@ -14662,7 +14924,7 @@ void Player::_LoadMailedItems(Mail *mail) Item *item = NewItemOrBag(proto); - if(!item->LoadFromDB(item_guid_low, 0)) + if(!item->LoadFromDB(item_guid_low, 0, result)) { sLog.outError( "Player::_LoadMailedItems - Item in mail (%u) doesn't exist !!!! - item guid: %u, deleted from mail", mail->messageID, item_guid_low); CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid = '%u'", item_guid_low); @@ -14748,7 +15010,7 @@ void Player::LoadPet() // just not added to the map if(IsInWorld()) { - Pet *pet = new Pet; + Pet *pet = new Pet(this); if(!pet->LoadPetFromDB(this,0,0,true)) delete pet; } @@ -14798,7 +15060,7 @@ void Player::_LoadQuestStatus(QueryResult *result) if (quest_time <= sWorld.GetGameTime()) questStatusData.m_timer = 1; else - questStatusData.m_timer = (quest_time - sWorld.GetGameTime()) * 1000; + questStatusData.m_timer = (quest_time - sWorld.GetGameTime()) * IN_MILISECONDS; } else quest_time = 0; @@ -14843,6 +15105,9 @@ void Player::_LoadQuestStatus(QueryResult *result) if(CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(pQuest->GetCharTitleId())) SetTitle(titleEntry); } + + if(pQuest->GetBonusTalents()) + m_questRewardTalentCount+=pQuest->GetBonusTalents(); } sLog.outDebug("Quest status is {%u} for quest {%u} for player (GUID: %u)", questStatusData.m_status, quest_id, GetGUIDLow()); @@ -14901,68 +15166,9 @@ void Player::_LoadDailyQuestStatus(QueryResult *result) m_DailyQuestChanged = false; } -void Player::_LoadReputation(QueryResult *result) -{ - m_factions.clear(); - - // Set initial reputations (so everything is nifty before DB data load) - SetInitialFactions(); - - //QueryResult *result = CharacterDatabase.PQuery("SELECT faction,standing,flags FROM character_reputation WHERE guid = '%u'",GetGUIDLow()); - - if(result) - { - do - { - Field *fields = result->Fetch(); - - FactionEntry const *factionEntry = sFactionStore.LookupEntry(fields[0].GetUInt32()); - if( factionEntry && (factionEntry->reputationListID >= 0)) - { - FactionState* faction = &m_factions[factionEntry->reputationListID]; - - // update standing to current - faction->Standing = int32(fields[1].GetUInt32()); - - uint32 dbFactionFlags = fields[2].GetUInt32(); - - if( dbFactionFlags & FACTION_FLAG_VISIBLE ) - SetFactionVisible(faction); // have internal checks for forced invisibility - - if( dbFactionFlags & FACTION_FLAG_INACTIVE) - SetFactionInactive(faction,true); // have internal checks for visibility requirement - - if( dbFactionFlags & FACTION_FLAG_AT_WAR ) // DB at war - SetFactionAtWar(faction,true); // have internal checks for FACTION_FLAG_PEACE_FORCED - else // DB not at war - { - // allow remove if visible (and then not FACTION_FLAG_INVISIBLE_FORCED or FACTION_FLAG_HIDDEN) - if( faction->Flags & FACTION_FLAG_VISIBLE ) - SetFactionAtWar(faction,false); // have internal checks for FACTION_FLAG_PEACE_FORCED - } - - // set atWar for hostile - if(GetReputationRank(factionEntry) <= REP_HOSTILE) - SetFactionAtWar(faction,true); - - // reset changed flag if values similar to saved in DB - if(faction->Flags==dbFactionFlags) - faction->Changed = false; - } - } - while( result->NextRow() ); - - delete result; - } -} - void Player::_LoadSpells(QueryResult *result) { - for (PlayerSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr) - delete itr->second; - m_spells.clear(); - - //QueryResult *result = CharacterDatabase.PQuery("SELECT spell,slot,active FROM character_spell WHERE guid = '%u'",GetGUIDLow()); + //QueryResult *result = CharacterDatabase.PQuery("SELECT spell,active,disabled FROM character_spell WHERE guid = '%u'",GetGUIDLow()); if(result) { @@ -14970,7 +15176,7 @@ void Player::_LoadSpells(QueryResult *result) { Field *fields = result->Fetch(); - addSpell(fields[0].GetUInt16(), fields[2].GetBool(), false, true, fields[1].GetUInt16(), fields[3].GetBool()); + addSpell(fields[0].GetUInt16(), fields[1].GetBool(), false, false, fields[2].GetBool()); } while( result->NextRow() ); @@ -15042,6 +15248,14 @@ void Player::_LoadBoundInstances(QueryResult *result) // so the value read from the DB may be wrong here but only if the InstanceSave is loaded // and in that case it is not used + MapEntry const* mapEntry = sMapStore.LookupEntry(mapId); + if(!mapEntry || !mapEntry->IsDungeon()) + { + sLog.outError("_LoadBoundInstances: player %s(%d) has bind to not existed or not dungeon map %d", GetName(), GetGUIDLow(), mapId); + CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%d' AND instance = '%d'", GetGUIDLow(), instanceId); + continue; + } + if(!perm && group) { sLog.outError("_LoadBoundInstances: player %s(%d) is in group %d but has a non-permanent character bind to map %d,%d,%d", GetName(), GetGUIDLow(), GUID_LOPART(group->GetLeaderGUID()), mapId, instanceId, difficulty); @@ -15119,29 +15333,29 @@ InstancePlayerBind* Player::BindToInstance(InstanceSave *save, bool permanent, b void Player::SendRaidInfo() { + uint32 counter = 0; + WorldPacket data(SMSG_RAID_INSTANCE_INFO, 4); - uint32 counter = 0, i; - for(i = 0; i < TOTAL_DIFFICULTIES; i++) - for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr) - if(itr->second.perm) counter++; + size_t p_counter = data.wpos(); + data << uint32(counter); // placeholder - data << counter; - for(i = 0; i < TOTAL_DIFFICULTIES; i++) + for(int i = 0; i < TOTAL_DIFFICULTIES; ++i) { for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr) { if(itr->second.perm) { InstanceSave *save = itr->second.save; - data << (save->GetMapId()); - data << (uint32)(save->GetResetTime() - time(NULL)); - data << save->GetInstanceId(); - data << uint32(counter); - counter--; + data << uint32(save->GetMapId()); + data << uint32(save->GetResetTime() - time(NULL)); + data << uint32(save->GetInstanceId()); + data << uint32(save->GetDifficulty()); + ++counter; } } } + data.put<uint32>(p_counter,counter); GetSession()->SendPacket(&data); } @@ -15275,7 +15489,7 @@ bool Player::Satisfy(AccessRequirement const *ar, uint32 target_map, bool report if(missingItem) GetSession()->SendAreaTriggerMessage(GetSession()->GetTrinityString(LANG_LEVEL_MINREQUIRED_AND_ITEM), ar->levelMin, objmgr.GetItemPrototype(missingItem)->Name1); else if(missingKey) - SendTransferAborted(target_map, TRANSFER_ABORT_DIFFICULTY2); + SendTransferAborted(target_map, TRANSFER_ABORT_DIFFICULTY); else if(missingHeroicQuest) GetSession()->SendAreaTriggerMessage(ar->heroicQuestFailedText.c_str()); else if(missingQuest) @@ -15291,6 +15505,13 @@ bool Player::Satisfy(AccessRequirement const *ar, uint32 target_map, bool report bool Player::_LoadHomeBind(QueryResult *result) { + PlayerInfo const *info = objmgr.GetPlayerInfo(getRace(), getClass()); + if(!info) + { + sLog.outError("Player have incorrect race/class pair. Can't be loaded."); + return false; + } + bool ok = false; //QueryResult *result = CharacterDatabase.PQuery("SELECT map,zone,position_x,position_y,position_z FROM character_homebind WHERE guid = '%u'", GUID_LOPART(playerGuid)); if (result) @@ -15303,9 +15524,11 @@ bool Player::_LoadHomeBind(QueryResult *result) m_homebindZ = fields[4].GetFloat(); delete result; - // accept saved data only for valid position (and non instanceable) + MapEntry const* bindMapEntry = sMapStore.LookupEntry(m_homebindMapId); + + // accept saved data only for valid position (and non instanceable), and accessable if( MapManager::IsValidMapCoord(m_homebindMapId,m_homebindX,m_homebindY,m_homebindZ) && - !sMapStore.LookupEntry(m_homebindMapId)->Instanceable() ) + !bindMapEntry->Instanceable() && GetSession()->Expansion() >= bindMapEntry->Expansion()) { ok = true; } @@ -15315,9 +15538,6 @@ bool Player::_LoadHomeBind(QueryResult *result) if(!ok) { - PlayerInfo const *info = objmgr.GetPlayerInfo(getRace(), getClass()); - if(!info) return false; - m_homebindMapId = info->mapId; m_homebindZoneId = info->zoneId; m_homebindX = info->positionX; @@ -15327,7 +15547,7 @@ bool Player::_LoadHomeBind(QueryResult *result) CharacterDatabase.PExecute("INSERT INTO character_homebind (guid,map,zone,position_x,position_y,position_z) VALUES ('%u', '%u', '%u', '%f', '%f', '%f')", GetGUIDLow(), m_homebindMapId, (uint32)m_homebindZoneId, m_homebindX, m_homebindY, m_homebindZ); } - DEBUG_LOG("Setting player home position: mapid is: %u, zoneid is %u, X is %f, Y is %f, Z is %f\n", + DEBUG_LOG("Setting player home position: mapid is: %u, zoneid is %u, X is %f, Y is %f, Z is %f", m_homebindMapId, m_homebindZoneId, m_homebindX, m_homebindY, m_homebindZ); return true; @@ -15345,12 +15565,6 @@ void Player::SaveToDB() // first save/honor gain after midnight will also update the player's honor fields UpdateHonorFields(); - // players aren't saved on battleground maps - uint32 mapid = IsBeingTeleported() ? GetTeleportDest().mapid : GetMapId(); - const MapEntry * me = sMapStore.LookupEntry(mapid); - if(!me || me->IsBattleGroundOrArena()) - return; - int is_save_resting = HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) ? 1 : 0; //save, far from tavern/city //save, but in tavern/city @@ -15365,10 +15579,9 @@ void Player::SaveToDB() uint32 tmp_displayid = GetDisplayId(); // Set player sit state to standing on save, also stealth and shifted form - SetByteValue(UNIT_FIELD_BYTES_1, 0, 0); // stand state + SetByteValue(UNIT_FIELD_BYTES_1, 0, UNIT_STAND_STATE_STAND); SetByteValue(UNIT_FIELD_BYTES_2, 3, 0); // shapeshift - SetByteValue(UNIT_FIELD_BYTES_1, 3, 0); // stand flags? - RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE); + RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); SetDisplayId(GetNativeDisplayId()); bool inworld = IsInWorld(); @@ -15386,23 +15599,14 @@ void Player::SaveToDB() "taximask, online, cinematic, " "totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, " "trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, " - "death_expire_time, taxi_path, arena_pending_points, latency) VALUES (" + "death_expire_time, taxi_path, arena_pending_points, latency, bgid, bgteam, bgmap, bgx, bgy, bgz, bgo) VALUES (" << GetGUIDLow() << ", " << GetSession()->GetAccountId() << ", '" << sql_name << "', " << m_race << ", " << m_class << ", "; - bool save_to_dest = false; - if(IsBeingTeleported()) - { - // don't save to battlegrounds or arenas - const MapEntry *entry = sMapStore.LookupEntry(GetTeleportDest().mapid); - if(entry && entry->map_type != MAP_BATTLEGROUND && entry->map_type != MAP_ARENA) - save_to_dest = true; - } - - if(!save_to_dest) + if(!IsBeingTeleported()) { ss << GetMapId() << ", " << (uint32)GetDifficulty() << ", " @@ -15427,12 +15631,11 @@ void Player::SaveToDB() ss << GetUInt32Value(i) << " "; } - ss << "', '"; + ss << "', "; - for( i = 0; i < 8; i++ ) - ss << m_taxi.GetTaximask(i) << " "; + ss << m_taxi; // string with TaxiMaskSize numbers - ss << "', "; + ss << ", "; ss << (inworld ? 1 : 0); ss << ", "; @@ -15486,9 +15689,19 @@ void Player::SaveToDB() ss << ", '"; ss << m_taxi.SaveTaxiDestinationsToString(); - ss << "', '0', '"; + ss << "', '0', "; ss << GetSession()->GetLatency(); - ss << "' )"; + ss << ", "; + ss << GetBattleGroundId(); + ss << ", "; + ss << GetBGTeam(); + ss << ", "; + ss << m_bgEntryPoint.mapid << ", " + << finiteAlways(m_bgEntryPoint.x) << ", " + << finiteAlways(m_bgEntryPoint.y) << ", " + << finiteAlways(m_bgEntryPoint.z) << ", " + << finiteAlways(m_bgEntryPoint.o); + ss << ")"; CharacterDatabase.Execute( ss.str().c_str() ); @@ -15503,7 +15716,8 @@ void Player::SaveToDB() _SaveSpellCooldowns(); _SaveActions(); _SaveAuras(); - _SaveReputation(); + m_achievementMgr.SaveToDB(); + m_reputationMgr.SaveToDB(); CharacterDatabase.CommitTransaction(); @@ -15517,23 +15731,6 @@ void Player::SaveToDB() // save pet (hunter pet level and experience and all type pets health/mana). if(Pet* pet = GetPet()) pet->SavePetToDB(PET_SAVE_AS_CURRENT); - - //to prevent access to DB we should cache some data, which is used very often - CachePlayerInfoMap::iterator _iter = objmgr.m_mPlayerInfoMap.find(GetGUIDLow()); - if(_iter != objmgr.m_mPlayerInfoMap.end())//skip new players - { - _iter->second->unLevel = getLevel(); - - _iter->second->unArenaInfoSlot0 = GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 0 * 6 + 5); - _iter->second->unArenaInfoSlot1 = GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 * 6 + 5); - _iter->second->unArenaInfoSlot2 = GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 2 * 6 + 5); - - _iter->second->unfield = GetUInt32Value(UNIT_FIELD_BYTES_0); - - _iter->second->unArenaInfoId0 = GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (0 * 6)); - _iter->second->unArenaInfoId1 = GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (1 * 6)); - _iter->second->unArenaInfoId2 = GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (2 * 6)); - } } // fast save function for item/money cheating preventing - save only inventory and money state @@ -15578,56 +15775,36 @@ void Player::_SaveAuras() CharacterDatabase.PExecute("DELETE FROM character_aura WHERE guid = '%u'",GetGUIDLow()); AuraMap const& auras = GetAuras(); + for(AuraMap::const_iterator itr = auras.begin(); itr !=auras.end() ; ++itr) + { + // skip: + // area auras or single cast auras casted by other unit + // passive auras and stances + if (itr->second->IsPassive() + || itr->second->IsAuraType(SPELL_AURA_MOD_SHAPESHIFT) + || itr->second->IsAuraType(SPELL_AURA_MOD_STEALTH) + || itr->second->IsRemovedOnShapeLost()) + continue; + bool isCaster = itr->second->GetCasterGUID() == GetGUID(); + if (!isCaster) + if (itr->second->IsSingleTarget() + || itr->second->IsAreaAura()) + continue; - if (auras.empty()) - return; - - spellEffectPair lastEffectPair = auras.begin()->first; - uint32 stackCounter = 1; - - for(AuraMap::const_iterator itr = auras.begin(); ; ++itr) - { - if(itr == auras.end() || lastEffectPair != itr->first) + int32 amounts[MAX_SPELL_EFFECTS]; + for (uint8 i=0;i<MAX_SPELL_EFFECTS;++i) { - AuraMap::const_iterator itr2 = itr; - // save previous spellEffectPair to db - itr2--; - SpellEntry const *spellInfo = itr2->second->GetSpellProto(); - - //skip all auras from spells that are passive or need a shapeshift - if (!(itr2->second->IsPassive() || itr2->second->IsRemovedOnShapeLost())) - { - //do not save single target auras (unless they were cast by the player) - if (!(itr2->second->GetCasterGUID() != GetGUID() && IsSingleTargetSpell(spellInfo))) - { - uint8 i; - // or apply at cast SPELL_AURA_MOD_SHAPESHIFT or SPELL_AURA_MOD_STEALTH auras - for (i = 0; i < 3; i++) - if (spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_SHAPESHIFT || - spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_STEALTH) - break; - - if (i == 3) - { - CharacterDatabase.PExecute("INSERT INTO character_aura (guid,caster_guid,spell,effect_index,stackcount,amount,maxduration,remaintime,remaincharges) " - "VALUES ('%u', '" I64FMTD "' ,'%u', '%u', '%u', '%d', '%d', '%d', '%d')", - GetGUIDLow(), itr2->second->GetCasterGUID(), (uint32)itr2->second->GetId(), (uint32)itr2->second->GetEffIndex(), (uint32)itr2->second->GetStackAmount(), itr2->second->GetModifier()->m_amount,int(itr2->second->GetAuraMaxDuration()),int(itr2->second->GetAuraDuration()),int(itr2->second->m_procCharges)); - } - } - } - - if(itr == auras.end()) - break; + if (AuraEffect * partAura = itr->second->GetPartAura(i)) + amounts[i]=partAura->GetAmount(); + else + amounts[i]=0; } - //TODO: if need delete this - if (lastEffectPair == itr->first) - stackCounter++; - else - { - lastEffectPair = itr->first; - stackCounter = 1; - } + CharacterDatabase.PExecute("INSERT INTO character_aura (guid,caster_guid,spell,effect_mask,stackcount,amount0, amount1, amount2,maxduration,remaintime,remaincharges) " + "VALUES ('%u', '" I64FMTD "', '%u', '%u', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", + GetGUIDLow(), itr->second->GetCasterGUID(),(uint32)itr->second->GetId(), (uint32)itr->second->GetEffectMask(), + (int32)itr->second->GetStackAmount(), (int32)amounts[0], (int32)amounts[1], (int32)amounts[2] + ,int(itr->second->GetAuraMaxDuration()),int(itr->second->GetAuraDuration()),int(itr->second->GetAuraCharges())); } } @@ -15767,11 +15944,11 @@ void Player::_SaveQuestStatus() case QUEST_NEW : CharacterDatabase.PExecute("INSERT INTO character_queststatus (guid,quest,status,rewarded,explored,timer,mobcount1,mobcount2,mobcount3,mobcount4,itemcount1,itemcount2,itemcount3,itemcount4) " "VALUES ('%u', '%u', '%u', '%u', '%u', '" I64FMTD "', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')", - GetGUIDLow(), i->first, i->second.m_status, i->second.m_rewarded, i->second.m_explored, uint64(i->second.m_timer / 1000 + sWorld.GetGameTime()), i->second.m_creatureOrGOcount[0], i->second.m_creatureOrGOcount[1], i->second.m_creatureOrGOcount[2], i->second.m_creatureOrGOcount[3], i->second.m_itemcount[0], i->second.m_itemcount[1], i->second.m_itemcount[2], i->second.m_itemcount[3]); + GetGUIDLow(), i->first, i->second.m_status, i->second.m_rewarded, i->second.m_explored, uint64(i->second.m_timer / IN_MILISECONDS+ sWorld.GetGameTime()), i->second.m_creatureOrGOcount[0], i->second.m_creatureOrGOcount[1], i->second.m_creatureOrGOcount[2], i->second.m_creatureOrGOcount[3], i->second.m_itemcount[0], i->second.m_itemcount[1], i->second.m_itemcount[2], i->second.m_itemcount[3]); break; case QUEST_CHANGED : CharacterDatabase.PExecute("UPDATE character_queststatus SET status = '%u',rewarded = '%u',explored = '%u',timer = '" I64FMTD "',mobcount1 = '%u',mobcount2 = '%u',mobcount3 = '%u',mobcount4 = '%u',itemcount1 = '%u',itemcount2 = '%u',itemcount3 = '%u',itemcount4 = '%u' WHERE guid = '%u' AND quest = '%u' ", - i->second.m_status, i->second.m_rewarded, i->second.m_explored, uint64(i->second.m_timer / 1000 + sWorld.GetGameTime()), i->second.m_creatureOrGOcount[0], i->second.m_creatureOrGOcount[1], i->second.m_creatureOrGOcount[2], i->second.m_creatureOrGOcount[3], i->second.m_itemcount[0], i->second.m_itemcount[1], i->second.m_itemcount[2], i->second.m_itemcount[3], GetGUIDLow(), i->first ); + i->second.m_status, i->second.m_rewarded, i->second.m_explored, uint64(i->second.m_timer / IN_MILISECONDS + sWorld.GetGameTime()), i->second.m_creatureOrGOcount[0], i->second.m_creatureOrGOcount[1], i->second.m_creatureOrGOcount[2], i->second.m_creatureOrGOcount[3], i->second.m_itemcount[0], i->second.m_itemcount[1], i->second.m_itemcount[2], i->second.m_itemcount[3], GetGUIDLow(), i->first ); break; case QUEST_UNCHANGED: break; @@ -15797,33 +15974,28 @@ void Player::_SaveDailyQuestStatus() GetGUIDLow(), GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx),uint64(m_lastDailyQuestTime)); } -void Player::_SaveReputation() -{ - for(FactionStateList::iterator itr = m_factions.begin(); itr != m_factions.end(); ++itr) - { - if (itr->second.Changed) - { - CharacterDatabase.PExecute("DELETE FROM character_reputation WHERE guid = '%u' AND faction='%u'", GetGUIDLow(), itr->second.ID); - CharacterDatabase.PExecute("INSERT INTO character_reputation (guid,faction,standing,flags) VALUES ('%u', '%u', '%i', '%u')", GetGUIDLow(), itr->second.ID, itr->second.Standing, itr->second.Flags); - itr->second.Changed = false; - } - } -} - 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) - CharacterDatabase.PExecute("INSERT INTO character_spell (guid,spell,slot,active,disabled) VALUES ('%u', '%u', '%u','%u','%u')", GetGUIDLow(), itr->first, itr->second->slotId,itr->second->active ? 1 : 0,itr->second->disabled ? 1 : 0); + + // 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; + } + } } @@ -15995,6 +16167,42 @@ void Player::SetFloatValueInDB(uint16 index, float value, uint64 guid) Player::SetUInt32ValueInDB(index, temp, guid); } +void Player::Customize(uint64 guid, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair) +{ + Tokens tokens; + if(!LoadValuesArrayFromDB(tokens, guid)) + return; + + uint32 unit_bytes0 = GetUInt32ValueFromArray(tokens, UNIT_FIELD_BYTES_0); + uint8 race = unit_bytes0 & 0xFF; + uint8 class_ = (unit_bytes0 >> 8) & 0xFF; + + PlayerInfo const* info = objmgr.GetPlayerInfo(race, class_); + if(!info) + return; + + unit_bytes0 &= ~(0xFF << 16); + unit_bytes0 |= (gender << 16); + SetUInt32ValueInArray(tokens, UNIT_FIELD_BYTES_0, unit_bytes0); + + SetUInt32ValueInArray(tokens, UNIT_FIELD_DISPLAYID, gender ? info->displayId_f : info->displayId_m); + SetUInt32ValueInArray(tokens, UNIT_FIELD_NATIVEDISPLAYID, gender ? info->displayId_f : info->displayId_m); + + SetUInt32ValueInArray(tokens, PLAYER_BYTES, (skin | (face << 8) | (hairStyle << 16) | (hairColor << 24))); + + uint32 player_bytes2 = GetUInt32ValueFromArray(tokens, PLAYER_BYTES_2); + player_bytes2 &= ~0xFF; + player_bytes2 |= facialHair; + SetUInt32ValueInArray(tokens, PLAYER_BYTES_2, player_bytes2); + + uint32 player_bytes3 = GetUInt32ValueFromArray(tokens, PLAYER_BYTES_3); + player_bytes3 &= ~0xFF; + player_bytes3 |= gender; + SetUInt32ValueInArray(tokens, PLAYER_BYTES_3, player_bytes3); + + SaveValuesArrayInDB(tokens, guid); +} + void Player::SendAttackSwingNotStanding() { WorldPacket data(SMSG_ATTACKSWING_NOTSTANDING, 0); @@ -16027,20 +16235,11 @@ void Player::SendAttackSwingBadFacingAttack() void Player::SendAutoRepeatCancel() { - WorldPacket data(SMSG_CANCEL_AUTO_REPEAT, 0); + WorldPacket data(SMSG_CANCEL_AUTO_REPEAT, GetPackGUID().size()); + data.append(GetPackGUID()); // may be it's target guid GetSession()->SendPacket( &data ); } -void Player::PlaySound(uint32 Sound, bool OnlySelf) -{ - WorldPacket data(SMSG_PLAY_SOUND, 4); - data << Sound; - if (OnlySelf) - GetSession()->SendPacket( &data ); - else - SendMessageToSet( &data, true ); -} - void Player::SendExplorationExperience(uint32 Area, uint32 Experience) { WorldPacket data( SMSG_EXPLORATION_EXPERIENCE, 8 ); @@ -16181,11 +16380,32 @@ void Player::UpdateDuelFlag(time_t currTime) duel->opponent->duel->startTime = currTime; } +Pet* Player::GetPet() const +{ + if(uint64 pet_guid = GetPetGUID()) + { + if(!IS_PET_GUID(pet_guid)) + return NULL; + + if(Pet* pet = ObjectAccessor::GetPet(pet_guid)) + return pet; + + //there may be a guardian in slot + //sLog.outError("Player::GetPet: Pet %u not exist.",GUID_LOPART(pet_guid)); + //const_cast<Player*>(this)->SetPetGUID(0); + } + + return NULL; +} + void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent) { if(!pet) pet = GetPet(); + if(pet) + sLog.outDebug("RemovePet %u, %u, %u", pet->GetEntry(), mode, returnreagent); + if(returnreagent && (pet || m_temporaryUnsummonedPetNumber)) { //returning of reagents only for players, so best done here @@ -16215,25 +16435,6 @@ void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent) if(!pet || pet->GetOwnerGUID()!=GetGUID()) return; - // only if current pet in slot - switch(pet->getPetType()) - { - case MINI_PET: - m_miniPet = 0; - break; - case GUARDIAN_PET: - m_guardianPets.erase(pet->GetGUID()); - break; - case POSSESSED_PET: - m_guardianPets.erase(pet->GetGUID()); - pet->RemoveCharmedOrPossessedBy(NULL); - break; - default: - if(GetPetGUID() == pet->GetGUID()) - SetPet(NULL); - break; - } - pet->CombatStop(); if(returnreagent) @@ -16250,7 +16451,18 @@ void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent) } } - pet->SavePetToDB(mode); + // only if current pet in slot + switch(pet->getPetType()) + { + case POSSESSED_PET: + pet->RemoveCharmedOrPossessedBy(NULL); + break; + default: + pet->SavePetToDB(mode); + break; + } + + SetGuardian(pet, false); pet->CleanupsBeforeDelete(); pet->AddObjectToRemoveList(); @@ -16258,8 +16470,9 @@ void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent) if(pet->isControlled()) { - WorldPacket data(SMSG_PET_SPELLS, 8); + WorldPacket data(SMSG_PET_SPELLS, 8+4); data << uint64(0); + data << uint32(0); GetSession()->SendPacket(&data); if(GetGroup()) @@ -16267,66 +16480,29 @@ void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent) } } -void Player::RemoveMiniPet() -{ - if(Pet* pet = GetMiniPet()) - { - pet->Remove(PET_SAVE_AS_DELETED); - m_miniPet = 0; - } -} - -Pet* Player::GetMiniPet() -{ - if(!m_miniPet) - return NULL; - return ObjectAccessor::GetPet(m_miniPet); -} - -void Player::RemoveGuardians() -{ - while(!m_guardianPets.empty()) - { - uint64 guid = *m_guardianPets.begin(); - if(Pet* pet = ObjectAccessor::GetPet(guid)) - pet->Remove(PET_SAVE_AS_DELETED); - - m_guardianPets.erase(guid); - } -} - -bool Player::HasGuardianWithEntry(uint32 entry) -{ - // pet guid middle part is entry (and creature also) - // and in guardian list must be guardians with same entry _always_ - for(GuardianPetList::const_iterator itr = m_guardianPets.begin(); itr != m_guardianPets.end(); ++itr) - if(GUID_ENPART(*itr)==entry) - return true; - - return false; -} - -void Player::Uncharm() +void Player::StopCastingCharm() { Unit* charm = GetCharm(); if(!charm) return; - if(charm->GetTypeId() == TYPEID_UNIT && ((Creature*)charm)->isPet() - && ((Pet*)charm)->getPetType() == POSSESSED_PET) + if(charm->GetTypeId() == TYPEID_UNIT) { - ((Pet*)charm)->Remove(PET_SAVE_AS_DELETED); + if(((Creature*)charm)->isPet() && ((Pet*)charm)->getPetType() == POSSESSED_PET) + ((Pet*)charm)->Remove(PET_SAVE_AS_DELETED); + else if(((Creature*)charm)->isVehicle()) + ExitVehicle((Vehicle*)charm); } - else + if(GetCharmGUID()) { - charm->RemoveSpellsCausingAura(SPELL_AURA_MOD_CHARM); - charm->RemoveSpellsCausingAura(SPELL_AURA_MOD_POSSESS_PET); - charm->RemoveSpellsCausingAura(SPELL_AURA_MOD_POSSESS); + charm->RemoveAurasByType(SPELL_AURA_MOD_CHARM); + charm->RemoveAurasByType(SPELL_AURA_MOD_POSSESS_PET); + charm->RemoveAurasByType(SPELL_AURA_MOD_POSSESS); } if(GetCharmGUID()) { - sLog.outError("CRASH ALARM! Player %s is not able to uncharm unit (Entry: %u, Type: %u)", GetName(), charm->GetEntry(), charm->GetTypeId()); + sLog.outCrash("Player %s is not able to uncharm unit (Entry: %u, Type: %u)", GetName(), charm->GetEntry(), charm->GetTypeId()); } } @@ -16347,6 +16523,10 @@ void Player::Say(const std::string& text, const uint32 language) WorldPacket data(SMSG_MESSAGECHAT, 200); BuildPlayerChat(&data, CHAT_MSG_SAY, text, language); SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY),true); + + if(sWorld.getConfig(CONFIG_CHATLOG_PUBLIC)) + sLog.outChat("[SAY] Player %s says (language %u): %s", + GetName(), language, text.c_str()); } void Player::Yell(const std::string& text, const uint32 language) @@ -16354,6 +16534,10 @@ void Player::Yell(const std::string& text, const uint32 language) WorldPacket data(SMSG_MESSAGECHAT, 200); BuildPlayerChat(&data, CHAT_MSG_YELL, text, language); SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_YELL),true); + + if(sWorld.getConfig(CONFIG_CHATLOG_PUBLIC)) + sLog.outChat("[YELL] Player %s yells (language %u): %s", + GetName(), language, text.c_str()); } void Player::TextEmote(const std::string& text) @@ -16361,6 +16545,10 @@ void Player::TextEmote(const std::string& text) WorldPacket data(SMSG_MESSAGECHAT, 200); BuildPlayerChat(&data, CHAT_MSG_EMOTE, text, LANG_UNIVERSAL); SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE),true, !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT), true ); + + if(sWorld.getConfig(CONFIG_CHATLOG_PUBLIC)) + sLog.outChat("[TEXTEMOTE] Player %s emotes: %s", + GetName(), text.c_str()); } void Player::Whisper(const std::string& text, uint32 language,uint64 receiver) @@ -16370,6 +16558,10 @@ void Player::Whisper(const std::string& text, uint32 language,uint64 receiver) Player *rPlayer = objmgr.GetPlayer(receiver); + if(sWorld.getConfig(CONFIG_CHATLOG_WHISPER)) + sLog.outChat("[WHISPER] Player %s tells %s: %s", + GetName(), rPlayer->GetName(), text.c_str()); + // when player you are whispering to is dnd, he cannot receive your message, unless you are in gm mode if(!rPlayer->isDND() || isGameMaster()) { @@ -16406,71 +16598,86 @@ void Player::PetSpellInitialize() { Pet* pet = GetPet(); - if(pet) - { - uint8 addlist = 0; + if(!pet) + return; - sLog.outDebug("Pet Spells Groups"); + sLog.outDebug("Pet Spells Groups"); - CreatureInfo const *cinfo = pet->GetCreatureInfo(); + CharmInfo *charmInfo = pet->GetCharmInfo(); - if(pet->isControlled() && (pet->getPetType() == HUNTER_PET || cinfo && cinfo->type == CREATURE_TYPE_DEMON && getClass() == CLASS_WARLOCK)) - { - for(PetSpellMap::iterator itr = pet->m_spells.begin();itr != pet->m_spells.end();++itr) - { - if(itr->second->state == PETSPELL_REMOVED) - continue; - ++addlist; - } - } + WorldPacket data(SMSG_PET_SPELLS, 8+4+4+4+10*4); + data << uint64(pet->GetGUID()); + data << uint32(pet->GetCreatureInfo()->family); // creature family (required for pet talents) + data << uint32(0); + data << uint8(pet->GetReactState()) << uint8(charmInfo->GetCommandState()) << uint16(0); - // first line + actionbar + spellcount + spells + last adds - WorldPacket data(SMSG_PET_SPELLS, 16+40+1+4*addlist+25); + // action bar loop + for(uint32 i = 0; i < MAX_SPELL_CONTROL_BAR; ++i) + { + data << uint32(charmInfo->GetActionBarEntry(i)->Raw); + } - CharmInfo *charmInfo = pet->GetCharmInfo(); + size_t spellsCountPos = data.wpos(); - //16 - data << (uint64)pet->GetGUID() << uint32(0x00000000) << uint8(pet->GetReactState()) << uint8(charmInfo->GetCommandState()) << uint16(0); + // spells count + uint8 addlist = 0; + data << uint8(addlist); // placeholder - for(uint32 i = 0; i < 10; i++) //40 + if(pet->isControlled() && ((pet->getPetType() == HUNTER_PET) || ((pet->GetCreatureInfo()->type == CREATURE_TYPE_DEMON) && (getClass() == CLASS_WARLOCK)))) + { + // spells loop + for (PetSpellMap::iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end(); ++itr) { - data << uint16(charmInfo->GetActionBarEntry(i)->SpellOrAction) << uint16(charmInfo->GetActionBarEntry(i)->Type); + if(itr->second->state == PETSPELL_REMOVED) + continue; + + data << uint16(itr->first); + data << uint16(itr->second->active); // pet spell active state isn't boolean + ++addlist; } + } - data << uint8(addlist); //1 + data.put<uint8>(spellsCountPos, addlist); - if(addlist && pet->isControlled()) - { - for (PetSpellMap::iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end(); ++itr) - { - if(itr->second->state == PETSPELL_REMOVED) - continue; + uint8 cooldownsCount = pet->m_CreatureSpellCooldowns.size() + pet->m_CreatureCategoryCooldowns.size(); + data << uint8(cooldownsCount); - data << uint16(itr->first); - data << uint16(itr->second->active); // pet spell active state isn't boolean - } - } + time_t curTime = time(NULL); + + for(CreatureSpellCooldowns::const_iterator itr = pet->m_CreatureSpellCooldowns.begin(); itr != pet->m_CreatureSpellCooldowns.end(); ++itr) + { + time_t cooldown = 0; - //data << uint8(0x01) << uint32(0x6010) << uint32(0x01) << uint32(0x05) << uint16(0x00); //15 - uint8 count = 3; //1+8+8+8=25 + if(itr->second > curTime) + cooldown = (itr->second - curTime) * IN_MILISECONDS; - // if count = 0, then end of packet... - data << count; - // uint32 value is spell id... - // uint64 value is constant 0, unknown... - data << uint32(0x6010) << uint64(0); // if count = 1, 2 or 3 - //data << uint32(0x5fd1) << uint64(0); // if count = 2 - data << uint32(0x8e8c) << uint64(0); // if count = 3 - data << uint32(0x8e8b) << uint64(0); // if count = 3 + data << uint16(itr->first); // spellid + data << uint16(0); // spell category? + data << uint32(itr->second); // cooldown + data << uint32(0); // category cooldown + } - GetSession()->SendPacket(&data); + for(CreatureSpellCooldowns::const_iterator itr = pet->m_CreatureCategoryCooldowns.begin(); itr != pet->m_CreatureCategoryCooldowns.end(); ++itr) + { + time_t cooldown = 0; + + if(itr->second > curTime) + cooldown = (itr->second - curTime) * IN_MILISECONDS; + + data << uint16(itr->first); // spellid + data << uint16(0); // spell category? + data << uint32(0); // cooldown + data << uint32(itr->second); // category cooldown } + + data.hexlike(); + + GetSession()->SendPacket(&data); } void Player::PossessSpellInitialize() { Unit* charm = GetCharm(); - if(!charm) return; @@ -16482,32 +16689,69 @@ void Player::PossessSpellInitialize() return; } - uint8 addlist = 0; - WorldPacket data(SMSG_PET_SPELLS, 16+40+1+4*addlist+25);// first line + actionbar + spellcount + spells + last adds + WorldPacket data(SMSG_PET_SPELLS, 20+40+1+1); - //16 - data << (uint64)charm->GetGUID() << uint32(0x00000000) << uint8(0) << uint8(0) << uint16(0); + //basic info 20 + data << uint64(charm->GetGUID()); + data << uint32(0x00000000); + data << uint32(0); + data << uint32(0); - for(uint32 i = 0; i < 10; i++) //40 - { + //action bar 40 + for(uint32 i = 0; i < 10; i++) data << uint16(charmInfo->GetActionBarEntry(i)->SpellOrAction) << uint16(charmInfo->GetActionBarEntry(i)->Type); - } - data << uint8(addlist); //1 + //addlist 1 + data << uint8(0); - uint8 count = 3; - data << count; - data << uint32(0x6010) << uint64(0); // if count = 1, 2 or 3 - data << uint32(0x8e8c) << uint64(0); // if count = 3 - data << uint32(0x8e8b) << uint64(0); // if count = 3 + //cooldown 1 + data << uint8(0); GetSession()->SendPacket(&data); } -void Player::CharmSpellInitialize() +void Player::VehicleSpellInitialize() { Unit* charm = GetCharm(); + if(!charm || charm->GetTypeId() != TYPEID_UNIT) + return; + + WorldPacket data(SMSG_PET_SPELLS, 8+4+4+4+4*10+1+1); + data << uint64(charm->GetGUID()); + data << uint32(0x00000000); + data << uint32(0x00000000); + data << uint32(0x00000101); + + for(uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i) + { + uint32 spellId = ((Creature*)charm)->m_spells[i]; + if(!spellId) + continue; + + SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId); + if(!spellInfo) + continue; + + if(IsPassiveSpell(spellId) || spellInfo->activeIconID == 2158) //flight + { + charm->CastSpell(charm, spellId, true); + data << uint16(0) << uint8(0) << uint8(i+8); + } + else + data << uint16(spellId) << uint8(0) << uint8(i+8); + } + + for(uint32 i = CREATURE_MAX_SPELLS; i < MAX_SPELL_CONTROL_BAR; ++i) + data << uint16(0) << uint8(0) << uint8(i+8); + + data << uint8(0); + data << uint8(0); + GetSession()->SendPacket(&data); +} +void Player::CharmSpellInitialize() +{ + Unit* charm = GetFirstControlled(); if(!charm) return; @@ -16519,14 +16763,12 @@ void Player::CharmSpellInitialize() } uint8 addlist = 0; - if(charm->GetTypeId() != TYPEID_PLAYER) { CreatureInfo const *cinfo = ((Creature*)charm)->GetCreatureInfo(); - - if(cinfo && cinfo->type == CREATURE_TYPE_DEMON && getClass() == CLASS_WARLOCK) + //if(cinfo && cinfo->type == CREATURE_TYPE_DEMON && getClass() == CLASS_WARLOCK) { - for(uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i) + for(uint32 i = 0; i < MAX_SPELL_CHARM; ++i) { if(charmInfo->GetCharmSpell(i)->spellId) ++addlist; @@ -16534,27 +16776,29 @@ void Player::CharmSpellInitialize() } } - WorldPacket data(SMSG_PET_SPELLS, 16+40+1+4*addlist+25);// first line + actionbar + spellcount + spells + last adds - - data << (uint64)charm->GetGUID() << uint32(0x00000000); + WorldPacket data(SMSG_PET_SPELLS, 20+40+1+4*addlist+1);// first line + actionbar + spellcount + spells + last adds + //basic info 20 + data << uint64(charm->GetGUID()); + data << uint32(0); + data << uint32(0); if(charm->GetTypeId() != TYPEID_PLAYER) data << uint8(((Creature*)charm)->GetReactState()) << uint8(charmInfo->GetCommandState()); else data << uint8(0) << uint8(0); - data << uint16(0); - for(uint32 i = 0; i < 10; i++) //40 + //action bar 40 + for(uint32 i = 0; i < MAX_SPELL_CONTROL_BAR; ++i) //40 { data << uint16(charmInfo->GetActionBarEntry(i)->SpellOrAction) << uint16(charmInfo->GetActionBarEntry(i)->Type); } + //add list data << uint8(addlist); //1 - if(addlist) { - for(uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i) + for(uint32 i = 0; i < MAX_SPELL_CHARM; ++i) { CharmSpellEntry *cspell = charmInfo->GetCharmSpell(i); if(cspell->spellId) @@ -16565,49 +16809,14 @@ void Player::CharmSpellInitialize() } } - uint8 count = 3; - data << count; - data << uint32(0x6010) << uint64(0); // if count = 1, 2 or 3 - data << uint32(0x8e8c) << uint64(0); // if count = 3 - data << uint32(0x8e8b) << uint64(0); // if count = 3 - - GetSession()->SendPacket(&data); -} - -int32 Player::GetTotalFlatMods(uint32 spellId, SpellModOp op) -{ - SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId); - if (!spellInfo) return 0; - int32 total = 0; - for (SpellModList::iterator itr = m_spellMods[op].begin(); itr != m_spellMods[op].end(); ++itr) - { - SpellModifier *mod = *itr; - - if(!IsAffectedBySpellmod(spellInfo,mod)) - continue; - - if (mod->type == SPELLMOD_FLAT) - total += mod->value; - } - return total; -} + //cooldown + uint8 count = 0; + data << uint8(count); // cooldowns count -int32 Player::GetTotalPctMods(uint32 spellId, SpellModOp op) -{ - SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId); - if (!spellInfo) return 0; - int32 total = 0; - for (SpellModList::iterator itr = m_spellMods[op].begin(); itr != m_spellMods[op].end(); ++itr) - { - SpellModifier *mod = *itr; + data.hexlike(); - if(!IsAffectedBySpellmod(spellInfo,mod)) - continue; - if (mod->type == SPELLMOD_PCT) - total += mod->value; - } - return total; + GetSession()->SendPacket(&data); } bool Player::IsAffectedBySpellmod(SpellEntry const *spellInfo, SpellModifier *mod, Spell const* spell) @@ -16627,22 +16836,27 @@ bool Player::IsAffectedBySpellmod(SpellEntry const *spellInfo, SpellModifier *mo return false; } - return spellmgr.IsAffectedBySpell(spellInfo,mod->spellId,mod->effectId,mod->mask); + return spellmgr.IsAffectedByMod(spellInfo, mod); } void Player::AddSpellMod(SpellModifier* mod, bool apply) { uint16 Opcode= (mod->type == SPELLMOD_FLAT) ? SMSG_SET_FLAT_SPELL_MODIFIER : SMSG_SET_PCT_SPELL_MODIFIER; - for(int eff=0;eff<64;++eff) + uint8 i=0; + flag96 _mask; + for(int eff=0;eff<96;++eff) { - uint64 _mask = uint64(1) << eff; + if ((eff!=0) && (eff%32==0)) + i++; + + _mask[i] = uint32(1) << (eff-(32*i)); if ( mod->mask & _mask) { int32 val = 0; for (SpellModList::iterator itr = m_spellMods[mod->op].begin(); itr != m_spellMods[mod->op].end(); ++itr) { - if ((*itr)->type == mod->type && (*itr)->mask & _mask) + if ((*itr)->type == mod->type && (*itr)->mask & _mask ) val += (*itr)->value; } val += apply ? mod->value : -(mod->value); @@ -16679,7 +16893,7 @@ void Player::RemoveSpellMods(Spell const* spell) if (mod && mod->charges == -1 && (mod->lastAffected == spell || mod->lastAffected==NULL)) { - RemoveAurasDueToSpell(mod->spellId); + RemoveAurasDueToSpell(mod->spellId, 0, AURA_REMOVE_BY_EXPIRE); if (m_spellMods[i].empty()) break; else @@ -16741,6 +16955,27 @@ void Player::RemovePetitionsAndSigns(uint64 guid, uint32 type) CharacterDatabase.CommitTransaction(); } +void Player::LeaveAllArenaTeams(uint64 guid) +{ + QueryResult *result = CharacterDatabase.PQuery("SELECT arena_team_member.arenateamid FROM arena_team_member JOIN arena_team ON arena_team_member.arenateamid = arena_team.arenateamid WHERE guid='%u'", GUID_LOPART(guid)); + if(!result) + return; + + do + { + Field *fields = result->Fetch(); + uint32 at_id = fields[0].GetUInt32(); + if(at_id != 0) + { + ArenaTeam * at = objmgr.GetArenaTeamById(at_id); + if(at) + at->DelMember(guid); + } + } while (result->NextRow()); + + delete result; +} + void Player::SetRestBonus (float rest_bonus_new) { // Prevent resting on max level @@ -16771,7 +17006,7 @@ void Player::HandleStealthedUnitsDetection() { std::list<Unit*> stealthedUnits; Trinity::AnyStealthedCheck u_check; - Trinity::UnitListSearcher<Trinity::AnyStealthedCheck > searcher(stealthedUnits, u_check); + Trinity::UnitListSearcher<Trinity::AnyStealthedCheck > searcher(this, stealthedUnits, u_check); VisitNearbyObject(World::GetMaxVisibleDistance(), searcher); for (std::list<Unit*>::iterator i = stealthedUnits.begin(); i != stealthedUnits.end(); ++i) @@ -16919,6 +17154,7 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, uint32 mount_i //Checks and preparations done, DO FLIGHT ModifyMoney(-(int32)totalcost); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING, totalcost); // prevent stealth flight //RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TALK); @@ -16961,7 +17197,7 @@ void Player::ProhibitSpellScholl(SpellSchoolMask idSchoolMask, uint32 unTimeMs ) { data << unSpellId; data << unTimeMs; // in m.secs - AddSpellCooldown(unSpellId, 0, curTime + unTimeMs/1000); + AddSpellCooldown(unSpellId, 0, curTime + unTimeMs/IN_MILISECONDS); } } GetSession()->SendPacket(&data); @@ -17328,6 +17564,102 @@ void Player::UpdatePvP(bool state, bool ovrride) } } +void Player::AddSpellAndCategoryCooldowns(SpellEntry const* spellInfo, uint32 itemId, Spell* spell, bool infinityCooldown) +{ + // init cooldown values + uint32 cat = 0; + int32 rec = -1; + int32 catrec = -1; + + // some special item spells without correct cooldown in SpellInfo + // cooldown information stored in item prototype + // This used in same way in WorldSession::HandleItemQuerySingleOpcode data sending to client. + + if(itemId) + { + if(ItemPrototype const* proto = ObjectMgr::GetItemPrototype(itemId)) + { + for(int idx = 0; idx < 5; ++idx) + { + if(proto->Spells[idx].SpellId == spellInfo->Id) + { + cat = proto->Spells[idx].SpellCategory; + rec = proto->Spells[idx].SpellCooldown; + catrec = proto->Spells[idx].SpellCategoryCooldown; + break; + } + } + } + } + + // if no cooldown found above then base at DBC data + if(rec < 0 && catrec < 0) + { + cat = spellInfo->Category; + rec = spellInfo->RecoveryTime; + catrec = spellInfo->CategoryRecoveryTime; + } + + time_t curTime = time(NULL); + + time_t catrecTime; + time_t recTime; + + // overwrite time for selected category + if(infinityCooldown) + { + // use +MONTH as infinity mark for spell cooldown (will checked as MONTH/2 at save ans skipped) + // but not allow ignore until reset or re-login + catrecTime = catrec > 0 ? curTime+MONTH : 0; + recTime = rec > 0 ? curTime+MONTH : catrecTime; + } + else + { + // shoot spells used equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK) + // prevent 0 cooldowns set by another way + if (rec <= 0 && catrec <= 0 && (cat == 76 || IsAutoRepeatRangedSpell(spellInfo) && spellInfo->Id != SPELL_ID_AUTOSHOT)) + rec = GetAttackTime(RANGED_ATTACK); + + // Now we have cooldown data (if found any), time to apply mods + if(rec > 0) + ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, rec, spell); + + if(catrec > 0) + ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, catrec, spell); + + // replace negative cooldowns by 0 + if (rec < 0) rec = 0; + if (catrec < 0) catrec = 0; + + // no cooldown after applying spell mods + if( rec == 0 && catrec == 0) + return; + + catrecTime = catrec ? curTime+catrec/IN_MILISECONDS : 0; + recTime = rec ? curTime+rec/IN_MILISECONDS : catrecTime; + } + + // self spell cooldown + if(recTime > 0) + AddSpellCooldown(spellInfo->Id, itemId, recTime); + + // category spells + if (cat && catrec > 0) + { + SpellCategoryStore::const_iterator i_scstore = sSpellCategoryStore.find(cat); + if(i_scstore != sSpellCategoryStore.end()) + { + for(SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset) + { + if(*i_scset == spellInfo->Id) // skip main spell, already handled above + continue; + + AddSpellCooldown(*i_scset, itemId, catrecTime); + } + } + } +} + void Player::AddSpellCooldown(uint32 spellid, uint32 itemid, time_t end_time) { SpellCooldown sc; @@ -17336,25 +17668,41 @@ void Player::AddSpellCooldown(uint32 spellid, uint32 itemid, time_t end_time) m_spellCooldowns[spellid] = sc; } -void Player::SendCooldownEvent(SpellEntry const *spellInfo) +void Player::SendCooldownEvent(SpellEntry const *spellInfo, uint32 itemId, Spell* spell) { - if ( !(spellInfo->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE) ) - return; + // start cooldowns at server side, if any + AddSpellAndCategoryCooldowns(spellInfo,itemId,spell); - // Get spell cooldown - int32 cooldown = GetSpellRecoveryTime(spellInfo); - // Apply spellmods - ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, cooldown); - if (cooldown < 0) - cooldown = 0; - // Add cooldown - AddSpellCooldown(spellInfo->Id, 0, time(NULL) + cooldown / 1000); - // Send activate + // Send activate cooldown timer (possible 0) at client side WorldPacket data(SMSG_COOLDOWN_EVENT, (4+8)); data << spellInfo->Id; data << GetGUID(); SendDirectMessage(&data); } + +void Player::UpdatePotionCooldown(Spell* spell) +{ + // no potion used i combat or still in combat + if(!m_lastPotionId || isInCombat()) + return; + + // Call not from spell cast, send cooldown event for item spells if no in combat + if(!spell) + { + // spell/item pair let set proper cooldown (except not existed charged spell cooldown spellmods for potions) + if(ItemPrototype const* proto = ObjectMgr::GetItemPrototype(m_lastPotionId)) + for(int idx = 0; idx < 5; ++idx) + if(proto->Spells[idx].SpellId && proto->Spells[idx].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE) + if(SpellEntry const* spellInfo = sSpellStore.LookupEntry(proto->Spells[idx].SpellId)) + SendCooldownEvent(spellInfo,m_lastPotionId); + } + // from spell cases (m_lastPotionId set in Spell::SendSpellCooldown) + else + SendCooldownEvent(spell->m_spellInfo,m_lastPotionId,spell); + + m_lastPotionId = 0; +} + //slot to be excluded while counting bool Player::EnchantmentFitsRequirements(uint32 enchantmentcondition, int8 slot) { @@ -17519,13 +17867,14 @@ void Player::LeaveBattleground(bool teleportToEntryPoint) { if(BattleGround *bg = GetBattleGround()) { - bool need_debuf = bg->isBattleGround() && (bg->GetStatus() == STATUS_IN_PROGRESS) && sWorld.getConfig(CONFIG_BATTLEGROUND_CAST_DESERTER); - bg->RemovePlayerAtLeave(GetGUID(), teleportToEntryPoint, true); // call after remove to be sure that player resurrected for correct cast - if(need_debuf) - CastSpell(this, 26013, true); // Deserter + if( bg->isBattleGround() && sWorld.getConfig(CONFIG_BATTLEGROUND_CAST_DESERTER) ) + { + if( bg->GetStatus() == STATUS_IN_PROGRESS || bg->GetStatus() == STATUS_WAIT_JOIN ) + CastSpell(this, 26013, true); // Deserter + } } } @@ -17571,9 +17920,13 @@ void Player::ReportedAfkBy(Player* reporter) bool Player::canSeeOrDetect(Unit const* u, bool detect, bool inVisibleList, bool is3dDistance) const { // Always can see self - if (u == this) + if (m_mover == u || this == u) return true; + // phased visibility (both must phased in same way) + if(!InSamePhase(u)) + return false; + // player visible for other player if not logout and at same transport // including case when player is out of world bool at_same_transport = @@ -17599,38 +17952,33 @@ bool Player::canSeeOrDetect(Unit const* u, bool detect, bool inVisibleList, bool if(!u->IsVisibleInGridForPlayer(this)) return false; - // If the player is currently channeling vision, update visibility from the target unit's location - const WorldObject* target = GetFarsightTarget(); - if (!target || !HasFarsightVision()) // Vision needs to be on the farsight target - target = this; - // different visible distance checks if(isInFlight()) // what see player in flight { - if (!target->IsWithinDistInMap(u,World::GetMaxVisibleDistanceInFlight()+(inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f), is3dDistance)) + if (!m_seer->IsWithinDistInMap(u,World::GetMaxVisibleDistanceInFlight()+(inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f), is3dDistance)) return false; } else if(!u->isAlive()) // distance for show body { - if (!target->IsWithinDistInMap(u,World::GetMaxVisibleDistanceForObject()+(inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f), is3dDistance)) + if (!m_seer->IsWithinDistInMap(u,World::GetMaxVisibleDistanceForObject()+(inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f), is3dDistance)) return false; } else if(u->GetTypeId()==TYPEID_PLAYER) // distance for show player { // Players far than max visible distance for player or not in our map are not visible too - if (!at_same_transport && !target->IsWithinDistInMap(u,World::GetMaxVisibleDistanceForPlayer()+(inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f), is3dDistance)) + if (!at_same_transport && !m_seer->IsWithinDistInMap(u,World::GetMaxVisibleDistanceForPlayer()+(inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f), is3dDistance)) return false; } else if(u->GetCharmerOrOwnerGUID()) // distance for show pet/charmed { // Pet/charmed far than max visible distance for player or not in our map are not visible too - if (!target->IsWithinDistInMap(u,World::GetMaxVisibleDistanceForPlayer()+(inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f), is3dDistance)) + if (!m_seer->IsWithinDistInMap(u,World::GetMaxVisibleDistanceForPlayer()+(inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f), is3dDistance)) return false; } else // distance for show creature { // Units far than max visible distance for creature or not in our map are not visible too - if (!target->IsWithinDistInMap(u + if (!m_seer->IsWithinDistInMap(u , u->isActiveObject() ? (MAX_VISIBILITY_DISTANCE - (inVisibleList ? 0.0f : World::GetVisibleUnitGreyDistance())) : (World::GetMaxVisibleDistanceForCreature() + (inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f)) , is3dDistance)) @@ -17651,7 +17999,7 @@ bool Player::canSeeOrDetect(Unit const* u, bool detect, bool inVisibleList, bool } // GM's can see everyone with invisibilitymask with less or equal security level - if(m_invisibilityMask || u->m_invisibilityMask) + if(m_mover->m_invisibilityMask || u->m_invisibilityMask) { if(isGameMaster()) { @@ -17662,7 +18010,7 @@ bool Player::canSeeOrDetect(Unit const* u, bool detect, bool inVisibleList, bool } // player see other player with stealth/invisibility only if he in same group or raid or same team (raid/team case dependent from conf setting) - if(!canDetectInvisibilityOf(u)) + if(!m_mover->canDetectInvisibilityOf(u)) if(!(u->GetTypeId()==TYPEID_PLAYER && !IsHostileTo(u) && IsGroupVisibleFor(((Player*)u)))) return false; } @@ -17677,7 +18025,7 @@ bool Player::canSeeOrDetect(Unit const* u, bool detect, bool inVisibleList, bool detect = false; if(m_DetectInvTimer < 300 || !HaveAtClient(u)) if(!(u->GetTypeId()==TYPEID_PLAYER && !IsHostileTo(u) && IsGroupVisibleFor(((Player*)u)))) - if(!detect || !canDetectStealthOf(u, GetDistance(u))) + if(!detect || !m_mover->canDetectStealthOf(u, GetDistance(u))) return false; } @@ -17790,7 +18138,7 @@ void Player::UpdateVisibilityOf(WorldObject* target) void Player::SendInitialVisiblePackets(Unit* target) { - SendAuraDurationsForTarget(target); + SendAurasForTarget(target); if(target->isAlive()) { if(target->GetMotionMaster()->GetCurrentMovementGeneratorType() != IDLE_MOTION_TYPE) @@ -17909,7 +18257,7 @@ void Player::AddComboPoints(Unit* target, int8 count) return; // without combo points lost (duration checked in aura) - RemoveSpellsCausingAura(SPELL_AURA_RETAIN_COMBO_POINTS); + RemoveAurasByType(SPELL_AURA_RETAIN_COMBO_POINTS); if(target->GetGUID() == m_comboTarget) { @@ -17939,7 +18287,7 @@ void Player::ClearComboPoints() return; // without combopoints lost (duration checked in aura) - RemoveSpellsCausingAura(SPELL_AURA_RETAIN_COMBO_POINTS); + RemoveAurasByType(SPELL_AURA_RETAIN_COMBO_POINTS); m_comboPoints = 0; @@ -17953,7 +18301,8 @@ void Player::ClearComboPoints() void Player::SetGroup(Group *group, int8 subgroup) { - if(group == NULL) m_group.unlink(); + if(group == NULL) + m_group.unlink(); else { // never use SetGroup without a subgroup unless you specify NULL for group @@ -17965,7 +18314,7 @@ void Player::SetGroup(Group *group, int8 subgroup) void Player::SendInitialPacketsBeforeAddToMap() { - WorldPacket data(SMSG_SET_REST_START, 4); + WorldPacket data(SMSG_SET_REST_START_OBSOLETE, 4); data << uint32(0); // unknown, may be rest state time or experience GetSession()->SendPacket(&data); @@ -17992,9 +18341,13 @@ void Player::SendInitialPacketsBeforeAddToMap() GetSession()->SendPacket(&data); SendInitialActionButtons(); - SendInitialReputations(); - UpdateZone(GetZoneId()); - SendInitWorldStates(); + m_reputationMgr.SendInitialReputations(); + m_achievementMgr.SendAllAchievementData(); + + // update zone + uint32 newzone, newarea; + GetZoneAndAreaId(newzone,newarea); + UpdateZone(newzone,newarea); // also call SendInitWorldStates(); // SMSG_SET_AURA_SINGLE @@ -18006,10 +18359,17 @@ void Player::SendInitialPacketsBeforeAddToMap() // set fly flag if in fly form or taxi flight to prevent visually drop at ground in showup moment if(HasAuraType(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED) || isInFlight()) AddUnitMovementFlag(MOVEMENTFLAG_FLYING2); + + m_mover = this; + m_seer = this; } void Player::SendInitialPacketsAfterAddToMap() { + WorldPacket data(SMSG_TIME_SYNC_REQ, 4); // new 2.0.x, enable movement + data << uint32(0x00000000); // on blizz it increments periodically + GetSession()->SendPacket(&data); + CastSpell(this, 836, true); // LOGINEFFECT // set some aura effects that send packet to player client after add player to map @@ -18023,7 +18383,7 @@ void Player::SendInitialPacketsAfterAddToMap() }; for(AuraType const* itr = &auratypes[0]; itr && itr[0] != SPELL_AURA_NONE; ++itr) { - Unit::AuraList const& auraList = GetAurasByType(*itr); + Unit::AuraEffectList const& auraList = GetAurasByType(*itr); if(!auraList.empty()) auraList.front()->ApplyModifier(true,true); } @@ -18040,6 +18400,7 @@ void Player::SendInitialPacketsAfterAddToMap() SendMessageToSet(&data,true); } + SendAurasForTarget(this); SendEnchantmentDurations(); // must be after add to map SendItemDurations(); // must be after add to map } @@ -18052,16 +18413,24 @@ void Player::SendUpdateToOutOfRangeGroupMembers() group->UpdatePlayerOutOfRange(this); m_groupUpdateMask = GROUP_UPDATE_FLAG_NONE; - m_auraUpdateMask = 0; + m_auraRaidUpdateMask = 0; if(Pet *pet = GetPet()) - pet->ResetAuraUpdateMask(); + pet->ResetAuraUpdateMaskForRaid(); } -void Player::SendTransferAborted(uint32 mapid, uint16 reason) +void Player::SendTransferAborted(uint32 mapid, uint8 reason, uint8 arg) { WorldPacket data(SMSG_TRANSFER_ABORTED, 4+2); data << uint32(mapid); - data << uint16(reason); // transfer abort reason + data << uint8(reason); // transfer abort reason + switch(reason) + { + case TRANSFER_ABORT_INSUF_EXPAN_LVL: + case TRANSFER_ABORT_DIFFICULTY: + case TRANSFER_ABORT_UNIQUE_MESSAGE: + data << uint8(arg); + break; + } GetSession()->SendPacket(&data); } @@ -18086,7 +18455,7 @@ void Player::SendInstanceResetWarning(uint32 mapid, uint32 time) void Player::ApplyEquipCooldown( Item * pItem ) { - for(int i = 0; i <5; ++i) + for(int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) { _Spell const& spellData = pItem->GetProto()->Spells[i]; @@ -18127,22 +18496,18 @@ void Player::resetSpells() learnQuestRewardedSpells(); } -void Player::learnDefaultSpells(bool loading) +void Player::learnDefaultSpells() { // learn default race/class spells PlayerInfo const *info = objmgr.GetPlayerInfo(getRace(),getClass()); - std::list<CreateSpellPair>::const_iterator spell_itr; - for (spell_itr = info->spell.begin(); spell_itr!=info->spell.end(); ++spell_itr) + for (PlayerCreateInfoSpells::const_iterator itr = info->spell.begin(); itr!=info->spell.end(); ++itr) { - uint16 tspell = spell_itr->first; - if (tspell) - { - sLog.outDebug("PLAYER: Adding initial spell, id = %u",tspell); - if(loading || !spell_itr->second) // not care about passive spells or loading case - addSpell(tspell,spell_itr->second); - else // but send in normal spell in game learn case - learnSpell(tspell); - } + 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,true,false); + else // but send in normal spell in game learn case + learnSpell(tspell,true); } } @@ -18234,7 +18599,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(); @@ -18252,35 +18617,56 @@ 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() +void Player::SendAurasForTarget(Unit *target) { - for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++) - { - if(!GetUInt32Value(PLAYER_SKILL_INDEX(i))) - continue; - - uint32 pskill = GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF; + if(!target || target->GetVisibleAuras()->empty()) // speedup things + return; - learnSkillRewardedSpells(pskill); - } -} + WorldPacket data(SMSG_AURA_UPDATE_ALL); + data.append(target->GetPackGUID()); -void Player::SendAuraDurationsForTarget(Unit* target) -{ - for(Unit::AuraMap::const_iterator itr = target->GetAuras().begin(); itr != target->GetAuras().end(); ++itr) + Unit::VisibleAuraMap const *visibleAuras = target->GetVisibleAuras(); + for(Unit::VisibleAuraMap::const_iterator itr = visibleAuras->begin(); itr != visibleAuras->end(); ++itr) { - Aura* aura = itr->second; - if(aura->GetAuraSlot() >= MAX_AURAS || aura->IsPassive() || aura->GetCasterGUID()!=GetGUID()) - continue; + Aura * aura=itr->second; + data << uint8(aura->GetAuraSlot()); + data << uint32(aura->GetId()); - aura->SendAuraDurationForCaster(this); + // flags + data << aura->m_auraFlags; + // level + data << aura->m_auraLevel; + // charges + data << uint8(aura->GetStackAmount()>1 ? aura->GetStackAmount() : aura->GetAuraCharges()); + + if(!(aura->m_auraFlags & AFLAG_CASTER)) + { + if (Unit * caster = aura->GetCaster()) + data.append(caster->GetPackGUID()); + else + data << uint8(0); + } + + if(aura->m_auraFlags & AFLAG_DURATION) // include aura duration + { + data << uint32(aura->GetAuraMaxDuration()); + data << uint32(aura->GetAuraDuration()); + } } + + GetSession()->SendPacket(&data); } void Player::SetDailyQuestStatus( uint32 quest_id ) @@ -18312,7 +18698,7 @@ BattleGround* Player::GetBattleGround() const if(GetBattleGroundId()==0) return NULL; - return sBattleGroundMgr.GetBattleGround(GetBattleGroundId()); + return sBattleGroundMgr.GetBattleGround(GetBattleGroundId(), m_bgTypeID); } bool Player::InArena() const @@ -18324,7 +18710,7 @@ bool Player::InArena() const return true; } -bool Player::GetBGAccessByLevel(uint32 bgTypeId) const +bool Player::GetBGAccessByLevel(BattleGroundTypeId bgTypeId) const { // get a template bg instead of running one BattleGround *bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); @@ -18337,46 +18723,26 @@ bool Player::GetBGAccessByLevel(uint32 bgTypeId) const return true; } -uint32 Player::GetMinLevelForBattleGroundQueueId(uint32 queue_id) -{ - if(queue_id < 1) - return 0; - - if(queue_id >=6) - queue_id = 6; - - return 10*(queue_id+1); -} - -uint32 Player::GetMaxLevelForBattleGroundQueueId(uint32 queue_id) -{ - if(queue_id >=6) - return 255; // hardcoded max level - - return 10*(queue_id+2)-1; -} - -//TODO make this more generic - current implementation is wrong -uint32 Player::GetBattleGroundQueueIdFromLevel() const +BGQueueIdBasedOnLevel Player::GetBattleGroundQueueIdFromLevel(BattleGroundTypeId bgTypeId) const { + //returned to hardcoded version of this function, because there is no way to code it dynamic uint32 level = getLevel(); - if(level <= 19) - return 0; - else if (level > 69) - return 6; - else - return level/10 - 1; // 20..29 -> 1, 30-39 -> 2, ... - /* - assert(bgTypeId < MAX_BATTLEGROUND_TYPES); - BattleGround *bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); - assert(bg); - return (getLevel() - bg->GetMinLevel()) / 10;*/ + if( bgTypeId == BATTLEGROUND_AV ) + level--; + + uint32 queue_id = (level / 10) - 1; // for ranges 0 - 19, 20 - 29, 30 - 39, 40 - 49, 50 - 59, 60 - 69, 70 -79, 80 + if( queue_id >= MAX_BATTLEGROUND_QUEUES ) + { + sLog.outError("BattleGround: too high queue_id %u this shouldn't happen", queue_id); + return QUEUE_ID_MAX_LEVEL_80; + } + return BGQueueIdBasedOnLevel(queue_id); } float Player::GetReputationPriceDiscount( Creature const* pCreature ) const { FactionTemplateEntry const* vendor_faction = pCreature->getFactionTemplateEntry(); - if(!vendor_faction) + if(!vendor_faction || !vendor_faction->faction) return 1.0f; ReputationRank rank = GetReputationRank(vendor_faction->faction); @@ -18393,28 +18759,42 @@ bool Player::IsSpellFitByClassAndRace( uint32 spell_id ) const SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spell_id); SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spell_id); + if(lower==upper) + return true; for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx) { // skip wrong race skills if( _spell_idx->second->racemask && (_spell_idx->second->racemask & racemask) == 0) - return false; + continue; // skip wrong class skills if( _spell_idx->second->classmask && (_spell_idx->second->classmask & classmask) == 0) - return false; + continue; + + return true; } - return true; + + return false; } -bool Player::HasQuestForGO(int32 GOId) +bool Player::HasQuestForGO(int32 GOId) const { - for( QuestStatusMap::iterator i = mQuestStatus.begin( ); i != mQuestStatus.end( ); ++i ) + for( int i = 0; i < MAX_QUEST_LOG_SIZE; ++i ) { - QuestStatusData qs=i->second; + uint32 questid = GetQuestSlotQuestId(i); + if ( questid == 0 ) + continue; + + QuestStatusMap::const_iterator qs_itr = mQuestStatus.find(questid); + if(qs_itr == mQuestStatus.end()) + continue; + + QuestStatusData const& qs = qs_itr->second; + if (qs.m_status == QUEST_STATUS_INCOMPLETE) { - Quest const* qinfo = objmgr.GetQuestTemplate(i->first); + Quest const* qinfo = objmgr.GetQuestTemplate(questid); if(!qinfo) continue; @@ -18474,6 +18854,7 @@ void Player::SummonIfPossible(bool agree) } // drop flag at summon + // this code can be reached only when GM is summoning player who carries flag, because player should be immune to summoning spells when he carries flag if(BattleGround *bg = GetBattleGround()) bg->EventPlayerDroppedFlag(this); @@ -18509,9 +18890,8 @@ void Player::AutoUnequipOffhandIfNeed() if(!offItem) return; - Item *mainItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND ); - - if(!mainItem || mainItem->GetProto()->InventoryType != INVTYPE_2HWEAPON) + // need unequip offhand for 2h-weapon without TitanGrip (in any from hands) + if (CanTitanGrip() || (offItem->GetProto()->InventoryType != INVTYPE_2HWEAPON && !IsTwoHandUsed())) return; ItemPosCountVec off_dest; @@ -18523,7 +18903,16 @@ void Player::AutoUnequipOffhandIfNeed() } else { - sLog.outError("Player::EquipItem: Can's store offhand item at 2hand item equip for player (GUID: %u).",GetGUIDLow()); + MailItemsInfo mi; + mi.AddItem(offItem->GetGUIDLow(), offItem->GetEntry(), offItem); + MoveItemFromInventory(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND, true); + CharacterDatabase.BeginTransaction(); + offItem->DeleteFromInventoryDB(); // deletes item from character's inventory + offItem->SaveToDB(); // recursive and not have transaction guard into self, item not in inventory and can be save standalone + CharacterDatabase.CommitTransaction(); + + std::string subject = GetSession()->GetMangosString(LANG_NOT_EQUIPPED_ITEM); + WorldSession::SendMailTo(this, MAIL_NORMAL, MAIL_STATIONERY_GM, GetGUIDLow(), GetGUIDLow(), subject, 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE); } } @@ -18544,7 +18933,7 @@ bool Player::HasItemFitToSpellReqirements(SpellEntry const* spellInfo, Item cons case ITEM_CLASS_WEAPON: { for(int i= EQUIPMENT_SLOT_MAINHAND; i < EQUIPMENT_SLOT_TABARD; ++i) - if(Item *item = GetItemByPos( INVENTORY_SLOT_BAG_0, i )) + if(Item *item = GetUseableItemByPos( INVENTORY_SLOT_BAG_0, i )) if(item!=ignoreItem && item->IsFitToSpellRequirements(spellInfo)) return true; break; @@ -18553,17 +18942,17 @@ bool Player::HasItemFitToSpellReqirements(SpellEntry const* spellInfo, Item cons { // tabard not have dependent spells for(int i= EQUIPMENT_SLOT_START; i< EQUIPMENT_SLOT_MAINHAND; ++i) - if(Item *item = GetItemByPos( INVENTORY_SLOT_BAG_0, i )) + if(Item *item = GetUseableItemByPos( INVENTORY_SLOT_BAG_0, i )) if(item!=ignoreItem && item->IsFitToSpellRequirements(spellInfo)) return true; // shields can be equipped to offhand slot - if(Item *item = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND)) + if(Item *item = GetUseableItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND)) if(item!=ignoreItem && item->IsFitToSpellRequirements(spellInfo)) return true; // ranged slot can have some armor subclasses - if(Item *item = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED)) + if(Item *item = GetUseableItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED)) if(item!=ignoreItem && item->IsFitToSpellRequirements(spellInfo)) return true; @@ -18577,6 +18966,24 @@ bool Player::HasItemFitToSpellReqirements(SpellEntry const* spellInfo, Item cons return false; } +bool Player::CanNoReagentCast(SpellEntry const* spellInfo) const +{ + // don't take reagents for spells with SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP + if (spellInfo->AttributesEx5 & SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP && + HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION)) + return true; + + // Check no reagent use mask + flag96 noReagentMask; + noReagentMask[0] = GetUInt32Value(PLAYER_NO_REAGENT_COST_1); + noReagentMask[1] = GetUInt32Value(PLAYER_NO_REAGENT_COST_1+1); + noReagentMask[2] = GetUInt32Value(PLAYER_NO_REAGENT_COST_1+2); + if (spellInfo->SpellFamilyFlags & noReagentMask) + return true; + + return false; +} + void Player::RemoveItemDependentAurasAndCasts( Item * pItem ) { AuraMap& auras = GetAuras(); @@ -18600,8 +19007,7 @@ void Player::RemoveItemDependentAurasAndCasts( Item * pItem ) } // no alt item, remove aura, restart check - RemoveAurasDueToSpell(aura->GetId()); - itr = auras.begin(); + RemoveAura(itr); } // currently casted spells can be dependent from item @@ -18618,11 +19024,11 @@ uint32 Player::GetResurrectionSpellId() // search priceless resurrection possibilities uint32 prio = 0; uint32 spell_id = 0; - AuraList const& dummyAuras = GetAurasByType(SPELL_AURA_DUMMY); - for(AuraList::const_iterator itr = dummyAuras.begin(); itr != dummyAuras.end(); ++itr) + AuraEffectList const& dummyAuras = GetAurasByType(SPELL_AURA_DUMMY); + for(AuraEffectList::const_iterator itr = dummyAuras.begin(); itr != dummyAuras.end(); ++itr) { // Soulstone Resurrection // prio: 3 (max, non death persistent) - if( prio < 2 && (*itr)->GetSpellProto()->SpellVisual == 99 && (*itr)->GetSpellProto()->SpellIconID == 92 ) + if( prio < 2 && (*itr)->GetSpellProto()->SpellVisual[0] == 99 && (*itr)->GetSpellProto()->SpellIconID == 92 ) { switch((*itr)->GetId()) { @@ -18632,6 +19038,7 @@ uint32 Player::GetResurrectionSpellId() case 20764: spell_id = 20760; break; // rank 4 case 20765: spell_id = 20761; break; // rank 5 case 27239: spell_id = 27240; break; // rank 6 + case 47883: spell_id = 47882; break; // rank 7 default: sLog.outError("Unhandled spell %%u: S.Resurrection",(*itr)->GetId()); continue; @@ -18654,6 +19061,26 @@ uint32 Player::GetResurrectionSpellId() return spell_id; } +// Used in triggers for check "Only to targets that grant experience or honor" req +bool Player::isHonorOrXPTarget(Unit* pVictim) +{ + uint32 v_level = pVictim->getLevel(); + uint32 k_grey = Trinity::XP::GetGrayLevel(getLevel()); + + // Victim level less gray level + if(v_level<=k_grey) + return false; + + if(pVictim->GetTypeId() == TYPEID_UNIT) + { + if (((Creature*)pVictim)->isTotem() || + ((Creature*)pVictim)->isPet() || + ((Creature*)pVictim)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_XP_AT_KILL) + return false; + } + return true; +} + bool Player::RewardPlayerAndGroupAtKill(Unit* pVictim) { bool PvP = pVictim->isCharmedOwnedByPlayerOrPlayer(); @@ -18751,6 +19178,31 @@ bool Player::RewardPlayerAndGroupAtKill(Unit* pVictim) return xp || honored_kill; } +void Player::RewardPlayerAndGroupAtEvent(uint32 creature_id, WorldObject* pRewardSource) +{ + uint64 creature_guid = pRewardSource->GetTypeId()==TYPEID_UNIT ? pRewardSource->GetGUID() : uint64(0); + + // prepare data for near group iteration + if(Group *pGroup = GetGroup()) + { + for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player* pGroupGuy = itr->getSource(); + if(!pGroupGuy) + continue; + + if(!pGroupGuy->IsAtGroupRewardDistance(pRewardSource)) + continue; // member (alive or dead) or his corpse at req. distance + + // quest objectives updated only for alive group member or dead but with not released body + if(pGroupGuy->isAlive()|| !pGroupGuy->GetCorpse()) + pGroupGuy->KilledMonster(creature_id, creature_guid); + } + } + else // if (!pGroup) + KilledMonster(creature_id, creature_guid); +} + bool Player::IsAtGroupRewardDistance(WorldObject const* pRewardSource) const { if(pRewardSource->GetDistance(this) <= sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE)) @@ -18781,6 +19233,9 @@ uint32 Player::GetBaseWeaponSkillValue (WeaponAttackType attType) const void Player::ResurectUsingRequestData() { + /// Teleport before resurrecting, otherwise the player might get attacked from creatures near his corpse + TeleportTo(m_resurrectMap, m_resurrectX, m_resurrectY, m_resurrectZ, GetOrientation()); + ResurrectPlayer(0.0f,false); if(GetMaxHealth() > m_resurrectHealth) @@ -18798,8 +19253,6 @@ void Player::ResurectUsingRequestData() SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY) ); SpawnCorpseBones(); - - TeleportTo(m_resurrectMap, m_resurrectX, m_resurrectY, m_resurrectZ, GetOrientation()); } void Player::SetClientControl(Unit* target, uint8 allowMove) @@ -18813,29 +19266,18 @@ void Player::SetClientControl(Unit* target, uint8 allowMove) void Player::UpdateZoneDependentAuras( uint32 newZone ) { // remove new continent flight forms - if( !isGameMaster() && - GetVirtualMapForMapAndZone(GetMapId(),newZone) != 530) + if( !IsAllowUseFlyMountsHere() ) { - RemoveSpellsCausingAura(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED); - RemoveSpellsCausingAura(SPELL_AURA_FLY); + RemoveAurasByType(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED); + RemoveAurasByType(SPELL_AURA_FLY); } - // Some spells applied at enter into zone (with subzones) - // Human Illusion - // NOTE: these are removed by RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CHANGE_MAP); - if ( newZone == 2367 ) // Old Hillsbrad Foothills - { - uint32 spellid = 0; - // all horde races - if( GetTeam() == HORDE ) - spellid = getGender() == GENDER_FEMALE ? 35481 : 35480; - // and some alliance races - else if( getRace() == RACE_NIGHTELF || getRace() == RACE_DRAENEI ) - spellid = getGender() == GENDER_FEMALE ? 35483 : 35482; - - if(spellid && !HasAura(spellid,0) ) - CastSpell(this,spellid,true); - } + // Some spells applied at enter into zone (with subzones), aura removed in UpdateAreaDependentAuras that called always at zone->area update + SpellAreaForAreaMapBounds saBounds = spellmgr.GetSpellAreaForAreaMapBounds(newZone); + for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) + if(itr->second->autocast && itr->second->IsFitToRequirements(this,newZone,0)) + if( !HasAura(itr->second->spellId) ) + CastSpell(this,itr->second->spellId,true); } void Player::UpdateAreaDependentAuras( uint32 newArea ) @@ -18844,26 +19286,18 @@ void Player::UpdateAreaDependentAuras( uint32 newArea ) for(AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end();) { // use m_zoneUpdateId for speed: UpdateArea called from UpdateZone or instead UpdateZone in both cases m_zoneUpdateId up-to-date - if(!IsSpellAllowedInLocation(iter->second->GetSpellProto(),GetMapId(),m_zoneUpdateId,newArea)) + if(spellmgr.GetSpellAllowedInLocationError(iter->second->GetSpellProto(),GetMapId(),m_zoneUpdateId,newArea,this) != SPELL_CAST_OK) RemoveAura(iter); else ++iter; } - // unmount if enter in this subzone - if( newArea == 35) - RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); - // Dragonmaw Illusion - else if( newArea == 3759 || newArea == 3966 || newArea == 3939 ) - { - if( GetDummyAura(40214) ) - { - if( !HasAura(40216,0) ) - CastSpell(this,40216,true); - if( !HasAura(42016,0) ) - CastSpell(this,42016,true); - } - } + // some auras applied at subzone enter + SpellAreaForAreaMapBounds saBounds = spellmgr.GetSpellAreaForAreaMapBounds(newArea); + for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) + if(itr->second->autocast && itr->second->IsFitToRequirements(this,m_zoneUpdateId,newArea)) + if( !HasAura(itr->second->spellId) ) + CastSpell(this,itr->second->spellId,true); } uint32 Player::GetCorpseReclaimDelay(bool pvp) const @@ -18949,7 +19383,7 @@ void Player::SendCorpseReclaimDelay(bool load) //! corpse reclaim delay 30 * 1000ms or longer at often deaths WorldPacket data(SMSG_CORPSE_RECLAIM_DELAY, 4); - data << uint32(delay*1000); + data << uint32(delay*IN_MILISECONDS); GetSession()->SendPacket( &data ); } @@ -18994,21 +19428,85 @@ PartyResult Player::CanUninviteFromGroup() const return PARTY_RESULT_OK; } +void Player::SetBattleGroundRaid(Group* group, int8 subgroup) +{ + //we must move references from m_group to m_originalGroup + SetOriginalGroup(GetGroup(), GetSubGroup()); + + m_group.unlink(); + m_group.link(group, this); + m_group.setSubGroup((uint8)subgroup); +} + +void Player::RemoveFromBattleGroundRaid() +{ + //remove existing reference + m_group.unlink(); + if( Group* group = GetOriginalGroup() ) + { + m_group.link(group, this); + m_group.setSubGroup(GetOriginalSubGroup()); + } + SetOriginalGroup(NULL); +} + +void Player::SetOriginalGroup(Group *group, int8 subgroup) +{ + if( group == NULL ) + m_originalGroup.unlink(); + else + { + // never use SetOriginalGroup without a subgroup unless you specify NULL for group + assert(subgroup >= 0); + m_originalGroup.link(group, this); + m_originalGroup.setSubGroup((uint8)subgroup); + } +} + void Player::UpdateUnderwaterState( Map* m, float x, float y, float z ) { - float water_z = m->GetWaterLevel(x,y); - float height_z = m->GetHeight(x,y,z, false); // use .map base surface height - uint8 flag1 = m->GetTerrainType(x,y); + LiquidData liquid_status; + ZLiquidStatus res = m->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &liquid_status); + if (!res) + { + m_MirrorTimerFlags &= ~(UNDERWATER_INWATER|UNDERWATER_INLAVA|UNDERWATER_INSLIME|UNDERWARER_INDARKWATER); + // Small hack for enable breath in WMO + if (IsInWater()) + m_MirrorTimerFlags|=UNDERWATER_INWATER; + return; + } + + // All liquids type - check under water position + if (liquid_status.type&(MAP_LIQUID_TYPE_WATER|MAP_LIQUID_TYPE_OCEAN|MAP_LIQUID_TYPE_MAGMA|MAP_LIQUID_TYPE_SLIME)) + { + if ( res & LIQUID_MAP_UNDER_WATER) + m_MirrorTimerFlags |= UNDERWATER_INWATER; + else + m_MirrorTimerFlags &= ~UNDERWATER_INWATER; + } - //!Underwater check, not in water if underground or above water level - if (height_z <= INVALID_HEIGHT || z < (height_z-2) || z > (water_z - 2) ) - m_isunderwater &= 0x7A; - else if ((z < (water_z - 2)) && (flag1 & 0x01)) - m_isunderwater |= 0x01; + // Allow travel in dark water on taxi or transport + if ((liquid_status.type & MAP_LIQUID_TYPE_DARK_WATER) && !isInFlight() && !GetTransport()) + m_MirrorTimerFlags |= UNDERWARER_INDARKWATER; + else + m_MirrorTimerFlags &= ~UNDERWARER_INDARKWATER; - //!in lava check, anywhere under lava level - if ((height_z <= INVALID_HEIGHT || z < (height_z - 0)) && (flag1 == 0x00) && IsInWater()) - m_isunderwater |= 0x80; + // in lava check, anywhere in lava level + if (liquid_status.type&MAP_LIQUID_TYPE_MAGMA) + { + if (res & (LIQUID_MAP_UNDER_WATER|LIQUID_MAP_IN_WATER|LIQUID_MAP_WATER_WALK)) + m_MirrorTimerFlags |= UNDERWATER_INLAVA; + else + m_MirrorTimerFlags &= ~UNDERWATER_INLAVA; + } + // in slime check, anywhere in slime level + if (liquid_status.type&MAP_LIQUID_TYPE_SLIME) + { + if (res & (LIQUID_MAP_UNDER_WATER|LIQUID_MAP_IN_WATER|LIQUID_MAP_WATER_WALK)) + m_MirrorTimerFlags |= UNDERWATER_INSLIME; + else + m_MirrorTimerFlags &= ~UNDERWATER_INSLIME; + } } void Player::SetCanParry( bool value ) @@ -19041,140 +19539,247 @@ bool ItemPosCount::isContainedIn(ItemPosCountVec const& vec) const //-------------TRINITY--------------- //*********************************** -void Player::HandleFallDamage(MovementInfo& movementInfo) +void Player::StopCastingBindSight() { - if(movementInfo.fallTime < 1500) - return; - - // calculate total z distance of the fall - float z_diff = m_lastFallZ - movementInfo.z; - sLog.outDebug("zDiff = %f", z_diff); - - //Players with low fall distance, Feather Fall or physical immunity (charges used) are ignored - // 14.57 can be calculated by resolving damageperc formular below to 0 - if (z_diff >= 14.57f && !isDead() && !isGameMaster() && - !HasAuraType(SPELL_AURA_HOVER) && !HasAuraType(SPELL_AURA_FEATHER_FALL) && - !HasAuraType(SPELL_AURA_FLY) && !IsImmunedToDamage(SPELL_SCHOOL_MASK_NORMAL,true) ) + if(WorldObject* target = GetViewpoint()) { - //Safe fall, fall height reduction - int32 safe_fall = GetTotalAuraModifier(SPELL_AURA_SAFE_FALL); - - float damageperc = 0.018f*(z_diff-safe_fall)-0.2426f; - - if(damageperc >0 ) + if(target->isType(TYPEMASK_UNIT)) { - uint32 damage = (uint32)(damageperc * GetMaxHealth()*sWorld.getRate(RATE_DAMAGE_FALL)); - - float height = movementInfo.z; - UpdateGroundPositionZ(movementInfo.x,movementInfo.y,height); - - if (damage > 0) - { - //Prevent fall damage from being more than the player maximum health - if (damage > GetMaxHealth()) - damage = GetMaxHealth(); - - // Gust of Wind - if (GetDummyAura(43621)) - damage = GetMaxHealth()/2; - - EnvironmentalDamage(GetGUID(), DAMAGE_FALL, damage); - } - - //Z given by moveinfo, LastZ, FallTime, WaterZ, MapZ, Damage, Safefall reduction - DEBUG_LOG("FALLDAMAGE z=%f sz=%f pZ=%f FallTime=%d mZ=%f damage=%d SF=%d" , movementInfo.z, height, GetPositionZ(), movementInfo.fallTime, height, damage, safe_fall); + ((Unit*)target)->RemoveAura(SPELL_AURA_BIND_SIGHT, GetGUID()); + ((Unit*)target)->RemoveAura(SPELL_AURA_MOD_POSSESS, GetGUID()); + ((Unit*)target)->RemoveAura(SPELL_AURA_MOD_POSSESS_PET, GetGUID()); } } } -void Player::HandleFallUnderMap() +void Player::SetViewpoint(WorldObject* target, bool apply) { - if(InBattleGround() - && GetBattleGround() - && GetBattleGround()->HandlePlayerUnderMap(this)) + if(apply) { - // do nothing, the handle already did if returned true + sLog.outDebug("Player::CreateViewpoint: Player %s create seer %u (TypeId: %u).", GetName(), target->GetEntry(), target->GetTypeId()); + + if(!AddUInt64Value(PLAYER_FARSIGHT, target->GetGUID())) + { + sLog.outCrash("Player::CreateViewpoint: Player %s cannot add new viewpoint!", GetName()); + return; + } + + if(target->isType(TYPEMASK_UNIT)) + ((Unit*)target)->AddPlayerToVision(this); } else { - // NOTE: this is actually called many times while falling - // even after the player has been teleported away - // TODO: discard movement packets after the player is rooted - if(isAlive()) + sLog.outDebug("Player::CreateViewpoint: Player %s remove seer", GetName()); + + if(!RemoveUInt64Value(PLAYER_FARSIGHT, target->GetGUID())) { - EnvironmentalDamage(GetGUID(),DAMAGE_FALL_TO_VOID, GetMaxHealth()); - // change the death state to CORPSE to prevent the death timer from - // starting in the next player update - KillPlayer(); - BuildPlayerRepop(); + sLog.outCrash("Player::CreateViewpoint: Player %s cannot remove current viewpoint!", GetName()); + return; } - // cancel the death timer here if started - RepopAtGraveyard(); + if(target->isType(TYPEMASK_UNIT)) + ((Unit*)target)->RemovePlayerFromVision(this); + + //must immediately set seer back otherwise may crash + m_seer = this; + + //WorldPacket data(SMSG_CLEAR_FAR_SIGHT_IMMEDIATE, 0); + //GetSession()->SendPacket(&data); } } -void Player::SetViewport(uint64 guid, bool moveable) +WorldObject* Player::GetViewpoint() const { - WorldPacket data(SMSG_CLIENT_CONTROL_UPDATE, 8+1); - data.appendPackGUID(guid); // Packed guid of object to set client's view to - data << (moveable ? uint8(0x01) : uint8(0x00)); // 0 - can't move; 1 - can move - m_session->SendPacket(&data); - sLog.outDetail("Viewport for "I64FMT" (%s) changed to "I64FMT, GetGUID(), GetName(), guid); + if(uint64 guid = GetUInt64Value(PLAYER_FARSIGHT)) + return (WorldObject*)ObjectAccessor::GetObjectByTypeMask(*this, guid, TYPEMASK_SEER); + return NULL; +} + +bool Player::CanUseBattleGroundObject() +{ + // TODO : some spells gives player ForceReaction to one faction (ReputationMgr::ApplyForceReaction) + // maybe gameobject code should handle that ForceReaction usage + // BUG: sometimes when player clicks on flag in AB - client won't send gameobject_use, only gameobject_report_use packet + return ( //InBattleGround() && // in battleground - not need, check in other cases + //!IsMounted() && - not correct, player is dismounted when he clicks on flag + //player cannot use object when he is invulnerable (immune) + !isTotalImmune() && // not totally immune + //i'm not sure if these two are correct, because invisible players should get visible when they click on flag + !HasStealthAura() && // not stealthed + !HasInvisibilityAura() && // not invisible + !HasAura(SPELL_RECENTLY_DROPPED_FLAG) && // can't pickup + isAlive() // live player + ); } -WorldObject* Player::GetFarsightTarget() const +bool Player::CanCaptureTowerPoint() { - // Players can have in farsight field another player's guid, a creature's guid, or a dynamic object's guid - if (uint64 guid = GetUInt64Value(PLAYER_FARSIGHT)) - return (WorldObject*)ObjectAccessor::GetObjectByTypeMask(*this, guid, TYPEMASK_PLAYER | TYPEMASK_UNIT | TYPEMASK_DYNAMICOBJECT); - return NULL; + return ( !HasStealthAura() && // not stealthed + !HasInvisibilityAura() && // not invisible + isAlive() // live player + ); } -void Player::StopCastingBindSight() +uint32 Player::GetBarberShopCost(uint8 newhairstyle, uint8 newhaircolor, uint8 newfacialhair) { - if(WorldObject* target = GetFarsightTarget()) - { - if(target->isType(TYPEMASK_UNIT)) - { - ((Unit*)target)->RemoveAuraTypeByCaster(SPELL_AURA_BIND_SIGHT, GetGUID()); - ((Unit*)target)->RemoveAuraTypeByCaster(SPELL_AURA_MOD_POSSESS, GetGUID()); - ((Unit*)target)->RemoveAuraTypeByCaster(SPELL_AURA_MOD_POSSESS_PET, GetGUID()); - } - } + uint32 level = getLevel(); + + if(level > GT_MAX_LEVEL) + level = GT_MAX_LEVEL; // max level in this dbc + + uint8 hairstyle = GetByteValue(PLAYER_BYTES, 2); + uint8 haircolor = GetByteValue(PLAYER_BYTES, 3); + uint8 facialhair = GetByteValue(PLAYER_BYTES_2, 0); + + if((hairstyle == newhairstyle) && (haircolor == newhaircolor) && (facialhair == newfacialhair)) + return 0; + + GtBarberShopCostBaseEntry const *bsc = sGtBarberShopCostBaseStore.LookupEntry(level - 1); + + if(!bsc) // shouldn't happen + return 0xFFFFFFFF; + + float cost = 0; + + if(hairstyle != newhairstyle) + cost += bsc->cost; // full price + + if((haircolor != newhaircolor) && (hairstyle == newhairstyle)) + cost += bsc->cost * 0.5f; // +1/2 of price + + if(facialhair != newfacialhair) + cost += bsc->cost * 0.75f; // +3/4 of price + + return uint32(cost); } -void Player::ClearFarsight() +void Player::InitGlyphsForLevel() { - if (GetUInt64Value(PLAYER_FARSIGHT)) - { - SetUInt64Value(PLAYER_FARSIGHT, 0); - WorldPacket data(SMSG_CLEAR_FAR_SIGHT_IMMEDIATE, 0); - GetSession()->SendPacket(&data); - } + for(uint32 i = 0; i < sGlyphSlotStore.GetNumRows(); ++i) + if(GlyphSlotEntry const * gs = sGlyphSlotStore.LookupEntry(i)) + if(gs->Order) + SetGlyphSlot(gs->Order - 1, gs->Id); + + uint32 level = getLevel(); + uint32 value = 0; + + // 0x3F = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 for 80 level + if(level >= 15) + value |= (0x01 | 0x02); + if(level >= 30) + value |= 0x08; + if(level >= 50) + value |= 0x04; + if(level >= 70) + value |= 0x10; + if(level >= 80) + value |= 0x20; + + SetUInt32Value(PLAYER_GLYPHS_ENABLED, value); } -void Player::SetFarsightTarget(WorldObject* obj) +void Player::EnterVehicle(Vehicle *vehicle) { - if (!obj || !obj->isType(TYPEMASK_PLAYER | TYPEMASK_UNIT | TYPEMASK_DYNAMICOBJECT)) + sLog.outDebug("Player %s enter vehicle entry %u id %u dbguid %u", GetName(), vehicle->GetEntry(), vehicle->GetVehicleId(), vehicle->GetDBTableGUIDLow()); + + if(vehicle->GetCharmerGUID()) return; - // Remove the current target if there is one + VehicleEntry const *ve = sVehicleStore.LookupEntry(vehicle->GetVehicleId()); + if(!ve) + return; + + VehicleSeatEntry const *veSeat = sVehicleSeatStore.LookupEntry(ve->m_seatID[0]); + if(!veSeat) + return; + + vehicle->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); + vehicle->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_24); + vehicle->setFaction(getFaction()); + + StopCastingCharm(); StopCastingBindSight(); - SetUInt64Value(PLAYER_FARSIGHT, obj->GetGUID()); + SetCharm(vehicle, true); + SetViewpoint(vehicle, true); + SetMover(vehicle); + SetClientControl(vehicle, 1); // redirect controls to vehicle + + addUnitState(UNIT_STAT_ONVEHICLE); + Relocate(vehicle->GetPositionX(), vehicle->GetPositionY(), vehicle->GetPositionZ(), vehicle->GetOrientation()); + AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT); + m_movementInfo.t_x = veSeat->m_attachmentOffsetX; + m_movementInfo.t_y = veSeat->m_attachmentOffsetY; + m_movementInfo.t_z = veSeat->m_attachmentOffsetZ; + m_movementInfo.t_o = 0; + m_movementInfo.t_time = getMSTime(); + m_movementInfo.t_seat = 0; + + WorldPacket data(SMSG_ON_CANCEL_EXPECTED_RIDE_VEHICLE_AURA, 0); + GetSession()->SendPacket(&data); + + BuildTeleportAckMsg(&data, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); + GetSession()->SendPacket(&data); + + BuildHeartBeatMsg(&data); + SendMessageToSet(&data, false); + + VehicleSpellInitialize(); } -bool Player::isAllowUseBattleGroundObject() +void Player::ExitVehicle(Vehicle *vehicle) { - return ( //InBattleGround() && // in battleground - not need, check in other cases - !IsMounted() && // not mounted - !isTotalImmunity() && // not totally immuned - !HasStealthAura() && // not stealthed - !HasInvisibilityAura() && // not invisible - !HasAura(SPELL_RECENTLY_DROPPED_FLAG, 0) && // can't pickup - isAlive() // live player - ); + vehicle->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); + vehicle->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_24); + vehicle->setFaction((GetTeam() == ALLIANCE) ? vehicle->GetCreatureInfo()->faction_A : vehicle->GetCreatureInfo()->faction_H); + + SetCharm(vehicle, false); + SetViewpoint(vehicle, false); + SetMover(this); + SetClientControl(vehicle, 0); + + clearUnitState(UNIT_STAT_ONVEHICLE); + Relocate(vehicle->GetPositionX(), vehicle->GetPositionY(), vehicle->GetPositionZ(), vehicle->GetOrientation()); + RemoveUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT); + m_movementInfo.t_x = 0; + m_movementInfo.t_y = 0; + m_movementInfo.t_z = 0; + m_movementInfo.t_o = 0; + m_movementInfo.t_time = 0; + m_movementInfo.t_seat = 0; + + WorldPacket data; + BuildTeleportAckMsg(&data, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); + GetSession()->SendPacket(&data); + + BuildHeartBeatMsg(&data); + SendMessageToSet(&data, false); + + data.Initialize(SMSG_PET_SPELLS, 8+4); + data << uint64(0); + data << uint32(0); + GetSession()->SendPacket(&data); + + // only for flyable vehicles? + //CastSpell(this, 45472, true); // Parachute + + //if(!vehicle->GetDBTableGUIDLow()) + if(vehicle->GetOwnerGUID() == GetGUID()) + vehicle->Dismiss(); +} + +bool Player::isTotalImmune() +{ + AuraEffectList const& immune = GetAurasByType(SPELL_AURA_SCHOOL_IMMUNITY); + + uint32 immuneMask = 0; + for(AuraEffectList::const_iterator itr = immune.begin(); itr != immune.end(); ++itr) + { + immuneMask |= (*itr)->GetMiscValue(); + if( immuneMask & SPELL_SCHOOL_MASK_ALL ) // total immunity + return true; + } + return false; } bool Player::HasTitle(uint32 bitIndex) @@ -19194,23 +19799,22 @@ void Player::SetTitle(CharTitlesEntry const* title) SetFlag(PLAYER__FIELD_KNOWN_TITLES+fieldIndexOffset, flag); } - /*-----------------------TRINITY--------------------------*/ bool Player::isTotalImmunity() { - AuraList const& immune = GetAurasByType(SPELL_AURA_SCHOOL_IMMUNITY); + AuraEffectList const& immune = GetAurasByType(SPELL_AURA_SCHOOL_IMMUNITY); - for(AuraList::const_iterator itr = immune.begin(); itr != immune.end(); ++itr) + for(AuraEffectList::const_iterator itr = immune.begin(); itr != immune.end(); ++itr) { - if (((*itr)->GetModifier()->m_miscvalue & SPELL_SCHOOL_MASK_ALL) !=0) // total immunity + if (((*itr)->GetMiscValue() & SPELL_SCHOOL_MASK_ALL) !=0) // total immunity { return true; } - if (((*itr)->GetModifier()->m_miscvalue & SPELL_SCHOOL_MASK_NORMAL) !=0) // physical damage immunity + if (((*itr)->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) !=0) // physical damage immunity { - for(AuraList::const_iterator i = immune.begin(); i != immune.end(); ++i) + for(AuraEffectList::const_iterator i = immune.begin(); i != immune.end(); ++i) { - if (((*i)->GetModifier()->m_miscvalue & SPELL_SCHOOL_MASK_MAGIC) !=0) // magic immunity + if (((*i)->GetMiscValue() & SPELL_SCHOOL_MASK_MAGIC) !=0) // magic immunity { return true; } @@ -19228,9 +19832,9 @@ void Player::UpdateCharmedAI() //kill self if charm aura has infinite duration if(charmer->IsInEvadeMode()) { - AuraList const& auras = GetAurasByType(SPELL_AURA_MOD_CHARM); - for(AuraList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter) - if((*iter)->GetCasterGUID() == charmer->GetGUID() && (*iter)->IsPermanent()) + AuraEffectList const& auras = GetAurasByType(SPELL_AURA_MOD_CHARM); + for(AuraEffectList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter) + if((*iter)->GetCasterGUID() == charmer->GetGUID() && (*iter)->GetParentAura()->IsPermanent()) { charmer->DealDamage(this, GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); return; @@ -19252,3 +19856,612 @@ void Player::UpdateCharmedAI() } } +void Player::ConvertRune(uint8 index, uint8 newType) +{ + SetCurrentRune(index, newType); + + WorldPacket data(SMSG_CONVERT_RUNE, 2); + data << uint8(index); + data << uint8(newType); + GetSession()->SendPacket(&data); +} + +void Player::ResyncRunes(uint8 count) +{ + WorldPacket data(SMSG_RESYNC_RUNES, count * 2); + for(uint32 i = 0; i < count; ++i) + { + data << uint8(GetCurrentRune(i)); // rune type + data << uint8(255 - (GetRuneCooldown(i) * 51)); // passed cooldown time (0-255) + } + GetSession()->SendPacket(&data); +} + +void Player::AddRunePower(uint8 index) +{ + WorldPacket data(SMSG_ADD_RUNE_POWER, 4); + data << uint32(1 << index); // mask (0x00-0x3F probably) + GetSession()->SendPacket(&data); +} + +void Player::InitRunes() +{ + if(getClass() != CLASS_DEATH_KNIGHT) + return; + + m_runes = new Runes; + + m_runes->runeState = 0; + + for(uint32 i = 0; i < MAX_RUNES; ++i) + { + SetBaseRune(i, i / 2); // init base types + SetCurrentRune(i, i / 2); // init current types + SetRuneCooldown(i, 0); // reset cooldowns + m_runes->SetRuneState(i); + } + + for(uint32 i = 0; i < NUM_RUNE_TYPES; ++i) + SetFloatValue(PLAYER_RUNE_REGEN_1 + i, 0.1f); +} + +void Player::AutoStoreLoot(uint8 bag, uint8 slot, uint32 loot_id, LootStore const& store, bool broadcast) +{ + Loot loot; + loot.FillLoot (loot_id,store,this,true); + + uint32 max_slot = loot.GetMaxSlotInLootFor(this); + for(uint32 i = 0; i < max_slot; ++i) + { + LootItem* lootItem = loot.LootItemInSlot(i,this); + + ItemPosCountVec dest; + uint8 msg = CanStoreNewItem (bag,slot,dest,lootItem->itemid,lootItem->count); + if(msg != EQUIP_ERR_OK && slot != NULL_SLOT) + msg = CanStoreNewItem( bag, NULL_SLOT,dest,lootItem->itemid,lootItem->count); + if( msg != EQUIP_ERR_OK && bag != NULL_BAG) + msg = CanStoreNewItem( NULL_BAG, NULL_SLOT,dest,lootItem->itemid,lootItem->count); + if(msg != EQUIP_ERR_OK) + { + SendEquipError( msg, NULL, NULL ); + continue; + } + + Item* pItem = StoreNewItem (dest,lootItem->itemid,true,lootItem->randomPropertyId); + SendNewItem(pItem, lootItem->count, false, false, broadcast); + } +} + +uint32 Player::CalculateTalentsPoints() const +{ + uint32 base_talent = getLevel() < 10 ? 0 : uint32((getLevel()-9)*sWorld.getRate(RATE_TALENT)); + + if(getClass() != CLASS_DEATH_KNIGHT) + return base_talent; + + uint32 talentPointsForLevel = + (getLevel() < 56 ? 0 : uint32((getLevel()-55)*sWorld.getRate(RATE_TALENT))) + + m_questRewardTalentCount; + + if(talentPointsForLevel > base_talent) + talentPointsForLevel = base_talent; + + return talentPointsForLevel; +} + +bool Player::IsAllowUseFlyMountsHere() const +{ + if (isGameMaster()) + return true; + + uint32 v_map = GetVirtualMapForMapAndZone(GetMapId(), GetZoneId()); + return v_map == 530 || v_map == 571 && HasSpell(54197); +} + +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)); + + // set fixed skill ranges + switch(GetSkillRangeType(pSkill,false)) + { + case SKILL_RANGE_LANGUAGE: // 300..300 + SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(300,300)); + break; + case SKILL_RANGE_MONO: // 1..1, grey monolite bar + SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(1,1)); + break; + default: + break; + } + + 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); + } +} + +uint32 Player::GetPhaseMaskForSpawn() const +{ + uint32 phase = PHASEMASK_NORMAL; + if(!isGameMaster()) + phase = GetPhaseMask(); + else + { + AuraEffectList const& phases = GetAurasByType(SPELL_AURA_PHASE); + if(!phases.empty()) + phase = phases.front()->GetMiscValue(); + } + + // some aura phases include 1 normal map in addition to phase itself + if(uint32 n_phase = phase & ~PHASEMASK_NORMAL) + return n_phase; + + return PHASEMASK_NORMAL; +} + +uint8 Player::CanEquipUniqueItem(Item* pItem, uint8 eslot, uint32 limit_count) const +{ + ItemPrototype const* pProto = pItem->GetProto(); + + // proto based limitations + if(uint8 res = CanEquipUniqueItem(pProto,eslot,limit_count)) + return res; + + // check unique-equipped on gems + for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot) + { + uint32 enchant_id = pItem->GetEnchantmentId(EnchantmentSlot(enchant_slot)); + if(!enchant_id) + continue; + SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id); + if(!enchantEntry) + continue; + + ItemPrototype const* pGem = objmgr.GetItemPrototype(enchantEntry->GemID); + if(!pGem) + continue; + + // include for check equip another gems with same limit category for not equipped item (and then not counted) + uint32 gem_limit_count = !pItem->IsEquipped() && pGem->ItemLimitCategory + ? pItem->GetGemCountWithLimitCategory(pGem->ItemLimitCategory) : 1; + + if(uint8 res = CanEquipUniqueItem(pGem, eslot,gem_limit_count)) + return res; + } + + return EQUIP_ERR_OK; +} + +uint8 Player::CanEquipUniqueItem( ItemPrototype const* itemProto, uint8 except_slot, uint32 limit_count) const +{ + // check unique-equipped on item + if (itemProto->Flags & ITEM_FLAGS_UNIQUE_EQUIPPED) + { + // there is an equip limit on this item + if(HasItemOrGemWithIdEquipped(itemProto->ItemId,1,except_slot)) + return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE; + } + + // check unique-equipped limit + if (itemProto->ItemLimitCategory) + { + ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(itemProto->ItemLimitCategory); + if(!limitEntry) + return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE; + + if(limit_count > limitEntry->maxCount) + return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE; // attempt add too many limit category items (gems) + + // there is an equip limit on this item + if(HasItemOrGemWithLimitCategoryEquipped(itemProto->ItemLimitCategory,limitEntry->maxCount-limit_count+1,except_slot)) + return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE; + } + + return EQUIP_ERR_OK; +} + +void Player::HandleFall(MovementInfo const& movementInfo) +{ + // calculate total z distance of the fall + float z_diff = m_lastFallZ - movementInfo.z; + //sLog.outDebug("zDiff = %f", z_diff); + + //Players with low fall distance, Feather Fall or physical immunity (charges used) are ignored + // 14.57 can be calculated by resolving damageperc formular below to 0 + if (z_diff >= 14.57f && !isDead() && !isGameMaster() && + !HasAuraType(SPELL_AURA_HOVER) && !HasAuraType(SPELL_AURA_FEATHER_FALL) && + !HasAuraType(SPELL_AURA_FLY) && !IsImmunedToDamage(SPELL_SCHOOL_MASK_NORMAL) ) + { + //Safe fall, fall height reduction + int32 safe_fall = GetTotalAuraModifier(SPELL_AURA_SAFE_FALL); + + float damageperc = 0.018f*(z_diff-safe_fall)-0.2426f; + + if(damageperc >0 ) + { + uint32 damage = (uint32)(damageperc * GetMaxHealth()*sWorld.getRate(RATE_DAMAGE_FALL)); + + float height = movementInfo.z; + UpdateGroundPositionZ(movementInfo.x,movementInfo.y,height); + + if (damage > 0) + { + //Prevent fall damage from being more than the player maximum health + if (damage > GetMaxHealth()) + damage = GetMaxHealth(); + + // Gust of Wind + if (GetDummyAura(43621)) + damage = GetMaxHealth()/2; + + EnvironmentalDamage(DAMAGE_FALL, damage); + + // recheck alive, might have died of EnvironmentalDamage + if (isAlive()) + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING, uint32(z_diff*100)); + } + + //Z given by moveinfo, LastZ, FallTime, WaterZ, MapZ, Damage, Safefall reduction + DEBUG_LOG("FALLDAMAGE z=%f sz=%f pZ=%f FallTime=%d mZ=%f damage=%d SF=%d" , movementInfo.z, height, GetPositionZ(), movementInfo.fallTime, height, damage, safe_fall); + } + } +} + +void Player::UpdateAchievementCriteria( AchievementCriteriaTypes type, uint32 miscvalue1/*=0*/, uint32 miscvalue2/*=0*/, Unit *unit/*=NULL*/, uint32 time/*=0*/ ) +{ + GetAchievementMgr().UpdateAchievementCriteria(type, miscvalue1,miscvalue2,unit,time); +} + +void Player::LearnTalent(uint32 talentId, uint32 talentRank) +{ + uint32 CurTalentPoints = GetFreeTalentPoints(); + + if(CurTalentPoints == 0) + return; + + if (talentRank >= MAX_TALENT_RANK) + return; + + TalentEntry const *talentInfo = sTalentStore.LookupEntry( talentId ); + + if(!talentInfo) + return; + + TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab ); + + if(!talentTabInfo) + return; + + // prevent learn talent for different class (cheating) + if( (getClassMask() & talentTabInfo->ClassMask) == 0 ) + return; + + // find current max talent rank + int32 curtalent_maxrank = 0; + for(int32 k = MAX_TALENT_RANK-1; k > -1; --k) + { + if(talentInfo->RankID[k] && HasSpell(talentInfo->RankID[k])) + { + curtalent_maxrank = k + 1; + break; + } + } + + // we already have same or higher talent rank learned + if(curtalent_maxrank >= (talentRank + 1)) + return; + + // check if we have enough talent points + if(CurTalentPoints < (talentRank - curtalent_maxrank + 1)) + return; + + // Check if it requires another talent + if (talentInfo->DependsOn > 0) + { + if(TalentEntry const *depTalentInfo = sTalentStore.LookupEntry(talentInfo->DependsOn)) + { + bool hasEnoughRank = false; + for (int i = talentInfo->DependsOnRank; i < MAX_TALENT_RANK; i++) + { + if (depTalentInfo->RankID[i] != 0) + if (HasSpell(depTalentInfo->RankID[i])) + hasEnoughRank = true; + } + if (!hasEnoughRank) + return; + } + } + + // Find out how many points we have in this field + uint32 spentPoints = 0; + + uint32 tTab = talentInfo->TalentTab; + if (talentInfo->Row > 0) + { + unsigned int numRows = sTalentStore.GetNumRows(); + for (unsigned int i = 0; i < numRows; i++) // Loop through all talents. + { + // Someday, someone needs to revamp + const TalentEntry *tmpTalent = sTalentStore.LookupEntry(i); + if (tmpTalent) // the way talents are tracked + { + if (tmpTalent->TalentTab == tTab) + { + for (int j = 0; j < MAX_TALENT_RANK; j++) + { + if (tmpTalent->RankID[j] != 0) + { + if (HasSpell(tmpTalent->RankID[j])) + { + spentPoints += j + 1; + } + } + } + } + } + } + } + + // not have required min points spent in talent tree + if(spentPoints < (talentInfo->Row * MAX_TALENT_RANK)) + return; + + // spell not set in talent.dbc + uint32 spellid = talentInfo->RankID[talentRank]; + if( spellid == 0 ) + { + sLog.outError("Talent.dbc have for talent: %u Rank: %u spell id = 0", talentId, talentRank); + return; + } + + // already known + if(HasSpell(spellid)) + return; + + // learn! (other talent ranks will unlearned at learning) + learnSpell(spellid, false); + sLog.outDetail("TalentID: %u Rank: %u Spell: %u\n", talentId, talentRank, spellid); + + // update free talent points + SetFreeTalentPoints(CurTalentPoints - (talentRank - curtalent_maxrank + 1)); +} + +void Player::LearnPetTalent(uint64 petGuid, uint32 talentId, uint32 talentRank) +{ + Pet *pet = GetPet(); + + if(!pet) + return; + + if(petGuid != pet->GetGUID()) + return; + + uint32 CurTalentPoints = pet->GetFreeTalentPoints(); + + if(CurTalentPoints == 0) + return; + + if (talentRank >= MAX_PET_TALENT_RANK) + return; + + TalentEntry const *talentInfo = sTalentStore.LookupEntry(talentId); + + if(!talentInfo) + return; + + TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab); + + if(!talentTabInfo) + return; + + CreatureInfo const *ci = pet->GetCreatureInfo(); + + if(!ci) + return; + + CreatureFamilyEntry const *pet_family = sCreatureFamilyStore.LookupEntry(ci->family); + + if(!pet_family) + return; + + if(pet_family->petTalentType < 0) // not hunter pet + return; + + // prevent learn talent for different family (cheating) + if(!((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask)) + return; + + // find current max talent rank + int32 curtalent_maxrank = 0; + for(int32 k = MAX_TALENT_RANK-1; k > -1; --k) + { + if(talentInfo->RankID[k] && pet->HasSpell(talentInfo->RankID[k])) + { + curtalent_maxrank = k + 1; + break; + } + } + + // we already have same or higher talent rank learned + if(curtalent_maxrank >= (talentRank + 1)) + return; + + // check if we have enough talent points + if(CurTalentPoints < (talentRank - curtalent_maxrank + 1)) + return; + + // Check if it requires another talent + if (talentInfo->DependsOn > 0) + { + if(TalentEntry const *depTalentInfo = sTalentStore.LookupEntry(talentInfo->DependsOn)) + { + bool hasEnoughRank = false; + for (int i = talentInfo->DependsOnRank; i < MAX_TALENT_RANK; i++) + { + if (depTalentInfo->RankID[i] != 0) + if (pet->HasSpell(depTalentInfo->RankID[i])) + hasEnoughRank = true; + } + if (!hasEnoughRank) + return; + } + } + + // Find out how many points we have in this field + uint32 spentPoints = 0; + + uint32 tTab = talentInfo->TalentTab; + if (talentInfo->Row > 0) + { + unsigned int numRows = sTalentStore.GetNumRows(); + for (unsigned int i = 0; i < numRows; ++i) // Loop through all talents. + { + // Someday, someone needs to revamp + const TalentEntry *tmpTalent = sTalentStore.LookupEntry(i); + if (tmpTalent) // the way talents are tracked + { + if (tmpTalent->TalentTab == tTab) + { + for (int j = 0; j < MAX_TALENT_RANK; j++) + { + if (tmpTalent->RankID[j] != 0) + { + if (pet->HasSpell(tmpTalent->RankID[j])) + { + spentPoints += j + 1; + } + } + } + } + } + } + } + + // not have required min points spent in talent tree + if(spentPoints < (talentInfo->Row * MAX_PET_TALENT_RANK)) + return; + + // spell not set in talent.dbc + uint32 spellid = talentInfo->RankID[talentRank]; + if( spellid == 0 ) + { + sLog.outError("Talent.dbc have for talent: %u Rank: %u spell id = 0", talentId, talentRank); + return; + } + + // already known + if(pet->HasSpell(spellid)) + return; + + // learn! (other talent ranks will unlearned at learning) + pet->learnSpell(spellid); + sLog.outDetail("PetTalentID: %u Rank: %u Spell: %u\n", talentId, talentRank, spellid); + + // update free talent points + pet->SetFreeTalentPoints(CurTalentPoints - (talentRank - curtalent_maxrank + 1)); +} + +void Player::UpdateKnownCurrencies(uint32 itemId, bool apply) +{ + if(CurrencyTypesEntry const* ctEntry = sCurrencyTypesStore.LookupEntry(itemId)) + { + if(apply) + SetFlag64(PLAYER_FIELD_KNOWN_CURRENCIES,(1LL << (ctEntry->BitIndex-1))); + else + RemoveFlag64(PLAYER_FIELD_KNOWN_CURRENCIES,(1LL << (ctEntry->BitIndex-1))); + } +} + +void Player::UpdateFallInformationIfNeed( MovementInfo const& minfo,uint16 opcode ) +{ + if (m_lastFallTime >= minfo.fallTime || m_lastFallZ <=minfo.z || opcode == MSG_MOVE_FALL_LAND) + SetFallInformation(minfo.fallTime, minfo.z); +} + +void Player::UnsummonPetTemporaryIfAny() +{ + Pet* pet = GetPet(); + if(!pet) + return; + + if(!m_temporaryUnsummonedPetNumber && pet->isControlled() && !pet->isTemporarySummoned() ) + { + m_temporaryUnsummonedPetNumber = pet->GetCharmInfo()->GetPetNumber(); + m_oldpetspell = pet->GetUInt32Value(UNIT_CREATED_BY_SPELL); + } + + RemovePet(pet, PET_SAVE_AS_CURRENT); +} + +void Player::ResummonPetTemporaryUnSummonedIfAny() +{ + if(!m_temporaryUnsummonedPetNumber) + return; + + // not resummon in not appropriate state + if(IsPetNeedBeTemporaryUnsummoned()) + return; + + if(GetPetGUID()) + return; + + Pet* NewPet = new Pet(this); + if(!NewPet->LoadPetFromDB(this, 0, m_temporaryUnsummonedPetNumber, true)) + delete NewPet; + + m_temporaryUnsummonedPetNumber = 0; +} diff --git a/src/game/Player.h b/src/game/Player.h index 9ba6c94ae24..60b2c7a0a1d 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,6 +35,9 @@ #include "Pet.h" #include "MapReference.h" #include "Util.h" // for Tokens typedef +#include "AchievementMgr.h" +#include "ReputationMgr.h" +#include "BattleGround.h" #include<string> #include<vector> @@ -47,8 +50,10 @@ class Pet; class PlayerMenu; class Transport; class UpdateMask; +class SpellCastTargets; class PlayerSocial; class OutdoorPvP; +class Vehicle; typedef std::deque<Mail*> PlayerMails; @@ -62,6 +67,18 @@ enum SpellModType SPELLMOD_PCT = 108 // SPELL_AURA_ADD_PCT_MODIFIER }; +// 2^n values, Player::m_isunderwater is a bitmask. These are mangos internal values, they are never send to any client +enum PlayerUnderwaterState +{ + UNDERWATER_NONE = 0x00, + UNDERWATER_INWATER = 0x01, // terrain type is water and player is afflicted by it + UNDERWATER_INLAVA = 0x02, // terrain type is lava and player is afflicted by it + UNDERWATER_INSLIME = 0x04, // terrain type is lava and player is afflicted by it + UNDERWARER_INDARKWATER = 0x08, // terrain type is dark water and player is afflicted by it + + UNDERWATER_EXIST_TIMERS = 0x10 +}; + enum PlayerSpellState { PLAYERSPELL_UNCHANGED = 0, @@ -72,27 +89,26 @@ enum PlayerSpellState struct PlayerSpell { - uint16 slotId : 16; 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 }; -#define SPELL_WITHOUT_SLOT_ID uint16(-1) - +// Spell modifier (used for modify other spells) struct SpellModifier { + SpellModifier() : charges(0), lastAffected(NULL) {} SpellModOp op : 8; SpellModType type : 8; int16 charges : 16; int32 value; - uint64 mask; + flag96 mask; uint32 spellId; - uint32 effectId; Spell const* lastAffected; }; -typedef UNORDERED_MAP<uint16, PlayerSpell*> PlayerSpellMap; +typedef UNORDERED_MAP<uint32, PlayerSpell*> PlayerSpellMap; typedef std::list<SpellModifier*> SpellModList; struct SpellCooldown @@ -141,8 +157,6 @@ enum ActionButtonType typedef std::map<uint8,ActionButton> ActionButtonList; -typedef std::pair<uint16, uint8> CreateSpellPair; - struct PlayerCreateInfoItem { PlayerCreateInfoItem(uint32 id, uint32 amount) : item_id(id), item_amount(amount) {} @@ -174,6 +188,8 @@ struct PlayerLevelInfo uint8 stats[MAX_STATS]; }; +typedef std::list<uint32> PlayerCreateInfoSpells; + struct PlayerInfo { // existence checked by displayId != 0 // existence checked by displayId != 0 @@ -189,7 +205,7 @@ struct PlayerInfo uint16 displayId_m; uint16 displayId_f; PlayerCreateInfoItems item; - std::list<CreateSpellPair> spell; + PlayerCreateInfoSpells spell; std::list<uint16> action[4]; PlayerLevelInfo* levelInfo; //[level-1] 0..MaxPlayerLevel-1 @@ -224,32 +240,38 @@ struct Areas float y2; }; -enum FactionFlags +#define MAX_RUNES 6 +#define RUNE_COOLDOWN 5 // 5*2=10 sec + +enum RuneType { - FACTION_FLAG_VISIBLE = 0x01, // makes visible in client (set or can be set at interaction with target of this faction) - FACTION_FLAG_AT_WAR = 0x02, // enable AtWar-button in client. player controlled (except opposition team always war state), Flag only set on initial creation - FACTION_FLAG_HIDDEN = 0x04, // hidden faction from reputation pane in client (player can gain reputation, but this update not sent to client) - FACTION_FLAG_INVISIBLE_FORCED = 0x08, // always overwrite FACTION_FLAG_VISIBLE and hide faction in rep.list, used for hide opposite team factions - FACTION_FLAG_PEACE_FORCED = 0x10, // always overwrite FACTION_FLAG_AT_WAR, used for prevent war with own team factions - FACTION_FLAG_INACTIVE = 0x20, // player controlled, state stored in characters.data ( CMSG_SET_FACTION_INACTIVE ) - FACTION_FLAG_RIVAL = 0x40 // flag for the two competing outland factions + RUNE_BLOOD = 0, + RUNE_UNHOLY = 1, + RUNE_FROST = 2, + RUNE_DEATH = 3, + NUM_RUNE_TYPES = 4 }; -typedef uint32 RepListID; -struct FactionState +struct RuneInfo { - uint32 ID; - RepListID ReputationListID; - uint32 Flags; - int32 Standing; - bool Changed; + uint8 BaseRune; + uint8 CurrentRune; + uint8 Cooldown; }; -typedef std::map<RepListID,FactionState> FactionStateList; - -typedef std::map<uint32,ReputationRank> ForcedReactions; +struct Runes +{ + RuneInfo runes[MAX_RUNES]; + uint8 runeState; // mask of available runes -typedef std::set<uint64> GuardianPetList; + void SetRuneState(uint8 index, bool set = true) + { + if(set) + runeState |= (1 << index); // usable + else + runeState &= ~(1 << index); // on cooldown + } +}; struct EnchantDuration { @@ -328,52 +350,6 @@ enum DrunkenState DRUNKEN_SMASHED = 3 }; -enum PlayerStateType -{ - /* - PLAYER_STATE_DANCE - PLAYER_STATE_SLEEP - PLAYER_STATE_SIT - PLAYER_STATE_STAND - PLAYER_STATE_READYUNARMED - PLAYER_STATE_WORK - PLAYER_STATE_POINT(DNR) - PLAYER_STATE_NONE // not used or just no state, just standing there? - PLAYER_STATE_STUN - PLAYER_STATE_DEAD - PLAYER_STATE_KNEEL - PLAYER_STATE_USESTANDING - PLAYER_STATE_STUN_NOSHEATHE - PLAYER_STATE_USESTANDING_NOSHEATHE - PLAYER_STATE_WORK_NOSHEATHE - PLAYER_STATE_SPELLPRECAST - PLAYER_STATE_READYRIFLE - PLAYER_STATE_WORK_NOSHEATHE_MINING - PLAYER_STATE_WORK_NOSHEATHE_CHOPWOOD - PLAYER_STATE_AT_EASE - PLAYER_STATE_READY1H - PLAYER_STATE_SPELLKNEELSTART - PLAYER_STATE_SUBMERGED - */ - - PLAYER_STATE_NONE = 0, - PLAYER_STATE_SIT = 1, - PLAYER_STATE_SIT_CHAIR = 2, - PLAYER_STATE_SLEEP = 3, - PLAYER_STATE_SIT_LOW_CHAIR = 4, - PLAYER_STATE_SIT_MEDIUM_CHAIR = 5, - PLAYER_STATE_SIT_HIGH_CHAIR = 6, - PLAYER_STATE_DEAD = 7, - PLAYER_STATE_KNEEL = 8, - - PLAYER_STATE_FORM_ALL = 0x00FF0000, - - PLAYER_STATE_FLAG_ALWAYS_STAND = 0x01, // byte 4 - PLAYER_STATE_FLAG_CREEP = 0x02000000, - PLAYER_STATE_FLAG_UNTRACKABLE = 0x04000000, - PLAYER_STATE_FLAG_ALL = 0xFF000000, -}; - enum PlayerFlags { PLAYER_FLAGS_GROUP_LEADER = 0x00000001, @@ -382,17 +358,25 @@ enum PlayerFlags PLAYER_FLAGS_GM = 0x00000008, PLAYER_FLAGS_GHOST = 0x00000010, PLAYER_FLAGS_RESTING = 0x00000020, - PLAYER_FLAGS_FFA_PVP = 0x00000080, + PLAYER_FLAGS_UNK7 = 0x00000040, + PLAYER_FLAGS_UNK8 = 0x00000080, // pre-3.0.3 PLAYER_FLAGS_FFA_PVP flag for FFA PVP state PLAYER_FLAGS_CONTESTED_PVP = 0x00000100, // Player has been involved in a PvP combat and will be attacked by contested guards PLAYER_FLAGS_IN_PVP = 0x00000200, PLAYER_FLAGS_HIDE_HELM = 0x00000400, PLAYER_FLAGS_HIDE_CLOAK = 0x00000800, - PLAYER_FLAGS_UNK1 = 0x00001000, // played long time - PLAYER_FLAGS_UNK2 = 0x00002000, // played too long time - PLAYER_FLAGS_UNK3 = 0x00008000, // strange visual effect (2.0.1), looks like PLAYER_FLAGS_GHOST flag - PLAYER_FLAGS_SANCTUARY = 0x00010000, // player entered sanctuary - PLAYER_FLAGS_UNK4 = 0x00020000, // taxi benchmark mode (on/off) (2.0.1) - PLAYER_UNK = 0x00040000, // 2.0.8... + PLAYER_FLAGS_UNK13 = 0x00001000, // played long time + PLAYER_FLAGS_UNK14 = 0x00002000, // played too long time + PLAYER_FLAGS_UNK15 = 0x00004000, + PLAYER_FLAGS_UNK16 = 0x00008000, // strange visual effect (2.0.1), looks like PLAYER_FLAGS_GHOST flag + PLAYER_FLAGS_UNK17 = 0x00010000, // pre-3.0.3 PLAYER_FLAGS_SANCTUARY flag for player entered sanctuary + PLAYER_FLAGS_UNK18 = 0x00020000, // taxi benchmark mode (on/off) (2.0.1) + PLAYER_FLAGS_PVP_TIMER = 0x00040000, // 3.0.2, pvp timer active (after you disable pvp manually) + PLAYER_FLAGS_UNK20 = 0x00080000, + PLAYER_FLAGS_UNK21 = 0x00100000, + PLAYER_FLAGS_UNK22 = 0x00200000, + PLAYER_FLAGS_UNK23 = 0x00400000, + PLAYER_ALLOW_ONLY_ABILITY = 0x00800000, // used by bladestorm and killing spree + PLAYER_FLAGS_UNK25 = 0x01000000 // disabled all melee ability on tab include autoattack }; // used for PLAYER__FIELD_KNOWN_TITLES field (uint64), (1<<bit_index) without (-1) @@ -481,7 +465,8 @@ enum LootType LOOT_DISENCHANTING = 5, // unsupported by client, sending LOOT_SKINNING instead LOOT_PROSPECTING = 6, // unsupported by client, sending LOOT_SKINNING instead LOOT_INSIGNIA = 7, // unsupported by client, sending LOOT_SKINNING instead - LOOT_FISHINGHOLE = 8 // unsupported by client, sending LOOT_FISHING instead + LOOT_FISHINGHOLE = 8, // unsupported by client, sending LOOT_FISHING instead + LOOT_MILLING = 9 // unsupported by client, sending LOOT_SKINNING instead }; enum MirrorTimerType @@ -490,6 +475,8 @@ enum MirrorTimerType BREATH_TIMER = 1, FIRE_TIMER = 2 }; +#define MAX_TIMERS 3 +#define DISABLED_MIRROR_TIMER -1 // 2^n values enum PlayerExtraFlags @@ -511,7 +498,8 @@ enum AtLoginFlags AT_LOGIN_NONE = 0, AT_LOGIN_RENAME = 1, AT_LOGIN_RESET_SPELLS = 2, - AT_LOGIN_RESET_TALENTS = 4 + AT_LOGIN_RESET_TALENTS = 4, + AT_LOGIN_CUSTOMIZE = 8 }; typedef std::map<uint32, QuestStatusData> QuestStatusMap; @@ -543,11 +531,13 @@ enum PlayerSlots // first slot for item stored (in any way in player m_items data) PLAYER_SLOT_START = 0, // last+1 slot for item stored (in any way in player m_items data) - PLAYER_SLOT_END = 118, + PLAYER_SLOT_END = 200, PLAYER_SLOTS_COUNT = (PLAYER_SLOT_END - PLAYER_SLOT_START) }; -enum EquipmentSlots +#define INVENTORY_SLOT_BAG_0 255 + +enum EquipmentSlots // 19 slots { EQUIPMENT_SLOT_START = 0, EQUIPMENT_SLOT_HEAD = 0, @@ -572,111 +562,67 @@ enum EquipmentSlots EQUIPMENT_SLOT_END = 19 }; -enum InventorySlots +enum InventorySlots // 4 slots { - INVENTORY_SLOT_BAG_0 = 255, INVENTORY_SLOT_BAG_START = 19, - INVENTORY_SLOT_BAG_1 = 19, - INVENTORY_SLOT_BAG_2 = 20, - INVENTORY_SLOT_BAG_3 = 21, - INVENTORY_SLOT_BAG_4 = 22, - INVENTORY_SLOT_BAG_END = 23, + INVENTORY_SLOT_BAG_END = 23 +}; +enum InventoryPackSlots // 16 slots +{ INVENTORY_SLOT_ITEM_START = 23, - INVENTORY_SLOT_ITEM_1 = 23, - INVENTORY_SLOT_ITEM_2 = 24, - INVENTORY_SLOT_ITEM_3 = 25, - INVENTORY_SLOT_ITEM_4 = 26, - INVENTORY_SLOT_ITEM_5 = 27, - INVENTORY_SLOT_ITEM_6 = 28, - INVENTORY_SLOT_ITEM_7 = 29, - INVENTORY_SLOT_ITEM_8 = 30, - INVENTORY_SLOT_ITEM_9 = 31, - INVENTORY_SLOT_ITEM_10 = 32, - INVENTORY_SLOT_ITEM_11 = 33, - INVENTORY_SLOT_ITEM_12 = 34, - INVENTORY_SLOT_ITEM_13 = 35, - INVENTORY_SLOT_ITEM_14 = 36, - INVENTORY_SLOT_ITEM_15 = 37, - INVENTORY_SLOT_ITEM_16 = 38, INVENTORY_SLOT_ITEM_END = 39 }; -enum BankSlots +enum BankItemSlots // 28 slots { BANK_SLOT_ITEM_START = 39, - BANK_SLOT_ITEM_1 = 39, - BANK_SLOT_ITEM_2 = 40, - BANK_SLOT_ITEM_3 = 41, - BANK_SLOT_ITEM_4 = 42, - BANK_SLOT_ITEM_5 = 43, - BANK_SLOT_ITEM_6 = 44, - BANK_SLOT_ITEM_7 = 45, - BANK_SLOT_ITEM_8 = 46, - BANK_SLOT_ITEM_9 = 47, - BANK_SLOT_ITEM_10 = 48, - BANK_SLOT_ITEM_11 = 49, - BANK_SLOT_ITEM_12 = 50, - BANK_SLOT_ITEM_13 = 51, - BANK_SLOT_ITEM_14 = 52, - BANK_SLOT_ITEM_15 = 53, - BANK_SLOT_ITEM_16 = 54, - BANK_SLOT_ITEM_17 = 55, - BANK_SLOT_ITEM_18 = 56, - BANK_SLOT_ITEM_19 = 57, - BANK_SLOT_ITEM_20 = 58, - BANK_SLOT_ITEM_21 = 59, - BANK_SLOT_ITEM_22 = 60, - BANK_SLOT_ITEM_23 = 61, - BANK_SLOT_ITEM_24 = 62, - BANK_SLOT_ITEM_25 = 63, - BANK_SLOT_ITEM_26 = 64, - BANK_SLOT_ITEM_27 = 65, - BANK_SLOT_ITEM_28 = 66, - BANK_SLOT_ITEM_END = 67, + BANK_SLOT_ITEM_END = 67 +}; +enum BankBagSlots // 7 slots +{ BANK_SLOT_BAG_START = 67, - BANK_SLOT_BAG_1 = 67, - BANK_SLOT_BAG_2 = 68, - BANK_SLOT_BAG_3 = 69, - BANK_SLOT_BAG_4 = 70, - BANK_SLOT_BAG_5 = 71, - BANK_SLOT_BAG_6 = 72, - BANK_SLOT_BAG_7 = 73, BANK_SLOT_BAG_END = 74 }; -enum BuyBackSlots +enum BuyBackSlots // 12 slots { // stored in m_buybackitems BUYBACK_SLOT_START = 74, - BUYBACK_SLOT_1 = 74, - BUYBACK_SLOT_2 = 75, - BUYBACK_SLOT_3 = 76, - BUYBACK_SLOT_4 = 77, - BUYBACK_SLOT_5 = 78, - BUYBACK_SLOT_6 = 79, - BUYBACK_SLOT_7 = 80, - BUYBACK_SLOT_8 = 81, - BUYBACK_SLOT_9 = 82, - BUYBACK_SLOT_10 = 83, - BUYBACK_SLOT_11 = 84, - BUYBACK_SLOT_12 = 85, BUYBACK_SLOT_END = 86 }; -enum KeyRingSlots +enum KeyRingSlots // 32 slots { KEYRING_SLOT_START = 86, KEYRING_SLOT_END = 118 }; +enum VanityPetSlots // 18 slots +{ + VANITYPET_SLOT_START = 118, // not use, vanity pets stored as spells + VANITYPET_SLOT_END = 136 // not allowed any content in. +}; + +enum CurrencyTokenSlots // 32 slots +{ + CURRENCYTOKEN_SLOT_START = 136, + CURRENCYTOKEN_SLOT_END = 168 +}; + +enum QuestBagSlots // 32 slots +{ + QUESTBAG_SLOT_START = 168, // not use + QUESTBAG_SLOT_END = 200 // not allowed any content in. +}; + struct ItemPosCount { - ItemPosCount(uint16 _pos, uint8 _count) : pos(_pos), count(_count) {} + ItemPosCount(uint16 _pos, uint32 _count) : pos(_pos), count(_count) {} bool isContainedIn(std::vector<ItemPosCount> const& vec) const; uint16 pos; - uint8 count; + uint32 count; }; typedef std::vector<ItemPosCount> ItemPosCountVec; @@ -689,14 +635,15 @@ enum TradeSlots enum TransferAbortReason { - TRANSFER_ABORT_MAX_PLAYERS = 0x0001, // Transfer Aborted: instance is full - TRANSFER_ABORT_NOT_FOUND = 0x0002, // Transfer Aborted: instance not found - TRANSFER_ABORT_TOO_MANY_INSTANCES = 0x0003, // You have entered too many instances recently. - TRANSFER_ABORT_ZONE_IN_COMBAT = 0x0005, // Unable to zone in while an encounter is in progress. - TRANSFER_ABORT_INSUF_EXPAN_LVL1 = 0x0106, // You must have TBC expansion installed to access this area. - TRANSFER_ABORT_DIFFICULTY1 = 0x0007, // Normal difficulty mode is not available for %s. - TRANSFER_ABORT_DIFFICULTY2 = 0x0107, // Heroic difficulty mode is not available for %s. - TRANSFER_ABORT_DIFFICULTY3 = 0x0207 // Epic difficulty mode is not available for %s. + TRANSFER_ABORT_ERROR = 0x00, + TRANSFER_ABORT_MAX_PLAYERS = 0x01, // Transfer Aborted: instance is full + TRANSFER_ABORT_NOT_FOUND = 0x02, // Transfer Aborted: instance not found + TRANSFER_ABORT_TOO_MANY_INSTANCES = 0x03, // You have entered too many instances recently. + TRANSFER_ABORT_ZONE_IN_COMBAT = 0x05, // Unable to zone in while an encounter is in progress. + TRANSFER_ABORT_INSUF_EXPAN_LVL = 0x06, // You must have <TBC,WotLK> expansion installed to access this area. + TRANSFER_ABORT_DIFFICULTY = 0x07, // <Normal,Heroic,Epic> difficulty mode is not available for %s. + TRANSFER_ABORT_UNIQUE_MESSAGE = 0x08, // Until you've escaped TLK's grasp, you cannot leave this place! + TRANSFER_ABORT_TOO_MANY_REALM_INSTANCES = 0x09 // Additional instances cannot be launched, please try again later. }; enum InstanceResetWarningType @@ -710,15 +657,16 @@ enum InstanceResetWarningType struct MovementInfo { // common - //uint32 flags; - uint8 unk1; + uint32 flags; + uint16 unk1; uint32 time; float x, y, z, o; // transport uint64 t_guid; float t_x, t_y, t_z, t_o; uint32 t_time; - // swimming and unk + int8 t_seat; + // swimming and unknown float s_pitch; // last fall time uint32 fallTime; @@ -729,17 +677,12 @@ struct MovementInfo MovementInfo() { - //flags = + flags = 0; time = t_time = fallTime = 0; unk1 = 0; x = y = z = o = t_x = t_y = t_z = t_o = s_pitch = j_unk = j_sinAngle = j_cosAngle = j_xyspeed = u_unk1 = 0.0f; t_guid = 0; } - - /*void SetMovementFlags(uint32 _flags) - { - flags = _flags; - }*/ }; // flags that use in movement check for example at spell casting @@ -812,8 +755,9 @@ enum PlayerLoginQueryIndex PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES = 16, PLAYER_LOGIN_QUERY_LOADGUILD = 17, PLAYER_LOGIN_QUERY_LOADARENAINFO = 18, - - MAX_PLAYER_LOGIN_QUERY + PLAYER_LOGIN_QUERY_LOADACHIEVEMENTS = 19, + PLAYER_LOGIN_QUERY_LOADCRITERIAPROGRESS = 20, + MAX_PLAYER_LOGIN_QUERY = 21 }; // Player summoning auto-decline time (in secs) @@ -840,9 +784,9 @@ struct AccessRequirement uint32 heroicKey2; uint32 quest; std::string questFailedText; - uint32 heroicQuest; + uint32 heroicQuest; std::string heroicQuestFailedText; - }; +}; class TRINITY_DLL_SPEC PlayerTaxi { @@ -850,11 +794,9 @@ class TRINITY_DLL_SPEC PlayerTaxi PlayerTaxi(); ~PlayerTaxi() {} // Nodes - void InitTaxiNodesForLevel(uint32 race, uint32 level); + void InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint32 level); void LoadTaxiMask(const char* data); - void SaveTaxiMask(const char* data); - uint32 GetTaximask( uint8 index ) const { return m_taximask[index]; } bool IsTaximaskNodeKnown(uint32 nodeidx) const { uint8 field = uint8((nodeidx - 1) / 32); @@ -876,7 +818,7 @@ class TRINITY_DLL_SPEC PlayerTaxi void AppendTaximaskTo(ByteBuffer& data,bool all); // Destinations - bool LoadTaxiDestinationsFromString(const std::string& values); + bool LoadTaxiDestinationsFromString(const std::string& values, uint32 team); std::string SaveTaxiDestinationsToString(); void ClearTaxiDestinations() { m_TaxiDestinations.clear(); } @@ -890,11 +832,15 @@ class TRINITY_DLL_SPEC PlayerTaxi return GetTaxiDestination(); } bool empty() const { return m_TaxiDestinations.empty(); } + + friend std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi); private: TaxiMask m_taximask; std::deque<uint32> m_TaxiDestinations; }; +std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi); + class TRINITY_DLL_SPEC Player : public Unit { friend class WorldSession; @@ -912,16 +858,6 @@ class TRINITY_DLL_SPEC Player : public Unit void AddToWorld(); void RemoveFromWorld(); - void SetViewport(uint64 guid, bool movable); - void StopCastingCharm() { Uncharm(); } - void StopCastingBindSight(); - WorldObject* GetFarsightTarget() const; - void ClearFarsight(); - void SetFarsightTarget(WorldObject* target); - // Controls if vision is currently on farsight object, updated in FAR_SIGHT opcode - void SetFarsightVision(bool apply) { m_farsightVision = apply; } - bool HasFarsightVision() const { return m_farsightVision; } - bool TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options = 0); bool TeleportTo(WorldLocation const &loc, uint32 options = 0) @@ -952,7 +888,7 @@ class TRINITY_DLL_SPEC Player : public Unit void SendInitialPacketsBeforeAddToMap(); void SendInitialPacketsAfterAddToMap(); - void SendTransferAborted(uint32 mapid, uint16 reason); + void SendTransferAborted(uint32 mapid, uint8 reason, uint8 arg = 0); void SendInstanceResetWarning(uint32 mapid, uint32 time); bool CanInteractWithNPCs(bool alive = true) const; @@ -965,10 +901,12 @@ class TRINITY_DLL_SPEC Player : public Unit std::string afkMsg; std::string dndMsg; + uint32 GetBarberShopCost(uint8 newhairstyle, uint8 newhaircolor, uint8 newfacialhair); + PlayerSocial *GetSocial() { return m_social; } PlayerTaxi m_taxi; - void InitTaxiNodesForLevel() { m_taxi.InitTaxiNodesForLevel(getRace(),getLevel()); } + void InitTaxiNodesForLevel() { m_taxi.InitTaxiNodesForLevel(getRace(), getClass(), getLevel()); } bool ActivateTaxiPathTo(std::vector<uint32> const& nodes, uint32 mount_id = 0 , Creature* npc = NULL); // mount_id can be used in scripting calls bool isAcceptWhispers() const { return m_ExtraFlags & PLAYER_EXTRA_ACCEPT_WHISPERS; } @@ -1019,16 +957,10 @@ class TRINITY_DLL_SPEC Player : public Unit int GetTimeInnEnter() const { return time_inn_enter; }; void UpdateInnerTime (int time) { time_inn_enter = time; }; + Pet* GetPet() const; Pet* SummonPet(uint32 entry, float x, float y, float z, float ang, PetType petType, uint32 despwtime); void RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent = false); - void RemoveMiniPet(); - Pet* GetMiniPet(); - void SetMiniPet(Pet* pet) { m_miniPet = pet->GetGUID(); } - void RemoveGuardians(); - bool HasGuardianWithEntry(uint32 entry); - void AddGuardian(Pet* pet) { m_guardianPets.insert(pet->GetGUID()); } - GuardianPetList const& GetGuardians() const { return m_guardianPets; } - void Uncharm(); + uint32 GetPhaseMaskForSpawn() const; // used for proper set phase for DB at GM-mode creature/GO spawn void Say(const std::string& text, const uint32 language); void Yell(const std::string& text, const uint32 language); @@ -1047,9 +979,15 @@ class TRINITY_DLL_SPEC Player : public Unit Item* GetItemByGuid( uint64 guid ) const; Item* GetItemByPos( uint16 pos ) const; Item* GetItemByPos( uint8 bag, uint8 slot ) const; + inline Item* GetUseableItemByPos( uint8 bag, uint8 slot ) const //Does additional check for disarmed weapons + { + if (!CanUseAttackType(GetAttackBySlot(slot))) + return NULL; + return GetItemByPos(bag, slot); + } Item* GetWeaponForAttack(WeaponAttackType attackType, bool useable = false) const; Item* GetShield(bool useable = false) const; - static uint32 GetAttackBySlot( uint8 slot ); // MAX_ATTACK if not weapon slot + static uint8 GetAttackBySlot( uint8 slot ); // MAX_ATTACK if not weapon slot std::vector<Item *> &GetItemUpdateQueue() { return m_itemUpdateQueue; } static bool IsInventoryPos( uint16 pos ) { return IsInventoryPos(pos >> 8,pos & 255); } static bool IsInventoryPos( uint8 bag, uint8 slot ); @@ -1063,7 +1001,9 @@ class TRINITY_DLL_SPEC Player : public Unit bool HasBankBagSlot( uint8 slot ) const; bool HasItemCount( uint32 item, uint32 count, bool inBankAlso = false ) const; bool HasItemFitToSpellReqirements(SpellEntry const* spellInfo, Item const* ignoreItem = NULL); - Item* GetItemOrItemWithGemEquipped( uint32 item ) const; + bool CanNoReagentCast(SpellEntry const* spellInfo) const; + bool HasItemOrGemWithIdEquipped( uint32 item, uint32 count, uint8 except_slot = NULL_SLOT) const; + bool HasItemOrGemWithLimitCategoryEquipped( uint32 limitCategory, uint32 count, uint8 except_slot = NULL_SLOT) const; uint8 CanTakeMoreSimilarItems(Item* pItem) const { return _CanTakeMoreSimilarItems(pItem->GetEntry(),pItem->GetCount(),pItem); } uint8 CanTakeMoreSimilarItems(uint32 entry, uint32 count) const { return _CanTakeMoreSimilarItems(entry,count,NULL); } uint8 CanStoreNewItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 item, uint32 count, uint32* no_space_count = NULL ) const @@ -1081,6 +1021,9 @@ class TRINITY_DLL_SPEC Player : public Unit uint8 CanStoreItems( Item **pItem,int count) const; uint8 CanEquipNewItem( uint8 slot, uint16 &dest, uint32 item, bool swap ) const; uint8 CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bool not_loading = true ) const; + + uint8 CanEquipUniqueItem( Item * pItem, uint8 except_slot = NULL_SLOT, uint32 limit_count = 1 ) const; + uint8 CanEquipUniqueItem( ItemPrototype const* itemProto, uint8 except_slot = NULL_SLOT, uint32 limit_count = 1 ) const; uint8 CanUnequipItems( uint32 item, uint32 count ) const; uint8 CanUnequipItem( uint16 src, bool swap ) const; uint8 CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, Item *pItem, bool swap, bool not_loading = true ) const; @@ -1094,6 +1037,8 @@ class TRINITY_DLL_SPEC Player : public Unit Item* EquipItem( uint16 pos, Item *pItem, bool update ); void AutoUnequipOffhandIfNeed(); bool StoreNewItemInBestSlots(uint32 item_id, uint32 item_count); + void AutoStoreLoot(uint8 bag, uint8 slot, uint32 loot_id, LootStore const& store, bool broadcast = false); + void AutoStoreLoot(uint32 loot_id, LootStore const& store, bool broadcast = false) { AutoStoreLoot(NULL_BAG,NULL_SLOT,loot_id,store,broadcast); } uint8 _CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count = NULL) const; uint8 _CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 entry, uint32 count, Item *pItem = NULL, bool swap = false, uint32* no_space_count = NULL ) const; @@ -1135,12 +1080,16 @@ class TRINITY_DLL_SPEC Player : public Unit void AddArmorProficiency(uint32 newflag) { m_ArmorProficiency |= newflag; } uint32 GetWeaponProficiency() const { return m_WeaponProficiency; } uint32 GetArmorProficiency() const { return m_ArmorProficiency; } - bool IsInFeralForm() const { return m_form == FORM_CAT || m_form == FORM_BEAR || m_form == FORM_DIREBEAR; } bool IsUseEquipedWeapon( bool mainhand ) const { // disarm applied only to mainhand weapon return !IsInFeralForm() && (!mainhand || !HasFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_DISARMED) ); } + bool IsTwoHandUsed() const + { + Item* mainItem = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); + 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); @@ -1171,6 +1120,8 @@ class TRINITY_DLL_SPEC Player : public Unit /*** QUEST SYSTEM ***/ /*********************************************************/ + uint32 GetQuestLevel( Quest const* pQuest ) const { return pQuest && pQuest->GetQuestLevel() ? pQuest->GetQuestLevel() : getLevel(); } + void PrepareQuestMenu( uint64 guid ); void SendPreparedQuest( uint64 guid ); bool IsActiveQuest( uint32 quest_id ) const; @@ -1238,7 +1189,6 @@ class TRINITY_DLL_SPEC Player : public Unit } } uint32 GetReqKillOrCastCurrentCount(uint32 quest_id, int32 entry); - void AdjustQuestReqItemCount( Quest const* pQuest ); void AreaExploredOrEventHappens( uint32 questId ); void GroupEventHappens( uint32 questId, WorldObject const* pEventObject ); void ItemAddedQuestCheck( uint32 entry, uint32 count ); @@ -1247,8 +1197,9 @@ class TRINITY_DLL_SPEC Player : public Unit void CastedCreatureOrGO( uint32 entry, uint64 guid, uint32 spell_id ); void TalkedToCreature( uint32 entry, uint64 guid ); void MoneyChanged( uint32 value ); + void ReputationChanged(FactionEntry const* factionEntry ); bool HasQuestForItem( uint32 itemid ) const; - bool HasQuestForGO(int32 GOId); + bool HasQuestForGO(int32 GOId) const; void UpdateForQuestsGO(); bool CanShareQuest(uint32 quest_id) const; @@ -1275,6 +1226,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); @@ -1296,6 +1248,7 @@ class TRINITY_DLL_SPEC Player : public Unit static void SetFloatValueInArray(Tokens& data,uint16 index, float value); static void SetUInt32ValueInDB(uint16 index, uint32 value, uint64 guid); static void SetFloatValueInDB(uint16 index, float value, uint64 guid); + static void Customize(uint64 guid, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair); static void SavePositionInDB(uint32 mapid, float x,float y,float z,float o,uint32 zone,uint64 guid); bool m_mailsLoaded; @@ -1352,7 +1305,7 @@ class TRINITY_DLL_SPEC Player : public Unit void SetSelection(const uint64 &guid) { m_curSelection = guid; SetUInt64Value(UNIT_FIELD_TARGET, guid); } uint8 GetComboPoints() { return m_comboPoints; } - uint64 GetComboTarget() { return m_comboTarget; } + const uint64& GetComboTarget() const { return m_comboTarget; } void AddComboPoints(Unit* target, int8 count); void ClearComboPoints(); @@ -1388,10 +1341,7 @@ class TRINITY_DLL_SPEC Player : public Unit Item* GetMItem(uint32 id) { ItemMap::const_iterator itr = mMitems.find(id); - if (itr != mMitems.end()) - return itr->second; - - return NULL; + return itr != mMitems.end() ? itr->second : NULL; } void AddMItem(Item* it) @@ -1403,30 +1353,29 @@ class TRINITY_DLL_SPEC Player : public Unit bool RemoveMItem(uint32 id) { - ItemMap::iterator i = mMitems.find(id); - if (i == mMitems.end()) - return false; - - mMitems.erase(i); - return true; + return mMitems.erase(id) ? true : false; } void PetSpellInitialize(); void CharmSpellInitialize(); void PossessSpellInitialize(); + void VehicleSpellInitialize(); 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 = true, bool loading = false, uint16 slot_id=SPELL_WITHOUT_SLOT_ID, bool disabled = false); - 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(bool loading = false); + 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); } @@ -1434,6 +1383,17 @@ class TRINITY_DLL_SPEC Player : public Unit uint32 resetTalentsCost() const; void InitTalentForLevel(); + void LearnTalent(uint32 talentId, uint32 talentRank); + void LearnPetTalent(uint64 petGuid, uint32 talentId, uint32 talentRank); + + uint32 CalculateTalentsPoints() const; + + void InitGlyphsForLevel(); + void SetGlyphSlot(uint8 slot, uint32 slottype) { SetUInt32Value(PLAYER_FIELD_GLYPH_SLOTS_1 + slot, slottype); } + uint32 GetGlyphSlot(uint8 slot) { return GetUInt32Value(PLAYER_FIELD_GLYPH_SLOTS_1 + slot); } + void SetGlyph(uint8 slot, uint32 glyph) { SetUInt32Value(PLAYER_FIELD_GLYPHS_1 + slot, glyph); } + uint32 GetGlyph(uint8 slot) { return GetUInt32Value(PLAYER_FIELD_GLYPHS_1 + slot); } + uint32 GetFreePrimaryProffesionPoints() const { return GetUInt32Value(PLAYER_CHARACTER_POINTS2); } void SetFreePrimaryProffesions(uint16 profs) { SetUInt32Value(PLAYER_CHARACTER_POINTS2,profs); } void InitPrimaryProffesions(); @@ -1442,8 +1402,6 @@ class TRINITY_DLL_SPEC Player : public Unit PlayerSpellMap & GetSpellMap() { return m_spells; } void AddSpellMod(SpellModifier* mod, bool apply); - int32 GetTotalFlatMods(uint32 spellId, SpellModOp op); - int32 GetTotalPctMods(uint32 spellId, SpellModOp op); bool IsAffectedBySpellmod(SpellEntry const *spellInfo, SpellModifier *mod, Spell const* spell = NULL); template <class T> T ApplySpellMod(uint32 spellId, SpellModOp op, T &basevalue, Spell const* spell = NULL); void RemoveSpellMods(Spell const* spell); @@ -1459,14 +1417,17 @@ class TRINITY_DLL_SPEC Player : public Unit time_t t = time(NULL); return itr != m_spellCooldowns.end() && itr->second.end > t ? itr->second.end - t : 0; } + void AddSpellAndCategoryCooldowns(SpellEntry const* spellInfo, uint32 itemId, Spell* spell = NULL, bool infinityCooldown = false ); void AddSpellCooldown(uint32 spell_id, uint32 itemid, time_t end_time); - void SendCooldownEvent(SpellEntry const *spellInfo); + void SendCooldownEvent(SpellEntry const *spellInfo, uint32 itemId = 0, Spell* spell = NULL); void ProhibitSpellScholl(SpellSchoolMask idSchoolMask, uint32 unTimeMs ); void RemoveSpellCooldown(uint32 spell_id) { m_spellCooldowns.erase(spell_id); } void RemoveArenaSpellCooldowns(); void RemoveAllSpellCooldown(); void _LoadSpellCooldowns(QueryResult *result); void _SaveSpellCooldowns(); + void SetLastPotionId(uint32 item_id) { m_lastPotionId = item_id; } + void UpdatePotionCooldown(Spell* spell = NULL); void setResurrectRequestData(uint64 guid, uint32 mapId, float X, float Y, float Z, uint32 health, uint32 mana) { @@ -1492,13 +1453,13 @@ class TRINITY_DLL_SPEC Player : public Unit m_cinematic = cine; } - void addActionButton(uint8 button, uint16 action, uint8 type, uint8 misc); + bool addActionButton(uint8 button, uint16 action, uint8 type, uint8 misc); void removeActionButton(uint8 button); void SendInitialActionButtons(); PvPInfo pvpInfo; void UpdatePvP(bool state, bool ovrride=false); - void UpdateZone(uint32 newZone); + void UpdateZone(uint32 newZone,uint32 newArea); void UpdateArea(uint32 newArea); void UpdateZoneDependentAuras( uint32 zone_id ); // zones @@ -1548,6 +1509,7 @@ class TRINITY_DLL_SPEC Player : public Unit static uint32 GetArenaTeamIdFromDB(uint64 guid, uint8 slot); void SetArenaTeamIdInvited(uint32 ArenaTeamId) { m_ArenaTeamIdInvited = ArenaTeamId; } uint32 GetArenaTeamIdInvited() { return m_ArenaTeamIdInvited; } + static void LeaveAllArenaTeams(uint64 guid); void SetDifficulty(uint32 dungeon_difficulty) { m_dungeonDifficulty = dungeon_difficulty; } uint8 GetDifficulty() { return m_dungeonDifficulty; } @@ -1573,9 +1535,12 @@ class TRINITY_DLL_SPEC Player : public Unit void UpdateArmor(); void UpdateMaxHealth(); void UpdateMaxPower(Powers power); + void ApplyFeralAPBonus(int32 amount, bool apply); void UpdateAttackPowerAndDamage(bool ranged = false); void UpdateShieldBlockValue(); void UpdateDamagePhysical(WeaponAttackType attType); + void ApplySpellDamageBonus(int32 amount, bool apply); + void ApplySpellHealingBonus(int32 amount, bool apply); void UpdateSpellDamageAndHealingBonus(); void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, float& min_damage, float& max_damage); @@ -1593,6 +1558,8 @@ class TRINITY_DLL_SPEC Player : public Unit uint32 GetRangedCritDamageReduction(uint32 damage) const; uint32 GetSpellCritDamageReduction(uint32 damage) const; uint32 GetDotDamageReduction(uint32 damage) const; + uint32 GetBaseSpellDamageBonus() { return m_baseSpellDamage;} + uint32 GetBaseSpellHealingBonus() { return m_baseSpellHealing;} float GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) const; void UpdateBlockPercentage(); @@ -1600,9 +1567,14 @@ class TRINITY_DLL_SPEC Player : public Unit void UpdateAllCritPercentages(); void UpdateParryPercentage(); void UpdateDodgePercentage(); + void UpdateMeleeHitChances(); + void UpdateRangedHitChances(); + void UpdateSpellHitChances(); + void UpdateAllSpellCritChances(); void UpdateSpellCritChance(uint32 school); void UpdateExpertise(WeaponAttackType attType); + void ApplyManaRegenBonus(int32 amount, bool apply); void UpdateManaRegen(); const uint64& GetLootGUID() const { return m_lootGuid; } @@ -1618,8 +1590,6 @@ class TRINITY_DLL_SPEC Player : public Unit void SendDelayResponse(const uint32); void SendLogXPGain(uint32 GivenXP,Unit* victim,uint32 RestXP); - //Low Level Packets - void PlaySound(uint32 Sound, bool OnlySelf); //notifiers void SendAttackSwingCantAttack(); void SendAttackSwingCancelAttack(); @@ -1662,6 +1632,7 @@ class TRINITY_DLL_SPEC Player : public Unit uint32 DurabilityRepairAll(bool cost, float discountMod, bool guildBank); uint32 DurabilityRepair(uint16 pos, bool cost, float discountMod, bool guildBank); + void UpdateMirrorTimers(); void StopMirrorTimers() { StopMirrorTimer(FATIGUE_TIMER); @@ -1679,21 +1650,25 @@ class TRINITY_DLL_SPEC Player : public Unit void UpdateDefense(); void UpdateWeaponSkill (WeaponAttackType attType); - void UpdateCombatSkills(Unit *pVictim, WeaponAttackType attType, MeleeHitOutcome outcome, bool defence); + void UpdateCombatSkills(Unit *pVictim, WeaponAttackType attType, bool defence); void SetSkill(uint32 id, uint16 currVal, uint16 maxVal); - uint16 GetMaxSkillValue(uint32 skill) const; // max + perm. bonus + uint16 GetMaxSkillValue(uint32 skill) const; // max + perm. bonus + temp bonus uint16 GetPureMaxSkillValue(uint32 skill) const; // max uint16 GetSkillValue(uint32 skill) const; // skill value + perm. bonus + temp bonus uint16 GetBaseSkillValue(uint32 skill) const; // skill value + perm. bonus uint16 GetPureSkillValue(uint32 skill) const; // skill value + 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; } + WorldLocation& GetTeleportDest() { return m_teleport_dest; } + bool IsBeingTeleported() const { return mSemaphoreTeleport_Near || mSemaphoreTeleport_Far; } + bool IsBeingTeleportedNear() const { return mSemaphoreTeleport_Near; } + bool IsBeingTeleportedFar() const { return mSemaphoreTeleport_Far; } + void SetSemaphoreTeleportNear(bool semphsetting) { mSemaphoreTeleport_Near = semphsetting; } + void SetSemaphoreTeleportFar(bool semphsetting) { mSemaphoreTeleport_Far = semphsetting; } void CheckExploreSystem(void); @@ -1704,40 +1679,15 @@ class TRINITY_DLL_SPEC Player : public Unit bool IsAtGroupRewardDistance(WorldObject const* pRewardSource) const; bool RewardPlayerAndGroupAtKill(Unit* pVictim); + void RewardPlayerAndGroupAtEvent(uint32 creature_id,WorldObject* pRewardSource); + bool isHonorOrXPTarget(Unit* pVictim); - FactionStateList m_factions; - ForcedReactions m_forcedReactions; - FactionStateList const& GetFactionStateList() { return m_factions; } - uint32 GetDefaultReputationFlags(const FactionEntry *factionEntry) const; - int32 GetBaseReputation(const FactionEntry *factionEntry) const; - int32 GetReputation(uint32 faction_id) const; - int32 GetReputation(const FactionEntry *factionEntry) const; - ReputationRank GetReputationRank(uint32 faction) const; - ReputationRank GetReputationRank(const FactionEntry *factionEntry) const; - ReputationRank GetBaseReputationRank(const FactionEntry *factionEntry) const; - ReputationRank ReputationToRank(int32 standing) const; - const static int32 ReputationRank_Length[MAX_REPUTATION_RANK]; - const static int32 Reputation_Cap = 42999; - const static int32 Reputation_Bottom = -42000; - bool ModifyFactionReputation(uint32 FactionTemplateId, int32 DeltaReputation); - bool ModifyFactionReputation(FactionEntry const* factionEntry, int32 standing); - bool ModifyOneFactionReputation(FactionEntry const* factionEntry, int32 standing); - bool SetFactionReputation(uint32 FactionTemplateId, int32 standing); - bool SetFactionReputation(FactionEntry const* factionEntry, int32 standing); - bool SetOneFactionReputation(FactionEntry const* factionEntry, int32 standing); - int32 CalculateReputationGain(uint32 creatureOrQuestLevel, int32 rep, bool for_quest); + ReputationMgr& GetReputationMgr() { return m_reputationMgr; } + ReputationMgr const& GetReputationMgr() const { return m_reputationMgr; } + ReputationRank GetReputationRank(uint32 faction_id) const; void RewardReputation(Unit *pVictim, float rate); void RewardReputation(Quest const *pQuest); - void SetInitialFactions(); - void UpdateReputation() const; - void SendFactionState(FactionState const* faction) const; - void SendInitialReputations(); - FactionState const* GetFactionState( FactionEntry const* factionEntry) const; - void SetFactionAtWar(FactionState* faction, bool atWar); - void SetFactionInactive(FactionState* faction, bool inactive); - void SetFactionVisible(FactionState* faction); - void SetFactionVisibleForFactionTemplateId(uint32 FactionTemplateId); - void SetFactionVisibleForFactionId(uint32 FactionId); + void UpdateSkillsForLevel(); void UpdateSkillsToMaxSkillsForLevel(); // for .levelup void ModifySkillBonus(uint32 skillid,int32 val, bool talent); @@ -1770,6 +1720,8 @@ class TRINITY_DLL_SPEC Player : public Unit void SetCanParry(bool value); bool CanBlock() const { return m_canBlock; } void SetCanBlock(bool value); + bool CanTitanGrip() const { return m_canTitanGrip ; } + void SetCanTitanGrip(bool value) { m_canTitanGrip = value; } void SetRegularAttackTime(); void SetBaseModValue(BaseModGroup modGroup, BaseModType modType, float value) { m_auraBaseMod[modGroup][modType] = value; } @@ -1781,8 +1733,8 @@ class TRINITY_DLL_SPEC Player : public Unit void _RemoveAllStatBonuses(); void _ApplyWeaponDependentAuraMods(Item *item, WeaponAttackType attackType, bool apply); - void _ApplyWeaponDependentAuraCritMod(Item *item, WeaponAttackType attackType, Aura* aura, bool apply); - void _ApplyWeaponDependentAuraDamageMod(Item *item, WeaponAttackType attackType, Aura* aura, bool apply); + void _ApplyWeaponDependentAuraCritMod(Item *item, WeaponAttackType attackType, AuraEffect* aura, bool apply); + void _ApplyWeaponDependentAuraDamageMod(Item *item, WeaponAttackType attackType, AuraEffect* aura, bool apply); void _ApplyItemMods(Item *item,uint8 slot,bool apply); void _RemoveAllItemMods(); @@ -1798,12 +1750,13 @@ class TRINITY_DLL_SPEC Player : public Unit void ApplyEquipSpell(SpellEntry const* spellInfo, Item* item, bool apply, bool form_change = false); void UpdateEquipSpellsAtFormChange(); void CastItemCombatSpell(Item *item,Unit* Target, WeaponAttackType attType); + void CastItemUseSpell(Item *item,SpellCastTargets const& targets,uint8 cast_count, uint32 glyphIndex); - void SendInitWorldStates(bool force = false, uint32 forceZoneId = 0); + void SendInitWorldStates(uint32 zone, uint32 area); void SendUpdateWorldState(uint32 Field, uint32 Value); void SendDirectMessage(WorldPacket *data); - void SendAuraDurationsForTarget(Unit* target); + void SendAurasForTarget(Unit *target); PlayerMenu* PlayerTalkClass; std::vector<ItemSetEffect *> ItemSetEff; @@ -1817,51 +1770,55 @@ class TRINITY_DLL_SPEC Player : public Unit /*** BATTLEGROUND SYSTEM ***/ /*********************************************************/ - bool InBattleGround() const { return m_bgBattleGroundID != 0; } - uint32 GetBattleGroundId() const { return m_bgBattleGroundID; } + bool InBattleGround() const { return m_bgBattleGroundID != 0; } + bool InArena() const; + uint32 GetBattleGroundId() const { return m_bgBattleGroundID; } + BattleGroundTypeId GetBattleGroundTypeId() const { return m_bgTypeID; } BattleGround* GetBattleGround() const; - bool InArena() const; - static uint32 GetMinLevelForBattleGroundQueueId(uint32 queue_id); - static uint32 GetMaxLevelForBattleGroundQueueId(uint32 queue_id); - uint32 GetBattleGroundQueueIdFromLevel() const; + + BGQueueIdBasedOnLevel GetBattleGroundQueueIdFromLevel(BattleGroundTypeId bgTypeId) const; bool InBattleGroundQueue() const { for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) - if (m_bgBattleGroundQueueID[i].bgQueueType != 0) + if (m_bgBattleGroundQueueID[i].bgQueueTypeId != BATTLEGROUND_QUEUE_NONE) return true; return false; } - uint32 GetBattleGroundQueueId(uint32 index) const { return m_bgBattleGroundQueueID[index].bgQueueType; } - uint32 GetBattleGroundQueueIndex(uint32 bgQueueType) const + BattleGroundQueueTypeId GetBattleGroundQueueTypeId(uint32 index) const { return m_bgBattleGroundQueueID[index].bgQueueTypeId; } + uint32 GetBattleGroundQueueIndex(BattleGroundQueueTypeId bgQueueTypeId) const { for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) - if (m_bgBattleGroundQueueID[i].bgQueueType == bgQueueType) + if (m_bgBattleGroundQueueID[i].bgQueueTypeId == bgQueueTypeId) return i; return PLAYER_MAX_BATTLEGROUND_QUEUES; } - bool IsInvitedForBattleGroundQueueType(uint32 bgQueueType) const + bool IsInvitedForBattleGroundQueueType(BattleGroundQueueTypeId bgQueueTypeId) const { for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) - if (m_bgBattleGroundQueueID[i].bgQueueType == bgQueueType) + if (m_bgBattleGroundQueueID[i].bgQueueTypeId == bgQueueTypeId) return m_bgBattleGroundQueueID[i].invitedToInstance != 0; return PLAYER_MAX_BATTLEGROUND_QUEUES; } - bool InBattleGroundQueueForBattleGroundQueueType(uint32 bgQueueType) const + bool InBattleGroundQueueForBattleGroundQueueType(BattleGroundQueueTypeId bgQueueTypeId) const { - return GetBattleGroundQueueIndex(bgQueueType) < PLAYER_MAX_BATTLEGROUND_QUEUES; + return GetBattleGroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES; } - void SetBattleGroundId(uint32 val) { m_bgBattleGroundID = val; } - uint32 AddBattleGroundQueueId(uint32 val) + void SetBattleGroundId(uint32 val, BattleGroundTypeId bgTypeId) + { + m_bgBattleGroundID = val; + m_bgTypeID = bgTypeId; + } + uint32 AddBattleGroundQueueId(BattleGroundQueueTypeId val) { for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) { - if (m_bgBattleGroundQueueID[i].bgQueueType == 0 || m_bgBattleGroundQueueID[i].bgQueueType == val) + if (m_bgBattleGroundQueueID[i].bgQueueTypeId == BATTLEGROUND_QUEUE_NONE || m_bgBattleGroundQueueID[i].bgQueueTypeId == val) { - m_bgBattleGroundQueueID[i].bgQueueType = val; + m_bgBattleGroundQueueID[i].bgQueueTypeId = val; m_bgBattleGroundQueueID[i].invitedToInstance = 0; return i; } @@ -1871,26 +1828,26 @@ class TRINITY_DLL_SPEC Player : public Unit bool HasFreeBattleGroundQueueId() { for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) - if (m_bgBattleGroundQueueID[i].bgQueueType == 0) + if (m_bgBattleGroundQueueID[i].bgQueueTypeId == BATTLEGROUND_QUEUE_NONE) return true; return false; } - void RemoveBattleGroundQueueId(uint32 val) + void RemoveBattleGroundQueueId(BattleGroundQueueTypeId val) { for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) { - if (m_bgBattleGroundQueueID[i].bgQueueType == val) + if (m_bgBattleGroundQueueID[i].bgQueueTypeId == val) { - m_bgBattleGroundQueueID[i].bgQueueType = 0; + m_bgBattleGroundQueueID[i].bgQueueTypeId = BATTLEGROUND_QUEUE_NONE; m_bgBattleGroundQueueID[i].invitedToInstance = 0; return; } } } - void SetInviteForBattleGroundQueueType(uint32 bgQueueType, uint32 instanceId) + void SetInviteForBattleGroundQueueType(BattleGroundQueueTypeId bgQueueTypeId, uint32 instanceId) { for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) - if (m_bgBattleGroundQueueID[i].bgQueueType == bgQueueType) + if (m_bgBattleGroundQueueID[i].bgQueueTypeId == bgQueueTypeId) m_bgBattleGroundQueueID[i].invitedToInstance = instanceId; } bool IsInvitedForBattleGroundInstance(uint32 instanceId) const @@ -1900,18 +1857,10 @@ class TRINITY_DLL_SPEC Player : public Unit return true; return false; } - uint32 GetBattleGroundEntryPointMap() const { return m_bgEntryPointMap; } - float GetBattleGroundEntryPointX() const { return m_bgEntryPointX; } - float GetBattleGroundEntryPointY() const { return m_bgEntryPointY; } - float GetBattleGroundEntryPointZ() const { return m_bgEntryPointZ; } - float GetBattleGroundEntryPointO() const { return m_bgEntryPointO; } + WorldLocation const& GetBattleGroundEntryPoint() const { return m_bgEntryPoint; } void SetBattleGroundEntryPoint(uint32 Map, float PosX, float PosY, float PosZ, float PosO ) { - m_bgEntryPointMap = Map; - m_bgEntryPointX = PosX; - m_bgEntryPointY = PosY; - m_bgEntryPointZ = PosZ; - m_bgEntryPointO = PosO; + m_bgEntryPoint = WorldLocation(Map,PosX,PosY,PosZ,PosO); } void SetBGTeam(uint32 team) { m_bgTeam = team; } @@ -1923,9 +1872,11 @@ class TRINITY_DLL_SPEC Player : public Unit void ReportedAfkBy(Player* reporter); void ClearAfkReports() { m_bgAfkReporter.clear(); } - bool GetBGAccessByLevel(uint32 bgTypeId) const; - bool isAllowUseBattleGroundObject(); + bool GetBGAccessByLevel(BattleGroundTypeId bgTypeId) const; bool isTotalImmunity(); + bool CanUseBattleGroundObject(); + bool isTotalImmune(); + bool CanCaptureTowerPoint(); /*********************************************************/ /*** OUTDOOR PVP SYSTEM ***/ @@ -1939,7 +1890,7 @@ class TRINITY_DLL_SPEC Player : public Unit /*** REST SYSTEM ***/ /*********************************************************/ - bool isRested() const { return GetRestTime() >= 10000; } + bool isRested() const { return GetRestTime() >= 10*IN_MILISECONDS; } uint32 GetXPRestBonus(uint32 xp); uint32 GetRestTime() const { return m_restTime;}; void SetRestTime(uint32 v) { m_restTime = v;}; @@ -1948,7 +1899,7 @@ class TRINITY_DLL_SPEC Player : public Unit /*** ENVIROMENTAL SYSTEM ***/ /*********************************************************/ - void EnvironmentalDamage(uint64 guid, EnviromentalDamage type, uint32 damage); + void EnvironmentalDamage(EnviromentalDamage type, uint32 damage); /*********************************************************/ /*** FLOOD FILTER SYSTEM ***/ @@ -1962,27 +1913,34 @@ class TRINITY_DLL_SPEC Player : public Unit /*** VARIOUS SYSTEMS ***/ /*********************************************************/ MovementInfo m_movementInfo; - uint32 m_lastFallTime; - float m_lastFallZ; + void UpdateFallInformationIfNeed(MovementInfo const& minfo,uint16 opcode); + Unit *m_mover; + WorldObject *m_seer; void SetFallInformation(uint32 time, float z) { m_lastFallTime = time; m_lastFallZ = z; } + void HandleFall(MovementInfo const& movementInfo); + bool isMoving() const { return HasUnitMovementFlag(movementFlagsMask); } bool isMovingOrTurning() const { return HasUnitMovementFlag(movementOrTurningFlagsMask); } bool CanFly() const { return HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY); } bool IsFlying() const { return HasUnitMovementFlag(MOVEMENTFLAG_FLYING); } - - void HandleDrowning(); - void HandleFallDamage(MovementInfo& movementInfo); - void HandleFallUnderMap(); + bool IsAllowUseFlyMountsHere() const; void SetClientControl(Unit* target, uint8 allowMove); - uint64 GetFarSight() const { return GetUInt64Value(PLAYER_FARSIGHT); } - void SetFarSight(uint64 guid) { SetUInt64Value(PLAYER_FARSIGHT, guid); } + void EnterVehicle(Vehicle *vehicle); + void ExitVehicle(Vehicle *vehicle); + + void SetMover(Unit* target) { m_mover = target; } + void SetSeer(WorldObject *target) { m_seer = target; } + void SetViewpoint(WorldObject *target, bool apply); + WorldObject* GetViewpoint() const; + void StopCastingCharm(); + void StopCastingBindSight(); // Transports Transport * GetTransport() const { return m_transport; } @@ -1993,6 +1951,7 @@ class TRINITY_DLL_SPEC Player : public Unit float GetTransOffsetZ() const { return m_movementInfo.t_z; } float GetTransOffsetO() const { return m_movementInfo.t_o; } uint32 GetTransTime() const { return m_movementInfo.t_time; } + int8 GetTransSeat() const { return m_movementInfo.t_seat; } uint32 GetSaveTimer() const { return m_nextSave; } void SetSaveTimer(uint32 timer) { m_nextSave = timer; } @@ -2011,6 +1970,7 @@ class TRINITY_DLL_SPEC Player : public Unit float m_homebindX; float m_homebindY; float m_homebindZ; + void RelocateToHomebind() { SetMapId(m_homebindMapId); Relocate(m_homebindX,m_homebindY,m_homebindZ); } // currently visible objects at player client typedef std::set<uint64> ClientGUIDs; @@ -2042,9 +2002,12 @@ class TRINITY_DLL_SPEC Player : public Unit // Temporarily removed pet cache uint32 GetTemporaryUnsummonedPetNumber() const { return m_temporaryUnsummonedPetNumber; } void SetTemporaryUnsummonedPetNumber(uint32 petnumber) { m_temporaryUnsummonedPetNumber = petnumber; } - uint32 GetOldPetSpell() const { return m_oldpetspell; } - void SetOldPetSpell(uint32 petspell) { m_oldpetspell = petspell; } + void UnsummonPetTemporaryIfAny(); + void ResummonPetTemporaryUnSummonedIfAny(); + bool IsPetNeedBeTemporaryUnsummoned() const { return !IsInWorld() || !isAlive() || IsMounted() /*+in flight*/; } + void SendCinematicStart(uint32 CinematicSequenceId); + void SendMovieStart(uint32 MovieId); /*********************************************************/ /*** INSTANCE SYSTEM ***/ @@ -2079,22 +2042,39 @@ class TRINITY_DLL_SPEC Player : public Unit GroupReference& GetGroupRef() { return m_group; } void SetGroup(Group *group, int8 subgroup = -1); uint8 GetSubGroup() const { return m_group.getSubGroup(); } - uint32 GetGroupUpdateFlag() { return m_groupUpdateMask; } + uint32 GetGroupUpdateFlag() const { return m_groupUpdateMask; } void SetGroupUpdateFlag(uint32 flag) { m_groupUpdateMask |= flag; } - uint64 GetAuraUpdateMask() { return m_auraUpdateMask; } - void SetAuraUpdateMask(uint8 slot) { m_auraUpdateMask |= (uint64(1) << slot); } - void UnsetAuraUpdateMask(uint8 slot) { m_auraUpdateMask &= ~(uint64(1) << slot); } + const uint64& GetAuraUpdateMaskForRaid() const { return m_auraRaidUpdateMask; } + void SetAuraUpdateMaskForRaid(uint8 slot) { m_auraRaidUpdateMask |= (uint64(1) << slot); } Player* GetNextRandomRaidMember(float radius); PartyResult CanUninviteFromGroup() const; + // BattleGround Group System + void SetBattleGroundRaid(Group *group, int8 subgroup = -1); + void RemoveFromBattleGroundRaid(); + Group * GetOriginalGroup() { return m_originalGroup.getTarget(); } + GroupReference& GetOriginalGroupRef() { return m_originalGroup; } + uint8 GetOriginalSubGroup() const { return m_originalGroup.getSubGroup(); } + void SetOriginalGroup(Group *group, int8 subgroup = -1); GridReference<Player> &GetGridRef() { return m_gridRef; } MapReference &GetMapRef() { return m_mapRef; } bool isAllowedToLoot(Creature* creature); - WorldLocation& GetTeleportDest() { return m_teleport_dest; } - DeclinedName const* GetDeclinedNames() const { return m_declinedname; } + uint8 GetRunesState() const { return m_runes->runeState; } + uint8 GetBaseRune(uint8 index) const { return m_runes->runes[index].BaseRune; } + uint8 GetCurrentRune(uint8 index) const { return m_runes->runes[index].CurrentRune; } + uint8 GetRuneCooldown(uint8 index) const { return m_runes->runes[index].Cooldown; } + void SetBaseRune(uint8 index, uint8 baseRune) { m_runes->runes[index].BaseRune = baseRune; } + void SetCurrentRune(uint8 index, uint8 currentRune) { m_runes->runes[index].CurrentRune = currentRune; } + void SetRuneCooldown(uint8 index, uint8 cooldown) { m_runes->runes[index].Cooldown = cooldown; m_runes->SetRuneState(index, (cooldown == 0) ? true : false); } + void ConvertRune(uint8 index, uint8 newType); + void ResyncRunes(uint8 count); + void AddRunePower(uint8 index); + void InitRunes(); + AchievementMgr& GetAchievementMgr() { return m_achievementMgr; } + void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1=0, uint32 miscvalue2=0, Unit *unit=NULL, uint32 time=0); bool HasTitle(uint32 bitIndex); bool HasTitle(CharTitlesEntry const* title) { return HasTitle(title->bit_index); } void SetTitle(CharTitlesEntry const* title); @@ -2107,20 +2087,17 @@ class TRINITY_DLL_SPEC Player : public Unit /* this variable is set to bg->m_InstanceID, when player is teleported to BG - (it is battleground's GUID)*/ uint32 m_bgBattleGroundID; + BattleGroundTypeId m_bgTypeID; /* this is an array of BG queues (BgTypeIDs) in which is player */ struct BgBattleGroundQueueID_Rec { - uint32 bgQueueType; + BattleGroundQueueTypeId bgQueueTypeId; uint32 invitedToInstance; }; BgBattleGroundQueueID_Rec m_bgBattleGroundQueueID[PLAYER_MAX_BATTLEGROUND_QUEUES]; - uint32 m_bgEntryPointMap; - float m_bgEntryPointX; - float m_bgEntryPointY; - float m_bgEntryPointZ; - float m_bgEntryPointO; + WorldLocation m_bgEntryPoint; std::set<uint32> m_bgAfkReporter; uint8 m_bgAfkReportedCount; @@ -2144,6 +2121,7 @@ class TRINITY_DLL_SPEC Player : public Unit void _LoadActions(QueryResult *result); void _LoadAuras(QueryResult *result, uint32 timediff); + void _LoadGlyphAuras(); void _LoadBoundInstances(QueryResult *result); void _LoadInventory(QueryResult *result, uint32 timediff); void _LoadMailInit(QueryResult *resultUnread, QueryResult *resultDelivery); @@ -2152,7 +2130,7 @@ class TRINITY_DLL_SPEC Player : public Unit void _LoadQuestStatus(QueryResult *result); 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); @@ -2170,7 +2148,6 @@ class TRINITY_DLL_SPEC Player : public Unit void _SaveMail(); void _SaveQuestStatus(); void _SaveDailyQuestStatus(); - void _SaveReputation(); void _SaveSpells(); void _SaveTutorials(); @@ -2180,13 +2157,11 @@ class TRINITY_DLL_SPEC Player : public Unit /*********************************************************/ /*** ENVIRONMENTAL SYSTEM ***/ /*********************************************************/ - void HandleLava(); void HandleSobering(); - void StartMirrorTimer(MirrorTimerType Type, uint32 MaxValue); - void ModifyMirrorTimer(MirrorTimerType Type, uint32 MaxValue, uint32 CurrentValue, uint32 Regen); + void SendMirrorTimer(MirrorTimerType Type, uint32 MaxValue, uint32 CurrentValue, int32 Regen); void StopMirrorTimer(MirrorTimerType Type); - uint8 m_isunderwater; - bool m_isInWater; + void HandleDrowning(uint32 time_diff); + int32 getMaxTimer(MirrorTimerType timer); /*********************************************************/ /*** HONOR SYSTEM ***/ @@ -2194,7 +2169,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; @@ -2227,10 +2201,16 @@ class TRINITY_DLL_SPEC Player : public Unit PlayerMails m_mail; PlayerSpellMap m_spells; SpellCooldowns m_spellCooldowns; + uint32 m_lastPotionId; // last used health/mana potion in combat, that block next potion use ActionButtonList m_actionButtons; float m_auraBaseMod[BASEMOD_END][MOD_END]; + int16 m_baseRatingValue[MAX_COMBAT_RATING]; + uint16 m_baseSpellDamage; + uint16 m_baseSpellHealing; + uint16 m_baseFeralAP; + uint16 m_baseManaRegen; SpellModList m_spellMods[MAX_SPELLMOD]; int32 m_SpellModRemoveCount; @@ -2247,8 +2227,6 @@ class TRINITY_DLL_SPEC Player : public Unit typedef std::list<Channel*> JoinedChannelsList; JoinedChannelsList m_channels; - bool m_dontMove; - int m_cinematic; Player *pTrader; @@ -2264,8 +2242,6 @@ class TRINITY_DLL_SPEC Player : public Unit bool m_DailyQuestChanged; time_t m_lastDailyQuestTime; - uint32 m_regenTimer; - uint32 m_breathTimer; uint32 m_drunkTimer; uint16 m_drunk; uint32 m_weaponChangeTimer; @@ -2283,8 +2259,10 @@ class TRINITY_DLL_SPEC Player : public Unit uint32 m_ArmorProficiency; bool m_canParry; bool m_canBlock; + bool m_canTitanGrip; uint8 m_swingErrorMsg; float m_ammoDPS; + ////////////////////Rest System///////////////////// int time_inn_enter; uint32 inn_pos_mapid; @@ -2301,22 +2279,17 @@ class TRINITY_DLL_SPEC Player : public Unit uint32 m_resetTalentsCost; time_t m_resetTalentsTime; uint32 m_usedTalentCount; + uint32 m_questRewardTalentCount; // Social PlayerSocial *m_social; // Groups GroupReference m_group; + GroupReference m_originalGroup; Group *m_groupInvite; uint32 m_groupUpdateMask; - uint64 m_auraUpdateMask; - - // Temporarily removed pet cache - uint32 m_temporaryUnsummonedPetNumber; - uint32 m_oldpetspell; - - uint64 m_miniPet; - GuardianPetList m_guardianPets; + uint64 m_auraRaidUpdateMask; // Player summoning time_t m_summon_expire; @@ -2325,12 +2298,8 @@ class TRINITY_DLL_SPEC Player : public Unit float m_summon_y; float m_summon_z; - // Far Teleport - WorldLocation m_teleport_dest; - - bool m_farsightVision; - DeclinedName *m_declinedname; + Runes *m_runes; private: // internal common parts for CanStore/StoreItem functions uint8 _CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountVec& dest, ItemPrototype const *pProto, uint32& count, bool swap, Item *pSrcItem ) const; @@ -2338,11 +2307,35 @@ class TRINITY_DLL_SPEC Player : public Unit uint8 _CanStoreItem_InInventorySlots( uint8 slot_begin, uint8 slot_end, ItemPosCountVec& dest, ItemPrototype const *pProto, uint32& count, bool merge, Item *pSrcItem, uint8 skip_bag, uint8 skip_slot ) const; Item* _StoreItem( uint16 pos, Item *pItem, uint32 count, bool clone, bool update ); + void UpdateKnownCurrencies(uint32 itemId, bool apply); + int32 CalculateReputationGain(uint32 creatureOrQuestLevel, int32 rep, bool for_quest); + void AdjustQuestReqItemCount( Quest const* pQuest, QuestStatusData& questStatusData ); + GridReference<Player> m_gridRef; MapReference m_mapRef; void UpdateCharmedAI(); UnitAI *i_AI; + + uint32 m_lastFallTime; + float m_lastFallZ; + + int32 m_MirrorTimer[MAX_TIMERS]; + uint8 m_MirrorTimerFlags; + uint8 m_MirrorTimerFlagsLast; + bool m_isInWater; + + // Current teleport data + WorldLocation m_teleport_dest; + bool mSemaphoreTeleport_Near; + bool mSemaphoreTeleport_Far; + + // Temporary removed pet cache + uint32 m_temporaryUnsummonedPetNumber; + uint32 m_oldpetspell; + + AchievementMgr m_achievementMgr; + ReputationMgr m_reputationMgr; }; void AddItemsSetItem(Player*player,Item *item); @@ -2378,7 +2371,6 @@ template <class T> T Player::ApplySpellMod(uint32 spellId, SpellModOp op, T &bas if (mod->charges > 0 ) { - if( !(spellInfo->SpellFamilyName == 8 && spellInfo->SpellFamilyFlags & 0x200000000LL) ) --mod->charges; if (mod->charges == 0) { diff --git a/src/game/PlayerDump.cpp b/src/game/PlayerDump.cpp index b061b63a06a..f8f4a0f9f8a 100644 --- a/src/game/PlayerDump.cpp +++ b/src/game/PlayerDump.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,7 +26,7 @@ #include "ObjectMgr.h" // Character Dump tables -#define DUMP_TABLE_COUNT 18 +#define DUMP_TABLE_COUNT 20 struct DumpTable { @@ -36,24 +36,27 @@ struct DumpTable static DumpTable dumpTables[DUMP_TABLE_COUNT] = { - { "characters", DTT_CHARACTER }, - { "character_queststatus", DTT_CHAR_TABLE }, - { "character_reputation", DTT_CHAR_TABLE }, - { "character_spell", DTT_CHAR_TABLE }, - { "character_spell_cooldown", DTT_CHAR_TABLE }, - { "character_action", DTT_CHAR_TABLE }, - { "character_aura", DTT_CHAR_TABLE }, - { "character_homebind", DTT_CHAR_TABLE }, - { "character_inventory", DTT_INVENTORY }, - { "mail", DTT_MAIL }, - { "mail_items", DTT_MAIL_ITEM }, - { "item_instance", DTT_ITEM }, - { "character_gifts", DTT_ITEM_GIFT }, - { "item_text", DTT_ITEM_TEXT }, - { "character_pet", DTT_PET }, - { "pet_aura", DTT_PET_TABLE }, - { "pet_spell", DTT_PET_TABLE }, - { "pet_spell_cooldown", DTT_PET_TABLE }, + { "characters", DTT_CHARACTER }, + { "character_achievement", DTT_CHAR_TABLE }, + { "character_achievement_progress", DTT_CHAR_TABLE }, + { "character_queststatus", DTT_CHAR_TABLE }, + { "character_reputation", DTT_CHAR_TABLE }, + { "character_spell", DTT_CHAR_TABLE }, + { "character_spell_cooldown", DTT_CHAR_TABLE }, + { "character_action", DTT_CHAR_TABLE }, + { "character_aura", DTT_CHAR_TABLE }, + { "character_homebind", DTT_CHAR_TABLE }, +// { "character_ticket", DTT_CHAR_TABLE }, + { "character_inventory", DTT_INVENTORY }, + { "mail", DTT_MAIL }, + { "mail_items", DTT_MAIL_ITEM }, + { "item_instance", DTT_ITEM }, + { "character_gifts", DTT_ITEM_GIFT }, + { "item_text", DTT_ITEM_TEXT }, + { "character_pet", DTT_PET }, + { "pet_aura", DTT_PET_TABLE }, + { "pet_spell", DTT_PET_TABLE }, + { "pet_spell_cooldown", DTT_PET_TABLE }, }; // Low level functions diff --git a/src/game/PlayerDump.h b/src/game/PlayerDump.h index 211a000103d..e3dfd192efb 100644 --- a/src/game/PlayerDump.h +++ b/src/game/PlayerDump.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,6 +20,7 @@ #ifndef _PLAYER_DUMP_H #define _PLAYER_DUMP_H + #include <string> #include <map> #include <set> @@ -28,10 +29,10 @@ enum DumpTableType { DTT_CHARACTER, // // characters - DTT_CHAR_TABLE, // // character_action, character_aura, character_homebind, - // character_queststatus, character_reputation, - // character_spell, character_spell_cooldown, character_ticket, - // character_tutorial + DTT_CHAR_TABLE, // // character_achievement, character_achievement_progress, + // character_action, character_aura, character_homebind, + // character_queststatus, character_reputation, character_spell, + // character_spell_cooldown, character_ticket, character_tutorial DTT_INVENTORY, // -> item guids collection // character_inventory diff --git a/src/game/PointMovementGenerator.cpp b/src/game/PointMovementGenerator.cpp index 6117dbf903d..084dad7cfa8 100644 --- a/src/game/PointMovementGenerator.cpp +++ b/src/game/PointMovementGenerator.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,16 +22,16 @@ #include "Errors.h" #include "Creature.h" #include "CreatureAI.h" -#include "MapManager.h" #include "DestinationHolderImp.h" //----- Point Movement Generator template<class T> void PointMovementGenerator<T>::Initialize(T &unit) { - unit.StopMoving(); + //unit.StopMoving(); + unit.clearUnitState(UNIT_STAT_MOVING); Traveller<T> traveller(unit); - i_destinationHolder.SetDestination(traveller,i_x,i_y,i_z); + i_destinationHolder.SetDestination(traveller,i_x,i_y,i_z, !unit.hasUnitState(UNIT_STAT_JUMPING)); if (unit.GetTypeId() == TYPEID_UNIT && ((Creature*)&unit)->canFly()) unit.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2); @@ -68,7 +68,7 @@ template<class T> void PointMovementGenerator<T>:: Finalize(T &unit) { if(unit.hasUnitState(UNIT_STAT_CHARGING)) - unit.clearUnitState(UNIT_STAT_CHARGING); + unit.clearUnitState(UNIT_STAT_CHARGING | UNIT_STAT_JUMPING); else if(arrived) MovementInform(unit); } diff --git a/src/game/PointMovementGenerator.h b/src/game/PointMovementGenerator.h index 5f1bf33c348..6dfb1d6f8ec 100644 --- a/src/game/PointMovementGenerator.h +++ b/src/game/PointMovementGenerator.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/PoolHandler.cpp b/src/game/PoolHandler.cpp new file mode 100644 index 00000000000..bb2276a6d24 --- /dev/null +++ b/src/game/PoolHandler.cpp @@ -0,0 +1,719 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "PoolHandler.h" +#include "ObjectMgr.h" +#include "ProgressBar.h" +#include "Log.h" +#include "MapManager.h" +#include "Policies/SingletonImp.h" + +INSTANTIATE_SINGLETON_1(PoolHandler); + +//////////////////////////////////////////////////////////// +// Methods of template class PoolGroup + +template <class T> +PoolGroup<T>::PoolGroup() +{ + Spawned = 0; +} + +// Method to add a gameobject/creature guid to the proper list depending on pool type and chance value +template <class T> +void PoolGroup<T>::AddEntry(PoolObject& poolitem, uint32 maxentries) +{ + if (poolitem.chance != 0 && maxentries == 1) + ExplicitlyChanced.push_back(poolitem); + else + EqualChanced.push_back(poolitem); +} + +// Method to check the chances are proper in this object pool +template <class T> +bool PoolGroup<T>::CheckPool(void) +{ + if (EqualChanced.size() == 0) + { + float chance = 0; + for (uint32 i=0; i<ExplicitlyChanced.size(); ++i) + chance += ExplicitlyChanced[i].chance; + if (chance != 100 && chance != 0) + return false; + } + return true; +} + +// Method that tell if the gameobject, creature or pool is spawned currently +template <class T> +bool PoolGroup<T>::IsSpawnedObject(uint32 guid) +{ + for (uint32 i=0; i<ExplicitlyChanced.size(); ++i) + if (ExplicitlyChanced[i].guid == guid) + return ExplicitlyChanced[i].spawned; + for (uint32 i=0; i<EqualChanced.size(); ++i) + if (EqualChanced[i].guid == guid) + return EqualChanced[i].spawned; + return false; +} + +// Method that return a guid of a rolled creature or gameobject +// Note: Copy from loot system because it's very similar and only few things change +template <class T> +uint32 PoolGroup<T>::RollOne(void) +{ + if (!ExplicitlyChanced.empty()) // First explicitly chanced entries are checked + { + float roll = rand_chance(); + + for (uint32 i=0; i<ExplicitlyChanced.size(); ++i) + { + roll -= ExplicitlyChanced[i].chance; + if (roll < 0) + return ExplicitlyChanced[i].guid; + } + } + if (!EqualChanced.empty()) + return EqualChanced[irand(0, EqualChanced.size()-1)].guid; + + return 0; // None found +} + +// Main method to despawn a creature or gameobject in a pool +// If no guid is passed, the pool is just removed (event end case) +// If guid is filled, cache will be used and no removal will occur, it just fill the cache +template<class T> +void PoolGroup<T>::DespawnObject(uint32 guid) +{ + for (int i=0; i<EqualChanced.size(); ++i) + { + if (EqualChanced[i].spawned) + { + if (!guid || EqualChanced[i].guid == guid) + { + if (guid) + CacheValue = EqualChanced[i].guid; + else + Despawn1Object(EqualChanced[i].guid); + + EqualChanced[i].spawned = false; + Spawned--; + } + } + } +} + +// Method that is actualy doing the removal job on one creature +template<> +void PoolGroup<Creature>::Despawn1Object(uint32 guid) +{ + if (CreatureData const* data = objmgr.GetCreatureData(guid)) + { + objmgr.RemoveCreatureFromGrid(guid, data); + + if (Creature* pCreature = ObjectAccessor::Instance().GetObjectInWorld(MAKE_NEW_GUID(guid, data->id, HIGHGUID_UNIT), (Creature*)NULL)) + { + pCreature->CleanupsBeforeDelete(); + pCreature->AddObjectToRemoveList(); + } + } +} + +// Same on one gameobject +template<> +void PoolGroup<GameObject>::Despawn1Object(uint32 guid) +{ + if (GameObjectData const* data = objmgr.GetGOData(guid)) + { + objmgr.RemoveGameobjectFromGrid(guid, data); + + if (GameObject* pGameobject = ObjectAccessor::Instance().GetObjectInWorld(MAKE_NEW_GUID(guid, data->id, HIGHGUID_GAMEOBJECT), (GameObject*)NULL)) + pGameobject->AddObjectToRemoveList(); + } +} + +// Same on one pool +template<> +void PoolGroup<Pool>::Despawn1Object(uint32 child_pool_id) +{ + poolhandler.DespawnPool(child_pool_id); +} + +// Method for a pool only to remove any found record causing a circular dependency loop +template<> +void PoolGroup<Pool>::RemoveOneRelation(uint16 child_pool_id) +{ + for (PoolObjectList::iterator itr = ExplicitlyChanced.begin(); itr != ExplicitlyChanced.end(); ++itr) + { + if(itr->guid == child_pool_id) + { + ExplicitlyChanced.erase(itr); + break; + } + } + for (PoolObjectList::iterator itr = EqualChanced.begin(); itr != EqualChanced.end(); ++itr) + { + if(itr->guid == child_pool_id) + { + EqualChanced.erase(itr); + break; + } + } +} + +// Method that Spawn 1+ creatures or gameobject +// if cache is false (initialization or event start), X creatures are spawned with X <= limit (< if limit higher that the number of creatures in pool) +// if cache is true, this means only one has to be spawned (or respawned if the rolled one is same as cached one) +template <class T> +void PoolGroup<T>::SpawnObject(uint32 limit, bool cache) +{ + if (limit == 1) // This is the only case where explicit chance is used + { + uint32 roll = RollOne(); + if (cache && CacheValue != roll) + Despawn1Object(CacheValue); + CacheValue = Spawn1Object(roll); + } + else if (limit < EqualChanced.size() && Spawned < limit) + { + std::vector<uint32> IndexList; + for (int i=0; i<EqualChanced.size(); ++i) + if (!EqualChanced[i].spawned) + IndexList.push_back(i); + + while (Spawned < limit && IndexList.size() > 0) + { + uint32 roll = urand(1, IndexList.size()) - 1; + uint32 index = IndexList[roll]; + if (!cache || (cache && EqualChanced[index].guid != CacheValue)) + { + if (cache) + Despawn1Object(CacheValue); + EqualChanced[index].spawned = Spawn1Object(EqualChanced[index].guid); + } + else + EqualChanced[index].spawned = ReSpawn1Object(EqualChanced[index].guid); + + if (EqualChanced[index].spawned) + ++Spawned; // limited group use the Spawned variable to store the number of actualy spawned creatures + std::vector<uint32>::iterator itr = IndexList.begin()+roll; + IndexList.erase(itr); + } + CacheValue = 0; + } + else // Not enough objects in pool, so spawn all + { + for (int i=0; i<EqualChanced.size(); ++i) + EqualChanced[i].spawned = Spawn1Object(EqualChanced[i].guid); + } +} + +// Method that is actualy doing the spawn job on 1 creature +template <> +bool PoolGroup<Creature>::Spawn1Object(uint32 guid) +{ + CreatureData const* data = objmgr.GetCreatureData(guid); + if (data) + { + objmgr.AddCreatureToGrid(guid, data); + + // Spawn if necessary (loaded grids only) + Map* map = const_cast<Map*>(MapManager::Instance().GetBaseMap(data->mapid)); + // We use spawn coords to spawn + if (!map->Instanceable() && !map->IsRemovalGrid(data->posX, data->posY)) + { + Creature* pCreature = new Creature; + //sLog.outDebug("Spawning creature %u",guid); + if (!pCreature->LoadFromDB(guid, map)) + { + delete pCreature; + } + else + { + map->Add(pCreature); + } + } + return true; + } + return false; +} + +// Same for 1 gameobject +template <> +bool PoolGroup<GameObject>::Spawn1Object(uint32 guid) +{ + GameObjectData const* data = objmgr.GetGOData(guid); + if (data) + { + objmgr.AddGameobjectToGrid(guid, data); + // Spawn if necessary (loaded grids only) + // this base map checked as non-instanced and then only existed + Map* map = const_cast<Map*>(MapManager::Instance().GetBaseMap(data->mapid)); + // We use current coords to unspawn, not spawn coords since creature can have changed grid + if (!map->Instanceable() && !map->IsRemovalGrid(data->posX, data->posY)) + { + GameObject* pGameobject = new GameObject; + //sLog.outDebug("Spawning gameobject %u", guid); + if (!pGameobject->LoadFromDB(guid, map)) + { + delete pGameobject; + } + else + { + if (pGameobject->isSpawnedByDefault()) + map->Add(pGameobject); + } + } + return true; + } + return false; +} + +// Same for 1 pool +template <> +bool PoolGroup<Pool>::Spawn1Object(uint32 child_pool_id) +{ + poolhandler.SpawnPool(child_pool_id); + return true; +} + +// Method that does the respawn job on the specified creature +template <> +bool PoolGroup<Creature>::ReSpawn1Object(uint32 guid) +{ + CreatureData const* data = objmgr.GetCreatureData(guid); + if (data) + { + if (Creature* pCreature = ObjectAccessor::Instance().GetObjectInWorld(MAKE_NEW_GUID(guid, data->id, HIGHGUID_UNIT), (Creature*)NULL)) + pCreature->GetMap()->Add(pCreature); + return true; + } + return false; +} + +// Same for 1 gameobject +template <> +bool PoolGroup<GameObject>::ReSpawn1Object(uint32 guid) +{ + GameObjectData const* data = objmgr.GetGOData(guid); + if (data) + { + if (GameObject* pGameobject = ObjectAccessor::Instance().GetObjectInWorld(MAKE_NEW_GUID(guid, data->id, HIGHGUID_GAMEOBJECT), (GameObject*)NULL)) + pGameobject->GetMap()->Add(pGameobject); + return true; + } + return false; +} + +// Nothing to do for a child Pool +template <> +bool PoolGroup<Pool>::ReSpawn1Object(uint32 guid) +{ + return true; +} + + +//////////////////////////////////////////////////////////// +// Methods of class PoolHandler + +PoolHandler::PoolHandler() +{ + isSystemInit = false; +} + +void PoolHandler::LoadFromDB() +{ + QueryResult *result = WorldDatabase.Query("SELECT MAX(entry) FROM pool_template"); + if (!result) + { + sLog.outString(">> Table pool_template is empty."); + sLog.outString(); + return; + } + else + { + Field *fields = result->Fetch(); + max_pool_id = fields[0].GetUInt16(); + delete result; + } + + mPoolTemplate.resize(max_pool_id + 1); + + result = WorldDatabase.Query("SELECT entry,max_limit FROM pool_template"); + if (!result) + { + mPoolTemplate.clear(); + sLog.outString(">> Table pool_template is empty:"); + sLog.outString(); + return; + } + + uint32 count = 0; + + barGoLink bar(result->GetRowCount()); + do + { + ++count; + Field *fields = result->Fetch(); + + bar.step(); + + uint16 pool_id = fields[0].GetUInt16(); + + PoolTemplateData& pPoolTemplate = mPoolTemplate[pool_id]; + pPoolTemplate.MaxLimit = fields[1].GetUInt32(); + + } while (result->NextRow()); + + sLog.outString(); + sLog.outString( ">> Loaded %u objects pools", count ); + delete result; + + // Creatures + + mPoolCreatureGroups.resize(max_pool_id + 1); + mCreatureSearchMap.clear(); + // 1 2 3 + result = WorldDatabase.Query("SELECT guid, pool_entry, chance FROM pool_creature"); + + count = 0; + if (!result) + { + barGoLink bar2(1); + bar2.step(); + + sLog.outString(); + sLog.outString(">> Loaded %u creatures in pools", count ); + } + else + { + + barGoLink bar2(result->GetRowCount()); + do + { + Field *fields = result->Fetch(); + + bar2.step(); + + uint32 guid = fields[0].GetUInt32(); + uint16 pool_id = fields[1].GetUInt16(); + float chance = fields[2].GetFloat(); + + CreatureData const* data = objmgr.GetCreatureData(guid); + if (!data) + { + sLog.outErrorDb("`pool_creature` has a non existing creature spawn (GUID: %u) defined for pool id (%u), skipped.", guid, pool_id ); + continue; + } + if (pool_id > max_pool_id) + { + sLog.outErrorDb("`pool_creature` pool id (%i) is out of range compared to max pool id in `pool_template`, skipped.",pool_id); + continue; + } + if (chance < 0 || chance > 100) + { + sLog.outErrorDb("`pool_creature` has an invalid chance (%f) for creature guid (%u) in pool id (%i), skipped.", chance, guid, pool_id); + continue; + } + PoolTemplateData *pPoolTemplate = &mPoolTemplate[pool_id]; + ++count; + + PoolObject plObject = PoolObject(guid, chance); + PoolGroup<Creature>& cregroup = mPoolCreatureGroups[pool_id]; + cregroup.AddEntry(plObject, pPoolTemplate->MaxLimit); + SearchPair p(guid, pool_id); + mCreatureSearchMap.insert(p); + + } while (result->NextRow()); + sLog.outString(); + sLog.outString( ">> Loaded %u creatures in pools", count ); + delete result; + } + + // Gameobjects + + mPoolGameobjectGroups.resize(max_pool_id + 1); + mGameobjectSearchMap.clear(); + // 1 2 3 + result = WorldDatabase.Query("SELECT guid, pool_entry, chance FROM pool_gameobject"); + + count = 0; + if (!result) + { + barGoLink bar2(1); + bar2.step(); + + sLog.outString(); + sLog.outString(">> Loaded %u gameobject in pools", count ); + } + else + { + + barGoLink bar2(result->GetRowCount()); + do + { + Field *fields = result->Fetch(); + + bar2.step(); + + uint32 guid = fields[0].GetUInt32(); + uint16 pool_id = fields[1].GetUInt16(); + float chance = fields[2].GetFloat(); + + GameObjectData const* data = objmgr.GetGOData(guid); + if (!data) + { + sLog.outErrorDb("`pool_gameobject` has a non existing gameobject spawn (GUID: %u) defined for pool id (%u), skipped.", guid, pool_id ); + continue; + } + GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(data->id); + if (goinfo->type != GAMEOBJECT_TYPE_CHEST && + goinfo->type != GAMEOBJECT_TYPE_GOOBER && + goinfo->type != GAMEOBJECT_TYPE_FISHINGHOLE) + { + sLog.outErrorDb("`pool_gameobject` has a not lootable gameobject spawn (GUID: %u, type: %u) defined for pool id (%u), skipped.", guid, goinfo->type, pool_id ); + continue; + } + if (pool_id > max_pool_id) + { + sLog.outErrorDb("`pool_gameobject` pool id (%i) is out of range compared to max pool id in `pool_template`, skipped.",pool_id); + continue; + } + if (chance < 0 || chance > 100) + { + sLog.outErrorDb("`pool_gameobject` has an invalid chance (%f) for gameobject guid (%u) in pool id (%i), skipped.", chance, guid, pool_id); + continue; + } + PoolTemplateData *pPoolTemplate = &mPoolTemplate[pool_id]; + + ++count; + + PoolObject plObject = PoolObject(guid, chance); + PoolGroup<GameObject>& gogroup = mPoolGameobjectGroups[pool_id]; + gogroup.AddEntry(plObject, pPoolTemplate->MaxLimit); + SearchPair p(guid, pool_id); + mGameobjectSearchMap.insert(p); + + } while( result->NextRow() ); + sLog.outString(); + sLog.outString( ">> Loaded %u gameobject in pools", count ); + delete result; + } + + // Pool of pools + mPoolPoolGroups.resize(max_pool_id + 1); + // 1 2 3 + result = WorldDatabase.Query("SELECT pool_id, mother_pool, chance FROM pool_pool"); + + count = 0; + if( !result ) + { + barGoLink bar2(1); + bar2.step(); + + sLog.outString(); + sLog.outString(">> Loaded %u pools in pools", count ); + } + else + { + + barGoLink bar2( result->GetRowCount() ); + do + { + Field *fields = result->Fetch(); + + bar2.step(); + + uint16 child_pool_id = fields[0].GetUInt16(); + uint16 mother_pool_id = fields[1].GetUInt16(); + float chance = fields[2].GetFloat(); + + if (mother_pool_id > max_pool_id) + { + sLog.outErrorDb("`pool_pool` mother_pool id (%i) is out of range compared to max pool id in `pool_template`, skipped.",mother_pool_id); + continue; + } + if (child_pool_id > max_pool_id) + { + sLog.outErrorDb("`pool_pool` included pool_id (%i) is out of range compared to max pool id in `pool_template`, skipped.",child_pool_id); + continue; + } + if (mother_pool_id == child_pool_id) + { + sLog.outErrorDb("`pool_pool` pool_id (%i) includes itself, dead-lock detected, skipped.",child_pool_id); + continue; + } + if (chance < 0 || chance > 100) + { + sLog.outErrorDb("`pool_pool` has an invalid chance (%f) for pool id (%u) in mother pool id (%i), skipped.", chance, child_pool_id, mother_pool_id); + continue; + } + PoolTemplateData *pPoolTemplateMother = &mPoolTemplate[mother_pool_id]; + + ++count; + + PoolObject plObject = PoolObject(child_pool_id, chance); + PoolGroup<Pool>& plgroup = mPoolPoolGroups[mother_pool_id]; + plgroup.AddEntry(plObject, pPoolTemplateMother->MaxLimit); + SearchPair p(child_pool_id, mother_pool_id); + mPoolSearchMap.insert(p); + + } while( result->NextRow() ); + + // Now check for circular reference + for(uint16 i=0; i<max_pool_id; ++i) + { + std::set<uint16> checkedPools; + for(SearchMap::iterator poolItr = mPoolSearchMap.find(i); poolItr != mPoolSearchMap.end(); poolItr = mPoolSearchMap.find(poolItr->second)) + { + checkedPools.insert(poolItr->first); + if(checkedPools.find(poolItr->second) != checkedPools.end()) + { + std::ostringstream ss; + ss<< "The pool(s) "; + for (std::set<uint16>::const_iterator itr=checkedPools.begin(); itr!=checkedPools.end(); ++itr) + ss << *itr << " "; + ss << "create(s) a circular reference, which can cause the server to freeze.\nRemoving the last link between mother pool " + << poolItr->first << " and child pool " << poolItr->second; + sLog.outErrorDb(ss.str().c_str()); + mPoolPoolGroups[poolItr->second].RemoveOneRelation(poolItr->first); + mPoolSearchMap.erase(poolItr); + --count; + break; + } + } + } + sLog.outString(); + sLog.outString( ">> Loaded %u pools in mother pools", count ); + delete result; + } +} + +// The initialize method will spawn all pools not in an event and not in another pool, this is why there is 2 left joins with 2 null checks +void PoolHandler::Initialize() +{ + QueryResult *result = WorldDatabase.Query("SELECT DISTINCT pool_template.entry FROM pool_template LEFT JOIN game_event_pool ON pool_template.entry=game_event_pool.pool_entry LEFT JOIN pool_pool ON pool_template.entry=pool_pool.pool_id WHERE game_event_pool.pool_entry IS NULL AND pool_pool.pool_id IS NULL"); + uint32 count=0; + if (result) + { + do + { + Field *fields = result->Fetch(); + uint16 pool_entry = fields[0].GetUInt16(); + if (!CheckPool(pool_entry)) + { + sLog.outErrorDb("Pool Id (%u) has all creatures or gameobjects with explicit chance sum <>100 and no equal chance defined. The pool system cannot pick one to spawn.", pool_entry); + continue; + } + SpawnPool(pool_entry); + count++; + } while (result->NextRow()); + } + + sLog.outBasic("Pool handling system initialized, %u pools spawned.", count); + isSystemInit = true; +} + +// Call to spawn a pool, if cache if true the method will spawn only if cached entry is different +// If it's same, the gameobject/creature is respawned only (added back to map) +void PoolHandler::SpawnPool(uint16 pool_id, bool cache) +{ + if (!mPoolPoolGroups[pool_id].isEmpty()) + mPoolPoolGroups[pool_id].SpawnObject(mPoolTemplate[pool_id].MaxLimit, cache); + if (!mPoolGameobjectGroups[pool_id].isEmpty()) + mPoolGameobjectGroups[pool_id].SpawnObject(mPoolTemplate[pool_id].MaxLimit, cache); + if (!mPoolCreatureGroups[pool_id].isEmpty()) + mPoolCreatureGroups[pool_id].SpawnObject(mPoolTemplate[pool_id].MaxLimit, cache); +} + +// Call to despawn a pool, all gameobjects/creatures in this pool are removed +void PoolHandler::DespawnPool(uint16 pool_id) +{ + if (!mPoolPoolGroups[pool_id].isEmpty()) + mPoolPoolGroups[pool_id].DespawnObject(); + if (!mPoolGameobjectGroups[pool_id].isEmpty()) + mPoolGameobjectGroups[pool_id].DespawnObject(); + if (!mPoolCreatureGroups[pool_id].isEmpty()) + mPoolCreatureGroups[pool_id].DespawnObject(); +} + +// Call to update the pool when a gameobject/creature part of pool [pool_id] is ready to respawn +// Here we cache only the creature/gameobject whose guid is passed as parameter +// Then the spawn pool call will use this cache to decide +void PoolHandler::UpdatePool(uint16 pool_id, uint32 guid, uint32 type) +{ + uint16 motherpoolid = IsPartOfAPool(pool_id, 0); + + if (motherpoolid) + mPoolPoolGroups[motherpoolid].DespawnObject(pool_id); + else if (type == TYPEID_GAMEOBJECT && !mPoolGameobjectGroups[pool_id].isEmpty()) + mPoolGameobjectGroups[pool_id].DespawnObject(guid); + else if (type != TYPEID_GAMEOBJECT && !mPoolCreatureGroups[pool_id].isEmpty()) + mPoolCreatureGroups[pool_id].DespawnObject(guid); + + if (motherpoolid) + SpawnPool(motherpoolid, true); + else + SpawnPool(pool_id, true); +} + +// Method that tell if the gameobject/creature is part of a pool and return the pool id if yes +uint16 PoolHandler::IsPartOfAPool(uint32 guid, uint32 type) +{ + if (type == 0) // pool of pool + { + SearchMap::const_iterator itr = mPoolSearchMap.find(guid); + if (itr != mPoolSearchMap.end()) + return itr->second; + } + else if (type == TYPEID_GAMEOBJECT) + { + SearchMap::const_iterator itr = mGameobjectSearchMap.find(guid); + if (itr != mGameobjectSearchMap.end()) + return itr->second; + } + else // creature + { + SearchMap::const_iterator itr = mCreatureSearchMap.find(guid); + if (itr != mCreatureSearchMap.end()) + return itr->second; + } + return 0; +} + +// Method that check chance integrity of the creatures and gameobjects in this pool +bool PoolHandler::CheckPool(uint16 pool_id) +{ + return pool_id <= max_pool_id && + mPoolGameobjectGroups[pool_id].CheckPool() && + mPoolCreatureGroups[pool_id].CheckPool() && + mPoolPoolGroups[pool_id].CheckPool(); +} + +// Method that tell if a creature or gameobject in pool_id is spawned currently +bool PoolHandler::IsSpawnedObject(uint16 pool_id, uint32 guid, uint32 type) +{ + if (pool_id > max_pool_id) + return false; + if (type == 0) + return mPoolPoolGroups[pool_id].IsSpawnedObject(guid); + else if (type == TYPEID_GAMEOBJECT) + return mPoolGameobjectGroups[pool_id].IsSpawnedObject(guid); + else + return mPoolCreatureGroups[pool_id].IsSpawnedObject(guid); +} diff --git a/src/game/PoolHandler.h b/src/game/PoolHandler.h new file mode 100644 index 00000000000..35e18c2d58c --- /dev/null +++ b/src/game/PoolHandler.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_POOLHANDLER_H +#define MANGOS_POOLHANDLER_H + +#include "Platform/Define.h" +#include "Policies/Singleton.h" +#include "Creature.h" +#include "GameObject.h" + +struct PoolTemplateData +{ + uint32 MaxLimit; +}; + +struct PoolObject +{ + uint32 guid; + float chance; + bool spawned; + PoolObject(uint32 _guid, float _chance): guid(_guid), chance(fabs(_chance)), spawned(false) {} +}; + +template <class T> +class PoolGroup +{ + public: + PoolGroup(); + ~PoolGroup() {}; + bool isEmpty() { return ExplicitlyChanced.size()==0 && EqualChanced.size()==0; } + void AddEntry(PoolObject& poolitem, uint32 maxentries); + bool CheckPool(void); + uint32 RollOne(void); + bool IsSpawnedObject(uint32 guid); + void DespawnObject(uint32 guid=0); + void Despawn1Object(uint32 guid); + void SpawnObject(uint32 limit, bool cache=false); + bool Spawn1Object(uint32 guid); + bool ReSpawn1Object(uint32 guid); + void RemoveOneRelation(uint16 child_pool_id); + private: + typedef std::vector<PoolObject> PoolObjectList; + uint32 CacheValue; // Store the guid of the removed creature/gameobject during a pool update + PoolObjectList ExplicitlyChanced; + PoolObjectList EqualChanced; + uint32 Spawned; // Used to know the number of spawned objects +}; + +class Pool // for Pool of Pool case +{ +}; + +class PoolHandler +{ + public: + PoolHandler(); + ~PoolHandler() {}; + void LoadFromDB(); + uint16 IsPartOfAPool(uint32 guid, uint32 type); + bool IsSpawnedObject(uint16 pool_id, uint32 guid, uint32 type); + bool CheckPool(uint16 pool_id); + void SpawnPool(uint16 pool_id, bool cache=false); + void DespawnPool(uint16 pool_id); + void UpdatePool(uint16 pool_id, uint32 guid, uint32 type); + void Initialize(); + + protected: + bool isSystemInit; + uint16 max_pool_id; + typedef std::vector<PoolTemplateData> PoolTemplateDataMap; + typedef std::vector<PoolGroup<Creature> > PoolGroupCreatureMap; + typedef std::vector<PoolGroup<GameObject> > PoolGroupGameObjectMap; + typedef std::vector<PoolGroup<Pool> > PoolGroupPoolMap; + typedef std::pair<uint32, uint16> SearchPair; + typedef std::map<uint32, uint16> SearchMap; + + PoolTemplateDataMap mPoolTemplate; + PoolGroupCreatureMap mPoolCreatureGroups; + PoolGroupGameObjectMap mPoolGameobjectGroups; + PoolGroupPoolMap mPoolPoolGroups; + SearchMap mCreatureSearchMap; + SearchMap mGameobjectSearchMap; + SearchMap mPoolSearchMap; + +}; + +#define poolhandler MaNGOS::Singleton<PoolHandler>::Instance() +#endif diff --git a/src/game/PossessedAI.cpp b/src/game/PossessedAI.cpp index bda0de88882..8fd2e5ca014 100644 --- a/src/game/PossessedAI.cpp +++ b/src/game/PossessedAI.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * - * Thanks to the original authors: MaNGOS <http://www.mangosproject.org/> + * Thanks to the original authors: MaNGOS <http://getmangos.com/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/PossessedAI.h b/src/game/PossessedAI.h index bbfacb6454c..bef7853246e 100644 --- a/src/game/PossessedAI.h +++ b/src/game/PossessedAI.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * - * Thanks to the original authors: MaNGOS <http://www.mangosproject.org/> + * Thanks to the original authors: MaNGOS <http://getmangos.com/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/QueryHandler.cpp b/src/game/QueryHandler.cpp index f7777367771..b27620e7857 100644 --- a/src/game/QueryHandler.cpp +++ b/src/game/QueryHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,8 +31,8 @@ #include "Player.h" #include "UpdateMask.h" #include "NPCHandler.h" -#include "ObjectAccessor.h" #include "Pet.h" +#include "MapManager.h" void WorldSession::SendNameQueryOpcode(Player *p) { @@ -102,20 +102,20 @@ void WorldSession::SendNameQueryOpcodeFromDBCallBack(QueryResult *result, uint32 WorldPacket data( SMSG_NAME_QUERY_RESPONSE, (8+1+4+4+4+10) ); data << MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER); data << name; - data << (uint8)0; - data << (uint32)(field & 0xFF); - data << (uint32)((field >> 16) & 0xFF); - data << (uint32)((field >> 8) & 0xFF); + data << uint8(0); + data << uint32(field & 0xFF); + data << uint32((field >> 16) & 0xFF); + data << uint32((field >> 8) & 0xFF); // if the first declined name field (3) is empty, the rest must be too if(sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) && fields[3].GetCppString() != "") { - data << (uint8)1; // is declined + data << uint8(1); // is declined for(int i = 3; i < MAX_DECLINED_NAME_CASES+3; ++i) data << fields[i].GetCppString(); } else - data << (uint8)0; // is declined + data << uint8(0); // is declined session->SendPacket( &data ); delete result; @@ -123,7 +123,7 @@ void WorldSession::SendNameQueryOpcodeFromDBCallBack(QueryResult *result, uint32 void WorldSession::HandleNameQueryOpcode( WorldPacket & recv_data ) { - CHECK_PACKET_SIZE(recv_data,8); + CHECK_PACKET_SIZE(recv_data, 8); uint64 guid; @@ -176,24 +176,23 @@ void WorldSession::HandleCreatureQueryOpcode( WorldPacket & recv_data ) sLog.outDetail("WORLD: CMSG_CREATURE_QUERY '%s' - Entry: %u.", ci->Name, entry); // guess size WorldPacket data( SMSG_CREATURE_QUERY_RESPONSE, 100 ); - data << (uint32)entry; // creature entry + data << uint32(entry); // creature entry data << Name; data << uint8(0) << uint8(0) << uint8(0); // name2, name3, name4, always empty data << SubName; data << ci->IconName; // "Directions" for guard, string for Icons 2.3.0 - data << (uint32)ci->type_flags; // flags wdbFeild7=wad flags1 - data << (uint32)ci->type; - data << (uint32)ci->family; // family wdbFeild9 - data << (uint32)ci->rank; // rank wdbFeild10 - data << (uint32)0; // unknown wdbFeild11 - data << (uint32)ci->PetSpellDataId; // Id from CreatureSpellData.dbc wdbField12 - data << (uint32)ci->Modelid1; // Modelid1 - data << (uint32)ci->Modelid2; // Modelid2 - data << (uint32)ci->Modelid3; // Modelid3 - data << (uint32)ci->Modelid4; // Modelid4 - data << (float)1.0f; // unk - data << (float)1.0f; // unk - data << (uint8)ci->RacialLeader; + data << uint32(ci->type_flags); // flags wdbFeild7=wad flags1 + data << uint32(ci->type); + data << uint32(ci->family); // family wdbFeild9 + data << uint32(ci->rank); // rank wdbFeild10 + data << uint32(ci->PetSpellDataId); // Id from CreatureSpellData.dbc wdbField12 + data << uint32(ci->Modelid1); // modelid_male1 + data << uint32(ci->Modelid2); // modelid_female1 ? + data << uint32(ci->Modelid3); // modelid_male2 ? + data << uint32(ci->Modelid4); // modelid_femmale2 ? + data << float(ci->unk16); // unk + data << float(ci->unk17); // unk + data << uint8(ci->RacialLeader); SendPacket( &data ); sLog.outDebug( "WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE " ); } @@ -207,7 +206,7 @@ void WorldSession::HandleCreatureQueryOpcode( WorldPacket & recv_data ) WorldPacket data( SMSG_CREATURE_QUERY_RESPONSE, 4 ); data << uint32(entry | 0x80000000); SendPacket( &data ); - sLog.outDebug( "WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE " ); + sLog.outDebug( "WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE " ); } } @@ -224,9 +223,11 @@ void WorldSession::HandleGameObjectQueryOpcode( WorldPacket & recv_data ) { std::string Name; + std::string IconName; std::string CastBarCaption; Name = info->name; + IconName = info->IconName; CastBarCaption = info->castBarCaption; int loc_idx = GetSessionDbLocaleIndex(); @@ -243,15 +244,16 @@ void WorldSession::HandleGameObjectQueryOpcode( WorldPacket & recv_data ) } sLog.outDetail("WORLD: CMSG_GAMEOBJECT_QUERY '%s' - Entry: %u. ", info->name, entryID); WorldPacket data ( SMSG_GAMEOBJECT_QUERY_RESPONSE, 150 ); - data << entryID; - data << (uint32)info->type; - data << (uint32)info->displayId; + data << uint32(entryID); + data << uint32(info->type); + data << uint32(info->displayId); data << Name; data << uint8(0) << uint8(0) << uint8(0); // name2, name3, name4 - data << uint8(0); // 2.0.3, string + data << IconName; // 2.0.3, string. Icon name to use instead of default icon for go's (ex: "Attack" makes sword) data << CastBarCaption; // 2.0.3, string. Text will appear in Cast Bar when using GO (ex: "Collecting") - data << uint8(0); // 2.0.3, probably string - data.append(info->raw.data,24); + data << uint8(0); // 2.0.3, string + data.append(info->raw.data, 24); + data << float(info->size); // go size SendPacket( &data ); sLog.outDebug( "WORLD: Sent CMSG_GAMEOBJECT_QUERY " ); } @@ -276,20 +278,43 @@ void WorldSession::HandleCorpseQueryOpcode(WorldPacket & /*recv_data*/) Corpse *corpse = GetPlayer()->GetCorpse(); - uint8 found = 1; if(!corpse) - found = 0; + { + WorldPacket data(MSG_CORPSE_QUERY, 1); + data << uint8(0); // corpse not found + SendPacket(&data); + return; + } - WorldPacket data(MSG_CORPSE_QUERY, (1+found*(5*4))); - data << uint8(found); - if(found) + int32 mapid = corpse->GetMapId(); + float x = corpse->GetPositionX(); + float y = corpse->GetPositionY(); + float z = corpse->GetPositionZ(); + int32 corpsemapid = _player->GetMapId(); + + if(Map *map = MapManager::Instance().FindMap(corpse->GetMapId(), corpse->GetInstanceId())) { - data << corpse->GetMapId(); - data << corpse->GetPositionX(); - data << corpse->GetPositionY(); - data << corpse->GetPositionZ(); - data << _player->GetMapId(); + if(map->IsDungeon()) + { + if(!map->GetEntrancePos(mapid, x, y)) + return; + + Map *entrance_map = MapManager::Instance().GetMap(mapid, _player); + if(!entrance_map) + return; + + z = entrance_map->GetHeight(x, y, MAX_HEIGHT); + corpsemapid = corpse->GetMapId(); + } } + + WorldPacket data(MSG_CORPSE_QUERY, 1+(5*4)); + data << uint8(1); // corpse found + data << int32(mapid); + data << float(x); + data << float(y); + data << float(z); + data << int32(corpsemapid); SendPacket(&data); } @@ -299,8 +324,6 @@ void WorldSession::HandleNpcTextQueryOpcode( WorldPacket & recv_data ) uint32 textID; uint64 guid; - GossipText *pGossip; - std::string GossipStr; recv_data >> textID; sLog.outDetail("WORLD: CMSG_NPC_TEXT_QUERY ID '%u'", textID); @@ -308,7 +331,7 @@ void WorldSession::HandleNpcTextQueryOpcode( WorldPacket & recv_data ) recv_data >> guid; GetPlayer()->SetUInt64Value(UNIT_FIELD_TARGET, guid); - pGossip = objmgr.GetGossipText(textID); + GossipText const* pGossip = objmgr.GetGossipText(textID); WorldPacket data( SMSG_NPC_TEXT_UPDATE, 100 ); // guess size data << textID; @@ -370,14 +393,11 @@ void WorldSession::HandleNpcTextQueryOpcode( WorldPacket & recv_data ) data << pGossip->Options[i].Language; - data << pGossip->Options[i].Emotes[0]._Delay; - data << pGossip->Options[i].Emotes[0]._Emote; - - data << pGossip->Options[i].Emotes[1]._Delay; - data << pGossip->Options[i].Emotes[1]._Emote; - - data << pGossip->Options[i].Emotes[2]._Delay; - data << pGossip->Options[i].Emotes[2]._Emote; + for(int j = 0; j < 3; ++j) + { + data << pGossip->Options[i].Emotes[j]._Delay; + data << pGossip->Options[i].Emotes[j]._Emote; + } } } diff --git a/src/game/QuestDef.cpp b/src/game/QuestDef.cpp index 3e190d67220..799896c8280 100644 --- a/src/game/QuestDef.cpp +++ b/src/game/QuestDef.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -44,88 +44,87 @@ Quest::Quest(Field * questRecord) QuestFlags = questRecord[17].GetUInt16(); uint32 SpecialFlags = questRecord[18].GetUInt16(); CharTitleId = questRecord[19].GetUInt32(); - PrevQuestId = questRecord[20].GetInt32(); - NextQuestId = questRecord[21].GetInt32(); - ExclusiveGroup = questRecord[22].GetInt32(); - NextQuestInChain = questRecord[23].GetUInt32(); - SrcItemId = questRecord[24].GetUInt32(); - SrcItemCount = questRecord[25].GetUInt32(); - SrcSpell = questRecord[26].GetUInt32(); - Title = questRecord[27].GetCppString(); - Details = questRecord[28].GetCppString(); - Objectives = questRecord[29].GetCppString(); - OfferRewardText = questRecord[30].GetCppString(); - RequestItemsText = questRecord[31].GetCppString(); - EndText = questRecord[32].GetCppString(); + PlayersSlain = questRecord[20].GetUInt32(); + BonusTalents = questRecord[21].GetUInt32(); + PrevQuestId = questRecord[22].GetInt32(); + NextQuestId = questRecord[23].GetInt32(); + ExclusiveGroup = questRecord[24].GetInt32(); + NextQuestInChain = questRecord[25].GetUInt32(); + SrcItemId = questRecord[26].GetUInt32(); + SrcItemCount = questRecord[27].GetUInt32(); + SrcSpell = questRecord[28].GetUInt32(); + Title = questRecord[29].GetCppString(); + Details = questRecord[30].GetCppString(); + Objectives = questRecord[31].GetCppString(); + OfferRewardText = questRecord[32].GetCppString(); + RequestItemsText = questRecord[33].GetCppString(); + EndText = questRecord[34].GetCppString(); for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) - ObjectiveText[i] = questRecord[33+i].GetCppString(); + ObjectiveText[i] = questRecord[35+i].GetCppString(); for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) - ReqItemId[i] = questRecord[37+i].GetUInt32(); + ReqItemId[i] = questRecord[39+i].GetUInt32(); for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) - ReqItemCount[i] = questRecord[41+i].GetUInt32(); + ReqItemCount[i] = questRecord[43+i].GetUInt32(); for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i) - ReqSourceId[i] = questRecord[45+i].GetUInt32(); + ReqSourceId[i] = questRecord[47+i].GetUInt32(); for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i) - ReqSourceCount[i] = questRecord[49+i].GetUInt32(); - - for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i) - ReqSourceRef[i] = questRecord[53+i].GetUInt32(); + ReqSourceCount[i] = questRecord[51+i].GetUInt32(); for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) - ReqCreatureOrGOId[i] = questRecord[57+i].GetInt32(); + ReqCreatureOrGOId[i] = questRecord[55+i].GetInt32(); for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) - ReqCreatureOrGOCount[i] = questRecord[61+i].GetUInt32(); + ReqCreatureOrGOCount[i] = questRecord[59+i].GetUInt32(); for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) - ReqSpell[i] = questRecord[65+i].GetUInt32(); + ReqSpell[i] = questRecord[63+i].GetUInt32(); for (int i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i) - RewChoiceItemId[i] = questRecord[69+i].GetUInt32(); + RewChoiceItemId[i] = questRecord[67+i].GetUInt32(); for (int i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i) - RewChoiceItemCount[i] = questRecord[75+i].GetUInt32(); + RewChoiceItemCount[i] = questRecord[73+i].GetUInt32(); for (int i = 0; i < QUEST_REWARDS_COUNT; ++i) - RewItemId[i] = questRecord[81+i].GetUInt32(); + RewItemId[i] = questRecord[79+i].GetUInt32(); for (int i = 0; i < QUEST_REWARDS_COUNT; ++i) - RewItemCount[i] = questRecord[85+i].GetUInt32(); + RewItemCount[i] = questRecord[83+i].GetUInt32(); for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i) - RewRepFaction[i] = questRecord[89+i].GetUInt32(); + RewRepFaction[i] = questRecord[87+i].GetUInt32(); for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i) - RewRepValue[i] = questRecord[94+i].GetInt32(); - - RewHonorableKills = questRecord[99].GetUInt32(); - RewOrReqMoney = questRecord[100].GetInt32(); - RewMoneyMaxLevel = questRecord[101].GetUInt32(); - RewSpell = questRecord[102].GetUInt32(); - RewSpellCast = questRecord[103].GetUInt32(); - RewMailTemplateId = questRecord[104].GetUInt32(); - RewMailDelaySecs = questRecord[105].GetUInt32(); - PointMapId = questRecord[106].GetUInt32(); - PointX = questRecord[107].GetFloat(); - PointY = questRecord[108].GetFloat(); - PointOpt = questRecord[109].GetUInt32(); + RewRepValue[i] = questRecord[92+i].GetInt32(); + + RewHonorableKills = questRecord[97].GetUInt32(); + RewOrReqMoney = questRecord[98].GetInt32(); + RewMoneyMaxLevel = questRecord[99].GetUInt32(); + RewSpell = questRecord[100].GetUInt32(); + RewSpellCast = questRecord[101].GetUInt32(); + RewMailTemplateId = questRecord[102].GetUInt32(); + RewMailDelaySecs = questRecord[103].GetUInt32(); + PointMapId = questRecord[104].GetUInt32(); + PointX = questRecord[105].GetFloat(); + PointY = questRecord[106].GetFloat(); + PointOpt = questRecord[107].GetUInt32(); for (int i = 0; i < QUEST_EMOTE_COUNT; ++i) - DetailsEmote[i] = questRecord[110+i].GetUInt32(); + DetailsEmote[i] = questRecord[108+i].GetUInt32(); - IncompleteEmote = questRecord[114].GetUInt32(); - CompleteEmote = questRecord[115].GetUInt32(); + IncompleteEmote = questRecord[112].GetUInt32(); + CompleteEmote = questRecord[113].GetUInt32(); for (int i = 0; i < QUEST_EMOTE_COUNT; ++i) - OfferRewardEmote[i] = questRecord[116+i].GetInt32(); + OfferRewardEmote[i] = questRecord[114+i].GetInt32(); - QuestStartScript = questRecord[120].GetUInt32(); - QuestCompleteScript = questRecord[121].GetUInt32(); + QuestStartScript = questRecord[118].GetUInt32(); + QuestCompleteScript = questRecord[119].GetUInt32(); QuestFlags |= SpecialFlags << 16; @@ -164,18 +163,20 @@ uint32 Quest::XPValue( Player *pPlayer ) const uint32 pLevel = pPlayer->getLevel(); uint32 qLevel = QuestLevel; float fullxp = 0; - if (qLevel >= 65) + if (qLevel >= 15) fullxp = RewMoneyMaxLevel / 6.0f; - else if (qLevel == 64) + else if (qLevel == 14) fullxp = RewMoneyMaxLevel / 4.8f; - else if (qLevel == 63) - fullxp = RewMoneyMaxLevel / 3.6f; - else if (qLevel == 62) + else if (qLevel == 13) + fullxp = RewMoneyMaxLevel / 3.666f; + else if (qLevel == 12) fullxp = RewMoneyMaxLevel / 2.4f; - else if (qLevel == 61) + else if (qLevel == 11) fullxp = RewMoneyMaxLevel / 1.2f; - else if (qLevel > 0 && qLevel <= 60) + else if (qLevel >= 1 && qLevel <= 10) fullxp = RewMoneyMaxLevel / 0.6f; + else if (qLevel == 0) + fullxp = RewMoneyMaxLevel; if( pLevel <= qLevel + 5 ) return (uint32)fullxp; diff --git a/src/game/QuestDef.h b/src/game/QuestDef.h index 7b447c611c2..242c10bf48f 100644 --- a/src/game/QuestDef.h +++ b/src/game/QuestDef.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -44,30 +44,33 @@ class ObjectMgr; enum QuestFailedReasons { INVALIDREASON_DONT_HAVE_REQ = 0, - INVALIDREASON_QUEST_FAILED_LOW_LEVEL = 1, //You are not high enough level for that quest. - INVALIDREASON_QUEST_FAILED_WRONG_RACE = 6, //That quest is not available to your race. - INVALIDREASON_QUEST_ALREADY_DONE = 7, //You have completed that quest. - INVALIDREASON_QUEST_ONLY_ONE_TIMED = 12, //You can only be on one timed quest at a time. - INVALIDREASON_QUEST_ALREADY_ON = 13, //You are already on that quest - INVALIDREASON_QUEST_FAILED_EXPANSION = 16, //This quest requires an expansion enabled account. - INVALIDREASON_QUEST_ALREADY_ON2 = 18, //You are already on that quest - INVALIDREASON_QUEST_FAILED_MISSING_ITEMS = 21, //You don't have the required items with you. Check storage. - INVALIDREASON_QUEST_FAILED_NOT_ENOUGH_MONEY = 23, //You don't have enough money for that quest. - INVALIDREASON_DAILY_QUESTS_REMAINING = 26, //You have already completed 10 daily quests today - INVALIDREASON_QUEST_FAILED_CAIS = 27, //You cannot complete quests once you have reached tired time + INVALIDREASON_QUEST_FAILED_LOW_LEVEL = 1, // You are not high enough level for that quest. + INVALIDREASON_QUEST_FAILED_WRONG_RACE = 6, // That quest is not available to your race. + INVALIDREASON_QUEST_ALREADY_DONE = 7, // You have completed that quest. + INVALIDREASON_QUEST_ONLY_ONE_TIMED = 12, // You can only be on one timed quest at a time. + INVALIDREASON_QUEST_ALREADY_ON = 13, // You are already on that quest. + INVALIDREASON_QUEST_FAILED_EXPANSION = 16, // This quest requires an expansion enabled account. + INVALIDREASON_QUEST_ALREADY_ON2 = 18, // You are already on that quest. + INVALIDREASON_QUEST_FAILED_MISSING_ITEMS = 21, // You don't have the required items with you. Check storage. + INVALIDREASON_QUEST_FAILED_NOT_ENOUGH_MONEY = 23, // You don't have enough money for that quest. + INVALIDREASON_DAILY_QUESTS_REMAINING = 26, // You have already completed 25 daily quests today. + INVALIDREASON_QUEST_FAILED_CAIS = 27, // You cannot complete quests once you have reached tired time. + INVALIDREASON_DAILY_QUEST_COMPLETED_TODAY = 29 // You have completed that daily quest today. }; enum QuestShareMessages { - QUEST_PARTY_MSG_SHARING_QUEST = 0, - QUEST_PARTY_MSG_CANT_TAKE_QUEST = 1, - QUEST_PARTY_MSG_ACCEPT_QUEST = 2, - QUEST_PARTY_MSG_REFUSE_QUEST = 3, - QUEST_PARTY_MSG_TOO_FAR = 4, - QUEST_PARTY_MSG_BUSY = 5, - QUEST_PARTY_MSG_LOG_FULL = 6, - QUEST_PARTY_MSG_HAVE_QUEST = 7, - QUEST_PARTY_MSG_FINISH_QUEST = 8, + QUEST_PARTY_MSG_SHARING_QUEST = 0, + QUEST_PARTY_MSG_CANT_TAKE_QUEST = 1, + QUEST_PARTY_MSG_ACCEPT_QUEST = 2, + QUEST_PARTY_MSG_DECLINE_QUEST = 3, + QUEST_PARTY_MSG_BUSY = 4, + QUEST_PARTY_MSG_LOG_FULL = 5, + QUEST_PARTY_MSG_HAVE_QUEST = 6, + QUEST_PARTY_MSG_FINISH_QUEST = 7, + QUEST_PARTY_MSG_CANT_BE_SHARED_TODAY = 8, + QUEST_PARTY_MSG_SHARING_TIMER_EXPIRED = 9, + QUEST_PARTY_MSG_NOT_IN_PARTY = 10 }; enum __QuestTradeSkill @@ -122,7 +125,7 @@ enum __QuestFlags //QUEST_FLAGS_NONE2 = 0x00000010, // Not used currently QUEST_FLAGS_EPIC = 0x00000020, // Not used currently: Unsure of content QUEST_FLAGS_RAID = 0x00000040, // Not used currently - QUEST_FLAGS_TBC = 0x00000080, // Not used currently: Available if TBC expension enabled only + QUEST_FLAGS_TBC = 0x00000080, // Not used currently: Available if TBC expansion enabled only QUEST_FLAGS_UNK2 = 0x00000100, // Not used currently: _DELIVER_MORE Quest needs more than normal _q-item_ drops from mobs QUEST_FLAGS_HIDDEN_REWARDS = 0x00000200, // Items and money rewarded only sent in SMSG_QUESTGIVER_OFFER_REWARD (not in SMSG_QUESTGIVER_QUEST_DETAILS or in client quest log(SMSG_QUEST_QUERY_RESPONSE)) QUEST_FLAGS_AUTO_REWARDED = 0x00000400, // These quests are automatically rewarded on quest complete and they will never appear in quest log client side. @@ -190,6 +193,8 @@ class Quest int32 GetExclusiveGroup() const { return ExclusiveGroup; } uint32 GetNextQuestInChain() const { return NextQuestInChain; } uint32 GetCharTitleId() const { return CharTitleId; } + uint32 GetPlayersSlain() const { return PlayersSlain; } + uint32 GetBonusTalents() const { return BonusTalents; } uint32 GetSrcItemId() const { return SrcItemId; } uint32 GetSrcItemCount() const { return SrcItemCount; } uint32 GetSrcSpell() const { return SrcSpell; } @@ -226,7 +231,6 @@ class Quest uint32 ReqItemCount[QUEST_OBJECTIVES_COUNT]; uint32 ReqSourceId[QUEST_SOURCE_ITEM_IDS_COUNT]; uint32 ReqSourceCount[QUEST_SOURCE_ITEM_IDS_COUNT]; - uint32 ReqSourceRef[QUEST_SOURCE_ITEM_IDS_COUNT]; int32 ReqCreatureOrGOId[QUEST_OBJECTIVES_COUNT]; // >0 Creature <0 Gameobject uint32 ReqCreatureOrGOCount[QUEST_OBJECTIVES_COUNT]; uint32 ReqSpell[QUEST_OBJECTIVES_COUNT]; @@ -277,6 +281,8 @@ class Quest uint32 LimitTime; uint32 QuestFlags; uint32 CharTitleId; + uint32 PlayersSlain; + uint32 BonusTalents; int32 PrevQuestId; int32 NextQuestId; int32 ExclusiveGroup; diff --git a/src/game/QuestHandler.cpp b/src/game/QuestHandler.cpp index c447b170d4c..c7f83c9b7ae 100644 --- a/src/game/QuestHandler.cpp +++ b/src/game/QuestHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -101,7 +101,7 @@ void WorldSession::HandleQuestgiverHelloOpcode( WorldPacket & recv_data ) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); // Stop the npc if moving pCreature->StopMoving(); @@ -180,7 +180,7 @@ void WorldSession::HandleQuestgiverAcceptQuestOpcode( WorldPacket & recv_data ) bool destroyItem = true; for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++) { - if ((qInfo->ReqItemId[i] == ((Item*)pObject)->GetEntry()) && (((Item*)pObject)->GetProto()->MaxCount != 0)) + if ((qInfo->ReqItemId[i] == ((Item*)pObject)->GetEntry()) && (((Item*)pObject)->GetProto()->MaxCount > 0)) { destroyItem = false; break; @@ -451,12 +451,6 @@ void WorldSession::HandleQuestPushToParty(WorldPacket& recvPacket) _player->SendPushToPartyResponse(pPlayer, QUEST_PARTY_MSG_SHARING_QUEST); - if( _player->GetDistance( pPlayer ) > 10 ) - { - _player->SendPushToPartyResponse( pPlayer, QUEST_PARTY_MSG_TOO_FAR ); - continue; - } - if( !pPlayer->SatisfyQuestStatus( pQuest, false ) ) { _player->SendPushToPartyResponse( pPlayer, QUEST_PARTY_MSG_HAVE_QUEST ); @@ -586,7 +580,7 @@ uint32 WorldSession::getDialogStatus(Player *pPlayer, Object* questgiver, uint32 { if ( pQuest->IsAutoComplete() || (pQuest->IsRepeatable() && pPlayer->getQuestStatusMap()[quest_id].m_rewarded)) result2 = DIALOG_STATUS_REWARD_REP; - else if (pPlayer->getLevel() <= pQuest->GetQuestLevel() + sWorld.getConfig(CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF) ) + else if (pPlayer->getLevel() <= pPlayer->GetQuestLevel(pQuest) + sWorld.getConfig(CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF) ) { if (pQuest->HasFlag(QUEST_FLAGS_DAILY)) result2 = DIALOG_STATUS_AVAILABLE_REP; diff --git a/src/game/RandomMovementGenerator.cpp b/src/game/RandomMovementGenerator.cpp index cb748883772..0081c702abd 100644 --- a/src/game/RandomMovementGenerator.cpp +++ b/src/game/RandomMovementGenerator.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,6 +26,7 @@ #include "DestinationHolderImp.h" #include "Map.h" #include "Util.h" +#include "CreatureGroups.h" #define RUNNING_CHANCE_RANDOMMV 20 //will be "1 / RUNNING_CHANCE_RANDOMMV" @@ -114,6 +115,12 @@ RandomMovementGenerator<Creature>::_setRandomLocation(Creature &creature) i_nextMoveTime.Reset(urand(500+i_destinationHolder.GetTotalTravelTime(),5000+i_destinationHolder.GetTotalTravelTime())); creature.SetUnitMovementFlags(MOVEMENTFLAG_WALK_MODE); } + + //Call for creature group update + if(creature.GetFormation() && creature.GetFormation()->getLeader() == &creature) + { + creature.GetFormation()->LeaderMoveTo(nx, ny, nz); + } } template<> diff --git a/src/game/RandomMovementGenerator.h b/src/game/RandomMovementGenerator.h index a7105284ac6..20afe23d375 100644 --- a/src/game/RandomMovementGenerator.h +++ b/src/game/RandomMovementGenerator.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/ReactorAI.cpp b/src/game/ReactorAI.cpp index d15c71b28ef..30847405c9e 100644 --- a/src/game/ReactorAI.cpp +++ b/src/game/ReactorAI.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -50,18 +50,18 @@ ReactorAI::IsVisible(Unit *) const void ReactorAI::UpdateAI(const uint32 /*time_diff*/) { - // update i_victimGuid if i_creature.getVictim() !=0 and changed + // update i_victimGuid if m_creature->getVictim() !=0 and changed if(!UpdateVictim()) return; - i_victimGuid = i_creature.getVictim()->GetGUID(); + i_victimGuid = m_creature->getVictim()->GetGUID(); - if( i_creature.isAttackReady() ) + if( m_creature->isAttackReady() ) { - if( i_creature.IsWithinMeleeRange(i_creature.getVictim())) + if( m_creature->IsWithinMeleeRange(m_creature->getVictim())) { - i_creature.AttackerStateUpdate(i_creature.getVictim()); - i_creature.resetAttackTimer(); + m_creature->AttackerStateUpdate(m_creature->getVictim()); + m_creature->resetAttackTimer(); } } } @@ -69,49 +69,49 @@ ReactorAI::UpdateAI(const uint32 /*time_diff*/) void ReactorAI::EnterEvadeMode() { - if( !i_creature.isAlive() ) + if( !m_creature->isAlive() ) { - DEBUG_LOG("Creature stoped attacking cuz his dead [guid=%u]", i_creature.GetGUIDLow()); - i_creature.GetMotionMaster()->MovementExpired(); - i_creature.GetMotionMaster()->MoveIdle(); + DEBUG_LOG("Creature stoped attacking cuz his dead [guid=%u]", m_creature->GetGUIDLow()); + m_creature->GetMotionMaster()->MovementExpired(); + m_creature->GetMotionMaster()->MoveIdle(); i_victimGuid = 0; - i_creature.CombatStop(); - i_creature.DeleteThreatList(); + m_creature->CombatStop(); + m_creature->DeleteThreatList(); return; } - Unit* victim = ObjectAccessor::GetUnit(i_creature, i_victimGuid ); + Unit* victim = ObjectAccessor::GetUnit(*m_creature, i_victimGuid ); if( !victim ) { - DEBUG_LOG("Creature stopped attacking because victim is non exist [guid=%u]", i_creature.GetGUIDLow()); + DEBUG_LOG("Creature stopped attacking because victim is non exist [guid=%u]", m_creature->GetGUIDLow()); } else if( victim->HasStealthAura() ) { - DEBUG_LOG("Creature stopped attacking cuz his victim is stealth [guid=%u]", i_creature.GetGUIDLow()); + DEBUG_LOG("Creature stopped attacking cuz his victim is stealth [guid=%u]", m_creature->GetGUIDLow()); } else if( victim->isInFlight() ) { - DEBUG_LOG("Creature stopped attacking cuz his victim is fly away [guid=%u]", i_creature.GetGUIDLow()); + DEBUG_LOG("Creature stopped attacking cuz his victim is fly away [guid=%u]", m_creature->GetGUIDLow()); } else { - DEBUG_LOG("Creature stopped attacking due to target %s [guid=%u]", victim->isAlive() ? "out run him" : "is dead", i_creature.GetGUIDLow()); + DEBUG_LOG("Creature stopped attacking due to target %s [guid=%u]", victim->isAlive() ? "out run him" : "is dead", m_creature->GetGUIDLow()); } - i_creature.RemoveAllAuras(); - i_creature.DeleteThreatList(); + m_creature->RemoveAllAuras(); + m_creature->DeleteThreatList(); i_victimGuid = 0; - i_creature.CombatStop(); - i_creature.SetLootRecipient(NULL); + m_creature->CombatStop(); + m_creature->SetLootRecipient(NULL); - if(!i_creature.GetCharmerOrOwner()) + if(!m_creature->GetCharmerOrOwner()) { // Remove TargetedMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead - if( i_creature.GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE ) - i_creature.GetMotionMaster()->MoveTargetedHome(); + if( m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE ) + m_creature->GetMotionMaster()->MoveTargetedHome(); } - else if (i_creature.GetOwner() && i_creature.GetOwner()->isAlive()) - i_creature.GetMotionMaster()->MoveFollow(i_creature.GetOwner(),PET_FOLLOW_DIST,PET_FOLLOW_ANGLE); + else if (m_creature->GetOwner() && m_creature->GetOwner()->isAlive()) + m_creature->GetMotionMaster()->MoveFollow(m_creature->GetOwner(),PET_FOLLOW_DIST,PET_FOLLOW_ANGLE); } diff --git a/src/game/ReactorAI.h b/src/game/ReactorAI.h index a1c99707736..baed6a8e6e1 100644 --- a/src/game/ReactorAI.h +++ b/src/game/ReactorAI.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,7 +29,7 @@ class TRINITY_DLL_DECL ReactorAI : public CreatureAI { public: - ReactorAI(Creature *c) : CreatureAI(c), i_creature(*c), i_victimGuid(0) {} + explicit ReactorAI(Creature *c) : CreatureAI(c), i_victimGuid(0) {} void MoveInLineOfSight(Unit *); void EnterEvadeMode(); @@ -39,7 +39,6 @@ class TRINITY_DLL_DECL ReactorAI : public CreatureAI static int Permissible(const Creature *); private: - Creature &i_creature; uint64 i_victimGuid; }; #endif diff --git a/src/game/ReputationMgr.cpp b/src/game/ReputationMgr.cpp new file mode 100644 index 00000000000..62b9504daff --- /dev/null +++ b/src/game/ReputationMgr.cpp @@ -0,0 +1,473 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ReputationMgr.h" +#include "DBCStores.h" +#include "Player.h" +#include "WorldPacket.h" + +const int32 ReputationMgr::PointsInRank[MAX_REPUTATION_RANK] = {36000, 3000, 3000, 3000, 6000, 12000, 21000, 1000}; + +ReputationRank ReputationMgr::ReputationToRank(int32 standing) +{ + int32 limit = Reputation_Cap + 1; + for (int i = MAX_REPUTATION_RANK-1; i >= MIN_REPUTATION_RANK; --i) + { + limit -= PointsInRank[i]; + if (standing >= limit ) + return ReputationRank(i); + } + return MIN_REPUTATION_RANK; +} + +int32 ReputationMgr::GetReputation(uint32 faction_id) const +{ + FactionEntry const *factionEntry = sFactionStore.LookupEntry(faction_id); + + if (!factionEntry) + { + sLog.outError("ReputationMgr::GetReputation: Can't get reputation of %s for unknown faction (faction id) #%u.",m_player->GetName(), faction_id); + return 0; + } + + return GetReputation(factionEntry); +} + +int32 ReputationMgr::GetBaseReputation(FactionEntry const* factionEntry) const +{ + if (!factionEntry) + return 0; + + uint32 raceMask = m_player->getRaceMask(); + uint32 classMask = m_player->getClassMask(); + for (int i=0; i < 4; i++) + { + if( (factionEntry->BaseRepRaceMask[i] & raceMask) && + (factionEntry->BaseRepClassMask[i]==0 || + (factionEntry->BaseRepClassMask[i] & classMask) ) ) + return factionEntry->BaseRepValue[i]; + } + + // in faction.dbc exist factions with (RepListId >=0, listed in character reputation list) with all BaseRepRaceMask[i]==0 + return 0; +} + +int32 ReputationMgr::GetReputation(FactionEntry const* factionEntry) const +{ + // Faction without recorded reputation. Just ignore. + if(!factionEntry) + return 0; + + if(FactionState const* state = GetState(factionEntry)) + return GetBaseReputation(factionEntry) + state->Standing; + + return 0; +} + +ReputationRank ReputationMgr::GetRank(FactionEntry const* factionEntry) const +{ + int32 reputation = GetReputation(factionEntry); + return ReputationToRank(reputation); +} + +ReputationRank ReputationMgr::GetBaseRank(FactionEntry const* factionEntry) const +{ + int32 reputation = GetBaseReputation(factionEntry); + return ReputationToRank(reputation); +} + +void ReputationMgr::ApplyForceReaction( uint32 faction_id,ReputationRank rank,bool apply ) +{ + if(apply) + m_forcedReactions[faction_id] = rank; + else + m_forcedReactions.erase(faction_id); +} + +uint32 ReputationMgr::GetDefaultStateFlags(FactionEntry const* factionEntry) const +{ + if (!factionEntry) + return 0; + + uint32 raceMask = m_player->getRaceMask(); + uint32 classMask = m_player->getClassMask(); + for (int i=0; i < 4; i++) + { + if( (factionEntry->BaseRepRaceMask[i] & raceMask) && + (factionEntry->BaseRepClassMask[i]==0 || + (factionEntry->BaseRepClassMask[i] & classMask) ) ) + return factionEntry->ReputationFlags[i]; + } + return 0; +} + +void ReputationMgr::SendForceReactions() +{ + WorldPacket data; + data.Initialize(SMSG_SET_FORCED_REACTIONS, 4+m_forcedReactions.size()*(4+4)); + data << uint32(m_forcedReactions.size()); + for(ForcedReactions::const_iterator itr = m_forcedReactions.begin(); itr != m_forcedReactions.end(); ++itr) + { + data << uint32(itr->first); // faction_id (Faction.dbc) + data << uint32(itr->second); // reputation rank + } + m_player->SendDirectMessage(&data); +} + +void ReputationMgr::SendState(FactionState const* faction) const +{ + if(faction->Flags & FACTION_FLAG_VISIBLE) //If faction is visible then update it + { + WorldPacket data(SMSG_SET_FACTION_STANDING, (16)); // last check 2.4.0 + data << (float) 0; // unk 2.4.0 + data << (uint8) 0; // wotlk 8634 + data << (uint32) 1; // count + // for + data << (uint32) faction->ReputationListID; + data << (uint32) faction->Standing; + // end for + m_player->SendDirectMessage(&data); + } +} + +void ReputationMgr::SendInitialReputations() +{ + WorldPacket data(SMSG_INITIALIZE_FACTIONS, (4+128*5)); + data << uint32 (0x00000080); + + RepListID a = 0; + + for (FactionStateList::const_iterator itr = m_factions.begin(); itr != m_factions.end(); ++itr) + { + // fill in absent fields + for (; a != itr->first; a++) + { + data << uint8 (0x00); + data << uint32 (0x00000000); + } + + // fill in encountered data + data << uint8 (itr->second.Flags); + data << uint32 (itr->second.Standing); + + ++a; + } + + // fill in absent fields + for (; a != 128; a++) + { + data << uint8 (0x00); + data << uint32 (0x00000000); + } + + m_player->SendDirectMessage(&data); +} + +void ReputationMgr::SendStates() const +{ + for(FactionStateList::const_iterator itr = m_factions.begin(); itr != m_factions.end(); ++itr) + SendState(&(itr->second)); +} + +void ReputationMgr::SendVisible(FactionState const* faction) const +{ + if(m_player->GetSession()->PlayerLoading()) + return; + + // make faction visible in reputation list at client + WorldPacket data(SMSG_SET_FACTION_VISIBLE, 4); + data << faction->ReputationListID; + m_player->SendDirectMessage(&data); +} + +void ReputationMgr::Initilize() +{ + m_factions.clear(); + m_visibleFactionCount = 0; + m_honoredFactionCount = 0; + m_reveredFactionCount = 0; + m_exaltedFactionCount = 0; + + for(unsigned int i = 1; i < sFactionStore.GetNumRows(); i++) + { + FactionEntry const *factionEntry = sFactionStore.LookupEntry(i); + + if( factionEntry && (factionEntry->reputationListID >= 0)) + { + FactionState newFaction; + newFaction.ID = factionEntry->ID; + newFaction.ReputationListID = factionEntry->reputationListID; + newFaction.Standing = 0; + newFaction.Flags = GetDefaultStateFlags(factionEntry); + newFaction.Changed = true; + + if( newFaction.Flags & FACTION_FLAG_VISIBLE ) + ++m_visibleFactionCount; + + UpdateRankCounters(REP_HOSTILE,GetBaseRank(factionEntry)); + + m_factions[newFaction.ReputationListID] = newFaction; + } + } +} + +bool ReputationMgr::SetReputation(FactionEntry const* factionEntry, int32 standing, bool incremental) +{ + SimpleFactionsList const* flist = GetFactionTeamList(factionEntry->ID); + if (flist) + { + bool res = false; + for (SimpleFactionsList::const_iterator itr = flist->begin();itr != flist->end();++itr) + { + FactionEntry const *factionEntryCalc = sFactionStore.LookupEntry(*itr); + if(factionEntryCalc) + res = SetOneFactionReputation(factionEntryCalc, standing, incremental); + } + return res; + } + else + return SetOneFactionReputation(factionEntry, standing, incremental); +} + +bool ReputationMgr::SetOneFactionReputation(FactionEntry const* factionEntry, int32 standing, bool incremental) +{ + FactionStateList::iterator itr = m_factions.find(factionEntry->reputationListID); + if (itr != m_factions.end()) + { + int32 BaseRep = GetBaseReputation(factionEntry); + + if(incremental) + standing += itr->second.Standing + BaseRep; + + if (standing > Reputation_Cap) + standing = Reputation_Cap; + else if (standing < Reputation_Bottom) + standing = Reputation_Bottom; + + ReputationRank old_rank = ReputationToRank(itr->second.Standing + BaseRep); + ReputationRank new_rank = ReputationToRank(standing); + + itr->second.Standing = standing - BaseRep; + itr->second.Changed = true; + + SetVisible(&itr->second); + + if(new_rank <= REP_HOSTILE) + SetAtWar(&itr->second,true); + + SendState(&itr->second); + + UpdateRankCounters(old_rank, new_rank); + + m_player->ReputationChanged(factionEntry); + m_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS, factionEntry->ID); + m_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION, factionEntry->ID); + m_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION,factionEntry->ID); + m_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION,factionEntry->ID); + m_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION,factionEntry->ID); + + return true; + } + return false; +} + +void ReputationMgr::SetVisible(FactionTemplateEntry const*factionTemplateEntry) +{ + if(!factionTemplateEntry->faction) + return; + + if(FactionEntry const *factionEntry = sFactionStore.LookupEntry(factionTemplateEntry->faction)) + SetVisible(factionEntry); +} + +void ReputationMgr::SetVisible(FactionEntry const *factionEntry) +{ + if(factionEntry->reputationListID < 0) + return; + + FactionStateList::iterator itr = m_factions.find(factionEntry->reputationListID); + if (itr == m_factions.end()) + return; + + SetVisible(&itr->second); +} + +void ReputationMgr::SetVisible(FactionState* faction) +{ + // always invisible or hidden faction can't be make visible + if(faction->Flags & (FACTION_FLAG_INVISIBLE_FORCED|FACTION_FLAG_HIDDEN)) + return; + + // already set + if(faction->Flags & FACTION_FLAG_VISIBLE) + return; + + faction->Flags |= FACTION_FLAG_VISIBLE; + faction->Changed = true; + + ++m_visibleFactionCount; + + SendVisible(faction); +} + +void ReputationMgr::SetAtWar( RepListID repListID, bool on ) +{ + FactionStateList::iterator itr = m_factions.find(repListID); + if (itr == m_factions.end()) + return; + + // always invisible or hidden faction can't change war state + if(itr->second.Flags & (FACTION_FLAG_INVISIBLE_FORCED|FACTION_FLAG_HIDDEN) ) + return; + + SetAtWar(&itr->second,on); +} + +void ReputationMgr::SetAtWar(FactionState* faction, bool atWar) +{ + // not allow declare war to own faction + if(atWar && (faction->Flags & FACTION_FLAG_PEACE_FORCED) ) + return; + + // already set + if(((faction->Flags & FACTION_FLAG_AT_WAR) != 0) == atWar) + return; + + if( atWar ) + faction->Flags |= FACTION_FLAG_AT_WAR; + else + faction->Flags &= ~FACTION_FLAG_AT_WAR; + + faction->Changed = true; +} + +void ReputationMgr::SetInactive( RepListID repListID, bool on ) +{ + FactionStateList::iterator itr = m_factions.find(repListID); + if (itr == m_factions.end()) + return; + + SetInactive(&itr->second,on); +} + +void ReputationMgr::SetInactive(FactionState* faction, bool inactive) +{ + // always invisible or hidden faction can't be inactive + if(inactive && ((faction->Flags & (FACTION_FLAG_INVISIBLE_FORCED|FACTION_FLAG_HIDDEN)) || !(faction->Flags & FACTION_FLAG_VISIBLE) ) ) + return; + + // already set + if(((faction->Flags & FACTION_FLAG_INACTIVE) != 0) == inactive) + return; + + if(inactive) + faction->Flags |= FACTION_FLAG_INACTIVE; + else + faction->Flags &= ~FACTION_FLAG_INACTIVE; + + faction->Changed = true; +} + +void ReputationMgr::LoadFromDB(QueryResult *result) +{ + // Set initial reputations (so everything is nifty before DB data load) + Initilize(); + + //QueryResult *result = CharacterDatabase.PQuery("SELECT faction,standing,flags FROM character_reputation WHERE guid = '%u'",GetGUIDLow()); + + if(result) + { + do + { + Field *fields = result->Fetch(); + + FactionEntry const *factionEntry = sFactionStore.LookupEntry(fields[0].GetUInt32()); + if( factionEntry && (factionEntry->reputationListID >= 0)) + { + FactionState* faction = &m_factions[factionEntry->reputationListID]; + + // update standing to current + faction->Standing = int32(fields[1].GetUInt32()); + + // update counters + int32 BaseRep = GetBaseReputation(factionEntry); + ReputationRank old_rank = ReputationToRank(BaseRep); + ReputationRank new_rank = ReputationToRank(BaseRep + faction->Standing); + UpdateRankCounters(old_rank,new_rank); + + uint32 dbFactionFlags = fields[2].GetUInt32(); + + if( dbFactionFlags & FACTION_FLAG_VISIBLE ) + SetVisible(faction); // have internal checks for forced invisibility + + if( dbFactionFlags & FACTION_FLAG_INACTIVE) + SetInactive(faction,true); // have internal checks for visibility requirement + + if( dbFactionFlags & FACTION_FLAG_AT_WAR ) // DB at war + SetAtWar(faction,true); // have internal checks for FACTION_FLAG_PEACE_FORCED + else // DB not at war + { + // allow remove if visible (and then not FACTION_FLAG_INVISIBLE_FORCED or FACTION_FLAG_HIDDEN) + if( faction->Flags & FACTION_FLAG_VISIBLE ) + SetAtWar(faction,false); // have internal checks for FACTION_FLAG_PEACE_FORCED + } + + // set atWar for hostile + if(GetRank(factionEntry) <= REP_HOSTILE) + SetAtWar(faction,true); + + // reset changed flag if values similar to saved in DB + if(faction->Flags==dbFactionFlags) + faction->Changed = false; + } + } + while( result->NextRow() ); + + delete result; + } +} + +void ReputationMgr::SaveToDB() +{ + for(FactionStateList::iterator itr = m_factions.begin(); itr != m_factions.end(); ++itr) + { + if (itr->second.Changed) + { + CharacterDatabase.PExecute("DELETE FROM character_reputation WHERE guid = '%u' AND faction='%u'", m_player->GetGUIDLow(), itr->second.ID); + CharacterDatabase.PExecute("INSERT INTO character_reputation (guid,faction,standing,flags) VALUES ('%u', '%u', '%i', '%u')", m_player->GetGUIDLow(), itr->second.ID, itr->second.Standing, itr->second.Flags); + itr->second.Changed = false; + } + } +} + +void ReputationMgr::UpdateRankCounters( ReputationRank old_rank, ReputationRank new_rank ) +{ + if(old_rank >= REP_EXALTED) + --m_exaltedFactionCount; + if(old_rank >= REP_REVERED) + --m_reveredFactionCount; + if(old_rank >= REP_HONORED) + --m_honoredFactionCount; + + if(new_rank >= REP_EXALTED) + ++m_exaltedFactionCount; + if(new_rank >= REP_REVERED) + ++m_reveredFactionCount; + if(new_rank >= REP_HONORED) + ++m_honoredFactionCount; +}
\ No newline at end of file diff --git a/src/game/ReputationMgr.h b/src/game/ReputationMgr.h new file mode 100644 index 00000000000..b81634119df --- /dev/null +++ b/src/game/ReputationMgr.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __MANGOS_REPUTATION_MGR_H +#define __MANGOS_REPUTATION_MGR_H + +#include "Common.h" +#include "SharedDefines.h" +#include "DBCStructure.h" +#include <map> + +enum FactionFlags +{ + FACTION_FLAG_VISIBLE = 0x01, // makes visible in client (set or can be set at interaction with target of this faction) + FACTION_FLAG_AT_WAR = 0x02, // enable AtWar-button in client. player controlled (except opposition team always war state), Flag only set on initial creation + FACTION_FLAG_HIDDEN = 0x04, // hidden faction from reputation pane in client (player can gain reputation, but this update not sent to client) + FACTION_FLAG_INVISIBLE_FORCED = 0x08, // always overwrite FACTION_FLAG_VISIBLE and hide faction in rep.list, used for hide opposite team factions + FACTION_FLAG_PEACE_FORCED = 0x10, // always overwrite FACTION_FLAG_AT_WAR, used for prevent war with own team factions + FACTION_FLAG_INACTIVE = 0x20, // player controlled, state stored in characters.data ( CMSG_SET_FACTION_INACTIVE ) + FACTION_FLAG_RIVAL = 0x40 // flag for the two competing outland factions +}; + +typedef uint32 RepListID; +struct FactionState +{ + uint32 ID; + RepListID ReputationListID; + uint32 Flags; + int32 Standing; + bool Changed; +}; + +typedef std::map<RepListID,FactionState> FactionStateList; +typedef std::pair<FactionStateList::const_iterator,FactionStateList::const_iterator> FactionStateListPair; + +typedef std::map<uint32,ReputationRank> ForcedReactions; + +class Player; +class QueryResult; + +class ReputationMgr +{ + public: // constructors and global modifiers + explicit ReputationMgr(Player* owner) : m_player(owner), + m_visibleFactionCount(0), m_honoredFactionCount(0), m_reveredFactionCount(0), m_exaltedFactionCount(0) {} + ~ReputationMgr() {} + + void SaveToDB(); + void LoadFromDB(QueryResult *result); + public: // statics + static const int32 PointsInRank[MAX_REPUTATION_RANK]; + static const int32 Reputation_Cap = 42999; + static const int32 Reputation_Bottom = -42000; + + static ReputationRank ReputationToRank(int32 standing); + public: // accessors + uint8 GetVisibleFactionCount() const { return m_visibleFactionCount; } + uint8 GetHonoredFactionCount() const { return m_honoredFactionCount; } + uint8 GetReveredFactionCount() const { return m_reveredFactionCount; } + uint8 GetExaltedFactionCount() const { return m_exaltedFactionCount; } + + FactionStateList const& GetStateList() const { return m_factions; } + + FactionState const* GetState(FactionEntry const* factionEntry) const + { + return factionEntry->reputationListID >= 0 ? GetState(factionEntry->reputationListID) : NULL; + } + + FactionState const* GetState(RepListID id) const + { + FactionStateList::const_iterator repItr = m_factions.find (id); + return repItr != m_factions.end() ? &repItr->second : NULL; + } + + int32 GetReputation(uint32 faction_id) const; + int32 GetReputation(FactionEntry const* factionEntry) const; + int32 GetBaseReputation(FactionEntry const* factionEntry) const; + + ReputationRank GetRank(FactionEntry const* factionEntry) const; + ReputationRank GetBaseRank(FactionEntry const* factionEntry) const; + + ReputationRank const* GetForcedRankIfAny(FactionTemplateEntry const* factionTemplateEntry) const + { + ForcedReactions::const_iterator forceItr = m_forcedReactions.find(factionTemplateEntry->faction); + return forceItr != m_forcedReactions.end() ? &forceItr->second : NULL; + } + + public: // modifiers + bool SetReputation(FactionEntry const* factionEntry, int32 standing) + { + return SetReputation(factionEntry, standing, false); + } + bool ModifyReputation(FactionEntry const* factionEntry, int32 standing) + { + return SetReputation(factionEntry, standing, true); + } + + void SetVisible(FactionTemplateEntry const* factionTemplateEntry); + void SetVisible(FactionEntry const* factionEntry); + void SetAtWar(RepListID repListID, bool on); + void SetInactive(RepListID repListID, bool on); + + void ApplyForceReaction(uint32 faction_id,ReputationRank rank,bool apply); + + public: // senders + void SendInitialReputations(); + void SendForceReactions(); + void SendState(FactionState const* faction) const; + void SendStates() const; + + private: // internal helper functions + void Initilize(); + uint32 GetDefaultStateFlags(const FactionEntry *factionEntry) const; + bool SetReputation(FactionEntry const* factionEntry, int32 standing, bool incremental); + bool SetOneFactionReputation(FactionEntry const* factionEntry, int32 standing, bool incremental); + void SetVisible(FactionState* faction); + void SetAtWar(FactionState* faction, bool atWar); + void SetInactive(FactionState* faction, bool inactive); + void SendVisible(FactionState const* faction) const; + void UpdateRankCounters( ReputationRank old_rank, ReputationRank new_rank ); + private: + Player* m_player; + FactionStateList m_factions; + ForcedReactions m_forcedReactions; + uint8 m_visibleFactionCount :8; + uint8 m_honoredFactionCount :8; + uint8 m_reveredFactionCount :8; + uint8 m_exaltedFactionCount :8; +}; + +#endif diff --git a/src/game/ScriptCalls.cpp b/src/game/ScriptCalls.cpp index bce96344931..fbcfee6db71 100644 --- a/src/game/ScriptCalls.cpp +++ b/src/game/ScriptCalls.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -78,6 +78,9 @@ bool LoadScriptingModule(char const* libName) ||!(testScript->GOQuestAccept =(scriptCallGOQuestAccept )TRINITY_GET_PROC_ADDR(testScript->hScriptsLib,"GOQuestAccept" )) ||!(testScript->ReceiveEmote =(scriptCallReceiveEmote )TRINITY_GET_PROC_ADDR(testScript->hScriptsLib,"ReceiveEmote" )) ||!(testScript->ItemUse =(scriptCallItemUse )TRINITY_GET_PROC_ADDR(testScript->hScriptsLib,"ItemUse" )) + ||!(testScript->EffectDummyGameObj =(scriptCallEffectDummyGameObj )TRINITY_GET_PROC_ADDR(testScript->hScriptsLib,"EffectDummyGameObj" )) + ||!(testScript->EffectDummyCreature =(scriptCallEffectDummyCreature )TRINITY_GET_PROC_ADDR(testScript->hScriptsLib,"EffectDummyCreature" )) + ||!(testScript->EffectDummyItem =(scriptCallEffectDummyItem )TRINITY_GET_PROC_ADDR(testScript->hScriptsLib,"EffectDummyItem" )) ||!(testScript->GetAI =(scriptCallGetAI )TRINITY_GET_PROC_ADDR(testScript->hScriptsLib,"GetAI" )) ||!(testScript->CreateInstanceData =(scriptCallCreateInstanceData )TRINITY_GET_PROC_ADDR(testScript->hScriptsLib,"CreateInstanceData" )) ) @@ -88,7 +91,8 @@ bool LoadScriptingModule(char const* libName) return false; } - printf("Scripts Library %s was successfully loaded.\n",name.c_str()); + sLog.outString(); + sLog.outString( ">>> Scripts Library %s was successfully loaded.\n", name.c_str() ); //heh we are still there :P we have a valid library //we reload script diff --git a/src/game/ScriptCalls.h b/src/game/ScriptCalls.h index 83a6572e0e1..b95c82eb13e 100644 --- a/src/game/ScriptCalls.h +++ b/src/game/ScriptCalls.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -60,6 +60,9 @@ typedef bool(TRINITY_IMPORT * scriptCallGOQuestAccept)(Player *player, GameObjec typedef bool(TRINITY_IMPORT * scriptCallGOChooseReward)(Player *player, GameObject *, Quest const*, uint32 opt ); typedef bool(TRINITY_IMPORT * scriptCallReceiveEmote) ( Player *player, Creature *_Creature, uint32 emote ); typedef bool(TRINITY_IMPORT * scriptCallItemUse) (Player *player, Item *_Item, SpellCastTargets const& targets); +typedef bool(TRINITY_IMPORT * scriptCallEffectDummyGameObj) (Unit *caster, uint32 spellId, uint32 effIndex, GameObject *gameObjTarget); +typedef bool(TRINITY_IMPORT * scriptCallEffectDummyCreature) (Unit *caster, uint32 spellId, uint32 effIndex, Creature *crTarget); +typedef bool(TRINITY_IMPORT * scriptCallEffectDummyItem) (Unit *caster, uint32 spellId, uint32 effIndex, Item *itemTarget); typedef CreatureAI* (TRINITY_IMPORT * scriptCallGetAI) ( Creature *_Creature ); typedef InstanceData* (TRINITY_IMPORT * scriptCallCreateInstanceData) (Map *map); @@ -88,6 +91,9 @@ typedef struct scriptCallGOQuestAccept GOQuestAccept; scriptCallReceiveEmote ReceiveEmote; scriptCallItemUse ItemUse; + scriptCallEffectDummyGameObj EffectDummyGameObj; + scriptCallEffectDummyCreature EffectDummyCreature; + scriptCallEffectDummyItem EffectDummyItem; scriptCallGetAI GetAI; scriptCallCreateInstanceData CreateInstanceData; diff --git a/src/game/SharedDefines.h b/src/game/SharedDefines.h index 61cbcaa35ad..61c266854ff 100644 --- a/src/game/SharedDefines.h +++ b/src/game/SharedDefines.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +24,9 @@ #include "Platform/Define.h" #include <cassert> +#define MaNGOS Trinity +#define GetMangosString GetTrinityString + enum Gender { GENDER_MALE = 0, @@ -88,7 +91,8 @@ enum Classes #define CLASSMASK_ALL_PLAYABLE \ ((1<<(CLASS_WARRIOR-1))|(1<<(CLASS_PALADIN-1))|(1<<(CLASS_HUNTER-1))| \ (1<<(CLASS_ROGUE-1)) |(1<<(CLASS_PRIEST-1)) |(1<<(CLASS_SHAMAN-1))| \ - (1<<(CLASS_MAGE-1)) |(1<<(CLASS_WARLOCK-1))|(1<<(CLASS_DRUID-1)) ) + (1<<(CLASS_MAGE-1)) |(1<<(CLASS_WARLOCK-1))|(1<<(CLASS_DRUID-1)) | \ + (1<<(CLASS_DEATH_KNIGHT-1)) ) #define CLASSMASK_WAND_USERS ((1<<(CLASS_PRIEST-1))|(1<<(CLASS_MAGE-1))|(1<<(CLASS_WARLOCK-1))) @@ -134,12 +138,13 @@ enum Powers POWER_FOCUS = 2, POWER_ENERGY = 3, POWER_HAPPINESS = 4, - POWER_RUNES = 5, + POWER_RUNE = 5, + POWER_RUNIC_POWER = 6, + MAX_POWERS = 7, + POWER_ALL = 127, // default for class? POWER_HEALTH = 0xFFFFFFFE // (-2 as signed value) }; -#define MAX_POWERS 5 // not count POWER_RUNES for now - enum SpellSchools { SPELL_SCHOOL_NORMAL = 0, @@ -199,10 +204,11 @@ enum ItemQualities ITEM_QUALITY_RARE = 3, //BLUE ITEM_QUALITY_EPIC = 4, //PURPLE ITEM_QUALITY_LEGENDARY = 5, //ORANGE - ITEM_QUALITY_ARTIFACT = 6 //LIGHT YELLOW + ITEM_QUALITY_ARTIFACT = 6, //LIGHT YELLOW + ITEM_QUALITY_HEIRLOOM = 7 }; -#define MAX_ITEM_QUALITY 7 +#define MAX_ITEM_QUALITY 8 enum SpellCategory { @@ -217,7 +223,7 @@ enum SpellCategory #define SPELL_ATTR_UNK0 0x00000001 // 0 #define SPELL_ATTR_RANGED 0x00000002 // 1 All ranged abilities have this flag #define SPELL_ATTR_ON_NEXT_SWING_1 0x00000004 // 2 on next swing -#define SPELL_ATTR_UNK3 0x00000008 // 3 not set in 2.4.2 +#define SPELL_ATTR_UNK3 0x00000008 // 3 not set in 3.0.3 #define SPELL_ATTR_UNK4 0x00000010 // 4 #define SPELL_ATTR_UNK5 0x00000020 // 5 trade spells? #define SPELL_ATTR_PASSIVE 0x00000040 // 6 Passive spell @@ -257,7 +263,7 @@ enum SpellCategory #define SPELL_ATTR_EX_NEGATIVE 0x00000080 // 7 #define SPELL_ATTR_EX_NOT_IN_COMBAT_TARGET 0x00000100 // 8 Spell req target not to be in combat state #define SPELL_ATTR_EX_UNK9 0x00000200 // 9 -#define SPELL_ATTR_EX_UNK10 0x00000400 // 10 +#define SPELL_ATTR_EX_NO_INITIAL_AGGRO 0x00000400 // 10 no generates threat on cast 100% #define SPELL_ATTR_EX_UNK11 0x00000800 // 11 #define SPELL_ATTR_EX_UNK12 0x00001000 // 12 #define SPELL_ATTR_EX_UNK13 0x00002000 // 13 @@ -272,7 +278,7 @@ enum SpellCategory #define SPELL_ATTR_EX_REQ_COMBO_POINTS2 0x00400000 // 22 Req combo points on target #define SPELL_ATTR_EX_UNK23 0x00800000 // 23 #define SPELL_ATTR_EX_UNK24 0x01000000 // 24 Req fishing pole?? -#define SPELL_ATTR_EX_UNK25 0x02000000 // 25 not set in 2.4.2 +#define SPELL_ATTR_EX_UNK25 0x02000000 // 25 #define SPELL_ATTR_EX_UNK26 0x04000000 // 26 #define SPELL_ATTR_EX_UNK27 0x08000000 // 27 #define SPELL_ATTR_EX_UNK28 0x10000000 // 28 @@ -282,33 +288,33 @@ enum SpellCategory #define SPELL_ATTR_EX2_UNK0 0x00000001 // 0 #define SPELL_ATTR_EX2_UNK1 0x00000002 // 1 -#define SPELL_ATTR_EX2_UNK2 0x00000004 // 2 boss spells? +#define SPELL_ATTR_EX2_CANT_REFLECTED 0x00000004 // 2 ? used for detect can or not spell reflected #define SPELL_ATTR_EX2_UNK3 0x00000008 // 3 #define SPELL_ATTR_EX2_UNK4 0x00000010 // 4 -#define SPELL_ATTR_EX2_UNK5 0x00000020 // 5 +#define SPELL_ATTR_EX2_AUTOREPEAT_FLAG 0x00000020 // 5 #define SPELL_ATTR_EX2_UNK6 0x00000040 // 6 #define SPELL_ATTR_EX2_UNK7 0x00000080 // 7 -#define SPELL_ATTR_EX2_UNK8 0x00000100 // 8 not set in 2.4.2 +#define SPELL_ATTR_EX2_UNK8 0x00000100 // 8 not set in 3.0.3 #define SPELL_ATTR_EX2_UNK9 0x00000200 // 9 #define SPELL_ATTR_EX2_UNK10 0x00000400 // 10 #define SPELL_ATTR_EX2_HEALTH_FUNNEL 0x00000800 // 11 #define SPELL_ATTR_EX2_UNK12 0x00001000 // 12 #define SPELL_ATTR_EX2_UNK13 0x00002000 // 13 #define SPELL_ATTR_EX2_UNK14 0x00004000 // 14 -#define SPELL_ATTR_EX2_UNK15 0x00008000 // 15 not set in 2.4.2 +#define SPELL_ATTR_EX2_UNK15 0x00008000 // 15 not set in 3.0.3 #define SPELL_ATTR_EX2_TAME_BEAST 0x00010000 // 16 #define SPELL_ATTR_EX2_NOT_RESET_AUTOSHOT 0x00020000 // 17 Hunters Shot and Stings only have this flag #define SPELL_ATTR_EX2_UNK18 0x00040000 // 18 Only Revive pet - possible req dead pet #define SPELL_ATTR_EX2_NOT_NEED_SHAPESHIFT 0x00080000 // 19 does not necessarly need shapeshift #define SPELL_ATTR_EX2_UNK20 0x00100000 // 20 -#define SPELL_ATTR_EX2_UNK21 0x00200000 // 21 +#define SPELL_ATTR_EX2_DAMAGE_REDUCED_SHIELD 0x00200000 // 21 for ice blocks, pala immunity buffs, priest absorb shields, but used also for other spells -> not sure! #define SPELL_ATTR_EX2_UNK22 0x00400000 // 22 #define SPELL_ATTR_EX2_UNK23 0x00800000 // 23 Only mage Arcane Concentration have this flag #define SPELL_ATTR_EX2_UNK24 0x01000000 // 24 #define SPELL_ATTR_EX2_UNK25 0x02000000 // 25 #define SPELL_ATTR_EX2_UNK26 0x04000000 // 26 unaffected by school immunity #define SPELL_ATTR_EX2_UNK27 0x08000000 // 27 -#define SPELL_ATTR_EX2_UNK28 0x10000000 // 28 +#define SPELL_ATTR_EX2_UNK28 0x10000000 // 28 no breaks stealth if it fails?? #define SPELL_ATTR_EX2_CANT_CRIT 0x20000000 // 29 Spell can't crit #define SPELL_ATTR_EX2_UNK30 0x40000000 // 30 #define SPELL_ATTR_EX2_FOOD 0x80000000 // 31 food, well-fed, and a few others @@ -329,8 +335,8 @@ enum SpellCategory #define SPELL_ATTR_EX3_UNK13 0x00002000 // 13 #define SPELL_ATTR_EX3_UNK14 0x00004000 // 14 "Honorless Target" only this spells have this flag #define SPELL_ATTR_EX3_UNK15 0x00008000 // 15 Auto Shoot, Shoot, Throw, - this is autoshot flag -#define SPELL_ATTR_EX3_UNK16 0x00010000 // 16 -#define SPELL_ATTR_EX3_NO_INITIAL_AGGRO 0x00020000 // 17 no initial aggro +#define SPELL_ATTR_EX3_UNK16 0x00010000 // 16 no triggers effects that trigger on casting a spell?? +#define SPELL_ATTR_EX3_UNK17 0x00020000 // 17 no triggers effects that trigger on casting a spell?? #define SPELL_ATTR_EX3_UNK18 0x00040000 // 18 #define SPELL_ATTR_EX3_UNK19 0x00080000 // 19 #define SPELL_ATTR_EX3_DEATH_PERSISTENT 0x00100000 // 20 Death persistent spells @@ -338,7 +344,7 @@ enum SpellCategory #define SPELL_ATTR_EX3_REQ_WAND 0x00400000 // 22 Req wand #define SPELL_ATTR_EX3_UNK23 0x00800000 // 23 #define SPELL_ATTR_EX3_REQ_OFFHAND 0x01000000 // 24 Req offhand weapon -#define SPELL_ATTR_EX3_UNK25 0x02000000 // 25 +#define SPELL_ATTR_EX3_UNK25 0x02000000 // 25 no cause spell pushback ? #define SPELL_ATTR_EX3_UNK26 0x04000000 // 26 #define SPELL_ATTR_EX3_UNK27 0x08000000 // 27 #define SPELL_ATTR_EX3_UNK28 0x10000000 // 28 @@ -350,9 +356,9 @@ enum SpellCategory #define SPELL_ATTR_EX4_UNK1 0x00000002 // 1 proc on finishing move? #define SPELL_ATTR_EX4_UNK2 0x00000004 // 2 #define SPELL_ATTR_EX4_UNK3 0x00000008 // 3 -#define SPELL_ATTR_EX4_UNK4 0x00000010 // 4 +#define SPELL_ATTR_EX4_UNK4 0x00000010 // 4 This will no longer cause guards to attack on use?? #define SPELL_ATTR_EX4_UNK5 0x00000020 // 5 -#define SPELL_ATTR_EX4_UNK6 0x00000040 // 6 +#define SPELL_ATTR_EX4_NOT_STEALABLE 0x00000040 // 6 although such auras might be dispellable, they cannot be stolen #define SPELL_ATTR_EX4_UNK7 0x00000080 // 7 #define SPELL_ATTR_EX4_UNK8 0x00000100 // 8 #define SPELL_ATTR_EX4_UNK9 0x00000200 // 9 @@ -388,7 +394,7 @@ enum SpellCategory #define SPELL_ATTR_EX5_UNK6 0x00000040 // 6 #define SPELL_ATTR_EX5_UNK7 0x00000080 // 7 #define SPELL_ATTR_EX5_UNK8 0x00000100 // 8 -#define SPELL_ATTR_EX5_UNK9 0x00000200 // 9 +#define SPELL_ATTR_EX5_START_PERIODIC_AT_APPLY 0x00000200 // 9 begin periodic tick at aura apply #define SPELL_ATTR_EX5_UNK10 0x00000400 // 10 #define SPELL_ATTR_EX5_UNK11 0x00000800 // 11 #define SPELL_ATTR_EX5_UNK12 0x00001000 // 12 @@ -413,37 +419,39 @@ enum SpellCategory #define SPELL_ATTR_EX5_UNK31 0x80000000 // 31 Forces all nearby enemies to focus attacks caster #define SPELL_ATTR_EX6_UNK0 0x00000001 // 0 Only Move spell have this flag -#define SPELL_ATTR_EX6_UNK1 0x00000002 // 1 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK1 0x00000002 // 1 not set in 3.0.3 #define SPELL_ATTR_EX6_UNK2 0x00000004 // 2 #define SPELL_ATTR_EX6_UNK3 0x00000008 // 3 -#define SPELL_ATTR_EX6_UNK4 0x00000010 // 4 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK4 0x00000010 // 4 #define SPELL_ATTR_EX6_UNK5 0x00000020 // 5 #define SPELL_ATTR_EX6_UNK6 0x00000040 // 6 #define SPELL_ATTR_EX6_UNK7 0x00000080 // 7 #define SPELL_ATTR_EX6_UNK8 0x00000100 // 8 -#define SPELL_ATTR_EX6_UNK9 0x00000200 // 9 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK9 0x00000200 // 9 #define SPELL_ATTR_EX6_UNK10 0x00000400 // 10 #define SPELL_ATTR_EX6_UNK11 0x00000800 // 11 -#define SPELL_ATTR_EX6_UNK12 0x00001000 // 12 not set in 2.4.2 -#define SPELL_ATTR_EX6_UNK13 0x00002000 // 13 not set in 2.4.2 -#define SPELL_ATTR_EX6_UNK14 0x00004000 // 14 not set in 2.4.2 -#define SPELL_ATTR_EX6_UNK15 0x00008000 // 15 not set in 2.4.2 -#define SPELL_ATTR_EX6_UNK16 0x00010000 // 16 not set in 2.4.2 -#define SPELL_ATTR_EX6_UNK17 0x00020000 // 17 not set in 2.4.2 -#define SPELL_ATTR_EX6_UNK18 0x00040000 // 18 not set in 2.4.2 -#define SPELL_ATTR_EX6_UNK19 0x00080000 // 19 not set in 2.4.2 -#define SPELL_ATTR_EX6_UNK20 0x00100000 // 20 not set in 2.4.2 -#define SPELL_ATTR_EX6_UNK21 0x00200000 // 21 not set in 2.4.2 -#define SPELL_ATTR_EX6_UNK22 0x00400000 // 22 not set in 2.4.2 -#define SPELL_ATTR_EX6_UNK23 0x00800000 // 23 not set in 2.4.2 -#define SPELL_ATTR_EX6_UNK24 0x01000000 // 24 not set in 2.4.2 -#define SPELL_ATTR_EX6_UNK25 0x02000000 // 25 not set in 2.4.2 -#define SPELL_ATTR_EX6_UNK26 0x04000000 // 26 not set in 2.4.2 -#define SPELL_ATTR_EX6_UNK27 0x08000000 // 27 not set in 2.4.2 -#define SPELL_ATTR_EX6_UNK28 0x10000000 // 28 not set in 2.4.2 -#define SPELL_ATTR_EX6_UNK29 0x20000000 // 29 not set in 2.4.2 -#define SPELL_ATTR_EX6_UNK30 0x40000000 // 30 not set in 2.4.2 -#define SPELL_ATTR_EX6_UNK31 0x80000000 // 31 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK12 0x00001000 // 12 +#define SPELL_ATTR_EX6_UNK13 0x00002000 // 13 +#define SPELL_ATTR_EX6_UNK14 0x00004000 // 14 +#define SPELL_ATTR_EX6_UNK15 0x00008000 // 15 not set in 3.0.3 +#define SPELL_ATTR_EX6_UNK16 0x00010000 // 16 +#define SPELL_ATTR_EX6_UNK17 0x00020000 // 17 +#define SPELL_ATTR_EX6_UNK18 0x00040000 // 18 +#define SPELL_ATTR_EX6_UNK19 0x00080000 // 19 +#define SPELL_ATTR_EX6_UNK20 0x00100000 // 20 +#define SPELL_ATTR_EX6_UNK21 0x00200000 // 21 +#define SPELL_ATTR_EX6_UNK22 0x00400000 // 22 +#define SPELL_ATTR_EX6_UNK23 0x00800000 // 23 not set in 3.0.3 +#define SPELL_ATTR_EX6_UNK24 0x01000000 // 24 not set in 3.0.3 +#define SPELL_ATTR_EX6_UNK25 0x02000000 // 25 not set in 3.0.3 +#define SPELL_ATTR_EX6_UNK26 0x04000000 // 26 not set in 3.0.3 +#define SPELL_ATTR_EX6_UNK27 0x08000000 // 27 not set in 3.0.3 +#define SPELL_ATTR_EX6_UNK28 0x10000000 // 28 not set in 3.0.3 +#define SPELL_ATTR_EX6_UNK29 0x20000000 // 29 not set in 3.0.3 +#define SPELL_ATTR_EX6_UNK30 0x40000000 // 30 not set in 3.0.3 +#define SPELL_ATTR_EX6_UNK31 0x80000000 // 31 not set in 3.0.3 + +#define MAX_GLYPH_SLOT_INDEX 6 enum SheathTypes { @@ -562,11 +570,11 @@ enum SpellEffects SPELL_EFFECT_DISPEL = 38, SPELL_EFFECT_LANGUAGE = 39, SPELL_EFFECT_DUAL_WIELD = 40, - SPELL_EFFECT_SUMMON_WILD = 41, - SPELL_EFFECT_SUMMON_GUARDIAN = 42, + SPELL_EFFECT_JUMP = 41, + SPELL_EFFECT_JUMP2 = 42, SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER= 43, SPELL_EFFECT_SKILL_STEP = 44, - SPELL_EFFECT_UNDEFINED_45 = 45, + SPELL_EFFECT_ADD_HONOR = 45, SPELL_EFFECT_SPAWN = 46, SPELL_EFFECT_TRADE_SKILL = 47, SPELL_EFFECT_STEALTH = 48, @@ -586,16 +594,16 @@ enum SpellEffects SPELL_EFFECT_POWER_BURN = 62, SPELL_EFFECT_THREAT = 63, SPELL_EFFECT_TRIGGER_SPELL = 64, - SPELL_EFFECT_HEALTH_FUNNEL = 65, - SPELL_EFFECT_POWER_FUNNEL = 66, + SPELL_EFFECT_APPLY_AREA_AURA_RAID = 65, + SPELL_EFFECT_CREATE_MANA_GEM = 66, SPELL_EFFECT_HEAL_MAX_HEALTH = 67, SPELL_EFFECT_INTERRUPT_CAST = 68, SPELL_EFFECT_DISTRACT = 69, SPELL_EFFECT_PULL = 70, SPELL_EFFECT_PICKPOCKET = 71, SPELL_EFFECT_ADD_FARSIGHT = 72, - SPELL_EFFECT_SUMMON_POSSESSED = 73, - SPELL_EFFECT_SUMMON_TOTEM = 74, + SPELL_EFFECT_UNTRAIN_TALENTS = 73, + SPELL_EFFECT_APPLY_GLYPH = 74, SPELL_EFFECT_HEAL_MECHANICAL = 75, SPELL_EFFECT_SUMMON_OBJECT_WILD = 76, SPELL_EFFECT_SCRIPT_EFFECT = 77, @@ -608,17 +616,17 @@ enum SpellEffects SPELL_EFFECT_STUCK = 84, SPELL_EFFECT_SUMMON_PLAYER = 85, SPELL_EFFECT_ACTIVATE_OBJECT = 86, - SPELL_EFFECT_SUMMON_TOTEM_SLOT1 = 87, - SPELL_EFFECT_SUMMON_TOTEM_SLOT2 = 88, - SPELL_EFFECT_SUMMON_TOTEM_SLOT3 = 89, - SPELL_EFFECT_SUMMON_TOTEM_SLOT4 = 90, + SPELL_EFFECT_WMO_DAMAGE = 87, + SPELL_EFFECT_WMO_REPAIR = 88, + SPELL_EFFECT_WMO_CHANGE = 89, + SPELL_EFFECT_KILL_CREDIT = 90, SPELL_EFFECT_THREAT_ALL = 91, SPELL_EFFECT_ENCHANT_HELD_ITEM = 92, SPELL_EFFECT_SUMMON_PHANTASM = 93, //unused SPELL_EFFECT_SELF_RESURRECT = 94, SPELL_EFFECT_SKINNING = 95, SPELL_EFFECT_CHARGE = 96, - SPELL_EFFECT_SUMMON_CRITTER = 97, + SPELL_EFFECT_97 = 97, SPELL_EFFECT_KNOCK_BACK = 98, SPELL_EFFECT_DISENCHANT = 99, SPELL_EFFECT_INEBRIATE = 100, @@ -633,7 +641,7 @@ enum SpellEffects SPELL_EFFECT_SUMMON_DEAD_PET = 109, SPELL_EFFECT_DESTROY_ALL_TOTEMS = 110, SPELL_EFFECT_DURABILITY_DAMAGE = 111, - SPELL_EFFECT_SUMMON_DEMON = 112, + SPELL_EFFECT_112 = 112, SPELL_EFFECT_RESURRECT_NEW = 113, SPELL_EFFECT_ATTACK_ME = 114, SPELL_EFFECT_DURABILITY_DAMAGE_PCT = 115, @@ -655,19 +663,19 @@ enum SpellEffects SPELL_EFFECT_131 = 131, SPELL_EFFECT_132 = 132, SPELL_EFFECT_UNLEARN_SPECIALIZATION = 133, - SPELL_EFFECT_KILL_CREDIT = 134, + SPELL_EFFECT_KILL_CREDIT2 = 134, SPELL_EFFECT_135 = 135, SPELL_EFFECT_HEAL_PCT = 136, SPELL_EFFECT_ENERGIZE_PCT = 137, SPELL_EFFECT_138 = 138, - SPELL_EFFECT_139 = 139, + SPELL_EFFECT_CLEAR_QUEST = 139, SPELL_EFFECT_FORCE_CAST = 140, SPELL_EFFECT_141 = 141, SPELL_EFFECT_TRIGGER_SPELL_WITH_VALUE = 142, SPELL_EFFECT_APPLY_AREA_AURA_OWNER = 143, SPELL_EFFECT_KNOCK_BACK_2 = 144, SPELL_EFFECT_145 = 145, - SPELL_EFFECT_146 = 146, + SPELL_EFFECT_ACTIVATE_RUNE = 146, SPELL_EFFECT_QUEST_FAIL = 147, SPELL_EFFECT_148 = 148, SPELL_EFFECT_149 = 149, @@ -675,7 +683,201 @@ enum SpellEffects SPELL_EFFECT_TRIGGER_SPELL_2 = 151, SPELL_EFFECT_152 = 152, SPELL_EFFECT_153 = 153, - TOTAL_SPELL_EFFECTS = 154 + SPELL_EFFECT_154 = 154, + SPELL_EFFECT_TITAN_GRIP = 155, + SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC = 156, + SPELL_EFFECT_CREATE_ITEM_2 = 157, + SPELL_EFFECT_MILLING = 158, + SPELL_EFFECT_ALLOW_RENAME_PET = 159, + TOTAL_SPELL_EFFECTS = 160 +}; + +enum SpellCastResult +{ + SPELL_FAILED_AFFECTING_COMBAT = 0, + SPELL_FAILED_ALREADY_AT_FULL_HEALTH = 1, + SPELL_FAILED_ALREADY_AT_FULL_MANA = 2, + SPELL_FAILED_ALREADY_AT_FULL_POWER = 3, + SPELL_FAILED_ALREADY_BEING_TAMED = 4, + SPELL_FAILED_ALREADY_HAVE_CHARM = 5, + SPELL_FAILED_ALREADY_HAVE_SUMMON = 6, + SPELL_FAILED_ALREADY_OPEN = 7, + SPELL_FAILED_AURA_BOUNCED = 8, + SPELL_FAILED_AUTOTRACK_INTERRUPTED = 9, + SPELL_FAILED_BAD_IMPLICIT_TARGETS = 10, + SPELL_FAILED_BAD_TARGETS = 11, + SPELL_FAILED_CANT_BE_CHARMED = 12, + SPELL_FAILED_CANT_BE_DISENCHANTED = 13, + SPELL_FAILED_CANT_BE_DISENCHANTED_SKILL = 14, + SPELL_FAILED_CANT_BE_MILLED = 15, + SPELL_FAILED_CANT_BE_PROSPECTED = 16, + SPELL_FAILED_CANT_CAST_ON_TAPPED = 17, + SPELL_FAILED_CANT_DUEL_WHILE_INVISIBLE = 18, + SPELL_FAILED_CANT_DUEL_WHILE_STEALTHED = 19, + SPELL_FAILED_CANT_STEALTH = 20, + SPELL_FAILED_CASTER_AURASTATE = 21, + SPELL_FAILED_CASTER_DEAD = 22, + SPELL_FAILED_CHARMED = 23, + SPELL_FAILED_CHEST_IN_USE = 24, + SPELL_FAILED_CONFUSED = 25, + SPELL_FAILED_DONT_REPORT = 26, + SPELL_FAILED_EQUIPPED_ITEM = 27, + SPELL_FAILED_EQUIPPED_ITEM_CLASS = 28, + SPELL_FAILED_EQUIPPED_ITEM_CLASS_MAINHAND = 29, + SPELL_FAILED_EQUIPPED_ITEM_CLASS_OFFHAND = 30, + SPELL_FAILED_ERROR = 31, + SPELL_FAILED_FIZZLE = 32, + SPELL_FAILED_FLEEING = 33, + SPELL_FAILED_FOOD_LOWLEVEL = 34, + SPELL_FAILED_HIGHLEVEL = 35, + SPELL_FAILED_HUNGER_SATIATED = 36, + SPELL_FAILED_IMMUNE = 37, + SPELL_FAILED_INCORRECT_AREA = 38, + SPELL_FAILED_INTERRUPTED = 39, + SPELL_FAILED_INTERRUPTED_COMBAT = 40, + SPELL_FAILED_ITEM_ALREADY_ENCHANTED = 41, + SPELL_FAILED_ITEM_GONE = 42, + SPELL_FAILED_ITEM_NOT_FOUND = 43, + SPELL_FAILED_ITEM_NOT_READY = 44, + SPELL_FAILED_LEVEL_REQUIREMENT = 45, + SPELL_FAILED_LINE_OF_SIGHT = 46, + SPELL_FAILED_LOWLEVEL = 47, + SPELL_FAILED_LOW_CASTLEVEL = 48, + SPELL_FAILED_MAINHAND_EMPTY = 49, + SPELL_FAILED_MOVING = 50, + SPELL_FAILED_NEED_AMMO = 51, + SPELL_FAILED_NEED_AMMO_POUCH = 52, + SPELL_FAILED_NEED_EXOTIC_AMMO = 53, + SPELL_FAILED_NEED_MORE_ITEMS = 54, + SPELL_FAILED_NOPATH = 55, + SPELL_FAILED_NOT_BEHIND = 56, + SPELL_FAILED_NOT_FISHABLE = 57, + SPELL_FAILED_NOT_FLYING = 58, + SPELL_FAILED_NOT_HERE = 59, + SPELL_FAILED_NOT_INFRONT = 60, + SPELL_FAILED_NOT_IN_CONTROL = 61, + SPELL_FAILED_NOT_KNOWN = 62, + SPELL_FAILED_NOT_MOUNTED = 63, + SPELL_FAILED_NOT_ON_TAXI = 64, + SPELL_FAILED_NOT_ON_TRANSPORT = 65, + SPELL_FAILED_NOT_READY = 66, + SPELL_FAILED_NOT_SHAPESHIFT = 67, + SPELL_FAILED_NOT_STANDING = 68, + SPELL_FAILED_NOT_TRADEABLE = 69, + SPELL_FAILED_NOT_TRADING = 70, + SPELL_FAILED_NOT_UNSHEATHED = 71, + SPELL_FAILED_NOT_WHILE_GHOST = 72, + SPELL_FAILED_NOT_WHILE_LOOTING = 73, + SPELL_FAILED_NO_AMMO = 74, + SPELL_FAILED_NO_CHARGES_REMAIN = 75, + SPELL_FAILED_NO_CHAMPION = 76, + SPELL_FAILED_NO_COMBO_POINTS = 77, + SPELL_FAILED_NO_DUELING = 78, + SPELL_FAILED_NO_ENDURANCE = 79, + SPELL_FAILED_NO_FISH = 80, + SPELL_FAILED_NO_ITEMS_WHILE_SHAPESHIFTED = 81, + SPELL_FAILED_NO_MOUNTS_ALLOWED = 82, + SPELL_FAILED_NO_PET = 83, + SPELL_FAILED_NO_POWER = 84, + SPELL_FAILED_NOTHING_TO_DISPEL = 85, + SPELL_FAILED_NOTHING_TO_STEAL = 86, + SPELL_FAILED_ONLY_ABOVEWATER = 87, + SPELL_FAILED_ONLY_DAYTIME = 88, + SPELL_FAILED_ONLY_INDOORS = 89, + SPELL_FAILED_ONLY_MOUNTED = 90, + SPELL_FAILED_ONLY_NIGHTTIME = 91, + SPELL_FAILED_ONLY_OUTDOORS = 92, + SPELL_FAILED_ONLY_SHAPESHIFT = 93, + SPELL_FAILED_ONLY_STEALTHED = 94, + SPELL_FAILED_ONLY_UNDERWATER = 95, + SPELL_FAILED_OUT_OF_RANGE = 96, + SPELL_FAILED_PACIFIED = 97, + SPELL_FAILED_POSSESSED = 98, + SPELL_FAILED_REAGENTS = 99, + SPELL_FAILED_REQUIRES_AREA = 100, + SPELL_FAILED_REQUIRES_SPELL_FOCUS = 101, + SPELL_FAILED_ROOTED = 102, + SPELL_FAILED_SILENCED = 103, + SPELL_FAILED_SPELL_IN_PROGRESS = 104, + SPELL_FAILED_SPELL_LEARNED = 105, + SPELL_FAILED_SPELL_UNAVAILABLE = 106, + SPELL_FAILED_STUNNED = 107, + SPELL_FAILED_TARGETS_DEAD = 108, + SPELL_FAILED_TARGET_AFFECTING_COMBAT = 109, + SPELL_FAILED_TARGET_AURASTATE = 110, + SPELL_FAILED_TARGET_DUELING = 111, + SPELL_FAILED_TARGET_ENEMY = 112, + SPELL_FAILED_TARGET_ENRAGED = 113, + SPELL_FAILED_TARGET_FRIENDLY = 114, + SPELL_FAILED_TARGET_IN_COMBAT = 115, + SPELL_FAILED_TARGET_IS_PLAYER = 116, + SPELL_FAILED_TARGET_IS_PLAYER_CONTROLLED = 117, + SPELL_FAILED_TARGET_NOT_DEAD = 118, + SPELL_FAILED_TARGET_NOT_IN_PARTY = 119, + SPELL_FAILED_TARGET_NOT_LOOTED = 120, + SPELL_FAILED_TARGET_NOT_PLAYER = 121, + SPELL_FAILED_TARGET_NO_POCKETS = 122, + SPELL_FAILED_TARGET_NO_WEAPONS = 123, + SPELL_FAILED_TARGET_NO_RANGED_WEAPONS = 124, + SPELL_FAILED_TARGET_UNSKINNABLE = 125, + SPELL_FAILED_THIRST_SATIATED = 126, + SPELL_FAILED_TOO_CLOSE = 127, + SPELL_FAILED_TOO_MANY_OF_ITEM = 128, + SPELL_FAILED_TOTEM_CATEGORY = 129, + SPELL_FAILED_TOTEMS = 130, + SPELL_FAILED_TRY_AGAIN = 131, + SPELL_FAILED_UNIT_NOT_BEHIND = 132, + SPELL_FAILED_UNIT_NOT_INFRONT = 133, + SPELL_FAILED_WRONG_PET_FOOD = 134, + SPELL_FAILED_NOT_WHILE_FATIGUED = 135, + SPELL_FAILED_TARGET_NOT_IN_INSTANCE = 136, + SPELL_FAILED_NOT_WHILE_TRADING = 137, + SPELL_FAILED_TARGET_NOT_IN_RAID = 138, + SPELL_FAILED_TARGET_FREEFORALL = 139, + SPELL_FAILED_NO_EDIBLE_CORPSES = 140, + SPELL_FAILED_ONLY_BATTLEGROUNDS = 141, + SPELL_FAILED_TARGET_NOT_GHOST = 142, + SPELL_FAILED_TRANSFORM_UNUSABLE = 143, + SPELL_FAILED_WRONG_WEATHER = 144, + SPELL_FAILED_DAMAGE_IMMUNE = 145, + SPELL_FAILED_PREVENTED_BY_MECHANIC = 146, + SPELL_FAILED_PLAY_TIME = 147, + SPELL_FAILED_REPUTATION = 148, + SPELL_FAILED_MIN_SKILL = 149, + SPELL_FAILED_NOT_IN_ARENA = 150, + SPELL_FAILED_NOT_ON_SHAPESHIFT = 151, + SPELL_FAILED_NOT_ON_STEALTHED = 152, + SPELL_FAILED_NOT_ON_DAMAGE_IMMUNE = 153, + SPELL_FAILED_NOT_ON_MOUNTED = 154, + SPELL_FAILED_TOO_SHALLOW = 155, + SPELL_FAILED_TARGET_NOT_IN_SANCTUARY = 156, + SPELL_FAILED_TARGET_IS_TRIVIAL = 157, + SPELL_FAILED_BM_OR_INVISGOD = 158, + SPELL_FAILED_EXPERT_RIDING_REQUIREMENT = 159, + SPELL_FAILED_ARTISAN_RIDING_REQUIREMENT = 160, + SPELL_FAILED_NOT_IDLE = 161, + SPELL_FAILED_NOT_INACTIVE = 162, + SPELL_FAILED_PARTIAL_PLAYTIME = 163, + SPELL_FAILED_NO_PLAYTIME = 164, + SPELL_FAILED_NOT_IN_BATTLEGROUND = 165, + SPELL_FAILED_NOT_IN_RAID_INSTANCE = 166, + SPELL_FAILED_ONLY_IN_ARENA = 167, + SPELL_FAILED_TARGET_LOCKED_TO_RAID_INSTANCE = 168, + SPELL_FAILED_ON_USE_ENCHANT = 169, + SPELL_FAILED_NOT_ON_GROUND = 170, + SPELL_FAILED_CUSTOM_ERROR = 171, + SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW = 172, + SPELL_FAILED_TOO_MANY_SOCKETS = 173, + SPELL_FAILED_INVALID_GLYPH = 174, + SPELL_FAILED_UNIQUE_GLYPH = 175, + SPELL_FAILED_GLYPH_SOCKET_LOCKED = 176, + SPELL_FAILED_NO_VALID_TARGETS = 177, + SPELL_FAILED_ITEM_AT_MAX_CHARGES = 178, + SPELL_FAILED_NOT_IN_BARBERSHOP = 179, + SPELL_FAILED_FISHING_TOO_LOW = 180, + SPELL_FAILED_UNKNOWN = 181, + + SPELL_CAST_OK = 255 //custom value, don't must be send to client }; // Spell aura states @@ -685,25 +887,28 @@ enum AuraState AURA_STATE_DEFENSE = 1, // C | AURA_STATE_HEALTHLESS_20_PERCENT = 2, // CcT | AURA_STATE_BERSERKING = 3, // C T | - //AURA_STATE_UNKNOWN4 = 4, // c t| some limitation to charge spells (?) and target test spells + AURA_STATE_FROZEN = 4, // c t| frozen target AURA_STATE_JUDGEMENT = 5, // C | //AURA_STATE_UNKNOWN6 = 6, // | not used AURA_STATE_HUNTER_PARRY = 7, // C | AURA_STATE_ROGUE_ATTACK_FROM_STEALTH = 7, // C | FIX ME: not implemented yet! - //AURA_STATE_UNKNOWN7c = 7, // c | random/focused bursts spells (?) + //AURA_STATE_UNKNOWN7 = 7, // c | random/focused bursts spells (?) //AURA_STATE_UNKNOWN8 = 8, // | not used //AURA_STATE_UNKNOWN9 = 9, // | not used AURA_STATE_WARRIOR_VICTORY_RUSH = 10, // C | warrior victory rush - AURA_STATE_HUNTER_CRIT_STRIKE = 10, // C | hunter crit strike - AURA_STATE_CRIT = 11, // C | + //AURA_STATE_UNKNOWN11 = 11, // t| AURA_STATE_FAERIE_FIRE = 12, // c t| AURA_STATE_HEALTHLESS_35_PERCENT = 13, // C T | AURA_STATE_IMMOLATE = 14, // T | AURA_STATE_SWIFTMEND = 15, // T | AURA_STATE_DEADLY_POISON = 16, // T | - AURA_STATE_FORBEARANCE = 17, // c t| - AURA_STATE_WEAKENED_SOUL = 18, // t| - AURA_STATE_HYPOTHERMIA = 19 // c | + AURA_STATE_ENRAGE = 17, // C | + //AURA_STATE_UNKNOWN18 = 18, // C t| + //AURA_STATE_UNKNOWN19 = 19, // | not used + //AURA_STATE_UNKNOWN20 = 20, // c | only (45317 Suicide) + //AURA_STATE_UNKNOWN21 = 21, // | not used + //AURA_STATE_UNKNOWN22 = 22, // C | not implemented yet (Requires Evasive Charges to use) + AURA_STATE_HEALTH_ABOVE_75_PERCENT = 23, // C | }; // Spell mechanics @@ -711,11 +916,11 @@ enum Mechanics { MECHANIC_NONE = 0, MECHANIC_CHARM = 1, - MECHANIC_CONFUSED = 2, + MECHANIC_DISORIENTED = 2, MECHANIC_DISARM = 3, MECHANIC_DISTRACT = 4, MECHANIC_FEAR = 5, - MECHANIC_FUMBLE = 6, + MECHANIC_GRIP = 6, MECHANIC_ROOT = 7, MECHANIC_PACIFY = 8, //0 spells use this mechanic MECHANIC_SILENCE = 9, @@ -731,7 +936,7 @@ enum Mechanics MECHANIC_SHIELD = 19, MECHANIC_SHACKLE = 20, MECHANIC_MOUNT = 21, - MECHANIC_PERSUADE = 22, //0 spells use this mechanic + MECHANIC_INFECTED = 22, MECHANIC_TURN = 23, MECHANIC_HORROR = 24, MECHANIC_INVULNERABILITY = 25, @@ -739,12 +944,13 @@ enum Mechanics MECHANIC_DAZE = 27, MECHANIC_DISCOVERY = 28, MECHANIC_IMMUNE_SHIELD = 29, // Divine (Blessing) Shield/Protection and Ice Block - MECHANIC_SAPPED = 30 + MECHANIC_SAPPED = 30, + MECHANIC_ENRAGED = 31 }; // Used for spell 42292 Immune Movement Impairment and Loss of Control (0x49967da6) #define IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK ( \ - (1<<MECHANIC_CHARM )|(1<<MECHANIC_CONFUSED )|(1<<MECHANIC_FEAR )| \ + (1<<MECHANIC_CHARM )|(1<<MECHANIC_DISORIENTED )|(1<<MECHANIC_FEAR )| \ (1<<MECHANIC_ROOT )|(1<<MECHANIC_PACIFY )|(1<<MECHANIC_SLEEP )| \ (1<<MECHANIC_SNARE )|(1<<MECHANIC_STUN )|(1<<MECHANIC_FREEZE)| \ (1<<MECHANIC_KNOCKOUT)|(1<<MECHANIC_POLYMORPH)|(1<<MECHANIC_BANISH)| \ @@ -764,7 +970,8 @@ enum DispelType DISPEL_ALL = 7, DISPEL_SPE_NPC_ONLY = 8, DISPEL_ENRAGE = 9, - DISPEL_ZG_TICKET = 10 + DISPEL_ZG_TICKET = 10, + DESPEL_OLD_UNUSED = 11 }; #define DISPEL_ALL_MASK ( (1<<DISPEL_MAGIC) | (1<<DISPEL_CURSE) | (1<<DISPEL_DISEASE) | (1<<DISPEL_POISON) ) @@ -790,10 +997,9 @@ enum Targets { TARGET_SELF = 1, TARGET_UNIT_CASTER = 1, - TARGET_RANDOM_ENEMY_CHAIN_IN_AREA = 2, // only one spell has that, but regardless, it's a target type after all - //TARGET_UNIT_NEARBY_ENEMY - TARGET_UNIT_SINGLE_UNKNOWN = 3, - TARGET_UNIT_NEARBY_ALLY = 4, + TARGET_UNIT_NEARBY_ENEMY = 2, + TARGET_UNIT_NEARBY_ALLY = 3, + TARGET_UNIT_NEARBY_ALLY_UNK = 4, TARGET_PET = 5, TARGET_UNIT_PET = 5, TARGET_CHAIN_DAMAGE = 6, @@ -816,7 +1022,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 @@ -986,9 +1191,10 @@ enum GameobjectTypes GAMEOBJECT_TYPE_BARBER_CHAIR = 32, GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING = 33, GAMEOBJECT_TYPE_GUILD_BANK = 34, + GAMEOBJECT_TYPE_TRAPDOOR = 35 }; -#define MAX_GAMEOBJECT_TYPE 35 // sending to client this or greater value can crash client. +#define MAX_GAMEOBJECT_TYPE 36 // sending to client this or greater value can crash client. #define GAMEOBJECT_FISHINGNODE_ENTRY 35591 // Better to define it somewhere instead of hardcoding everywhere @@ -1555,7 +1761,9 @@ enum LockType LOCKTYPE_BLASTING = 16, LOCKTYPE_SLOW_OPEN = 17, LOCKTYPE_SLOW_CLOSE = 18, - LOCKTYPE_FISHING = 19 + LOCKTYPE_FISHING = 19, + LOCKTYPE_INSCRIPTION = 20, + LOCKTYPE_OPEN_FROM_VEHICLE = 21 }; enum TrainerType // this is important type for npcs! @@ -1568,6 +1776,7 @@ enum TrainerType // this is important #define MAX_TRAINER_TYPE 4 +// CreatureType.dbc enum CreatureType { CREATURE_TYPE_BEAST = 1, @@ -1587,6 +1796,7 @@ enum CreatureType uint32 const CREATURE_TYPEMASK_HUMANOID_OR_UNDEAD = (1 << (CREATURE_TYPE_HUMANOID-1)) | (1 << (CREATURE_TYPE_UNDEAD-1)); +// CreatureFamily.dbc enum CreatureFamily { CREATURE_FAMILY_WOLF = 1, @@ -1598,6 +1808,7 @@ enum CreatureFamily CREATURE_FAMILY_CARRION_BIRD = 7, CREATURE_FAMILY_CRAB = 8, CREATURE_FAMILY_GORILLA = 9, + CREATURE_FAMILY_HORSE_CUSTOM = 10, // not exist in DBC but used for horse like beasts in DB CREATURE_FAMILY_RAPTOR = 11, CREATURE_FAMILY_TALLSTRIDER = 12, CREATURE_FAMILY_FELHUNTER = 15, @@ -1609,7 +1820,7 @@ enum CreatureFamily CREATURE_FAMILY_IMP = 23, CREATURE_FAMILY_BAT = 24, CREATURE_FAMILY_HYENA = 25, - CREATURE_FAMILY_OWL = 26, + CREATURE_FAMILY_BIRD_OF_PREY = 26, CREATURE_FAMILY_WIND_SERPENT = 27, CREATURE_FAMILY_REMOTE_CONTROL = 28, CREATURE_FAMILY_FELGUARD = 29, @@ -1619,14 +1830,24 @@ enum CreatureFamily CREATURE_FAMILY_SPOREBAT = 33, CREATURE_FAMILY_NETHER_RAY = 34, CREATURE_FAMILY_SERPENT = 35, - CREATURE_FAMILY_SEA_LION = 36 + CREATURE_FAMILY_MOTH = 37, + CREATURE_FAMILY_CHIMAERA = 38, + CREATURE_FAMILY_DEVILSAUR = 39, + CREATURE_FAMILY_GHOUL = 40, + CREATURE_FAMILY_SILITHID = 41, + CREATURE_FAMILY_WORM = 42, + CREATURE_FAMILY_RHINO = 43, + CREATURE_FAMILY_WASP = 44, + CREATURE_FAMILY_CORE_HOUND = 45, + CREATURE_FAMILY_SPIRIT_BEAST = 46 }; enum CreatureTypeFlags { - CREATURE_TYPEFLAGS_TAMEABLE = 0x0001, - CREATURE_TYPEFLAGS_HERBLOOT = 0x0100, - CREATURE_TYPEFLAGS_MININGLOOT = 0x0200 + CREATURE_TYPEFLAGS_TAMEABLE = 0x0001, + CREATURE_TYPEFLAGS_HERBLOOT = 0x0100, + CREATURE_TYPEFLAGS_MININGLOOT = 0x0200, + CREATURE_TYPEFLAGS_ENGINEERLOOT = 0x8000 }; enum CreatureEliteType @@ -1639,6 +1860,31 @@ enum CreatureEliteType CREATURE_UNKNOWN = 5 // found in 2.2.3 for 2 mobs }; +// values based at Holidays.dbc +enum HolidayIds +{ + HOLIDAY_FIREWORKS_SPECTACULAR = 62, + HOLIDAY_FEAST_OF_WINTER_VEIL = 141, + HOLIDAY_NOBLEGARDEN = 181, + HOLIDAY_CHILDRENS_WEEK = 201, + HOLIDAY_CALL_TO_ARMS_AV = 283, + HOLIDAY_CALL_TO_ARMS_WG = 284, + HOLIDAY_CALL_TO_ARMS_AB = 285, + HOLIDAY_FISHING_EXTRAVAGANZA = 301, + HOLIDAY_HARVEST_FESTIVAL = 321, + HOLIDAY_HALLOWS_END = 324, + HOLIDAY_LUNAR_FESTIVAL = 327, + HOLIDAY_LOVE_IS_IN_THE_AIR = 335, + HOLIDAY_FIRE_FESTIVAL = 341, + HOLIDAY_CALL_TO_ARMS_ES = 353, + HOLIDAY_BREWFEST = 372, + HOLIDAY_DARKMOON_FAIRE_ELWYNN = 374, + HOLIDAY_DARKMOON_FAIRE_THUNDER = 375, + HOLIDAY_DARKMOON_FAIRE_SHATTRATH = 376, + HOLIDAY_CALL_TO_ARMS_SA = 400, + HOLIDAY_WOTLK_LAUNCH = 406 +}; + // values based at QuestInfo.dbc enum QuestTypes { @@ -1651,6 +1897,8 @@ enum QuestTypes QUEST_TYPE_LEGENDARY = 83, QUEST_TYPE_ESCORT = 84, QUEST_TYPE_HEROIC = 85, + QUEST_TYPE_RAID_10 = 88, + QUEST_TYPE_RAID_25 = 89 }; // values based at QuestSort.dbc @@ -1661,7 +1909,7 @@ enum QuestSort QUEST_SORT_SEASONAL = 22, QUEST_SORT_UNDERCITY_OLD = 23, QUEST_SORT_HERBALISM = 24, - QUEST_SORT_SCARLET_MONASTERY_OLD= 25, + QUEST_SORT_BATTLEGROUNDS = 25, QUEST_SORT_ULDAMN_OLD = 41, QUEST_SORT_WARLOCK = 61, QUEST_SORT_WARRIOR = 81, @@ -1690,34 +1938,39 @@ enum QuestSort QUEST_SORT_REPUTATION = 367, QUEST_SORT_INVASION = 368, QUEST_SORT_MIDSUMMER = 369, - QUEST_SORT_BREWFEST = 370 + QUEST_SORT_BREWFEST = 370, + QUEST_SORT_INSCRIPTION = 371, + QUEST_SORT_DEATH_KNIGHT = 372, + QUEST_SORT_JEWELCRAFTING = 373 }; inline uint8 ClassByQuestSort(int32 QuestSort) { switch(QuestSort) { - case QUEST_SORT_WARLOCK: return CLASS_WARLOCK; - case QUEST_SORT_WARRIOR: return CLASS_WARRIOR; - case QUEST_SORT_SHAMAN: return CLASS_SHAMAN; - case QUEST_SORT_PALADIN: return CLASS_PALADIN; - case QUEST_SORT_MAGE: return CLASS_MAGE; - case QUEST_SORT_ROGUE: return CLASS_ROGUE; - case QUEST_SORT_HUNTER: return CLASS_HUNTER; - case QUEST_SORT_PRIEST: return CLASS_PRIEST; - case QUEST_SORT_DRUID: return CLASS_DRUID; + case QUEST_SORT_WARLOCK: return CLASS_WARLOCK; + case QUEST_SORT_WARRIOR: return CLASS_WARRIOR; + case QUEST_SORT_SHAMAN: return CLASS_SHAMAN; + case QUEST_SORT_PALADIN: return CLASS_PALADIN; + case QUEST_SORT_MAGE: return CLASS_MAGE; + case QUEST_SORT_ROGUE: return CLASS_ROGUE; + case QUEST_SORT_HUNTER: return CLASS_HUNTER; + case QUEST_SORT_PRIEST: return CLASS_PRIEST; + case QUEST_SORT_DRUID: return CLASS_DRUID; + case QUEST_SORT_DEATH_KNIGHT: return CLASS_DEATH_KNIGHT; } return 0; } enum SkillType { + SKILL_NONE = 0, + SKILL_FROST = 6, SKILL_FIRE = 8, SKILL_ARMS = 26, SKILL_COMBAT = 38, SKILL_SUBTLETY = 39, - SKILL_POISONS = 40, SKILL_SWORDS = 43, SKILL_AXES = 44, SKILL_BOWS = 45, @@ -1725,8 +1978,8 @@ enum SkillType SKILL_BEAST_MASTERY = 50, SKILL_SURVIVAL = 51, SKILL_MACES = 54, - SKILL_HOLY = 56, SKILL_2H_SWORDS = 55, + SKILL_HOLY = 56, SKILL_SHADOW = 78, SKILL_DEFENSE = 95, SKILL_LANG_COMMON = 98, @@ -1782,24 +2035,20 @@ enum SkillType SKILL_PET_BOAR = 211, SKILL_PET_CROCILISK = 212, SKILL_PET_CARRION_BIRD = 213, - SKILL_PET_GORILLA = 215, SKILL_PET_CRAB = 214, + SKILL_PET_GORILLA = 215, SKILL_PET_RAPTOR = 217, SKILL_PET_TALLSTRIDER = 218, SKILL_RACIAL_UNDED = 220, - SKILL_WEAPON_TALENTS = 222, SKILL_CROSSBOWS = 226, - SKILL_SPEARS = 227, SKILL_WANDS = 228, SKILL_POLEARMS = 229, SKILL_PET_SCORPID = 236, SKILL_ARCANE = 237, - SKILL_OPEN_LOCK = 242, SKILL_PET_TURTLE = 251, SKILL_ASSASSINATION = 253, SKILL_FURY = 256, SKILL_PROTECTION = 257, - SKILL_BEAST_TRAINING = 261, SKILL_PROTECTION2 = 267, SKILL_PET_TALENTS = 270, SKILL_PLATE_MAIL = 293, @@ -1829,7 +2078,7 @@ enum SkillType SKILL_LOCKPICKING = 633, SKILL_PET_BAT = 653, SKILL_PET_HYENA = 654, - SKILL_PET_OWL = 655, + SKILL_PET_BIRD_OF_PREY = 655, SKILL_PET_WIND_SERPENT = 656, SKILL_LANG_GUTTERSPEAK = 673, SKILL_RIDING_KODO = 713, @@ -1849,10 +2098,41 @@ enum SkillType SKILL_PET_WARP_STALKER = 766, SKILL_PET_RAVAGER = 767, SKILL_PET_SERPENT = 768, - SKILL_INTERNAL = 769 + SKILL_INTERNAL = 769, + SKILL_DK_BLOOD = 770, + SKILL_DK_FROST = 771, + SKILL_DK_UNHOLY = 772, + SKILL_INSCRIPTION = 773, + SKILL_PET_MOTH = 775, + SKILL_RUNEFORGING = 776, + SKILL_MOUNTS = 777, + SKILL_COMPANIONS = 778, + SKILL_PET_EXOTIC_CHIMAERA = 780, + SKILL_PET_EXOTIC_DEVILSAUR = 781, + SKILL_PET_GHOUL = 782, + SKILL_PET_EXOTIC_SILITHID = 783, + SKILL_PET_EXOTIC_WORM = 784, + SKILL_PET_WASP = 785, + SKILL_PET_EXOTIC_RHINO = 786, + SKILL_PET_EXOTIC_CORE_HOUND = 787, + SKILL_PET_EXOTIC_SPIRIT_BEAST = 788 }; -#define MAX_SKILL_TYPE 770 +#define MAX_SKILL_TYPE 789 + +inline SkillType SkillByLockType(LockType locktype) +{ + switch(locktype) + { + case LOCKTYPE_PICKLOCK: return SKILL_LOCKPICKING; + case LOCKTYPE_HERBALISM: return SKILL_HERBALISM; + case LOCKTYPE_MINING: return SKILL_MINING; + case LOCKTYPE_FISHING: return SKILL_FISHING; + case LOCKTYPE_INSCRIPTION: return SKILL_INSCRIPTION; + default: break; + } + return SKILL_NONE; +} inline uint32 SkillByQuestSort(int32 QuestSort) { @@ -1867,25 +2147,27 @@ inline uint32 SkillByQuestSort(int32 QuestSort) case QUEST_SORT_TAILORING: return SKILL_TAILORING; case QUEST_SORT_COOKING: return SKILL_COOKING; case QUEST_SORT_FIRST_AID: return SKILL_FIRST_AID; + case QUEST_SORT_JEWELCRAFTING: return SKILL_JEWELCRAFTING; + case QUEST_SORT_INSCRIPTION: return SKILL_INSCRIPTION; } return 0; } enum SkillCategory { - SKILL_CATEGORY_ATTRIBUTES = 5, - SKILL_CATEGORY_WEAPON = 6, - SKILL_CATEGORY_CLASS = 7, - SKILL_CATEGORY_ARMOR = 8, - SKILL_CATEGORY_SECONDARY = 9, // secondary professions + SKILL_CATEGORY_ATTRIBUTES = 5, + SKILL_CATEGORY_WEAPON = 6, + SKILL_CATEGORY_CLASS = 7, + SKILL_CATEGORY_ARMOR = 8, + SKILL_CATEGORY_SECONDARY = 9, // secondary professions SKILL_CATEGORY_LANGUAGES = 10, SKILL_CATEGORY_PROFESSION = 11, // primary professions - SKILL_CATEGORY_NOT_DISPLAYED = 12 + SKILL_CATEGORY_GENERIC = 12 }; enum TotemCategory { - TC_SKINNING_SKIFE = 1, + TC_SKINNING_SKIFE_OLD = 1, TC_EARTH_TOTEM = 2, TC_AIR_TOTEM = 3, TC_FIRE_TOTEM = 4, @@ -1895,15 +2177,28 @@ enum TotemCategory TC_GOLDEN_ROD = 8, TC_TRUESILVER_ROD = 9, TC_ARCANITE_ROD = 10, - TC_MINING_PICK = 11, + TC_MINING_PICK_OLD = 11, TC_PHILOSOPHERS_STONE = 12, - TC_BLACKSMITH_HAMMER = 13, + TC_BLACKSMITH_HAMMER_OLD = 13, TC_ARCLIGHT_SPANNER = 14, TC_GYROMATIC_MA = 15, TC_MASTER_TOTEM = 21, TC_FEL_IRON_ROD = 41, TC_ADAMANTITE_ROD = 62, - TC_ETERNIUM_ROD = 63 + TC_ETERNIUM_ROD = 63, + TC_HOLLOW_QUILL = 81, + TC_RUNED_AZURITE_ROD = 101, + TC_VIRTUOSO_INKING_SET = 121, + TC_DRUMS = 141, + TC_GNOMISH_ARMY_KNIFE = 161, + TC_BLACKSMITH_HAMMER = 162, + TC_MINING_PICK = 165, + TC_SKINNING_KNIFE = 166, + TC_HAMMER_PICK = 167, + TC_BLADED_PICKAXE = 168, + TC_FLINT_AND_TINDER = 169, + TC_RUNED_COBALT_ROD = 189, + TC_RUNED_TITANIUM_ROD = 190 }; enum UnitDynFlags @@ -1913,7 +2208,8 @@ enum UnitDynFlags UNIT_DYNFLAG_OTHER_TAGGER = 0x0004, UNIT_DYNFLAG_ROOTED = 0x0008, UNIT_DYNFLAG_SPECIALINFO = 0x0010, - UNIT_DYNFLAG_DEAD = 0x0020 + UNIT_DYNFLAG_DEAD = 0x0020, + UNIT_DYNFLAG_REFER_A_FRIEND = 0x0040 }; enum CorpseDynFlags @@ -1922,11 +2218,14 @@ enum CorpseDynFlags }; // Passive Spell codes explicit used in code -#define SPELL_ID_GENERIC_LEARN 483 -#define SPELL_ID_PASSIVE_BATTLE_STANCE 2457 -#define SPELL_ID_PASSIVE_RESURRECTION_SICKNESS 15007 -#define SPELL_ID_WEAPON_SWITCH_COOLDOWN_1_5s 6119 -#define SPELL_ID_WEAPON_SWITCH_COOLDOWN_1_0s 6123 +#define SPELL_ID_GENERIC_LEARN 483 +#define SPELL_ID_GENERIC_LEARN_PET 55884 // used for learning mounts and companions +#define SPELL_ID_PASSIVE_BATTLE_STANCE 2457 +#define SPELL_ID_PASSIVE_RESURRECTION_SICKNESS 15007 +#define SPELL_ID_WEAPON_SWITCH_COOLDOWN_1_5s 6119 +#define SPELL_ID_WEAPON_SWITCH_COOLDOWN_1_0s 6123 +#define SPELL_ID_AUTOSHOT 75 // used for checks in other spells interruption +#define SPELL_ID_SHADOWMELD 58984 // used for check ignore stealth stance state enum WeatherType { @@ -1990,9 +2289,12 @@ enum ChatMsg CHAT_MSG_BATTLEGROUND = 0x2C, CHAT_MSG_BATTLEGROUND_LEADER = 0x2D, CHAT_MSG_RESTRICTED = 0x2E, + CHAT_MSG_BN = 0x2F, + CHAT_MSG_ACHIEVEMENT = 0x30, + CHAT_MSG_GUILD_ACHIEVEMENT = 0x31 }; -#define MAX_CHAT_MSG_TYPE 0x2F +#define MAX_CHAT_MSG_TYPE 0x32 // Values from ItemPetFood (power of (value-1) used for compare with CreatureFamilyEntry.petDietMask enum PetDiet @@ -2072,27 +2374,45 @@ enum DungeonDifficulties TOTAL_DIFFICULTIES }; +enum SummonCategory +{ + SUMMON_CATEGORY_WILD = 0, + SUMMON_CATEGORY_ALLY = 1, + SUMMON_CATEGORY_PET = 2, + SUMMON_CATEGORY_POSSESSED = 3, + SUMMON_CATEGORY_VEHICLE = 4, +}; + enum SummonType { + SUMMON_TYPE_NONE = 0, + SUMMON_TYPE_PET = 1, + SUMMON_TYPE_GUARDIAN = 2, + SUMMON_TYPE_MINION = 3, + SUMMON_TYPE_TOTEM = 4, + SUMMON_TYPE_MINIPET = 5, + SUMMON_TYPE_GUARDIAN2 = 6, + SUMMON_TYPE_WILD2 = 7, + SUMMON_TYPE_WILD3 = 8, + SUMMON_TYPE_WILD4 = 9, + SUMMON_TYPE_VEHICLE = 10, + SUMMON_TYPE_OBJECT = 11, + SUMMON_TYPE_CRITTER = 41, - SUMMON_TYPE_GUARDIAN = 61, - SUMMON_TYPE_TOTEM_SLOT1 = 63, + //SUMMON_TYPE_GUARDIAN = 61, SUMMON_TYPE_WILD = 64, - SUMMON_TYPE_POSESSED = 65, SUMMON_TYPE_DEMON = 66, SUMMON_TYPE_SUMMON = 67, - SUMMON_TYPE_TOTEM_SLOT2 = 81, - SUMMON_TYPE_TOTEM_SLOT3 = 82, - SUMMON_TYPE_TOTEM_SLOT4 = 83, - SUMMON_TYPE_TOTEM = 121, SUMMON_TYPE_UNKNOWN3 = 181, SUMMON_TYPE_UNKNOWN4 = 187, SUMMON_TYPE_UNKNOWN1 = 247, SUMMON_TYPE_CRITTER2 = 407, SUMMON_TYPE_CRITTER3 = 307, SUMMON_TYPE_UNKNOWN5 = 409, - SUMMON_TYPE_POSESSED3 = 427, - SUMMON_TYPE_POSESSED2 = 428 + SUMMON_TYPE_FROZEN_EARTH = 429, + SUMMON_TYPE_FORCE_OF_NATURE = 669, + SUMMON_TYPE_LIGHTWELL = 1141, + SUMMON_TYPE_FERAL_SPIRIT = 1161 }; enum ResponseCodes @@ -2161,42 +2481,45 @@ enum ResponseCodes CHAR_CREATE_SERVER_QUEUE = 0x37, CHAR_CREATE_ONLY_EXISTING = 0x38, CHAR_CREATE_EXPANSION = 0x39, - - CHAR_DELETE_IN_PROGRESS = 0x3A, - CHAR_DELETE_SUCCESS = 0x3B, - CHAR_DELETE_FAILED = 0x3C, - CHAR_DELETE_FAILED_LOCKED_FOR_TRANSFER = 0x3D, - CHAR_DELETE_FAILED_GUILD_LEADER = 0x3E, - CHAR_DELETE_FAILED_ARENA_CAPTAIN = 0x3F, - - CHAR_LOGIN_IN_PROGRESS = 0x40, - CHAR_LOGIN_SUCCESS = 0x41, - CHAR_LOGIN_NO_WORLD = 0x42, - CHAR_LOGIN_DUPLICATE_CHARACTER = 0x43, - CHAR_LOGIN_NO_INSTANCES = 0x44, - CHAR_LOGIN_FAILED = 0x45, - CHAR_LOGIN_DISABLED = 0x46, - CHAR_LOGIN_NO_CHARACTER = 0x47, - CHAR_LOGIN_LOCKED_FOR_TRANSFER = 0x48, - CHAR_LOGIN_LOCKED_BY_BILLING = 0x49, - - CHAR_NAME_SUCCESS = 0x4A, - CHAR_NAME_FAILURE = 0x4B, - CHAR_NAME_NO_NAME = 0x4C, - CHAR_NAME_TOO_SHORT = 0x4D, - CHAR_NAME_TOO_LONG = 0x4E, - CHAR_NAME_INVALID_CHARACTER = 0x4F, - CHAR_NAME_MIXED_LANGUAGES = 0x50, - CHAR_NAME_PROFANE = 0x51, - CHAR_NAME_RESERVED = 0x52, - CHAR_NAME_INVALID_APOSTROPHE = 0x53, - CHAR_NAME_MULTIPLE_APOSTROPHES = 0x54, - CHAR_NAME_THREE_CONSECUTIVE = 0x55, - CHAR_NAME_INVALID_SPACE = 0x56, - CHAR_NAME_CONSECUTIVE_SPACES = 0x57, - CHAR_NAME_RUSSIAN_CONSECUTIVE_SILENT_CHARACTERS = 0x58, - CHAR_NAME_RUSSIAN_SILENT_CHARACTER_AT_BEGINNING_OR_END = 0x59, - CHAR_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME = 0x5A, + CHAR_CREATE_EXPANSION_CLASS = 0x3A, + CHAR_CREATE_LEVEL_REQUIREMENT = 0x3B, + CHAR_CREATE_UNIQUE_CLASS_LIMIT = 0x3C, + + CHAR_DELETE_IN_PROGRESS = 0x3D, + CHAR_DELETE_SUCCESS = 0x3E, + CHAR_DELETE_FAILED = 0x3F, + CHAR_DELETE_FAILED_LOCKED_FOR_TRANSFER = 0x40, + CHAR_DELETE_FAILED_GUILD_LEADER = 0x41, + CHAR_DELETE_FAILED_ARENA_CAPTAIN = 0x42, + + CHAR_LOGIN_IN_PROGRESS = 0x43, + CHAR_LOGIN_SUCCESS = 0x44, + CHAR_LOGIN_NO_WORLD = 0x45, + CHAR_LOGIN_DUPLICATE_CHARACTER = 0x46, + CHAR_LOGIN_NO_INSTANCES = 0x47, + CHAR_LOGIN_FAILED = 0x48, + CHAR_LOGIN_DISABLED = 0x49, + CHAR_LOGIN_NO_CHARACTER = 0x4A, + CHAR_LOGIN_LOCKED_FOR_TRANSFER = 0x4B, + CHAR_LOGIN_LOCKED_BY_BILLING = 0x4C, + + CHAR_NAME_SUCCESS = 0x4D, + CHAR_NAME_FAILURE = 0x4E, + CHAR_NAME_NO_NAME = 0x4F, + CHAR_NAME_TOO_SHORT = 0x50, + CHAR_NAME_TOO_LONG = 0x51, + CHAR_NAME_INVALID_CHARACTER = 0x52, + CHAR_NAME_MIXED_LANGUAGES = 0x53, + CHAR_NAME_PROFANE = 0x54, + CHAR_NAME_RESERVED = 0x55, + CHAR_NAME_INVALID_APOSTROPHE = 0x56, + CHAR_NAME_MULTIPLE_APOSTROPHES = 0x57, + CHAR_NAME_THREE_CONSECUTIVE = 0x58, + CHAR_NAME_INVALID_SPACE = 0x59, + CHAR_NAME_CONSECUTIVE_SPACES = 0x5A, + CHAR_NAME_RUSSIAN_CONSECUTIVE_SILENT_CHARACTERS = 0x5B, + CHAR_NAME_RUSSIAN_SILENT_CHARACTER_AT_BEGINNING_OR_END = 0x5C, + CHAR_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME = 0x5D }; /// Ban function modes @@ -2214,5 +2537,24 @@ enum BanReturn BAN_SYNTAX_ERROR, BAN_NOTFOUND }; + +// indexes of BattlemasterList.dbc +enum BattleGroundTypeId +{ + BATTLEGROUND_TYPE_NONE = 0, + BATTLEGROUND_AV = 1, + BATTLEGROUND_WS = 2, + BATTLEGROUND_AB = 3, + BATTLEGROUND_NA = 4, + BATTLEGROUND_BE = 5, + BATTLEGROUND_AA = 6, + BATTLEGROUND_EY = 7, + BATTLEGROUND_RL = 8, + BATTLEGROUND_SA = 9, + BATTLEGROUND_DS = 10, + BATTLEGROUND_RV = 11 +}; +#define MAX_BATTLEGROUND_TYPE_ID 12 + #endif diff --git a/src/game/SkillDiscovery.cpp b/src/game/SkillDiscovery.cpp index 5a5869047a5..0fbaeb0fbaf 100644 --- a/src/game/SkillDiscovery.cpp +++ b/src/game/SkillDiscovery.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,7 +22,6 @@ #include "Log.h" #include "ProgressBar.h" #include "Policies/SingletonImp.h" -#include "ObjectAccessor.h" #include "World.h" #include "Util.h" #include "SkillDiscovery.h" @@ -31,14 +30,15 @@ struct SkillDiscoveryEntry { - uint32 spellId; - float chance; + uint32 spellId; // discavered spell + uint32 reqSkillValue; // skill level limitation + float chance; // chance SkillDiscoveryEntry() - : spellId(0), chance(0) {} + : spellId(0), reqSkillValue(0), chance(0) {} - SkillDiscoveryEntry(uint16 _spellId, float _chance) - : spellId(_spellId), chance(_chance) {} + SkillDiscoveryEntry(uint16 _spellId, uint32 req_skill_val, float _chance) + : spellId(_spellId), reqSkillValue(req_skill_val), chance(_chance) {} }; typedef std::list<SkillDiscoveryEntry> SkillDiscoveryList; @@ -53,8 +53,8 @@ void LoadSkillDiscoveryTable() uint32 count = 0; - // 0 1 2 - QueryResult *result = WorldDatabase.Query("SELECT spellId, reqSpell, chance FROM skill_discovery_template"); + // 0 1 2 3 + QueryResult *result = WorldDatabase.Query("SELECT spellId, reqSpell, reqSkillValue, chance FROM skill_discovery_template"); if (result) { @@ -69,11 +69,13 @@ void LoadSkillDiscoveryTable() uint32 spellId = fields[0].GetUInt32(); int32 reqSkillOrSpell = fields[1].GetInt32(); - float chance = fields[2].GetFloat(); + uint32 reqSkillValue = fields[2].GetInt32(); + float chance = fields[3].GetFloat(); if( chance <= 0 ) // chance { - ssNonDiscoverableEntries << "spellId = " << spellId << " reqSkillOrSpell = " << reqSkillOrSpell << " chance = " << chance << "\n"; + ssNonDiscoverableEntries << "spellId = " << spellId << " reqSkillOrSpell = " << reqSkillOrSpell + << " reqSkillValue = " << reqSkillValue << " chance = " << chance << "(chance problem)\n"; continue; } @@ -86,13 +88,16 @@ void LoadSkillDiscoveryTable() continue; } - if( spellEntry->Mechanic != MECHANIC_DISCOVERY ) + // mechanic discovery + if (spellEntry->Mechanic != MECHANIC_DISCOVERY && + // explicit discovery ability + !IsExplicitDiscoverySpell(spellEntry)) { - sLog.outErrorDb("Spell (ID: %u) not have have MECHANIC_DISCOVERY (28) value in Mechanic field in spell.dbc but listed in `skill_discovery_template` table",spellId); + sLog.outErrorDb("Spell (ID: %u) not have have MECHANIC_DISCOVERY (28) value in Mechanic field in spell.dbc and not 100% chance random discovery ability but listed in `skill_discovery_template` table",spellId); continue; } - SkillDiscoveryStore[reqSkillOrSpell].push_back( SkillDiscoveryEntry(spellId, chance) ); + SkillDiscoveryStore[reqSkillOrSpell].push_back( SkillDiscoveryEntry(spellId, reqSkillValue, chance) ); } else if( reqSkillOrSpell == 0 ) // skill case { @@ -107,7 +112,7 @@ void LoadSkillDiscoveryTable() for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx) { - SkillDiscoveryStore[-int32(_spell_idx->second->skillId)].push_back( SkillDiscoveryEntry(spellId, chance) ); + SkillDiscoveryStore[-int32(_spell_idx->second->skillId)].push_back( SkillDiscoveryEntry(spellId, reqSkillValue, chance) ); } } else @@ -115,6 +120,7 @@ void LoadSkillDiscoveryTable() sLog.outErrorDb("Spell (ID: %u) have negative value in `reqSpell` field in `skill_discovery_template` table",spellId); continue; } + ++count; } while (result->NextRow()); @@ -132,8 +138,48 @@ void LoadSkillDiscoveryTable() } } +uint32 GetExplicitDiscoverySpell(uint32 spellId, Player* player) +{ + // explicit discovery spell chances (always success if case exist) + // in this case we have both skill and spell + SkillDiscoveryMap::iterator tab = SkillDiscoveryStore.find(spellId); + if(tab == SkillDiscoveryStore.end()) + return 0; + + SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spellId); + SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spellId); + uint32 skillvalue = lower != upper ? player->GetSkillValue(lower->second->skillId) : 0; + + float full_chance = 0; + for(SkillDiscoveryList::iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter) + if(item_iter->reqSkillValue <= skillvalue) + if(!player->HasSpell(item_iter->spellId)) + full_chance += item_iter->chance; + + float rate = full_chance / 100.0f; + float roll = rand_chance() * rate; // roll now in range 0..full_chance + + for(SkillDiscoveryList::iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter) + { + if(item_iter->reqSkillValue > skillvalue) + continue; + + if(player->HasSpell(item_iter->spellId)) + continue; + + if(item_iter->chance > roll) + return item_iter->spellId; + + roll -= item_iter->chance; + } + + return 0; +} + uint32 GetSkillDiscoverySpell(uint32 skillId, uint32 spellId, Player* player) { + uint32 skillvalue = skillId ? player->GetSkillValue(skillId) : 0; + // check spell case SkillDiscoveryMap::iterator tab = SkillDiscoveryStore.find(spellId); @@ -142,6 +188,7 @@ uint32 GetSkillDiscoverySpell(uint32 skillId, uint32 spellId, Player* player) for(SkillDiscoveryList::iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter) { if( roll_chance_f(item_iter->chance * sWorld.getRate(RATE_SKILL_DISCOVERY)) + && item_iter->reqSkillValue <= skillvalue && !player->HasSpell(item_iter->spellId) ) return item_iter->spellId; } @@ -149,6 +196,9 @@ uint32 GetSkillDiscoverySpell(uint32 skillId, uint32 spellId, Player* player) return 0; } + if(!skillId) + return 0; + // check skill line case tab = SkillDiscoveryStore.find(-(int32)skillId); if(tab != SkillDiscoveryStore.end()) @@ -156,6 +206,7 @@ uint32 GetSkillDiscoverySpell(uint32 skillId, uint32 spellId, Player* player) for(SkillDiscoveryList::iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter) { if( roll_chance_f(item_iter->chance * sWorld.getRate(RATE_SKILL_DISCOVERY)) + && item_iter->reqSkillValue <= skillvalue && !player->HasSpell(item_iter->spellId) ) return item_iter->spellId; } diff --git a/src/game/SkillDiscovery.h b/src/game/SkillDiscovery.h index 350062101fe..9981eb133f6 100644 --- a/src/game/SkillDiscovery.h +++ b/src/game/SkillDiscovery.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,5 +27,6 @@ class Player; void LoadSkillDiscoveryTable(); uint32 GetSkillDiscoverySpell(uint32 skillId, uint32 spellId, Player* player); +uint32 GetExplicitDiscoverySpell(uint32 spellId, Player* player); #endif diff --git a/src/game/SkillExtraItems.cpp b/src/game/SkillExtraItems.cpp index d89cbbdcddf..f0061f97c00 100644 --- a/src/game/SkillExtraItems.cpp +++ b/src/game/SkillExtraItems.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/SkillExtraItems.h b/src/game/SkillExtraItems.h index 38684b9933a..3a44619a862 100644 --- a/src/game/SkillExtraItems.h +++ b/src/game/SkillExtraItems.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/SkillHandler.cpp b/src/game/SkillHandler.cpp index 973d0bf9c39..06a7317d09d 100644 --- a/src/game/SkillHandler.cpp +++ b/src/game/SkillHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,12 +23,10 @@ #include "Opcodes.h" #include "Log.h" #include "Player.h" -#include "World.h" #include "WorldPacket.h" #include "WorldSession.h" #include "ObjectAccessor.h" #include "UpdateMask.h" -#include "SpellAuras.h" void WorldSession::HandleLearnTalentOpcode( WorldPacket & recv_data ) { @@ -37,107 +35,7 @@ void WorldSession::HandleLearnTalentOpcode( WorldPacket & recv_data ) uint32 talent_id, requested_rank; recv_data >> talent_id >> requested_rank; - uint32 CurTalentPoints = GetPlayer()->GetFreeTalentPoints(); - - if(CurTalentPoints == 0) - return; - - if (requested_rank > 4) - return; - - TalentEntry const *talentInfo = sTalentStore.LookupEntry( talent_id ); - - if(!talentInfo) - return; - - TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab ); - - if(!talentTabInfo) - return; - - Player * player = GetPlayer(); - - // prevent learn talent for different class (cheating) - if( (player->getClassMask() & talentTabInfo->ClassMask) == 0 ) - return; - - // prevent skip talent ranks (cheating) - if(requested_rank > 0 && !player->HasSpell(talentInfo->RankID[requested_rank-1])) - return; - - // Check if it requires another talent - if (talentInfo->DependsOn > 0) - { - if(TalentEntry const *depTalentInfo = sTalentStore.LookupEntry(talentInfo->DependsOn)) - { - bool hasEnoughRank = false; - for (int i = talentInfo->DependsOnRank; i <= 4; i++) - { - if (depTalentInfo->RankID[i] != 0) - if (player->HasSpell(depTalentInfo->RankID[i])) - hasEnoughRank = true; - } - if (!hasEnoughRank) - return; - } - } - - // Check if it requires spell - if( talentInfo->DependsOnSpell && !player->HasSpell(talentInfo->DependsOnSpell) ) - return; - - // Find out how many points we have in this field - uint32 spentPoints = 0; - - uint32 tTab = talentInfo->TalentTab; - if (talentInfo->Row > 0) - { - unsigned int numRows = sTalentStore.GetNumRows(); - for (unsigned int i = 0; i < numRows; i++) // Loop through all talents. - { - // Someday, someone needs to revamp - const TalentEntry *tmpTalent = sTalentStore.LookupEntry(i); - if (tmpTalent) // the way talents are tracked - { - if (tmpTalent->TalentTab == tTab) - { - for (int j = 0; j <= 4; j++) - { - if (tmpTalent->RankID[j] != 0) - { - if (player->HasSpell(tmpTalent->RankID[j])) - { - spentPoints += j + 1; - } - } - } - } - } - } - } - - // not have required min points spent in talent tree - if(spentPoints < (talentInfo->Row * 5)) - return; - - // spell not set in talent.dbc - uint32 spellid = talentInfo->RankID[requested_rank]; - if( spellid == 0 ) - { - sLog.outError("Talent.dbc have for talent: %u Rank: %u spell id = 0", talent_id, requested_rank); - return; - } - - // already known - if(GetPlayer( )->HasSpell(spellid)) - return; - - // learn! (other talent ranks will unlearned at learning) - GetPlayer( )->learnSpell(spellid); - sLog.outDetail("TalentID: %u Rank: %u Spell: %u\n", talent_id, requested_rank, spellid); - - // update free talent points - GetPlayer()->SetFreeTalentPoints(CurTalentPoints - 1); + _player->LearnTalent(talent_id, requested_rank); } void WorldSession::HandleTalentWipeOpcode( WorldPacket & recv_data ) @@ -157,7 +55,7 @@ void WorldSession::HandleTalentWipeOpcode( WorldPacket & recv_data ) // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); if(!(_player->resetTalents())) { diff --git a/src/game/SocialMgr.cpp b/src/game/SocialMgr.cpp index a0338f4abb1..529517d824a 100644 --- a/src/game/SocialMgr.cpp +++ b/src/game/SocialMgr.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,7 +23,6 @@ #include "Database/DatabaseEnv.h" #include "Opcodes.h" #include "WorldPacket.h" -#include "WorldSession.h" #include "Player.h" #include "ObjectMgr.h" #include "World.h" @@ -182,13 +181,6 @@ SocialMgr::~SocialMgr() } -void SocialMgr::RemovePlayerSocial(uint32 guid) -{ - SocialMap::iterator itr = m_socialMap.find(guid); - if(itr != m_socialMap.end()) - m_socialMap.erase(itr); -} - void SocialMgr::GetFriendInfo(Player *player, uint32 friendGUID, FriendInfo &friendInfo) { if(!player) @@ -197,7 +189,7 @@ void SocialMgr::GetFriendInfo(Player *player, uint32 friendGUID, FriendInfo &fri Player *pFriend = ObjectAccessor::FindPlayer(friendGUID); if(!pFriend) - return; + return; uint32 team = player->GetTeam(); uint32 security = player->GetSession()->GetSecurity(); @@ -285,7 +277,7 @@ void SocialMgr::BroadcastToFriendListers(Player *player, WorldPacket *packet) for(SocialMap::iterator itr = m_socialMap.begin(); itr != m_socialMap.end(); ++itr) { - PlayerSocialMap::iterator itr2 = itr->second.m_playerSocialMap.find(guid); + PlayerSocialMap::const_iterator itr2 = itr->second.m_playerSocialMap.find(guid); if(itr2 != itr->second.m_playerSocialMap.end() && (itr2->second.Flags & SOCIAL_FLAG_FRIEND)) { Player *pFriend = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER)); diff --git a/src/game/SocialMgr.h b/src/game/SocialMgr.h index f5bf3c8600c..1ca04d69707 100644 --- a/src/game/SocialMgr.h +++ b/src/game/SocialMgr.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -143,7 +143,8 @@ class SocialMgr SocialMgr(); ~SocialMgr(); // Misc - void RemovePlayerSocial(uint32 guid); + void RemovePlayerSocial(uint32 guid) { m_socialMap.erase(guid); } + void GetFriendInfo(Player *player, uint32 friendGUID, FriendInfo &friendInfo); // Packet management void MakeFriendStatusPacket(FriendsResult result, uint32 friend_guid, WorldPacket *data); diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index ee9855dea09..25149245037 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,7 +35,6 @@ #include "Unit.h" #include "Spell.h" #include "DynamicObject.h" -#include "SpellAuras.h" #include "Group.h" #include "UpdateData.h" #include "MapManager.h" @@ -43,14 +42,13 @@ #include "CellImpl.h" #include "Policies/SingletonImp.h" #include "SharedDefines.h" -#include "Tools.h" #include "LootMgr.h" #include "VMapFactory.h" #include "BattleGround.h" #include "Util.h" #include "TemporarySummon.h" -#define SPELL_CHANNEL_UPDATE_INTERVAL 1000 +#define SPELL_CHANNEL_UPDATE_INTERVAL (1*IN_MILISECONDS) extern pEffect SpellEffects[TOTAL_SPELL_EFFECTS]; @@ -120,6 +118,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; @@ -171,6 +177,8 @@ bool SpellCastTargets::read ( WorldPacket * data, Unit *caster ) if(data->rpos()+4 > data->size()) return false; + //data->hexlike(); + *data >> m_targetMask; sLog.outDebug("Spell read, target mask = %u", m_targetMask); @@ -178,19 +186,23 @@ bool SpellCastTargets::read ( WorldPacket * data, Unit *caster ) return true; // TARGET_FLAG_UNK2 is used for non-combat pets, maybe other? - if( m_targetMask & (TARGET_FLAG_UNIT|TARGET_FLAG_UNK2) ) - if(!readGUID(*data, m_unitTargetGUID)) + if( m_targetMask & ( TARGET_FLAG_UNIT | TARGET_FLAG_UNK2 )) + if(!data->readPackGUID(m_unitTargetGUID)) return false; - if( m_targetMask & ( TARGET_FLAG_OBJECT | TARGET_FLAG_OBJECT_UNK )) - if(!readGUID(*data, m_GOTargetGUID)) + if( m_targetMask & ( TARGET_FLAG_OBJECT )) + if(!data->readPackGUID(m_GOTargetGUID)) return false; if(( m_targetMask & ( TARGET_FLAG_ITEM | TARGET_FLAG_TRADE_ITEM )) && caster->GetTypeId() == TYPEID_PLAYER) - if(!readGUID(*data, m_itemTargetGUID)) + if(!data->readPackGUID(m_itemTargetGUID)) + return false; + + if( m_targetMask & (TARGET_FLAG_CORPSE | TARGET_FLAG_PVP_CORPSE ) ) + if(!data->readPackGUID(m_CorpseTargetGUID)) return false; - /*if( m_targetMask & TARGET_FLAG_SOURCE_LOCATION ) + if( m_targetMask & TARGET_FLAG_SOURCE_LOCATION ) { if(data->rpos()+4+4+4 > data->size()) return false; @@ -198,17 +210,23 @@ bool SpellCastTargets::read ( WorldPacket * data, Unit *caster ) *data >> m_srcX >> m_srcY >> m_srcZ; if(!Trinity::IsValidMapCoord(m_srcX, m_srcY, m_srcZ)) return false; - }*/ - if( m_targetMask & (TARGET_FLAG_SOURCE_LOCATION | TARGET_FLAG_DEST_LOCATION) ) + m_hasDest = true; + } + + if( m_targetMask & TARGET_FLAG_DEST_LOCATION ) { - if(data->rpos()+4+4+4 > data->size()) + if(data->rpos()+1+4+4+4 > data->size()) + return false; + + if(!data->readPackGUID(m_unitTargetGUID)) return false; *data >> m_destX >> m_destY >> m_destZ; - m_hasDest = true; if(!Trinity::IsValidMapCoord(m_destX, m_destY, m_destZ)) return false; + + m_hasDest = true; } if( m_targetMask & TARGET_FLAG_STRING ) @@ -219,10 +237,6 @@ bool SpellCastTargets::read ( WorldPacket * data, Unit *caster ) *data >> m_strTarget; } - if( m_targetMask & (TARGET_FLAG_CORPSE | TARGET_FLAG_PVP_CORPSE ) ) - if(!readGUID(*data, m_CorpseTargetGUID)) - return false; - // find real units/GOs Update(caster); return true; @@ -242,7 +256,7 @@ void SpellCastTargets::write ( WorldPacket * data ) else *data << uint8(0); } - else if( m_targetMask & ( TARGET_FLAG_OBJECT | TARGET_FLAG_OBJECT_UNK ) ) + else if( m_targetMask & TARGET_FLAG_OBJECT ) { if(m_GOTarget) data->append(m_GOTarget->GetPackGUID()); @@ -267,7 +281,14 @@ void SpellCastTargets::write ( WorldPacket * data ) *data << m_srcX << m_srcY << m_srcZ; if( m_targetMask & TARGET_FLAG_DEST_LOCATION ) + { + if(m_unitTarget) + data->append(m_unitTarget->GetPackGUID()); + else + *data << uint8(0); + *data << m_destX << m_destY << m_destZ; + } if( m_targetMask & TARGET_FLAG_STRING ) *data << m_strTarget; @@ -286,6 +307,7 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi m_triggeringContainer = triggeringContainer; m_referencedFromCurrentSpell = false; m_executedCurrently = false; + m_needComboPoints = NeedsComboPoints(m_spellInfo); m_delayStart = 0; m_delayAtDamageCount = 0; @@ -305,7 +327,7 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi break; default: // Wands - if (m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_REQ_WAND) + if (m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_AUTOREPEAT_FLAG) m_attackType = RANGED_ATTACK; else m_attackType = BASE_ATTACK; @@ -320,7 +342,7 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi if((m_caster->getClassMask() & CLASSMASK_WAND_USERS) != 0 && m_caster->GetTypeId()==TYPEID_PLAYER) { if(Item* pItem = ((Player*)m_caster)->GetWeaponForAttack(RANGED_ATTACK)) - m_spellSchoolMask = SpellSchoolMask(1 << pItem->GetProto()->Damage->DamageType); + m_spellSchoolMask = SpellSchoolMask(1 << pItem->GetProto()->Damage[0].DamageType); } } // Set health leech amount to zero @@ -355,14 +377,15 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi gameObjTarget = NULL; focusObject = NULL; m_cast_count = 0; + m_glyphIndex = 0; + m_preCastSpell = 0; m_triggeredByAuraSpell = NULL; + m_spellAura = NULL; - //Auto Shot & Shoot - if( m_spellInfo->AttributesEx2 == 0x000020 && !triggered ) - m_autoRepeat = true; - else - m_autoRepeat = false; + //Auto Shot & Shoot (wand) + m_autoRepeat = IsAutoRepeatRangedSpell(m_spellInfo); + m_runesState = 0; m_powerCost = 0; // setup to correct value in Spell::prepare, don't must be used before. m_casttime = 0; // setup to correct value in Spell::prepare, don't must be used before. m_timer = 0; // will set to castime in prepare @@ -372,7 +395,7 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi // determine reflection m_canReflect = false; - if(m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && (m_spellInfo->AttributesEx2 & 0x4)==0) + if(m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && !(m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_CANT_REFLECTED)) { for(int j=0;j<3;j++) { @@ -457,11 +480,11 @@ void Spell::FillTargetMap() { // non-standard target selection SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex); - float max_range = GetSpellMaxRange(srange); + float max_range = GetSpellMaxRangeForHostile(srange); WorldObject* result = NULL; Trinity::CannibalizeObjectCheck u_check(m_caster, max_range); - Trinity::WorldObjectSearcher<Trinity::CannibalizeObjectCheck > searcher(result, u_check); + Trinity::WorldObjectSearcher<Trinity::CannibalizeObjectCheck > searcher(m_caster, result, u_check); m_caster->VisitNearbyGridObject(max_range, searcher); if(!result) m_caster->VisitNearbyWorldObject(max_range, searcher); @@ -511,12 +534,15 @@ void Spell::FillTargetMap() case SPELL_EFFECT_CREATE_ITEM: case SPELL_EFFECT_TRIGGER_SPELL: case SPELL_EFFECT_SKILL_STEP: + case SPELL_EFFECT_PROFICIENCY: + case SPELL_EFFECT_SUMMON_OBJECT_WILD: case SPELL_EFFECT_SELF_RESURRECT: case SPELL_EFFECT_REPUTATION: case SPELL_EFFECT_LEARN_SPELL: if(m_targets.getUnitTarget()) tmpUnitMap.push_back(m_targets.getUnitTarget()); - else + // Triggered spells have additional spell targets - cast them even if no explicit unit target is given (required for spell 50516 for example) + else if(m_spellInfo->Effect[i] == SPELL_EFFECT_TRIGGER_SPELL) tmpUnitMap.push_back(m_caster); break; case SPELL_EFFECT_SUMMON_PLAYER: @@ -543,19 +569,22 @@ void Spell::FillTargetMap() break; case SPELL_EFFECT_SUMMON_CHANGE_ITEM: case SPELL_EFFECT_ADD_FARSIGHT: + case SPELL_EFFECT_APPLY_GLYPH: case SPELL_EFFECT_STUCK: + case SPELL_EFFECT_FEED_PET: case SPELL_EFFECT_DESTROY_ALL_TOTEMS: tmpUnitMap.push_back(m_caster); break; case SPELL_EFFECT_LEARN_PET_SPELL: - if(Pet* pet = m_caster->GetPet()) + if(Guardian* pet = m_caster->GetGuardianPet()) tmpUnitMap.push_back(pet); break; /*case SPELL_EFFECT_ENCHANT_ITEM: case SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY: + case SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC: case SPELL_EFFECT_DISENCHANT: - case SPELL_EFFECT_FEED_PET: case SPELL_EFFECT_PROSPECTING: + case SPELL_EFFECT_MILLING: if(m_targets.getItemTarget()) AddItemTarget(m_targets.getItemTarget(), i); break;*/ @@ -596,13 +625,12 @@ void Spell::FillTargetMap() } } - if(IsChanneledSpell(m_spellInfo) && !tmpUnitMap.empty()) m_needAliveTargetMask |= (1<<i); for (std::list<Unit*>::iterator itr = tmpUnitMap.begin() ; itr != tmpUnitMap.end();) { - if(!CheckTarget(*itr, i, false )) + if(!CheckTarget(*itr, i)) { itr = tmpUnitMap.erase(itr); continue; @@ -633,37 +661,43 @@ void Spell::prepareDataForTriggerSystem() // Ñan spell trigger another or not ( m_canTrigger ) // Create base triggers flags for Attacker and Victim ( m_procAttacker and m_procVictim) //========================================================================================== - // Fill flag can spell trigger or not - if (!m_IsTriggeredSpell) + // TODO: possible exist spell attribute for this + m_canTrigger = false; + + if (m_CastItem) + m_canTrigger = false; // Do not trigger from item cast spell + else if (!m_IsTriggeredSpell) m_canTrigger = true; // Normal cast - can trigger else if (!m_triggeredByAuraSpell) m_canTrigger = true; // Triggered from SPELL_EFFECT_TRIGGER_SPELL - can trigger - else // Exceptions (some periodic triggers) + + if (!m_canTrigger) // Exceptions (some periodic triggers) { - m_canTrigger = false; // Triggered spells can`t trigger another switch (m_spellInfo->SpellFamilyName) { case SPELLFAMILY_MAGE: // Arcane Missles / Blizzard triggers need do it - if (m_spellInfo->SpellFamilyFlags & 0x0000000000200080LL) m_canTrigger = true; + if (m_spellInfo->SpellFamilyFlags[0] & 0x200080) m_canTrigger = true; break; case SPELLFAMILY_WARLOCK: // For Hellfire Effect / Rain of Fire / Seed of Corruption triggers need do it - if (m_spellInfo->SpellFamilyFlags & 0x0000800000000060LL) m_canTrigger = true; + if (m_spellInfo->SpellFamilyFlags[1] & 0x00008000 || m_spellInfo->SpellFamilyFlags[0] & 0x00000060) m_canTrigger = true; + break; + case SPELLFAMILY_PRIEST: // For Penance heal/damage triggers need do it + if (m_spellInfo->SpellFamilyFlags[1] & 0x00018000) m_canTrigger = true; break; - case SPELLFAMILY_HUNTER: // Hunter Explosive Trap Effect/Immolation Trap Effect/Frost Trap Aura/Snake Trap Effect - if (m_spellInfo->SpellFamilyFlags & 0x0000200000000014LL) m_canTrigger = true; + case SPELLFAMILY_ROGUE: // For poisons need do it + if (m_spellInfo->SpellFamilyFlags[1] & 0x00000010 || m_spellInfo->SpellFamilyFlags[0] & 0x1001E000) m_canTrigger = true; break; - case SPELLFAMILY_PALADIN: // For Holy Shock triggers need do it - if (m_spellInfo->SpellFamilyFlags & 0x0001000000200000LL) m_canTrigger = true; + case SPELLFAMILY_HUNTER: // Hunter Rapid Killing/Explosive Trap Effect/Immolation Trap Effect/Frost Trap Aura/Snake Trap Effect/Explosive Shot + if (m_spellInfo->SpellFamilyFlags[1] & 0x01002000 + || m_spellInfo->SpellFamilyFlags[0] & 0x00000214 || + m_spellInfo->SpellFamilyFlags[2] & 0x200) m_canTrigger = true; break; - case SPELLFAMILY_ROGUE: // mutilate mainhand + offhand - if (m_spellInfo->SpellFamilyFlags & 0x600000000LL) m_canTrigger = true; + case SPELLFAMILY_PALADIN: // For Judgements (all) / Holy Shock triggers need do it + if (m_spellInfo->SpellFamilyFlags[1] & 0x00010009 || m_spellInfo->SpellFamilyFlags[0] & 0x00B80400) m_canTrigger = true; break; } } - // Do not trigger from item cast spell - if (m_CastItem) - m_canTrigger = false; // Get data for type of attack and fill base info for trigger switch (m_spellInfo->DmgClass) @@ -673,21 +707,30 @@ void Spell::prepareDataForTriggerSystem() m_procVictim = PROC_FLAG_TAKEN_MELEE_SPELL_HIT; break; case SPELL_DAMAGE_CLASS_RANGED: - m_procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_SPELL_HIT; - m_procVictim = PROC_FLAG_TAKEN_RANGED_SPELL_HIT; + // Auto attack + if (m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_AUTOREPEAT_FLAG) + { + m_procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_HIT; + m_procVictim = PROC_FLAG_TAKEN_RANGED_HIT; + } + else // Ranged spell attack + { + m_procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_SPELL_HIT; + m_procVictim = PROC_FLAG_TAKEN_RANGED_SPELL_HIT; + } break; default: - if (IsPositiveSpell(m_spellInfo->Id)) // Check for positive spell + if (IsPositiveSpell(m_spellInfo->Id)) // Check for positive spell { m_procAttacker = PROC_FLAG_SUCCESSFUL_POSITIVE_SPELL; m_procVictim = PROC_FLAG_TAKEN_POSITIVE_SPELL; } - else if (m_spellInfo->Id == 5019) // Wands + else if (m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_AUTOREPEAT_FLAG) // Wands auto attack { - m_procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_SPELL_HIT; - m_procVictim = PROC_FLAG_TAKEN_RANGED_SPELL_HIT; + m_procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_HIT; + m_procVictim = PROC_FLAG_TAKEN_RANGED_HIT; } - else + else // Negative spell { m_procAttacker = PROC_FLAG_SUCCESSFUL_NEGATIVE_SPELL_HIT; m_procVictim = PROC_FLAG_TAKEN_NEGATIVE_SPELL_HIT; @@ -696,7 +739,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[1] & 0x00002000 || m_spellInfo->SpellFamilyFlags[0] & 0x1C)) m_procAttacker |= PROC_FLAG_ON_TRAP_ACTIVATION; } @@ -705,8 +748,6 @@ void Spell::CleanupTargetList() m_UniqueTargetInfo.clear(); m_UniqueGOTargetInfo.clear(); m_UniqueItemInfo.clear(); - m_countOfHit = 0; - m_countOfMiss = 0; m_delayMoment = 0; } @@ -715,6 +756,9 @@ void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex) if( m_spellInfo->Effect[effIndex]==0 ) return; + // Check for effect immune skip if immuned + bool immuned = pVictim->IsImmunedToSpellEffect(m_spellInfo, effIndex); + uint64 targetGUID = pVictim->GetGUID(); // Lookup target in already in list @@ -722,7 +766,8 @@ void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex) { if (targetGUID == ihit->targetGUID) // Found in list { - ihit->effectMask |= 1<<effIndex; // Add only effect mask + if (!immuned) + ihit->effectMask |= 1<<effIndex; // Add only effect mask if not immuned return; } } @@ -732,7 +777,7 @@ void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex) // Get spell hit result on target TargetInfo target; target.targetGUID = targetGUID; // Store target GUID - target.effectMask = 1<<effIndex; // Store index of effect + target.effectMask = immuned ? 0 : 1<<effIndex; // Store index of effect if not immuned target.processed = false; // Effects not apply on target target.damage = 0; @@ -746,11 +791,6 @@ void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex) else target.missCondition = SPELL_MISS_EVADE; //SPELL_MISS_NONE; - if (target.missCondition == SPELL_MISS_NONE) - ++m_countOfHit; - else - ++m_countOfMiss; - // Spell have speed - need calculate incoming time if (m_spellInfo->speed > 0.0f) { @@ -831,8 +871,6 @@ void Spell::AddGOTarget(GameObject* pVictim, uint32 effIndex) else target.timeDelay = 0LL; - ++m_countOfHit; - // Add target to list m_UniqueGOTargetInfo.push_back(target); } @@ -866,83 +904,6 @@ void Spell::AddItemTarget(Item* pitem, uint32 effIndex) target.effectMask = 1<<effIndex; m_UniqueItemInfo.push_back(target); } -/* -void Spell::doTriggers(SpellMissInfo missInfo, uint32 damage, SpellSchoolMask damageSchoolMask, uint32 block, uint32 absorb, bool crit) -{ - // Do triggers depends from hit result (triggers on hit do in effects) - // Set aura states depends from hit result - if (missInfo!=SPELL_MISS_NONE) - { - // Miss/dodge/parry/block only for melee based spells - // Resist only for magic based spells - switch (missInfo) - { - case SPELL_MISS_MISS: - if(m_caster->GetTypeId()== TYPEID_PLAYER) - ((Player*)m_caster)->UpdateWeaponSkill(BASE_ATTACK); - - m_caster->CastMeleeProcDamageAndSpell(unitTarget, 0, damageSchoolMask, m_attackType, MELEE_HIT_MISS, m_spellInfo, m_IsTriggeredSpell); - break; - case SPELL_MISS_RESIST: - m_caster->ProcDamageAndSpell(unitTarget, PROC_FLAG_TARGET_RESISTS, PROC_FLAG_RESIST_SPELL, 0, damageSchoolMask, m_spellInfo, m_IsTriggeredSpell); - break; - case SPELL_MISS_DODGE: - if(unitTarget->GetTypeId() == TYPEID_PLAYER) - ((Player*)unitTarget)->UpdateDefense(); - - // Overpower - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->getClass() == CLASS_WARRIOR) - { - ((Player*) m_caster)->AddComboPoints(unitTarget, 1); - m_caster->StartReactiveTimer( REACTIVE_OVERPOWER ); - } - - // Riposte - if (unitTarget->getClass() != CLASS_ROGUE) - { - unitTarget->ModifyAuraState(AURA_STATE_DEFENSE, true); - unitTarget->StartReactiveTimer( REACTIVE_DEFENSE ); - } - - m_caster->CastMeleeProcDamageAndSpell(unitTarget, 0, damageSchoolMask, m_attackType, MELEE_HIT_DODGE, m_spellInfo, m_IsTriggeredSpell); - break; - case SPELL_MISS_PARRY: - // Update victim defense ? - if(unitTarget->GetTypeId() == TYPEID_PLAYER) - ((Player*)unitTarget)->UpdateDefense(); - // Mongoose bite - set only Counterattack here - if (unitTarget->getClass() == CLASS_HUNTER) - { - unitTarget->ModifyAuraState(AURA_STATE_HUNTER_PARRY,true); - unitTarget->StartReactiveTimer( REACTIVE_HUNTER_PARRY ); - } - else - { - unitTarget->ModifyAuraState(AURA_STATE_DEFENSE, true); - unitTarget->StartReactiveTimer( REACTIVE_DEFENSE ); - } - m_caster->CastMeleeProcDamageAndSpell(unitTarget, 0, damageSchoolMask, m_attackType, MELEE_HIT_PARRY, m_spellInfo, m_IsTriggeredSpell); - break; - case SPELL_MISS_BLOCK: - unitTarget->ModifyAuraState(AURA_STATE_DEFENSE, true); - unitTarget->StartReactiveTimer( REACTIVE_DEFENSE ); - - m_caster->CastMeleeProcDamageAndSpell(unitTarget, 0, damageSchoolMask, m_attackType, MELEE_HIT_BLOCK, m_spellInfo, m_IsTriggeredSpell); - break; - // Trigger from this events not supported - case SPELL_MISS_EVADE: - case SPELL_MISS_IMMUNE: - case SPELL_MISS_IMMUNE2: - case SPELL_MISS_DEFLECT: - case SPELL_MISS_ABSORB: - // Trigger from reflects need do after get reflect result - case SPELL_MISS_REFLECT: - break; - default: - break; - } - } -}*/ void Spell::DoAllEffectOnTarget(TargetInfo *target) { @@ -952,15 +913,13 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) // Get mask of effects for target uint32 mask = target->effectMask; - if (mask == 0) // No effects - return; Unit* unit = m_caster->GetGUID()==target->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster,target->targetGUID); if (!unit) 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) @@ -980,6 +939,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) uint32 procAttacker = m_procAttacker; uint32 procVictim = m_procVictim; uint32 procEx = PROC_EX_NONE; + m_spellAura = NULL; // Set aura to null for every target-make sure that pointer is not used for unit without aura applied if (missInfo==SPELL_MISS_NONE) // In case spell hit target, do all effect on that target DoSpellHitOnUnit(unit, mask); @@ -988,6 +948,10 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) if (target->reflectResult == SPELL_MISS_NONE) // If reflected spell hit caster -> do all effect on him DoSpellHitOnUnit(m_caster, mask); } + // Do not take combo points on dodge + if (m_needComboPoints && m_targets.getUnitTargetGUID() == target->targetGUID) + if( missInfo != SPELL_MISS_NONE && missInfo != SPELL_MISS_MISS) + m_needComboPoints = false; /*else //TODO: This is a hack. need fix { uint32 tempMask = 0; @@ -1008,7 +972,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) if (crit) { procEx |= PROC_EX_CRITICAL_HIT; - addhealth = caster->SpellCriticalBonus(m_spellInfo, addhealth, NULL); + addhealth = caster->SpellCriticalHealingBonus(m_spellInfo, addhealth, NULL); } else procEx |= PROC_EX_NORMAL_HIT; @@ -1019,6 +983,9 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) if (m_canTrigger && missInfo != SPELL_MISS_REFLECT) caster->ProcDamageAndSpell(unitTarget, procAttacker, procVictim, procEx, addhealth, m_attackType, m_spellInfo); + if (m_spellAura) + m_spellAura->SetProcDamage(addhealth); + int32 gain = unitTarget->ModifyHealth( int32(addhealth) ); unitTarget->getHostilRefManager().threatAssist(caster, float(gain) * 0.5f, m_spellInfo); @@ -1045,46 +1012,17 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) if (m_canTrigger && missInfo != SPELL_MISS_REFLECT) caster->ProcDamageAndSpell(unitTarget, procAttacker, procVictim, procEx, damageInfo.damage, m_attackType, m_spellInfo); + if (m_spellAura) + m_spellAura->SetProcDamage(damageInfo.damage); + caster->DealSpellDamage(&damageInfo, true); - // Shadow Word: Death - deals damage equal to damage done to caster if victim is not killed - if (m_spellInfo->SpellFamilyName == SPELLFAMILY_PRIEST && m_spellInfo->SpellFamilyFlags&0x0000000200000000LL && - caster != unitTarget && unitTarget->isAlive()) - { - // Redirect damage to caster if victim Alive - damageInfo.target = caster; - damageInfo.absorb = 0; - damageInfo.resist = 0; - damageInfo.blocked = 0; - // Send log damage message to client - caster->SendSpellNonMeleeDamageLog(&damageInfo); - caster->DealSpellDamage(&damageInfo, true); - } // Judgement of Blood - else if (m_spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN && m_spellInfo->SpellFamilyFlags & 0x0000000800000000LL && m_spellInfo->SpellIconID==153) + if (m_spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN && m_spellInfo->SpellFamilyFlags[1] & 0x00000008 && m_spellInfo->SpellIconID==153) { 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 @@ -1108,14 +1046,14 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) if( !m_caster->IsFriendlyTo(unit) && !IsPositiveSpell(m_spellInfo->Id)) { - if( !(m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_NO_INITIAL_AGGRO) ) + if( !(m_spellInfo->AttributesEx & SPELL_ATTR_EX_NO_INITIAL_AGGRO) ) { m_caster->CombatStart(unit); } else if(m_customAttr & SPELL_ATTR_CU_AURA_CC) { if(!unit->IsStandState()) - unit->SetStandState(PLAYER_STATE_NONE); + unit->SetStandState(UNIT_STAND_STATE_STAND); } } } @@ -1128,25 +1066,37 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) // Recheck immune (only for delayed spells) if( m_spellInfo->speed && !(m_spellInfo->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY) - && (unit->IsImmunedToDamage(GetSpellSchoolMask(m_spellInfo),true) || - unit->IsImmunedToSpell(m_spellInfo,true) )) + && (unit->IsImmunedToDamage(GetSpellSchoolMask(m_spellInfo)) || + unit->IsImmunedToSpell(m_spellInfo))) { m_caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_IMMUNE); m_damage = 0; return; } + if (unit->GetTypeId() == TYPEID_PLAYER) + { + ((Player*)unit)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, m_spellInfo->Id); + ((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( m_caster != unit ) { - if (unit->GetCharmerOrOwnerGUID() != m_caster->GetGUID()) + // 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()) { - if (unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) - { - m_caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE); + m_caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE); m_damage = 0; - return; - } + return; } + if( !m_caster->IsFriendlyTo(unit) ) { // for delayed spells ignore not visible explicit target @@ -1158,8 +1108,8 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) } unit->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_HITBYSPELL); - if(m_customAttr & SPELL_ATTR_CU_AURA_CC) - unit->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CC); + //if(m_customAttr & SPELL_ATTR_CU_AURA_CC) + //unit->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CC); } else { @@ -1178,7 +1128,7 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) m_caster->SetContestedPvP(); //m_caster->UpdatePvP(true); } - if( unit->isInCombat() && !(m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_NO_INITIAL_AGGRO) ) + 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); @@ -1196,9 +1146,62 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) unit->IncrDiminishing(m_diminishGroup); } + // Apply additional spell effects to target + if (m_preCastSpell) + { + // Special spell id + // TODO: Handle all of special spells in one place? + if(m_preCastSpell==61988) + { + //Cast Forbearance + m_caster->CastSpell(unit,25771, true, m_CastItem); + // Cast Avenging Wrath Marker + m_caster->CastSpell(unit,61987, true, m_CastItem); + } + else + m_caster->CastSpell(unit,m_preCastSpell, true, m_CastItem); + } + uint8 t_effmask=0; + for (uint8 i=0;i<3;++i) + if (effectMask & (1<<i) && (m_spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA || IsAreaAuraEffect(m_spellInfo->Effect[i]))) + t_effmask |=1<<i; + + if (t_effmask) + { + Unit * caster = m_originalCaster ? m_originalCaster : m_caster; + Aura * Aur= new Aura(m_spellInfo, t_effmask, &m_currentBasePoints[0], unit, caster , m_CastItem); + + if (!Aur->IsAreaAura()) + { + // Now Reduce spell duration using data received at spell hit + int32 duration = Aur->GetAuraMaxDuration(); + unit->ApplyDiminishingToDuration(m_diminishGroup,duration,caster,m_diminishLevel); + Aur->setDiminishGroup(m_diminishGroup); + + duration = caster->ModSpellDuration(m_spellInfo, unit, duration, Aur->IsPositive()); + + //mod duration of channeled aura by spell haste + if (IsChanneledSpell(m_spellInfo)) + caster->ModSpellCastTime(m_spellInfo, duration, this); + + if(duration != Aur->GetAuraMaxDuration()) + { + Aur->SetAuraMaxDuration(duration); + Aur->SetAuraDuration(duration); + } + + // Prayer of Mending (jump animation), we need formal caster instead original for correct animation + if( m_spellInfo->SpellFamilyName == SPELLFAMILY_PRIEST && (m_spellInfo->SpellFamilyFlags[1] & 0x000020)) + m_caster->CastSpell(unit, 41637, true, NULL, NULL, m_originalCasterGUID); + } + // Set aura only when successfully applied + if (unit->AddAura(Aur)) + m_spellAura = Aur; + } + t_effmask = effectMask& ~t_effmask; for(uint32 effectNumber=0;effectNumber<3;effectNumber++) { - if (effectMask & (1<<effectNumber)) + if (t_effmask & (1<<effectNumber)) { HandleEffects(unit,NULL,NULL,effectNumber/*,m_damageMultipliers[effectNumber]*/); //Only damage and heal spells need this @@ -1226,20 +1229,23 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) int _duration=0; for(ChanceTriggerSpells::const_iterator i = m_ChanceTriggerSpells.begin(); i != m_ChanceTriggerSpells.end(); ++i) { + // SPELL_AURA_ADD_TARGET_TRIGGER auras shouldn't trigger auras without duration + // set duration equal to triggering spell if(roll_chance_i(i->second)) { m_caster->CastSpell(unit, i->first, true); - // SPELL_AURA_ADD_TARGET_TRIGGER auras shouldn't trigger auras without duration - // set duration equal to triggering spell - if (GetSpellDuration(i->first)==-1) + } + if (GetSpellDuration(i->first)==-1) + { + if (Aura * triggeredAur = unit->GetAura(i->first->Id, m_caster->GetGUID())) { // get duration from aura-only once if (!_duration) { - Aura * aur = unit->GetAuraByCasterSpell(m_spellInfo->Id, m_caster->GetGUID()); + Aura * aur = unit->GetAura(m_spellInfo->Id, m_caster->GetGUID()); _duration = aur ? aur->GetAuraDuration() : -1; } - unit->SetAurasDurationByCasterSpell(i->first->Id, m_caster->GetGUID(), _duration); + triggeredAur->SetAuraDuration(_duration); } } } @@ -1373,6 +1379,16 @@ void Spell::SearchChainTarget(std::list<Unit*> &TagUnitMap, float max_range, uin if(!cur) return; + // Get spell max affected targets + /*uint32 unMaxTargets = m_spellInfo->MaxAffectedTargets; + Unit::AuraList const& mod = m_caster->GetAurasByType(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS); + for(Unit::AuraList::const_iterator m = mod.begin(); m != mod.end(); ++m) + { + if (!(*m)->isAffectedOnSpell(m_spellInfo)) + continue; + unMaxTargets+=(*m)->GetAmount(); + }*/ + //FIXME: This very like horrible hack and wrong for most spells if(m_spellInfo->DmgClass != SPELL_DAMAGE_CLASS_MELEE) max_range += num * CHAIN_SPELL_JUMP_RADIUS; @@ -1484,7 +1500,7 @@ Unit* Spell::SearchNearbyTarget(float radius, SpellTargets TargetType, uint32 en { Creature* target = NULL; Trinity::NearestCreatureEntryWithLiveStateInObjectRangeCheck u_check(*m_caster, entry, true, radius); - Trinity::CreatureLastSearcher<Trinity::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(target, u_check); + Trinity::CreatureLastSearcher<Trinity::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(m_caster, target, u_check); m_caster->VisitNearbyObject(radius, searcher); return target; } @@ -1498,13 +1514,13 @@ Unit* Spell::SearchNearbyTarget(float radius, SpellTargets TargetType, uint32 en case SPELL_TARGETS_ENEMY: { Trinity::AnyUnfriendlyUnitInObjectRangeCheck u_check(m_caster, m_caster, radius); - Trinity::UnitLastSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(target, u_check); + Trinity::UnitLastSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(m_caster, target, u_check); m_caster->VisitNearbyObject(radius, searcher); }break; case SPELL_TARGETS_ALLY: { Trinity::AnyFriendlyUnitInObjectRangeCheck u_check(m_caster, m_caster, radius); - Trinity::UnitLastSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(target, u_check); + Trinity::UnitLastSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(m_caster, target, u_check); m_caster->VisitNearbyObject(radius, searcher); }break; } @@ -1513,21 +1529,35 @@ Unit* Spell::SearchNearbyTarget(float radius, SpellTargets TargetType, uint32 en void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) { - float radius; + float radius_h, radius_f; if (m_spellInfo->EffectRadiusIndex[i]) - radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + { + radius_h = GetSpellRadiusForHostile(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + radius_f = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + } else - radius = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); + { + radius_h = GetSpellMaxRangeForHostile(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); + radius_f = GetSpellMaxRangeForFriend(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); + } //Chain: 2, 6, 22, 25, 45, 77 uint32 EffectChainTarget = m_spellInfo->EffectChainTarget[i]; uint32 unMaxTargets = m_spellInfo->MaxAffectedTargets; + Unit::AuraEffectList const& Auras = m_caster->GetAurasByType(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS); + for(Unit::AuraEffectList::const_iterator j = Auras.begin();j != Auras.end(); ++j) + { + if((*j)->isAffectedOnSpell(m_spellInfo)) + unMaxTargets+=(*j)->GetAmount(); + } + if(m_originalCaster) { if(Player* modOwner = m_originalCaster->GetSpellModOwner()) { - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius,this); + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius_f,this); + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius_h,this); modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_JUMP_TARGETS, EffectChainTarget, this); } } @@ -1554,17 +1584,17 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) TagUnitMap.push_back(owner); break; case TARGET_UNIT_PET: - if(Pet* pet = m_caster->GetPet()) + if(Guardian* pet = m_caster->GetGuardianPet()) TagUnitMap.push_back(pet); break; case TARGET_UNIT_PARTY_CASTER: - m_caster->GetPartyMember(TagUnitMap, radius); + m_caster->GetPartyMember(TagUnitMap, radius_f); break; case TARGET_UNIT_RAID: - if(Unit *target = m_targets.getUnitTarget()) - TagUnitMap.push_back(target); - else - m_caster->GetRaidMember(TagUnitMap, radius); + //if(Unit *target = m_targets.getUnitTarget()) + // TagUnitMap.push_back(target); + //else + m_caster->GetRaidMember(TagUnitMap, radius_f); break; } }break; @@ -1581,30 +1611,29 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) switch(cur) { case TARGET_UNIT_MINIPET: - if( target->GetTypeId() == TYPEID_UNIT && ((Creature*)target)->isPet() && ((Pet*)target)->getPetType() == MINI_PET) + if(target->GetGUID() == m_caster->m_SummonSlot[4]) TagUnitMap.push_back(target); break; case TARGET_UNIT_TARGET_ALLY: case TARGET_UNIT_TARGET_RAID: case TARGET_UNIT_TARGET_ANY: // SelectMagnetTarget()? case TARGET_UNIT_TARGET_PARTY: - case TARGET_UNIT_SINGLE_UNKNOWN: TagUnitMap.push_back(target); break; case TARGET_UNIT_PARTY_TARGET: - target->GetPartyMember(TagUnitMap, radius); + target->GetPartyMember(TagUnitMap, radius_f); break; case TARGET_UNIT_TARGET_ENEMY: if(EffectChainTarget <= 1) TagUnitMap.push_back(SelectMagnetTarget()); else if(SelectMagnetTarget()) //TODO: chain target should also use magnet target - SearchChainTarget(TagUnitMap, radius, EffectChainTarget, SPELL_TARGETS_ENEMY); + SearchChainTarget(TagUnitMap, radius_h, EffectChainTarget, SPELL_TARGETS_ENEMY); break; case TARGET_UNIT_CHAINHEAL: if(EffectChainTarget <= 1) TagUnitMap.push_back(target); else - SearchChainTarget(TagUnitMap, radius, EffectChainTarget, SPELL_TARGETS_CHAINHEAL); + SearchChainTarget(TagUnitMap, radius_f, EffectChainTarget, SPELL_TARGETS_CHAINHEAL); break; } }break; @@ -1651,17 +1680,17 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) case TARGET_UNIT_AREA_ENEMY_GROUND: m_targets.m_targetMask |= TARGET_FLAG_DEST_LOCATION; case TARGET_UNIT_AREA_ENEMY: - SearchAreaTarget(TagUnitMap, radius, PUSH_DEST_CENTER, SPELL_TARGETS_ENEMY); + SearchAreaTarget(TagUnitMap, radius_h, PUSH_DEST_CENTER, SPELL_TARGETS_ENEMY); break; case TARGET_UNIT_AREA_ALLY_GROUND: m_targets.m_targetMask |= TARGET_FLAG_DEST_LOCATION; case TARGET_UNIT_AREA_ALLY: - SearchAreaTarget(TagUnitMap, radius, PUSH_DEST_CENTER, SPELL_TARGETS_ALLY); + SearchAreaTarget(TagUnitMap, radius_f, PUSH_DEST_CENTER, SPELL_TARGETS_ALLY); break; case TARGET_UNIT_AREA_PARTY_GROUND: m_targets.m_targetMask |= TARGET_FLAG_DEST_LOCATION; case TARGET_UNIT_AREA_PARTY: - m_caster->GetPartyMember(TagUnitMap, radius); + m_caster->GetPartyMember(TagUnitMap, radius_f); break; case TARGET_UNIT_AREA_ENTRY_GROUND: m_targets.m_targetMask |= TARGET_FLAG_DEST_LOCATION; @@ -1671,7 +1700,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) SpellScriptTarget::const_iterator upper = spellmgr.GetEndSpellScriptTarget(m_spellInfo->Id); if(lower==upper) { - SearchAreaTarget(TagUnitMap, radius, PUSH_DEST_CENTER, SPELL_TARGETS_ENEMY); + SearchAreaTarget(TagUnitMap, radius_h, PUSH_DEST_CENTER, SPELL_TARGETS_ENEMY); sLog.outErrorDb("Spell (ID: %u) (caster Entry: %u) does not have record in `spell_script_target`", m_spellInfo->Id, m_caster->GetEntry()); break; } @@ -1683,7 +1712,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) sLog.outError( "SPELL: spell ID %u requires non-creature target\n", m_spellInfo->Id ); continue; } - SearchAreaTarget(TagUnitMap, radius, PUSH_DEST_CENTER, SPELL_TARGETS_ENTRY, i_spellST->second.targetEntry); + SearchAreaTarget(TagUnitMap, radius_f, PUSH_DEST_CENTER, SPELL_TARGETS_ENTRY, i_spellST->second.targetEntry); } } break; @@ -1727,41 +1756,41 @@ 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) - SearchAreaTarget(TagUnitMap, radius, PUSH_IN_BACK, SPELL_TARGETS_ENEMY); + SearchAreaTarget(TagUnitMap, radius_h, PUSH_IN_BACK, SPELL_TARGETS_ENEMY); else if(m_customAttr & SPELL_ATTR_CU_CONE_LINE) - SearchAreaTarget(TagUnitMap, radius, PUSH_IN_LINE, SPELL_TARGETS_ENEMY); + SearchAreaTarget(TagUnitMap, radius_h, PUSH_IN_LINE, SPELL_TARGETS_ENEMY); else - SearchAreaTarget(TagUnitMap, radius, PUSH_IN_FRONT, SPELL_TARGETS_ENEMY); + SearchAreaTarget(TagUnitMap, radius_h, PUSH_IN_FRONT, SPELL_TARGETS_ENEMY); break; case TARGET_UNIT_CONE_ALLY: - SearchAreaTarget(TagUnitMap, radius, PUSH_IN_FRONT, SPELL_TARGETS_ALLY); + SearchAreaTarget(TagUnitMap, radius_f, PUSH_IN_FRONT, SPELL_TARGETS_ALLY); break; // nearby target case TARGET_UNIT_NEARBY_ALLY: + case TARGET_UNIT_NEARBY_ALLY_UNK: if(!m_targets.getUnitTarget()) - m_targets.setUnitTarget(SearchNearbyTarget(radius, SPELL_TARGETS_ALLY)); + m_targets.setUnitTarget(SearchNearbyTarget(radius_f, SPELL_TARGETS_ALLY)); if(m_targets.getUnitTarget()) { if(EffectChainTarget <= 1) TagUnitMap.push_back(m_targets.getUnitTarget()); else - SearchChainTarget(TagUnitMap, radius, EffectChainTarget, SPELL_TARGETS_ALLY); + SearchChainTarget(TagUnitMap, radius_f, EffectChainTarget, SPELL_TARGETS_ALLY); } break; - case TARGET_RANDOM_ENEMY_CHAIN_IN_AREA: + case TARGET_UNIT_NEARBY_ENEMY: if(!m_targets.getUnitTarget()) - m_targets.setUnitTarget(SearchNearbyTarget(radius, SPELL_TARGETS_ENEMY)); + m_targets.setUnitTarget(SearchNearbyTarget(radius_h, SPELL_TARGETS_ENEMY)); if(m_targets.getUnitTarget()) { if(EffectChainTarget <= 1) TagUnitMap.push_back(m_targets.getUnitTarget()); else - SearchChainTarget(TagUnitMap, radius, EffectChainTarget, SPELL_TARGETS_ENEMY); + SearchChainTarget(TagUnitMap, radius_h, EffectChainTarget, SPELL_TARGETS_ENEMY); } break; case TARGET_SCRIPT: @@ -1778,7 +1807,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) } SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex); - float range = GetSpellMaxRange(srange); + float range = GetSpellMaxRangeForHostile(srange); Creature* creatureScriptTarget = NULL; GameObject* goScriptTarget = NULL; @@ -1794,7 +1823,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) if(i_spellST->second.targetEntry) { Trinity::NearestGameObjectEntryInObjectRangeCheck go_check(*m_caster,i_spellST->second.targetEntry,range); - Trinity::GameObjectLastSearcher<Trinity::NearestGameObjectEntryInObjectRangeCheck> checker(p_GameObject,go_check); + Trinity::GameObjectLastSearcher<Trinity::NearestGameObjectEntryInObjectRangeCheck> checker(m_caster, p_GameObject,go_check); m_caster->VisitNearbyGridObject(range, checker); if(p_GameObject) @@ -1824,7 +1853,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) Creature *p_Creature = NULL; Trinity::NearestCreatureEntryWithLiveStateInObjectRangeCheck u_check(*m_caster,i_spellST->second.targetEntry,i_spellST->second.type!=SPELL_TARGET_TYPE_DEAD,range); - Trinity::CreatureLastSearcher<Trinity::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(p_Creature, u_check); + Trinity::CreatureLastSearcher<Trinity::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(m_caster, p_Creature, u_check); m_caster->VisitNearbyObject(range, searcher); if(p_Creature ) @@ -1873,7 +1902,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) Player* Target = itr->getSource(); // IsHostileTo check duel and controlled by enemy - if( Target && targetPlayer->IsWithinDistInMap(Target, radius) && + if( Target && targetPlayer->IsWithinDistInMap(Target, radius_f) && targetPlayer->getClass() == Target->getClass() && !m_caster->IsHostileTo(Target) ) { @@ -1904,7 +1933,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) float x, y, z, angle, dist; float objSize = m_caster->GetObjectSize(); - dist = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + dist = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); if(dist < objSize) dist = objSize; else if(cur == TARGET_DEST_CASTER_RANDOM) @@ -1947,7 +1976,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) float x, y, z, angle, dist; float objSize = target->GetObjectSize(); - dist = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + dist = target->GetSpellRadiusForTarget(target, sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); if(dist < objSize) dist = objSize; else if(cur == TARGET_DEST_CASTER_RANDOM) @@ -1983,7 +2012,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) float x, y, z, angle, dist; - dist = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + dist = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); if (cur == TARGET_DEST_DEST_RANDOM) dist *= rand_norm(); @@ -2017,10 +2046,71 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap) TagUnitMap.remove(m_targets.getUnitTarget()); Trinity::RandomResizeList(TagUnitMap, unMaxTargets); + + /*if(m_spellInfo->Id==57669) //Replenishment (special target selection) + { + if(pGroup) + { + typedef std::priority_queue<PrioritizeManaPlayerWraper, std::vector<PrioritizeManaPlayerWraper>, PrioritizeMana> Top10; + Top10 manaUsers; + + for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL && manaUsers.size() < 10; itr = itr->next()) + { + Player* Target = itr->getSource(); + if (m_caster->GetGUID() != Target->GetGUID() && Target->getPowerType() == POWER_MANA && + !Target->isDead() && m_caster->IsWithinDistInMap(Target, radius)) + { + PrioritizeManaPlayerWraper WTarget(Target); + manaUsers.push(WTarget); + } + } + + while(!manaUsers.empty()) + { + TagUnitMap.push_back(manaUsers.top().getPlayer()); + manaUsers.pop(); + } + } + else + { + Unit* ownerOrSelf = pTarget ? pTarget : m_caster->GetCharmerOrOwnerOrSelf(); + if ((ownerOrSelf==m_caster || m_caster->IsWithinDistInMap(ownerOrSelf, radius)) && + ownerOrSelf->getPowerType() == POWER_MANA) + TagUnitMap.push_back(ownerOrSelf); + + if(Pet* pet = ownerOrSelf->GetGuardianPet()) + if( m_caster->IsWithinDistInMap(pet, radius) && pet->getPowerType() == POWER_MANA ) + TagUnitMap.push_back(pet); + } + }*/ } } -void Spell::prepare(SpellCastTargets * targets, Aura* triggeredByAura) +class PrioritizeManaPlayerWraper +{ + friend struct PrioritizeMana; + + public: + explicit PrioritizeManaPlayerWraper(Player* player) : player(player) + { + uint32 maxmana = player->GetMaxPower(POWER_MANA); + percentMana = maxmana ? player->GetPower(POWER_MANA) * 100 / maxmana : 101; + } + Player* getPlayer() const { return player; } + private: + Player* player; + uint32 percentMana; +}; + +struct PrioritizeMana +{ + int operator()( PrioritizeManaPlayerWraper const& x, PrioritizeManaPlayerWraper const& y ) const + { + return x.percentMana < y.percentMana; + } +}; + +void Spell::prepare(SpellCastTargets const* targets, AuraEffect* triggeredByAura) { if(m_CastItem) m_castItemGUID = m_CastItem->GetGUID(); @@ -2029,6 +2119,13 @@ void Spell::prepare(SpellCastTargets * targets, Aura* triggeredByAura) m_targets = *targets; + if(!m_targets.getUnitTargetGUID() && m_spellInfo->Targets & TARGET_FLAG_UNIT) + { + if(Unit *target = ObjectAccessor::GetUnit(*m_caster, m_caster->GetUInt64Value(UNIT_FIELD_TARGET))) + if(IsValidSingleTargetSpell(target)) + m_targets.setUnitTarget(target); + } + m_spellState = SPELL_STATE_PREPARING; m_caster->GetPosition(m_castPositionX, m_castPositionY, m_castPositionZ); @@ -2042,7 +2139,7 @@ void Spell::prepare(SpellCastTargets * targets, Aura* triggeredByAura) m_caster->m_Events.AddEvent(Event, m_caster->m_Events.CalculateTime(1)); //Prevent casting at cast another spell (ServerSide check) - if(m_caster->IsNonMeleeSpellCasted(false, true) && m_cast_count) + if(m_caster->IsNonMeleeSpellCasted(false, true, true) && m_cast_count) { SendCastResult(SPELL_FAILED_SPELL_IN_PROGRESS); finish(false); @@ -2079,13 +2176,13 @@ void Spell::prepare(SpellCastTargets * targets, Aura* triggeredByAura) // Fill cost data m_powerCost = CalculatePowerCost(); - uint8 result = CanCast(true); - if(result != 0 && !IsAutoRepeat()) //always cast autorepeat dummy for triggering + SpellCastResult result = CheckCast(true); + if(result != SPELL_CAST_OK && !IsAutoRepeat()) //always cast autorepeat dummy for triggering { if(triggeredByAura) { SendChannelUpdate(0); - triggeredByAura->SetAuraDuration(0); + triggeredByAura->GetParentAura()->SetAuraDuration(0); } SendCastResult(result); finish(false); @@ -2095,13 +2192,18 @@ void Spell::prepare(SpellCastTargets * targets, Aura* triggeredByAura) // Prepare data for triggers prepareDataForTriggerSystem(); - // calculate cast time (calculated after first CanCast check to prevent charge counting for first CanCast fail) + // Set combo point requirement + if (m_IsTriggeredSpell || m_CastItem || m_caster->GetTypeId()!=TYPEID_PLAYER) + m_needComboPoints = false; + + // calculate cast time (calculated after first CheckCast check to prevent charge counting for first CheckCast fail) m_casttime = GetSpellCastTime(m_spellInfo, this); // set timer base at cast time ReSetTimer(); - - if(m_IsTriggeredSpell) + //Containers for channeled spells have to be set + //TODO:Apply this to all casted spells if needed + if(m_IsTriggeredSpell && !IsChanneledSpell(m_spellInfo)) cast(true); else { @@ -2110,6 +2212,9 @@ void Spell::prepare(SpellCastTargets * targets, Aura* triggeredByAura) if(isSpellBreakStealth(m_spellInfo) ) m_caster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CAST); + if(!m_IsTriggeredSpell) + m_caster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ANY_CAST); + m_caster->SetCurrentCastedSpell( this ); m_selfContainer = &(m_caster->m_currentSpells[GetCurrentContainer()]); SendSpellStart(); @@ -2151,11 +2256,10 @@ void Spell::cancel() { Unit* unit = m_caster->GetGUID()==(*ihit).targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID); if( unit && unit->isAlive() ) - unit->RemoveAurasByCasterSpell(m_spellInfo->Id, m_caster->GetGUID()); + unit->RemoveAurasDueToSpell(m_spellInfo->Id, m_caster->GetGUID(), AURA_REMOVE_BY_CANCEL); } } - - m_caster->RemoveAurasByCasterSpell(m_spellInfo->Id, m_caster->GetGUID()); + m_caster->RemoveAurasDueToSpell(m_spellInfo->Id, m_caster->GetGUID(), AURA_REMOVE_BY_CANCEL); SendChannelUpdate(0); SendInterrupted(0); SendCastResult(SPELL_FAILED_INTERRUPTED); @@ -2179,8 +2283,6 @@ void Spell::cast(bool skipCheck) { SetExecutedCurrently(true); - uint8 castResult = 0; - // update pointers base at GUIDs to prevent access to non-existed already object UpdatePointers(); @@ -2195,23 +2297,12 @@ void Spell::cast(bool skipCheck) if(m_caster->GetTypeId() != TYPEID_PLAYER && m_targets.getUnitTarget() && m_targets.getUnitTarget() != m_caster) m_caster->SetInFront(m_targets.getUnitTarget()); - if(!m_IsTriggeredSpell) - { - castResult = CheckPower(); - if(castResult != 0) - { - SendCastResult(castResult); - finish(false); - SetExecutedCurrently(false); - return; - } - } // triggered cast called from Spell::prepare where it was already checked - if(!skipCheck) + if(!m_IsTriggeredSpell || !skipCheck) { - castResult = CanCast(false); - if(castResult != 0) + SpellCastResult castResult = CheckCast(false); + if(castResult != SPELL_CAST_OK) { SendCastResult(castResult); finish(false); @@ -2222,48 +2313,70 @@ void Spell::cast(bool skipCheck) FillTargetMap(); - if(m_spellState == SPELL_STATE_FINISHED) // stop cast if spell marked as finish somewhere in Take*/FillTargetMap + if(m_spellInfo->SpellFamilyName) { - SetExecutedCurrently(false); - return; + if (m_spellInfo->excludeCasterAuraSpell && !IsPositiveSpell(m_spellInfo->excludeCasterAuraSpell)) + m_preCastSpell = m_spellInfo->excludeCasterAuraSpell; + else if (m_spellInfo->excludeTargetAuraSpell && !IsPositiveSpell(m_spellInfo->excludeTargetAuraSpell)) + m_preCastSpell = m_spellInfo->excludeTargetAuraSpell; + } + 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) + m_preCastSpell = 23230; // Blood Fury - Healing Reduction + 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(); - if(!m_IsTriggeredSpell) + if (m_caster->GetTypeId() == TYPEID_PLAYER) { - //TakePower(); - TakeReagents(); // we must remove reagents before HandleEffects to allow place crafted item in same slot - } + if (!m_IsTriggeredSpell && m_CastItem) + ((Player*)m_caster)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM, m_CastItem->GetEntry()); - // CAST SPELL - SendSpellCooldown(); - //SendCastResult(castResult); - SendSpellGo(); // we must send smsg_spell_go packet before m_castItem delete in TakeCastItem()... - - if(m_customAttr & SPELL_ATTR_CU_DIRECT_DAMAGE) - CalculateDamageDoneForAllTargets(); + ((Player*)m_caster)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL, m_spellInfo->Id); + } - //handle SPELL_AURA_ADD_TARGET_TRIGGER auras - //are there any spells need to be triggered after hit? - Unit::AuraList const& targetTriggers = m_caster->GetAurasByType(SPELL_AURA_ADD_TARGET_TRIGGER); - for(Unit::AuraList::const_iterator i = targetTriggers.begin(); i != targetTriggers.end(); ++i) + // this is related to combo points so must be done before takepower + // are there any spells need to be triggered after hit? + // handle SPELL_AURA_ADD_TARGET_TRIGGER auras + Unit::AuraEffectList const& targetTriggers = m_caster->GetAurasByType(SPELL_AURA_ADD_TARGET_TRIGGER); + for(Unit::AuraEffectList::const_iterator i = targetTriggers.begin(); i != targetTriggers.end(); ++i) { + if (!(*i)->isAffectedOnSpell(m_spellInfo)) + continue; SpellEntry const *auraSpellInfo = (*i)->GetSpellProto(); uint32 auraSpellIdx = (*i)->GetEffIndex(); - if (IsAffectedBy(auraSpellInfo, auraSpellIdx)) + if(SpellEntry const *spellInfo = sSpellStore.LookupEntry(auraSpellInfo->EffectTriggerSpell[auraSpellIdx])) { - if(SpellEntry const *spellInfo = sSpellStore.LookupEntry(auraSpellInfo->EffectTriggerSpell[auraSpellIdx])) - { - // Calculate chance at that moment (can be depend for example from combo points) - int32 chance = m_caster->CalculateSpellDamage(auraSpellInfo, auraSpellIdx, (*i)->GetBasePoints(), NULL); - m_ChanceTriggerSpells.push_back(std::make_pair(spellInfo, chance * (*i)->GetStackAmount())); - } + // Calculate chance at that moment (can be depend for example from combo points) + int32 chance = m_caster->CalculateSpellDamage(auraSpellInfo, auraSpellIdx, (*i)->GetBasePoints(), NULL); + m_ChanceTriggerSpells.push_back(std::make_pair(spellInfo, chance * (*i)->GetParentAura()->GetStackAmount())); } } + // this is related to combo points so must be done before takepower + if(m_customAttr & SPELL_ATTR_CU_DIRECT_DAMAGE) + CalculateDamageDoneForAllTargets(); + + if(!m_IsTriggeredSpell) + { + // Powers have to be taken before SendSpellGo + TakePower(); + TakeReagents(); // we must remove reagents before HandleEffects to allow place crafted item in same slot + } + + // CAST SPELL + SendSpellCooldown(); + //SendCastResult(castResult); + SendSpellGo(); // we must send smsg_spell_go packet before m_castItem delete in TakeCastItem()... + if(m_customAttr & SPELL_ATTR_CU_CHARGE) EffectCharge(0); @@ -2285,12 +2398,6 @@ void Spell::cast(bool skipCheck) handle_immediate(); } - // combo points should not be taken before SPELL_AURA_ADD_TARGET_TRIGGER auras are handled - if(!m_IsTriggeredSpell) - { - TakePower(); - } - if(m_customAttr & SPELL_ATTR_CU_LINK_CAST) { if(const std::vector<int32> *spell_triggered = spellmgr.GetSpellLinked(m_spellInfo->Id)) @@ -2309,9 +2416,18 @@ void Spell::handle_immediate() // start channeling if applicable if(IsChanneledSpell(m_spellInfo)) { - m_spellState = SPELL_STATE_CASTING; - m_caster->AddInterruptMask(m_spellInfo->ChannelInterruptFlags); - SendChannelStart(GetSpellDuration(m_spellInfo)); + int32 duration = GetSpellDuration(m_spellInfo); + if (duration) + { + //apply haste mods + m_caster->ModSpellCastTime(m_spellInfo, duration, this); + // Apply duration mod + if(Player* modOwner = m_caster->GetSpellModOwner()) + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration); + m_spellState = SPELL_STATE_CASTING; + m_caster->AddInterruptMask(m_spellInfo->ChannelInterruptFlags); + SendChannelStart(duration); + } } // process immediate effects (items, ground, etc.) also initialize some variables @@ -2436,6 +2552,10 @@ void Spell::_handle_immediate_phase() void Spell::_handle_finish_phase() { + // Take for real after all targets are processed + if (m_needComboPoints) + ((Player*)m_caster)->ClearComboPoints(); + // spell log if(m_needSpellLog) SendLogExecute(); @@ -2447,93 +2567,20 @@ void Spell::SendSpellCooldown() return; Player* _player = (Player*)m_caster; - // Add cooldown for max (disable spell) - // Cooldown started on SendCooldownEvent call - if (m_spellInfo->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE) - { - _player->AddSpellCooldown(m_spellInfo->Id, 0, time(NULL) - 1); - return; - } - - // init cooldown values - uint32 cat = 0; - int32 rec = -1; - int32 catrec = -1; - - // some special item spells without correct cooldown in SpellInfo - // cooldown information stored in item prototype - // This used in same way in WorldSession::HandleItemQuerySingleOpcode data sending to client. - - if(m_CastItem) - { - ItemPrototype const* proto = m_CastItem->GetProto(); - if(proto) - { - for(int idx = 0; idx < 5; ++idx) - { - if(proto->Spells[idx].SpellId == m_spellInfo->Id) - { - cat = proto->Spells[idx].SpellCategory; - rec = proto->Spells[idx].SpellCooldown; - catrec = proto->Spells[idx].SpellCategoryCooldown; - break; - } - } - } - } - // if no cooldown found above then base at DBC data - if(rec < 0 && catrec < 0) + // mana/health/etc potions, disabled by client (until combat out as declarate) + if (m_CastItem && m_CastItem->IsPotion()) { - cat = m_spellInfo->Category; - rec = m_spellInfo->RecoveryTime; - catrec = m_spellInfo->CategoryRecoveryTime; + // need in some way provided data for Spell::finish SendCooldownEvent + _player->SetLastPotionId(m_CastItem->GetEntry()); + return; } - // shoot spells used equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK) - // prevent 0 cooldowns set by another way - if (rec <= 0 && catrec <= 0 && (cat == 76 || cat == 351)) - rec = _player->GetAttackTime(RANGED_ATTACK); - - // Now we have cooldown data (if found any), time to apply mods - if(rec > 0) - _player->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COOLDOWN, rec, this); - - if(catrec > 0) - _player->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COOLDOWN, catrec, this); - - // replace negative cooldowns by 0 - if (rec < 0) rec = 0; - if (catrec < 0) catrec = 0; - - // no cooldown after applying spell mods - if( rec == 0 && catrec == 0) + // have infinity cooldown but set at aura apply // do not set cooldown for triggered spells (needed by reincarnation) + if(m_spellInfo->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE || m_IsTriggeredSpell) return; - time_t curTime = time(NULL); - - time_t catrecTime = catrec ? curTime+catrec/1000 : 0; // in secs - time_t recTime = rec ? curTime+rec/1000 : catrecTime;// in secs - - // self spell cooldown - if(recTime > 0) - _player->AddSpellCooldown(m_spellInfo->Id, m_CastItem ? m_CastItem->GetEntry() : 0, recTime); - - // category spells - if (catrec > 0) - { - SpellCategoryStore::const_iterator i_scstore = sSpellCategoryStore.find(cat); - if(i_scstore != sSpellCategoryStore.end()) - { - for(SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset) - { - if(*i_scset == m_spellInfo->Id) // skip main spell, already handled above - continue; - - _player->AddSpellCooldown(m_spellInfo->Id, m_CastItem ? m_CastItem->GetEntry() : 0, catrecTime); - } - } - } + _player->AddSpellAndCategoryCooldowns(m_spellInfo,m_CastItem ? m_CastItem->GetEntry() : 0, this); } void Spell::update(uint32 difftime) @@ -2687,6 +2734,10 @@ void Spell::finish(bool ok) m_caster->resetAttackTimer(RANGED_ATTACK); } + // potions disabled by client, send event "not in combat" if need + if (m_caster->GetTypeId() == TYPEID_PLAYER) + ((Player*)m_caster)->UpdatePotionCooldown(this); + // call triggered spell only at successful cast (after clear combo points -> for add some if need) // I assume what he means is that some triggered spells may add combo points if(!m_TriggerSpells.empty()) @@ -2697,72 +2748,65 @@ void Spell::finish(bool ok) m_caster->AttackStop(); } -void Spell::SendCastResult(uint8 result) +void Spell::SendCastResult(SpellCastResult result) { + if(result == SPELL_CAST_OK) + return; + if (m_caster->GetTypeId() != TYPEID_PLAYER) return; if(((Player*)m_caster)->GetSession()->PlayerLoading()) // don't send cast results at loading time return; - if(result != 0) - { - WorldPacket data(SMSG_CAST_FAILED, (4+1+1)); - data << uint32(m_spellInfo->Id); - data << uint8(result); // problem - data << uint8(m_cast_count); // single cast or multi 2.3 (0/1) - switch (result) - { - case SPELL_FAILED_REQUIRES_SPELL_FOCUS: - data << uint32(m_spellInfo->RequiresSpellFocus); - break; - case SPELL_FAILED_REQUIRES_AREA: - // hardcode areas limitation case - switch(m_spellInfo->Id) - { - case 41617: // Cenarion Mana Salve - case 41619: // Cenarion Healing Salve - data << uint32(3905); - break; - case 41618: // Bottled Nethergon Energy - case 41620: // Bottled Nethergon Vapor - data << uint32(3842); - break; - case 45373: // Bloodberry Elixir - data << uint32(4075); - break; - default: // default case - data << uint32(m_spellInfo->AreaId); - break; - } - break; - case SPELL_FAILED_TOTEMS: - if(m_spellInfo->Totem[0]) - data << uint32(m_spellInfo->Totem[0]); - if(m_spellInfo->Totem[1]) - data << uint32(m_spellInfo->Totem[1]); - break; - case SPELL_FAILED_TOTEM_CATEGORY: - if(m_spellInfo->TotemCategory[0]) - data << uint32(m_spellInfo->TotemCategory[0]); - if(m_spellInfo->TotemCategory[1]) - data << uint32(m_spellInfo->TotemCategory[1]); - break; - case SPELL_FAILED_EQUIPPED_ITEM_CLASS: - data << uint32(m_spellInfo->EquippedItemClass); - data << uint32(m_spellInfo->EquippedItemSubClassMask); - data << uint32(m_spellInfo->EquippedItemInventoryTypeMask); - break; - } - ((Player*)m_caster)->GetSession()->SendPacket(&data); - } - else + WorldPacket data(SMSG_CAST_FAILED, (4+1+1)); + data << uint8(m_cast_count); // single cast or multi 2.3 (0/1) + data << uint32(m_spellInfo->Id); + data << uint8(result); // problem + switch (result) { - WorldPacket data(SMSG_CLEAR_EXTRA_AURA_INFO, (8+4)); - data.append(m_caster->GetPackGUID()); - data << uint32(m_spellInfo->Id); - ((Player*)m_caster)->GetSession()->SendPacket(&data); + case SPELL_FAILED_REQUIRES_SPELL_FOCUS: + data << uint32(m_spellInfo->RequiresSpellFocus); + break; + case SPELL_FAILED_REQUIRES_AREA: + // hardcode areas limitation case + switch(m_spellInfo->Id) + { + case 41617: // Cenarion Mana Salve + case 41619: // Cenarion Healing Salve + data << uint32(3905); + break; + case 41618: // Bottled Nethergon Energy + case 41620: // Bottled Nethergon Vapor + data << uint32(3842); + break; + case 45373: // Bloodberry Elixir + data << uint32(4075); + break; + default: // default case (don't must be) + data << uint32(0); + break; + } + break; + case SPELL_FAILED_TOTEMS: + if(m_spellInfo->Totem[0]) + data << uint32(m_spellInfo->Totem[0]); + if(m_spellInfo->Totem[1]) + data << uint32(m_spellInfo->Totem[1]); + break; + case SPELL_FAILED_TOTEM_CATEGORY: + if(m_spellInfo->TotemCategory[0]) + data << uint32(m_spellInfo->TotemCategory[0]); + if(m_spellInfo->TotemCategory[1]) + data << uint32(m_spellInfo->TotemCategory[1]); + break; + case SPELL_FAILED_EQUIPPED_ITEM_CLASS: + data << uint32(m_spellInfo->EquippedItemClass); + data << uint32(m_spellInfo->EquippedItemSubClassMask); + //data << uint32(m_spellInfo->EquippedItemInventoryTypeMask); + break; } + ((Player*)m_caster)->GetSession()->SendPacket(&data); } void Spell::SendSpellStart() @@ -2775,6 +2819,13 @@ void Spell::SendSpellStart() uint32 castFlags = CAST_FLAG_UNKNOWN1; if(IsRangedSpell()) castFlags |= CAST_FLAG_AMMO; + if ((m_caster->GetTypeId() == TYPEID_PLAYER || + (m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->isPet())) + && m_spellInfo->powerType != POWER_HEALTH ) + castFlags |= CAST_FLAG_POWER_LEFT_SELF; + + if(m_spellInfo->runeCostID) + castFlags |= CAST_FLAG_UNKNOWN10; Unit *target = m_targets.getUnitTarget() ? m_targets.getUnitTarget() : m_caster; @@ -2785,14 +2836,17 @@ void Spell::SendSpellStart() data.append(m_caster->GetPackGUID()); data.append(m_caster->GetPackGUID()); - data << uint32(m_spellInfo->Id); - data << uint8(m_cast_count); // single cast or multi 2.3 (0/1) - data << uint16(castFlags); - data << uint32(m_timer); + data << uint8(m_cast_count); // pending spell cast? + data << uint32(m_spellInfo->Id); // spellId + data << uint32(castFlags); // cast flags + data << uint32(m_timer); // delay? m_targets.write(&data); - if( castFlags & CAST_FLAG_AMMO ) + if(castFlags & CAST_FLAG_POWER_LEFT_SELF) + data << uint32(m_caster->GetPower((Powers)m_spellInfo->powerType)); + + if ( castFlags & CAST_FLAG_AMMO ) WriteAmmoToPacket(&data); m_caster->SendMessageToSet(&data, true); @@ -2811,25 +2865,72 @@ void Spell::SendSpellGo() uint32 castFlags = CAST_FLAG_UNKNOWN3; if(IsRangedSpell()) castFlags |= CAST_FLAG_AMMO; // arrows/bullets visual + if ((m_caster->GetTypeId() == TYPEID_PLAYER || + (m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->isPet())) + && m_spellInfo->powerType != POWER_HEALTH ) + castFlags |= CAST_FLAG_POWER_LEFT_SELF; // should only be sent to self, but the current messaging doesn't make that possible + + if((m_caster->GetTypeId() == TYPEID_PLAYER) && (m_caster->getClass() == CLASS_DEATH_KNIGHT) && m_spellInfo->runeCostID) + { + castFlags |= CAST_FLAG_UNKNOWN10; // same as in SMSG_SPELL_START + castFlags |= CAST_FLAG_UNKNOWN7; // rune cooldowns list + } WorldPacket data(SMSG_SPELL_GO, 50); // guess size + if(m_CastItem) data.append(m_CastItem->GetPackGUID()); else data.append(m_caster->GetPackGUID()); data.append(m_caster->GetPackGUID()); - data << uint32(m_spellInfo->Id); - data << uint16(castFlags); + data << uint8(m_cast_count); // pending spell cast? + data << uint32(m_spellInfo->Id); // spellId + data << uint32(castFlags); // cast flags data << uint32(getMSTime()); // timestamp WriteSpellGoTargets(&data); m_targets.write(&data); - if( castFlags & CAST_FLAG_AMMO ) + if(castFlags & CAST_FLAG_POWER_LEFT_SELF) + data << uint32(m_caster->GetPower((Powers)m_spellInfo->powerType)); + + if ( castFlags & CAST_FLAG_UNKNOWN7 ) // rune cooldowns list + { + uint8 v1 = m_runesState; + uint8 v2 = ((Player*)m_caster)->GetRunesState(); + data << uint8(v1); // runes state before + data << uint8(v2); // runes state after + for(uint8 i = 0; i < MAX_RUNES; ++i) + { + uint8 m = (1 << i); + if(m & v1) // usable before... + if(!(m & v2)) // ...but on cooldown now... + data << uint8(0); // some unknown byte (time?) + } + } + + if ( castFlags & CAST_FLAG_UNKNOWN4 ) // unknown wotlk + { + data << float(0); + data << uint32(0); + } + + if ( castFlags & CAST_FLAG_AMMO ) WriteAmmoToPacket(&data); + if ( castFlags & CAST_FLAG_UNKNOWN5 ) // unknown wotlk + { + data << uint32(0); + data << uint32(0); + } + + if ( m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION ) + { + data << uint8(0); + } + m_caster->SendMessageToSet(&data, true); } @@ -2874,15 +2975,36 @@ void Spell::WriteAmmoToPacket( WorldPacket * data ) void Spell::WriteSpellGoTargets( WorldPacket * data ) { - *data << (uint8)m_countOfHit; + // 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) + { + if ((*ihit).effectMask == 0) // No effect apply - all immuned add state + { + // possibly SPELL_MISS_IMMUNE2 for this?? + ihit->missCondition = SPELL_MISS_IMMUNE2; + miss++; + } + else if ((*ihit).missCondition == SPELL_MISS_NONE) + hit++; + else + miss++; + } + + *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 - *data << (uint8)m_countOfMiss; + *data << (uint8)miss; for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit) { if( ihit->missCondition != SPELL_MISS_NONE ) // Add only miss @@ -2893,6 +3015,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() @@ -2957,30 +3082,19 @@ void Spell::SendLogExecute() data << uint8(0); break; case SPELL_EFFECT_CREATE_ITEM: + case SPELL_EFFECT_CREATE_ITEM_2: data << uint32(m_spellInfo->EffectItemType[0]); break; case SPELL_EFFECT_SUMMON: - case SPELL_EFFECT_SUMMON_WILD: - case SPELL_EFFECT_SUMMON_GUARDIAN: case SPELL_EFFECT_TRANS_DOOR: case SPELL_EFFECT_SUMMON_PET: - case SPELL_EFFECT_SUMMON_POSSESSED: - case SPELL_EFFECT_SUMMON_TOTEM: case SPELL_EFFECT_SUMMON_OBJECT_WILD: case SPELL_EFFECT_CREATE_HOUSE: case SPELL_EFFECT_DUEL: - case SPELL_EFFECT_SUMMON_TOTEM_SLOT1: - case SPELL_EFFECT_SUMMON_TOTEM_SLOT2: - case SPELL_EFFECT_SUMMON_TOTEM_SLOT3: - case SPELL_EFFECT_SUMMON_TOTEM_SLOT4: - case SPELL_EFFECT_SUMMON_PHANTASM: - case SPELL_EFFECT_SUMMON_CRITTER: case SPELL_EFFECT_SUMMON_OBJECT_SLOT1: case SPELL_EFFECT_SUMMON_OBJECT_SLOT2: case SPELL_EFFECT_SUMMON_OBJECT_SLOT3: case SPELL_EFFECT_SUMMON_OBJECT_SLOT4: - case SPELL_EFFECT_SUMMON_DEMON: - case SPELL_EFFECT_150: if(Unit *unit = m_targets.getUnitTarget()) data.append(unit->GetPackGUID()); else if(m_targets.getItemTargetGUID()) @@ -2999,6 +3113,13 @@ void Spell::SendLogExecute() else data << uint8(0); break; + case SPELL_EFFECT_RESURRECT: + case SPELL_EFFECT_RESURRECT_NEW: + if(Unit *unit = m_targets.getUnitTarget()) + data.append(unit->GetPackGUID()); + else + data << uint8(0); + break; default: return; } @@ -3012,13 +3133,16 @@ void Spell::SendInterrupted(uint8 result) { WorldPacket data(SMSG_SPELL_FAILURE, (8+4+1)); data.append(m_caster->GetPackGUID()); - data << m_spellInfo->Id; - data << result; + data << uint8(m_cast_count); + data << uint32(m_spellInfo->Id); + data << uint8(result); m_caster->SendMessageToSet(&data, true); data.Initialize(SMSG_SPELL_FAILED_OTHER, (8+4)); data.append(m_caster->GetPackGUID()); - data << m_spellInfo->Id; + data << uint8(m_cast_count); + data << uint32(m_spellInfo->Id); + data << uint8(result); m_caster->SendMessageToSet(&data, true); } @@ -3086,10 +3210,19 @@ void Spell::SendChannelStart(uint32 duration) void Spell::SendResurrectRequest(Player* target) { - WorldPacket data(SMSG_RESURRECT_REQUEST, (8+4+2+4)); - data << m_caster->GetGUID(); - data << uint32(1) << uint16(0) << uint32(1); + // Both players and NPCs can resurrect using spells - have a look at creature 28487 for example + // However, the packet structure differs slightly + + const char* sentName = m_caster->GetTypeId()==TYPEID_PLAYER ?"":m_caster->GetNameForLocaleIdx(target->GetSession()->GetSessionDbLocaleIndex()); + + WorldPacket data(SMSG_RESURRECT_REQUEST, (8+4+strlen(sentName)+1+1+1)); + data << uint64(m_caster->GetGUID()); + data << uint32(strlen(sentName)+1); + + data << sentName; + data << uint8(0); + data << uint8(m_caster->GetTypeId()==TYPEID_PLAYER ?0:1); target->GetSession()->SendPacket(&data); } @@ -3126,7 +3259,7 @@ void Spell::TakeCastItem() bool expendable = false; bool withoutCharges = false; - for (int i = 0; i<5; i++) + for (int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) { if (proto->Spells[i].SpellId) { @@ -3142,7 +3275,7 @@ void Spell::TakeCastItem() if (charges) { (charges > 0) ? --charges : ++charges; // abs(charges) less at 1 after use - if (proto->Stackable < 2) + if (proto->Stackable == 1) m_CastItem->SetSpellCharges(i, charges); m_CastItem->SetState(ITEM_CHANGED, (Player*)m_caster); } @@ -3168,7 +3301,7 @@ void Spell::TakeCastItem() void Spell::TakePower() { - if(m_CastItem || m_triggeredByAuraSpell || !m_powerCost) + if(m_CastItem || m_triggeredByAuraSpell) return; bool hit = true; @@ -3181,12 +3314,27 @@ void Spell::TakePower() { if(ihit->missCondition != SPELL_MISS_NONE && ihit->missCondition != SPELL_MISS_MISS/* && ihit->targetGUID!=m_caster->GetGUID()*/) hit = false; + if (ihit->missCondition != SPELL_MISS_NONE) + { + //lower spell cost on fail (by talent aura) + if(Player *modOwner = ((Player*)m_caster)->GetSpellModOwner()) + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_SPELL_COST_REFUND_ON_FAIL, m_powerCost); + } break; } - if(hit && NeedsComboPoints(m_spellInfo)) - ((Player*)m_caster)->ClearComboPoints(); } + Powers powerType = Powers(m_spellInfo->powerType); + + if(powerType == POWER_RUNE) + { + TakeRunePower(); + return; + } + + if (!m_powerCost) + return; + // health as power used if(m_spellInfo->powerType == POWER_HEALTH) { @@ -3200,8 +3348,6 @@ void Spell::TakePower() return; } - Powers powerType = Powers(m_spellInfo->powerType); - if(hit) m_caster->ModifyPower(powerType, -m_powerCost); else @@ -3212,6 +3358,136 @@ void Spell::TakePower() m_caster->SetLastManaUse(getMSTime()); } +void Spell::TakeAmmo() +{ + if(m_attackType == RANGED_ATTACK && m_caster->GetTypeId() == TYPEID_PLAYER) + { + Item *pItem = ((Player*)m_caster)->GetWeaponForAttack( RANGED_ATTACK ); + + // wands don't have ammo + if(!pItem || pItem->IsBroken() || pItem->GetProto()->SubClass==ITEM_SUBCLASS_WEAPON_WAND) + return; + + if( pItem->GetProto()->InventoryType == INVTYPE_THROWN ) + { + if(pItem->GetMaxStackCount()==1) + { + // decrease durability for non-stackable throw weapon + ((Player*)m_caster)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_RANGED); + } + else + { + // decrease items amount for stackable throw weapon + uint32 count = 1; + ((Player*)m_caster)->DestroyItemCount( pItem, count, true); + } + } + else if(uint32 ammo = ((Player*)m_caster)->GetUInt32Value(PLAYER_AMMO_ID)) + ((Player*)m_caster)->DestroyItemCount(ammo, 1, true); + } +} + +SpellCastResult Spell::CheckRuneCost(uint32 runeCostID) +{ + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return SPELL_CAST_OK; + + Player *plr = (Player*)m_caster; + + if(plr->getClass() != CLASS_DEATH_KNIGHT) + return SPELL_CAST_OK; + + SpellRuneCostEntry const *src = sSpellRuneCostStore.LookupEntry(runeCostID); + + if(!src) + return SPELL_CAST_OK; + + if(src->NoRuneCost()) + return SPELL_CAST_OK; + + int32 runeCost[NUM_RUNE_TYPES]; // blood, frost, unholy, death + + for(uint32 i = 0; i < RUNE_DEATH; ++i) + runeCost[i] = src->RuneCost[i]; + + runeCost[RUNE_DEATH] = MAX_RUNES; // calculated later + + for(uint32 i = 0; i < MAX_RUNES; ++i) + { + uint8 rune = plr->GetCurrentRune(i); + if((plr->GetRuneCooldown(i) == 0) && (runeCost[rune] > 0)) + runeCost[rune]--; + } + + for(uint32 i = 0; i < RUNE_DEATH; ++i) + if(runeCost[i] > 0) + runeCost[RUNE_DEATH] += runeCost[i]; + + if(runeCost[RUNE_DEATH] > MAX_RUNES) + return SPELL_FAILED_NO_POWER; // not sure if result code is correct + + return SPELL_CAST_OK; +} + +void Spell::TakeRunePower() +{ + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + Player *plr = (Player*)m_caster; + + if(plr->getClass() != CLASS_DEATH_KNIGHT) + return; + + SpellRuneCostEntry const *src = sSpellRuneCostStore.LookupEntry(m_spellInfo->runeCostID); + + if(!src || (src->NoRuneCost() && src->NoRunicPowerGain())) + return; + m_runesState = plr->GetRunesState(); // store previous state + + int32 runeCost[NUM_RUNE_TYPES]; // blood, frost, unholy, death + + for(uint32 i = 0; i < RUNE_DEATH; ++i) + { + runeCost[i] = src->RuneCost[i]; + } + + runeCost[RUNE_DEATH] = 0; // calculated later + + for(uint32 i = 0; i < MAX_RUNES; ++i) + { + uint8 rune = plr->GetCurrentRune(i); + if((plr->GetRuneCooldown(i) == 0) && (runeCost[rune] > 0)) + { + plr->SetRuneCooldown(i, RUNE_COOLDOWN); // 5*2=10 sec + runeCost[rune]--; + } + } + + runeCost[RUNE_DEATH] = runeCost[RUNE_BLOOD] + runeCost[RUNE_UNHOLY] + runeCost[RUNE_FROST]; + + if(runeCost[RUNE_DEATH] > 0) + { + for(uint32 i = 0; i < MAX_RUNES; ++i) + { + uint8 rune = plr->GetCurrentRune(i); + if((plr->GetRuneCooldown(i) == 0) && (rune == RUNE_DEATH)) + { + plr->SetRuneCooldown(i, RUNE_COOLDOWN); // 5*2=10 sec + runeCost[rune]--; + plr->ConvertRune(i, plr->GetBaseRune(i)); + if(runeCost[RUNE_DEATH] == 0) + break; + } + } + } + + // you can gain some runic power when use runes + float rp = src->runePowerGain; + rp *= sWorld.getRate(RATE_POWER_RUNICPOWER_INCOME); + plr->ModifyPower(POWER_RUNIC_POWER, (int32)rp); +} + void Spell::TakeReagents() { if(m_IsTriggeredSpell) // reagents used in triggered spell removed by original spell or don't must be removed. @@ -3220,11 +3496,9 @@ void Spell::TakeReagents() if (m_caster->GetTypeId() != TYPEID_PLAYER) return; - if (m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP && - m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION)) - return; - Player* p_caster = (Player*)m_caster; + if (p_caster->CanNoReagentCast(m_spellInfo)) + return; for(uint32 x=0;x<8;x++) { @@ -3240,7 +3514,7 @@ void Spell::TakeReagents() ItemPrototype const *proto = m_CastItem->GetProto(); if( proto && proto->ItemId == itemid ) { - for(int s=0;s<5;s++) + for(int s=0;s < MAX_ITEM_PROTO_SPELLS; ++s) { // CastItem will be used up and does not count as reagent int32 charges = m_CastItem->GetSpellCharges(s); @@ -3287,14 +3561,9 @@ void Spell::HandleEffects(Unit *pUnitTarget,Item *pItemTarget,GameObject *pGOTar gameObjTarget = pGOTarget; uint8 eff = m_spellInfo->Effect[i]; - uint32 mechanic = m_spellInfo->EffectMechanic[i]; sLog.outDebug( "Spell: Effect : %u", eff); - //Simply return. Do not display "immune" in red text on client - if(unitTarget && unitTarget->IsImmunedToSpellEffect(eff, mechanic)) - return; - //we do not need DamageMultiplier here. damage = CalculateDamage(i, NULL); @@ -3311,7 +3580,7 @@ void Spell::HandleEffects(Unit *pUnitTarget,Item *pItemTarget,GameObject *pGOTar EffectEnchantItemTmp(i); else { - sLog.outError("SPELL: unknown effect %u spell id %u\n", + sLog.outError("SPELL: unknown effect %u spell id %u", eff, m_spellInfo->Id); } } @@ -3327,11 +3596,14 @@ void Spell::TriggerSpell() } } -uint8 Spell::CanCast(bool strict) +SpellCastResult Spell::CheckCast(bool strict) { // check cooldowns to prevent cheating if(m_caster->GetTypeId()==TYPEID_PLAYER && ((Player*)m_caster)->HasSpellCooldown(m_spellInfo->Id)) { + //can cast triggered (by aura only?) spells while have this flag + if (!m_IsTriggeredSpell && ((Player*)m_caster)->HasFlag(PLAYER_FLAGS, PLAYER_ALLOW_ONLY_ABILITY)) + return SPELL_FAILED_SPELL_IN_PROGRESS; if(m_triggeredByAuraSpell) return SPELL_FAILED_DONT_REPORT; else @@ -3348,19 +3620,59 @@ uint8 Spell::CanCast(bool strict) // for now, ignore triggered spells if( strict && !m_IsTriggeredSpell) { - // Cannot be used in this stance/form - if(uint8 shapeError = GetErrorAtShapeshiftedCast(m_spellInfo, m_caster->m_form)) - return shapeError; + bool checkForm = true; + // Ignore form req aura + Unit::AuraEffectList const& ignore = m_caster->GetAurasByType(SPELL_AURA_MOD_IGNORE_SHAPESHIFT); + for(Unit::AuraEffectList::const_iterator i = ignore.begin(); i != ignore.end(); ++i) + { + if (!(*i)->isAffectedOnSpell(m_spellInfo)) + continue; + checkForm = false; + break; + } + if (checkForm) + { + // Cannot be used in this stance/form + SpellCastResult shapeError = GetErrorAtShapeshiftedCast(m_spellInfo, m_caster->m_form); + if(shapeError != SPELL_CAST_OK) + return shapeError; - if ((m_spellInfo->Attributes & SPELL_ATTR_ONLY_STEALTHED) && !(m_caster->HasStealthAura())) - return SPELL_FAILED_ONLY_STEALTHED; + if ((m_spellInfo->Attributes & SPELL_ATTR_ONLY_STEALTHED) && !(m_caster->HasStealthAura())) + return SPELL_FAILED_ONLY_STEALTHED; + } } - // caster state requirements - if(m_spellInfo->CasterAuraState && !m_caster->HasAuraState(AuraState(m_spellInfo->CasterAuraState))) - return SPELL_FAILED_CASTER_AURASTATE; - if(m_spellInfo->CasterAuraStateNot && m_caster->HasAuraState(AuraState(m_spellInfo->CasterAuraStateNot))) - return SPELL_FAILED_CASTER_AURASTATE; + bool reqCombat=true; + Unit::AuraEffectList const& stateAuras = m_caster->GetAurasByType(SPELL_AURA_ABILITY_IGNORE_AURASTATE); + for(Unit::AuraEffectList::const_iterator j = stateAuras.begin();j != stateAuras.end(); ++j) + { + if((*j)->isAffectedOnSpell(m_spellInfo)) + { + if ((*j)->GetMiscValue()==1) + { + reqCombat=false; + break; + } + } + } + + // caster state requirements + // not for triggered spells (needed by execute) + if (!m_IsTriggeredSpell) + { + if(m_spellInfo->CasterAuraState && !m_caster->HasAuraState(AuraState(m_spellInfo->CasterAuraState), m_spellInfo, m_caster)) + return SPELL_FAILED_CASTER_AURASTATE; + if(m_spellInfo->CasterAuraStateNot && m_caster->HasAuraState(AuraState(m_spellInfo->CasterAuraStateNot), m_spellInfo, m_caster)) + return SPELL_FAILED_CASTER_AURASTATE; + + 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(reqCombat && m_caster->isInCombat() && IsNonCombatSpell(m_spellInfo)) + return SPELL_FAILED_AFFECTING_COMBAT; + } // cancel autorepeat spells if cast start when moving // (not wand currently autorepeat cast delayed to moving stop anyway in spell update code) @@ -3372,22 +3684,26 @@ uint8 Spell::CanCast(bool strict) return SPELL_FAILED_MOVING; } - Unit *target = m_targets.getUnitTarget(); - - if(target) + if(Unit *target = m_targets.getUnitTarget()) { // target state requirements (not allowed state), apply to self also - if(m_spellInfo->TargetAuraStateNot && target->HasAuraState(AuraState(m_spellInfo->TargetAuraStateNot))) + if(!m_IsTriggeredSpell && m_spellInfo->TargetAuraStateNot && target->HasAuraState(AuraState(m_spellInfo->TargetAuraStateNot), m_spellInfo, m_caster)) + return SPELL_FAILED_TARGET_AURASTATE; + + if(m_spellInfo->targetAuraSpell && !target->HasAura(m_spellInfo->targetAuraSpell)) + return SPELL_FAILED_TARGET_AURASTATE; + + if(m_spellInfo->excludeTargetAuraSpell && target->HasAura(m_spellInfo->excludeTargetAuraSpell)) return SPELL_FAILED_TARGET_AURASTATE; if(target != m_caster) { // target state requirements (apply to non-self only), to allow cast affects to self like Dirty Deeds - if(m_spellInfo->TargetAuraState && !target->HasAuraState(AuraState(m_spellInfo->TargetAuraState))) + if(!m_IsTriggeredSpell && m_spellInfo->TargetAuraState && !target->HasAuraState(AuraState(m_spellInfo->TargetAuraState), m_spellInfo, m_caster)) return SPELL_FAILED_TARGET_AURASTATE; // Not allow casting on flying player - if (target->isInFlight()) + if (target->hasUnitState(UNIT_STAT_UNATTACKABLE)) return SPELL_FAILED_BAD_TARGETS; if(!m_IsTriggeredSpell && VMAP::VMapFactory::checkSpellForLoS(m_spellInfo->Id) && !m_caster->IsWithinLOSInMap(target)) @@ -3396,13 +3712,23 @@ uint8 Spell::CanCast(bool strict) // auto selection spell rank implemented in WorldSession::HandleCastSpellOpcode // this case can be triggered if rank not found (too low-level target for first rank) if(m_caster->GetTypeId() == TYPEID_PLAYER && !IsPassiveSpell(m_spellInfo->Id) && !m_CastItem) - { for(int i=0;i<3;i++) - { if(IsPositiveEffect(m_spellInfo->Id, i) && m_spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA) if(target->getLevel() + 10 < m_spellInfo->spellLevel) return SPELL_FAILED_LOWLEVEL; - } + } + else if (m_caster->GetTypeId()==TYPEID_PLAYER) // Target - is player caster + { + // Additional check for some spells + // If 0 spell effect empty - client not send target data (need use selection) + // TODO: check it on next client version + if (m_targets.m_targetMask == TARGET_FLAG_SELF && + m_spellInfo->EffectImplicitTargetA[1] == TARGET_CHAIN_DAMAGE) + { + if (target = m_caster->GetUnit(*m_caster, ((Player *)m_caster)->GetSelection())) + m_targets.setUnitTarget(target); + else + return SPELL_FAILED_BAD_TARGETS; } } @@ -3411,7 +3737,7 @@ uint8 Spell::CanCast(bool strict) { if(m_spellInfo->EffectImplicitTargetA[j] == TARGET_PET) { - target = m_caster->GetPet(); + target = m_caster->GetGuardianPet(); if(!target) { if(m_triggeredByAuraSpell) // not report pet not existence for triggered spells @@ -3455,14 +3781,15 @@ uint8 Spell::CanCast(bool strict) } if(IsPositiveSpell(m_spellInfo->Id)) - { - if(target->IsImmunedToSpell(m_spellInfo,false)) + if(target->IsImmunedToSpell(m_spellInfo)) return SPELL_FAILED_TARGET_AURASTATE; - } //Must be behind the target. if( m_spellInfo->AttributesEx2 == 0x100000 && (m_spellInfo->AttributesEx & 0x200) == 0x200 && target->HasInArc(M_PI, m_caster) - && (m_spellInfo->SpellFamilyName != SPELLFAMILY_DRUID || m_spellInfo->SpellFamilyFlags != 0x0000000000020000LL)) + //Exclusion for Pounce: Facing Limitation was removed in 2.0.1, but it still uses the same, old Ex-Flags + && (!(m_spellInfo->SpellFamilyName == SPELLFAMILY_DRUID && m_spellInfo->SpellFamilyFlags.IsEqual(0x20000,0,0))) + //Mutilate no longer requires you be behind the target as of patch 3.0.3 + && (!(m_spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && m_spellInfo->SpellFamilyFlags[1] & 0x200000))) { SendInterrupted(2); return SPELL_FAILED_NOT_BEHIND; @@ -3477,10 +3804,9 @@ uint8 Spell::CanCast(bool strict) // check if target is in combat if (target != m_caster && (m_spellInfo->AttributesEx & SPELL_ATTR_EX_NOT_IN_COMBAT_TARGET) && target->isInCombat()) - { return SPELL_FAILED_TARGET_AFFECTING_COMBAT; - } } + // Spell casted only on battleground if((m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_BATTLEGROUND) && m_caster->GetTypeId()==TYPEID_PLAYER) if(!((Player*)m_caster)->InBattleGround()) @@ -3490,14 +3816,19 @@ uint8 Spell::CanCast(bool strict) // - with greater than 15 min CD without SPELL_ATTR_EX4_USABLE_IN_ARENA flag // - with SPELL_ATTR_EX4_NOT_USABLE_IN_ARENA flag if( (m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_NOT_USABLE_IN_ARENA) || - GetSpellRecoveryTime(m_spellInfo) > 15 * MINUTE * 1000 && !(m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_USABLE_IN_ARENA) ) + GetSpellRecoveryTime(m_spellInfo) > 15 * MINUTE * IN_MILISECONDS && !(m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_USABLE_IN_ARENA) ) if(MapEntry const* mapEntry = sMapStore.LookupEntry(m_caster->GetMapId())) if(mapEntry->IsBattleArena()) return SPELL_FAILED_NOT_IN_ARENA; // zone check - if(!IsSpellAllowedInLocation(m_spellInfo,m_caster->GetMapId(),m_caster->GetZoneId(),m_caster->GetAreaId())) - return SPELL_FAILED_REQUIRES_AREA; + uint32 zone, area; + m_caster->GetZoneAndAreaId(zone,area); + + SpellCastResult locRes= spellmgr.GetSpellAllowedInLocationError(m_spellInfo,m_caster->GetMapId(),zone,area, + m_caster->GetTypeId()==TYPEID_PLAYER ? ((Player*)m_caster) : NULL); + if(locRes != SPELL_CAST_OK) + return locRes; // not let players cast spells at mount (and let do it to creatures) if( m_caster->IsMounted() && m_caster->GetTypeId()==TYPEID_PLAYER && !m_IsTriggeredSpell && @@ -3511,11 +3842,14 @@ uint8 Spell::CanCast(bool strict) // always (except passive spells) check items (focus object can be required for any type casts) if(!IsPassiveSpell(m_spellInfo->Id)) - if(uint8 castResult = CheckItems()) + { + SpellCastResult castResult = CheckItems(); + if(castResult != SPELL_CAST_OK) return castResult; + } /*//ImpliciteTargetA-B = 38, If fact there is 0 Spell with ImpliciteTargetB=38 - if(m_UniqueTargetInfo.empty()) // skip second canCast apply (for delayed spells for example) + if(m_UniqueTargetInfo.empty()) // skip second CheckCast apply (for delayed spells for example) { for(uint8 j = 0; j < 3; j++) { @@ -3550,7 +3884,7 @@ uint8 Spell::CanCast(bool strict) cell.data.Part.reserved = ALL_DISTRICT; MaNGOS::NearestGameObjectEntryInObjectRangeCheck go_check(*m_caster,i_spellST->second.targetEntry,range); - MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck> checker(p_GameObject,go_check); + MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck> checker(m_caster, p_GameObject,go_check); TypeContainerVisitor<MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck>, GridTypeMapContainer > object_checker(checker); CellLock<GridReadGuard> cell_lock(cell, p); @@ -3588,7 +3922,7 @@ uint8 Spell::CanCast(bool strict) cell.SetNoCreate(); // Really don't know what is that??? MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck u_check(*m_caster,i_spellST->second.targetEntry,i_spellST->second.type!=SPELL_TARGET_TYPE_DEAD,range); - MaNGOS::CreatureLastSearcher<MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(p_Creature, u_check); + MaNGOS::CreatureLastSearcher<MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(m_caster, p_Creature, u_check); TypeContainerVisitor<MaNGOS::CreatureLastSearcher<MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck>, GridTypeMapContainer > grid_creature_searcher(searcher); @@ -3651,13 +3985,16 @@ uint8 Spell::CanCast(bool strict) if(!m_IsTriggeredSpell) { - if(uint8 castResult = CheckRange(strict)) + SpellCastResult castResult = CheckRange(strict); + if(castResult != SPELL_CAST_OK) return castResult; - if(uint8 castResult = CheckPower()) + castResult = CheckPower(); + if(castResult != SPELL_CAST_OK) return castResult; - if(uint8 castResult = CheckCasterAuras()) + castResult = CheckCasterAuras(); + if(castResult != SPELL_CAST_OK) return castResult; } @@ -3668,12 +4005,7 @@ uint8 Spell::CanCast(bool strict) { case SPELL_EFFECT_DUMMY: { - if(m_spellInfo->SpellIconID == 1648) // Execute - { - if(!m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetHealth() > m_targets.getUnitTarget()->GetMaxHealth()*0.2) - return SPELL_FAILED_BAD_TARGETS; - } - else if (m_spellInfo->Id == 51582) // Rocket Boots Engaged + if (m_spellInfo->Id == 51582) // Rocket Boots Engaged { if(m_caster->IsInWater()) return SPELL_FAILED_ONLY_ABOVEWATER; @@ -3682,7 +4014,7 @@ uint8 Spell::CanCast(bool strict) { // spell different for friends and enemies // hart version required facing - if(m_targets.getUnitTarget() && !m_caster->IsFriendlyTo(m_targets.getUnitTarget()) && !m_caster->HasInArc( M_PI, target )) + if(m_targets.getUnitTarget() && !m_caster->IsFriendlyTo(m_targets.getUnitTarget()) && !m_caster->HasInArc( M_PI, m_targets.getUnitTarget() )) return SPELL_FAILED_UNIT_NOT_INFRONT; } else if (m_spellInfo->Id == 19938) // Awaken Peon @@ -3693,25 +4025,15 @@ uint8 Spell::CanCast(bool strict) } break; } - case SPELL_EFFECT_SCHOOL_DAMAGE: - { - // Hammer of Wrath - if(m_spellInfo->SpellVisual == 7250) - { - if (!m_targets.getUnitTarget()) - return SPELL_FAILED_BAD_IMPLICIT_TARGETS; - - if(m_targets.getUnitTarget()->GetHealth() > m_targets.getUnitTarget()->GetMaxHealth()*0.2) - return SPELL_FAILED_BAD_TARGETS; - } - break; - } case SPELL_EFFECT_LEARN_SPELL: { + if (m_caster->GetTypeId() != TYPEID_PLAYER) + return SPELL_FAILED_BAD_TARGETS; + if(m_spellInfo->EffectImplicitTargetA[i] != TARGET_PET) break; - Pet* pet = m_caster->GetPet(); + Pet* pet = ((Player*)m_caster)->GetPet(); if(!pet) return SPELL_FAILED_NO_PET; @@ -3721,21 +4043,17 @@ uint8 Spell::CanCast(bool strict) if(!learn_spellproto) return SPELL_FAILED_NOT_KNOWN; - if(!pet->CanTakeMoreActiveSpells(learn_spellproto->Id)) - return SPELL_FAILED_TOO_MANY_SKILLS; - if(m_spellInfo->spellLevel > pet->getLevel()) return SPELL_FAILED_LOWLEVEL; - if(!pet->HasTPForSpell(learn_spellproto->Id)) - return SPELL_FAILED_TRAINING_POINTS; - break; } case SPELL_EFFECT_LEARN_PET_SPELL: { - Pet* pet = m_caster->GetPet(); + if (m_caster->GetTypeId() != TYPEID_PLAYER) + return SPELL_FAILED_BAD_TARGETS; + Pet* pet = ((Player*)m_caster)->GetPet(); if(!pet) return SPELL_FAILED_NO_PET; @@ -3744,31 +4062,29 @@ uint8 Spell::CanCast(bool strict) if(!learn_spellproto) return SPELL_FAILED_NOT_KNOWN; - if(!pet->CanTakeMoreActiveSpells(learn_spellproto->Id)) - return SPELL_FAILED_TOO_MANY_SKILLS; - if(m_spellInfo->spellLevel > pet->getLevel()) return SPELL_FAILED_LOWLEVEL; - if(!pet->HasTPForSpell(learn_spellproto->Id)) - return SPELL_FAILED_TRAINING_POINTS; - break; } case SPELL_EFFECT_FEED_PET: { - if (m_caster->GetTypeId() != TYPEID_PLAYER || !m_targets.getItemTarget() ) + if (m_caster->GetTypeId() != TYPEID_PLAYER) return SPELL_FAILED_BAD_TARGETS; - Pet* pet = m_caster->GetPet(); + Item* foodItem = m_targets.getItemTarget(); + if(!foodItem) + return SPELL_FAILED_BAD_TARGETS; + + Pet* pet = ((Player*)m_caster)->GetPet(); if(!pet) return SPELL_FAILED_NO_PET; - if(!pet->HaveInDiet(m_targets.getItemTarget()->GetProto())) + if(!pet->HaveInDiet(foodItem->GetProto())) return SPELL_FAILED_WRONG_PET_FOOD; - if(!pet->GetCurrentFoodBenefitLevel(m_targets.getItemTarget()->GetProto()->ItemLevel)) + if(!pet->GetCurrentFoodBenefitLevel(foodItem->GetProto()->ItemLevel)) return SPELL_FAILED_FOOD_LOWLEVEL; if(m_caster->isInCombat() || pet->isInCombat()) @@ -3840,130 +4156,40 @@ uint8 Spell::CanCast(bool strict) // In BattleGround players can use only flags and banners if( ((Player*)m_caster)->InBattleGround() && - !((Player*)m_caster)->isAllowUseBattleGroundObject() ) + !((Player*)m_caster)->CanUseBattleGroundObject() ) return SPELL_FAILED_TRY_AGAIN; // get the lock entry - LockEntry const *lockInfo = NULL; + uint32 lockId = 0; if (GameObject* go=m_targets.getGOTarget()) - lockInfo = sLockStore.LookupEntry(go->GetLockId()); + lockId = go->GetLockId(); else if(Item* itm=m_targets.getItemTarget()) - lockInfo = sLockStore.LookupEntry(itm->GetProto()->LockID); - - // check lock compatibility - if (lockInfo) - { - // check for lock - key pair (checked by client also, just prevent cheating - bool ok_key = false; - for(int it = 0; it < 5; ++it) - { - switch(lockInfo->keytype[it]) - { - case LOCK_KEY_NONE: - break; - case LOCK_KEY_ITEM: - { - if(lockInfo->key[it]) - { - if(m_CastItem && m_CastItem->GetEntry()==lockInfo->key[it]) - ok_key =true; - break; - } - } - case LOCK_KEY_SKILL: - { - if(uint32(m_spellInfo->EffectMiscValue[i])!=lockInfo->key[it]) - break; + lockId = itm->GetProto()->LockID; - switch(lockInfo->key[it]) - { - case LOCKTYPE_HERBALISM: - if(((Player*)m_caster)->HasSkill(SKILL_HERBALISM)) - ok_key =true; - break; - case LOCKTYPE_MINING: - if(((Player*)m_caster)->HasSkill(SKILL_MINING)) - ok_key =true; - break; - default: - ok_key =true; - break; - } - } - } - if(ok_key) - break; - } + SkillType skillId =SKILL_NONE; + int32 reqSkillValue = 0; + int32 skillValue = 0; - if(!ok_key) - return SPELL_FAILED_BAD_TARGETS; - } + // check lock compatibility + SpellCastResult res = CanOpenLock(i,lockId,skillId,reqSkillValue,skillValue); + if(res != SPELL_CAST_OK) + return res; // chance for fail at orange mining/herb/LockPicking gathering attempt - if (!m_selfContainer || ((*m_selfContainer) != this)) - break; - - // get the skill value of the player - int32 SkillValue = 0; - bool canFailAtMax = true; - if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_HERBALISM) + // second check prevent fail at rechecks + if(skillId != SKILL_NONE && (!m_selfContainer || ((*m_selfContainer) != this))) { - SkillValue = ((Player*)m_caster)->GetSkillValue(SKILL_HERBALISM); - canFailAtMax = false; - } - else if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_MINING) - { - SkillValue = ((Player*)m_caster)->GetSkillValue(SKILL_MINING); - canFailAtMax = false; - } - else if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_PICKLOCK) - SkillValue = ((Player*)m_caster)->GetSkillValue(SKILL_LOCKPICKING); - - // castitem check: rogue using skeleton keys. the skill values should not be added in this case. - if(m_CastItem) - SkillValue = 0; - - // add the damage modifier from the spell casted (cheat lock / skeleton key etc.) (use m_currentBasePoints, CalculateDamage returns wrong value) - // TODO: is this a hack? - SkillValue += m_currentBasePoints[i]+1; - - // get the required lock value - int32 ReqValue=0; - if (lockInfo) - { - // check for lock - key pair - bool ok = false; - for(int it = 0; it < 5; ++it) - { - if(lockInfo->keytype[it]==LOCK_KEY_ITEM && lockInfo->key[it] && m_CastItem && m_CastItem->GetEntry()==lockInfo->key[it]) - { - // if so, we're good to go - ok = true; - break; - } - } - if(ok) - break; + bool canFailAtMax = skillId != SKILL_HERBALISM && skillId != SKILL_MINING; - if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_PICKLOCK) - ReqValue = lockInfo->requiredlockskill; - else - ReqValue = lockInfo->requiredminingskill; + // chance for failure in orange gather / lockpick (gathering skill can't fail at maxskill) + if((canFailAtMax || skillValue < sWorld.GetConfigMaxSkillValue()) && reqSkillValue > irand(skillValue-25, skillValue+37)) + return SPELL_FAILED_TRY_AGAIN; } - - // skill doesn't meet the required value - if (ReqValue > SkillValue) - return SPELL_FAILED_LOW_CASTLEVEL; - - // chance for failure in orange gather / lockpick (gathering skill can't fail at maxskill) - if((canFailAtMax || SkillValue < sWorld.GetConfigMaxSkillValue()) && ReqValue > irand(SkillValue-25, SkillValue+37)) - return SPELL_FAILED_TRY_AGAIN; - break; } case SPELL_EFFECT_SUMMON_DEAD_PET: { - Creature *pet = m_caster->GetPet(); + Creature *pet = m_caster->GetGuardianPet(); if(!pet) return SPELL_FAILED_NO_PET; @@ -3972,16 +4198,11 @@ uint8 Spell::CanCast(bool strict) break; } - // This is generic summon effect now and don't make this check for summon types similar - // SPELL_EFFECT_SUMMON_CRITTER, SPELL_EFFECT_SUMMON_WILD or SPELL_EFFECT_SUMMON_GUARDIAN. - // These won't show up in m_caster->GetPetGUID() + // This is generic summon effect case SPELL_EFFECT_SUMMON: { switch(m_spellInfo->EffectMiscValueB[i]) { - case SUMMON_TYPE_POSESSED: - case SUMMON_TYPE_POSESSED2: - case SUMMON_TYPE_POSESSED3: case SUMMON_TYPE_DEMON: case SUMMON_TYPE_SUMMON: { @@ -3993,13 +4214,25 @@ uint8 Spell::CanCast(bool strict) break; } } + SummonPropertiesEntry const *SummonProperties = sSummonPropertiesStore.LookupEntry(m_spellInfo->EffectMiscValueB[i]); + if(!SummonProperties) + break; + switch(SummonProperties->Category) + { + case SUMMON_CATEGORY_POSSESSED: + { + if(m_caster->GetPetGUID()) + return SPELL_FAILED_ALREADY_HAVE_SUMMON; + + if(m_caster->GetCharmGUID()) + return SPELL_FAILED_ALREADY_HAVE_CHARM; + break; + } + } break; } - // Don't make this check for SPELL_EFFECT_SUMMON_CRITTER, SPELL_EFFECT_SUMMON_WILD or SPELL_EFFECT_SUMMON_GUARDIAN. - // These won't show up in m_caster->GetPetGUID() - case SPELL_EFFECT_SUMMON_POSSESSED: + // Not used for summon? case SPELL_EFFECT_SUMMON_PHANTASM: - case SPELL_EFFECT_SUMMON_DEMON: { if(m_caster->GetPetGUID()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; @@ -4013,13 +4246,11 @@ uint8 Spell::CanCast(bool strict) { if(m_caster->GetPetGUID()) //let warlock do a replacement summon { - - Pet* pet = ((Player*)m_caster)->GetPet(); - if (m_caster->GetTypeId()==TYPEID_PLAYER && m_caster->getClass()==CLASS_WARLOCK) { if (strict) //starting cast, trigger pet stun (cast by pet so it doesn't attack player) - pet->CastSpell(pet, 32752, true, NULL, NULL, pet->GetGUID()); + if(Pet* pet = ((Player*)m_caster)->GetPet()) + pet->CastSpell(pet, 32752, true, NULL, NULL, pet->GetGUID()); } else return SPELL_FAILED_ALREADY_HAVE_SUMMON; @@ -4055,7 +4286,7 @@ uint8 Spell::CanCast(bool strict) case SPELL_EFFECT_LEAP: case SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER: { - float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + float dis = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); float fx = m_caster->GetPositionX() + dis * cos(m_caster->GetOrientation()); float fy = m_caster->GetPositionY() + dis * sin(m_caster->GetOrientation()); // teleport a bit above terrain level to avoid falling below it @@ -4091,25 +4322,38 @@ uint8 Spell::CanCast(bool strict) { case SPELL_AURA_DUMMY: { - if(m_spellInfo->Id == 1515) + //custom check + switch(m_spellInfo->Id) { - if (!m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetTypeId() == TYPEID_PLAYER) - return SPELL_FAILED_BAD_IMPLICIT_TARGETS; + case 61336: + if(m_caster->GetTypeId()!=TYPEID_PLAYER || !((Player*)m_caster)->IsInFeralForm()) + return SPELL_FAILED_ONLY_SHAPESHIFT; + break; + case 1515: + { + if (!m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetTypeId() == TYPEID_PLAYER) + return SPELL_FAILED_BAD_IMPLICIT_TARGETS; - if (m_targets.getUnitTarget()->getLevel() > m_caster->getLevel()) - return SPELL_FAILED_HIGHLEVEL; + if (m_targets.getUnitTarget()->getLevel() > m_caster->getLevel()) + return SPELL_FAILED_HIGHLEVEL; - // use SMSG_PET_TAME_FAILURE? - if (!((Creature*)m_targets.getUnitTarget())->GetCreatureInfo()->isTameable ()) - return SPELL_FAILED_BAD_TARGETS; + // use SMSG_PET_TAME_FAILURE? + if (!((Creature*)m_targets.getUnitTarget())->GetCreatureInfo()->isTameable ()) + return SPELL_FAILED_BAD_TARGETS; - if(m_caster->GetPetGUID()) - return SPELL_FAILED_ALREADY_HAVE_SUMMON; + if(m_caster->GetPetGUID()) + return SPELL_FAILED_ALREADY_HAVE_SUMMON; - if(m_caster->GetCharmGUID()) - return SPELL_FAILED_ALREADY_HAVE_CHARM; + if(m_caster->GetCharmGUID()) + return SPELL_FAILED_ALREADY_HAVE_CHARM; + + break; + } + default: + break; } - }break; + break; + } case SPELL_AURA_MOD_POSSESS: case SPELL_AURA_MOD_CHARM: { @@ -4142,17 +4386,14 @@ uint8 Spell::CanCast(bool strict) return SPELL_FAILED_NO_MOUNTS_ALLOWED; // Ignore map check if spell have AreaId. AreaId already checked and this prevent special mount spells - if (m_caster->GetTypeId()==TYPEID_PLAYER && !sMapStore.LookupEntry(m_caster->GetMapId())->IsMountAllowed() && !m_IsTriggeredSpell && !m_spellInfo->AreaId) - return SPELL_FAILED_NO_MOUNTS_ALLOWED; - - if (m_caster->GetAreaId()==35) + if (m_caster->GetTypeId()==TYPEID_PLAYER && !sMapStore.LookupEntry(m_caster->GetMapId())->IsMountAllowed() && !m_IsTriggeredSpell && !m_spellInfo->AreaGroupId) return SPELL_FAILED_NO_MOUNTS_ALLOWED; ShapeshiftForm form = m_caster->m_form; if( form == FORM_CAT || form == FORM_TREE || form == FORM_TRAVEL || form == FORM_AQUA || form == FORM_BEAR || form == FORM_DIREBEAR || form == FORM_CREATUREBEAR || form == FORM_GHOSTWOLF || form == FORM_FLIGHT || - form == FORM_FLIGHT_EPIC || form == FORM_MOONKIN ) + form == FORM_FLIGHT_EPIC || form == FORM_MOONKIN || form == FORM_METAMORPHOSIS ) return SPELL_FAILED_NOT_SHAPESHIFT; break; @@ -4171,11 +4412,10 @@ uint8 Spell::CanCast(bool strict) case SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED: case SPELL_AURA_FLY: { - // not allow cast fly spells at old maps by players (all spells is self target) + // not allow cast fly spells at old maps by players (all spells is self target) if(m_caster->GetTypeId()==TYPEID_PLAYER) { - if( !((Player*)m_caster)->isGameMaster() && - GetVirtualMapForMapAndZone(m_caster->GetMapId(),m_caster->GetZoneId()) != 530) + if( !((Player*)m_caster)->IsAllowUseFlyMountsHere() ) return SPELL_FAILED_NOT_HERE; } break; @@ -4199,10 +4439,10 @@ uint8 Spell::CanCast(bool strict) } // all ok - return 0; + return SPELL_CAST_OK; } -int16 Spell::PetCanCast(Unit* target) +SpellCastResult Spell::CheckPetCast(Unit* target) { if(!m_caster->isAlive()) return SPELL_FAILED_CASTER_DEAD; @@ -4250,20 +4490,16 @@ int16 Spell::PetCanCast(Unit* target) return SPELL_FAILED_NOT_READY; } - uint16 result = CanCast(true); - if(result != 0) - return result; - else - return -1; //this allows to check spell fail 0, in combat + return CheckCast(true); } -uint8 Spell::CheckCasterAuras() const +SpellCastResult Spell::CheckCasterAuras() const { // Flag drop spells totally immuned to caster auras // FIXME: find more nice check for all totally immuned spells // AttributesEx3 & 0x10000000? if(m_spellInfo->Id==23336 || m_spellInfo->Id==23334 || m_spellInfo->Id==34991) - return 0; + return SPELL_CAST_OK; uint8 school_immune = 0; uint32 mechanic_immune = 0; @@ -4283,26 +4519,27 @@ uint8 Spell::CheckCasterAuras() const dispel_immune |= GetDispellMask(DispelType(m_spellInfo->EffectMiscValue[i])); } //immune movement impairment and loss of control - if(m_spellInfo->Id==(uint32)42292) + if(m_spellInfo->Id==42292 || m_spellInfo->Id==59752) mechanic_immune = IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; } //Check whether the cast should be prevented by any state you might have. - uint8 prevented_reason = 0; + SpellCastResult prevented_reason = SPELL_CAST_OK; // Have to check if there is a stun aura. Otherwise will have problems with ghost aura apply while logging out - if(!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_STUNNED) && m_caster->HasAuraType(SPELL_AURA_MOD_STUN)) + uint32 unitflag = m_caster->GetUInt32Value(UNIT_FIELD_FLAGS); // Get unit state + if(unitflag & UNIT_FLAG_STUNNED && !(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_STUNNED)) prevented_reason = SPELL_FAILED_STUNNED; - else if(m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED) && !(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_CONFUSED)) + else if(unitflag & UNIT_FLAG_CONFUSED && !(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_CONFUSED)) prevented_reason = SPELL_FAILED_CONFUSED; - else if(m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING) && !(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_FEARED)) + else if(unitflag & UNIT_FLAG_FLEEING && !(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_FEARED)) prevented_reason = SPELL_FAILED_FLEEING; - else if(m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED) && m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_SILENCE) + else if(unitflag & UNIT_FLAG_SILENCED && m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_SILENCE) prevented_reason = SPELL_FAILED_SILENCED; - else if(m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED) && m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_PACIFY) + else if(unitflag & UNIT_FLAG_PACIFIED && m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_PACIFY) prevented_reason = SPELL_FAILED_PACIFIED; // Attr must make flag drop spell totally immune from all effects - if(prevented_reason) + if(prevented_reason != SPELL_CAST_OK) { if(school_immune || mechanic_immune || dispel_immune) { @@ -4312,7 +4549,7 @@ uint8 Spell::CheckCasterAuras() const { if(itr->second) { - if( GetSpellMechanicMask(itr->second->GetSpellProto(), itr->second->GetEffIndex()) & mechanic_immune ) + if( GetAllSpellMechanicMask(itr->second->GetSpellProto()) & mechanic_immune ) continue; if( GetSpellSchoolMask(itr->second->GetSpellProto()) & school_immune ) continue; @@ -4321,28 +4558,34 @@ uint8 Spell::CheckCasterAuras() const //Make a second check for spell failed so the right SPELL_FAILED message is returned. //That is needed when your casting is prevented by multiple states and you are only immune to some of them. - switch(itr->second->GetModifier()->m_auraname) + for (uint8 i=0;i<MAX_SPELL_EFFECTS;++i) { - case SPELL_AURA_MOD_STUN: - if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_STUNNED)) - return SPELL_FAILED_STUNNED; - break; - case SPELL_AURA_MOD_CONFUSE: - if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_CONFUSED)) - return SPELL_FAILED_CONFUSED; - break; - case SPELL_AURA_MOD_FEAR: - if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_FEARED)) - return SPELL_FAILED_FLEEING; - break; - case SPELL_AURA_MOD_SILENCE: - case SPELL_AURA_MOD_PACIFY: - case SPELL_AURA_MOD_PACIFY_SILENCE: - if( m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_PACIFY) - return SPELL_FAILED_PACIFIED; - else if ( m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_SILENCE) - return SPELL_FAILED_SILENCED; - break; + if (AuraEffect * part = itr->second->GetPartAura(i)) + { + switch(part->GetAuraName()) + { + case SPELL_AURA_MOD_STUN: + if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_STUNNED)) + return SPELL_FAILED_STUNNED; + break; + case SPELL_AURA_MOD_CONFUSE: + if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_CONFUSED)) + return SPELL_FAILED_CONFUSED; + break; + case SPELL_AURA_MOD_FEAR: + if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_FEARED)) + return SPELL_FAILED_FLEEING; + break; + case SPELL_AURA_MOD_SILENCE: + case SPELL_AURA_MOD_PACIFY: + case SPELL_AURA_MOD_PACIFY_SILENCE: + if( m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_PACIFY) + return SPELL_FAILED_PACIFIED; + else if ( m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_SILENCE) + return SPELL_FAILED_SILENCED; + break; + } + } } } } @@ -4351,7 +4594,7 @@ uint8 Spell::CheckCasterAuras() const else return prevented_reason; } - return 0; // all ok + return SPELL_CAST_OK; } bool Spell::CanAutoCast(Unit* target) @@ -4364,25 +4607,26 @@ bool Spell::CanAutoCast(Unit* target) { if( m_spellInfo->StackAmount <= 1) { - if( target->HasAura(m_spellInfo->Id, j) ) + if( target->HasAuraEffect(m_spellInfo->Id, j) ) return false; } else { - if( target->GetAuras().count(Unit::spellEffectPair(m_spellInfo->Id, j)) >= m_spellInfo->StackAmount) - return false; + if( AuraEffect * aureff = target->GetAuraEffect(m_spellInfo->Id, j)) + if (aureff->GetParentAura()->GetStackAmount() >= m_spellInfo->StackAmount) + return false; } } else if ( IsAreaAuraEffect( m_spellInfo->Effect[j] )) { - if( target->HasAura(m_spellInfo->Id, j) ) + if( target->HasAuraEffect(m_spellInfo->Id, j) ) return false; } } - int16 result = PetCanCast(target); + SpellCastResult result = CheckPetCast(target); - if(result == -1 || result == SPELL_FAILED_UNIT_NOT_INFRONT) + if(result == SPELL_CAST_OK || result == SPELL_FAILED_UNIT_NOT_INFRONT) { FillTargetMap(); //check if among target units, our WANTED target is as well (->only self cast spells return false) @@ -4393,12 +4637,13 @@ bool Spell::CanAutoCast(Unit* target) return false; //target invalid } -uint8 Spell::CheckRange(bool strict) +SpellCastResult Spell::CheckRange(bool strict) { //float range_mod; // self cast doesn't need range checking -- also for Starshards fix - if (m_spellInfo->rangeIndex == 1) return 0; + if (m_spellInfo->rangeIndex == 1) + return SPELL_CAST_OK; // i do not know why we need this /*if (strict) //add radius of caster @@ -4407,15 +4652,15 @@ uint8 Spell::CheckRange(bool strict) range_mod = 6.25;*/ SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex); - float max_range = GetSpellMaxRange(srange); // + range_mod; - float min_range = GetSpellMinRange(srange); + + Unit *target = m_targets.getUnitTarget(); + float max_range = m_caster->GetSpellMaxRangeForTarget(target, srange); // + range_mod; + float min_range = m_caster->GetSpellMinRangeForTarget(target, srange); uint32 range_type = GetSpellRangeType(srange); if(Player* modOwner = m_caster->GetSpellModOwner()) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, max_range, this); - Unit *target = m_targets.getUnitTarget(); - if(target && target != m_caster) { if(range_type == SPELL_RANGE_MELEE) @@ -4449,7 +4694,7 @@ uint8 Spell::CheckRange(bool strict) return SPELL_FAILED_TOO_CLOSE; } - return 0; // ok + return SPELL_CAST_OK; } int32 Spell::CalculatePowerCost() @@ -4489,9 +4734,12 @@ int32 Spell::CalculatePowerCost() case POWER_FOCUS: case POWER_ENERGY: case POWER_HAPPINESS: - // case POWER_RUNES: powerCost += m_spellInfo->ManaCostPercentage * m_caster->GetMaxPower(Powers(m_spellInfo->powerType)) / 100; break; + case POWER_RUNE: + case POWER_RUNIC_POWER: + sLog.outDebug("Spell::CalculateManaCost: Not implemented yet!"); + break; default: sLog.outError("Spell::CalculateManaCost: Unknown power type '%d' in spell %d", m_spellInfo->powerType, m_spellInfo->Id); return 0; @@ -4517,39 +4765,43 @@ int32 Spell::CalculatePowerCost() return powerCost; } -uint8 Spell::CheckPower() +SpellCastResult Spell::CheckPower() { // item cast not used power if(m_CastItem) - return 0; + return SPELL_CAST_OK; // health as power used - need check health amount if(m_spellInfo->powerType == POWER_HEALTH) { if(m_caster->GetHealth() <= m_powerCost) return SPELL_FAILED_CASTER_AURASTATE; - return 0; + return SPELL_CAST_OK; } // Check valid power type if( m_spellInfo->powerType >= MAX_POWERS ) { - sLog.outError("Spell::CheckMana: Unknown power type '%d'", m_spellInfo->powerType); + sLog.outError("Spell::CheckPower: Unknown power type '%d'", m_spellInfo->powerType); return SPELL_FAILED_UNKNOWN; } + + SpellCastResult failReason = CheckRuneCost(m_spellInfo->runeCostID); + if(failReason != SPELL_CAST_OK) + return failReason; + // Check power amount Powers powerType = Powers(m_spellInfo->powerType); if(m_caster->GetPower(powerType) < m_powerCost) return SPELL_FAILED_NO_POWER; else - return 0; + return SPELL_CAST_OK; } -uint8 Spell::CheckItems() +SpellCastResult Spell::CheckItems() { if (m_caster->GetTypeId() != TYPEID_PLAYER) - return 0; + return SPELL_CAST_OK; - uint32 itemid, itemcount; Player* p_caster = (Player*)m_caster; if(!m_CastItem) @@ -4559,77 +4811,72 @@ uint8 Spell::CheckItems() } else { - itemid = m_CastItem->GetEntry(); + uint32 itemid = m_CastItem->GetEntry(); if( !p_caster->HasItemCount(itemid,1) ) return SPELL_FAILED_ITEM_NOT_READY; - else - { - ItemPrototype const *proto = m_CastItem->GetProto(); - if(!proto) - return SPELL_FAILED_ITEM_NOT_READY; - for (int i = 0; i<5; i++) + ItemPrototype const *proto = m_CastItem->GetProto(); + if(!proto) + return SPELL_FAILED_ITEM_NOT_READY; + + for (int i = 0; i<5; i++) + if (proto->Spells[i].SpellCharges) + if(m_CastItem->GetSpellCharges(i)==0) + return SPELL_FAILED_NO_CHARGES_REMAIN; + + // consumable cast item checks + if (proto->Class == ITEM_CLASS_CONSUMABLE && m_targets.getUnitTarget()) + { + // such items should only fail if there is no suitable effect at all - see Rejuvenation Potions for example + SpellCastResult failReason = SPELL_CAST_OK; + for (int i = 0; i < 3; i++) { - if (proto->Spells[i].SpellCharges) + // skip check, pet not required like checks, and for TARGET_PET m_targets.getUnitTarget() is not the real target but the caster + if (m_spellInfo->EffectImplicitTargetA[i] == TARGET_PET) + continue; + + if (m_spellInfo->Effect[i] == SPELL_EFFECT_HEAL) { - if(m_CastItem->GetSpellCharges(i)==0) - return SPELL_FAILED_NO_CHARGES_REMAIN; + if (m_targets.getUnitTarget()->GetHealth() == m_targets.getUnitTarget()->GetMaxHealth()) + { + failReason = SPELL_FAILED_ALREADY_AT_FULL_HEALTH; + continue; + } + else + { + failReason = SPELL_CAST_OK; + break; + } } - } - uint32 ItemClass = proto->Class; - if (ItemClass == ITEM_CLASS_CONSUMABLE && m_targets.getUnitTarget()) - { - // such items should only fail if there is no suitable effect at all - see Rejuvenation Potions for example - uint8 failReason = 0; - for (int i = 0; i < 3; i++) + // Mana Potion, Rage Potion, Thistle Tea(Rogue), ... + if (m_spellInfo->Effect[i] == SPELL_EFFECT_ENERGIZE) { - // skip check, pet not required like checks, and for TARGET_PET m_targets.getUnitTarget() is not the real target but the caster - if (m_spellInfo->EffectImplicitTargetA[i] == TARGET_PET) + if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS) + { + failReason = SPELL_FAILED_ALREADY_AT_FULL_POWER; continue; + } - if (m_spellInfo->Effect[i] == SPELL_EFFECT_HEAL) + Powers power = Powers(m_spellInfo->EffectMiscValue[i]); + if (m_targets.getUnitTarget()->GetPower(power) == m_targets.getUnitTarget()->GetMaxPower(power)) { - if (m_targets.getUnitTarget()->GetHealth() == m_targets.getUnitTarget()->GetMaxHealth()) - { - failReason = (uint8)SPELL_FAILED_ALREADY_AT_FULL_HEALTH; - continue; - } - else - { - failReason = 0; - break; - } + failReason = SPELL_FAILED_ALREADY_AT_FULL_POWER; + continue; } - - // Mana Potion, Rage Potion, Thistle Tea(Rogue), ... - if (m_spellInfo->Effect[i] == SPELL_EFFECT_ENERGIZE) + else { - if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS) - { - failReason = (uint8)SPELL_FAILED_ALREADY_AT_FULL_POWER; - continue; - } - - Powers power = Powers(m_spellInfo->EffectMiscValue[i]); - if (m_targets.getUnitTarget()->GetPower(power) == m_targets.getUnitTarget()->GetMaxPower(power)) - { - failReason = (uint8)SPELL_FAILED_ALREADY_AT_FULL_POWER; - continue; - } - else - { - failReason = 0; - break; - } + failReason = SPELL_CAST_OK; + break; } } - if (failReason) - return failReason; } + if (failReason != SPELL_CAST_OK) + return failReason; } } + // check target item if(m_targets.getItemTargetGUID()) { if(m_caster->GetTypeId() != TYPEID_PLAYER) @@ -4648,6 +4895,7 @@ uint8 Spell::CheckItems() return SPELL_FAILED_EQUIPPED_ITEM_CLASS; } + // check spell focus object if(m_spellInfo->RequiresSpellFocus) { CellPair p(Trinity::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY())); @@ -4655,29 +4903,29 @@ uint8 Spell::CheckItems() cell.data.Part.reserved = ALL_DISTRICT; GameObject* ok = NULL; - Trinity::GameObjectFocusCheck go_check(m_caster,m_spellInfo->RequiresSpellFocus); - Trinity::GameObjectSearcher<Trinity::GameObjectFocusCheck> checker(ok,go_check); + MaNGOS::GameObjectFocusCheck go_check(m_caster,m_spellInfo->RequiresSpellFocus); + MaNGOS::GameObjectSearcher<MaNGOS::GameObjectFocusCheck> checker(m_caster,ok,go_check); TypeContainerVisitor<Trinity::GameObjectSearcher<Trinity::GameObjectFocusCheck>, GridTypeMapContainer > object_checker(checker); CellLock<GridReadGuard> cell_lock(cell, p); cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster)); if(!ok) - return (uint8)SPELL_FAILED_REQUIRES_SPELL_FOCUS; + return SPELL_FAILED_REQUIRES_SPELL_FOCUS; focusObject = ok; // game object found in range } - if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP && - m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION))) + // check reagents (ignore triggered spells with reagents processed by original spell) and special reagent ignore case. + if (!m_IsTriggeredSpell && !p_caster->CanNoReagentCast(m_spellInfo)) { for(uint32 i=0;i<8;i++) { if(m_spellInfo->Reagent[i] <= 0) continue; - itemid = m_spellInfo->Reagent[i]; - itemcount = m_spellInfo->ReagentCount[i]; + uint32 itemid = m_spellInfo->Reagent[i]; + uint32 itemcount = m_spellInfo->ReagentCount[i]; // if CastItem is also spell reagent if( m_CastItem && m_CastItem->GetEntry() == itemid ) @@ -4685,7 +4933,7 @@ uint8 Spell::CheckItems() ItemPrototype const *proto = m_CastItem->GetProto(); if(!proto) return SPELL_FAILED_ITEM_NOT_READY; - for(int s=0;s<5;s++) + for(int s=0; s < MAX_ITEM_PROTO_SPELLS; ++s) { // CastItem will be used up and does not count as reagent int32 charges = m_CastItem->GetSpellCharges(s); @@ -4697,10 +4945,11 @@ uint8 Spell::CheckItems() } } if( !p_caster->HasItemCount(itemid,itemcount) ) - return (uint8)SPELL_FAILED_ITEM_NOT_READY; //0x54 + return SPELL_FAILED_ITEM_NOT_READY; //0x54 } } + // check totem-item requirements (items presence in inventory) uint32 totems = 2; for(int i=0;i<2;++i) { @@ -4715,9 +4964,9 @@ uint8 Spell::CheckItems() totems -= 1; } if(totems != 0) - return (uint8)SPELL_FAILED_TOTEMS; //0x7C + return SPELL_FAILED_TOTEMS; //0x7C - //Check items for TotemCategory + // Check items for TotemCategory (items presence in inventory) uint32 TotemCategory = 2; for(int i=0;i<2;++i) { @@ -4733,8 +4982,9 @@ uint8 Spell::CheckItems() TotemCategory -= 1; } if(TotemCategory != 0) - return (uint8)SPELL_FAILED_TOTEM_CATEGORY; //0x7B + return SPELL_FAILED_TOTEM_CATEGORY; //0x7B + // special checks for spell effects for(int i = 0; i < 3; i++) { switch (m_spellInfo->Effect[i]) @@ -4754,6 +5004,7 @@ uint8 Spell::CheckItems() break; } case SPELL_EFFECT_ENCHANT_ITEM: + case SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC: { Item* targetItem = m_targets.getItemTarget(); if(!targetItem) @@ -4837,13 +5088,36 @@ uint8 Spell::CheckItems() return SPELL_FAILED_LOW_CASTLEVEL; //make sure the player has the required ores in inventory if(m_targets.getItemTarget()->GetCount() < 5) - return SPELL_FAILED_PROSPECT_NEED_MORE; + return SPELL_FAILED_NEED_MORE_ITEMS; if(!LootTemplates_Prospecting.HaveLootFor(m_targets.getItemTargetEntry())) return SPELL_FAILED_CANT_BE_PROSPECTED; break; } + case SPELL_EFFECT_MILLING: + { + if(!m_targets.getItemTarget()) + return SPELL_FAILED_CANT_BE_MILLED; + //ensure item is a millable herb + if(!(m_targets.getItemTarget()->GetProto()->BagFamily & BAG_FAMILY_MASK_HERBS) || m_targets.getItemTarget()->GetProto()->Class != ITEM_CLASS_TRADE_GOODS) + return SPELL_FAILED_CANT_BE_MILLED; + //prevent milling in trade slot + if( m_targets.getItemTarget()->GetOwnerGUID() != m_caster->GetGUID() ) + return SPELL_FAILED_CANT_BE_MILLED; + //Check for enough skill in inscription + uint32 item_millingskilllevel = m_targets.getItemTarget()->GetProto()->RequiredSkillRank; + if(item_millingskilllevel >p_caster->GetSkillValue(SKILL_INSCRIPTION)) + return SPELL_FAILED_LOW_CASTLEVEL; + //make sure the player has the required herbs in inventory + if(m_targets.getItemTarget()->GetCount() < 5) + return SPELL_FAILED_NEED_MORE_ITEMS; + + if(!LootTemplates_Milling.HaveLootFor(m_targets.getItemTargetEntry())) + return SPELL_FAILED_CANT_BE_MILLED; + + break; + } case SPELL_EFFECT_WEAPON_DAMAGE: case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: { @@ -4912,7 +5186,7 @@ uint8 Spell::CheckItems() } } - return uint8(0); + return SPELL_CAST_OK; } void Spell::Delayed() // only called in DealDamage() @@ -4923,18 +5197,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) { @@ -4958,14 +5236,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) { @@ -4984,11 +5266,8 @@ void Spell::DelayedChannel() Unit* unit = m_caster->GetGUID()==ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID); if (unit) { - for (int j=0;j<3;j++) - if( ihit->effectMask & (1<<j) ) - unit->DelayAura(m_spellInfo->Id, j, delaytime); + unit->DelayAura(m_spellInfo->Id, m_caster->GetGUID(), delaytime); } - } } @@ -5019,17 +5298,12 @@ void Spell::UpdatePointers() m_targets.Update(m_caster); } -bool Spell::IsAffectedBy(SpellEntry const *spellInfo, uint32 effectId) -{ - return spellmgr.IsAffectedBySpell(m_spellInfo,spellInfo->Id,effectId,spellInfo->EffectItemType[effectId]); -} - bool Spell::CheckTargetCreatureType(Unit* target) const { uint32 spellCreatureTargetMask = m_spellInfo->TargetCreatureType; // Curse of Doom : not find another way to fix spell target check :/ - if(m_spellInfo->SpellFamilyName==SPELLFAMILY_WARLOCK && m_spellInfo->SpellFamilyFlags == 0x0200000000LL) + if(m_spellInfo->SpellFamilyName==SPELLFAMILY_WARLOCK && m_spellInfo->SpellFamilyFlags.IsEqual(0,0x02,0)) { // not allow cast at player if(target->GetTypeId()==TYPEID_PLAYER) @@ -5063,7 +5337,7 @@ CurrentSpellTypes Spell::GetCurrentContainer() return(CURRENT_GENERIC_SPELL); } -bool Spell::CheckTarget( Unit* target, uint32 eff, bool hitPhase ) +bool Spell::CheckTarget( Unit* target, uint32 eff ) { // Check targets for creature type mask and remove not appropriate (skip explicit self target case, maybe need other explicit targets) if(m_spellInfo->EffectImplicitTargetA[eff]!=TARGET_SELF) @@ -5072,6 +5346,12 @@ bool Spell::CheckTarget( Unit* target, uint32 eff, bool hitPhase ) return false; } + // Check Aura spell req (need for AoE spells) + if(m_spellInfo->targetAuraSpell && !target->HasAura(m_spellInfo->targetAuraSpell)) + return false; + if (m_spellInfo->excludeTargetAuraSpell && target->HasAura(m_spellInfo->excludeTargetAuraSpell)) + return false; + // Check targets for not_selectable unit flag and remove // A player can cast spells on his pet (or other controlled unit) though in any state if (target != m_caster && target->GetCharmerOrOwnerGUID() != m_caster->GetGUID()) @@ -5132,7 +5412,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; } @@ -5146,14 +5432,13 @@ Unit* Spell::SelectMagnetTarget() if(target && m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && target->HasAuraType(SPELL_AURA_SPELL_MAGNET)) //Attributes & 0x10 what is this? { - Unit::AuraList const& magnetAuras = target->GetAurasByType(SPELL_AURA_SPELL_MAGNET); - for(Unit::AuraList::const_iterator itr = magnetAuras.begin(); itr != magnetAuras.end(); ++itr) + Unit::AuraEffectList const& magnetAuras = target->GetAurasByType(SPELL_AURA_SPELL_MAGNET); + for(Unit::AuraEffectList::const_iterator itr = magnetAuras.begin(); itr != magnetAuras.end(); ++itr) { if(Unit* magnet = (*itr)->GetCaster()) { - if((*itr)->m_procCharges>0) + if((*itr)->GetParentAura()->DropAuraCharge()) { - (*itr)->SetAuraProcCharges((*itr)->m_procCharges-1); target = magnet; m_targets.setUnitTarget(target); AddUnitTarget(target, 0); @@ -5177,7 +5462,7 @@ Unit* Spell::SelectMagnetTarget() bool Spell::IsNeedSendToClient() const { - return m_spellInfo->SpellVisual!=0 || IsChanneledSpell(m_spellInfo) || + return m_spellInfo->SpellVisual[0] || m_spellInfo->SpellVisual[1] || IsChanneledSpell(m_spellInfo) || m_spellInfo->speed > 0.0f || !m_triggeredByAuraSpell && !m_IsTriggeredSpell; } @@ -5256,9 +5541,6 @@ bool SpellEvent::Execute(uint64 e_time, uint32 p_time) // another non-melee non-delayed spell is casted now, abort m_Spell->cancel(); } - // Check if target of channeled spell still in range - else if (m_Spell->CheckRange(false)) - m_Spell->cancel(); else { // do the action (pass spell to channeling state) @@ -5361,6 +5643,14 @@ void Spell::CalculateDamageDoneForAllTargets() } } + bool usesAmmo=true; + Unit::AuraEffectList const& Auras = m_caster->GetAurasByType(SPELL_AURA_ABILITY_CONSUME_NO_AMMO); + for(Unit::AuraEffectList::const_iterator j = Auras.begin();j != Auras.end(); ++j) + { + if((*j)->isAffectedOnSpell(m_spellInfo)) + usesAmmo=false; + } + for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) { TargetInfo &target = *ihit; @@ -5373,6 +5663,28 @@ void Spell::CalculateDamageDoneForAllTargets() if (!unit) continue; + if (usesAmmo) + { + bool ammoTaken=false; + for (uint8 i=0;i<3;i++) + { + if (!(mask & 1<<i)) + continue; + switch (m_spellInfo->Effect[i]) + { + case SPELL_EFFECT_SCHOOL_DAMAGE: + case SPELL_EFFECT_WEAPON_DAMAGE: + case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: + case SPELL_EFFECT_NORMALIZED_WEAPON_DMG: + case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE: + ammoTaken=true; + TakeAmmo(); + } + if (ammoTaken) + break; + } + } + if (target.missCondition==SPELL_MISS_NONE) // In case spell hit target, do all effect on that target target.damage += CalculateDamageDone(unit, mask, multiplier); else if (target.missCondition == SPELL_MISS_REFLECT) // In case spell reflect from target, do all effect on caster (if hit) @@ -5431,3 +5743,64 @@ int32 Spell::CalculateDamageDone(Unit *unit, const uint32 effectMask, float *mul return damageDone; } +SpellCastResult Spell::CanOpenLock(uint32 effIndex, uint32 lockId, SkillType& skillId, int32& reqSkillValue, int32& skillValue) +{ + if(!lockId) // possible case for GO and maybe for items. + return SPELL_CAST_OK; + + // Get LockInfo + LockEntry const *lockInfo = sLockStore.LookupEntry(lockId); + + if (!lockInfo) + return SPELL_FAILED_BAD_TARGETS; + + bool reqKey = false; // some locks not have reqs + + for(int j = 0; j < 8; ++j) + { + switch(lockInfo->Type[j]) + { + // check key item (many fit cases can be) + case LOCK_KEY_ITEM: + if(lockInfo->Index[j] && m_CastItem && m_CastItem->GetEntry()==lockInfo->Index[j]) + return SPELL_CAST_OK; + reqKey = true; + break; + // check key skill (only single first fit case can be) + case LOCK_KEY_SKILL: + { + reqKey = true; + + // wrong locktype, skip + if(uint32(m_spellInfo->EffectMiscValue[effIndex]) != lockInfo->Index[j]) + continue; + + skillId = SkillByLockType(LockType(lockInfo->Index[j])); + + if ( skillId != SKILL_NONE ) + { + // skill bonus provided by casting spell (mostly item spells) + // add the damage modifier from the spell casted (cheat lock / skeleton key etc.) (use m_currentBasePoints, CalculateDamage returns wrong value) + uint32 spellSkillBonus = uint32(m_currentBasePoints[effIndex]+1); + reqSkillValue = lockInfo->Skill[j]; + + // castitem check: rogue using skeleton keys. the skill values should not be added in this case. + skillValue = m_CastItem || m_caster->GetTypeId()!= TYPEID_PLAYER ? + 0 : ((Player*)m_caster)->GetSkillValue(skillId); + + skillValue += spellSkillBonus; + + if (skillValue < reqSkillValue) + return SPELL_FAILED_LOW_CASTLEVEL; + } + + return SPELL_CAST_OK; + } + } + } + + if(reqKey) + return SPELL_FAILED_BAD_TARGETS; + + return SPELL_CAST_OK; +} diff --git a/src/game/Spell.h b/src/game/Spell.h index df630a33432..f012fdff231 100644 --- a/src/game/Spell.h +++ b/src/game/Spell.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,6 +22,7 @@ #define __SPELL_H #include "GridDefines.h" +#include "SharedDefines.h" class WorldSession; class Unit; @@ -47,26 +48,51 @@ enum SpellCastTargetFlags TARGET_FLAG_RESURRECTABLE = 0x8000*/ TARGET_FLAG_SELF = 0x00000000, + TARGET_FLAG_UNUSED1 = 0x00000001, // not used in any spells as of 3.0.3 (can be set dynamically) TARGET_FLAG_UNIT = 0x00000002, // pguid + TARGET_FLAG_UNUSED2 = 0x00000004, // not used in any spells as of 3.0.3 (can be set dynamically) + TARGET_FLAG_UNUSED3 = 0x00000008, // not used in any spells as of 3.0.3 (can be set dynamically) TARGET_FLAG_ITEM = 0x00000010, // pguid TARGET_FLAG_SOURCE_LOCATION = 0x00000020, // 3 float TARGET_FLAG_DEST_LOCATION = 0x00000040, // 3 float - TARGET_FLAG_OBJECT_UNK = 0x00000080, // ? + TARGET_FLAG_OBJECT_UNK = 0x00000080, // used in 7 spells only + TARGET_FLAG_UNIT_UNK = 0x00000100, // looks like self target (480 spells) TARGET_FLAG_PVP_CORPSE = 0x00000200, // pguid - TARGET_FLAG_OBJECT = 0x00000800, // pguid - TARGET_FLAG_TRADE_ITEM = 0x00001000, // pguid - TARGET_FLAG_STRING = 0x00002000, // string - TARGET_FLAG_UNK1 = 0x00004000, // ? - TARGET_FLAG_CORPSE = 0x00008000, // pguid - TARGET_FLAG_UNK2 = 0x00010000 // pguid + TARGET_FLAG_UNIT_CORPSE = 0x00000400, // 10 spells (gathering professions) + TARGET_FLAG_OBJECT = 0x00000800, // pguid, 2 spells + TARGET_FLAG_TRADE_ITEM = 0x00001000, // pguid, 0 spells + TARGET_FLAG_STRING = 0x00002000, // string, 0 spells + TARGET_FLAG_UNK1 = 0x00004000, // 199 spells, opening object/lock + TARGET_FLAG_CORPSE = 0x00008000, // pguid, resurrection spells + TARGET_FLAG_UNK2 = 0x00010000, // pguid, not used in any spells as of 3.0.3 (can be set dynamically) + TARGET_FLAG_GLYPH = 0x00020000 // used in glyph spells }; enum SpellCastFlags { + CAST_FLAG_NONE = 0x00000000, + CAST_FLAG_UNKNOWN0 = 0x00000001, // may be pending spell cast CAST_FLAG_UNKNOWN1 = 0x00000002, + CAST_FLAG_UNKNOWN11 = 0x00000004, + CAST_FLAG_UNKNOWN12 = 0x00000008, CAST_FLAG_UNKNOWN2 = 0x00000010, - CAST_FLAG_AMMO = 0x00000020, - CAST_FLAG_UNKNOWN3 = 0x00000100 + CAST_FLAG_AMMO = 0x00000020, // Projectiles visual + CAST_FLAG_UNKNOWN8 = 0x00000040, + CAST_FLAG_UNKNOWN9 = 0x00000080, + CAST_FLAG_UNKNOWN3 = 0x00000100, + CAST_FLAG_UNKNOWN13 = 0x00000200, + CAST_FLAG_UNKNOWN14 = 0x00000400, + CAST_FLAG_POWER_LEFT_SELF = 0x00000800, // wotlk + CAST_FLAG_UNKNOWN15 = 0x00001000, + CAST_FLAG_UNKNOWN16 = 0x00002000, + CAST_FLAG_UNKNOWN17 = 0x00004000, + CAST_FLAG_UNKNOWN18 = 0x00008000, + CAST_FLAG_UNKNOWN19 = 0x00010000, + CAST_FLAG_UNKNOWN4 = 0x00020000, // wotlk + CAST_FLAG_UNKNOWN10 = 0x00040000, + CAST_FLAG_UNKNOWN5 = 0x00080000, // wotlk + CAST_FLAG_UNKNOWN20 = 0x00100000, + CAST_FLAG_UNKNOWN7 = 0x00200000 // wotlk, rune cooldown list }; enum SpellRangeFlag @@ -136,6 +162,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; } @@ -208,7 +235,7 @@ enum SpellTargets SPELL_TARGETS_CHAINHEAL, }; -#define SPELL_SPELL_CHANNEL_UPDATE_INTERVAL 1000 +#define SPELL_SPELL_CHANNEL_UPDATE_INTERVAL (1*IN_MILISECONDS) typedef std::multimap<uint64, uint64> SpellTargetTimeMap; @@ -234,6 +261,7 @@ class Spell void EffectHealthLeech(uint32 i); void EffectQuestComplete(uint32 i); void EffectCreateItem(uint32 i); + void EffectCreateItem2(uint32 i); void EffectPersistentAA(uint32 i); void EffectEnergize(uint32 i); void EffectOpenLock(uint32 i); @@ -242,16 +270,15 @@ class Spell void EffectProficiency(uint32 i); void EffectApplyAreaAura(uint32 i); void EffectSummonType(uint32 i); - void EffectSummon(uint32 i); void EffectLearnSpell(uint32 i); void EffectDispel(uint32 i); void EffectDualWield(uint32 i); void EffectPickPocket(uint32 i); void EffectAddFarsight(uint32 i); - void EffectSummonPossessed(uint32 i); void EffectSummonWild(uint32 i); - void EffectSummonGuardian(uint32 i); void EffectHealMechanical(uint32 i); + void EffectJump(uint32 i); + void EffectJump2(uint32 i); void EffectTeleUnitsFaceCaster(uint32 i); void EffectLearnSkill(uint32 i); void EffectAddHonor(uint32 i); @@ -276,7 +303,7 @@ class Spell void EffectStuck(uint32 i); void EffectSummonPlayer(uint32 i); void EffectActivateObject(uint32 i); - void EffectSummonTotem(uint32 i); + void EffectApplyGlyph(uint32 i); void EffectEnchantHeldItem(uint32 i); void EffectSummonObject(uint32 i); void EffectResurrect(uint32 i); @@ -293,6 +320,8 @@ class Spell void EffectSkinning(uint32 i); void EffectCharge(uint32 i); void EffectProspecting(uint32 i); + void EffectMilling(uint32 i); + void EffectRenamePet(uint32 i); void EffectSendTaxi(uint32 i); void EffectSummonCritter(uint32 i); void EffectKnockBack(uint32 i); @@ -319,22 +348,28 @@ class Spell void EffectKillCredit(uint32 i); void EffectQuestFail(uint32 i); void EffectRedirectThreat(uint32 i); + void EffectActivateRune(uint32 i); + void EffectTitanGrip(uint32 i); + void EffectEnchantItemPrismatic(uint32 i); Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 originalCasterGUID = 0, Spell** triggeringContainer = NULL, bool skipCheck = false ); ~Spell(); - void prepare(SpellCastTargets * targets, Aura* triggeredByAura = NULL); + void prepare(SpellCastTargets const* targets, AuraEffect* triggeredByAura = NULL); void cancel(); void update(uint32 difftime); void cast(bool skipCheck = false); void finish(bool ok = true); void TakePower(); + void TakeAmmo(); + + void TakeRunePower(); void TakeReagents(); void TakeCastItem(); void TriggerSpell(); - uint8 CanCast(bool strict); - int16 PetCanCast(Unit* target); - bool CanAutoCast(Unit* target); + + SpellCastResult CheckCast(bool strict); + SpellCastResult CheckPetCast(Unit* target); // handlers void handle_immediate(); @@ -343,10 +378,11 @@ class Spell void _handle_immediate_phase(); void _handle_finish_phase(); - uint8 CheckItems(); - uint8 CheckRange(bool strict); - uint8 CheckPower(); - uint8 CheckCasterAuras() const; + SpellCastResult CheckItems(); + SpellCastResult CheckRange(bool strict); + SpellCastResult CheckPower(); + SpellCastResult CheckRuneCost(uint32 runeCostID); + SpellCastResult CheckCasterAuras() const; int32 CalculateDamage(uint8 i, Unit* target) { return m_caster->CalculateSpellDamage(m_spellInfo,i,m_currentBasePoints[i],target); } int32 CalculatePowerCost(); @@ -354,11 +390,10 @@ class Spell bool HaveTargetsForEffect(uint8 effect) const; void Delayed(); void DelayedChannel(); - inline uint32 getState() const { return m_spellState; } + uint32 getState() const { return m_spellState; } void setState(uint32 state) { m_spellState = state; } void DoCreateItem(uint32 i, uint32 itemtype); - void WriteSpellGoTargets( WorldPacket * data ); void WriteAmmoToPacket( WorldPacket * data ); void FillTargetMap(); @@ -366,9 +401,10 @@ class Spell void SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap); Unit* SelectMagnetTarget(); - bool CheckTarget( Unit* target, uint32 eff, bool hitPhase ); + bool CheckTarget( Unit* target, uint32 eff ); + bool CanAutoCast(Unit* target); - void SendCastResult(uint8 result); + void SendCastResult(SpellCastResult result); void SendSpellStart(); void SendSpellGo(); void SendSpellCooldown(); @@ -388,6 +424,8 @@ class Spell Item* m_CastItem; uint64 m_castItemGUID; uint8 m_cast_count; + uint32 m_glyphIndex; + uint32 m_preCastSpell; SpellCastTargets m_targets; int32 GetCastTime() const { return m_casttime; } @@ -424,8 +462,6 @@ class Spell void UpdatePointers(); // must be used at call Spell code after time delay (non triggered spell cast/update spell call/etc) - bool IsAffectedBy(SpellEntry const *spellInfo, uint32 effectId); - bool CheckTargetCreatureType(Unit* target) const; void AddTriggeredSpell(SpellEntry const* spell) { m_TriggerSpells.push_back(spell); } @@ -451,9 +487,17 @@ class Spell int32 m_casttime; // Calculated spell cast time initialized only in Spell::prepare bool m_canReflect; // can reflect this spell? bool m_autoRepeat; + 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 @@ -464,6 +508,7 @@ class Spell bool m_referencedFromCurrentSpell; // mark as references to prevent deleted and access by dead pointers bool m_executedCurrently; // mark as executed to prevent deleted and access by dead pointers bool m_needSpellLog; // need to send spell log? + bool m_needComboPoints; uint8 m_applyMultiplierMask; // by effect: damage multiplier needed? float m_damageMultipliers[3]; // by effect: damage multiplier @@ -472,6 +517,7 @@ class Spell Item* itemTarget; GameObject* gameObjTarget; int32 damage; + Aura * m_spellAura; // only used in DoAllEffectOnTarget // this is set in Spell Hit, but used in Apply Aura handler DiminishingLevels m_diminishLevel; @@ -497,8 +543,6 @@ class Spell // Spell target subsystem //***************************************** // Targets store structures and data - uint32 m_countOfHit; - uint32 m_countOfMiss; struct TargetInfo { uint64 targetGUID; @@ -550,6 +594,14 @@ class Spell void SpellDamageSchoolDmg(uint32 i); void SpellDamageWeaponDmg(uint32 i); void SpellDamageHeal(uint32 i); + + void GetSummonPosition(float &x, float &y, float &z, float radius = 0.0f, uint32 count = 0); + void SummonTotem (uint32 entry, SummonPropertiesEntry const *properties); + void SummonGuardian (uint32 entry, SummonPropertiesEntry const *properties); + void SummonPossessed(uint32 entry, SummonPropertiesEntry const *properties); + void SummonVehicle (uint32 entry, SummonPropertiesEntry const *properties); + + SpellCastResult CanOpenLock(uint32 effIndex, uint32 lockid, SkillType& skillid, int32& reqSkillValue, int32& skillValue); // ------------------------------------------- //List For Triggered Spells @@ -585,7 +637,7 @@ namespace Trinity const uint32& i_push_type; float i_radius, i_radiusSq; SpellTargets i_TargetType; - Unit* i_caster; + Unit* i_source; uint32 i_entry; float i_x, i_y, i_z; @@ -594,72 +646,64 @@ namespace Trinity : i_data(&data), i_spell(spell), i_push_type(type), i_radius(radius), i_radiusSq(radius*radius) , i_TargetType(TargetType), i_entry(entry), i_x(x), i_y(y), i_z(z) { - i_caster = spell.GetCaster(); + i_source = spell.GetCaster(); + assert(i_source); } template<class T> inline void Visit(GridRefManager<T> &m) { - assert(i_data); - - if(!i_caster) - return; - for(typename GridRefManager<T>::iterator itr = m.begin(); itr != m.end(); ++itr) { - if( !itr->getSource()->isAlive() || (itr->getSource()->GetTypeId() == TYPEID_PLAYER && ((Player*)itr->getSource())->isInFlight())) - continue; + Unit *target = (Unit*)itr->getSource(); switch (i_TargetType) { - case SPELL_TARGETS_ALLY: - if (!itr->getSource()->isAttackableByAOE() || !i_caster->IsFriendlyTo( itr->getSource() )) - continue; - break; case SPELL_TARGETS_ENEMY: - { - if(itr->getSource()->GetTypeId()==TYPEID_UNIT && ((Creature*)itr->getSource())->isTotem()) + if(target->GetTypeId() == TYPEID_UNIT && ((Creature*)target)->isTotem()) continue; - if(!itr->getSource()->isAttackableByAOE()) + if(!target->isAttackableByAOE()) continue; - - Unit* check = i_caster->GetCharmerOrOwnerOrSelf(); - - if( check->GetTypeId()==TYPEID_PLAYER ) + if(i_source->GetTypeId()==TYPEID_PLAYER) { - if (check->IsFriendlyTo( itr->getSource() )) + if(i_source->IsFriendlyTo(target)) continue; } else { - if (!check->IsHostileTo( itr->getSource() )) + if(!i_source->IsHostileTo(target)) continue; } - }break; + break; + case SPELL_TARGETS_ALLY: + if(!target->isAttackableByAOE() || !i_source->IsFriendlyTo(target)) + continue; + break; case SPELL_TARGETS_ENTRY: - { - if(itr->getSource()->GetEntry()!= i_entry) + if(target->GetEntry()!= i_entry) continue; - }break; - default: continue; + break; + default: + continue; } switch(i_push_type) { + case PUSH_DEST_CENTER: + case PUSH_TARGET_CENTER: + if((target->GetDistanceSq(i_x, i_y, i_z) < i_radiusSq)) + i_data->push_back(target); + break; case PUSH_IN_FRONT: - if(i_caster->isInFront((Unit*)(itr->getSource()), i_radius, M_PI/3 )) - i_data->push_back(itr->getSource()); + if(i_source->isInFront(target, i_radius, M_PI/3)) + i_data->push_back(target); break; case PUSH_IN_BACK: - if(i_caster->isInBack((Unit*)(itr->getSource()), i_radius, M_PI/3 )) - i_data->push_back(itr->getSource()); + if(i_source->isInBack(target, i_radius, M_PI/3)) + i_data->push_back(target); break; case PUSH_IN_LINE: - if(i_caster->isInLine((Unit*)(itr->getSource()), i_radius )) - i_data->push_back(itr->getSource()); - break; - default: - if((itr->getSource()->GetDistanceSq(i_x, i_y, i_z) < i_radiusSq)) - i_data->push_back(itr->getSource()); + if(i_source->isInLine(target, i_radius)) + i_data->push_back(target); break; } } diff --git a/src/game/SpellAuraDefines.h b/src/game/SpellAuraDefines.h index d8a9d73cf46..5953ae9fb13 100644 --- a/src/game/SpellAuraDefines.h +++ b/src/game/SpellAuraDefines.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,14 +20,20 @@ #ifndef TRINITY_SPELLAURADEFINES_H #define TRINITY_SPELLAURADEFINES_H -#define MAX_AURAS 56 -#define MAX_POSITIVE_AURAS 40 +#define MAX_AURAS 64 // client support up to 255, but it will cause problems with group auras updating +#define FRIENDLY_AA_REMOVE_TIME 2*IN_MILISECONDS enum AURA_FLAGS { - AFLAG_NEGATIVE = 0x09, - AFLAG_POSITIVE = 0x1F, - AFLAG_MASK = 0xFF + AFLAG_NONE = 0x00, + AFLAG_EFF_INDEX_0 = 0x01, + AFLAG_EFF_INDEX_1 = 0x02, + AFLAG_EFF_INDEX_2 = 0x04, + AFLAG_CASTER = 0x08, + AFLAG_POSITIVE = 0x10, + AFLAG_DURATION = 0x20, + AFLAG_UNK2 = 0x40, + AFLAG_NEGATIVE = 0x80 }; //m_schoolAbsorb @@ -60,7 +66,7 @@ enum AuraType SPELL_AURA_MOD_INVISIBILITY = 18, SPELL_AURA_MOD_INVISIBILITY_DETECTION = 19, SPELL_AURA_OBS_MOD_HEALTH = 20, //20,21 unofficial - SPELL_AURA_OBS_MOD_MANA = 21, + SPELL_AURA_OBS_MOD_ENERGY = 21, SPELL_AURA_MOD_RESISTANCE = 22, SPELL_AURA_PERIODIC_TRIGGER_SPELL = 23, SPELL_AURA_PERIODIC_ENERGIZE = 24, @@ -85,11 +91,11 @@ enum AuraType SPELL_AURA_PROC_TRIGGER_DAMAGE = 43, SPELL_AURA_TRACK_CREATURES = 44, SPELL_AURA_TRACK_RESOURCES = 45, - SPELL_AURA_MOD_PARRY_SKILL = 46, + SPELL_AURA_46 = 46, // Ignore all Gear test spells SPELL_AURA_MOD_PARRY_PERCENT = 47, - SPELL_AURA_MOD_DODGE_SKILL = 48, + SPELL_AURA_48 = 48, // One periodic spell SPELL_AURA_MOD_DODGE_PERCENT = 49, - SPELL_AURA_MOD_BLOCK_SKILL = 50, + SPELL_AURA_MOD_CRITICAL_HEALING_AMOUNT = 50, SPELL_AURA_MOD_BLOCK_PERCENT = 51, SPELL_AURA_MOD_CRIT_PERCENT = 52, SPELL_AURA_PERIODIC_LEECH = 53, @@ -102,9 +108,9 @@ enum AuraType SPELL_AURA_MOD_PACIFY_SILENCE = 60, SPELL_AURA_MOD_SCALE = 61, SPELL_AURA_PERIODIC_HEALTH_FUNNEL = 62, - SPELL_AURA_PERIODIC_MANA_FUNNEL = 63, + SPELL_AURA_63 = 63, // old SPELL_AURA_PERIODIC_MANA_FUNNEL SPELL_AURA_PERIODIC_MANA_LEECH = 64, - SPELL_AURA_MOD_CASTING_SPEED = 65, + SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK = 65, SPELL_AURA_FEIGN_DEATH = 66, SPELL_AURA_MOD_DISARM = 67, SPELL_AURA_MOD_STALKED = 68, @@ -129,7 +135,7 @@ enum AuraType SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN = 87, SPELL_AURA_MOD_HEALTH_REGEN_PERCENT = 88, SPELL_AURA_PERIODIC_DAMAGE_PERCENT = 89, - SPELL_AURA_MOD_RESIST_CHANCE = 90, + SPELL_AURA_90 = 90, // old SPELL_AURA_MOD_RESIST_CHANCE SPELL_AURA_MOD_DETECT_RANGE = 91, SPELL_AURA_PREVENTS_FLEEING = 92, SPELL_AURA_MOD_UNATTACKABLE = 93, @@ -158,7 +164,7 @@ enum AuraType SPELL_AURA_MOD_REGEN_DURING_COMBAT = 116, SPELL_AURA_MOD_MECHANIC_RESISTANCE = 117, SPELL_AURA_MOD_HEALING_PCT = 118, - SPELL_AURA_SHARE_PET_TRACKING = 119, + SPELL_AURA_119 = 119, // old SPELL_AURA_SHARE_PET_TRACKING SPELL_AURA_UNTRACKABLE = 120, SPELL_AURA_EMPATHY = 121, SPELL_AURA_MOD_OFFHAND_DAMAGE_PCT = 122, @@ -184,11 +190,11 @@ enum AuraType SPELL_AURA_MOD_BASE_RESISTANCE_PCT = 142, SPELL_AURA_MOD_RESISTANCE_EXCLUSIVE = 143, SPELL_AURA_SAFE_FALL = 144, - SPELL_AURA_CHARISMA = 145, - SPELL_AURA_PERSUADED = 146, - SPELL_AURA_ADD_CREATURE_IMMUNITY = 147, + SPELL_AURA_MOD_PET_TALENT_POINTS = 145, + SPELL_AURA_ALLOW_TAME_PET_TYPE = 146, + SPELL_AURA_MECHANIC_IMMUNITY_MASK = 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 @@ -212,15 +218,15 @@ enum AuraType SPELL_AURA_DETECT_AMORE = 170, SPELL_AURA_MOD_SPEED_NOT_STACK = 171, SPELL_AURA_MOD_MOUNTED_SPEED_NOT_STACK = 172, - SPELL_AURA_ALLOW_CHAMPION_SPELLS = 173, - SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT = 174, // by default intellect, dependent from SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT + SPELL_AURA_173 = 173, // old SPELL_AURA_ALLOW_CHAMPION_SPELLS + SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT = 174, // by defeult intelect, dependent from SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT = 175, SPELL_AURA_SPIRIT_OF_REDEMPTION = 176, SPELL_AURA_AOE_CHARM = 177, SPELL_AURA_MOD_DEBUFF_RESISTANCE = 178, SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE = 179, SPELL_AURA_MOD_FLAT_SPELL_DAMAGE_VERSUS = 180, - SPELL_AURA_MOD_FLAT_SPELL_CRIT_DAMAGE_VERSUS = 181, // unused - possible flat spell crit damage versus + SPELL_AURA_181 = 181, // old SPELL_AURA_MOD_FLAT_SPELL_CRIT_DAMAGE_VERSUS - possible flat spell crit damage versus SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT = 182, SPELL_AURA_MOD_CRITICAL_THREAT = 183, SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE = 184, @@ -233,11 +239,11 @@ enum AuraType SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED = 191, SPELL_AURA_HASTE_MELEE = 192, SPELL_AURA_MELEE_SLOW = 193, - SPELL_AURA_MOD_DEPRICATED_1 = 194, // not used now, old SPELL_AURA_MOD_SPELL_DAMAGE_OF_INTELLECT - SPELL_AURA_MOD_DEPRICATED_2 = 195, // not used now, old SPELL_AURA_MOD_SPELL_HEALING_OF_INTELLECT + SPELL_AURA_MOD_TARGET_ABSORB_SCHOOL = 194, + SPELL_AURA_MOD_TARGET_ABILITY_ABSORB_SCHOOL = 195, SPELL_AURA_MOD_COOLDOWN = 196, // only 24818 Noxious Breath SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE = 197, - SPELL_AURA_MOD_ALL_WEAPON_SKILLS = 198, + SPELL_AURA_198 = 198, // old SPELL_AURA_MOD_ALL_WEAPON_SKILLS SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT = 199, SPELL_AURA_MOD_XP_PCT = 200, SPELL_AURA_FLY = 201, @@ -262,20 +268,20 @@ enum AuraType SPELL_AURA_MOD_RATING_FROM_STAT = 220, SPELL_AURA_221 = 221, SPELL_AURA_222 = 222, - SPELL_AURA_223 = 223, + SPELL_AURA_RAID_PROC_FROM_CHARGE = 223, SPELL_AURA_224 = 224, - SPELL_AURA_PRAYER_OF_MENDING = 225, + SPELL_AURA_RAID_PROC_FROM_CHARGE_WITH_VALUE = 225, SPELL_AURA_PERIODIC_DUMMY = 226, - SPELL_AURA_227 = 227, + SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE = 227, SPELL_AURA_DETECT_STEALTH = 228, SPELL_AURA_MOD_AOE_DAMAGE_AVOIDANCE = 229, SPELL_AURA_230 = 230, - SPELL_AURA_231 = 231, + SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE = 231, SPELL_AURA_MECHANIC_DURATION_MOD = 232, SPELL_AURA_233 = 233, SPELL_AURA_MECHANIC_DURATION_MOD_NOT_STACK = 234, SPELL_AURA_MOD_DISPEL_RESIST = 235, - SPELL_AURA_236 = 236, + SPELL_AURA_CONTROL_VEHICLE = 236, SPELL_AURA_MOD_SPELL_DAMAGE_OF_ATTACK_POWER = 237, SPELL_AURA_MOD_SPELL_HEALING_OF_ATTACK_POWER = 238, SPELL_AURA_MOD_SCALE_2 = 239, @@ -284,29 +290,57 @@ enum AuraType SPELL_AURA_MOD_SPELL_DAMAGE_FROM_HEALING = 242, SPELL_AURA_243 = 243, SPELL_AURA_COMPREHEND_LANGUAGE = 244, - SPELL_AURA_MOD_DURATION_OF_MAGIC_EFFECTS = 245, - SPELL_AURA_246 = 246, + SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL = 245, + SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL_NOT_STACK = 246, SPELL_AURA_247 = 247, SPELL_AURA_MOD_COMBAT_RESULT_CHANCE = 248, - SPELL_AURA_249 = 249, + SPELL_AURA_CONVERT_RUNE = 249, SPELL_AURA_MOD_INCREASE_HEALTH_2 = 250, SPELL_AURA_MOD_ENEMY_DODGE = 251, SPELL_AURA_252 = 252, - SPELL_AURA_253 = 253, - SPELL_AURA_254 = 254, - SPELL_AURA_255 = 255, - SPELL_AURA_256 = 256, - SPELL_AURA_257 = 257, + SPELL_AURA_MOD_BLOCK_CRIT_CHANCE = 253, + SPELL_AURA_MOD_DISARM_OFFHAND = 254, + SPELL_AURA_MOD_MECHANIC_DAMAGE_TAKEN_PERCENT = 255, + SPELL_AURA_NO_REAGENT_USE = 256, + SPELL_AURA_MOD_TARGET_RESIST_BY_SPELL_CLASS = 257, SPELL_AURA_258 = 258, - SPELL_AURA_259 = 259, - SPELL_AURA_260 = 260, - SPELL_AURA_261 = 261, - TOTAL_AURAS=262 + SPELL_AURA_MOD_HOT_PCT = 259, + SPELL_AURA_SCREEN_EFFECT = 260, + SPELL_AURA_PHASE = 261, + SPELL_AURA_ABILITY_IGNORE_AURASTATE = 262, + SPELL_AURA_ALLOW_ONLY_ABILITY = 263, + SPELL_AURA_264 = 264, + SPELL_AURA_265 = 265, + SPELL_AURA_266 = 266, + SPELL_AURA_MOD_IMMUNE_AURA_APPLY_SCHOOL = 267, + SPELL_AURA_MOD_ATTACK_POWER_OF_STAT_PERCENT = 268, + SPELL_AURA_MOD_IGNORE_TARGET_RESIST = 269, + SPELL_AURA_MOD_ABILITY_IGNORE_TARGET_RESIST = 270, // Possibly need swap vs 195 aura used only in 1 spell Chaos Bolt Passive + SPELL_AURA_MOD_DAMAGE_FROM_CASTER = 271, + SPELL_AURA_272 = 272, + SPELL_AURA_273 = 273, + SPELL_AURA_ABILITY_CONSUME_NO_AMMO = 274, + SPELL_AURA_MOD_IGNORE_SHAPESHIFT = 275, + SPELL_AURA_276 = 276, // Only "Test Mod Damage % Mechanic" spell, possible mod damage done + SPELL_AURA_MOD_MAX_AFFECTED_TARGETS = 277, + SPELL_AURA_MOD_DISARM_RANGED = 278, + SPELL_AURA_279 = 279, + SPELL_AURA_MOD_WEAPONTYPE_IGNORE_TARGET_RESISTANCE = 280, + SPELL_AURA_MOD_HONOR_GAIN_PCT = 281, + SPELL_AURA_MOD_BASE_HEALTH_PCT = 282, + SPELL_AURA_MOD_HEALING_RECEIVED = 283, // Possibly only for some spell family class spells + SPELL_AURA_284, + SPELL_AURA_285, + SPELL_AURA_286, + SPELL_AURA_DEFLECT_SPELLS, + SPELL_AURA_288, + TOTAL_AURAS = 289 }; enum AreaAuraType { AREA_AURA_PARTY, + AREA_AURA_RAID, AREA_AURA_FRIEND, AREA_AURA_ENEMY, AREA_AURA_PET, diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index 1a837de8775..b70da73b6b1 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,11 +31,9 @@ #include "Player.h" #include "Unit.h" #include "Spell.h" -#include "SpellAuras.h" #include "DynamicObject.h" #include "Group.h" #include "UpdateData.h" -#include "MapManager.h" #include "ObjectAccessor.h" #include "Policies/SingletonImp.h" #include "Totem.h" @@ -45,310 +43,323 @@ #include "OutdoorPvP.h" #include "OutdoorPvPMgr.h" #include "CreatureAI.h" +#include "ScriptCalls.h" #include "Util.h" #include "GridNotifiers.h" #include "GridNotifiersImpl.h" #include "CellImpl.h" -#define NULL_AURA_SLOT 0xFF - pAuraHandler AuraHandler[TOTAL_AURAS]= { - &Aura::HandleNULL, // 0 SPELL_AURA_NONE - &Aura::HandleBindSight, // 1 SPELL_AURA_BIND_SIGHT - &Aura::HandleModPossess, // 2 SPELL_AURA_MOD_POSSESS - &Aura::HandlePeriodicDamage, // 3 SPELL_AURA_PERIODIC_DAMAGE - &Aura::HandleAuraDummy, // 4 SPELL_AURA_DUMMY - &Aura::HandleModConfuse, // 5 SPELL_AURA_MOD_CONFUSE - &Aura::HandleModCharm, // 6 SPELL_AURA_MOD_CHARM - &Aura::HandleModFear, // 7 SPELL_AURA_MOD_FEAR - &Aura::HandlePeriodicHeal, // 8 SPELL_AURA_PERIODIC_HEAL - &Aura::HandleModAttackSpeed, // 9 SPELL_AURA_MOD_ATTACKSPEED - &Aura::HandleModThreat, // 10 SPELL_AURA_MOD_THREAT - &Aura::HandleModTaunt, // 11 SPELL_AURA_MOD_TAUNT - &Aura::HandleAuraModStun, // 12 SPELL_AURA_MOD_STUN - &Aura::HandleModDamageDone, // 13 SPELL_AURA_MOD_DAMAGE_DONE - &Aura::HandleNoImmediateEffect, // 14 SPELL_AURA_MOD_DAMAGE_TAKEN implemented in Unit::MeleeDamageBonus and Unit::SpellDamageBonus - &Aura::HandleNoImmediateEffect, // 15 SPELL_AURA_DAMAGE_SHIELD implemented in Unit::DoAttackDamage - &Aura::HandleModStealth, // 16 SPELL_AURA_MOD_STEALTH - &Aura::HandleNoImmediateEffect, // 17 SPELL_AURA_MOD_STEALTH_DETECT - &Aura::HandleInvisibility, // 18 SPELL_AURA_MOD_INVISIBILITY - &Aura::HandleInvisibilityDetect, // 19 SPELL_AURA_MOD_INVISIBILITY_DETECTION - &Aura::HandleAuraModTotalHealthPercentRegen, // 20 SPELL_AURA_OBS_MOD_HEALTH - &Aura::HandleAuraModTotalManaPercentRegen, // 21 SPELL_AURA_OBS_MOD_MANA - &Aura::HandleAuraModResistance, // 22 SPELL_AURA_MOD_RESISTANCE - &Aura::HandlePeriodicTriggerSpell, // 23 SPELL_AURA_PERIODIC_TRIGGER_SPELL - &Aura::HandlePeriodicEnergize, // 24 SPELL_AURA_PERIODIC_ENERGIZE - &Aura::HandleAuraModPacify, // 25 SPELL_AURA_MOD_PACIFY - &Aura::HandleAuraModRoot, // 26 SPELL_AURA_MOD_ROOT - &Aura::HandleAuraModSilence, // 27 SPELL_AURA_MOD_SILENCE - &Aura::HandleNoImmediateEffect, // 28 SPELL_AURA_REFLECT_SPELLS implement in Unit::SpellHitResult - &Aura::HandleAuraModStat, // 29 SPELL_AURA_MOD_STAT - &Aura::HandleAuraModSkill, // 30 SPELL_AURA_MOD_SKILL - &Aura::HandleAuraModIncreaseSpeed, // 31 SPELL_AURA_MOD_INCREASE_SPEED - &Aura::HandleAuraModIncreaseMountedSpeed, // 32 SPELL_AURA_MOD_INCREASE_MOUNTED_SPEED - &Aura::HandleAuraModDecreaseSpeed, // 33 SPELL_AURA_MOD_DECREASE_SPEED - &Aura::HandleAuraModIncreaseHealth, // 34 SPELL_AURA_MOD_INCREASE_HEALTH - &Aura::HandleAuraModIncreaseEnergy, // 35 SPELL_AURA_MOD_INCREASE_ENERGY - &Aura::HandleAuraModShapeshift, // 36 SPELL_AURA_MOD_SHAPESHIFT - &Aura::HandleAuraModEffectImmunity, // 37 SPELL_AURA_EFFECT_IMMUNITY - &Aura::HandleAuraModStateImmunity, // 38 SPELL_AURA_STATE_IMMUNITY - &Aura::HandleAuraModSchoolImmunity, // 39 SPELL_AURA_SCHOOL_IMMUNITY - &Aura::HandleAuraModDmgImmunity, // 40 SPELL_AURA_DAMAGE_IMMUNITY - &Aura::HandleAuraModDispelImmunity, // 41 SPELL_AURA_DISPEL_IMMUNITY - &Aura::HandleAuraProcTriggerSpell, // 42 SPELL_AURA_PROC_TRIGGER_SPELL implemented in Unit::ProcDamageAndSpellFor and Unit::HandleProcTriggerSpell - &Aura::HandleNoImmediateEffect, // 43 SPELL_AURA_PROC_TRIGGER_DAMAGE implemented in Unit::ProcDamageAndSpellFor - &Aura::HandleAuraTrackCreatures, // 44 SPELL_AURA_TRACK_CREATURES - &Aura::HandleAuraTrackResources, // 45 SPELL_AURA_TRACK_RESOURCES - &Aura::HandleUnused, // 46 SPELL_AURA_MOD_PARRY_SKILL obsolete? - &Aura::HandleAuraModParryPercent, // 47 SPELL_AURA_MOD_PARRY_PERCENT - &Aura::HandleUnused, // 48 SPELL_AURA_MOD_DODGE_SKILL obsolete? - &Aura::HandleAuraModDodgePercent, // 49 SPELL_AURA_MOD_DODGE_PERCENT - &Aura::HandleUnused, // 50 SPELL_AURA_MOD_BLOCK_SKILL obsolete? - &Aura::HandleAuraModBlockPercent, // 51 SPELL_AURA_MOD_BLOCK_PERCENT - &Aura::HandleAuraModCritPercent, // 52 SPELL_AURA_MOD_CRIT_PERCENT - &Aura::HandlePeriodicLeech, // 53 SPELL_AURA_PERIODIC_LEECH - &Aura::HandleModHitChance, // 54 SPELL_AURA_MOD_HIT_CHANCE - &Aura::HandleModSpellHitChance, // 55 SPELL_AURA_MOD_SPELL_HIT_CHANCE - &Aura::HandleAuraTransform, // 56 SPELL_AURA_TRANSFORM - &Aura::HandleModSpellCritChance, // 57 SPELL_AURA_MOD_SPELL_CRIT_CHANCE - &Aura::HandleAuraModIncreaseSwimSpeed, // 58 SPELL_AURA_MOD_INCREASE_SWIM_SPEED - &Aura::HandleNoImmediateEffect, // 59 SPELL_AURA_MOD_DAMAGE_DONE_CREATURE implemented in Unit::MeleeDamageBonus and Unit::SpellDamageBonus - &Aura::HandleAuraModPacifyAndSilence, // 60 SPELL_AURA_MOD_PACIFY_SILENCE - &Aura::HandleAuraModScale, // 61 SPELL_AURA_MOD_SCALE - &Aura::HandleNULL, // 62 SPELL_AURA_PERIODIC_HEALTH_FUNNEL - &Aura::HandleUnused, // 63 SPELL_AURA_PERIODIC_MANA_FUNNEL obsolete? - &Aura::HandlePeriodicManaLeech, // 64 SPELL_AURA_PERIODIC_MANA_LEECH - &Aura::HandleModCastingSpeed, // 65 SPELL_AURA_MOD_CASTING_SPEED - &Aura::HandleFeignDeath, // 66 SPELL_AURA_FEIGN_DEATH - &Aura::HandleAuraModDisarm, // 67 SPELL_AURA_MOD_DISARM - &Aura::HandleAuraModStalked, // 68 SPELL_AURA_MOD_STALKED - &Aura::HandleSchoolAbsorb, // 69 SPELL_AURA_SCHOOL_ABSORB implemented in Unit::CalcAbsorbResist - &Aura::HandleUnused, // 70 SPELL_AURA_EXTRA_ATTACKS Useless, used by only one spell that has only visual effect - &Aura::HandleModSpellCritChanceShool, // 71 SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL - &Aura::HandleModPowerCostPCT, // 72 SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT - &Aura::HandleModPowerCost, // 73 SPELL_AURA_MOD_POWER_COST_SCHOOL - &Aura::HandleNoImmediateEffect, // 74 SPELL_AURA_REFLECT_SPELLS_SCHOOL implemented in Unit::SpellHitResult - &Aura::HandleNoImmediateEffect, // 75 SPELL_AURA_MOD_LANGUAGE - &Aura::HandleFarSight, // 76 SPELL_AURA_FAR_SIGHT - &Aura::HandleModMechanicImmunity, // 77 SPELL_AURA_MECHANIC_IMMUNITY - &Aura::HandleAuraMounted, // 78 SPELL_AURA_MOUNTED - &Aura::HandleModDamagePercentDone, // 79 SPELL_AURA_MOD_DAMAGE_PERCENT_DONE - &Aura::HandleModPercentStat, // 80 SPELL_AURA_MOD_PERCENT_STAT - &Aura::HandleNoImmediateEffect, // 81 SPELL_AURA_SPLIT_DAMAGE_PCT - &Aura::HandleWaterBreathing, // 82 SPELL_AURA_WATER_BREATHING - &Aura::HandleModBaseResistance, // 83 SPELL_AURA_MOD_BASE_RESISTANCE - &Aura::HandleModRegen, // 84 SPELL_AURA_MOD_REGEN - &Aura::HandleModPowerRegen, // 85 SPELL_AURA_MOD_POWER_REGEN - &Aura::HandleChannelDeathItem, // 86 SPELL_AURA_CHANNEL_DEATH_ITEM - &Aura::HandleNoImmediateEffect, // 87 SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN implemented in Unit::MeleeDamageBonus and Unit::SpellDamageBonus - &Aura::HandleNoImmediateEffect, // 88 SPELL_AURA_MOD_HEALTH_REGEN_PERCENT - &Aura::HandlePeriodicDamagePCT, // 89 SPELL_AURA_PERIODIC_DAMAGE_PERCENT - &Aura::HandleUnused, // 90 SPELL_AURA_MOD_RESIST_CHANCE Useless - &Aura::HandleNoImmediateEffect, // 91 SPELL_AURA_MOD_DETECT_RANGE implemented in Creature::GetAttackDistance - &Aura::HandlePreventFleeing, // 92 SPELL_AURA_PREVENTS_FLEEING - &Aura::HandleModUnattackable, // 93 SPELL_AURA_MOD_UNATTACKABLE - &Aura::HandleNoImmediateEffect, // 94 SPELL_AURA_INTERRUPT_REGEN implemented in Player::RegenerateAll - &Aura::HandleAuraGhost, // 95 SPELL_AURA_GHOST - &Aura::HandleNoImmediateEffect, // 96 SPELL_AURA_SPELL_MAGNET implemented in Spell::SelectMagnetTarget - &Aura::HandleManaShield, // 97 SPELL_AURA_MANA_SHIELD implemented in Unit::CalcAbsorbResist - &Aura::HandleAuraModSkill, // 98 SPELL_AURA_MOD_SKILL_TALENT - &Aura::HandleAuraModAttackPower, // 99 SPELL_AURA_MOD_ATTACK_POWER - &Aura::HandleUnused, //100 SPELL_AURA_AURAS_VISIBLE obsolete? all player can see all auras now - &Aura::HandleModResistancePercent, //101 SPELL_AURA_MOD_RESISTANCE_PCT - &Aura::HandleNoImmediateEffect, //102 SPELL_AURA_MOD_MELEE_ATTACK_POWER_VERSUS implemented in Unit::MeleeDamageBonus - &Aura::HandleAuraModTotalThreat, //103 SPELL_AURA_MOD_TOTAL_THREAT - &Aura::HandleAuraWaterWalk, //104 SPELL_AURA_WATER_WALK - &Aura::HandleAuraFeatherFall, //105 SPELL_AURA_FEATHER_FALL - &Aura::HandleAuraHover, //106 SPELL_AURA_HOVER - &Aura::HandleAddModifier, //107 SPELL_AURA_ADD_FLAT_MODIFIER - &Aura::HandleAddModifier, //108 SPELL_AURA_ADD_PCT_MODIFIER - &Aura::HandleNoImmediateEffect, //109 SPELL_AURA_ADD_TARGET_TRIGGER - &Aura::HandleModPowerRegenPCT, //110 SPELL_AURA_MOD_POWER_REGEN_PERCENT - &Aura::HandleNULL, //111 SPELL_AURA_ADD_CASTER_HIT_TRIGGER - &Aura::HandleNoImmediateEffect, //112 SPELL_AURA_OVERRIDE_CLASS_SCRIPTS - &Aura::HandleNoImmediateEffect, //113 SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN implemented in Unit::MeleeDamageBonus - &Aura::HandleNoImmediateEffect, //114 SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN_PCT implemented in Unit::MeleeDamageBonus - &Aura::HandleNoImmediateEffect, //115 SPELL_AURA_MOD_HEALING implemented in Unit::SpellBaseHealingBonusForVictim - &Aura::HandleNoImmediateEffect, //116 SPELL_AURA_MOD_REGEN_DURING_COMBAT - &Aura::HandleNoImmediateEffect, //117 SPELL_AURA_MOD_MECHANIC_RESISTANCE implemented in Unit::MagicSpellHitResult - &Aura::HandleNoImmediateEffect, //118 SPELL_AURA_MOD_HEALING_PCT implemented in Unit::SpellHealingBonus - &Aura::HandleUnused, //119 SPELL_AURA_SHARE_PET_TRACKING useless - &Aura::HandleAuraUntrackable, //120 SPELL_AURA_UNTRACKABLE - &Aura::HandleAuraEmpathy, //121 SPELL_AURA_EMPATHY - &Aura::HandleModOffhandDamagePercent, //122 SPELL_AURA_MOD_OFFHAND_DAMAGE_PCT - &Aura::HandleModTargetResistance, //123 SPELL_AURA_MOD_TARGET_RESISTANCE - &Aura::HandleAuraModRangedAttackPower, //124 SPELL_AURA_MOD_RANGED_ATTACK_POWER - &Aura::HandleNoImmediateEffect, //125 SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN implemented in Unit::MeleeDamageBonus - &Aura::HandleNoImmediateEffect, //126 SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN_PCT implemented in Unit::MeleeDamageBonus - &Aura::HandleNoImmediateEffect, //127 SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS implemented in Unit::MeleeDamageBonus - &Aura::HandleModPossessPet, //128 SPELL_AURA_MOD_POSSESS_PET - &Aura::HandleAuraModIncreaseSpeed, //129 SPELL_AURA_MOD_SPEED_ALWAYS - &Aura::HandleAuraModIncreaseMountedSpeed, //130 SPELL_AURA_MOD_MOUNTED_SPEED_ALWAYS - &Aura::HandleNoImmediateEffect, //131 SPELL_AURA_MOD_RANGED_ATTACK_POWER_VERSUS implemented in Unit::MeleeDamageBonus - &Aura::HandleAuraModIncreaseEnergyPercent, //132 SPELL_AURA_MOD_INCREASE_ENERGY_PERCENT - &Aura::HandleAuraModIncreaseHealthPercent, //133 SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT - &Aura::HandleAuraModRegenInterrupt, //134 SPELL_AURA_MOD_MANA_REGEN_INTERRUPT - &Aura::HandleModHealingDone, //135 SPELL_AURA_MOD_HEALING_DONE - &Aura::HandleNoImmediateEffect, //136 SPELL_AURA_MOD_HEALING_DONE_PERCENT implemented in Unit::SpellHealingBonus - &Aura::HandleModTotalPercentStat, //137 SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE - &Aura::HandleHaste, //138 SPELL_AURA_MOD_HASTE - &Aura::HandleForceReaction, //139 SPELL_AURA_FORCE_REACTION - &Aura::HandleAuraModRangedHaste, //140 SPELL_AURA_MOD_RANGED_HASTE - &Aura::HandleRangedAmmoHaste, //141 SPELL_AURA_MOD_RANGED_AMMO_HASTE - &Aura::HandleAuraModBaseResistancePCT, //142 SPELL_AURA_MOD_BASE_RESISTANCE_PCT - &Aura::HandleAuraModResistanceExclusive, //143 SPELL_AURA_MOD_RESISTANCE_EXCLUSIVE - &Aura::HandleNoImmediateEffect, //144 SPELL_AURA_SAFE_FALL implemented in WorldSession::HandleMovementOpcodes - &Aura::HandleUnused, //145 SPELL_AURA_CHARISMA obsolete? - &Aura::HandleUnused, //146 SPELL_AURA_PERSUADED obsolete? - &Aura::HandleNULL, //147 SPELL_AURA_ADD_CREATURE_IMMUNITY - &Aura::HandleAuraRetainComboPoints, //148 SPELL_AURA_RETAIN_COMBO_POINTS - &Aura::HandleNoImmediateEffect, //149 SPELL_AURA_RESIST_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 - &Aura::HandleNoImmediateEffect, //153 SPELL_AURA_SPLIT_DAMAGE_FLAT - &Aura::HandleNoImmediateEffect, //154 SPELL_AURA_MOD_STEALTH_LEVEL - &Aura::HandleNoImmediateEffect, //155 SPELL_AURA_MOD_WATER_BREATHING - &Aura::HandleNoImmediateEffect, //156 SPELL_AURA_MOD_REPUTATION_GAIN - &Aura::HandleNULL, //157 SPELL_AURA_PET_DAMAGE_MULTI - &Aura::HandleShieldBlockValue, //158 SPELL_AURA_MOD_SHIELD_BLOCKVALUE - &Aura::HandleNoImmediateEffect, //159 SPELL_AURA_NO_PVP_CREDIT only for Honorless Target spell - &Aura::HandleNoImmediateEffect, //160 SPELL_AURA_MOD_AOE_AVOIDANCE implemented in Unit::MagicSpellHitResult - &Aura::HandleNoImmediateEffect, //161 SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT - &Aura::HandleAuraPowerBurn, //162 SPELL_AURA_POWER_BURN_MANA - &Aura::HandleNoImmediateEffect, //163 SPELL_AURA_MOD_CRIT_DAMAGE_BONUS_MELEE - &Aura::HandleUnused, //164 useless, only one test spell - &Aura::HandleNoImmediateEffect, //165 SPELL_AURA_MELEE_ATTACK_POWER_ATTACKER_BONUS implemented in Unit::MeleeDamageBonus - &Aura::HandleAuraModAttackPowerPercent, //166 SPELL_AURA_MOD_ATTACK_POWER_PCT - &Aura::HandleAuraModRangedAttackPowerPercent, //167 SPELL_AURA_MOD_RANGED_ATTACK_POWER_PCT - &Aura::HandleNoImmediateEffect, //168 SPELL_AURA_MOD_DAMAGE_DONE_VERSUS implemented in Unit::SpellDamageBonus, Unit::MeleeDamageBonus - &Aura::HandleNoImmediateEffect, //169 SPELL_AURA_MOD_CRIT_PERCENT_VERSUS implemented in Unit::DealDamageBySchool, Unit::DoAttackDamage, Unit::SpellCriticalBonus - &Aura::HandleNULL, //170 SPELL_AURA_DETECT_AMORE only for Detect Amore spell - &Aura::HandleAuraModIncreaseSpeed, //171 SPELL_AURA_MOD_SPEED_NOT_STACK - &Aura::HandleAuraModIncreaseMountedSpeed, //172 SPELL_AURA_MOD_MOUNTED_SPEED_NOT_STACK - &Aura::HandleUnused, //173 SPELL_AURA_ALLOW_CHAMPION_SPELLS only for Proclaim Champion spell - &Aura::HandleModSpellDamagePercentFromStat, //174 SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT implemented in Unit::SpellBaseDamageBonus (by default intellect, dependent from SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT) - &Aura::HandleModSpellHealingPercentFromStat, //175 SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT implemented in Unit::SpellBaseHealingBonus - &Aura::HandleSpiritOfRedemption, //176 SPELL_AURA_SPIRIT_OF_REDEMPTION only for Spirit of Redemption spell, die at aura end - &Aura::HandleNULL, //177 SPELL_AURA_AOE_CHARM - &Aura::HandleNoImmediateEffect, //178 SPELL_AURA_MOD_DEBUFF_RESISTANCE implemented in Unit::MagicSpellHitResult - &Aura::HandleNoImmediateEffect, //179 SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE implemented in Unit::SpellCriticalBonus - &Aura::HandleNoImmediateEffect, //180 SPELL_AURA_MOD_FLAT_SPELL_DAMAGE_VERSUS implemented in Unit::SpellDamageBonus - &Aura::HandleUnused, //181 SPELL_AURA_MOD_FLAT_SPELL_CRIT_DAMAGE_VERSUS unused - &Aura::HandleAuraModResistenceOfStatPercent, //182 SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT - &Aura::HandleNULL, //183 SPELL_AURA_MOD_CRITICAL_THREAT - &Aura::HandleNoImmediateEffect, //184 SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE implemented in Unit::RollMeleeOutcomeAgainst - &Aura::HandleNoImmediateEffect, //185 SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE implemented in Unit::RollMeleeOutcomeAgainst - &Aura::HandleNoImmediateEffect, //186 SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE implemented in Unit::MagicSpellHitResult - &Aura::HandleNoImmediateEffect, //187 SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_CHANCE implemented in Unit::GetUnitCriticalChance - &Aura::HandleNoImmediateEffect, //188 SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_CHANCE implemented in Unit::GetUnitCriticalChance - &Aura::HandleModRating, //189 SPELL_AURA_MOD_RATING - &Aura::HandleNULL, //190 SPELL_AURA_MOD_FACTION_REPUTATION_GAIN - &Aura::HandleAuraModUseNormalSpeed, //191 SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED - &Aura::HandleModMeleeRangedSpeedPct, //192 SPELL_AURA_HASTE_MELEE - &Aura::HandleModCombatSpeedPct, //193 SPELL_AURA_MELEE_SLOW (in fact combat (any type attack) speed pct) - &Aura::HandleUnused, //194 SPELL_AURA_MOD_DEPRICATED_1 not used now (old SPELL_AURA_MOD_SPELL_DAMAGE_OF_INTELLECT) - &Aura::HandleUnused, //195 SPELL_AURA_MOD_DEPRICATED_2 not used now (old SPELL_AURA_MOD_SPELL_HEALING_OF_INTELLECT) - &Aura::HandleNULL, //196 SPELL_AURA_MOD_COOLDOWN - &Aura::HandleNoImmediateEffect, //197 SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE implemented in Unit::SpellCriticalBonus Unit::GetUnitCriticalChance - &Aura::HandleUnused, //198 SPELL_AURA_MOD_ALL_WEAPON_SKILLS - &Aura::HandleNoImmediateEffect, //199 SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT implemented in Unit::MagicSpellHitResult - &Aura::HandleNoImmediateEffect, //200 SPELL_AURA_MOD_XP_PCT implemented in Player::GiveXP - &Aura::HandleAuraAllowFlight, //201 SPELL_AURA_FLY this aura enable flight mode... - &Aura::HandleNoImmediateEffect, //202 SPELL_AURA_CANNOT_BE_DODGED implemented in Unit::RollPhysicalOutcomeAgainst - &Aura::HandleNoImmediateEffect, //203 SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE implemented in Unit::CalculateMeleeDamage and Unit::CalculateSpellDamage - &Aura::HandleNoImmediateEffect, //204 SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE implemented in Unit::CalculateMeleeDamage and Unit::CalculateSpellDamage - &Aura::HandleNULL, //205 vulnerable to school dmg? - &Aura::HandleNULL, //206 SPELL_AURA_MOD_SPEED_MOUNTED - &Aura::HandleAuraModIncreaseFlightSpeed, //207 SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED - &Aura::HandleAuraModIncreaseFlightSpeed, //208 SPELL_AURA_MOD_SPEED_FLIGHT, used only in spell: Flight Form (Passive) - &Aura::HandleAuraModIncreaseFlightSpeed, //209 SPELL_AURA_MOD_FLIGHT_SPEED_ALWAYS - &Aura::HandleNULL, //210 Commentator's Command - &Aura::HandleAuraModIncreaseFlightSpeed, //211 SPELL_AURA_MOD_FLIGHT_SPEED_NOT_STACK - &Aura::HandleAuraModRangedAttackPowerOfStatPercent, //212 SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT - &Aura::HandleNoImmediateEffect, //213 SPELL_AURA_MOD_RAGE_FROM_DAMAGE_DEALT implemented in Player::RewardRage - &Aura::HandleNULL, //214 Tamed Pet Passive - &Aura::HandleArenaPreparation, //215 SPELL_AURA_ARENA_PREPARATION - &Aura::HandleModCastingSpeed, //216 SPELL_AURA_HASTE_SPELLS - &Aura::HandleUnused, //217 unused - &Aura::HandleAuraModRangedHaste, //218 SPELL_AURA_HASTE_RANGED - &Aura::HandleModManaRegen, //219 SPELL_AURA_MOD_MANA_REGEN_FROM_STAT - &Aura::HandleNULL, //220 SPELL_AURA_MOD_RATING_FROM_STAT - &Aura::HandleNULL, //221 ignored - &Aura::HandleUnused, //222 unused - &Aura::HandleNULL, //223 Cold Stare - &Aura::HandleUnused, //224 unused - &Aura::HandleNoImmediateEffect, //225 SPELL_AURA_PRAYER_OF_MENDING - &Aura::HandleAuraPeriodicDummy, //226 SPELL_AURA_PERIODIC_DUMMY - &Aura::HandleNULL, //227 periodic trigger spell - &Aura::HandleNoImmediateEffect, //228 stealth detection - &Aura::HandleNULL, //229 SPELL_AURA_MOD_AOE_DAMAGE_AVOIDANCE - &Aura::HandleAuraModIncreaseMaxHealth, //230 Commanding Shout - &Aura::HandleNULL, //231 - &Aura::HandleNoImmediateEffect, //232 SPELL_AURA_MECHANIC_DURATION_MOD implement in Unit::CalculateSpellDuration - &Aura::HandleNULL, //233 set model id to the one of the creature with id m_modifier.m_miscvalue - &Aura::HandleNoImmediateEffect, //234 SPELL_AURA_MECHANIC_DURATION_MOD_NOT_STACK implement in Unit::CalculateSpellDuration - &Aura::HandleAuraModDispelResist, //235 SPELL_AURA_MOD_DISPEL_RESIST implement in Unit::MagicSpellHitResult - &Aura::HandleUnused, //236 unused - &Aura::HandleModSpellDamagePercentFromAttackPower, //237 SPELL_AURA_MOD_SPELL_DAMAGE_OF_ATTACK_POWER implemented in Unit::SpellBaseDamageBonus - &Aura::HandleModSpellHealingPercentFromAttackPower, //238 SPELL_AURA_MOD_SPELL_HEALING_OF_ATTACK_POWER implemented in Unit::SpellBaseHealingBonus - &Aura::HandleAuraModScale, //239 SPELL_AURA_MOD_SCALE_2 only in Noggenfogger Elixir (16595) before 2.3.0 aura 61 - &Aura::HandleAuraModExpertise, //240 SPELL_AURA_MOD_EXPERTISE - &Aura::HandleForceMoveForward, //241 Forces the player to move forward - &Aura::HandleUnused, //242 SPELL_AURA_MOD_SPELL_DAMAGE_FROM_HEALING - &Aura::HandleUnused, //243 used by two test spells - &Aura::HandleComprehendLanguage, //244 Comprehend language - &Aura::HandleUnused, //245 SPELL_AURA_MOD_DURATION_OF_MAGIC_EFFECTS - &Aura::HandleUnused, //246 unused - &Aura::HandleUnused, //247 unused - &Aura::HandleNoImmediateEffect, //248 SPELL_AURA_MOD_COMBAT_RESULT_CHANCE implemented in Unit::RollMeleeOutcomeAgainst - &Aura::HandleNULL, //249 - &Aura::HandleAuraModIncreaseHealth, //250 SPELL_AURA_MOD_INCREASE_HEALTH_2 - &Aura::HandleNULL, //251 SPELL_AURA_MOD_ENEMY_DODGE - &Aura::HandleUnused, //252 unused - &Aura::HandleUnused, //253 unused - &Aura::HandleUnused, //254 unused - &Aura::HandleUnused, //255 unused - &Aura::HandleUnused, //256 unused - &Aura::HandleUnused, //257 unused - &Aura::HandleUnused, //258 unused - &Aura::HandleUnused, //259 unused - &Aura::HandleUnused, //260 unused - &Aura::HandleNULL //261 SPELL_AURA_261 some phased state (44856 spell) + &AuraEffect::HandleNULL, // 0 SPELL_AURA_NONE + &AuraEffect::HandleBindSight, // 1 SPELL_AURA_BIND_SIGHT + &AuraEffect::HandleModPossess, // 2 SPELL_AURA_MOD_POSSESS + &AuraEffect::HandlePeriodicDamage, // 3 SPELL_AURA_PERIODIC_DAMAGE + &AuraEffect::HandleAuraDummy, // 4 SPELL_AURA_DUMMY + &AuraEffect::HandleModConfuse, // 5 SPELL_AURA_MOD_CONFUSE + &AuraEffect::HandleModCharm, // 6 SPELL_AURA_MOD_CHARM + &AuraEffect::HandleModFear, // 7 SPELL_AURA_MOD_FEAR + &AuraEffect::HandlePeriodicHeal, // 8 SPELL_AURA_PERIODIC_HEAL + &AuraEffect::HandleModAttackSpeed, // 9 SPELL_AURA_MOD_ATTACKSPEED + &AuraEffect::HandleModThreat, // 10 SPELL_AURA_MOD_THREAT + &AuraEffect::HandleModTaunt, // 11 SPELL_AURA_MOD_TAUNT + &AuraEffect::HandleAuraModStun, // 12 SPELL_AURA_MOD_STUN + &AuraEffect::HandleModDamageDone, // 13 SPELL_AURA_MOD_DAMAGE_DONE + &AuraEffect::HandleNoImmediateEffect, // 14 SPELL_AURA_MOD_DAMAGE_TAKEN implemented in Unit::MeleeDamageBonus and Unit::SpellDamageBonus + &AuraEffect::HandleNoImmediateEffect, // 15 SPELL_AURA_DAMAGE_SHIELD implemented in Unit::DoAttackDamage + &AuraEffect::HandleModStealth, // 16 SPELL_AURA_MOD_STEALTH + &AuraEffect::HandleNoImmediateEffect, // 17 SPELL_AURA_MOD_STEALTH_DETECT + &AuraEffect::HandleInvisibility, // 18 SPELL_AURA_MOD_INVISIBILITY + &AuraEffect::HandleInvisibilityDetect, // 19 SPELL_AURA_MOD_INVISIBILITY_DETECTION + &AuraEffect::HandleAuraModTotalHealthPercentRegen, // 20 SPELL_AURA_OBS_MOD_HEALTH + &AuraEffect::HandleAuraModTotalEnergyPercentRegen, // 21 SPELL_AURA_OBS_MOD_ENERGY + &AuraEffect::HandleAuraModResistance, // 22 SPELL_AURA_MOD_RESISTANCE + &AuraEffect::HandlePeriodicTriggerSpell, // 23 SPELL_AURA_PERIODIC_TRIGGER_SPELL + &AuraEffect::HandlePeriodicEnergize, // 24 SPELL_AURA_PERIODIC_ENERGIZE + &AuraEffect::HandleAuraModPacify, // 25 SPELL_AURA_MOD_PACIFY + &AuraEffect::HandleAuraModRoot, // 26 SPELL_AURA_MOD_ROOT + &AuraEffect::HandleAuraModSilence, // 27 SPELL_AURA_MOD_SILENCE + &AuraEffect::HandleNoImmediateEffect, // 28 SPELL_AURA_REFLECT_SPELLS implement in Unit::SpellHitResult + &AuraEffect::HandleAuraModStat, // 29 SPELL_AURA_MOD_STAT + &AuraEffect::HandleAuraModSkill, // 30 SPELL_AURA_MOD_SKILL + &AuraEffect::HandleAuraModIncreaseSpeed, // 31 SPELL_AURA_MOD_INCREASE_SPEED + &AuraEffect::HandleAuraModIncreaseMountedSpeed, // 32 SPELL_AURA_MOD_INCREASE_MOUNTED_SPEED + &AuraEffect::HandleAuraModDecreaseSpeed, // 33 SPELL_AURA_MOD_DECREASE_SPEED + &AuraEffect::HandleAuraModIncreaseHealth, // 34 SPELL_AURA_MOD_INCREASE_HEALTH + &AuraEffect::HandleAuraModIncreaseEnergy, // 35 SPELL_AURA_MOD_INCREASE_ENERGY + &AuraEffect::HandleAuraModShapeshift, // 36 SPELL_AURA_MOD_SHAPESHIFT + &AuraEffect::HandleAuraModEffectImmunity, // 37 SPELL_AURA_EFFECT_IMMUNITY + &AuraEffect::HandleAuraModStateImmunity, // 38 SPELL_AURA_STATE_IMMUNITY + &AuraEffect::HandleAuraModSchoolImmunity, // 39 SPELL_AURA_SCHOOL_IMMUNITY + &AuraEffect::HandleAuraModDmgImmunity, // 40 SPELL_AURA_DAMAGE_IMMUNITY + &AuraEffect::HandleAuraModDispelImmunity, // 41 SPELL_AURA_DISPEL_IMMUNITY + &AuraEffect::HandleAuraProcTriggerSpell, // 42 SPELL_AURA_PROC_TRIGGER_SPELL implemented in Unit::ProcDamageAndSpellFor and Unit::HandleProcTriggerSpell + &AuraEffect::HandleNoImmediateEffect, // 43 SPELL_AURA_PROC_TRIGGER_DAMAGE implemented in Unit::ProcDamageAndSpellFor + &AuraEffect::HandleAuraTrackCreatures, // 44 SPELL_AURA_TRACK_CREATURES + &AuraEffect::HandleAuraTrackResources, // 45 SPELL_AURA_TRACK_RESOURCES + &AuraEffect::HandleUnused, // 46 SPELL_AURA_46 (used in test spells 54054 and 54058, and spell 48050) (3.0.8a) + &AuraEffect::HandleAuraModParryPercent, // 47 SPELL_AURA_MOD_PARRY_PERCENT + &AuraEffect::HandleNULL, // 48 SPELL_AURA_48 spell Napalm (area damage spell with additional delayed damage effect) + &AuraEffect::HandleAuraModDodgePercent, // 49 SPELL_AURA_MOD_DODGE_PERCENT + &AuraEffect::HandleNoImmediateEffect, // 50 SPELL_AURA_MOD_CRITICAL_HEALING_AMOUNT implemented in Unit::SpellCriticalHealingBonus + &AuraEffect::HandleAuraModBlockPercent, // 51 SPELL_AURA_MOD_BLOCK_PERCENT + &AuraEffect::HandleAuraModCritPercent, // 52 SPELL_AURA_MOD_CRIT_PERCENT + &AuraEffect::HandlePeriodicLeech, // 53 SPELL_AURA_PERIODIC_LEECH + &AuraEffect::HandleModHitChance, // 54 SPELL_AURA_MOD_HIT_CHANCE + &AuraEffect::HandleModSpellHitChance, // 55 SPELL_AURA_MOD_SPELL_HIT_CHANCE + &AuraEffect::HandleAuraTransform, // 56 SPELL_AURA_TRANSFORM + &AuraEffect::HandleModSpellCritChance, // 57 SPELL_AURA_MOD_SPELL_CRIT_CHANCE + &AuraEffect::HandleAuraModIncreaseSwimSpeed, // 58 SPELL_AURA_MOD_INCREASE_SWIM_SPEED + &AuraEffect::HandleNoImmediateEffect, // 59 SPELL_AURA_MOD_DAMAGE_DONE_CREATURE implemented in Unit::MeleeDamageBonus and Unit::SpellDamageBonus + &AuraEffect::HandleAuraModPacifyAndSilence, // 60 SPELL_AURA_MOD_PACIFY_SILENCE + &AuraEffect::HandleAuraModScale, // 61 SPELL_AURA_MOD_SCALE + &AuraEffect::HandlePeriodicHealthFunnel, // 62 SPELL_AURA_PERIODIC_HEALTH_FUNNEL + &AuraEffect::HandleUnused, // 63 unused (3.0.8a) old SPELL_AURA_PERIODIC_MANA_FUNNEL + &AuraEffect::HandlePeriodicManaLeech, // 64 SPELL_AURA_PERIODIC_MANA_LEECH + &AuraEffect::HandleModCastingSpeed, // 65 SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK + &AuraEffect::HandleFeignDeath, // 66 SPELL_AURA_FEIGN_DEATH + &AuraEffect::HandleAuraModDisarm, // 67 SPELL_AURA_MOD_DISARM + &AuraEffect::HandleAuraModStalked, // 68 SPELL_AURA_MOD_STALKED + &AuraEffect::HandleSchoolAbsorb, // 69 SPELL_AURA_SCHOOL_ABSORB implemented in Unit::CalcAbsorbResist + &AuraEffect::HandleUnused, // 70 SPELL_AURA_EXTRA_ATTACKS Useless, used by only one spell that has only visual effect + &AuraEffect::HandleModSpellCritChanceShool, // 71 SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL + &AuraEffect::HandleModPowerCostPCT, // 72 SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT + &AuraEffect::HandleModPowerCost, // 73 SPELL_AURA_MOD_POWER_COST_SCHOOL + &AuraEffect::HandleNoImmediateEffect, // 74 SPELL_AURA_REFLECT_SPELLS_SCHOOL implemented in Unit::SpellHitResult + &AuraEffect::HandleNoImmediateEffect, // 75 SPELL_AURA_MOD_LANGUAGE + &AuraEffect::HandleFarSight, // 76 SPELL_AURA_FAR_SIGHT + &AuraEffect::HandleModMechanicImmunity, // 77 SPELL_AURA_MECHANIC_IMMUNITY + &AuraEffect::HandleAuraMounted, // 78 SPELL_AURA_MOUNTED + &AuraEffect::HandleModDamagePercentDone, // 79 SPELL_AURA_MOD_DAMAGE_PERCENT_DONE + &AuraEffect::HandleModPercentStat, // 80 SPELL_AURA_MOD_PERCENT_STAT + &AuraEffect::HandleNoImmediateEffect, // 81 SPELL_AURA_SPLIT_DAMAGE_PCT + &AuraEffect::HandleWaterBreathing, // 82 SPELL_AURA_WATER_BREATHING + &AuraEffect::HandleModBaseResistance, // 83 SPELL_AURA_MOD_BASE_RESISTANCE + &AuraEffect::HandleModRegen, // 84 SPELL_AURA_MOD_REGEN + &AuraEffect::HandleModPowerRegen, // 85 SPELL_AURA_MOD_POWER_REGEN + &AuraEffect::HandleChannelDeathItem, // 86 SPELL_AURA_CHANNEL_DEATH_ITEM + &AuraEffect::HandleNoImmediateEffect, // 87 SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN implemented in Unit::MeleeDamageBonus and Unit::SpellDamageBonus + &AuraEffect::HandleNoImmediateEffect, // 88 SPELL_AURA_MOD_HEALTH_REGEN_PERCENT + &AuraEffect::HandlePeriodicDamagePCT, // 89 SPELL_AURA_PERIODIC_DAMAGE_PERCENT + &AuraEffect::HandleUnused, // 90 unused (3.0.8a) old SPELL_AURA_MOD_RESIST_CHANCE + &AuraEffect::HandleNoImmediateEffect, // 91 SPELL_AURA_MOD_DETECT_RANGE implemented in Creature::GetAttackDistance + &AuraEffect::HandlePreventFleeing, // 92 SPELL_AURA_PREVENTS_FLEEING + &AuraEffect::HandleModUnattackable, // 93 SPELL_AURA_MOD_UNATTACKABLE + &AuraEffect::HandleNoImmediateEffect, // 94 SPELL_AURA_INTERRUPT_REGEN implemented in Player::RegenerateAll + &AuraEffect::HandleAuraGhost, // 95 SPELL_AURA_GHOST + &AuraEffect::HandleNoImmediateEffect, // 96 SPELL_AURA_SPELL_MAGNET implemented in Spell::SelectMagnetTarget + &AuraEffect::HandleManaShield, // 97 SPELL_AURA_MANA_SHIELD implemented in Unit::CalcAbsorbResist + &AuraEffect::HandleAuraModSkill, // 98 SPELL_AURA_MOD_SKILL_TALENT + &AuraEffect::HandleAuraModAttackPower, // 99 SPELL_AURA_MOD_ATTACK_POWER + &AuraEffect::HandleUnused, //100 SPELL_AURA_AURAS_VISIBLE obsolete? all player can see all auras now, but still have spells including GM-spell + &AuraEffect::HandleModResistancePercent, //101 SPELL_AURA_MOD_RESISTANCE_PCT + &AuraEffect::HandleNoImmediateEffect, //102 SPELL_AURA_MOD_MELEE_ATTACK_POWER_VERSUS implemented in Unit::MeleeDamageBonus + &AuraEffect::HandleAuraModTotalThreat, //103 SPELL_AURA_MOD_TOTAL_THREAT + &AuraEffect::HandleAuraWaterWalk, //104 SPELL_AURA_WATER_WALK + &AuraEffect::HandleAuraFeatherFall, //105 SPELL_AURA_FEATHER_FALL + &AuraEffect::HandleAuraHover, //106 SPELL_AURA_HOVER + &AuraEffect::HandleAddModifier, //107 SPELL_AURA_ADD_FLAT_MODIFIER + &AuraEffect::HandleAddModifier, //108 SPELL_AURA_ADD_PCT_MODIFIER + &AuraEffect::HandleAddTargetTrigger, //109 SPELL_AURA_ADD_TARGET_TRIGGER + &AuraEffect::HandleModPowerRegenPCT, //110 SPELL_AURA_MOD_POWER_REGEN_PERCENT + &AuraEffect::HandleNULL, //111 SPELL_AURA_ADD_CASTER_HIT_TRIGGER chance redirect attack to caster + &AuraEffect::HandleNoImmediateEffect, //112 SPELL_AURA_OVERRIDE_CLASS_SCRIPTS + &AuraEffect::HandleNoImmediateEffect, //113 SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN implemented in Unit::MeleeDamageBonus + &AuraEffect::HandleNoImmediateEffect, //114 SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN_PCT implemented in Unit::MeleeDamageBonus + &AuraEffect::HandleNoImmediateEffect, //115 SPELL_AURA_MOD_HEALING implemented in Unit::SpellBaseHealingBonusForVictim + &AuraEffect::HandleNoImmediateEffect, //116 SPELL_AURA_MOD_REGEN_DURING_COMBAT + &AuraEffect::HandleNoImmediateEffect, //117 SPELL_AURA_MOD_MECHANIC_RESISTANCE implemented in Unit::MagicSpellHitResult + &AuraEffect::HandleNoImmediateEffect, //118 SPELL_AURA_MOD_HEALING_PCT implemented in Unit::SpellHealingBonus + &AuraEffect::HandleUnused, //119 unused (3.0.8a) old SPELL_AURA_SHARE_PET_TRACKING + &AuraEffect::HandleAuraUntrackable, //120 SPELL_AURA_UNTRACKABLE + &AuraEffect::HandleAuraEmpathy, //121 SPELL_AURA_EMPATHY + &AuraEffect::HandleModOffhandDamagePercent, //122 SPELL_AURA_MOD_OFFHAND_DAMAGE_PCT + &AuraEffect::HandleModTargetResistance, //123 SPELL_AURA_MOD_TARGET_RESISTANCE + &AuraEffect::HandleAuraModRangedAttackPower, //124 SPELL_AURA_MOD_RANGED_ATTACK_POWER + &AuraEffect::HandleNoImmediateEffect, //125 SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN implemented in Unit::MeleeDamageBonus + &AuraEffect::HandleNoImmediateEffect, //126 SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN_PCT implemented in Unit::MeleeDamageBonus + &AuraEffect::HandleNoImmediateEffect, //127 SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS implemented in Unit::MeleeDamageBonus + &AuraEffect::HandleModPossessPet, //128 SPELL_AURA_MOD_POSSESS_PET + &AuraEffect::HandleAuraModIncreaseSpeed, //129 SPELL_AURA_MOD_SPEED_ALWAYS + &AuraEffect::HandleAuraModIncreaseMountedSpeed, //130 SPELL_AURA_MOD_MOUNTED_SPEED_ALWAYS + &AuraEffect::HandleNoImmediateEffect, //131 SPELL_AURA_MOD_RANGED_ATTACK_POWER_VERSUS implemented in Unit::MeleeDamageBonus + &AuraEffect::HandleAuraModIncreaseEnergyPercent, //132 SPELL_AURA_MOD_INCREASE_ENERGY_PERCENT + &AuraEffect::HandleAuraModIncreaseHealthPercent, //133 SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT + &AuraEffect::HandleAuraModRegenInterrupt, //134 SPELL_AURA_MOD_MANA_REGEN_INTERRUPT + &AuraEffect::HandleModHealingDone, //135 SPELL_AURA_MOD_HEALING_DONE + &AuraEffect::HandleNoImmediateEffect, //136 SPELL_AURA_MOD_HEALING_DONE_PERCENT implemented in Unit::SpellHealingBonus + &AuraEffect::HandleModTotalPercentStat, //137 SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE + &AuraEffect::HandleHaste, //138 SPELL_AURA_MOD_HASTE + &AuraEffect::HandleForceReaction, //139 SPELL_AURA_FORCE_REACTION + &AuraEffect::HandleAuraModRangedHaste, //140 SPELL_AURA_MOD_RANGED_HASTE + &AuraEffect::HandleRangedAmmoHaste, //141 SPELL_AURA_MOD_RANGED_AMMO_HASTE + &AuraEffect::HandleAuraModBaseResistancePCT, //142 SPELL_AURA_MOD_BASE_RESISTANCE_PCT + &AuraEffect::HandleAuraModResistanceExclusive, //143 SPELL_AURA_MOD_RESISTANCE_EXCLUSIVE + &AuraEffect::HandleNoImmediateEffect, //144 SPELL_AURA_SAFE_FALL implemented in WorldSession::HandleMovementOpcodes + &AuraEffect::HandleAuraModPetTalentsPoints, //145 SPELL_AURA_MOD_PET_TALENT_POINTS + &AuraEffect::HandleNoImmediateEffect, //146 SPELL_AURA_ALLOW_TAME_PET_TYPE + &AuraEffect::HandleModStateImmunityMask, //147 SPELL_AURA_MECHANIC_IMMUNITY_MASK + &AuraEffect::HandleAuraRetainComboPoints, //148 SPELL_AURA_RETAIN_COMBO_POINTS + &AuraEffect::HandleNoImmediateEffect, //149 SPELL_AURA_REDUCE_PUSHBACK + &AuraEffect::HandleShieldBlockValue, //150 SPELL_AURA_MOD_SHIELD_BLOCKVALUE_PCT + &AuraEffect::HandleAuraTrackStealthed, //151 SPELL_AURA_TRACK_STEALTHED + &AuraEffect::HandleNoImmediateEffect, //152 SPELL_AURA_MOD_DETECTED_RANGE implemented in Creature::GetAttackDistance + &AuraEffect::HandleNoImmediateEffect, //153 SPELL_AURA_SPLIT_DAMAGE_FLAT + &AuraEffect::HandleNoImmediateEffect, //154 SPELL_AURA_MOD_STEALTH_LEVEL + &AuraEffect::HandleNoImmediateEffect, //155 SPELL_AURA_MOD_WATER_BREATHING + &AuraEffect::HandleNoImmediateEffect, //156 SPELL_AURA_MOD_REPUTATION_GAIN + &AuraEffect::HandleNULL, //157 SPELL_AURA_PET_DAMAGE_MULTI + &AuraEffect::HandleShieldBlockValue, //158 SPELL_AURA_MOD_SHIELD_BLOCKVALUE + &AuraEffect::HandleNoImmediateEffect, //159 SPELL_AURA_NO_PVP_CREDIT only for Honorless Target spell + &AuraEffect::HandleNoImmediateEffect, //160 SPELL_AURA_MOD_AOE_AVOIDANCE implemented in Unit::MagicSpellHitResult + &AuraEffect::HandleNoImmediateEffect, //161 SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT + &AuraEffect::HandleAuraPowerBurn, //162 SPELL_AURA_POWER_BURN_MANA + &AuraEffect::HandleNoImmediateEffect, //163 SPELL_AURA_MOD_CRIT_DAMAGE_BONUS_MELEE + &AuraEffect::HandleUnused, //164 unused (3.0.8a), only one test spell + &AuraEffect::HandleNoImmediateEffect, //165 SPELL_AURA_MELEE_ATTACK_POWER_ATTACKER_BONUS implemented in Unit::MeleeDamageBonus + &AuraEffect::HandleAuraModAttackPowerPercent, //166 SPELL_AURA_MOD_ATTACK_POWER_PCT + &AuraEffect::HandleAuraModRangedAttackPowerPercent, //167 SPELL_AURA_MOD_RANGED_ATTACK_POWER_PCT + &AuraEffect::HandleNoImmediateEffect, //168 SPELL_AURA_MOD_DAMAGE_DONE_VERSUS implemented in Unit::SpellDamageBonus, Unit::MeleeDamageBonus + &AuraEffect::HandleNoImmediateEffect, //169 SPELL_AURA_MOD_CRIT_PERCENT_VERSUS implemented in Unit::DealDamageBySchool, Unit::DoAttackDamage, Unit::SpellCriticalBonus + &AuraEffect::HandleNULL, //170 SPELL_AURA_DETECT_AMORE different spells that ignore transformation effects + &AuraEffect::HandleAuraModIncreaseSpeed, //171 SPELL_AURA_MOD_SPEED_NOT_STACK + &AuraEffect::HandleAuraModIncreaseMountedSpeed, //172 SPELL_AURA_MOD_MOUNTED_SPEED_NOT_STACK + &AuraEffect::HandleUnused, //173 unused (3.0.8a) no spells, old SPELL_AURA_ALLOW_CHAMPION_SPELLS only for Proclaim Champion spell + &AuraEffect::HandleModSpellDamagePercentFromStat, //174 SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT implemented in Unit::SpellBaseDamageBonus + &AuraEffect::HandleModSpellHealingPercentFromStat, //175 SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT implemented in Unit::SpellBaseHealingBonus + &AuraEffect::HandleSpiritOfRedemption, //176 SPELL_AURA_SPIRIT_OF_REDEMPTION only for Spirit of Redemption spell, die at aura end + &AuraEffect::HandleNULL, //177 SPELL_AURA_AOE_CHARM + &AuraEffect::HandleNoImmediateEffect, //178 SPELL_AURA_MOD_DEBUFF_RESISTANCE implemented in Unit::MagicSpellHitResult + &AuraEffect::HandleNoImmediateEffect, //179 SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE implemented in Unit::SpellCriticalBonus + &AuraEffect::HandleNoImmediateEffect, //180 SPELL_AURA_MOD_FLAT_SPELL_DAMAGE_VERSUS implemented in Unit::SpellDamageBonus + &AuraEffect::HandleUnused, //181 unused (3.0.8a) old SPELL_AURA_MOD_FLAT_SPELL_CRIT_DAMAGE_VERSUS + &AuraEffect::HandleAuraModResistenceOfStatPercent, //182 SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT + &AuraEffect::HandleNULL, //183 SPELL_AURA_MOD_CRITICAL_THREAT only used in 28746 + &AuraEffect::HandleNoImmediateEffect, //184 SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE implemented in Unit::RollMeleeOutcomeAgainst + &AuraEffect::HandleNoImmediateEffect, //185 SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE implemented in Unit::RollMeleeOutcomeAgainst + &AuraEffect::HandleNoImmediateEffect, //186 SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE implemented in Unit::MagicSpellHitResult + &AuraEffect::HandleNoImmediateEffect, //187 SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_CHANCE implemented in Unit::GetUnitCriticalChance + &AuraEffect::HandleNoImmediateEffect, //188 SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_CHANCE implemented in Unit::GetUnitCriticalChance + &AuraEffect::HandleModRating, //189 SPELL_AURA_MOD_RATING + &AuraEffect::HandleNULL, //190 SPELL_AURA_MOD_FACTION_REPUTATION_GAIN + &AuraEffect::HandleAuraModUseNormalSpeed, //191 SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED + &AuraEffect::HandleModMeleeRangedSpeedPct, //192 SPELL_AURA_HASTE_MELEE + &AuraEffect::HandleModCombatSpeedPct, //193 SPELL_AURA_MELEE_SLOW (in fact combat (any type attack) speed pct) + &AuraEffect::HandleNULL, //194 SPELL_AURA_MOD_TARGET_ABSORB_SCHOOL implemented in Unit::CalcAbsorbResist + &AuraEffect::HandleNoImmediateEffect, //195 SPELL_AURA_MOD_TARGET_ABILITY_ABSORB_SCHOOL implemented in Unit::CalcAbsorbResist + &AuraEffect::HandleNULL, //196 SPELL_AURA_MOD_COOLDOWN + &AuraEffect::HandleNoImmediateEffect, //197 SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE implemented in Unit::SpellCriticalBonus Unit::GetUnitCriticalChance + &AuraEffect::HandleUnused, //198 unused (3.0.8a) old SPELL_AURA_MOD_ALL_WEAPON_SKILLS + &AuraEffect::HandleNoImmediateEffect, //199 SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT implemented in Unit::MagicSpellHitResult + &AuraEffect::HandleNoImmediateEffect, //200 SPELL_AURA_MOD_XP_PCT implemented in Player::GiveXP + &AuraEffect::HandleAuraAllowFlight, //201 SPELL_AURA_FLY this aura enable flight mode... + &AuraEffect::HandleNoImmediateEffect, //202 SPELL_AURA_CANNOT_BE_DODGED implemented in Unit::RollPhysicalOutcomeAgainst + &AuraEffect::HandleNoImmediateEffect, //203 SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE implemented in Unit::CalculateMeleeDamage and Unit::CalculateSpellDamage + &AuraEffect::HandleNoImmediateEffect, //204 SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE implemented in Unit::CalculateMeleeDamage and Unit::CalculateSpellDamage + &AuraEffect::HandleNULL, //205 vulnerable to school dmg? + &AuraEffect::HandleNULL, //206 SPELL_AURA_MOD_SPEED_MOUNTED + &AuraEffect::HandleAuraModIncreaseFlightSpeed, //207 SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED + &AuraEffect::HandleAuraModIncreaseFlightSpeed, //208 SPELL_AURA_MOD_SPEED_FLIGHT, used only in spell: Flight Form (Passive) + &AuraEffect::HandleAuraModIncreaseFlightSpeed, //209 SPELL_AURA_MOD_FLIGHT_SPEED_ALWAYS + &AuraEffect::HandleNULL, //210 Commentator's Command + &AuraEffect::HandleAuraModIncreaseFlightSpeed, //211 SPELL_AURA_MOD_FLIGHT_SPEED_NOT_STACK + &AuraEffect::HandleAuraModRangedAttackPowerOfStatPercent, //212 SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT + &AuraEffect::HandleNoImmediateEffect, //213 SPELL_AURA_MOD_RAGE_FROM_DAMAGE_DEALT implemented in Player::RewardRage + &AuraEffect::HandleNULL, //214 Tamed Pet Passive + &AuraEffect::HandleArenaPreparation, //215 SPELL_AURA_ARENA_PREPARATION + &AuraEffect::HandleModCastingSpeed, //216 SPELL_AURA_HASTE_SPELLS + &AuraEffect::HandleUnused, //217 unused (3.0.8a) + &AuraEffect::HandleAuraModRangedHaste, //218 SPELL_AURA_HASTE_RANGED + &AuraEffect::HandleModManaRegen, //219 SPELL_AURA_MOD_MANA_REGEN_FROM_STAT + &AuraEffect::HandleModRatingFromStat, //220 SPELL_AURA_MOD_RATING_FROM_STAT + &AuraEffect::HandleNULL, //221 ignored + &AuraEffect::HandleUnused, //222 unused (3.0.8a) only for spell 44586 that not used in real spell cast + &AuraEffect::HandleNoImmediateEffect, //223 SPELL_AURA_RAID_PROC_FROM_CHARGE + &AuraEffect::HandleUnused, //224 unused (3.0.8a) + &AuraEffect::HandleNoImmediateEffect, //225 SPELL_AURA_RAID_PROC_FROM_CHARGE_WITH_VALUE + &AuraEffect::HandleAuraPeriodicDummy, //226 SPELL_AURA_PERIODIC_DUMMY + &AuraEffect::HandlePeriodicTriggerSpellWithValue, //227 SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE + &AuraEffect::HandleNoImmediateEffect, //228 stealth detection + &AuraEffect::HandleNULL, //229 SPELL_AURA_MOD_AOE_DAMAGE_AVOIDANCE + &AuraEffect::HandleAuraModIncreaseHealth, //230 SPELL_AURA_MOD_INCREASE_HEALTH_2 + &AuraEffect::HandleNoImmediateEffect, //231 SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE + &AuraEffect::HandleNoImmediateEffect, //232 SPELL_AURA_MECHANIC_DURATION_MOD implement in Unit::CalculateSpellDuration + &AuraEffect::HandleNULL, //233 set model id to the one of the creature with id GetMiscValue() + &AuraEffect::HandleNoImmediateEffect, //234 SPELL_AURA_MECHANIC_DURATION_MOD_NOT_STACK implement in Unit::CalculateSpellDuration + &AuraEffect::HandleAuraModDispelResist, //235 SPELL_AURA_MOD_DISPEL_RESIST implement in Unit::MagicSpellHitResult + &AuraEffect::HandleAuraControlVehicle, //236 SPELL_AURA_CONTROL_VEHICLE + &AuraEffect::HandleModSpellDamagePercentFromAttackPower, //237 SPELL_AURA_MOD_SPELL_DAMAGE_OF_ATTACK_POWER implemented in Unit::SpellBaseDamageBonus + &AuraEffect::HandleModSpellHealingPercentFromAttackPower, //238 SPELL_AURA_MOD_SPELL_HEALING_OF_ATTACK_POWER implemented in Unit::SpellBaseHealingBonus + &AuraEffect::HandleAuraModScale, //239 SPELL_AURA_MOD_SCALE_2 only in Noggenfogger Elixir (16595) before 2.3.0 aura 61 + &AuraEffect::HandleAuraModExpertise, //240 SPELL_AURA_MOD_EXPERTISE + &AuraEffect::HandleForceMoveForward, //241 Forces the player to move forward + &AuraEffect::HandleUnused, //242 SPELL_AURA_MOD_SPELL_DAMAGE_FROM_HEALING + &AuraEffect::HandleNULL, //243 faction reaction override spells + &AuraEffect::HandleComprehendLanguage, //244 Comprehend language + &AuraEffect::HandleNoImmediateEffect, //245 SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL + &AuraEffect::HandleNoImmediateEffect, //246 SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL_NOT_STACK implemented in Spell::EffectApplyAura + &AuraEffect::HandleNULL, //247 target to become a clone of the caster + &AuraEffect::HandleNoImmediateEffect, //248 SPELL_AURA_MOD_COMBAT_RESULT_CHANCE implemented in Unit::RollMeleeOutcomeAgainst + &AuraEffect::HandleAuraConvertRune, //249 SPELL_AURA_CONVERT_RUNE + &AuraEffect::HandleAuraModIncreaseHealth, //250 SPELL_AURA_MOD_INCREASE_HEALTH_2 + &AuraEffect::HandleNoImmediateEffect, //251 SPELL_AURA_MOD_ENEMY_DODGE + &AuraEffect::HandleNULL, //252 haste all? + &AuraEffect::HandleNoImmediateEffect, //253 SPELL_AURA_MOD_BLOCK_CRIT_CHANCE implemented in Unit::isBlockCritical + &AuraEffect::HandleAuraModDisarm, //254 SPELL_AURA_MOD_DISARM_OFFHAND + &AuraEffect::HandleNoImmediateEffect, //255 SPELL_AURA_MOD_MECHANIC_DAMAGE_TAKEN_PERCENT implemented in Unit::SpellDamageBonus + &AuraEffect::HandleNoReagentUseAura, //256 SPELL_AURA_NO_REAGENT_USE Use SpellClassMask for spell select + &AuraEffect::HandleNULL, //257 SPELL_AURA_MOD_TARGET_RESIST_BY_SPELL_CLASS Use SpellClassMask for spell select + &AuraEffect::HandleNULL, //258 SPELL_AURA_MOD_SPELL_VISUAL + &AuraEffect::HandleNoImmediateEffect, //259 SPELL_AURA_MOD_HOT_PCT implemented in Unit::SpellHealingBonus + &AuraEffect::HandleNoImmediateEffect, //260 SPELL_AURA_SCREEN_EFFECT (miscvalue = id in ScreenEffect.dbc) not required any code + &AuraEffect::HandlePhase, //261 SPELL_AURA_PHASE undetactable invisibility? implemented in Unit::isVisibleForOrDetect + &AuraEffect::HandleNoImmediateEffect, //262 SPELL_AURA_ABILITY_IGNORE_AURASTATE implemented in spell::cancast + &AuraEffect::HandleAuraAllowOnlyAbility, //263 SPELL_AURA_ALLOW_ONLY_ABILITY player can use only abilities set in SpellClassMask + &AuraEffect::HandleUnused, //264 unused (3.0.8a) + &AuraEffect::HandleUnused, //265 unused (3.0.8a) + &AuraEffect::HandleUnused, //266 unused (3.0.8a) + &AuraEffect::HandleNoImmediateEffect, //267 SPELL_AURA_MOD_IMMUNE_AURA_APPLY_SCHOOL implemented in Unit::IsImmunedToSpellEffect + &AuraEffect::HandleAuraModAttackPowerOfStatPercent, //268 SPELL_AURA_MOD_ATTACK_POWER_OF_STAT_PERCENT + &AuraEffect::HandleNoImmediateEffect, //269 SPELL_AURA_MOD_IGNORE_TARGET_RESIST implemented in Unit::CalcAbsorbResist and CalcArmorReducedDamage + &AuraEffect::HandleNoImmediateEffect, //270 SPELL_AURA_MOD_ABILITY_IGNORE_TARGET_RESIST implemented in Unit::CalcAbsorbResist and CalcArmorReducedDamage + &AuraEffect::HandleNoImmediateEffect, //271 SPELL_AURA_MOD_DAMAGE_FROM_CASTER implemented in Unit::SpellDamageBonus + &AuraEffect::HandleNULL, //272 reduce spell cast time? + &AuraEffect::HandleUnused, //273 clientside + &AuraEffect::HandleNoImmediateEffect, //274 SPELL_AURA_CONSUME_NO_AMMO implemented in spell::CalculateDamageDoneForAllTargets + &AuraEffect::HandleNoImmediateEffect, //275 SPELL_AURA_MOD_IGNORE_SHAPESHIFT Use SpellClassMask for spell select + &AuraEffect::HandleNULL, //276 mod damage % mechanic? + &AuraEffect::HandleNoImmediateEffect, //277 SPELL_AURA_MOD_ABILITY_AFFECTED_TARGETS implemented in spell::settargetmap + &AuraEffect::HandleAuraModDisarm, //278 SPELL_AURA_MOD_DISARM_RANGED disarm ranged weapon + &AuraEffect::HandleNULL, //279 visual effects? 58836 and 57507 + &AuraEffect::HandleNoImmediateEffect, //280 SPELL_AURA_MOD_WEAPONTYPE_IGNORE_TARGET_RESISTANCE + &AuraEffect::HandleNoImmediateEffect, //281 SPELL_AURA_MOD_HONOR_GAIN_PCT implemented in Player::RewardHonor + &AuraEffect::HandleAuraIncreaseBaseHealthPercent, //282 SPELL_AURA_INCREASE_BASE_HEALTH_PERCENT + &AuraEffect::HandleNoImmediateEffect, //283 SPELL_AURA_MOD_HEALING_RECEIVED implemented in Unit::SpellHealingBonus + &AuraEffect::HandleUnused, //284 not used by any spells (3.08a) + &AuraEffect::HandleUnused, //285 not used by any spells (3.08a) + &AuraEffect::HandleUnused, //286 not used by any spells (3.08a) + &AuraEffect::HandleNoImmediateEffect, //287 SPELL_AURA_DEFLECT_SPELLS implemented in Unit::MagicSpellHitResult and Unit::MeleeSpellHitResult + &AuraEffect::HandleUnused, //288 not used by any spells (3.09) except 1 test spell. }; -Aura::Aura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster, Item* castItem) : -m_procCharges(0), m_stackAmount(1), m_spellmod(NULL), m_effIndex(eff), m_caster_guid(0), m_target(target), -m_timeCla(1000), m_castItemGuid(castItem?castItem->GetGUID():0), m_auraSlot(MAX_AURAS), -m_positive(false), m_permanent(false), m_isPeriodic(false), m_isTrigger(false), m_isAreaAura(false), -m_isPersistent(false), m_updated(false), m_removeMode(AURA_REMOVE_BY_DEFAULT), m_isRemovedOnShapeLost(true), m_in_use(false), -m_periodicTimer(0), m_PeriodicEventId(0), m_AuraDRGroup(DIMINISHING_NONE) -,m_tickNumber(0) +Aura::Aura(SpellEntry const* spellproto, uint32 effMask, int32 *currentBasePoints, Unit *target, Unit *caster, Item* castItem) : +m_caster_guid(0), m_castItemGuid(castItem?castItem->GetGUID():0), m_target(target), +m_timeCla(1000), m_removeMode(AURA_REMOVE_BY_DEFAULT), m_AuraDRGroup(DIMINISHING_NONE), +m_auraSlot(MAX_AURAS), m_auraLevel(1), m_procCharges(0), m_stackAmount(1),m_auraStateMask(0), m_updated(false), m_isRemoved(false) { assert(target); assert(spellproto && spellproto == sSpellStore.LookupEntry( spellproto->Id ) && "`info` must be pointer to sSpellStore element"); - m_spellProto = spellproto; + m_auraFlags = effMask; - int32 damage; - if(currentBasePoints) - { - damage = *currentBasePoints; - m_currentBasePoints = damage - 1; - } - else - { - m_currentBasePoints = m_spellProto->EffectBasePoints[eff]; - if(caster) - damage = caster->CalculateSpellDamage(m_spellProto, m_effIndex, m_currentBasePoints, target); - else - damage = m_currentBasePoints + 1; - } + m_spellProto = spellproto; m_isPassive = IsPassiveSpell(GetId()); - m_positive = IsPositiveEffect(GetId(), m_effIndex); + + m_auraStateMask = 0; + + m_isSingleTargetAura = IsSingleTargetSpell(m_spellProto); m_applyTime = time(NULL); @@ -356,112 +367,192 @@ m_periodicTimer(0), m_PeriodicEventId(0), m_AuraDRGroup(DIMINISHING_NONE) { m_caster_guid = target->GetGUID(); //damage = m_currentBasePoints+1; // stored value-1 - m_maxduration = target->CalculateSpellDuration(m_spellProto, m_effIndex, target); + m_maxduration = target->CalcSpellDuration(m_spellProto); } else { m_caster_guid = caster->GetGUID(); //damage = caster->CalculateSpellDamage(m_spellProto,m_effIndex,m_currentBasePoints,target); - m_maxduration = caster->CalculateSpellDuration(m_spellProto, m_effIndex, target); - - if (!damage && castItem && castItem->GetItemSuffixFactor()) - { - ItemRandomSuffixEntry const *item_rand_suffix = sItemRandomSuffixStore.LookupEntry(abs(castItem->GetItemRandomPropertyId())); - if(item_rand_suffix) - { - for (int k=0; k<3; k++) - { - SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(item_rand_suffix->enchant_id[k]); - if(pEnchant) - { - for (int t=0; t<3; t++) - if(pEnchant->spellid[t] == m_spellProto->Id) - { - damage = uint32((item_rand_suffix->prefix[k]*castItem->GetItemSuffixFactor()) / 10000 ); - break; - } - } - - if(damage) - break; - } - } - } + m_maxduration = caster->CalcSpellDuration(m_spellProto); } if(m_maxduration == -1 || m_isPassive && m_spellProto->DurationIndex == 0) m_permanent = true; + else + m_permanent = false; Player* modOwner = caster ? caster->GetSpellModOwner() : NULL; if(!m_permanent && modOwner) + { modOwner->ApplySpellMod(GetId(), SPELLMOD_DURATION, m_maxduration); + // Get zero duration aura after - need set m_maxduration > 0 for apply/remove aura work + if (m_maxduration<=0) + m_maxduration = 1; + } m_duration = m_maxduration; - if(modOwner) - modOwner->ApplySpellMod(GetId(), SPELLMOD_ACTIVATION_TIME, m_periodicTimer); - - sLog.outDebug("Aura: construct Spellid : %u, Aura : %u Duration : %d Target : %d Damage : %d", m_spellProto->Id, m_spellProto->EffectApplyAuraName[eff], m_maxduration, m_spellProto->EffectImplicitTargetA[eff],damage); + m_isDeathPersist = IsDeathPersistentSpell(m_spellProto); - m_effIndex = eff; - SetModifier(AuraType(m_spellProto->EffectApplyAuraName[eff]), damage, m_spellProto->EffectAmplitude[eff], m_spellProto->EffectMiscValue[eff]); + m_procCharges = m_spellProto->procCharges; + if(modOwner) + modOwner->ApplySpellMod(GetId(), SPELLMOD_CHARGES, m_procCharges); - m_isDeathPersist = IsDeathPersistentSpell(m_spellProto); + m_isRemovedOnShapeLost = (m_caster_guid==m_target->GetGUID() && + m_spellProto->Stances && + !(m_spellProto->AttributesEx2 & SPELL_ATTR_EX2_NOT_NEED_SHAPESHIFT) && + !(m_spellProto->Attributes & SPELL_ATTR_NOT_SHAPESHIFT)); - if(m_spellProto->procCharges) + for (uint8 i=0 ;i<MAX_SPELL_EFFECTS;++i) { - m_procCharges = m_spellProto->procCharges; - - if(modOwner) - modOwner->ApplySpellMod(GetId(), SPELLMOD_CHARGES, m_procCharges); + if (m_auraFlags & (uint8(1) << i)) + { + if (¤tBasePoints[0]) + m_partAuras[i]=CreateAuraEffect(this, i,¤tBasePoints[0]+i, caster); + else + m_partAuras[i]=CreateAuraEffect(this, i, NULL , caster); + // correct flags if aura couldn't be created + if (!m_partAuras[i]) + m_auraFlags &= uint8(~(1<< i)); + } + else + { + m_partAuras[i]=NULL; + } } - else - m_procCharges = -1; - m_isRemovedOnShapeLost = (m_caster_guid==m_target->GetGUID() && m_spellProto->Stances && - !(m_spellProto->AttributesEx2 & 0x80000) && !(m_spellProto->Attributes & 0x10000)); + // Aura is positive when it is casted by friend and at least one aura is positive + // or when it is casted by enemy and at least one aura is negative + bool swap=false; + if (!caster || caster==target) // caster == target - 1 negative effect is enough for aura to be negative + m_positive = false; + else + m_positive = !caster->IsHostileTo(m_target); + for (uint8 i=0;i<MAX_SPELL_EFFECTS;++i) + { + if (!(1<<i & GetEffectMask())) + continue; + if (m_positive == IsPositiveEffect(GetId(), i)) + { + swap = true; + break; + } + } + if (!swap) + m_positive=!m_positive; } Aura::~Aura() { + // free part auras memory + for (uint8 i=0 ; i<MAX_SPELL_EFFECTS;++i) + if (m_partAuras[i]) + delete m_partAuras[i]; +} + +AuraEffect::AuraEffect(Aura * parentAura, uint8 effIndex, int32 * currentBasePoints , Unit * caster, Item* castItem) : +m_parentAura(parentAura), m_spellmod(NULL), m_periodicTimer(0), m_isPeriodic(false), m_isAreaAura(false), m_isPersistent(false), +m_target(parentAura->GetTarget()), m_tickNumber(0) +{ + m_spellProto = parentAura->GetSpellProto(); + m_effIndex = effIndex; + m_auraName = AuraType(m_spellProto->EffectApplyAuraName[m_effIndex]); + + if(currentBasePoints) + m_currentBasePoints = *currentBasePoints; + else + m_currentBasePoints = m_spellProto->EffectBasePoints[m_effIndex]; + + if(caster) + m_amount = caster->CalculateSpellDamage(m_spellProto, m_effIndex, m_currentBasePoints, m_target); + else + m_amount = m_currentBasePoints + 1; + + if (!m_amount && castItem && castItem->GetItemSuffixFactor()) + { + ItemRandomSuffixEntry const *item_rand_suffix = sItemRandomSuffixStore.LookupEntry(abs(castItem->GetItemRandomPropertyId())); + if(item_rand_suffix) + { + for (int k=0; k<3; k++) + { + SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(item_rand_suffix->enchant_id[k]); + if(pEnchant) + { + for (int t=0; t<3; t++) + if(pEnchant->spellid[t] == m_spellProto->Id) + { + m_amount = uint32((item_rand_suffix->prefix[k]*castItem->GetItemSuffixFactor()) / 10000 ); + break; + } + } + + if(m_amount) + break; + } + } + } + + Player* modOwner = caster ? caster->GetSpellModOwner() : NULL; + m_amplitude = m_spellProto->EffectAmplitude[m_effIndex]; + + //apply casting time mods for channeled spells + if (modOwner && m_amplitude && IsChanneledSpell(m_spellProto)) + modOwner->ModSpellCastTime(m_spellProto, m_amplitude); + + // Apply periodic time mod + if(modOwner && m_amplitude) + modOwner->ApplySpellMod(GetId(), SPELLMOD_ACTIVATION_TIME, m_amplitude); + + // Start periodic on next tick or at aura apply + if (!(m_spellProto->AttributesEx5 & SPELL_ATTR_EX5_START_PERIODIC_AT_APPLY)) + m_periodicTimer += m_amplitude; } -AreaAura::AreaAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, -Unit *caster, Item* castItem) : Aura(spellproto, eff, currentBasePoints, target, caster, castItem) +AreaAuraEffect::AreaAuraEffect(Aura * parentAura, uint32 effIndex, int32 * currentBasePoints, Unit * caster, Item * castItem) +: AuraEffect(parentAura, effIndex, currentBasePoints, caster, castItem) { + m_removeTime = FRIENDLY_AA_REMOVE_TIME; m_isAreaAura = true; - // caster==NULL in constructor args if target==caster in fact - Unit* caster_ptr = caster ? caster : target; + Unit* caster_ptr = caster ? caster : m_target; + + if (m_spellProto->Effect[effIndex] == SPELL_EFFECT_APPLY_AREA_AURA_ENEMY) + m_radius = GetSpellRadiusForHostile(sSpellRadiusStore.LookupEntry(GetSpellProto()->EffectRadiusIndex[m_effIndex])); + else + m_radius = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(GetSpellProto()->EffectRadiusIndex[m_effIndex])); - m_radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(GetSpellProto()->EffectRadiusIndex[m_effIndex])); if(Player* modOwner = caster_ptr->GetSpellModOwner()) modOwner->ApplySpellMod(GetId(), SPELLMOD_RADIUS, m_radius); - switch(spellproto->Effect[eff]) + switch(m_spellProto->Effect[effIndex]) { case SPELL_EFFECT_APPLY_AREA_AURA_PARTY: m_areaAuraType = AREA_AURA_PARTY; - if(target->GetTypeId() == TYPEID_UNIT && ((Creature*)target)->isTotem()) - m_modifier.m_auraname = SPELL_AURA_NONE; + if(m_target->GetTypeId() == TYPEID_UNIT && ((Creature*)m_target)->isTotem()) + m_auraName = SPELL_AURA_NONE; + break; + case SPELL_EFFECT_APPLY_AREA_AURA_RAID: + m_areaAuraType = AREA_AURA_RAID; + if(m_target->GetTypeId() == TYPEID_UNIT && ((Creature*)m_target)->isTotem()) + m_auraName = SPELL_AURA_NONE; break; case SPELL_EFFECT_APPLY_AREA_AURA_FRIEND: m_areaAuraType = AREA_AURA_FRIEND; break; case SPELL_EFFECT_APPLY_AREA_AURA_ENEMY: m_areaAuraType = AREA_AURA_ENEMY; - if(target == caster_ptr) - m_modifier.m_auraname = SPELL_AURA_NONE; // Do not do any effect on self + if(m_target == caster_ptr) + m_auraName = SPELL_AURA_NONE; // Do not do any effect on self break; case SPELL_EFFECT_APPLY_AREA_AURA_PET: m_areaAuraType = AREA_AURA_PET; break; case SPELL_EFFECT_APPLY_AREA_AURA_OWNER: m_areaAuraType = AREA_AURA_OWNER; - if(target == caster_ptr) - m_modifier.m_auraname = SPELL_AURA_NONE; + if(m_target == caster_ptr) + m_auraName = SPELL_AURA_NONE; break; default: sLog.outError("Wrong spell effect in AreaAura constructor"); @@ -470,26 +561,30 @@ Unit *caster, Item* castItem) : Aura(spellproto, eff, currentBasePoints, target, } } -AreaAura::~AreaAura() +AreaAuraEffect::~AreaAuraEffect() { } -PersistentAreaAura::PersistentAreaAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, -Unit *caster, Item* castItem) : Aura(spellproto, eff, currentBasePoints, target, caster, castItem) +PersistentAreaAuraEffect::PersistentAreaAuraEffect(Aura * parentAura, uint32 effIndex, int32 * currentBasePoints, Unit * caster,Item * castItem) +: AuraEffect(parentAura, effIndex, currentBasePoints, caster, castItem) { m_isPersistent = true; } -PersistentAreaAura::~PersistentAreaAura() +PersistentAreaAuraEffect::~PersistentAreaAuraEffect() { } -Aura* CreateAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster, Item* castItem) +AuraEffect* CreateAuraEffect(Aura * parentAura, uint32 effIndex, int32 *currentBasePoints, Unit * caster, Item * castItem) { - if (IsAreaAuraEffect(spellproto->Effect[eff])) - return new AreaAura(spellproto, eff, currentBasePoints, target, caster, castItem); - - return new Aura(spellproto, eff, currentBasePoints, target, caster, castItem); + assert (parentAura); + if (IsAreaAuraEffect(parentAura->GetSpellProto()->Effect[effIndex])) + return new AreaAuraEffect(parentAura, effIndex, currentBasePoints, caster, castItem); + else if (parentAura->GetSpellProto()->Effect[effIndex] == SPELL_EFFECT_APPLY_AURA) + return new AuraEffect(parentAura, effIndex, currentBasePoints, caster, castItem); + else if (parentAura->GetSpellProto()->Effect[effIndex] == SPELL_EFFECT_PERSISTENT_AREA_AURA) + return new PersistentAreaAuraEffect(parentAura, effIndex, currentBasePoints, caster, castItem); + else return NULL; } Unit* Aura::GetCaster() const @@ -503,14 +598,6 @@ Unit* Aura::GetCaster() const return unit && unit->IsInWorld() ? unit : NULL; } -void Aura::SetModifier(AuraType t, int32 a, uint32 pt, int32 miscValue) -{ - m_modifier.m_auraname = t; - m_modifier.m_amount = a; - m_modifier.m_miscvalue = miscValue; - m_modifier.periodictime = pt; -} - void Aura::Update(uint32 diff) { if (m_duration > 0) @@ -518,49 +605,72 @@ void Aura::Update(uint32 diff) m_duration -= diff; if (m_duration < 0) m_duration = 0; - m_timeCla -= diff; - // GetEffIndex()==0 prevent double/triple apply manaPerSecond/manaPerSecondPerLevel to same spell with many auras // all spells with manaPerSecond/manaPerSecondPerLevel have aura in effect 0 - if(GetEffIndex()==0 && m_timeCla <= 0) + if(m_timeCla > 0) + m_timeCla -= diff; + if(m_timeCla <= 0) { if(Unit* caster = GetCaster()) { - Powers powertype = Powers(m_spellProto->powerType); - int32 manaPerSecond = m_spellProto->manaPerSecond + m_spellProto->manaPerSecondPerLevel * caster->getLevel(); - m_timeCla = 1000; - if (manaPerSecond) + if(int32 manaPerSecond = m_spellProto->manaPerSecond + m_spellProto->manaPerSecondPerLevel * caster->getLevel()) { - if(powertype==POWER_HEALTH) - caster->ModifyHealth(-manaPerSecond); + m_timeCla = 1000; + + Powers powertype = Powers(m_spellProto->powerType); + if(powertype == POWER_HEALTH) + { + if (caster->GetHealth() > manaPerSecond) + caster->ModifyHealth(-manaPerSecond); + else + { + m_target->RemoveAura(this); + return; + } + } else - caster->ModifyPower(powertype,-manaPerSecond); + { + if (caster->GetPower(powertype) >= manaPerSecond) + caster->ModifyPower(powertype, -manaPerSecond); + else + { + m_target->RemoveAura(this); + return; + } + } } } } } - // Channeled aura required check distance from caster except in possessed cases - if(IsChanneledSpell(m_spellProto) && m_caster_guid != m_target->GetGUID() && !m_target->isPossessed()) + // Channeled aura required check distance from caster + if(IsChanneledSpell(m_spellProto) && m_caster_guid != m_target->GetGUID() + && !IsAreaAura() && !IsPersistent() // check for these is done in auraeffect + /* && !m_target->isPossessed()*/) { Unit* caster = GetCaster(); if(!caster) { - m_target->RemoveAura(GetId(),GetEffIndex()); + m_target->RemoveAura(this); return; } - // Get spell range - float radius; + float radius=-1.0f; SpellModOp mod; - if (m_spellProto->EffectRadiusIndex[GetEffIndex()]) + // get part aura with lowest radius + for (uint8 i=0;i<3;++i) { - radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellProto->EffectRadiusIndex[GetEffIndex()])); - mod = SPELLMOD_RADIUS; + if (HasEffect(i) && m_spellProto->EffectRadiusIndex[i]) + { + float new_radius = caster->GetSpellRadiusForTarget(m_target, sSpellRadiusStore.LookupEntry(m_spellProto->EffectRadiusIndex[i])); + if (radius < 0.0f || radius > new_radius) + radius = new_radius; + mod = SPELLMOD_RADIUS; + } } - else + if (radius < 0.0f) { - radius = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellProto->rangeIndex)); + radius = caster->GetSpellMaxRangeForTarget(m_target, sSpellRangeStore.LookupEntry(m_spellProto->rangeIndex)) ; mod = SPELLMOD_RANGE; } @@ -569,46 +679,38 @@ void Aura::Update(uint32 diff) if(!caster->IsWithinDistInMap(m_target,radius)) { - m_target->RemoveAura(GetId(),GetEffIndex()); + m_target->RemoveAura(this); return; } } - if(m_isPeriodic && (m_duration >= 0 || m_isPassive || m_permanent)) + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + if (m_partAuras[i]) + m_partAuras[i]->Update(diff); +} + +void AuraEffect::Update(uint32 diff) +{ + if (m_isPeriodic && (GetParentAura()->GetAuraDuration() >=0 || GetParentAura()->IsPassive() || GetParentAura()->IsPermanent())) { m_periodicTimer -= diff; - if(m_periodicTimer <= 0) // tick also at m_periodicTimer==0 to prevent lost last tick in case max m_duration == (max m_periodicTimer)*N + if(m_periodicTimer <= 0) // tick also at m_periodicTimer==0 to prevent lost last tick in case max m_duration == (max m_periodicTimer)*N { ++m_tickNumber; - if( m_modifier.m_auraname == SPELL_AURA_MOD_REGEN || - m_modifier.m_auraname == SPELL_AURA_MOD_POWER_REGEN || - // Cannibalize, eating items and other spells - m_modifier.m_auraname == SPELL_AURA_OBS_MOD_HEALTH || - // Eating items and other spells - m_modifier.m_auraname == SPELL_AURA_OBS_MOD_MANA ) - { - ApplyModifier(true); - return; - } // update before applying (aura can be removed in TriggerSpell or PeriodicTick calls) - m_periodicTimer += m_modifier.periodictime; + m_periodicTimer += m_amplitude; if(!m_target->hasUnitState(UNIT_STAT_ISOLATED)) - { - if(m_isTrigger) - TriggerSpell(); - else - PeriodicTick(); - } + PeriodicTick(); } } } -void AreaAura::Update(uint32 diff) +void AreaAuraEffect::Update(uint32 diff) { // update for the caster of the aura - if(m_caster_guid == m_target->GetGUID()) + if(GetCasterGUID() == m_target->GetGUID()) { Unit* caster = m_target; @@ -621,17 +723,20 @@ void AreaAura::Update(uint32 diff) case AREA_AURA_PARTY: caster->GetPartyMember(targets, m_radius); break; + case AREA_AURA_RAID: + caster->GetRaidMember(targets, m_radius); + break; case AREA_AURA_FRIEND: { Trinity::AnyFriendlyUnitInObjectRangeCheck u_check(caster, caster, m_radius); - Trinity::UnitListSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(targets, u_check); + Trinity::UnitListSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(caster, targets, u_check); caster->VisitNearbyObject(m_radius, searcher); break; } case AREA_AURA_ENEMY: { Trinity::AnyAoETargetUnitInObjectRangeCheck u_check(caster, caster, m_radius); // No GetCharmer in searcher - Trinity::UnitListSearcher<Trinity::AnyAoETargetUnitInObjectRangeCheck> searcher(targets, u_check); + Trinity::UnitListSearcher<Trinity::AnyAoETargetUnitInObjectRangeCheck> searcher(caster, targets, u_check); caster->VisitNearbyObject(m_radius, searcher); break; } @@ -646,7 +751,7 @@ void AreaAura::Update(uint32 diff) for(std::list<Unit *>::iterator tIter = targets.begin(); tIter != targets.end(); tIter++) { - if((*tIter)->HasAura(GetId(), m_effIndex)) + if((*tIter)->HasAuraEffect(GetId(), GetEffIndex(), caster->GetGUID())) continue; if(SpellEntry const *actualSpellInfo = spellmgr.SelectAuraRankForPlayerLevel(GetSpellProto(), (*tIter)->getLevel())) @@ -655,136 +760,167 @@ void AreaAura::Update(uint32 diff) // recalculate basepoints for lower rank (all AreaAura spell not use custom basepoints?) //if(actualSpellInfo != GetSpellProto()) // actualBasePoints = actualSpellInfo->EffectBasePoints[m_effIndex]; - AreaAura *aur; - if(actualSpellInfo == GetSpellProto()) - aur = new AreaAura(actualSpellInfo, m_effIndex, &m_modifier.m_amount, (*tIter), caster, NULL); - else - aur = new AreaAura(actualSpellInfo, m_effIndex, NULL, (*tIter), caster, NULL); - (*tIter)->AddAura(aur); + Aura * aur = (*tIter)->AddAuraEffect(actualSpellInfo->Id, GetEffIndex(), caster); if(m_areaAuraType == AREA_AURA_ENEMY) caster->CombatStart(*tIter); } } } - Aura::Update(diff); + AuraEffect::Update(diff); } else // aura at non-caster { - Unit * tmp_target = m_target; - Unit* caster = GetCaster(); - uint32 tmp_spellId = GetId(), tmp_effIndex = m_effIndex; - // WARNING: the aura may get deleted during the update // DO NOT access its members after update! - Aura::Update(diff); + AuraEffect::Update(diff); + + // Speedup - no need to do more checks + if (GetParentAura()->IsRemoved()) + return; + + // Caster may be deleted due to update + Unit* caster = GetCaster(); // remove aura if out-of-range from caster (after teleport for example) // or caster is isolated or caster no longer has the aura // or caster is (no longer) friendly bool needFriendly = (m_areaAuraType == AREA_AURA_ENEMY ? false : true); if( !caster || caster->hasUnitState(UNIT_STAT_ISOLATED) || - !caster->IsWithinDistInMap(tmp_target, m_radius) || - !caster->HasAura(tmp_spellId, tmp_effIndex) || - caster->IsFriendlyTo(tmp_target) != needFriendly + !caster->HasAuraEffect(GetId(), m_effIndex) || + caster->IsFriendlyTo(m_target) != needFriendly ) { - tmp_target->RemoveAura(tmp_spellId, tmp_effIndex); + m_target->RemoveAura(GetParentAura()); } - else if( m_areaAuraType == AREA_AURA_PARTY) // check if in same sub group + else if (!caster->IsWithinDistInMap(m_target, m_radius)) { - if(!tmp_target->IsInPartyWith(caster)) - tmp_target->RemoveAura(tmp_spellId, tmp_effIndex); + if (needFriendly) + { + m_removeTime -= diff; + if (m_removeTime < 0) + m_target->RemoveAura(GetParentAura()); + } + else + m_target->RemoveAura(GetParentAura()); } - else if( m_areaAuraType == AREA_AURA_PET || m_areaAuraType == AREA_AURA_OWNER ) + else { - if( tmp_target->GetGUID() != caster->GetCharmerOrOwnerGUID() ) - tmp_target->RemoveAura(tmp_spellId, tmp_effIndex); + // Reset aura remove timer + m_removeTime = FRIENDLY_AA_REMOVE_TIME; + if( m_areaAuraType == AREA_AURA_PARTY) // check if in same sub group + { + if(!m_target->IsInPartyWith(caster)) + m_target->RemoveAura(GetParentAura()); + } + else if( m_areaAuraType == AREA_AURA_RAID) + { + if(!m_target->IsInRaidWith(caster)) + m_target->RemoveAura(GetParentAura()); + } + else if( m_areaAuraType == AREA_AURA_PET || m_areaAuraType == AREA_AURA_OWNER ) + { + if( m_target->GetGUID() != caster->GetCharmerOrOwnerGUID() ) + m_target->RemoveAura(GetParentAura()); + } } } } -void PersistentAreaAura::Update(uint32 diff) +void PersistentAreaAuraEffect::Update(uint32 diff) { - bool remove = false; - - // remove the aura if its caster or the dynamic object causing it was removed - // or if the target moves too far from the dynamic object - Unit *caster = GetCaster(); - if (caster) + if(Unit *caster = GetCaster()) { - DynamicObject *dynObj = caster->GetDynObject(GetId(), GetEffIndex()); - if (dynObj) + if(DynamicObject *dynObj = caster->GetDynObject(GetId(), GetEffIndex())) { - if (!m_target->IsWithinDistInMap(dynObj, dynObj->GetRadius())) - remove = true; + if(m_target->IsWithinDistInMap(dynObj, dynObj->GetRadius())) + { + AuraEffect::Update(diff); + return; + } } - else - remove = true; } - else - remove = true; - Unit *tmp_target = m_target; - uint32 tmp_id = GetId(), tmp_index = GetEffIndex(); - - // WARNING: the aura may get deleted during the update - // DO NOT access its members after update! - Aura::Update(diff); - - if(remove) - tmp_target->RemoveAura(tmp_id, tmp_index); + // remove the aura if its caster or the dynamic object causing it was removed + // or if the target moves too far from the dynamic object + m_target->RemoveAura(GetParentAura()); } -void Aura::ApplyModifier(bool apply, bool Real) +void AuraEffect::ApplyModifier(bool apply, bool Real) { - AuraType aura = m_modifier.m_auraname; + if (GetParentAura()->IsRemoved()) + return; - m_in_use = true; - if(aura<TOTAL_AURAS) - (*this.*AuraHandler [aura])(apply,Real); - m_in_use = false; + if(m_auraName<TOTAL_AURAS) + (*this.*AuraHandler [m_auraName])(apply,Real); } -void Aura::UpdateAuraDuration() +void AuraEffect::CleanupTriggeredSpells() { - if(m_auraSlot >= MAX_AURAS || m_isPassive) + uint32 tSpellId = m_spellProto->EffectTriggerSpell[GetEffIndex()]; + if(!tSpellId) return; - if( m_target->GetTypeId() == TYPEID_PLAYER) - { - WorldPacket data(SMSG_UPDATE_AURA_DURATION, 5); - data << (uint8)m_auraSlot << (uint32)m_duration; - ((Player*)m_target)->SendDirectMessage(&data); + SpellEntry const* tProto = sSpellStore.LookupEntry(tSpellId); + if(!tProto) + return; - data.Initialize(SMSG_SET_EXTRA_AURA_INFO, (8+1+4+4+4)); - data.append(m_target->GetPackGUID()); - data << uint8(m_auraSlot); - data << uint32(GetId()); - data << uint32(GetAuraMaxDuration()); - data << uint32(GetAuraDuration()); - ((Player*)m_target)->SendDirectMessage(&data); - } + if(GetSpellDuration(tProto) != -1) + return; - // not send in case player loading (will not work anyway until player not added to map), sent in visibility change code - if(m_target->GetTypeId() == TYPEID_PLAYER && ((Player*)m_target)->GetSession()->PlayerLoading()) + // needed for spell 43680, maybe others + // TODO: is there a spell flag, which can solve this in a more sophisticated way? + if(m_spellProto->EffectApplyAuraName[GetEffIndex()] == SPELL_AURA_PERIODIC_TRIGGER_SPELL && + GetSpellDuration(m_spellProto) == m_spellProto->EffectAmplitude[GetEffIndex()]) return; - Unit* caster = GetCaster(); + m_target->RemoveAurasDueToSpell(tSpellId, GetCasterGUID()); +} - if(caster && caster->GetTypeId() == TYPEID_PLAYER && caster != m_target) - SendAuraDurationForCaster((Player*)caster); +void Aura::ApplyAllModifiers(bool apply, bool Real) +{ + for (uint8 i = 0; i<MAX_SPELL_EFFECTS;++i) + if (m_partAuras[i]) + m_partAuras[i]->ApplyModifier(apply, Real); } -void Aura::SendAuraDurationForCaster(Player* caster) +void Aura::SendAuraUpdate() { - WorldPacket data(SMSG_SET_EXTRA_AURA_INFO_NEED_UPDATE, (8+1+4+4+4)); + if (m_auraSlot>=MAX_AURAS) + return; + WorldPacket data(SMSG_AURA_UPDATE); + data.append(m_target->GetPackGUID()); data << uint8(m_auraSlot); + + if(!m_target->GetVisibleAura(m_auraSlot)) + { + data << uint32(0); + sLog.outDebug("Aura %u removed slot %u",GetId(), m_auraSlot); + m_target->SendMessageToSet(&data, true); + return; + } + data << uint32(GetId()); - data << uint32(GetAuraMaxDuration()); // full - data << uint32(GetAuraDuration()); // remain - caster->GetSession()->SendPacket(&data); + data << uint8(m_auraFlags); + data << uint8(m_auraLevel); + data << uint8(m_stackAmount>1 ? m_stackAmount : m_procCharges); + + if(!(m_auraFlags & AFLAG_CASTER)) + { + if (Unit * caster = GetCaster()) + data.append(caster->GetPackGUID()); + else + data << uint8(0); + } + + if(m_auraFlags & AFLAG_DURATION) + { + data << uint32(m_maxduration); + data << uint32(m_duration); + } + + m_target->SendMessageToSet(&data, true); } void Aura::_AddAura() @@ -794,139 +930,157 @@ void Aura::_AddAura() if(!m_target) return; - // we can found aura in NULL_AURA_SLOT and then need store state instead check slot != NULL_AURA_SLOT - bool secondaura = false; - uint8 slot = NULL_AURA_SLOT; + Unit* caster = GetCaster(); - for(uint8 i = 0; i < 3; i++) + // set infinity cooldown state for spells + if(caster && caster->GetTypeId() == TYPEID_PLAYER) { - Unit::spellEffectPair spair = Unit::spellEffectPair(GetId(), i); - for(Unit::AuraMap::const_iterator itr = m_target->GetAuras().lower_bound(spair); itr != m_target->GetAuras().upper_bound(spair); ++itr) + if (m_spellProto->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE) + { + Item* castItem = m_castItemGuid ? ((Player*)caster)->GetItemByGuid(m_castItemGuid) : NULL; + ((Player*)caster)->AddSpellAndCategoryCooldowns(m_spellProto,castItem ? castItem->GetEntry() : 0, NULL,true); + } + } + + // passive auras (except totem auras) do not get placed in the slots + // area auras with SPELL_AURA_NONE are not shown on target + if((m_spellProto->Attributes & 0x80 && GetTalentSpellPos(GetId())) + || !m_isPassive + || (caster && caster->GetTypeId() == TYPEID_UNIT && ((Creature*)caster)->isTotem()) + || (IsAuraType(SPELL_AURA_ABILITY_IGNORE_AURASTATE)) + && (!IsAreaAura() + || m_target!=caster || + (m_spellProto->Effect[0]!=SPELL_EFFECT_APPLY_AREA_AURA_ENEMY + && m_spellProto->Effect[1]!=SPELL_EFFECT_APPLY_AREA_AURA_ENEMY + && m_spellProto->Effect[2]!=SPELL_EFFECT_APPLY_AREA_AURA_ENEMY))) + { + // Try find slot for aura + uint8 slot = MAX_AURAS; + // Lookup for auras already applied from spell + if (Aura * foundAura = m_target->GetAura(GetId(), GetCasterGUID())) { // allow use single slot only by auras from same caster - if(itr->second->GetCasterGUID()==GetCasterGUID()) + slot = foundAura->GetAuraSlot(); + } + else + { + Unit::VisibleAuraMap const * visibleAuras= m_target->GetVisibleAuras(); + // lookup for free slots in units visibleAuras + Unit::VisibleAuraMap::const_iterator itr = visibleAuras->find(0); + for(uint32 freeSlot = 0; freeSlot < MAX_AURAS; ++itr , ++freeSlot) { - secondaura = true; - slot = itr->second->GetAuraSlot(); - break; + if(itr == visibleAuras->end() || itr->first != freeSlot) + { + slot = freeSlot; + break; + } } } - if(secondaura) - break; + // Register Visible Aura + if(slot < MAX_AURAS) + { + m_auraFlags |= (IsPositive() ? AFLAG_POSITIVE : AFLAG_NEGATIVE) | + (GetCasterGUID() == m_target->GetGUID() ? AFLAG_CASTER : AFLAG_NONE) | + (GetAuraMaxDuration() > 0 ? AFLAG_DURATION : AFLAG_NONE); + m_auraLevel = (caster ? caster->getLevel() : sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)); + SetAuraSlot( slot ); + m_target->SetVisibleAura(slot, this); + m_target->UpdateAuraForGroup(slot); + SendAuraUpdate(); + sLog.outDebug("Aura: %u Effect: %d put to unit visible auras slot: %u",GetId(), GetEffectMask(), slot); + } + else + sLog.outDebug("Aura: %u Effect: %d could not find empty unit visible slot",GetId(), GetEffectMask()); } - Unit* caster = GetCaster(); + // Sitdown on apply aura req seated + if (m_spellProto->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED && !m_target->IsSitState()) + m_target->SetStandState(UNIT_STAND_STATE_SIT); - // not call total regen auras at adding - switch (m_modifier.m_auraname) - { - /*case SPELL_AURA_PERIODIC_DAMAGE: - case SPELL_AURA_PERIODIC_LEECH: - if(caster) - m_modifier.m_amount = caster->SpellDamageBonus(m_target, m_spellProto, m_modifier.m_amount, DOT); - break; - case SPELL_AURA_PERIODIC_HEAL: - if(caster) - m_modifier.m_amount = caster->SpellHealingBonus(m_spellProto, m_modifier.m_amount, DOT, m_target); - break;*/ - case SPELL_AURA_OBS_MOD_HEALTH: - case SPELL_AURA_OBS_MOD_MANA: - m_periodicTimer = m_modifier.periodictime; - break; - case SPELL_AURA_MOD_REGEN: - case SPELL_AURA_MOD_POWER_REGEN: - case SPELL_AURA_MOD_MANA_REGEN_FROM_STAT: - m_periodicTimer = 5000; - break; - } - - // register aura + // register aura diminishing on apply if (getDiminishGroup() != DIMINISHING_NONE ) m_target->ApplyDiminishingAura(getDiminishGroup(),true); - // passive auras (except totem auras) do not get placed in the slots - // area auras with SPELL_AURA_NONE are not shown on target - if((!m_isPassive || (caster && caster->GetTypeId() == TYPEID_UNIT && ((Creature*)caster)->isTotem())) && - (m_spellProto->Effect[GetEffIndex()] != SPELL_EFFECT_APPLY_AREA_AURA_ENEMY || m_target != caster)) + // Apply linked auras (On first aura apply) + uint32 id = GetId(); + if(spellmgr.GetSpellCustomAttr(id) & SPELL_ATTR_CU_LINK_AURA) { - if(!secondaura) // new slot need - { - if (IsPositive()) // empty positive slot - { - for (uint8 i = 0; i < MAX_POSITIVE_AURAS; i++) - { - if (m_target->GetUInt32Value((uint16)(UNIT_FIELD_AURA + i)) == 0) - { - slot = i; - break; - } - } - } - else // empty negative slot + if(const std::vector<int32> *spell_triggered = spellmgr.GetSpellLinked(id + SPELL_LINK_AURA)) + for(std::vector<int32>::const_iterator itr = spell_triggered->begin(); itr != spell_triggered->end(); ++itr) { - for (uint8 i = MAX_POSITIVE_AURAS; i < MAX_AURAS; i++) - { - if (m_target->GetUInt32Value((uint16)(UNIT_FIELD_AURA + i)) == 0) - { - slot = i; - break; - } - } + if(*itr < 0) + m_target->ApplySpellImmune(id, IMMUNITY_ID, -(*itr), m_target); + else if(Unit* caster = GetCaster()) + m_target->AddAura(*itr, m_target); } + } - SetAuraSlot( slot ); + //***************************************************** + // Update target aura state flag + //***************************************************** - // Not update fields for not first spell's aura, all data already in fields - if(slot < MAX_AURAS) // slot found - { - SetAura(slot, false); - SetAuraFlag(slot, true); - SetAuraLevel(slot,caster ? caster->getLevel() : sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)); - UpdateAuraCharges(); + // Update Seals information + if (IsSealSpell(m_spellProto)) + SetAuraState(AURA_STATE_JUDGEMENT); - // update for out of range group members - m_target->UpdateAuraForGroup(slot); - } - } - else // use found slot - { - SetAuraSlot( slot ); - } + // Conflagrate aura state on Immolate + if (m_spellProto->SpellFamilyName == SPELLFAMILY_WARLOCK && m_spellProto->SpellFamilyFlags[0] & 4) + SetAuraState(AURA_STATE_IMMOLATE); + + // Faerie Fire (druid versions) + if (m_spellProto->SpellFamilyName == SPELLFAMILY_DRUID && m_spellProto->SpellFamilyFlags[0] & 0x400) + SetAuraState(AURA_STATE_FAERIE_FIRE); - UpdateSlotCounterAndDuration(); + // Victorious + if (m_spellProto->SpellFamilyName == SPELLFAMILY_WARRIOR && m_spellProto->SpellFamilyFlags[1] & 0x00040000) + SetAuraState(AURA_STATE_WARRIOR_VICTORY_RUSH); - // Update Seals information - if( IsSealSpell(GetSpellProto()) ) - m_target->ModifyAuraState(AURA_STATE_JUDGEMENT, true); + // Swiftmend state on Regrowth & Rejuvenation + if (m_spellProto->SpellFamilyName == SPELLFAMILY_DRUID && m_spellProto->SpellFamilyFlags[0] & 0x50 ) + SetAuraState(AURA_STATE_SWIFTMEND); - // Conflagrate aura state - if (GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK && (GetSpellProto()->SpellFamilyFlags & 4)) - m_target->ModifyAuraState(AURA_STATE_IMMOLATE, true); + // Deadly poison aura state + if(m_spellProto->SpellFamilyName == SPELLFAMILY_ROGUE && m_spellProto->SpellFamilyFlags[0] & 0x10000) + SetAuraState(AURA_STATE_DEADLY_POISON); - if(GetSpellProto()->SpellFamilyName == SPELLFAMILY_DRUID - && (GetSpellProto()->SpellFamilyFlags == 0x40 || GetSpellProto()->SpellFamilyFlags == 0x10)) + // Enrage aura state + if(m_spellProto->Dispel == DISPEL_ENRAGE) + SetAuraState(AURA_STATE_ENRAGE); + + if(GetSpellSchoolMask(m_spellProto) & SPELL_SCHOOL_MASK_FROST) + { + for (uint8 i = 0;i<MAX_SPELL_EFFECTS;++i) { - m_target->ModifyAuraState(AURA_STATE_SWIFTMEND, true); + if (m_spellProto->EffectApplyAuraName[i]==SPELL_AURA_MOD_STUN + || m_spellProto->EffectApplyAuraName[i]==SPELL_AURA_MOD_ROOT) + { + SetAuraState(AURA_STATE_FROZEN); + break; + } } } + + m_target->ApplyModFlag(UNIT_FIELD_AURASTATE, GetAuraStateMask(), true); +} + +bool Aura::SetPartAura(AuraEffect* aurEff, uint8 effIndex) +{ + if (IsRemoved()) + return false; + if (m_auraFlags & (1<<effIndex)) + return false; + m_auraFlags |= 1<<effIndex; + m_partAuras[effIndex]=aurEff; + m_target->HandleAuraEffect(aurEff, true); + SendAuraUpdate(); + return true; } void Aura::_RemoveAura() { Unit* caster = GetCaster(); - if(caster && IsPersistent()) - { - DynamicObject *dynObj = caster->GetDynObject(GetId(), GetEffIndex()); - if (dynObj) - dynObj->RemoveAffected(m_target); - } - - // unregister aura - if (getDiminishGroup() != DIMINISHING_NONE ) - m_target->ApplyDiminishingAura(getDiminishGroup(),false); - //passive auras do not get put in slots // Note: but totem can be not accessible for aura target in time remove (to far for find in grid) //if(m_isPassive && !(caster && caster->GetTypeId() == TYPEID_UNIT && ((Creature*)caster)->isTotem())) @@ -934,185 +1088,424 @@ void Aura::_RemoveAura() uint8 slot = GetAuraSlot(); - if(slot >= MAX_AURAS) // slot not set - return; + if (Aura * foundAura = m_target->GetAura(GetId(), GetCasterGUID())) + { + // allow use single slot only by auras from same caster + slot = foundAura->GetAuraSlot(); + if(slot < MAX_AURAS) // slot not set + if (Aura *entry = m_target->GetVisibleAura(slot)) + { + // set not valid slot for aura - prevent removing other visible aura + slot = MAX_AURAS; + } + } - if(m_target->GetUInt32Value((uint16)(UNIT_FIELD_AURA + slot)) == 0) - return; + // update for out of range group members + if (slot < MAX_AURAS) + { + m_target->RemoveVisibleAura(slot); + m_target->UpdateAuraForGroup(slot); + SendAuraUpdate(); + } - bool samespell = false; + // unregister aura diminishing (and store last time) + if (getDiminishGroup() != DIMINISHING_NONE ) + m_target->ApplyDiminishingAura(getDiminishGroup(),false); - // find other aura in same slot (current already removed from list) - for(uint8 i = 0; i < 3; i++) + // Check needed only if aura applies aurastate + if(GetAuraStateMask()) { - Unit::spellEffectPair spair = Unit::spellEffectPair(GetId(), i); - for(Unit::AuraMap::const_iterator itr = m_target->GetAuras().lower_bound(spair); itr != m_target->GetAuras().upper_bound(spair); ++itr) + uint32 foundMask = 0; + Unit::AuraMap& Auras = m_target->GetAuras(); + // Get mask of all aurastates from remaining auras + for(Unit::AuraMap::iterator i = Auras.begin(); i != Auras.end(); ++i) { - if(itr->second->GetAuraSlot()==slot) + foundMask|=(*i).second->GetAuraStateMask(); + } + // Remove only aurastates which were not found + foundMask = GetAuraStateMask() &~foundMask; + if (foundMask) + m_target->ApplyModFlag(UNIT_FIELD_AURASTATE, foundMask, false); + } + + // since now aura cannot apply/remove it's modifiers + m_isRemoved = true; + // disable client server communication for removed aura + SetAuraSlot(MAX_AURAS); + + // reset cooldown state for spells + if(caster && caster->GetTypeId() == TYPEID_PLAYER) + { + if ( GetSpellProto()->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE ) + // note: item based cooldowns and cooldown spell mods with charges ignored (unknown existed cases) + ((Player*)caster)->SendCooldownEvent(GetSpellProto()); + } + uint32 id = GetId(); + // Remove Linked Auras + if(spellmgr.GetSpellCustomAttr(id) & SPELL_ATTR_CU_LINK_REMOVE) + { + if(const std::vector<int32> *spell_triggered = spellmgr.GetSpellLinked(-(int32)id)) + for(std::vector<int32>::const_iterator itr = spell_triggered->begin(); itr != spell_triggered->end(); ++itr) { - samespell = true; + if(*itr < 0) + m_target->RemoveAurasDueToSpell(-(*itr)); + else if(Unit* caster = GetCaster()) + if (m_removeMode==AURA_REMOVE_BY_EXPIRE) + m_target->CastSpell(m_target, *itr, true, 0, 0, caster->GetGUID()); + } + } + if(spellmgr.GetSpellCustomAttr(id) & SPELL_ATTR_CU_LINK_AURA) + { + if(const std::vector<int32> *spell_triggered = spellmgr.GetSpellLinked(id + SPELL_LINK_AURA)) + for(std::vector<int32>::const_iterator itr = spell_triggered->begin(); itr != spell_triggered->end(); ++itr) + { + if(*itr < 0) + m_target->ApplySpellImmune(id, IMMUNITY_ID, -(*itr), false); + else + m_target->RemoveAurasDueToSpell(*itr); + } + } - break; + // Proc on aura remove (only spell flags for now) + if (caster) + { + uint32 procEx=0; + if (m_removeMode == AURA_REMOVE_BY_ENEMY_SPELL) + procEx = PROC_EX_AURA_REMOVE_DESTROY; + else if (m_removeMode == AURA_REMOVE_BY_EXPIRE || m_removeMode == AURA_REMOVE_BY_CANCEL) + procEx = PROC_EX_AURA_REMOVE_EXPIRE; + if (procEx) + { + uint32 ProcCaster, ProcVictim; + if (IsPositiveSpell(GetId())) + { + ProcCaster = PROC_FLAG_SUCCESSFUL_POSITIVE_SPELL; + ProcVictim = PROC_FLAG_TAKEN_POSITIVE_SPELL; } + else + { + ProcCaster = PROC_FLAG_SUCCESSFUL_NEGATIVE_SPELL_HIT; + ProcVictim = PROC_FLAG_TAKEN_NEGATIVE_SPELL_HIT; + } + caster->ProcDamageAndSpell(m_target,ProcCaster, ProcVictim, procEx, m_procDamage, BASE_ATTACK, m_spellProto); } - if(samespell) - break; } +} - // only remove icon when the last aura of the spell is removed (current aura already removed from list) - if (!samespell) +void Aura::SetStackAmount(uint8 stackAmount) +{ + if (stackAmount != m_stackAmount) { - SetAura(slot, true); - SetAuraFlag(slot, false); - SetAuraLevel(slot,caster ? caster->getLevel() : sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)); - - SetAuraApplication(slot, 0); - // update for out of range group members - m_target->UpdateAuraForGroup(slot); - - if( IsSealSpell(GetSpellProto()) ) - m_target->ModifyAuraState(AURA_STATE_JUDGEMENT,false); - - // Conflagrate aura state - if (GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK && (GetSpellProto()->SpellFamilyFlags & 4)) - m_target->ModifyAuraState(AURA_STATE_IMMOLATE, false); - - // Swiftmend aura state - if(GetSpellProto()->SpellFamilyName == SPELLFAMILY_DRUID - && (GetSpellProto()->SpellFamilyFlags == 0x40 || GetSpellProto()->SpellFamilyFlags == 0x10)) + Unit *target = GetTarget(); + Unit *caster = GetCaster(); + if (target && caster) { - bool found = false; - Unit::AuraList const& RejorRegr = m_target->GetAurasByType(SPELL_AURA_PERIODIC_HEAL); - for(Unit::AuraList::const_iterator i = RejorRegr.begin(); i != RejorRegr.end(); ++i) + m_stackAmount = stackAmount; + for (uint8 i=0;i<MAX_SPELL_EFFECTS;++i) { - if((*i)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_DRUID - && ((*i)->GetSpellProto()->SpellFamilyFlags == 0x40 || (*i)->GetSpellProto()->SpellFamilyFlags == 0x10) ) + if (AuraEffect * part = GetPartAura(i)) { - found = true; - break; + int32 amount = m_stackAmount * caster->CalculateSpellDamage(m_spellProto, part->GetEffIndex(), part->GetBasePoints(), target); + // Reapply if amount change + if (amount!=part->GetAmount()) + { + bool Real = bool (part->m_spellmod); + // Auras which are applying spellmod should have removed spellmods for real + part->ApplyModifier(false,Real); + part->SetAmount(amount); + part->ApplyModifier(true, Real); + } } } - if(!found) - m_target->ModifyAuraState(AURA_STATE_SWIFTMEND, false); } + } + RefreshAura(); +} + +bool Aura::modStackAmount(int32 num) +{ + // Can`t mod + if (!m_spellProto->StackAmount) + return true; + + // Modify stack but limit it + int32 stackAmount = m_stackAmount + num; + if (stackAmount > m_spellProto->StackAmount) + stackAmount = m_spellProto->StackAmount; + else if (stackAmount <=0) // Last aura from stack removed + { + m_stackAmount = 0; + return true; // need remove aura + } + + // Update stack amount + SetStackAmount(stackAmount); + return false; +} + +void Aura::SetAuraDuration(int32 duration) +{ + m_duration = duration; + //if (duration<0) + //m_permanent=true; + //else + //m_permanent=false; + SendAuraUpdate(); +} - // reset cooldown state for spells - if(caster && caster->GetTypeId() == TYPEID_PLAYER) +void Aura::SetAuraCharges(uint8 charges) +{ + if (m_procCharges == charges) + return; + m_procCharges = charges; + SendAuraUpdate(); +} + +bool Aura::DropAuraCharge() +{ + if (m_procCharges == 0) + return false; + m_procCharges--; + SendAuraUpdate(); + // return true if last charge dropped + return m_procCharges == 0; +} + +bool Aura::IsPersistent() const +{ + for (uint8 i=0; i<MAX_SPELL_EFFECTS;++i) + { + if (m_partAuras[i]) { - if ( GetSpellProto()->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE ) - ((Player*)caster)->SendCooldownEvent(GetSpellProto()); + if (m_partAuras[i]->IsPersistent()) + return true; } } + return false; } -void Aura::SetAuraFlag(uint32 slot, bool add) +bool Aura::IsAreaAura() const { - uint32 index = slot / 4; - uint32 byte = (slot % 4) * 8; - uint32 val = m_target->GetUInt32Value(UNIT_FIELD_AURAFLAGS + index); - val &= ~((uint32)AFLAG_MASK << byte); - if(add) + for (uint8 i=0; i<MAX_SPELL_EFFECTS;++i) { - if (IsPositive()) - val |= ((uint32)AFLAG_POSITIVE << byte); - else - val |= ((uint32)AFLAG_NEGATIVE << byte); + if (m_partAuras[i]) + { + if (m_partAuras[i]->IsAreaAura()) + return true; + } } - m_target->SetUInt32Value(UNIT_FIELD_AURAFLAGS + index, val); + return false; } -void Aura::SetAuraLevel(uint32 slot,uint32 level) +bool Aura::IsAuraType(AuraType type) const { - uint32 index = slot / 4; - uint32 byte = (slot % 4) * 8; - uint32 val = m_target->GetUInt32Value(UNIT_FIELD_AURALEVELS + index); - val &= ~(0xFF << byte); - val |= (level << byte); - m_target->SetUInt32Value(UNIT_FIELD_AURALEVELS + index, val); + for (uint8 i=0; i<MAX_SPELL_EFFECTS;++i) + { + if (m_partAuras[i]) + { + if (m_partAuras[i]->GetAuraName()==type) + return true; + } + } + return false; } -void Aura::SetAuraApplication(uint32 slot, int8 count) +void Aura::SetLoadedState(uint64 caster_guid,int32 maxduration,int32 duration,int32 charges, uint8 stackamount, int32 * amount) { - uint32 index = slot / 4; - uint32 byte = (slot % 4) * 8; - uint32 val = m_target->GetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS + index); - val &= ~(0xFF << byte); - val |= ((uint8(count)) << byte); - m_target->SetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS + index, val); + m_caster_guid = caster_guid; + m_maxduration = maxduration; + m_duration = duration; + m_procCharges = charges; + m_stackAmount = stackamount; + for (uint8 i=0; i<MAX_SPELL_EFFECTS;++i) + if (m_partAuras[i]) + m_partAuras[i]->SetAmount(amount[i]); } -void Aura::UpdateSlotCounterAndDuration() +void AuraEffect::HandleShapeshiftBoosts(bool apply) { - uint8 slot = GetAuraSlot(); - if(slot >= MAX_AURAS) - return; + uint32 spellId = 0; + uint32 spellId2 = 0; + uint32 HotWSpellId = 0; + + switch(GetMiscValue()) + { + case FORM_CAT: + spellId = 3025; + HotWSpellId = 24900; + break; + case FORM_TREE: + spellId = 5420; + spellId2 = 34123; + break; + case FORM_TRAVEL: + spellId = 5419; + break; + case FORM_AQUA: + spellId = 5421; + break; + case FORM_BEAR: + spellId = 1178; + spellId2 = 21178; + HotWSpellId = 24899; + break; + case FORM_DIREBEAR: + spellId = 9635; + spellId2 = 21178; + HotWSpellId = 24899; + break; + case FORM_BATTLESTANCE: + spellId = 21156; + break; + case FORM_DEFENSIVESTANCE: + spellId = 7376; + break; + case FORM_BERSERKERSTANCE: + spellId = 7381; + break; + case FORM_MOONKIN: + spellId = 24905; + // aura from effect trigger spell + spellId2 = 24907; + break; + case FORM_FLIGHT: + spellId = 33948; + spellId2 = 34764; + break; + case FORM_FLIGHT_EPIC: + spellId = 40122; + spellId2 = 40121; + break; + case FORM_METAMORPHOSIS: + spellId = 54817; + spellId2 = 54879; + break; + case FORM_SPIRITOFREDEMPTION: + spellId = 27792; + spellId2 = 27795; // must be second, this important at aura remove to prevent to early iterator invalidation. + break; + case FORM_GHOSTWOLF: + case FORM_AMBIENT: + case FORM_GHOUL: + case FORM_SHADOW: + case FORM_STEALTH: + case FORM_CREATURECAT: + case FORM_CREATUREBEAR: + spellId = 0; + break; + } + + uint32 form = GetMiscValue()-1; + + if(apply) + { + if (spellId) m_target->CastSpell(m_target, spellId, true, NULL, this ); + if (spellId2) m_target->CastSpell(m_target, spellId2, true, NULL, this); + + if(m_target->GetTypeId() == TYPEID_PLAYER) + { + const PlayerSpellMap& sp_list = ((Player *)m_target)->GetSpellMap(); + for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr) + { + if(itr->second->state == PLAYERSPELL_REMOVED) continue; + if(itr->first==spellId || itr->first==spellId2) continue; + SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first); + if (!spellInfo || !(spellInfo->Attributes & (SPELL_ATTR_PASSIVE | (1<<7)))) continue; + if (spellInfo->Stances & (1<<form)) + m_target->CastSpell(m_target, itr->first, true, NULL, this); + } + //LotP + if (((Player*)m_target)->HasSpell(17007)) + { + SpellEntry const *spellInfo = sSpellStore.LookupEntry(24932); + if (spellInfo && spellInfo->Stances & (1<<form)) + m_target->CastSpell(m_target, 24932, true, NULL, this); + } + // HotW + if (HotWSpellId) + { + Unit::AuraEffectList const& mModTotalStatPct = m_target->GetAurasByType(SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE); + for(Unit::AuraEffectList::const_iterator i = mModTotalStatPct.begin(); i != mModTotalStatPct.end(); ++i) + { + if ((*i)->GetSpellProto()->SpellIconID == 240 && (*i)->GetMiscValue() == 3) + { + int32 HotWMod = (*i)->GetAmount(); + if(GetMiscValue() == FORM_CAT) + HotWMod /= 2; - // Three possibilities: - // Charge = 0; Stack >= 0 - // Charge = 1; Stack >= 0 - // Charge > 1; Stack = 0 - if(m_procCharges < 2) - SetAuraApplication(slot, m_stackAmount-1); + m_target->CastCustomSpell(m_target, HotWSpellId, &HotWMod, NULL, NULL, true, NULL, this); + break; + } + } + } + } + } + else + { + m_target->RemoveAurasDueToSpell(spellId); + m_target->RemoveAurasDueToSpell(spellId2); - UpdateAuraDuration(); + Unit::AuraMap& tAuras = m_target->GetAuras(); + for (Unit::AuraMap::iterator itr = tAuras.begin(); itr != tAuras.end();) + { + if (itr->second->IsRemovedOnShapeLost()) + { + m_target->RemoveAura(itr); + } + else + { + ++itr; + } + } + } +} + +bool AuraEffect::isAffectedOnSpell(SpellEntry const *spell) const +{ + if (!spell) + return false; + // Check family name + if (spell->SpellFamilyName != m_spellProto->SpellFamilyName) + return false; + // Check EffectClassMask + if (m_spellProto->EffectSpellClassMask[m_effIndex] & spell->SpellFamilyFlags) + return true; + return false; } /*********************************************************/ /*** BASIC AURA FUNCTION ***/ /*********************************************************/ -void Aura::HandleAddModifier(bool apply, bool Real) +void AuraEffect::HandleAddModifier(bool apply, bool Real) { if(m_target->GetTypeId() != TYPEID_PLAYER || !Real) return; - SpellEntry const *spellInfo = GetSpellProto(); - if(!spellInfo) - return; - - if(m_modifier.m_miscvalue >= MAX_SPELLMOD) + if(GetMiscValue() >= MAX_SPELLMOD) return; if (apply) { - // Add custom charges for some mod aura - switch (m_spellProto->Id) - { - case 17941: // Shadow Trance - case 22008: // Netherwind Focus - case 34936: // Backlash - m_procCharges = 1; - break; - } SpellModifier *mod = new SpellModifier; - mod->op = SpellModOp(m_modifier.m_miscvalue); - mod->value = GetModifierValue(); - mod->type = SpellModType(m_modifier.m_auraname); // SpellModType value == spell aura types + mod->op = SpellModOp(GetMiscValue()); + mod->value = m_amount; + mod->type = SpellModType(m_auraName); // SpellModType value == spell aura types mod->spellId = GetId(); - mod->effectId = m_effIndex; - mod->lastAffected = NULL; - - uint64 spellAffectMask = spellmgr.GetSpellAffectMask(GetId(), m_effIndex); - - if (spellAffectMask) - mod->mask = spellAffectMask; - else - mod->mask = spellInfo->EffectItemType[m_effIndex]; - if (m_procCharges > 0) - mod->charges = m_procCharges; - else - mod->charges = 0; + flag96 const *spellAffect = spellmgr.GetSpellAffect(GetId(), m_effIndex); + if (!spellAffect) + spellAffect = &m_spellProto->EffectSpellClassMask[m_effIndex]; + mod->mask = *spellAffect; + mod->charges = GetParentAura()->GetAuraCharges(); m_spellmod = mod; } - uint64 spellFamilyMask = m_spellmod->mask; - ((Player*)m_target)->AddSpellMod(m_spellmod, apply); // reapply some passive spells after add/remove related spellmods - if(spellInfo->SpellFamilyName==SPELLFAMILY_WARRIOR && (spellFamilyMask & 0x0000100000000000LL)) + if(m_spellProto->SpellFamilyName==SPELLFAMILY_WARRIOR && (m_spellmod->mask[1] & 0x00001000)) { m_target->RemoveAurasDueToSpell(45471); @@ -1120,8 +1513,31 @@ void Aura::HandleAddModifier(bool apply, bool Real) m_target->CastSpell(m_target,45471,true); } } +void AuraEffect::HandleAddTargetTrigger(bool apply, bool Real) +{ + // Use SpellModifier structure for check + // used only fields: + // spellId, mask, mask2 + if (apply) + { + SpellModifier *mod = new SpellModifier; + mod->spellId = GetId(); + + flag96 const *spellAffect = spellmgr.GetSpellAffect(GetId(), m_effIndex); + if (!spellAffect) + spellAffect = &m_spellProto->EffectSpellClassMask[m_effIndex]; + + mod->mask = *spellAffect; + m_spellmod = mod; + } + else + { + delete m_spellmod; + m_spellmod = NULL; + } +} -void Aura::TriggerSpell() +void AuraEffect::TriggerSpell() { Unit* caster = GetCaster(); Unit* target = GetTriggerTarget(); @@ -1132,8 +1548,6 @@ void Aura::TriggerSpell() // generic casting code with custom spells and target/caster customs uint32 trigger_spell_id = GetSpellProto()->EffectTriggerSpell[m_effIndex]; - uint64 originalCasterGUID = GetCasterGUID(); - SpellEntry const *triggeredSpellInfo = sSpellStore.LookupEntry(trigger_spell_id); SpellEntry const *auraSpellInfo = GetSpellProto(); uint32 auraId = auraSpellInfo->Id; @@ -1172,7 +1586,7 @@ void Aura::TriggerSpell() } // remove old enchanting before applying new ((Player*)caster)->ApplyEnchantment(item,TEMP_ENCHANTMENT_SLOT,false); - item->SetEnchantment(TEMP_ENCHANTMENT_SLOT, enchant_id, m_modifier.periodictime+1000, 0); + item->SetEnchantment(TEMP_ENCHANTMENT_SLOT, enchant_id, m_amplitude+1000, 0); // add new enchanting ((Player*)caster)->ApplyEnchantment(item,TEMP_ENCHANTMENT_SLOT,true); return; @@ -1228,7 +1642,7 @@ void Aura::TriggerSpell() caster->ModifyPower( POWER_MANA, mana ); caster->SendEnergizeSpellLog(caster, 23493, mana, POWER_MANA); } - break; + return; } // // Stoneclaw Totem Passive TEST // case 23792: break; @@ -1285,8 +1699,13 @@ void Aura::TriggerSpell() } break; // // Steam Tank Passive // case 27747: break; -// // Frost Blast -// case 27808: break; + // Frost Blast + case 27808: + { + int32 bpDamage = target->GetMaxHealth()*26/100; + caster->CastCustomSpell(target,29879,&bpDamage,NULL,NULL,true,NULL,this); + return; + } // // Detonate Mana // case 27819: break; // // Controller Timer @@ -1307,8 +1726,11 @@ void Aura::TriggerSpell() // case 28522: break; // // Silithyst // case 29519: break; -// // Inoculate Nestlewood Owlkin - case 29528: trigger_spell_id = 28713; break; + // Inoculate Nestlewood Owlkin + case 29528: + if(target->GetTypeId()!=TYPEID_UNIT)// prevent error reports in case ignored player target + return; + break; // // Overload // case 29768: break; // // Return Fire @@ -1341,32 +1763,15 @@ void Aura::TriggerSpell() Creature* creature = (Creature*)target; // missing lootid has been reported on startup - just return if (!creature->GetCreatureInfo()->SkinLootId) - { return; - } - Loot *loot = &creature->loot; - loot->clear(); - loot->FillLoot(creature->GetCreatureInfo()->SkinLootId, LootTemplates_Skinning, NULL); - for(uint8 i=0;i<loot->items.size();i++) - { - LootItem *item = loot->LootItemInSlot(i,player); - ItemPosCountVec dest; - uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item->itemid, item->count ); - if ( msg == EQUIP_ERR_OK ) - { - Item * newitem = player->StoreNewItem( dest, item->itemid, true, item->randomPropertyId); - - player->SendNewItem(newitem, uint32(item->count), false, false, true); - } - else - player->SendEquipError( msg, NULL, NULL ); - } + + player->AutoStoreLoot(creature->GetCreatureInfo()->SkinLootId,LootTemplates_Skinning,true); + creature->setDeathState(JUST_DIED); creature->RemoveCorpse(); creature->SetHealth(0); // just for nice GM-mode view } return; - break; } // Quake case 30576: trigger_spell_id = 30571; break; @@ -1569,13 +1974,13 @@ void Aura::TriggerSpell() // Aura of Desire case 41350: { - Unit::AuraList const& mMod = m_target->GetAurasByType(SPELL_AURA_MOD_INCREASE_ENERGY_PERCENT); - for(Unit::AuraList::const_iterator i = mMod.begin(); i != mMod.end(); ++i) + Unit::AuraEffectList const& mMod = m_target->GetAurasByType(SPELL_AURA_MOD_INCREASE_ENERGY_PERCENT); + for(Unit::AuraEffectList::const_iterator i = mMod.begin(); i != mMod.end(); ++i) { if ((*i)->GetId() == 41350) { (*i)->ApplyModifier(false); - (*i)->GetModifier()->m_amount -= 5; + (*i)->SetAmount((*i)->GetAmount()-5); (*i)->ApplyModifier(true); break; } @@ -1616,11 +2021,8 @@ void Aura::TriggerSpell() { // Invisibility case 66: - { - if(!m_duration) - m_target->CastSpell(m_target, 32612, true, NULL, this); + // Here need periodic triger reducing threat spell (or do it manually) return; - } default: break; } @@ -1666,7 +2068,7 @@ void Aura::TriggerSpell() case 22896: case 26999: { - int32 LifePerRage = GetModifier()->m_amount; + int32 LifePerRage = GetAmount(); int32 lRage = m_target->GetPower(POWER_RAGE); if(lRage > 100) // rage stored as rage*10 @@ -1730,7 +2132,7 @@ void Aura::TriggerSpell() { SpellEntry const* spell = itr->second->GetSpellProto(); if( spell->SpellFamilyName == SPELLFAMILY_SHAMAN && - spell->SpellFamilyFlags & 0x0000000000000400L) + spell->SpellFamilyFlags[0] & 0x400) return; } target->RemoveAurasDueToSpell(28820); @@ -1740,9 +2142,9 @@ void Aura::TriggerSpell() case 38443: { bool all = true; - for(int i = 0; i < MAX_TOTEM; ++i) + for(int i = SUMMON_SLOT_TOTEM; i < MAX_TOTEM_SLOT; ++i) { - if(!caster->m_TotemSlot[i]) + if(!caster->m_SummonSlot[i]) { all = false; break; @@ -1763,13 +2165,9 @@ void Aura::TriggerSpell() default: break; } + // Reget trigger spell proto triggeredSpellInfo = sSpellStore.LookupEntry(trigger_spell_id); - if(triggeredSpellInfo == NULL) - { - sLog.outError("Aura::TriggerSpell: Spell %u have 0 in EffectTriggered[%d], not handled custom case?",GetId(),GetEffIndex()); - return; - } } else { @@ -1786,22 +2184,22 @@ void Aura::TriggerSpell() // 2) maybe aura must be replace by new with accumulative stat mods instead stacking // prevent cast by triggered auras - if(m_caster_guid == m_target->GetGUID()) + if(GetCasterGUID() == m_target->GetGUID()) return; // stop triggering after each affected stats lost > 90 int32 intellectLoss = 0; int32 spiritLoss = 0; - Unit::AuraList const& mModStat = m_target->GetAurasByType(SPELL_AURA_MOD_STAT); - for(Unit::AuraList::const_iterator i = mModStat.begin(); i != mModStat.end(); ++i) + Unit::AuraEffectList const& mModStat = m_target->GetAurasByType(SPELL_AURA_MOD_STAT); + for(Unit::AuraEffectList::const_iterator i = mModStat.begin(); i != mModStat.end(); ++i) { if ((*i)->GetId() == 1010) { - switch((*i)->GetModifier()->m_miscvalue) + switch((*i)->GetMiscValue()) { - case STAT_INTELLECT: intellectLoss += (*i)->GetModifierValue(); break; - case STAT_SPIRIT: spiritLoss += (*i)->GetModifierValue(); break; + case STAT_INTELLECT: intellectLoss += (*i)->GetAmount(); break; + case STAT_SPIRIT: spiritLoss += (*i)->GetAmount(); break; default: break; } } @@ -1811,36 +2209,53 @@ void Aura::TriggerSpell() return; caster = target; - originalCasterGUID = 0; break; } // Mana Tide case 16191: { - caster->CastCustomSpell(target, trigger_spell_id, &m_modifier.m_amount, NULL, NULL, true, NULL, this, originalCasterGUID); + caster->CastCustomSpell(target, trigger_spell_id, &m_amount, NULL, NULL, true, NULL, this); return; } } } - if(!GetSpellMaxRange(sSpellRangeStore.LookupEntry(triggeredSpellInfo->rangeIndex))) - target = m_target; //for druid dispel poison - m_target->CastSpell(target, triggeredSpellInfo, true, 0, this, originalCasterGUID); + + if(triggeredSpellInfo) + { + if(!caster->GetSpellMaxRangeForTarget(m_target,sSpellRangeStore.LookupEntry(triggeredSpellInfo->rangeIndex))) + target = m_target; //for druid dispel poison + m_target->CastSpell(target, triggeredSpellInfo, true, 0, this, GetCasterGUID()); + } + else if(target->GetTypeId()!=TYPEID_UNIT || !Script->EffectDummyCreature(caster, GetId(), GetEffIndex(), (Creature*)target)) + sLog.outError("AuraEffect::TriggerSpell: Spell %u have 0 in EffectTriggered[%d], not handled custom case?",GetId(),GetEffIndex()); } -Unit* Aura::GetTriggerTarget() const +Unit* AuraEffect::GetTriggerTarget() const { - Unit* target = ObjectAccessor::GetUnit(*m_target, - /*m_target->GetTypeId()==TYPEID_PLAYER ? - ((Player*)m_target)->GetSelection() :*/ - m_target->GetUInt64Value(UNIT_FIELD_TARGET)); + Unit* target = ObjectAccessor::GetUnit(*m_target, m_target->GetUInt64Value(UNIT_FIELD_TARGET)); return target ? target : m_target; } +void AuraEffect::TriggerSpellWithValue() +{ + Unit* caster = GetCaster(); + Unit* target = GetTriggerTarget(); + + if(!caster || !target) + return; + + // generic casting code with custom spells and target/caster customs + uint32 trigger_spell_id = GetSpellProto()->EffectTriggerSpell[m_effIndex]; + int32 basepoints0 = this->GetAmount(); + + caster->CastCustomSpell(target, trigger_spell_id, &basepoints0, 0, 0, true, 0, this); +} + /*********************************************************/ /*** AURA EFFECTS ***/ /*********************************************************/ -void Aura::HandleAuraDummy(bool apply, bool Real) +void AuraEffect::HandleAuraDummy(bool apply, bool Real) { // spells required only Real aura add/remove if(!Real) @@ -1853,6 +2268,12 @@ void Aura::HandleAuraDummy(bool apply, bool Real) { switch(GetId()) { + // Haunting Spirits - perdiodic trigger demon + case 7057: + m_isPeriodic = true; + m_amplitude = irand (0, 60) + 30; + m_amplitude *= IN_MILISECONDS; + return; case 1515: // Tame beast // FIX_ME: this is 2.0.12 threat effect replaced in 2.1.x by dummy aura, must be checked for correctness if( caster && m_target->CanHaveThreatList()) @@ -1863,13 +2284,50 @@ void Aura::HandleAuraDummy(bool apply, bool Real) if(caster) caster->CastSpell(caster,13138,true,NULL,this); return; + case 34026: // kill command + { + Unit * pet = m_target->GetGuardianPet(); + if (!pet) + return; + + m_target->CastSpell(m_target,34027,true,NULL,this); + + // set 3 stacks and 3 charges (to make all auras not disappear at once) + Aura* owner_aura = m_target->GetAura(34027,GetCasterGUID()); + Aura* pet_aura = pet->GetAura(58914, GetCasterGUID()); + if( owner_aura ) + { + owner_aura->SetStackAmount(owner_aura->GetSpellProto()->StackAmount); + } + if( pet_aura ) + { + pet_aura->SetAuraCharges(0); + pet_aura->SetStackAmount(owner_aura->GetSpellProto()->StackAmount); + } + return; + } + case 55198: // Tidal Force + { + m_target->CastSpell(m_target,55166,true,NULL,this); + // set 3 stacks and 3 charges (to make all auras not disappear at once) + Aura* owner_aura = m_target->GetAura(55166,GetCasterGUID()); + if( owner_aura ) + { + // This aura lasts 2 sec, need this hack to properly proc spells + // TODO: drop aura charges for ApplySpellMod in ProcDamageAndSpell + GetParentAura()->SetAuraDuration(owner_aura->GetAuraDuration()); + // Make aura be not charged-this prevents removing charge on not crit spells + owner_aura->SetAuraCharges(0); + owner_aura->SetStackAmount(owner_aura->GetSpellProto()->StackAmount); + } + return; + } case 39850: // Rocket Blast if(roll_chance_i(20)) // backfire stun m_target->CastSpell(m_target, 51581, true, NULL, this); return; case 43873: // Headless Horseman Laugh - if(caster->GetTypeId() == TYPEID_PLAYER) - ((Player*)caster)->SendPlaySound(11965, false); + m_target->PlayDistanceSound(11965); return; case 46354: // Blood Elf Illusion if(caster) @@ -1894,24 +2352,26 @@ void Aura::HandleAuraDummy(bool apply, bool Real) } // Earth Shield - if ( caster && GetSpellProto()->SpellFamilyName == SPELLFAMILY_SHAMAN && (GetSpellProto()->SpellFamilyFlags & 0x40000000000LL)) + if ( caster && GetSpellProto()->SpellFamilyName == SPELLFAMILY_SHAMAN && (GetSpellProto()->SpellFamilyFlags[1] & 0x400)) { // prevent double apply bonuses if(m_target->GetTypeId()!=TYPEID_PLAYER || !((Player*)m_target)->GetSession()->PlayerLoading()) - m_modifier.m_amount = caster->SpellHealingBonus(GetSpellProto(), m_modifier.m_amount, SPELL_DIRECT_DAMAGE, m_target); + m_amount = caster->SpellHealingBonus(m_target, GetSpellProto(), m_amount, SPELL_DIRECT_DAMAGE); return; } + //Druid, Survival Instincts + if(GetSpellProto()->SpellFamilyName==SPELLFAMILY_DRUID && GetSpellProto()->SpellFamilyFlags[2]& 0x40 ) + { + if(!m_target) + return; + + int32 bp0 = int32(m_target->GetMaxHealth() * m_amount / 100); + m_target->CastCustomSpell(m_target, 50322, &bp0, NULL, NULL, true); + } } // AT REMOVE else { - if( m_target->GetTypeId() == TYPEID_PLAYER && GetSpellProto()->Effect[0]==72 ) - { - // spells with SpellEffect=72 and aura=4: 6196, 6197, 21171, 21425 - ((Player*)m_target)->ClearFarsight(); - return; - } - if( (IsQuestTameSpell(GetId())) && caster && caster->isAlive() && m_target->isAlive()) { uint32 finalSpelId = 0; @@ -1965,6 +2425,29 @@ void Aura::HandleAuraDummy(bool apply, bool Real) m_target->SetReducedThreatPercent(0, 0); return; } + + switch(m_spellProto->SpellFamilyName) + { + case SPELLFAMILY_WARLOCK: + // Haunt + if(m_spellProto->SpellFamilyFlags[1] & 0x40000) + { + int32 bp0 = GetParentAura()->GetProcDamage() * m_amount / 100; + if(caster) + caster->CastCustomSpell(caster, 48210, &bp0, 0, 0, true, NULL, this); + return; + } + break; + case SPELLFAMILY_MAGE: + // Living Bomb + if(m_spellProto->SpellFamilyFlags[1] & 0x20000) + { + if(caster && (GetParentAura()->GetRemoveMode() == AURA_REMOVE_BY_ENEMY_SPELL || GetParentAura()->GetRemoveMode() == AURA_REMOVE_BY_DEATH || GetParentAura()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE)) + caster->CastSpell(m_target, GetAmount(), true); + return; + } + break; + } } // AT APPLY & REMOVE @@ -2005,12 +2488,6 @@ void Aura::HandleAuraDummy(bool apply, bool Real) m_target->RemoveAurasDueToSpell(spellId); return; } - // Victorious - if(GetId()==32216 && m_target->getClass()==CLASS_WARRIOR) - { - m_target->ModifyAuraState(AURA_STATE_WARRIOR_VICTORY_RUSH, apply); - return; - } //Summon Fire Elemental if (GetId() == 40133 && caster) { @@ -2043,35 +2520,102 @@ void Aura::HandleAuraDummy(bool apply, bool Real) } case SPELLFAMILY_MAGE: { - // Hypothermia - if( GetId()==41425 ) + break; + } + case SPELLFAMILY_PRIEST: + { + // Pain and Suffering + if( m_spellProto->SpellIconID == 2874 && m_target->GetTypeId()==TYPEID_PLAYER ) { - m_target->ModifyAuraState(AURA_STATE_HYPOTHERMIA,apply); + if(apply) + { + // Reduce backfire damage (dot damage) from Shadow Word: Death + SpellModifier *mod = new SpellModifier; + mod->op = SPELLMOD_DOT; + mod->value = m_amount; + mod->type = SPELLMOD_PCT; + mod->spellId = GetId(); + mod->mask[1] = 0x00002000; + m_spellmod = mod; + } + ((Player*)m_target)->AddSpellMod(m_spellmod, apply); return; } break; } case SPELLFAMILY_DRUID: { + switch(GetId()) + { + case 34246: // Idol of the Emerald Queen + { + if (m_target->GetTypeId() != TYPEID_PLAYER) + return; + + if(apply) + { + SpellModifier *mod = new SpellModifier; + mod->op = SPELLMOD_DOT; + mod->value = m_amount/7; + mod->type = SPELLMOD_FLAT; + mod->spellId = GetId(); + mod->mask[1] = 0x0010; + + m_spellmod = mod; + } + + ((Player*)m_target)->AddSpellMod(m_spellmod, apply); + return; + } + case 61336: // Survival Instincts + { + if(apply) + { + if (!m_target->IsInFeralForm()) + return; + + int32 bp0 = int32(m_target->GetMaxHealth() * m_amount / 100); + m_target->CastCustomSpell(m_target, 50322, &bp0, NULL, NULL, true); + } + else + m_target-> RemoveAurasDueToSpell(50322); + return; + } + } + // Lifebloom - if ( GetSpellProto()->SpellFamilyFlags & 0x1000000000LL ) + if ( GetSpellProto()->SpellFamilyFlags[1] & 0x10 ) { if ( apply ) { if ( caster ) // prevent double apply bonuses if(m_target->GetTypeId()!=TYPEID_PLAYER || !((Player*)m_target)->GetSession()->PlayerLoading()) - m_modifier.m_amount = caster->SpellHealingBonus(GetSpellProto(), m_modifier.m_amount, SPELL_DIRECT_DAMAGE, m_target); + m_amount = caster->SpellHealingBonus(m_target, GetSpellProto(), m_amount, SPELL_DIRECT_DAMAGE); } - // Do final heal for real !apply - else if (Real) + else { - if (GetAuraDuration() <= 0 || m_removeMode==AURA_REMOVE_BY_DISPEL) + // Final heal only on dispelled or duration end + if (GetParentAura()->GetRemoveMode() != AURA_REMOVE_BY_EXPIRE && GetParentAura()->GetRemoveMode() != AURA_REMOVE_BY_ENEMY_SPELL) + return; + + // final heal + if(m_target->IsInWorld()) + m_target->CastCustomSpell(m_target,33778,&m_amount,NULL,NULL,true,NULL,this,GetCasterGUID()); + + /*// have a look if there is still some other Lifebloom dummy aura + Unit::AuraList auras = m_target->GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraList::iterator itr = auras.begin(); itr!=auras.end(); ++itr) + if((*itr)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_DRUID && + (*itr)->GetSpellProto()->SpellFamilyFlags & 0x1000000000LL) + return; + + // final heal + if(m_target->IsInWorld() && m_stackAmount > 0) { - // final heal - if(m_target->IsInWorld()) - m_target->CastCustomSpell(m_target,33778,&m_modifier.m_amount,NULL,NULL,true,NULL,this,GetCasterGUID()); - } + int32 amount = m_amount / m_stackAmount; + m_target->CastCustomSpell(m_target,33778,&amount,NULL,NULL,true,NULL,this,GetCasterGUID()); + }*/ } return; } @@ -2082,20 +2626,22 @@ void Aura::HandleAuraDummy(bool apply, bool Real) ((Player*)m_target)->UpdateAttackPowerAndDamage(); return; } - // Idol of the Emerald Queen - if ( GetId() == 34246 && m_target->GetTypeId()==TYPEID_PLAYER ) + break; + } + case SPELLFAMILY_HUNTER: + { + // Improved Aspect of the Viper + if( GetId()==38390 && m_target->GetTypeId()==TYPEID_PLAYER ) { if(apply) { + // + effect value for Aspect of the Viper SpellModifier *mod = new SpellModifier; - mod->op = SPELLMOD_DOT; - mod->value = m_modifier.m_amount/7; + mod->op = SPELLMOD_EFFECT1; + mod->value = m_amount; mod->type = SPELLMOD_FLAT; mod->spellId = GetId(); - mod->effectId = m_effIndex; - mod->lastAffected = NULL; - mod->mask = 0x001000000000LL; - mod->charges = 0; + mod->mask[1] = 0x40000; m_spellmod = mod; } @@ -2103,29 +2649,22 @@ void Aura::HandleAuraDummy(bool apply, bool Real) ((Player*)m_target)->AddSpellMod(m_spellmod, apply); return; } - break; - } - case SPELLFAMILY_HUNTER: - { - // Improved Aspect of the Viper - if( GetId()==38390 && m_target->GetTypeId()==TYPEID_PLAYER ) + // Glyph of Aspect of the Monkey + if(m_spellProto->Id==56833) { if(apply) { - // + effect value for Aspect of the Viper + // Reduce backfire damage (dot damage) from Shadow Word: Death SpellModifier *mod = new SpellModifier; - mod->op = SPELLMOD_EFFECT1; - mod->value = m_modifier.m_amount; + mod->op = SPELLMOD_CHANCE_OF_SUCCESS; + mod->value = 100; mod->type = SPELLMOD_FLAT; mod->spellId = GetId(); - mod->effectId = m_effIndex; - mod->lastAffected = NULL; - mod->mask = 0x4000000000000LL; - mod->charges = 0; - + mod->mask[2] = 8192; + mod->mask[1] = 0x00000000; + mod->mask[0] = 524288; m_spellmod = mod; } - ((Player*)m_target)->AddSpellMod(m_spellmod, apply); return; } @@ -2140,21 +2679,18 @@ void Aura::HandleAuraDummy(bool apply, bool Real) { SpellModifier *mod = new SpellModifier; mod->op = SPELLMOD_EFFECT1; - mod->value = m_modifier.m_amount; + mod->value = m_amount; mod->type = SPELLMOD_PCT; mod->spellId = GetId(); - mod->effectId = m_effIndex; - mod->lastAffected = NULL; switch (m_effIndex) { case 0: - mod->mask = 0x00200000000LL; // Windfury Totem + mod->mask[1] = 0x002; // Windfury Totem break; case 1: - mod->mask = 0x00400000000LL; // Flametongue Totem + mod->mask[1] = 0x004; // Flametongue Totem break; } - mod->charges = 0; m_spellmod = mod; } @@ -2168,7 +2704,7 @@ void Aura::HandleAuraDummy(bool apply, bool Real) { if (apply) { - uint64 guid = caster->m_TotemSlot[3]; + uint64 guid = caster->m_SummonSlot[3]; if (guid) { Creature *totem = ObjectAccessor::GetCreature(*caster, guid); @@ -2193,45 +2729,32 @@ void Aura::HandleAuraDummy(bool apply, bool Real) m_target->RemovePetAura(petSpell); return; } -} - -void Aura::HandleAuraPeriodicDummy(bool apply, bool Real) -{ - // spells required only Real aura add/remove - if(!Real) - return; - SpellEntry const*spell = GetSpellProto(); - switch( spell->SpellFamilyName) + if(GetEffIndex()==0 && m_target->GetTypeId()==TYPEID_PLAYER) { - case SPELLFAMILY_ROGUE: + SpellAreaForAreaMapBounds saBounds = spellmgr.GetSpellAreaForAuraMapBounds(GetId()); + if(saBounds.first != saBounds.second) { - // Master of Subtlety - if (spell->Id==31666 && !apply && Real) - { - m_target->RemoveAurasDueToSpell(31665); - break; - } - break; - } - case SPELLFAMILY_HUNTER: - { - // Aspect of the Viper - if (spell->SpellFamilyFlags&0x0004000000000000LL) + uint32 zone, area; + m_target->GetZoneAndAreaId(zone,area); + + for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) { - // Update regen on remove - if (!apply && m_target->GetTypeId() == TYPEID_PLAYER) - ((Player*)m_target)->UpdateManaRegen(); - break; + // some auras remove at aura remove + if(!itr->second->IsFitToRequirements((Player*)m_target,zone,area)) + m_target->RemoveAurasDueToSpell(itr->second->spellId); + // some auras applied at aura apply + else if(itr->second->autocast) + { + if( !m_target->HasAuraEffect(itr->second->spellId,0) ) + m_target->CastSpell(m_target,itr->second->spellId,true); + } } - break; } } - - m_isPeriodic = apply; } -void Aura::HandleAuraMounted(bool apply, bool Real) +void AuraEffect::HandleAuraMounted(bool apply, bool Real) { // only at real add/remove aura if(!Real) @@ -2239,10 +2762,10 @@ void Aura::HandleAuraMounted(bool apply, bool Real) if(apply) { - CreatureInfo const* ci = objmgr.GetCreatureTemplate(m_modifier.m_miscvalue); + CreatureInfo const* ci = objmgr.GetCreatureTemplate(GetMiscValue()); if(!ci) { - sLog.outErrorDb("AuraMounted: `creature_template`='%u' not found in database (only need it modelid)", m_modifier.m_miscvalue); + sLog.outErrorDb("AuraMounted: `creature_template`='%u' not found in database (only need it modelid)",GetMiscValue()); return; } @@ -2263,7 +2786,7 @@ void Aura::HandleAuraMounted(bool apply, bool Real) } } -void Aura::HandleAuraWaterWalk(bool apply, bool Real) +void AuraEffect::HandleAuraWaterWalk(bool apply, bool Real) { // only at real add/remove aura if(!Real) @@ -2279,7 +2802,7 @@ void Aura::HandleAuraWaterWalk(bool apply, bool Real) m_target->SendMessageToSet(&data,true); } -void Aura::HandleAuraFeatherFall(bool apply, bool Real) +void AuraEffect::HandleAuraFeatherFall(bool apply, bool Real) { // only at real add/remove aura if(!Real) @@ -2295,7 +2818,7 @@ void Aura::HandleAuraFeatherFall(bool apply, bool Real) m_target->SendMessageToSet(&data,true); } -void Aura::HandleAuraHover(bool apply, bool Real) +void AuraEffect::HandleAuraHover(bool apply, bool Real) { // only at real add/remove aura if(!Real) @@ -2311,31 +2834,21 @@ void Aura::HandleAuraHover(bool apply, bool Real) m_target->SendMessageToSet(&data,true); } -void Aura::HandleWaterBreathing(bool apply, bool Real) +void AuraEffect::HandleWaterBreathing(bool apply, bool Real) { - if(apply) - m_target->waterbreath = true; - else if(m_target->GetAurasByType(SPELL_AURA_WATER_BREATHING).empty()) - { - m_target->waterbreath = false; - - // update for enable timer in case not moving target - if(m_target->GetTypeId()==TYPEID_PLAYER && m_target->IsInWorld()) - { - ((Player*)m_target)->UpdateUnderwaterState(m_target->GetMap(),m_target->GetPositionX(),m_target->GetPositionY(),m_target->GetPositionZ()); - ((Player*)m_target)->HandleDrowning(); - } - } + // update timers in client + if(m_target->GetTypeId()==TYPEID_PLAYER) + ((Player*)m_target)->UpdateMirrorTimers(); } -void Aura::HandleAuraModShapeshift(bool apply, bool Real) +void AuraEffect::HandleAuraModShapeshift(bool apply, bool Real) { if(!Real) return; uint32 modelid = 0; Powers PowerType = POWER_MANA; - ShapeshiftForm form = ShapeshiftForm(m_modifier.m_miscvalue); + ShapeshiftForm form = ShapeshiftForm(GetMiscValue()); switch(form) { case FORM_CAT: @@ -2396,6 +2909,9 @@ void Aura::HandleAuraModShapeshift(bool apply, bool Real) else modelid = 21244; break; + case FORM_METAMORPHOSIS: + modelid = 25277; + break; case FORM_AMBIENT: case FORM_SHADOW: case FORM_STEALTH: @@ -2412,7 +2928,7 @@ void Aura::HandleAuraModShapeshift(bool apply, bool Real) modelid = 16031; break; default: - sLog.outError("Auras: Unknown Shapeshift Type: %u", m_modifier.m_miscvalue); + sLog.outError("Auras: Unknown Shapeshift Type: %u", GetMiscValue()); } // remove polymorph before changing display id to keep new display id @@ -2428,8 +2944,8 @@ void Aura::HandleAuraModShapeshift(bool apply, bool Real) case FORM_FLIGHT: case FORM_MOONKIN: // remove movement affects - m_target->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT); - m_target->RemoveSpellsCausingAura(SPELL_AURA_MOD_DECREASE_SPEED); + m_target->RemoveAurasByType(SPELL_AURA_MOD_ROOT); + m_target->RemoveAurasByType(SPELL_AURA_MOD_DECREASE_SPEED); // and polymorphic affects if(m_target->IsPolymorphed()) @@ -2443,7 +2959,7 @@ void Aura::HandleAuraModShapeshift(bool apply, bool Real) { // remove other shapeshift before applying a new one if(m_target->m_ShapeShiftFormSpellId) - m_target->RemoveAurasDueToSpell(m_target->m_ShapeShiftFormSpellId,this); + m_target->RemoveAurasDueToSpell(m_target->m_ShapeShiftFormSpellId); m_target->SetByteValue(UNIT_FIELD_BYTES_2, 3, form); @@ -2464,17 +2980,17 @@ void Aura::HandleAuraModShapeshift(bool apply, bool Real) { // get furor proc chance uint32 FurorChance = 0; - Unit::AuraList const& mDummy = m_target->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraList::const_iterator i = mDummy.begin(); i != mDummy.end(); ++i) + Unit::AuraEffectList const& mDummy = m_target->GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraEffectList::const_iterator i = mDummy.begin(); i != mDummy.end(); ++i) { if ((*i)->GetSpellProto()->SpellIconID == 238) { - FurorChance = (*i)->GetModifier()->m_amount; + FurorChance = (*i)->GetAmount(); break; } } - if (m_modifier.m_miscvalue == FORM_CAT) + if (GetMiscValue() == FORM_CAT) { m_target->SetPower(POWER_ENERGY,0); if(urand(1,100) <= FurorChance) @@ -2535,14 +3051,14 @@ void Aura::HandleAuraModShapeshift(bool apply, bool Real) case FORM_DIREBEAR: case FORM_CAT: { - if(Aura* dummy = m_target->GetDummyAura(37315) ) + if(AuraEffect* dummy = m_target->GetDummyAura(37315) ) m_target->CastSpell(m_target,37316,true,NULL,dummy); break; } // Nordrassil Regalia - bonus case FORM_MOONKIN: { - if(Aura* dummy = m_target->GetDummyAura(37324) ) + if(AuraEffect* dummy = m_target->GetDummyAura(37324) ) m_target->CastSpell(m_target,37325,true,NULL,dummy); break; } @@ -2557,18 +3073,18 @@ void Aura::HandleAuraModShapeshift(bool apply, bool Real) ((Player*)m_target)->InitDataForForm(); } -void Aura::HandleAuraTransform(bool apply, bool Real) +void AuraEffect::HandleAuraTransform(bool apply, bool Real) { if (apply) { // special case (spell specific functionality) - if(m_modifier.m_miscvalue==0) + if(GetMiscValue()==0) { // player applied only - if(m_target->GetTypeId()!=TYPEID_PLAYER) + if (m_target->GetTypeId()!=TYPEID_PLAYER) return; - switch(GetId()) + switch (GetId()) { // Orb of Deception case 16739: @@ -2627,12 +3143,12 @@ void Aura::HandleAuraTransform(bool apply, bool Real) } else { - CreatureInfo const * ci = objmgr.GetCreatureTemplate(m_modifier.m_miscvalue); + CreatureInfo const * ci = objmgr.GetCreatureTemplate(GetMiscValue()); if(!ci) { //pig pink ^_^ m_target->SetDisplayId(16358); - sLog.outError("Auras: unknown creature id = %d (only need its modelid) Form Spell Aura Transform in Spell ID = %d", m_modifier.m_miscvalue, GetId()); + sLog.outError("Auras: unknown creature id = %d (only need its modelid) Form Spell Aura Transform in Spell ID = %d", GetMiscValue(), GetId()); } else { @@ -2644,38 +3160,41 @@ void Aura::HandleAuraTransform(bool apply, bool Real) if(GetId()==42016 && m_target->GetMountID() && !m_target->GetAurasByType(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED).empty()) m_target->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID,16314); } - m_target->setTransForm(GetId()); } + // update active transform spell only not set or not overwriting negative by positive case + if (!m_target->getTransForm() || !IsPositiveSpell(GetId()) || IsPositiveSpell(m_target->getTransForm())) + m_target->setTransForm(GetId()); + // polymorph case - if( Real && m_target->GetTypeId() == TYPEID_PLAYER && m_target->IsPolymorphed()) + if (Real && m_target->GetTypeId() == TYPEID_PLAYER && m_target->IsPolymorphed()) { // for players, start regeneration after 1s (in polymorph fast regeneration case) // only if caster is Player (after patch 2.4.2) - if(IS_PLAYER_GUID(GetCasterGUID()) ) - ((Player*)m_target)->setRegenTimer(1000); + if (IS_PLAYER_GUID(GetCasterGUID()) ) + ((Player*)m_target)->setRegenTimer(1*IN_MILISECONDS); //dismount polymorphed target (after patch 2.4.2) if (m_target->IsMounted()) - m_target->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); + m_target->RemoveAurasByType(SPELL_AURA_MOUNTED); } } else { - Unit::AuraList const& otherTransforms = m_target->GetAurasByType(SPELL_AURA_TRANSFORM); - if(otherTransforms.empty()) - { - m_target->SetDisplayId(m_target->GetNativeDisplayId()); - m_target->setTransForm(0); - } - else + // ApplyModifier(true) will reapply it if need + m_target->setTransForm(0); + m_target->SetDisplayId(m_target->GetNativeDisplayId()); + + // re-aplly some from still active with preference negative cases + Unit::AuraEffectList const& otherTransforms = m_target->GetAurasByType(SPELL_AURA_TRANSFORM); + if (!otherTransforms.empty()) { // look for other transform auras - Aura* handledAura = *otherTransforms.begin(); - for(Unit::AuraList::const_iterator i = otherTransforms.begin();i != otherTransforms.end(); ++i) + AuraEffect* handledAura = *otherTransforms.begin(); + for(Unit::AuraEffectList::const_iterator i = otherTransforms.begin();i != otherTransforms.end(); ++i) { // negative auras are preferred - if(!IsPositiveSpell((*i)->GetSpellProto()->Id)) + if (!IsPositiveSpell((*i)->GetSpellProto()->Id)) { handledAura = *i; break; @@ -2685,11 +3204,11 @@ void Aura::HandleAuraTransform(bool apply, bool Real) } // Dragonmaw Illusion (restore mount model) - if(GetId()==42016 && m_target->GetMountID()==16314) + if (GetId()==42016 && m_target->GetMountID()==16314) { - if(!m_target->GetAurasByType(SPELL_AURA_MOUNTED).empty()) + if (!m_target->GetAurasByType(SPELL_AURA_MOUNTED).empty()) { - uint32 cr_id = m_target->GetAurasByType(SPELL_AURA_MOUNTED).front()->GetModifier()->m_miscvalue; + uint32 cr_id = m_target->GetAurasByType(SPELL_AURA_MOUNTED).front()->GetMiscValue(); if(CreatureInfo const* ci = objmgr.GetCreatureTemplate(cr_id)) { uint32 team = 0; @@ -2708,7 +3227,7 @@ void Aura::HandleAuraTransform(bool apply, bool Real) } } -void Aura::HandleForceReaction(bool apply, bool Real) +void AuraEffect::HandleForceReaction(bool apply, bool Real) { if(m_target->GetTypeId() != TYPEID_PLAYER) return; @@ -2718,44 +3237,32 @@ void Aura::HandleForceReaction(bool apply, bool Real) Player* player = (Player*)m_target; - uint32 faction_id = m_modifier.m_miscvalue; - uint32 faction_rank = m_modifier.m_amount; + uint32 faction_id = GetMiscValue(); + uint32 faction_rank = m_amount; - if(apply) - player->m_forcedReactions[faction_id] = ReputationRank(faction_rank); - else - player->m_forcedReactions.erase(faction_id); - - WorldPacket data; - data.Initialize(SMSG_SET_FORCED_REACTIONS, 4+player->m_forcedReactions.size()*(4+4)); - data << uint32(player->m_forcedReactions.size()); - for(ForcedReactions::const_iterator itr = player->m_forcedReactions.begin(); itr != player->m_forcedReactions.end(); ++itr) - { - data << uint32(itr->first); // faction_id (Faction.dbc) - data << uint32(itr->second); // reputation rank - } - player->SendDirectMessage(&data); + player->GetReputationMgr().ApplyForceReaction(faction_id,ReputationRank(faction_rank),apply); + player->GetReputationMgr().SendForceReactions(); } -void Aura::HandleAuraModSkill(bool apply, bool Real) +void AuraEffect::HandleAuraModSkill(bool apply, bool Real) { if(m_target->GetTypeId() != TYPEID_PLAYER) return; uint32 prot=GetSpellProto()->EffectMiscValue[m_effIndex]; - int32 points = GetModifierValue(); + int32 points = m_amount; - ((Player*)m_target)->ModifySkillBonus(prot,(apply ? points: -points),m_modifier.m_auraname==SPELL_AURA_MOD_SKILL_TALENT); + ((Player*)m_target)->ModifySkillBonus(prot,(apply ? points: -points),m_auraName==SPELL_AURA_MOD_SKILL_TALENT); if(prot == SKILL_DEFENSE) ((Player*)m_target)->UpdateDefenseBonusesMod(); } -void Aura::HandleChannelDeathItem(bool apply, bool Real) +void AuraEffect::HandleChannelDeathItem(bool apply, bool Real) { if(Real && !apply) { Unit* caster = GetCaster(); - Unit* victim = GetTarget(); + Unit* victim = m_target; if(!caster || caster->GetTypeId() != TYPEID_PLAYER || !victim)// || m_removeMode!=AURA_REMOVE_BY_DEATH) return; @@ -2763,6 +3270,9 @@ void Aura::HandleChannelDeathItem(bool apply, bool Real) //talent will remove the caster's aura->interrupt channel->remove victim aura if(victim->GetHealth() > 0) return; + // Item amount + if (m_amount <= 0) + return; SpellEntry const *spellInfo = GetSpellProto(); if(spellInfo->EffectItemType[m_effIndex] == 0) @@ -2773,100 +3283,156 @@ void Aura::HandleChannelDeathItem(bool apply, bool Real) (victim->getLevel() <= Trinity::XP::GetGrayLevel(caster->getLevel()) || victim->GetTypeId()==TYPEID_UNIT && !((Player*)caster)->isAllowedToLoot((Creature*)victim)) ) return; + //Adding items + uint32 noSpaceForCount = 0; + uint32 count = m_amount; + ItemPosCountVec dest; - uint8 msg = ((Player*)caster)->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, spellInfo->EffectItemType[m_effIndex], 1 ); + uint8 msg = ((Player*)caster)->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, spellInfo->EffectItemType[m_effIndex], count, &noSpaceForCount); if( msg != EQUIP_ERR_OK ) { + count-=noSpaceForCount; ((Player*)caster)->SendEquipError( msg, NULL, NULL ); - return; + if (count==0) + return; } Item* newitem = ((Player*)caster)->StoreNewItem(dest, spellInfo->EffectItemType[m_effIndex], true); - ((Player*)caster)->SendNewItem(newitem, 1, true, false); + ((Player*)caster)->SendNewItem(newitem, count, true, false); } } -void Aura::HandleBindSight(bool apply, bool Real) +void AuraEffect::HandleBindSight(bool apply, bool Real) { Unit* caster = GetCaster(); if(!caster || caster->GetTypeId() != TYPEID_PLAYER) return; - if (apply) - m_target->AddPlayerToVision((Player*)caster); - else - m_target->RemovePlayerFromVision((Player*)caster); + ((Player*)caster)->SetViewpoint(m_target, apply); } -void Aura::HandleFarSight(bool apply, bool Real) +void AuraEffect::HandleFarSight(bool apply, bool Real) { - Unit* caster = GetCaster(); - if(!caster || caster->GetTypeId() != TYPEID_PLAYER) - return; - - ((Player*)caster)->SetFarSight(apply ? m_target->GetGUID() : NULL); + //Handled by client } -void Aura::HandleAuraTrackCreatures(bool apply, bool Real) +void AuraEffect::HandleAuraTrackCreatures(bool apply, bool Real) { if(m_target->GetTypeId()!=TYPEID_PLAYER) return; - if(apply) - m_target->RemoveNoStackAurasDueToAura(this); - m_target->SetUInt32Value(PLAYER_TRACK_CREATURES, apply ? ((uint32)1)<<(m_modifier.m_miscvalue-1) : 0 ); + m_target->SetUInt32Value(PLAYER_TRACK_CREATURES, apply ? ((uint32)1)<<(GetMiscValue()-1) : 0 ); } -void Aura::HandleAuraTrackResources(bool apply, bool Real) +void AuraEffect::HandleAuraTrackResources(bool apply, bool Real) { if(m_target->GetTypeId()!=TYPEID_PLAYER) return; - if(apply) - m_target->RemoveNoStackAurasDueToAura(this); - m_target->SetUInt32Value(PLAYER_TRACK_RESOURCES, apply ? ((uint32)1)<<(m_modifier.m_miscvalue-1): 0 ); + m_target->SetUInt32Value(PLAYER_TRACK_RESOURCES, apply ? ((uint32)1)<<(GetMiscValue()-1): 0 ); } -void Aura::HandleAuraTrackStealthed(bool apply, bool Real) +void AuraEffect::HandleAuraTrackStealthed(bool apply, bool Real) { if(m_target->GetTypeId()!=TYPEID_PLAYER) return; - if(apply) - m_target->RemoveNoStackAurasDueToAura(this); - m_target->ApplyModFlag(PLAYER_FIELD_BYTES,PLAYER_FIELD_BYTE_TRACK_STEALTHED,apply); } -void Aura::HandleAuraModScale(bool apply, bool Real) +void AuraEffect::HandleAuraModScale(bool apply, bool Real) { - m_target->ApplyPercentModFloatValue(OBJECT_FIELD_SCALE_X,GetModifierValue(),apply); + m_target->ApplyPercentModFloatValue(OBJECT_FIELD_SCALE_X,m_amount,apply); } -void Aura::HandleModPossess(bool apply, bool Real) +/*void AuraEffect::HandleModPossess(bool apply, bool Real) { if(!Real) return; + if(m_target->getLevel() > m_amount) + return; + + // not possess yourself + if(GetCasterGUID() == m_target->GetGUID()) + return; + Unit* caster = GetCaster(); - if(caster && caster->GetTypeId() == TYPEID_UNIT) - { - HandleModCharm(apply, Real); + if(!caster) return; - } - if(apply) + if( apply ) { - if(m_target->getLevel() > m_modifier.m_amount) - return; + m_target->SetCharmerGUID(GetCasterGUID()); + m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,caster->getFaction()); - m_target->SetCharmedOrPossessedBy(caster, true); + caster->SetCharm(m_target); + + if(caster->GetTypeId() == TYPEID_PLAYER) + { + ((Player*)caster)->SetFarSightGUID(m_target->GetGUID()); + ((Player*)caster)->SetClientControl(m_target, 1); + } + + m_target->CombatStop(); + m_target->DeleteThreatList(); + if(m_target->GetTypeId() == TYPEID_UNIT) + { + m_target->StopMoving(); + m_target->GetMotionMaster()->Clear(); + m_target->GetMotionMaster()->MoveIdle(); + } + else if(m_target->GetTypeId() == TYPEID_PLAYER) + { + ((Player*)m_target)->SetClientControl(m_target, 0); + } + + if(CharmInfo *charmInfo = m_target->InitCharmInfo(m_target)) + charmInfo->InitPossessCreateSpells(); + + if(caster->GetTypeId() == TYPEID_PLAYER) + ((Player*)caster)->PossessSpellInitialize(); } else - m_target->RemoveCharmedOrPossessedBy(caster); + { + m_target->SetCharmerGUID(0); + caster->InterruptSpell(CURRENT_CHANNELED_SPELL); // the spell is not automatically canceled when interrupted, do it now + + if(m_target->GetTypeId() == TYPEID_PLAYER) + { + ((Player*)m_target)->setFactionForRace(m_target->getRace()); + ((Player*)m_target)->SetClientControl(m_target, 1); + } + else if(m_target->GetTypeId() == TYPEID_UNIT) + { + CreatureInfo const *cinfo = ((Creature*)m_target)->GetCreatureInfo(); + m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,cinfo->faction_A); + } + + caster->SetCharm(NULL); + + if(caster->GetTypeId() == TYPEID_PLAYER) + { + ((Player*)caster)->SetFarSightGUID(0); + ((Player*)caster)->SetClientControl(m_target,0); + + WorldPacket data(SMSG_PET_SPELLS, 8+4); + data << uint64(0); + data << uint32(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); + } + } } -void Aura::HandleModPossessPet(bool apply, bool Real) +void AuraEffect::HandleModPossessPet(bool apply, bool Real) { if(!Real) return; @@ -2875,46 +3441,155 @@ void Aura::HandleModPossessPet(bool apply, bool Real) if(!caster || caster->GetTypeId() != TYPEID_PLAYER) return; + Pet *pet = caster->GetGuardianPet(); + if(!pet || pet != m_target) + return; + if(apply) - { - if(caster->GetPet() != m_target) - return; + pet->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_24); + else + pet->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_24); - m_target->SetCharmedOrPossessedBy(caster, true); + ((Player*)caster)->SetFarSightGUID(apply ? pet->GetGUID() : 0); + ((Player*)caster)->SetCharm(apply ? pet : NULL); + ((Player*)caster)->SetClientControl(pet, apply ? 1 : 0); + + if(apply) + { + pet->StopMoving(); + pet->GetMotionMaster()->Clear(); + pet->GetMotionMaster()->MoveIdle(); } else { - m_target->RemoveCharmedOrPossessedBy(caster); - - // Reinitialize the pet bar and make the pet come back to the owner - ((Player*)caster)->PetSpellInitialize(); - if(!m_target->getVictim()) - { - m_target->GetMotionMaster()->MoveFollow(caster, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); - m_target->GetCharmInfo()->SetCommandState(COMMAND_FOLLOW); - } + pet->AttackStop(); + pet->GetMotionMaster()->MoveFollow(caster, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + pet->SetUnitMovementFlags(MOVEMENTFLAG_NONE); } +}*/ + +void AuraEffect::HandleAuraModPetTalentsPoints(bool Apply, bool Real) +{ + if(!Real) + return; + + if(m_target->GetTypeId() != TYPEID_PLAYER) + return; + + // Recalculate pet tlaent points + if (Pet *pet = ((Player*)m_target)->GetPet()) + pet->InitTalentForLevel(); } -void Aura::HandleModCharm(bool apply, bool Real) +/*void AuraEffect::HandleModCharm(bool apply, bool Real) { if(!Real) return; + // not charm yourself + if(GetCasterGUID() == m_target->GetGUID()) + return; + Unit* caster = GetCaster(); + if(!caster) + return; - if(apply) + if(int32(m_target->getLevel()) <= m_amount) { - if(int32(m_target->getLevel()) > m_modifier.m_amount) - return; + if( apply ) + { + m_target->SetCharmerGUID(GetCasterGUID()); + m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,caster->getFaction()); + m_target->CastStop(m_target==caster ? GetId() : 0); + caster->SetCharm(m_target); - m_target->SetCharmedOrPossessedBy(caster, false); + 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) + { + //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 + { + CreatureInfo const *cinfo = ((Creature*)m_target)->GetCreatureInfo(); + + // restore faction + if(((Creature*)m_target)->isPet()) + { + if(Unit* owner = m_target->GetOwner()) + m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,owner->getFaction()); + else if(cinfo) + m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,cinfo->faction_A); + } + else if(cinfo) // normal creature + m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,cinfo->faction_A); + + // restore UNIT_FIELD_BYTES_0 + if(cinfo && caster->GetTypeId() == TYPEID_PLAYER && caster->getClass() == CLASS_WARLOCK && cinfo->type == CREATURE_TYPE_DEMON) + { + 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("AuraEffect::HandleModCharm: target="I64FMTD" with typeid=%d has a charm aura but no charm info!", m_target->GetGUID(), m_target->GetTypeId()); + } + } + + caster->SetCharm(NULL); + + if(caster->GetTypeId() == TYPEID_PLAYER) + { + WorldPacket data(SMSG_PET_SPELLS, 8+4); + data << uint64(0); + data << uint32(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); + } + } } - else - m_target->RemoveCharmedOrPossessedBy(caster); -} +}*/ -void Aura::HandleModConfuse(bool apply, bool Real) +void AuraEffect::HandleModConfuse(bool apply, bool Real) { if(!Real) return; @@ -2923,7 +3598,7 @@ void Aura::HandleModConfuse(bool apply, bool Real) m_target->SetControlled(apply, UNIT_STAT_CONFUSED); } -void Aura::HandleModFear(bool apply, bool Real) +void AuraEffect::HandleModFear(bool apply, bool Real) { if (!Real) return; @@ -2932,7 +3607,7 @@ void Aura::HandleModFear(bool apply, bool Real) m_target->SetControlled(apply, UNIT_STAT_FLEEING); } -void Aura::HandleFeignDeath(bool apply, bool Real) +void AuraEffect::HandleFeignDeath(bool apply, bool Real) { if(!Real) return; @@ -2951,7 +3626,7 @@ void Aura::HandleFeignDeath(bool apply, bool Real) std::list<Unit*> targets; Trinity::AnyUnfriendlyUnitInObjectRangeCheck u_check(m_target, m_target, World::GetMaxVisibleDistance()); - Trinity::UnitListSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(targets, u_check); + Trinity::UnitListSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(m_target, targets, u_check); m_target->VisitNearbyObject(World::GetMaxVisibleDistance(), searcher); for(std::list<Unit*>::iterator iter = targets.begin(); iter != targets.end(); ++iter) { @@ -2968,7 +3643,7 @@ void Aura::HandleFeignDeath(bool apply, bool Real) } } // blizz like 2.0.x - m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNKNOWN6); + m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_29); // blizz like 2.0.x m_target->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH); // blizz like 2.0.x @@ -2976,10 +3651,10 @@ void Aura::HandleFeignDeath(bool apply, bool Real) m_target->addUnitState(UNIT_STAT_DIED); m_target->CombatStop(); - m_target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_UNATTACKABLE); + m_target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION); // prevent interrupt message - if(m_caster_guid==m_target->GetGUID() && m_target->m_currentSpells[CURRENT_GENERIC_SPELL]) + if(GetCasterGUID()==m_target->GetGUID() && m_target->m_currentSpells[CURRENT_GENERIC_SPELL]) m_target->m_currentSpells[CURRENT_GENERIC_SPELL]->finish(); m_target->InterruptNonMeleeSpells(true); m_target->getHostilRefManager().deleteReferences(); @@ -2993,7 +3668,7 @@ void Aura::HandleFeignDeath(bool apply, bool Real) m_target->SendMessageToSet(&data,true); */ // blizz like 2.0.x - m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNKNOWN6); + m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_29); // blizz like 2.0.x m_target->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH); // blizz like 2.0.x @@ -3003,221 +3678,142 @@ void Aura::HandleFeignDeath(bool apply, bool Real) } } -void Aura::HandleAuraModDisarm(bool apply, bool Real) +void AuraEffect::HandleAuraModDisarm(bool apply, bool Real) { - if(!Real) + if (!Real) return; + AuraType type = AuraType(GetAuraName()); - if(!apply && m_target->HasAuraType(SPELL_AURA_MOD_DISARM)) + //Prevent handling aura twice + if(apply && m_target->GetAurasByType(type).size()>1) return; - - // not sure for it's correctness + if(!apply && m_target->HasAuraType(type)) + return; + uint32 field, flag, slot; + WeaponAttackType attType; + switch (type) + { + case SPELL_AURA_MOD_DISARM: + field=UNIT_FIELD_FLAGS; + flag=UNIT_FLAG_DISARMED; + slot=EQUIPMENT_SLOT_MAINHAND; + attType=BASE_ATTACK; + break; + case SPELL_AURA_MOD_DISARM_OFFHAND: + field=UNIT_FIELD_FLAGS_2; + flag=UNIT_FLAG2_DISARM_OFFHAND; + slot=EQUIPMENT_SLOT_OFFHAND; + attType=OFF_ATTACK; + break; + case SPELL_AURA_MOD_DISARM_RANGED: + field=UNIT_FIELD_FLAGS_2; + flag=UNIT_FLAG2_DISARM_RANGED; + slot=EQUIPMENT_SLOT_RANGED; + attType=RANGED_ATTACK; + break; + } if(apply) - m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED); + m_target->SetFlag(field, flag); else - m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED); + m_target->RemoveFlag(field, flag); if (m_target->GetTypeId() == TYPEID_PLAYER) { - // main-hand attack speed already set to special value for feral form already and don't must change and reset at remove. - if (((Player *)m_target)->IsInFeralForm()) - return; - - if (apply) - m_target->SetAttackTime(BASE_ATTACK,BASE_ATTACK_TIME); - else - ((Player *)m_target)->SetRegularAttackTime(); - } - else - { - // creature does not have equipment - if(apply && !((Creature*)m_target)->GetCurrentEquipmentId()) + Item *pItem = ((Player*)m_target)->GetItemByPos( INVENTORY_SLOT_BAG_0, slot ); + if(!pItem ) return; + ((Player*)m_target)->_ApplyItemMods(pItem, slot, !apply); } - - m_target->UpdateDamagePhysical(BASE_ATTACK); + else if (((Creature*)m_target)->GetCurrentEquipmentId()) + m_target->UpdateDamagePhysical(attType); } -void Aura::HandleAuraModStun(bool apply, bool Real) +void AuraEffect::HandleModStealth(bool apply, bool Real) { if(!Real) return; - m_target->SetControlled(apply, UNIT_STAT_STUNNED); -} - -void Aura::HandleModStealth(bool apply, bool Real) -{ if(apply) { - if(Real && m_target->GetTypeId()==TYPEID_PLAYER) - { - // drop flag at stealth in bg - m_target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_UNATTACKABLE); - - // remove player from the objective's active player count at stealth - if(OutdoorPvP * pvp = ((Player*)m_target)->GetOutdoorPvP()) - pvp->HandlePlayerActivityChanged((Player*)m_target); - } + // drop flag at stealth in bg + m_target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION); - // only at real aura add - if(Real) - { - m_target->SetByteValue(UNIT_FIELD_BYTES_1, 2, 0x02); - if(m_target->GetTypeId()==TYPEID_PLAYER) - m_target->SetFlag(PLAYER_FIELD_BYTES2, 0x2000); - - // apply only if not in GM invisibility (and overwrite invisibility state) - if(m_target->GetVisibility()!=VISIBILITY_OFF) - { - //m_target->SetVisibility(VISIBILITY_GROUP_NO_DETECT); - //m_target->SetVisibility(VISIBILITY_OFF); - m_target->SetVisibility(VISIBILITY_GROUP_STEALTH); - } + m_target->SetStandFlags(UNIT_STAND_FLAGS_CREEP); + if(m_target->GetTypeId()==TYPEID_PLAYER) + m_target->SetFlag(PLAYER_FIELD_BYTES2, 0x2000); - // for RACE_NIGHTELF stealth - if(m_target->GetTypeId()==TYPEID_PLAYER && GetId()==20580) - m_target->CastSpell(m_target, 21009, true, NULL, this); - } + // apply only if not in GM invisibility (and overwrite invisibility state) + if(m_target->GetVisibility() != VISIBILITY_OFF) + m_target->SetVisibility(VISIBILITY_GROUP_STEALTH); } - else + else if(!m_target->HasAuraType(SPELL_AURA_MOD_STEALTH)) // if last SPELL_AURA_MOD_STEALTH { - // only at real aura remove - if(Real) - { - // for RACE_NIGHTELF stealth - if(m_target->GetTypeId()==TYPEID_PLAYER && GetId()==20580) - m_target->RemoveAurasDueToSpell(21009); - - // if last SPELL_AURA_MOD_STEALTH and no GM invisibility - if(!m_target->HasAuraType(SPELL_AURA_MOD_STEALTH) && m_target->GetVisibility()!=VISIBILITY_OFF) - { - m_target->SetByteValue(UNIT_FIELD_BYTES_1, 2, 0x00); - if(m_target->GetTypeId()==TYPEID_PLAYER) - m_target->RemoveFlag(PLAYER_FIELD_BYTES2, 0x2000); - - // restore invisibility if any - if(m_target->HasAuraType(SPELL_AURA_MOD_INVISIBILITY)) - { - //m_target->SetVisibility(VISIBILITY_GROUP_NO_DETECT); - //m_target->SetVisibility(VISIBILITY_GROUP_INVISIBILITY); - m_target->SetVisibility(VISIBILITY_ON); - } - else - { - m_target->SetVisibility(VISIBILITY_ON); - if(m_target->GetTypeId() == TYPEID_PLAYER) - if(OutdoorPvP * pvp = ((Player*)m_target)->GetOutdoorPvP()) - pvp->HandlePlayerActivityChanged((Player*)m_target); - } - } - } - } + m_target->RemoveStandFlags(UNIT_STAND_FLAGS_CREEP); + if(m_target->GetTypeId()==TYPEID_PLAYER) + m_target->RemoveFlag(PLAYER_FIELD_BYTES2, 0x2000); - // Master of Subtlety - Unit::AuraList const& mDummyAuras = m_target->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i) - { - if ((*i)->GetSpellProto()->SpellIconID == 2114 && Real) - { - if (apply) - { - int32 bp = (*i)->GetModifier()->m_amount; - m_target->CastCustomSpell(m_target,31665,&bp,NULL,NULL,true); - } - else - m_target->CastSpell(m_target,31666,true); - break; - } + if(m_target->GetVisibility() != VISIBILITY_OFF) + m_target->SetVisibility(VISIBILITY_ON); } } -void Aura::HandleInvisibility(bool apply, bool Real) +void AuraEffect::HandleInvisibility(bool apply, bool Real) { if(apply) { - m_target->m_invisibilityMask |= (1 << m_modifier.m_miscvalue); - - m_target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_UNATTACKABLE); + m_target->m_invisibilityMask |= (1 << GetMiscValue()); - if(Real && m_target->GetTypeId()==TYPEID_PLAYER) + if(Real) { + m_target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION); + // apply glow vision - m_target->SetFlag(PLAYER_FIELD_BYTES2,PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW); - // remove player from the objective's active player count at invisibility - if(OutdoorPvP * pvp = ((Player*)m_target)->GetOutdoorPvP()) - pvp->HandlePlayerActivityChanged((Player*)m_target); - } + if(m_target->GetTypeId()==TYPEID_PLAYER) + m_target->SetFlag(PLAYER_FIELD_BYTES2,PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW); - // apply only if not in GM invisibility and not stealth - if(m_target->GetVisibility()==VISIBILITY_ON) - { - // Aura not added yet but visibility code expect temporary add aura - //m_target->SetVisibility(VISIBILITY_GROUP_NO_DETECT); - //m_target->SetVisibility(VISIBILITY_GROUP_INVISIBILITY); - m_target->SetVisibility(VISIBILITY_ON); + m_target->SetToNotify(); } } else { // recalculate value at modifier remove (current aura already removed) m_target->m_invisibilityMask = 0; - Unit::AuraList const& auras = m_target->GetAurasByType(SPELL_AURA_MOD_INVISIBILITY); - for(Unit::AuraList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - m_target->m_invisibilityMask |= (1 << m_modifier.m_miscvalue); + Unit::AuraEffectList const& auras = m_target->GetAurasByType(SPELL_AURA_MOD_INVISIBILITY); + for(Unit::AuraEffectList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) + m_target->m_invisibilityMask |= (1 << GetMiscValue()); // only at real aura remove and if not have different invisibility auras. - if(Real && m_target->m_invisibilityMask==0) + if(Real) { // remove glow vision - if(m_target->GetTypeId() == TYPEID_PLAYER) + if(!m_target->m_invisibilityMask && m_target->GetTypeId() == TYPEID_PLAYER) m_target->RemoveFlag(PLAYER_FIELD_BYTES2,PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW); - // apply only if not in GM invisibility & not stealthed while invisible - if(m_target->GetVisibility()!=VISIBILITY_OFF) - { - // if have stealth aura then already have stealth visibility - if(!m_target->HasAuraType(SPELL_AURA_MOD_STEALTH)) - { - m_target->SetVisibility(VISIBILITY_ON); - if(m_target->GetTypeId() == TYPEID_PLAYER) - if(OutdoorPvP * pvp = ((Player*)m_target)->GetOutdoorPvP()) - pvp->HandlePlayerActivityChanged((Player*)m_target); - } - } + m_target->SetToNotify(); } } } -void Aura::HandleInvisibilityDetect(bool apply, bool Real) +void AuraEffect::HandleInvisibilityDetect(bool apply, bool Real) { if(apply) { - m_target->m_detectInvisibilityMask |= (1 << m_modifier.m_miscvalue); + m_target->m_detectInvisibilityMask |= (1 << GetMiscValue()); } else { // recalculate value at modifier remove (current aura already removed) m_target->m_detectInvisibilityMask = 0; - Unit::AuraList const& auras = m_target->GetAurasByType(SPELL_AURA_MOD_INVISIBILITY_DETECTION); - for(Unit::AuraList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - m_target->m_detectInvisibilityMask |= (1 << m_modifier.m_miscvalue); + Unit::AuraEffectList const& auras = m_target->GetAurasByType(SPELL_AURA_MOD_INVISIBILITY_DETECTION); + for(Unit::AuraEffectList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) + m_target->m_detectInvisibilityMask |= (1 << GetMiscValue()); } if(Real && m_target->GetTypeId()==TYPEID_PLAYER) //ObjectAccessor::UpdateVisibilityForPlayer((Player*)m_target); m_target->SetToNotify(); } -void Aura::HandleAuraModRoot(bool apply, bool Real) -{ - // only at real add/remove aura - if(!Real) - return; - - m_target->SetControlled(apply, UNIT_STAT_ROOT); -} - -void Aura::HandleAuraModSilence(bool apply, bool Real) +void AuraEffect::HandleAuraModSilence(bool apply, bool Real) { // only at real add/remove aura if(!Real) @@ -3228,18 +3824,8 @@ void Aura::HandleAuraModSilence(bool apply, bool Real) m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED); // Stop cast only spells vs PreventionType == SPELL_PREVENTION_TYPE_SILENCE for (uint32 i = CURRENT_MELEE_SPELL; i < CURRENT_MAX_SPELL;i++) - { - Spell* currentSpell = m_target->m_currentSpells[i]; - if (currentSpell && currentSpell->m_spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE) - { - uint32 state = currentSpell->getState(); - // Stop spells on prepare or casting state - if ( state == SPELL_STATE_PREPARING || state == SPELL_STATE_CASTING ) - { - currentSpell->cancel(); - } - } - } + if (m_target->m_currentSpells[i] && m_target->m_currentSpells[i]->m_spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE) + m_target->InterruptSpell(i,false); // Stop spells on prepare or casting state switch (GetId()) { @@ -3251,10 +3837,10 @@ void Aura::HandleAuraModSilence(bool apply, bool Real) return; // Search Mana Tap auras on caster - Aura * dummy = m_target->GetDummyAura(28734); + AuraEffect * dummy = caster->GetDummyAura(28734); if (dummy) { - int32 bp = dummy->GetStackAmount() * 10; + int32 bp = dummy->GetParentAura()->GetStackAmount() * 10; caster->CastCustomSpell(caster, 25048, &bp, NULL, NULL, true); m_target->RemoveAurasDueToSpell(28734); } @@ -3271,7 +3857,7 @@ void Aura::HandleAuraModSilence(bool apply, bool Real) } } -void Aura::HandleModThreat(bool apply, bool Real) +void AuraEffect::HandleModThreat(bool apply, bool Real) { // only at real add/remove aura if(!Real) @@ -3301,19 +3887,19 @@ void Aura::HandleModThreat(bool apply, bool Real) break; } if (level_diff > 0) - m_modifier.m_amount += multiplier * level_diff; + m_amount += multiplier * level_diff; for(int8 x=0;x < MAX_SPELL_SCHOOL;x++) { - if(m_modifier.m_miscvalue & int32(1<<x)) + if(GetMiscValue() & int32(1<<x)) { if(m_target->GetTypeId() == TYPEID_PLAYER) - ApplyPercentModFloatVar(m_target->m_threatModifier[x], m_positive ? GetModifierValue() : -GetModifierValue(), apply); + ApplyPercentModFloatVar(m_target->m_threatModifier[x], GetParentAura()->IsPositive() ? m_amount : -m_amount, apply); } } } -void Aura::HandleAuraModTotalThreat(bool apply, bool Real) +void AuraEffect::HandleAuraModTotalThreat(bool apply, bool Real) { // only at real add/remove aura if(!Real) @@ -3329,14 +3915,14 @@ void Aura::HandleAuraModTotalThreat(bool apply, bool Real) float threatMod = 0.0f; if(apply) - threatMod = float(GetModifierValue()); + threatMod = float(m_amount); else - threatMod = float(-GetModifierValue()); + threatMod = float(-m_amount); m_target->getHostilRefManager().threatAssist(caster, threatMod); } -void Aura::HandleModTaunt(bool apply, bool Real) +void AuraEffect::HandleModTaunt(bool apply, bool Real) { // only at real add/remove aura if(!Real) @@ -3362,7 +3948,7 @@ void Aura::HandleModTaunt(bool apply, bool Real) /*********************************************************/ /*** MODIFY SPEED ***/ /*********************************************************/ -void Aura::HandleAuraModIncreaseSpeed(bool /*apply*/, bool Real) +void AuraEffect::HandleAuraModIncreaseSpeed(bool /*apply*/, bool Real) { // all applied/removed only at real aura add/remove if(!Real) @@ -3371,7 +3957,7 @@ void Aura::HandleAuraModIncreaseSpeed(bool /*apply*/, bool Real) m_target->UpdateSpeed(MOVE_RUN, true); } -void Aura::HandleAuraModIncreaseMountedSpeed(bool /*apply*/, bool Real) +void AuraEffect::HandleAuraModIncreaseMountedSpeed(bool /*apply*/, bool Real) { // all applied/removed only at real aura add/remove if(!Real) @@ -3380,14 +3966,14 @@ void Aura::HandleAuraModIncreaseMountedSpeed(bool /*apply*/, bool Real) m_target->UpdateSpeed(MOVE_RUN, true); } -void Aura::HandleAuraModIncreaseFlightSpeed(bool apply, bool Real) +void AuraEffect::HandleAuraModIncreaseFlightSpeed(bool apply, bool Real) { // all applied/removed only at real aura add/remove if(!Real) return; // Enable Fly mode for flying mounts - if (m_modifier.m_auraname == SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED) + if (m_auraName == SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED) { WorldPacket data; if(apply) @@ -3403,14 +3989,14 @@ void Aura::HandleAuraModIncreaseFlightSpeed(bool apply, bool Real) m_target->ApplySpellImmune(GetId(),IMMUNITY_MECHANIC,MECHANIC_POLYMORPH,apply); // Dragonmaw Illusion (overwrite mount model, mounted aura already applied) - if( apply && m_target->HasAura(42016,0) && m_target->GetMountID()) + if( apply && m_target->HasAuraEffect(42016,0) && m_target->GetMountID()) m_target->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID,16314); } m_target->UpdateSpeed(MOVE_FLIGHT, true); } -void Aura::HandleAuraModIncreaseSwimSpeed(bool /*apply*/, bool Real) +void AuraEffect::HandleAuraModIncreaseSwimSpeed(bool /*apply*/, bool Real) { // all applied/removed only at real aura add/remove if(!Real) @@ -3419,7 +4005,7 @@ void Aura::HandleAuraModIncreaseSwimSpeed(bool /*apply*/, bool Real) m_target->UpdateSpeed(MOVE_SWIM, true); } -void Aura::HandleAuraModDecreaseSpeed(bool /*apply*/, bool Real) +void AuraEffect::HandleAuraModDecreaseSpeed(bool /*apply*/, bool Real) { // all applied/removed only at real aura add/remove if(!Real) @@ -3430,7 +4016,7 @@ void Aura::HandleAuraModDecreaseSpeed(bool /*apply*/, bool Real) m_target->UpdateSpeed(MOVE_FLIGHT, true); } -void Aura::HandleAuraModUseNormalSpeed(bool /*apply*/, bool Real) +void AuraEffect::HandleAuraModUseNormalSpeed(bool /*apply*/, bool Real) { // all applied/removed only at real aura add/remove if(!Real) @@ -3445,51 +4031,81 @@ void Aura::HandleAuraModUseNormalSpeed(bool /*apply*/, bool Real) /*** IMMUNITY ***/ /*********************************************************/ -void Aura::HandleModMechanicImmunity(bool apply, bool Real) +void AuraEffect::HandleModStateImmunityMask(bool apply, bool Real) +{ + std::list <AuraType> immunity_list; + if (GetMiscValue() & (1<<10)) + immunity_list.push_back(SPELL_AURA_MOD_STUN); + if (GetMiscValue() & (1<<7)) + immunity_list.push_back(SPELL_AURA_MOD_DISARM); + if (GetMiscValue() & (1<<1)) + immunity_list.push_back(SPELL_AURA_MOD_TAUNT); + + // These flag can be recognized wrong: + if (GetMiscValue() & (1<<6)) + immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED); + if (GetMiscValue() & (1<<0)) + immunity_list.push_back(SPELL_AURA_MOD_ROOT); + if (GetMiscValue() & (1<<3)) + immunity_list.push_back(SPELL_AURA_MOD_CONFUSE); + if (GetMiscValue() & (1<<9)) + immunity_list.push_back(SPELL_AURA_MOD_FEAR); + + // Patch 3.0.3 Bladestorm now breaks all snares and roots on the warrior when activated. + // however not all mechanic specified in immunity + if (apply && GetId()==46924) + { + m_target->RemoveAurasByType(SPELL_AURA_MOD_ROOT); + m_target->RemoveAurasByType(SPELL_AURA_MOD_DECREASE_SPEED); + } + + if(apply && GetSpellProto()->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY) + { + for (std::list <AuraType>::iterator iter = immunity_list.begin(); iter != immunity_list.end();++iter) + { + m_target->RemoveAurasByType(*iter); + } + } + for (std::list <AuraType>::iterator iter = immunity_list.begin(); iter != immunity_list.end();++iter) + { + m_target->ApplySpellImmune(GetId(),IMMUNITY_STATE,*iter,apply); + } +} + +void AuraEffect::HandleModMechanicImmunity(bool apply, bool Real) { - uint32 mechanic = 1 << m_modifier.m_miscvalue; + uint32 mechanic; + mechanic = 1 << GetMiscValue(); //immune movement impairment and loss of control - if(GetId()==42292) + if(GetId()==42292 || GetId()==59752) mechanic=IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; + if (!mechanic) + return; if(apply && GetSpellProto()->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY) { Unit::AuraMap& Auras = m_target->GetAuras(); - for(Unit::AuraMap::iterator iter = Auras.begin(), next; iter != Auras.end(); iter = next) + for(Unit::AuraMap::iterator iter = Auras.begin(); iter != Auras.end();) { - next = iter; - ++next; SpellEntry const *spell = iter->second->GetSpellProto(); - if (!( spell->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY) // spells unaffected by invulnerability - && !iter->second->IsPositive() // only remove negative spells - && spell->Id != GetId()) + if (!( spell->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY) && // spells unaffected by invulnerability + spell->Id != GetId()) { //check for mechanic mask - if(GetSpellMechanicMask(spell, iter->second->GetEffIndex()) & mechanic) + if(GetAllSpellMechanicMask(spell) & mechanic) { - m_target->RemoveAurasDueToSpell(spell->Id); - if(Auras.empty()) - break; - else - next = Auras.begin(); + m_target->RemoveAura(iter); } + else + ++iter; } + else + ++iter; } } - m_target->ApplySpellImmune(GetId(),IMMUNITY_MECHANIC,m_modifier.m_miscvalue,apply); - - // special cases - switch(m_modifier.m_miscvalue) - { - case MECHANIC_INVULNERABILITY: - m_target->ModifyAuraState(AURA_STATE_FORBEARANCE,apply); - break; - case MECHANIC_SHIELD: - m_target->ModifyAuraState(AURA_STATE_WEAKENED_SOUL,apply); - break; - } + m_target->ApplySpellImmune(GetId(),IMMUNITY_MECHANIC,GetMiscValue(),apply); // Bestial Wrath if ( GetSpellProto()->SpellFamilyName == SPELLFAMILY_HUNTER && GetSpellProto()->Id == 19574) @@ -3498,8 +4114,8 @@ void Aura::HandleModMechanicImmunity(bool apply, bool Real) if ( Unit* owner = m_target->GetOwner() ) { // Search talent - Unit::AuraList const& m_dummyAuras = owner->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraList::const_iterator i = m_dummyAuras.begin(); i != m_dummyAuras.end(); ++i) + Unit::AuraEffectList const& m_dummyAuras = owner->GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraEffectList::const_iterator i = m_dummyAuras.begin(); i != m_dummyAuras.end(); ++i) { if ( (*i)->GetSpellProto()->SpellIconID == 2229 ) { @@ -3531,103 +4147,82 @@ void Aura::HandleModMechanicImmunity(bool apply, bool Real) m_target->RemoveAurasDueToSpell(26592); } } + + // Heroic Fury (remove Intercept cooldown) + if( apply && GetId() == 60970 && m_target->GetTypeId() == TYPEID_PLAYER ) + { + ((Player*)m_target)->RemoveSpellCooldown(20252); + + WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8)); + data << uint32(20252); + data << uint64(m_target->GetGUID()); + ((Player*)m_target)->GetSession()->SendPacket(&data); + } } -void Aura::HandleAuraModEffectImmunity(bool apply, bool Real) +//this method is called whenever we add / remove aura which gives m_target some imunity to some spell effect +void AuraEffect::HandleAuraModEffectImmunity(bool apply, bool Real) { - if(!apply) + // when removing flag aura, handle flag drop + if( !apply && m_target->GetTypeId() == TYPEID_PLAYER + && (GetSpellProto()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION) ) { if(m_target->GetTypeId() == TYPEID_PLAYER) { if(((Player*)m_target)->InBattleGround()) { - BattleGround *bg = ((Player*)m_target)->GetBattleGround(); - if(bg) - { - switch(bg->GetTypeID()) - { - case BATTLEGROUND_AV: - { - break; - } - case BATTLEGROUND_WS: - { - // Warsong Flag, horde // Silverwing Flag, alliance - if(GetId() == 23333 || GetId() == 23335) - bg->EventPlayerDroppedFlag(((Player*)m_target)); - break; - } - case BATTLEGROUND_AB: - { - break; - } - case BATTLEGROUND_EY: - { - if(GetId() == 34976) - bg->EventPlayerDroppedFlag(((Player*)m_target)); - break; - } - } - } + if( BattleGround *bg = ((Player*)m_target)->GetBattleGround() ) + bg->EventPlayerDroppedFlag(((Player*)m_target)); } else sOutdoorPvPMgr.HandleDropFlag((Player*)m_target,GetSpellProto()->Id); } } - m_target->ApplySpellImmune(GetId(),IMMUNITY_EFFECT,m_modifier.m_miscvalue,apply); + m_target->ApplySpellImmune(GetId(),IMMUNITY_EFFECT,GetMiscValue(),apply); } -void Aura::HandleAuraModStateImmunity(bool apply, bool Real) +void AuraEffect::HandleAuraModStateImmunity(bool apply, bool Real) { if(apply && Real && GetSpellProto()->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY) { - Unit::AuraList const& auraList = m_target->GetAurasByType(AuraType(m_modifier.m_miscvalue)); - for(Unit::AuraList::const_iterator itr = auraList.begin(); itr != auraList.end();) - { - if (auraList.front() != this) // skip itself aura (it already added) - { - m_target->RemoveAurasDueToSpell(auraList.front()->GetId()); - itr = auraList.begin(); - } - else - ++itr; - } + m_target->RemoveAurasByType(AuraType(GetMiscValue()), NULL , GetParentAura()); } - m_target->ApplySpellImmune(GetId(),IMMUNITY_STATE,m_modifier.m_miscvalue,apply); + m_target->ApplySpellImmune(GetId(),IMMUNITY_STATE,GetMiscValue(),apply); } -void Aura::HandleAuraModSchoolImmunity(bool apply, bool Real) +void AuraEffect::HandleAuraModSchoolImmunity(bool apply, bool Real) { - if(apply && m_modifier.m_miscvalue == SPELL_SCHOOL_MASK_NORMAL) - m_target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_UNATTACKABLE); + if(apply && GetMiscValue() == SPELL_SCHOOL_MASK_NORMAL) + m_target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION); + + m_target->ApplySpellImmune(GetId(),IMMUNITY_SCHOOL,GetMiscValue(),apply); - m_target->ApplySpellImmune(GetId(),IMMUNITY_SCHOOL,m_modifier.m_miscvalue,apply); + // remove all flag auras (they are positive, but they must be removed when you are immune) + if( this->GetSpellProto()->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY + && this->GetSpellProto()->AttributesEx2 & SPELL_ATTR_EX2_DAMAGE_REDUCED_SHIELD ) + m_target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION); - if(Real && apply && GetSpellProto()->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY) + // TODO: optimalize this cycle - use RemoveAurasWithInterruptFlags call or something else + if( Real && apply + && GetSpellProto()->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY + && IsPositiveSpell(GetId()) ) //Only positive immunity removes auras { - if(IsPositiveSpell(GetId())) //Only positive immunity removes auras + uint32 school_mask = GetMiscValue(); + Unit::AuraMap& Auras = m_target->GetAuras(); + for(Unit::AuraMap::iterator iter = Auras.begin(); iter != Auras.end();) { - uint32 school_mask = m_modifier.m_miscvalue; - Unit::AuraMap& Auras = m_target->GetAuras(); - for(Unit::AuraMap::iterator iter = Auras.begin(), next; iter != Auras.end(); iter = next) + SpellEntry const *spell = iter->second->GetSpellProto(); + if((GetSpellSchoolMask(spell) & school_mask)//Check for school mask + && !( spell->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY) //Spells unaffected by invulnerability + && !iter->second->IsPositive() //Don't remove positive spells + && spell->Id != GetId() ) //Don't remove self { - next = iter; - ++next; - SpellEntry const *spell = iter->second->GetSpellProto(); - if((GetSpellSchoolMask(spell) & school_mask)//Check for school mask - && !( spell->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY) //Spells unaffected by invulnerability - && !iter->second->IsPositive() //Don't remove positive spells - && spell->Id != GetId() ) //Don't remove self - { - m_target->RemoveAurasDueToSpell(spell->Id); - if(Auras.empty()) - break; - else - next = Auras.begin(); - } + m_target->RemoveAura(iter); } + else + ++iter; } } if( Real && GetSpellProto()->Mechanic == MECHANIC_BANISH ) @@ -3639,40 +4234,27 @@ void Aura::HandleAuraModSchoolImmunity(bool apply, bool Real) } } -void Aura::HandleAuraModDmgImmunity(bool apply, bool Real) +void AuraEffect::HandleAuraModDmgImmunity(bool apply, bool Real) { - m_target->ApplySpellImmune(GetId(),IMMUNITY_DAMAGE,m_modifier.m_miscvalue,apply); + m_target->ApplySpellImmune(GetId(),IMMUNITY_DAMAGE,GetMiscValue(),apply); } -void Aura::HandleAuraModDispelImmunity(bool apply, bool Real) +void AuraEffect::HandleAuraModDispelImmunity(bool apply, bool Real) { // all applied/removed only at real aura add/remove if(!Real) return; - m_target->ApplySpellDispelImmunity(m_spellProto, DispelType(m_modifier.m_miscvalue), apply); + m_target->ApplySpellDispelImmunity(m_spellProto, DispelType(GetMiscValue()), apply); } -void Aura::HandleAuraProcTriggerSpell(bool apply, bool Real) +void AuraEffect::HandleAuraProcTriggerSpell(bool apply, bool Real) { if(!Real) return; - - if(apply) - { - // some spell have charges by functionality not have its in spell data - switch (GetId()) - { - case 28200: // Ascendance (Talisman of Ascendance trinket) - m_procCharges = 6; - UpdateAuraCharges(); - break; - default: break; - } - } } -void Aura::HandleAuraModStalked(bool apply, bool Real) +void AuraEffect::HandleAuraModStalked(bool apply, bool Real) { // used by spells: Hunter's Mark, Mind Vision, Syndicate Tracker (MURP) DND if(apply) @@ -3685,20 +4267,19 @@ void Aura::HandleAuraModStalked(bool apply, bool Real) /*** PERIODIC ***/ /*********************************************************/ -void Aura::HandlePeriodicTriggerSpell(bool apply, bool Real) +void AuraEffect::HandlePeriodicTriggerSpell(bool apply, bool Real) { - if (m_periodicTimer <= 0) - m_periodicTimer += m_modifier.periodictime; - m_isPeriodic = apply; - m_isTrigger = apply; - - // Curse of the Plaguebringer - if (!apply && m_spellProto->Id == 29213 && m_removeMode!=AURA_REMOVE_BY_DISPEL) + if (m_spellProto->Id == 66 && !apply) { - // Cast Wrath of the Plaguebringer if not dispelled - m_target->CastSpell(m_target, 29214, true, 0, this); + if (GetParentAura()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE) + m_target->CastSpell(m_target, 32612, true, NULL, this); } +} + +void AuraEffect::HandlePeriodicTriggerSpellWithValue(bool apply, bool Real) +{ + m_isPeriodic = apply; // Wrath of the Astromancer if(!apply && m_spellProto->Id == 42783) @@ -3707,89 +4288,83 @@ void Aura::HandlePeriodicTriggerSpell(bool apply, bool Real) } } -void Aura::HandlePeriodicEnergize(bool apply, bool Real) +void AuraEffect::HandlePeriodicEnergize(bool apply, bool Real) { - if (m_periodicTimer <= 0) - m_periodicTimer += m_modifier.periodictime; + if (!Real) + return; m_isPeriodic = apply; + + // Replenishment (0.25% from max) + // Infinite Replenishment + if (m_spellProto->SpellIconID == 3184 && m_spellProto->SpellVisual[0] == 12495) + m_amount = m_target->GetMaxPower(POWER_MANA) * 25 / 10000; } -void Aura::HandlePeriodicHeal(bool apply, bool Real) +void AuraEffect::HandleAuraPowerBurn(bool apply, bool Real) { - if (m_periodicTimer <= 0) - m_periodicTimer += m_modifier.periodictime; - m_isPeriodic = apply; +} - // only at real apply - if (Real && apply && GetSpellProto()->Mechanic == MECHANIC_BANDAGE) - { - // provided m_target as original caster to prevent apply aura caster selection for this negative buff - m_target->CastSpell(m_target,11196,true,NULL,this,m_target->GetGUID()); - } +void AuraEffect::HandleAuraPeriodicDummy(bool apply, bool Real) +{ + // spells required only Real aura add/remove + if(!Real) + return; // For prevent double apply bonuses bool loading = (m_target->GetTypeId() == TYPEID_PLAYER && ((Player*)m_target)->GetSession()->PlayerLoading()); - if(!loading && apply) + Unit* caster = GetCaster(); + + SpellEntry const*spell = GetSpellProto(); + switch( spell->SpellFamilyName) { - switch (m_spellProto->SpellFamilyName) + case SPELLFAMILY_HUNTER: { - case SPELLFAMILY_DRUID: - { - // Rejuvenation - if(m_spellProto->SpellFamilyFlags & 0x0000000000000010LL) - { - if(Unit* caster = GetCaster()) - { - Unit::AuraList const& classScripts = caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); - for(Unit::AuraList::const_iterator k = classScripts.begin(); k != classScripts.end(); ++k) - { - int32 tickcount = GetSpellDuration(m_spellProto) / m_spellProto->EffectAmplitude[m_effIndex]; - switch((*k)->GetModifier()->m_miscvalue) - { - case 4953: // Increased Rejuvenation Healing - Harold's Rejuvenating Broach Aura - case 4415: // Increased Rejuvenation Healing - Idol of Rejuvenation Aura - { - m_modifier.m_amount += (*k)->GetModifier()->m_amount / tickcount; - break; - } - } - } - } - } - } + // Explosive Shot + if (apply && !loading && caster) + m_amount += int32(caster->GetTotalAttackPowerValue(RANGED_ATTACK) * 16 / 100); + break; } } + + m_isPeriodic = apply; +} + +void AuraEffect::HandlePeriodicHeal(bool apply, bool Real) +{ + m_isPeriodic = apply; } -void Aura::HandlePeriodicDamage(bool apply, bool Real) +void AuraEffect::HandlePeriodicDamage(bool apply, bool Real) { // spells required only Real aura add/remove if(!Real) return; - if (m_periodicTimer <= 0) - m_periodicTimer += m_modifier.periodictime; - m_isPeriodic = apply; // For prevent double apply bonuses bool loading = (m_target->GetTypeId() == TYPEID_PLAYER && ((Player*)m_target)->GetSession()->PlayerLoading()); + // Custom damage calculation after + if (!apply || loading) + return; + Unit *caster = GetCaster(); + if (!caster) + return; switch (m_spellProto->SpellFamilyName) { case SPELLFAMILY_GENERIC: { // Pounce Bleed - if ( m_spellProto->SpellIconID == 147 && m_spellProto->SpellVisual == 0 ) + if ( m_spellProto->SpellIconID == 147 && m_spellProto->SpellVisual[0] == 0 ) { // $AP*0.18/6 bonus per tick - if (apply && !loading && caster) - m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * 3 / 100); + m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * 3 / 100); return; } break; @@ -3797,18 +4372,14 @@ void Aura::HandlePeriodicDamage(bool apply, bool Real) case SPELLFAMILY_WARRIOR: { // Rend - if (m_spellProto->SpellFamilyFlags & 0x0000000000000020LL) + if (m_spellProto->SpellFamilyFlags[0] & 0x20) { - // 0.00743*(($MWB+$mwb)/2+$AP/14*$MWS) bonus per tick - if (apply && !loading && caster) - { - float ap = caster->GetTotalAttackPowerValue(BASE_ATTACK); - int32 mws = caster->GetAttackTime(BASE_ATTACK); - float mwb_min = caster->GetWeaponDamageRange(BASE_ATTACK,MINDAMAGE); - float mwb_max = caster->GetWeaponDamageRange(BASE_ATTACK,MAXDAMAGE); - // WARNING! in 3.0 multiplier 0.00743f change to 0.6 - m_modifier.m_amount+=int32(((mwb_min+mwb_max)/2+ap*mws/14000)*0.00743f); - } + // $0.2*(($MWB+$mwb)/2+$AP/14*$MWS) bonus per tick + float ap = caster->GetTotalAttackPowerValue(BASE_ATTACK); + int32 mws = caster->GetAttackTime(BASE_ATTACK); + float mwb_min = caster->GetWeaponDamageRange(BASE_ATTACK,MINDAMAGE); + float mwb_max = caster->GetWeaponDamageRange(BASE_ATTACK,MAXDAMAGE); + m_amount+=int32(((mwb_min+mwb_max)/2+ap*mws/14000)*0.2f); return; } break; @@ -3816,92 +4387,80 @@ void Aura::HandlePeriodicDamage(bool apply, bool Real) case SPELLFAMILY_DRUID: { // Rake - if (m_spellProto->SpellFamilyFlags & 0x0000000000001000LL) + if (m_spellProto->SpellFamilyFlags[0] & 0x1000) { - // $AP*0.06/3 bonus per tick - if (apply && !loading && caster) - m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * 2 / 100); + // $AP*0.06 bonus per tick + m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * 6 / 100); return; } // Lacerate - if (m_spellProto->SpellFamilyFlags & 0x000000010000000000LL) + if (m_spellProto->SpellFamilyFlags[1] & 0x0000000100) { // $AP*0.05/5 bonus per tick - if (apply && !loading && caster) - m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100); + m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100); return; } // Rip - if (m_spellProto->SpellFamilyFlags & 0x000000000000800000LL) + if (m_spellProto->SpellFamilyFlags[1] & 0x800000) { - // $AP * min(0.06*$cp, 0.24)/6 [Yes, there is no difference, whether 4 or 5 CPs are being used] - if (apply && !loading && caster && caster->GetTypeId() == TYPEID_PLAYER) - { - uint8 cp = ((Player*)caster)->GetComboPoints(); + // 0.01*$AP*cp + if (caster->GetTypeId() != TYPEID_PLAYER) + return; + + uint8 cp = ((Player*)caster)->GetComboPoints(); - // Idol of Feral Shadows. Cant be handled as SpellMod in SpellAura:Dummy due its dependency from CPs - Unit::AuraList const& dummyAuras = caster->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraList::const_iterator itr = dummyAuras.begin(); itr != dummyAuras.end(); ++itr) + // Idol of Feral Shadows. Cant be handled as SpellMod in SpellAura:Dummy due its dependency from CPs + Unit::AuraEffectList const& dummyAuras = caster->GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraEffectList::const_iterator itr = dummyAuras.begin(); itr != dummyAuras.end(); ++itr) + { + if((*itr)->GetId()==34241) { - if((*itr)->GetId()==34241) - { - m_modifier.m_amount += cp * (*itr)->GetModifier()->m_amount; - break; - } + m_amount += cp * (*itr)->GetAmount(); + break; } - - if (cp > 4) cp = 4; - m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * cp / 100); } + m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * cp / 100); + return; + } + // Lock Jaw + if (m_spellProto->SpellFamilyFlags[1] & 0x10000000) + { + // 0.15*$AP + m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * 15 / 100); return; } break; } case SPELLFAMILY_ROGUE: { - // Deadly poison aura state - if((m_spellProto->SpellFamilyFlags & 0x10000) && m_spellProto->SpellVisual==5100) + // Rupture + if (m_spellProto->SpellFamilyFlags[0] & 0x100000) { - if(apply) - m_target->ModifyAuraState(AURA_STATE_DEADLY_POISON,true); - else - { - // current aura already removed, search present of another - bool found = false; - Unit::AuraList const& auras = m_target->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE); - for(Unit::AuraList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - { - SpellEntry const* itr_spell = (*itr)->GetSpellProto(); - if(itr_spell && itr_spell->SpellFamilyName==SPELLFAMILY_ROGUE && (itr_spell->SpellFamilyFlags & 0x10000) && itr_spell->SpellVisual==5100) - { - found = true; - break; - } - } - // this has been last deadly poison aura - if(!found) - m_target->ModifyAuraState(AURA_STATE_DEADLY_POISON,false); - } + if (caster->GetTypeId() != TYPEID_PLAYER) + return; + //1 point : ${($m1+$b1*1+0.015*$AP)*4} damage over 8 secs + //2 points: ${($m1+$b1*2+0.024*$AP)*5} damage over 10 secs + //3 points: ${($m1+$b1*3+0.03*$AP)*6} damage over 12 secs + //4 points: ${($m1+$b1*4+0.03428571*$AP)*7} damage over 14 secs + //5 points: ${($m1+$b1*5+0.0375*$AP)*8} damage over 16 secs + float AP_per_combo[] = {0, 0.015f, 0.024, 0.03, 0.03428571, 0.0375}; + uint8 cp = ((Player*)caster)->GetComboPoints(); + if (cp > 5) cp = 5; + m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * AP_per_combo[cp]); return; } - // Rupture - if (m_spellProto->SpellFamilyFlags & 0x000000000000100000LL) + // Garrote + if (m_spellProto->SpellFamilyFlags[0] & 0x100) { - // Dmg/tick = $AP*min(0.01*$cp, 0.03) [Like Rip: only the first three CP increase the contribution from AP] - if (apply && !loading && caster && caster->GetTypeId() == TYPEID_PLAYER) - { - uint8 cp = ((Player*)caster)->GetComboPoints(); - if (cp > 3) cp = 3; - m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * cp / 100); - } + // $AP*0.07 bonus per tick + m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * 7 / 100); return; } - // Garrote - if (m_spellProto->SpellFamilyFlags & 0x000000000000000100LL) + // Deadly Poison + if (m_spellProto->SpellFamilyFlags[0] & 0x10000) { - // $AP*0.18/6 bonus per tick - if (apply && !loading && caster) - m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * 3 / 100); + // 0.08*$AP / 4 * amount of stack + m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * 2 * GetParentAura()->GetStackAmount() / 100); return; } break; @@ -3909,47 +4468,17 @@ void Aura::HandlePeriodicDamage(bool apply, bool Real) case SPELLFAMILY_HUNTER: { // Serpent Sting - if (m_spellProto->SpellFamilyFlags & 0x0000000000004000LL) + if (m_spellProto->SpellFamilyFlags[0] & 0x4000) { // $RAP*0.1/5 bonus per tick - if (apply && !loading && caster) - m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(RANGED_ATTACK) * 10 / 500); + m_amount += int32(caster->GetTotalAttackPowerValue(RANGED_ATTACK) * 10 / 500); return; } // Immolation Trap - if (m_spellProto->SpellFamilyFlags & 0x0000000000000004LL && m_spellProto->SpellIconID == 678) + if (m_spellProto->SpellFamilyFlags[0] & 0x4 && m_spellProto->SpellIconID == 678) { // $RAP*0.1/5 bonus per tick - if (apply && !loading && caster) - m_modifier.m_amount += int32(caster->GetTotalAttackPowerValue(RANGED_ATTACK) * 10 / 500); - return; - } - break; - } - case SPELLFAMILY_PALADIN: - { - // Consecration - if (m_spellProto->SpellFamilyFlags & 0x0000000000000020LL) - { - if (apply && !loading) - { - if(Unit* caster = GetCaster()) - { - Unit::AuraList const& classScripts = caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); - for(Unit::AuraList::const_iterator k = classScripts.begin(); k != classScripts.end(); ++k) - { - int32 tickcount = GetSpellDuration(m_spellProto) / m_spellProto->EffectAmplitude[m_effIndex]; - switch((*k)->GetModifier()->m_miscvalue) - { - case 5147: // Improved Consecration - Libram of the Eternal Rest - { - m_modifier.m_amount += (*k)->GetModifier()->m_amount / tickcount; - break; - } - } - } - } - } + m_amount += int32(caster->GetTotalAttackPowerValue(RANGED_ATTACK) * 10 / 500); return; } break; @@ -3959,27 +4488,23 @@ void Aura::HandlePeriodicDamage(bool apply, bool Real) } } -void Aura::HandlePeriodicDamagePCT(bool apply, bool Real) +void AuraEffect::HandlePeriodicDamagePCT(bool apply, bool Real) { - if (m_periodicTimer <= 0) - m_periodicTimer += m_modifier.periodictime; - m_isPeriodic = apply; } -void Aura::HandlePeriodicLeech(bool apply, bool Real) +void AuraEffect::HandlePeriodicLeech(bool apply, bool Real) { - if (m_periodicTimer <= 0) - m_periodicTimer += m_modifier.periodictime; - m_isPeriodic = apply; } -void Aura::HandlePeriodicManaLeech(bool apply, bool Real) +void AuraEffect::HandlePeriodicManaLeech(bool apply, bool Real) { - if (m_periodicTimer <= 0) - m_periodicTimer += m_modifier.periodictime; + m_isPeriodic = apply; +} +void AuraEffect::HandlePeriodicHealthFunnel(bool apply, bool Real) +{ m_isPeriodic = apply; } @@ -3991,90 +4516,82 @@ void Aura::HandlePeriodicManaLeech(bool apply, bool Real) /*** RESISTANCE ***/ /********************************/ -void Aura::HandleAuraModResistanceExclusive(bool apply, bool Real) +void AuraEffect::HandleAuraModResistanceExclusive(bool apply, bool Real) { for(int8 x = SPELL_SCHOOL_NORMAL; x < MAX_SPELL_SCHOOL;x++) { - if(m_modifier.m_miscvalue & int32(1<<x)) + if(GetMiscValue() & int32(1<<x)) { - m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + x), BASE_VALUE, float(GetModifierValue()), apply); + m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + x), BASE_VALUE, float(m_amount), apply); if(m_target->GetTypeId() == TYPEID_PLAYER) - m_target->ApplyResistanceBuffModsMod(SpellSchools(x),m_positive,GetModifierValue(), apply); + m_target->ApplyResistanceBuffModsMod(SpellSchools(x),GetParentAura()->IsPositive(),m_amount, apply); } } } -void Aura::HandleAuraModResistance(bool apply, bool Real) +void AuraEffect::HandleAuraModResistance(bool apply, bool Real) { for(int8 x = SPELL_SCHOOL_NORMAL; x < MAX_SPELL_SCHOOL;x++) { - if(m_modifier.m_miscvalue & int32(1<<x)) + if(GetMiscValue() & int32(1<<x)) { - m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + x), TOTAL_VALUE, float(GetModifierValue()), apply); + m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + x), TOTAL_VALUE, float(m_amount), apply); if(m_target->GetTypeId() == TYPEID_PLAYER || ((Creature*)m_target)->isPet()) - m_target->ApplyResistanceBuffModsMod(SpellSchools(x),m_positive,GetModifierValue(), apply); + m_target->ApplyResistanceBuffModsMod(SpellSchools(x),GetParentAura()->IsPositive(),m_amount, apply); } } - // Faerie Fire (druid versions) - if( (m_spellProto->SpellIconID == 109 && - m_spellProto->SpellFamilyName == SPELLFAMILY_DRUID && - m_spellProto->SpellFamilyFlags & 0x0000000000000400LL) - || m_spellProto->Id == 35325) - { - m_target->ModifyAuraState(AURA_STATE_FAERIE_FIRE,apply); - } } -void Aura::HandleAuraModBaseResistancePCT(bool apply, bool Real) +void AuraEffect::HandleAuraModBaseResistancePCT(bool apply, bool Real) { // only players have base stats if(m_target->GetTypeId() != TYPEID_PLAYER) { //pets only have base armor - if(((Creature*)m_target)->isPet() && (m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)) - m_target->HandleStatModifier(UNIT_MOD_ARMOR, BASE_PCT, float(GetModifierValue()), apply); + if(((Creature*)m_target)->isPet() && (GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL)) + m_target->HandleStatModifier(UNIT_MOD_ARMOR, BASE_PCT, float(m_amount), apply); } else { for(int8 x = SPELL_SCHOOL_NORMAL; x < MAX_SPELL_SCHOOL;x++) { - if(m_modifier.m_miscvalue & int32(1<<x)) - m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + x), BASE_PCT, float(GetModifierValue()), apply); + if(GetMiscValue() & int32(1<<x)) + m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + x), BASE_PCT, float(m_amount), apply); } } } -void Aura::HandleModResistancePercent(bool apply, bool Real) +void AuraEffect::HandleModResistancePercent(bool apply, bool Real) { for(int8 i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++) { - if(m_modifier.m_miscvalue & int32(1<<i)) + if(GetMiscValue() & int32(1<<i)) { - m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + i), TOTAL_PCT, float(GetModifierValue()), apply); + m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + i), TOTAL_PCT, float(m_amount), apply); if(m_target->GetTypeId() == TYPEID_PLAYER || ((Creature*)m_target)->isPet()) { - m_target->ApplyResistanceBuffModsPercentMod(SpellSchools(i),true,GetModifierValue(), apply); - m_target->ApplyResistanceBuffModsPercentMod(SpellSchools(i),false,GetModifierValue(), apply); + m_target->ApplyResistanceBuffModsPercentMod(SpellSchools(i),true,m_amount, apply); + m_target->ApplyResistanceBuffModsPercentMod(SpellSchools(i),false,m_amount, apply); } } } } -void Aura::HandleModBaseResistance(bool apply, bool Real) +void AuraEffect::HandleModBaseResistance(bool apply, bool Real) { // only players have base stats if(m_target->GetTypeId() != TYPEID_PLAYER) { //only pets have base stats - if(((Creature*)m_target)->isPet() && (m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)) - m_target->HandleStatModifier(UNIT_MOD_ARMOR, TOTAL_VALUE, float(GetModifierValue()), apply); + if(((Creature*)m_target)->isPet() && (GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL)) + m_target->HandleStatModifier(UNIT_MOD_ARMOR, TOTAL_VALUE, float(m_amount), apply); } else { for(int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++) - if(m_modifier.m_miscvalue & (1<<i)) - m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + i), TOTAL_VALUE, float(GetModifierValue()), apply); + if(GetMiscValue() & (1<<i)) + m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + i), TOTAL_VALUE, float(m_amount), apply); } } @@ -4082,30 +4599,30 @@ void Aura::HandleModBaseResistance(bool apply, bool Real) /*** STAT ***/ /********************************/ -void Aura::HandleAuraModStat(bool apply, bool Real) +void AuraEffect::HandleAuraModStat(bool apply, bool Real) { - if (m_modifier.m_miscvalue < -2 || m_modifier.m_miscvalue > 4) + if (GetMiscValue() < -2 || GetMiscValue() > 4) { - sLog.outError("WARNING: Spell %u effect %u have unsupported misc value (%i) for SPELL_AURA_MOD_STAT ",GetId(),GetEffIndex(),m_modifier.m_miscvalue); + sLog.outError("WARNING: Spell %u effect %u have unsupported misc value (%i) for SPELL_AURA_MOD_STAT ",GetId(),GetEffIndex(),GetMiscValue()); return; } for(int32 i = STAT_STRENGTH; i < MAX_STATS; i++) { // -1 or -2 is all stats ( misc < -2 checked in function beginning ) - if (m_modifier.m_miscvalue < 0 || m_modifier.m_miscvalue == i) + if (GetMiscValue() < 0 || GetMiscValue() == i) { - //m_target->ApplyStatMod(Stats(i), m_modifier.m_amount,apply); - m_target->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_VALUE, float(GetModifierValue()), apply); + //m_target->ApplyStatMod(Stats(i), m_amount,apply); + m_target->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_VALUE, float(m_amount), apply); if(m_target->GetTypeId() == TYPEID_PLAYER || ((Creature*)m_target)->isPet()) - m_target->ApplyStatBuffMod(Stats(i),GetModifierValue(),apply); + m_target->ApplyStatBuffMod(Stats(i),m_amount,apply); } } } -void Aura::HandleModPercentStat(bool apply, bool Real) +void AuraEffect::HandleModPercentStat(bool apply, bool Real) { - if (m_modifier.m_miscvalue < -1 || m_modifier.m_miscvalue > 4) + if (GetMiscValue() < -1 || GetMiscValue() > 4) { sLog.outError("WARNING: Misc Value for SPELL_AURA_MOD_PERCENT_STAT not valid"); return; @@ -4117,12 +4634,12 @@ void Aura::HandleModPercentStat(bool apply, bool Real) for (int32 i = STAT_STRENGTH; i < MAX_STATS; ++i) { - if(m_modifier.m_miscvalue == i || m_modifier.m_miscvalue == -1) - m_target->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + i), BASE_PCT, float(GetModifierValue()), apply); + if(GetMiscValue() == i || GetMiscValue() == -1) + m_target->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + i), BASE_PCT, float(m_amount), apply); } } -void Aura::HandleModSpellDamagePercentFromStat(bool /*apply*/, bool Real) +void AuraEffect::HandleModSpellDamagePercentFromStat(bool /*apply*/, bool Real) { if(m_target->GetTypeId() != TYPEID_PLAYER) return; @@ -4133,7 +4650,7 @@ void Aura::HandleModSpellDamagePercentFromStat(bool /*apply*/, bool Real) ((Player*)m_target)->UpdateSpellDamageAndHealingBonus(); } -void Aura::HandleModSpellHealingPercentFromStat(bool /*apply*/, bool Real) +void AuraEffect::HandleModSpellHealingPercentFromStat(bool /*apply*/, bool Real) { if(m_target->GetTypeId() != TYPEID_PLAYER) return; @@ -4142,7 +4659,7 @@ void Aura::HandleModSpellHealingPercentFromStat(bool /*apply*/, bool Real) ((Player*)m_target)->UpdateSpellDamageAndHealingBonus(); } -void Aura::HandleAuraModDispelResist(bool apply, bool Real) +void AuraEffect::HandleAuraModDispelResist(bool apply, bool Real) { if(!Real || !apply) return; @@ -4151,7 +4668,7 @@ void Aura::HandleAuraModDispelResist(bool apply, bool Real) m_target->CastSpell(m_target,44416,true,NULL,this,GetCasterGUID()); } -void Aura::HandleModSpellDamagePercentFromAttackPower(bool /*apply*/, bool Real) +void AuraEffect::HandleModSpellDamagePercentFromAttackPower(bool /*apply*/, bool Real) { if(m_target->GetTypeId() != TYPEID_PLAYER) return; @@ -4162,7 +4679,7 @@ void Aura::HandleModSpellDamagePercentFromAttackPower(bool /*apply*/, bool Real) ((Player*)m_target)->UpdateSpellDamageAndHealingBonus(); } -void Aura::HandleModSpellHealingPercentFromAttackPower(bool /*apply*/, bool Real) +void AuraEffect::HandleModSpellHealingPercentFromAttackPower(bool /*apply*/, bool Real) { if(m_target->GetTypeId() != TYPEID_PLAYER) return; @@ -4171,7 +4688,7 @@ void Aura::HandleModSpellHealingPercentFromAttackPower(bool /*apply*/, bool Real ((Player*)m_target)->UpdateSpellDamageAndHealingBonus(); } -void Aura::HandleModHealingDone(bool /*apply*/, bool Real) +void AuraEffect::HandleModHealingDone(bool /*apply*/, bool Real) { if(m_target->GetTypeId() != TYPEID_PLAYER) return; @@ -4180,9 +4697,9 @@ void Aura::HandleModHealingDone(bool /*apply*/, bool Real) ((Player*)m_target)->UpdateSpellDamageAndHealingBonus(); } -void Aura::HandleModTotalPercentStat(bool apply, bool Real) +void AuraEffect::HandleModTotalPercentStat(bool apply, bool Real) { - if (m_modifier.m_miscvalue < -1 || m_modifier.m_miscvalue > 4) + if (GetMiscValue() < -1 || GetMiscValue() > 4) { sLog.outError("WARNING: Misc Value for SPELL_AURA_MOD_PERCENT_STAT not valid"); return; @@ -4194,16 +4711,16 @@ void Aura::HandleModTotalPercentStat(bool apply, bool Real) for (int32 i = STAT_STRENGTH; i < MAX_STATS; i++) { - if(m_modifier.m_miscvalue == i || m_modifier.m_miscvalue == -1) + if(GetMiscValue() == i || GetMiscValue() == -1) { - m_target->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_PCT, float(GetModifierValue()), apply); + m_target->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_PCT, float(m_amount), apply); if(m_target->GetTypeId() == TYPEID_PLAYER || ((Creature*)m_target)->isPet()) - m_target->ApplyStatPercentBuffMod(Stats(i), GetModifierValue(), apply ); + m_target->ApplyStatPercentBuffMod(Stats(i), m_amount, apply ); } } //recalculate current HP/MP after applying aura modifications (only for spells with 0x10 flag) - if ((m_modifier.m_miscvalue == STAT_STAMINA) && (maxHPValue > 0) && (m_spellProto->Attributes & 0x10)) + if ((GetMiscValue() == STAT_STAMINA) && (maxHPValue > 0) && (m_spellProto->Attributes & 0x10)) { // newHP = (curHP / maxHP) * newMaxHP = (newMaxHP * curHP) / maxHP -> which is better because no int -> double -> int conversion is needed uint32 newHPValue = (m_target->GetMaxHealth() * curHPValue) / maxHPValue; @@ -4211,12 +4728,12 @@ void Aura::HandleModTotalPercentStat(bool apply, bool Real) } } -void Aura::HandleAuraModResistenceOfStatPercent(bool /*apply*/, bool Real) +void AuraEffect::HandleAuraModResistenceOfStatPercent(bool /*apply*/, bool Real) { if(m_target->GetTypeId() != TYPEID_PLAYER) return; - if(m_modifier.m_miscvalue != SPELL_SCHOOL_MASK_NORMAL) + if(GetMiscValue() != SPELL_SCHOOL_MASK_NORMAL) { // support required adding replace UpdateArmor by loop by UpdateResistence at intellect update // and include in UpdateResistence same code as in UpdateArmor for aura mod apply. @@ -4231,128 +4748,53 @@ void Aura::HandleAuraModResistenceOfStatPercent(bool /*apply*/, bool Real) /********************************/ /*** HEAL & ENERGIZE ***/ /********************************/ -void Aura::HandleAuraModTotalHealthPercentRegen(bool apply, bool Real) +void AuraEffect::HandleAuraModTotalHealthPercentRegen(bool apply, bool Real) { - /* - Need additional checking for auras who reduce or increase healing, magic effect like Dumpen Magic, - so this aura not fully working. - */ - if(apply) - { - if(!m_target->isAlive()) - return; - - if((GetSpellProto()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) && !m_target->IsSitState()) - m_target->SetStandState(PLAYER_STATE_SIT); - - if(m_periodicTimer <= 0) - { - m_periodicTimer += m_modifier.periodictime; - - if(m_target->GetHealth() < m_target->GetMaxHealth()) - { - // PeriodicTick can cast triggered spells with stats changes - PeriodicTick(); - } - } - } - m_isPeriodic = apply; } -void Aura::HandleAuraModTotalManaPercentRegen(bool apply, bool Real) +void AuraEffect::HandleAuraModTotalEnergyPercentRegen(bool apply, bool Real) { - if((GetSpellProto()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) && apply && !m_target->IsSitState()) - m_target->SetStandState(PLAYER_STATE_SIT); - if(apply) - { - if(m_modifier.periodictime == 0) - m_modifier.periodictime = 1000; - if(m_periodicTimer <= 0 && m_target->getPowerType() == POWER_MANA) - { - m_periodicTimer += m_modifier.periodictime; - - if(m_target->GetPower(POWER_MANA) < m_target->GetMaxPower(POWER_MANA)) - { - // PeriodicTick can cast triggered spells with stats changes - PeriodicTick(); - } - } - } + if(m_amplitude == 0) + m_amplitude = 1000; + m_periodicTimer = m_amplitude; m_isPeriodic = apply; } -void Aura::HandleModRegen(bool apply, bool Real) // eating +void AuraEffect::HandleModRegen(bool apply, bool Real) // eating { - if(apply) - { - if(!m_target->isAlive()) - return; - - if ((GetSpellProto()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) && !m_target->IsSitState()) - m_target->SetStandState(PLAYER_STATE_SIT); - - if(m_periodicTimer <= 0) - { - m_periodicTimer += 5000; - int32 gain = m_target->ModifyHealth(GetModifierValue()); - Unit *caster = GetCaster(); - if (caster) - { - SpellEntry const *spellProto = GetSpellProto(); - if (spellProto) - m_target->getHostilRefManager().threatAssist(caster, float(gain) * 0.5f, spellProto); - } - } - } + if(m_amplitude == 0) + m_amplitude = 5000; + m_periodicTimer = 5000; m_isPeriodic = apply; } -void Aura::HandleModPowerRegen(bool apply, bool Real) // drinking +void AuraEffect::HandleModPowerRegen(bool apply, bool Real) // drinking { - if ((GetSpellProto()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) && apply && !m_target->IsSitState()) - m_target->SetStandState(PLAYER_STATE_SIT); + if (!Real) + return; - if(apply && m_periodicTimer <= 0) + Powers pt = m_target->getPowerType(); + if(m_amplitude == 0) { - - Powers pt = m_target->getPowerType(); + // Anger Management (only spell use this aura for rage) if (pt == POWER_RAGE) - m_periodicTimer = 3000; - else - m_periodicTimer = 2000; + m_amplitude = 3000; + else + m_amplitude = 2000; + } - if(int32(pt) != m_modifier.m_miscvalue) - return; + m_periodicTimer = 5000; - if ( GetSpellProto()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED ) - { - // eating anim - m_target->HandleEmoteCommand(EMOTE_ONESHOT_EAT); - } - else if( GetId() == 20577 ) - { - // cannibalize anim - m_target->HandleEmoteCommand(398); - } + if (m_target->GetTypeId() == TYPEID_PLAYER && GetMiscValue() == POWER_MANA) + ((Player*)m_target)->UpdateManaRegen(); - // Warrior talent, gain 1 rage every 3 seconds while in combat - // Anger Menagement - // amount = 1+ 16 = 17 = 3,4*5 = 10,2*5/3 - // so 17 is rounded amount for 5 sec tick grow ~ 1 range grow in 3 sec - if(pt == POWER_RAGE) - { - m_target->ModifyPower(pt, m_modifier.m_amount*3/5); - } - } m_isPeriodic = apply; - if (Real && m_target->GetTypeId() == TYPEID_PLAYER && m_modifier.m_miscvalue == POWER_MANA) - ((Player*)m_target)->UpdateManaRegen(); } -void Aura::HandleModPowerRegenPCT(bool /*apply*/, bool Real) +void AuraEffect::HandleModPowerRegenPCT(bool /*apply*/, bool Real) { // spells required only Real aura add/remove if(!Real) @@ -4362,11 +4804,11 @@ void Aura::HandleModPowerRegenPCT(bool /*apply*/, bool Real) return; // Update manaregen value - if (m_modifier.m_miscvalue == POWER_MANA) + if (GetMiscValue() == POWER_MANA) ((Player*)m_target)->UpdateManaRegen(); } -void Aura::HandleModManaRegen(bool /*apply*/, bool Real) +void AuraEffect::HandleModManaRegen(bool /*apply*/, bool Real) { // spells required only Real aura add/remove if(!Real) @@ -4379,7 +4821,7 @@ void Aura::HandleModManaRegen(bool /*apply*/, bool Real) ((Player*)m_target)->UpdateManaRegen(); } -void Aura::HandleComprehendLanguage(bool apply, bool Real) +void AuraEffect::HandleComprehendLanguage(bool apply, bool Real) { if(apply) m_target->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_COMPREHEND_LANG); @@ -4387,32 +4829,32 @@ void Aura::HandleComprehendLanguage(bool apply, bool Real) m_target->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_COMPREHEND_LANG); } -void Aura::HandleAuraModIncreaseHealth(bool apply, bool Real) +void AuraEffect::HandleAuraModIncreaseHealth(bool apply, bool Real) { if(Real) { if(apply) { - m_target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(GetModifierValue()), apply); - m_target->ModifyHealth(m_modifier.m_amount); + m_target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(m_amount), apply); + m_target->ModifyHealth(m_amount); } else { - if (int32(m_target->GetHealth()) > m_modifier.m_amount) - m_target->ModifyHealth(-m_modifier.m_amount); + if (int32(m_target->GetHealth()) > m_amount) + m_target->ModifyHealth(-m_amount); else m_target->SetHealth(1); - m_target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(GetModifierValue()), apply); + m_target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(m_amount), apply); } } } -void Aura::HandleAuraModIncreaseMaxHealth(bool apply, bool Real) +void AuraEffect::HandleAuraModIncreaseMaxHealth(bool apply, bool Real) { uint32 oldhealth = m_target->GetHealth(); double healthPercentage = (double)oldhealth / (double)m_target->GetMaxHealth(); - m_target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(m_modifier.m_amount), apply); + m_target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(m_amount), apply); // refresh percentage if(oldhealth > 0) @@ -4425,38 +4867,43 @@ void Aura::HandleAuraModIncreaseMaxHealth(bool apply, bool Real) } } -void Aura::HandleAuraModIncreaseEnergy(bool apply, bool Real) +void AuraEffect::HandleAuraModIncreaseEnergy(bool apply, bool Real) { Powers powerType = m_target->getPowerType(); - if(int32(powerType) != m_modifier.m_miscvalue) + if(int32(powerType) != GetMiscValue()) return; UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + powerType); - m_target->HandleStatModifier(unitMod, TOTAL_VALUE, float(GetModifierValue()), apply); + m_target->HandleStatModifier(unitMod, TOTAL_VALUE, float(m_amount), apply); } -void Aura::HandleAuraModIncreaseEnergyPercent(bool apply, bool /*Real*/) +void AuraEffect::HandleAuraModIncreaseEnergyPercent(bool apply, bool /*Real*/) { Powers powerType = m_target->getPowerType(); - if(int32(powerType) != m_modifier.m_miscvalue) + if(int32(powerType) != GetMiscValue()) return; UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + powerType); - m_target->HandleStatModifier(unitMod, TOTAL_PCT, float(GetModifierValue()), apply); + m_target->HandleStatModifier(unitMod, TOTAL_PCT, float(m_amount), apply); +} + +void AuraEffect::HandleAuraModIncreaseHealthPercent(bool apply, bool /*Real*/) +{ + m_target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_PCT, float(m_amount), apply); } -void Aura::HandleAuraModIncreaseHealthPercent(bool apply, bool /*Real*/) +void AuraEffect::HandleAuraIncreaseBaseHealthPercent(bool apply, bool /*Real*/) { - m_target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_PCT, float(GetModifierValue()), apply); + m_target->HandleStatModifier(UNIT_MOD_HEALTH, BASE_PCT, float(m_amount), apply); } /********************************/ /*** FIGHT ***/ /********************************/ -void Aura::HandleAuraModParryPercent(bool /*apply*/, bool Real) +void AuraEffect::HandleAuraModParryPercent(bool /*apply*/, bool Real) { if(m_target->GetTypeId()!=TYPEID_PLAYER) return; @@ -4464,25 +4911,25 @@ void Aura::HandleAuraModParryPercent(bool /*apply*/, bool Real) ((Player*)m_target)->UpdateParryPercentage(); } -void Aura::HandleAuraModDodgePercent(bool /*apply*/, bool Real) +void AuraEffect::HandleAuraModDodgePercent(bool /*apply*/, bool Real) { if(m_target->GetTypeId()!=TYPEID_PLAYER) return; ((Player*)m_target)->UpdateDodgePercentage(); - //sLog.outError("BONUS DODGE CHANCE: + %f", float(m_modifier.m_amount)); + //sLog.outError("BONUS DODGE CHANCE: + %f", float(m_amount)); } -void Aura::HandleAuraModBlockPercent(bool /*apply*/, bool Real) +void AuraEffect::HandleAuraModBlockPercent(bool /*apply*/, bool Real) { if(m_target->GetTypeId()!=TYPEID_PLAYER) return; ((Player*)m_target)->UpdateBlockPercentage(); - //sLog.outError("BONUS BLOCK CHANCE: + %f", float(m_modifier.m_amount)); + //sLog.outError("BONUS BLOCK CHANCE: + %f", float(m_amount)); } -void Aura::HandleAuraModRegenInterrupt(bool /*apply*/, bool Real) +void AuraEffect::HandleAuraModRegenInterrupt(bool /*apply*/, bool Real) { // spells required only Real aura add/remove if(!Real) @@ -4494,7 +4941,7 @@ void Aura::HandleAuraModRegenInterrupt(bool /*apply*/, bool Real) ((Player*)m_target)->UpdateManaRegen(); } -void Aura::HandleAuraModCritPercent(bool apply, bool Real) +void AuraEffect::HandleAuraModCritPercent(bool apply, bool Real) { if(m_target->GetTypeId()!=TYPEID_PLAYER) return; @@ -4509,13 +4956,13 @@ void Aura::HandleAuraModCritPercent(bool apply, bool Real) // mods must be applied base at equipped weapon class and subclass comparison // with spell->EquippedItemClass and EquippedItemSubClassMask and EquippedItemInventoryTypeMask - // m_modifier.m_miscvalue comparison with item generated damage types + // GetMiscValue() comparison with item generated damage types if (GetSpellProto()->EquippedItemClass == -1) { - ((Player*)m_target)->HandleBaseModValue(CRIT_PERCENTAGE, FLAT_MOD, float (GetModifierValue()), apply); - ((Player*)m_target)->HandleBaseModValue(OFFHAND_CRIT_PERCENTAGE, FLAT_MOD, float (GetModifierValue()), apply); - ((Player*)m_target)->HandleBaseModValue(RANGED_CRIT_PERCENTAGE, FLAT_MOD, float (GetModifierValue()), apply); + ((Player*)m_target)->HandleBaseModValue(CRIT_PERCENTAGE, FLAT_MOD, float (m_amount), apply); + ((Player*)m_target)->HandleBaseModValue(OFFHAND_CRIT_PERCENTAGE, FLAT_MOD, float (m_amount), apply); + ((Player*)m_target)->HandleBaseModValue(RANGED_CRIT_PERCENTAGE, FLAT_MOD, float (m_amount), apply); } else { @@ -4523,18 +4970,33 @@ void Aura::HandleAuraModCritPercent(bool apply, bool Real) } } -void Aura::HandleModHitChance(bool apply, bool Real) +void AuraEffect::HandleModHitChance(bool apply, bool Real) { - m_target->m_modMeleeHitChance += apply ? GetModifierValue() : -GetModifierValue(); - m_target->m_modRangedHitChance += apply ? GetModifierValue() : -GetModifierValue(); + if(m_target->GetTypeId() == TYPEID_PLAYER) + { + ((Player*)m_target)->UpdateMeleeHitChances(); + ((Player*)m_target)->UpdateRangedHitChances(); + } + else + { + m_target->m_modMeleeHitChance += apply ? m_amount : (-m_amount); + m_target->m_modRangedHitChance += apply ? m_amount : (-m_amount); + } } -void Aura::HandleModSpellHitChance(bool apply, bool Real) +void AuraEffect::HandleModSpellHitChance(bool apply, bool Real) { - m_target->m_modSpellHitChance += apply ? GetModifierValue(): -GetModifierValue(); + if(m_target->GetTypeId() == TYPEID_PLAYER) + { + ((Player*)m_target)->UpdateSpellHitChances(); + } + else + { + m_target->m_modSpellHitChance += apply ? m_amount: (-m_amount); + } } -void Aura::HandleModSpellCritChance(bool apply, bool Real) +void AuraEffect::HandleModSpellCritChance(bool apply, bool Real) { // spells required only Real aura add/remove if(!Real) @@ -4546,11 +5008,11 @@ void Aura::HandleModSpellCritChance(bool apply, bool Real) } else { - m_target->m_baseSpellCritChance += apply ? GetModifierValue():-GetModifierValue(); + m_target->m_baseSpellCritChance += apply ? m_amount:-m_amount; } } -void Aura::HandleModSpellCritChanceShool(bool /*apply*/, bool Real) +void AuraEffect::HandleModSpellCritChanceShool(bool /*apply*/, bool Real) { // spells required only Real aura add/remove if(!Real) @@ -4560,7 +5022,7 @@ void Aura::HandleModSpellCritChanceShool(bool /*apply*/, bool Real) return; for(int school = SPELL_SCHOOL_NORMAL; school < MAX_SPELL_SCHOOL; ++school) - if (m_modifier.m_miscvalue & (1<<school)) + if (GetMiscValue() & (1<<school)) ((Player*)m_target)->UpdateSpellCritChance(school); } @@ -4568,109 +5030,111 @@ void Aura::HandleModSpellCritChanceShool(bool /*apply*/, bool Real) /*** ATTACK SPEED ***/ /********************************/ -void Aura::HandleModCastingSpeed(bool apply, bool Real) +void AuraEffect::HandleModCastingSpeed(bool apply, bool Real) { - m_target->ApplyCastTimePercentMod(GetModifierValue(),apply); + m_target->ApplyCastTimePercentMod(m_amount,apply); } -void Aura::HandleModMeleeRangedSpeedPct(bool apply, bool Real) +void AuraEffect::HandleModMeleeRangedSpeedPct(bool apply, bool Real) { - m_target->ApplyAttackTimePercentMod(BASE_ATTACK,GetModifierValue(),apply); - m_target->ApplyAttackTimePercentMod(OFF_ATTACK,GetModifierValue(),apply); - m_target->ApplyAttackTimePercentMod(RANGED_ATTACK, GetModifierValue(), apply); + m_target->ApplyAttackTimePercentMod(BASE_ATTACK,m_amount,apply); + m_target->ApplyAttackTimePercentMod(OFF_ATTACK,m_amount,apply); + m_target->ApplyAttackTimePercentMod(RANGED_ATTACK, m_amount, apply); } -void Aura::HandleModCombatSpeedPct(bool apply, bool Real) +void AuraEffect::HandleModCombatSpeedPct(bool apply, bool Real) { - m_target->ApplyCastTimePercentMod(GetModifierValue(),apply); - m_target->ApplyAttackTimePercentMod(BASE_ATTACK,GetModifierValue(),apply); - m_target->ApplyAttackTimePercentMod(OFF_ATTACK,GetModifierValue(),apply); - m_target->ApplyAttackTimePercentMod(RANGED_ATTACK, GetModifierValue(), apply); + m_target->ApplyCastTimePercentMod(m_amount,apply); + m_target->ApplyAttackTimePercentMod(BASE_ATTACK,m_amount,apply); + m_target->ApplyAttackTimePercentMod(OFF_ATTACK,m_amount,apply); + m_target->ApplyAttackTimePercentMod(RANGED_ATTACK, m_amount, apply); } -void Aura::HandleModAttackSpeed(bool apply, bool Real) +void AuraEffect::HandleModAttackSpeed(bool apply, bool Real) { if(!m_target->isAlive() ) return; - m_target->ApplyAttackTimePercentMod(BASE_ATTACK,GetModifierValue(),apply); + m_target->ApplyAttackTimePercentMod(BASE_ATTACK,m_amount,apply); } -void Aura::HandleHaste(bool apply, bool Real) +void AuraEffect::HandleHaste(bool apply, bool Real) { - m_target->ApplyAttackTimePercentMod(BASE_ATTACK, GetModifierValue(),apply); - m_target->ApplyAttackTimePercentMod(OFF_ATTACK, GetModifierValue(),apply); - m_target->ApplyAttackTimePercentMod(RANGED_ATTACK,GetModifierValue(),apply); + m_target->ApplyAttackTimePercentMod(BASE_ATTACK, m_amount,apply); + m_target->ApplyAttackTimePercentMod(OFF_ATTACK, m_amount,apply); + m_target->ApplyAttackTimePercentMod(RANGED_ATTACK,m_amount,apply); } -void Aura::HandleAuraModRangedHaste(bool apply, bool Real) +void AuraEffect::HandleAuraModRangedHaste(bool apply, bool Real) { - m_target->ApplyAttackTimePercentMod(RANGED_ATTACK, GetModifierValue(), apply); + m_target->ApplyAttackTimePercentMod(RANGED_ATTACK, m_amount, apply); } -void Aura::HandleRangedAmmoHaste(bool apply, bool Real) +void AuraEffect::HandleRangedAmmoHaste(bool apply, bool Real) { if(m_target->GetTypeId() != TYPEID_PLAYER) return; - m_target->ApplyAttackTimePercentMod(RANGED_ATTACK,GetModifierValue(), apply); + m_target->ApplyAttackTimePercentMod(RANGED_ATTACK,m_amount, apply); } /********************************/ /*** ATTACK POWER ***/ /********************************/ -void Aura::HandleAuraModAttackPower(bool apply, bool Real) +void AuraEffect::HandleAuraModAttackPower(bool apply, bool Real) { - m_target->HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(GetModifierValue()), apply); + m_target->HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(m_amount), apply); } -void Aura::HandleAuraModRangedAttackPower(bool apply, bool Real) +void AuraEffect::HandleAuraModRangedAttackPower(bool apply, bool Real) { if((m_target->getClassMask() & CLASSMASK_WAND_USERS)!=0) return; - m_target->HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(GetModifierValue()), apply); + m_target->HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(m_amount), apply); } -void Aura::HandleAuraModAttackPowerPercent(bool apply, bool Real) +void AuraEffect::HandleAuraModAttackPowerPercent(bool apply, bool Real) { //UNIT_FIELD_ATTACK_POWER_MULTIPLIER = multiplier - 1 - m_target->HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_PCT, float(GetModifierValue()), apply); + m_target->HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_PCT, float(m_amount), apply); } -void Aura::HandleAuraModRangedAttackPowerPercent(bool apply, bool Real) +void AuraEffect::HandleAuraModRangedAttackPowerPercent(bool apply, bool Real) { if((m_target->getClassMask() & CLASSMASK_WAND_USERS)!=0) return; //UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER = multiplier - 1 - m_target->HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_PCT, float(GetModifierValue()), apply); + m_target->HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_PCT, float(m_amount), apply); } -void Aura::HandleAuraModRangedAttackPowerOfStatPercent(bool apply, bool Real) +void AuraEffect::HandleAuraModRangedAttackPowerOfStatPercent(bool apply, bool Real) { // spells required only Real aura add/remove if(!Real) return; - if(m_target->GetTypeId() == TYPEID_PLAYER && (m_target->getClassMask() & CLASSMASK_WAND_USERS)!=0) - return; + // Recalculate bonus + if(m_target->GetTypeId() == TYPEID_PLAYER && !(m_target->getClassMask() & CLASSMASK_WAND_USERS)) + ((Player*)m_target)->UpdateAttackPowerAndDamage(true); +} - if(m_modifier.m_miscvalue != STAT_INTELLECT) - { - // support required adding UpdateAttackPowerAndDamage calls at stat update - sLog.outError("Aura SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT (212) need support non-intellect stats!"); +void AuraEffect::HandleAuraModAttackPowerOfStatPercent(bool apply, bool Real) +{ + // spells required only Real aura add/remove + if(!Real) return; - } // Recalculate bonus - ((Player*)m_target)->UpdateAttackPowerAndDamage(true); + if(m_target->GetTypeId() == TYPEID_PLAYER) + ((Player*)m_target)->UpdateAttackPowerAndDamage(false); } /********************************/ /*** DAMAGE BONUS ***/ /********************************/ -void Aura::HandleModDamageDone(bool apply, bool Real) +void AuraEffect::HandleModDamageDone(bool apply, bool Real) { // apply item specific bonuses for already equipped weapon if(Real && m_target->GetTypeId()==TYPEID_PLAYER) @@ -4680,23 +5144,23 @@ void Aura::HandleModDamageDone(bool apply, bool Real) ((Player*)m_target)->_ApplyWeaponDependentAuraDamageMod(pItem,WeaponAttackType(i),this,apply); } - // m_modifier.m_miscvalue is bitmask of spell schools + // GetMiscValue() is bitmask of spell schools // 1 ( 0-bit ) - normal school damage (SPELL_SCHOOL_MASK_NORMAL) // 126 - full bitmask all magic damages (SPELL_SCHOOL_MASK_MAGIC) including wands // 127 - full bitmask any damages // // mods must be applied base at equipped weapon class and subclass comparison // with spell->EquippedItemClass and EquippedItemSubClassMask and EquippedItemInventoryTypeMask - // m_modifier.m_miscvalue comparison with item generated damage types + // GetMiscValue() comparison with item generated damage types - if((m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_NORMAL) != 0) + if((GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) != 0) { // apply generic physical damage bonuses including wand case if (GetSpellProto()->EquippedItemClass == -1 || m_target->GetTypeId() != TYPEID_PLAYER) { - m_target->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE, float(GetModifierValue()), apply); - m_target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE, float(GetModifierValue()), apply); - m_target->HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_VALUE, float(GetModifierValue()), apply); + m_target->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE, float(m_amount), apply); + m_target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE, float(m_amount), apply); + m_target->HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_VALUE, float(m_amount), apply); } else { @@ -4705,15 +5169,15 @@ void Aura::HandleModDamageDone(bool apply, bool Real) if(m_target->GetTypeId() == TYPEID_PLAYER) { - if(m_positive) - m_target->ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS,GetModifierValue(),apply); + if(GetParentAura()->IsPositive()) + m_target->ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS,m_amount,apply); else - m_target->ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG,GetModifierValue(),apply); + m_target->ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG,m_amount,apply); } } // Skip non magic case for speedup - if((m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_MAGIC) == 0) + if((GetMiscValue() & SPELL_SCHOOL_MASK_MAGIC) == 0) return; if( GetSpellProto()->EquippedItemClass != -1 || GetSpellProto()->EquippedItemInventoryTypeMask != 0 ) @@ -4729,31 +5193,31 @@ void Aura::HandleModDamageDone(bool apply, bool Real) // This information for client side use only if(m_target->GetTypeId() == TYPEID_PLAYER) { - if(m_positive) + if(GetParentAura()->IsPositive()) { for(int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; i++) { - if((m_modifier.m_miscvalue & (1<<i)) != 0) - m_target->ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS+i,GetModifierValue(),apply); + if((GetMiscValue() & (1<<i)) != 0) + m_target->ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS+i,m_amount,apply); } } else { for(int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; i++) { - if((m_modifier.m_miscvalue & (1<<i)) != 0) - m_target->ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG+i,GetModifierValue(),apply); + if((GetMiscValue() & (1<<i)) != 0) + m_target->ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG+i,m_amount,apply); } } - Pet* pet = m_target->GetPet(); + Pet* pet = ((Player*)m_target)->GetPet(); if(pet) pet->UpdateAttackPowerAndDamage(); } } -void Aura::HandleModDamagePercentDone(bool apply, bool Real) +void AuraEffect::HandleModDamagePercentDone(bool apply, bool Real) { - sLog.outDebug("AURA MOD DAMAGE type:%u negative:%u", m_modifier.m_miscvalue, m_positive ? 0 : 1); + sLog.outDebug("AURA MOD DAMAGE type:%u negative:%u", GetMiscValue(), GetParentAura()->IsPositive() ? 0 : 1); // apply item specific bonuses for already equipped weapon if(Real && m_target->GetTypeId()==TYPEID_PLAYER) @@ -4763,23 +5227,23 @@ void Aura::HandleModDamagePercentDone(bool apply, bool Real) ((Player*)m_target)->_ApplyWeaponDependentAuraDamageMod(pItem,WeaponAttackType(i),this,apply); } - // m_modifier.m_miscvalue is bitmask of spell schools + // GetMiscValue() is bitmask of spell schools // 1 ( 0-bit ) - normal school damage (SPELL_SCHOOL_MASK_NORMAL) // 126 - full bitmask all magic damages (SPELL_SCHOOL_MASK_MAGIC) including wand // 127 - full bitmask any damages // // mods must be applied base at equipped weapon class and subclass comparison // with spell->EquippedItemClass and EquippedItemSubClassMask and EquippedItemInventoryTypeMask - // m_modifier.m_miscvalue comparison with item generated damage types + // GetMiscValue() comparison with item generated damage types - if((m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_NORMAL) != 0) + if((GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) != 0) { // apply generic physical damage bonuses including wand case if (GetSpellProto()->EquippedItemClass == -1 || m_target->GetTypeId() != TYPEID_PLAYER) { - m_target->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, float(GetModifierValue()), apply); - m_target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT, float(GetModifierValue()), apply); - m_target->HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_PCT, float(GetModifierValue()), apply); + m_target->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, float(m_amount), apply); + m_target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT, float(m_amount), apply); + m_target->HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_PCT, float(m_amount), apply); } else { @@ -4787,11 +5251,11 @@ void Aura::HandleModDamagePercentDone(bool apply, bool Real) } // For show in client if(m_target->GetTypeId() == TYPEID_PLAYER) - m_target->ApplyModSignedFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT,m_modifier.m_amount/100.0f,apply); + m_target->ApplyModSignedFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT,m_amount/100.0f,apply); } // Skip non magic case for speedup - if((m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_MAGIC) == 0) + if((GetMiscValue() & SPELL_SCHOOL_MASK_MAGIC) == 0) return; if( GetSpellProto()->EquippedItemClass != -1 || GetSpellProto()->EquippedItemInventoryTypeMask != 0 ) @@ -4807,10 +5271,10 @@ void Aura::HandleModDamagePercentDone(bool apply, bool Real) // Send info to client if(m_target->GetTypeId() == TYPEID_PLAYER) for(int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i) - m_target->ApplyModSignedFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT+i,m_modifier.m_amount/100.0f,apply); + m_target->ApplyModSignedFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT+i,m_amount/100.0f,apply); } -void Aura::HandleModOffhandDamagePercent(bool apply, bool Real) +void AuraEffect::HandleModOffhandDamagePercent(bool apply, bool Real) { // spells required only Real aura add/remove if(!Real) @@ -4818,177 +5282,75 @@ void Aura::HandleModOffhandDamagePercent(bool apply, bool Real) sLog.outDebug("AURA MOD OFFHAND DAMAGE"); - m_target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT, float(GetModifierValue()), apply); + m_target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT, float(m_amount), apply); } /********************************/ /*** POWER COST ***/ /********************************/ -void Aura::HandleModPowerCostPCT(bool apply, bool Real) +void AuraEffect::HandleModPowerCostPCT(bool apply, bool Real) { // spells required only Real aura add/remove if(!Real) return; - float amount = GetModifierValue() /100.0f; + float amount = m_amount /100.0f; for(int i = 0; i < MAX_SPELL_SCHOOL; ++i) - if(m_modifier.m_miscvalue & (1<<i)) + if(GetMiscValue() & (1<<i)) m_target->ApplyModSignedFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER+i,amount,apply); } -void Aura::HandleModPowerCost(bool apply, bool Real) +void AuraEffect::HandleModPowerCost(bool apply, bool Real) { // spells required only Real aura add/remove if(!Real) return; for(int i = 0; i < MAX_SPELL_SCHOOL; ++i) - if(m_modifier.m_miscvalue & (1<<i)) - m_target->ApplyModInt32Value(UNIT_FIELD_POWER_COST_MODIFIER+i,GetModifierValue(),apply); + if(GetMiscValue() & (1<<i)) + m_target->ApplyModInt32Value(UNIT_FIELD_POWER_COST_MODIFIER+i,m_amount,apply); +} + +void AuraEffect::HandleNoReagentUseAura(bool Apply, bool Real) +{ + // spells required only Real aura add/remove + if(!Real) + return; + if(m_target->GetTypeId() != TYPEID_PLAYER) + return; + flag96 mask; + Unit::AuraEffectList const& noReagent = m_target->GetAurasByType(SPELL_AURA_NO_REAGENT_USE); + for(Unit::AuraEffectList::const_iterator i = noReagent.begin(); i != noReagent.end(); ++i) + mask |= (*i)->m_spellProto->EffectSpellClassMask[(*i)->m_effIndex]; + + m_target->SetUInt32Value(PLAYER_NO_REAGENT_COST_1 , mask[0]); + m_target->SetUInt32Value(PLAYER_NO_REAGENT_COST_1+1, mask[1]); + m_target->SetUInt32Value(PLAYER_NO_REAGENT_COST_1+2, mask[2]); } /*********************************************************/ /*** OTHERS ***/ /*********************************************************/ -void Aura::HandleShapeshiftBoosts(bool apply) +void AuraEffect::HandleAuraAllowOnlyAbility(bool apply, bool Real) { - uint32 spellId = 0; - uint32 spellId2 = 0; - uint32 HotWSpellId = 0; - - switch(GetModifier()->m_miscvalue) - { - case FORM_CAT: - spellId = 3025; - HotWSpellId = 24900; - break; - case FORM_TREE: - spellId = 5420; - break; - case FORM_TRAVEL: - spellId = 5419; - break; - case FORM_AQUA: - spellId = 5421; - break; - case FORM_BEAR: - spellId = 1178; - spellId2 = 21178; - HotWSpellId = 24899; - break; - case FORM_DIREBEAR: - spellId = 9635; - spellId2 = 21178; - HotWSpellId = 24899; - break; - case FORM_BATTLESTANCE: - spellId = 21156; - break; - case FORM_DEFENSIVESTANCE: - spellId = 7376; - break; - case FORM_BERSERKERSTANCE: - spellId = 7381; - break; - case FORM_MOONKIN: - spellId = 24905; - // aura from effect trigger spell - spellId2 = 24907; - break; - case FORM_FLIGHT: - spellId = 33948; - break; - case FORM_FLIGHT_EPIC: - spellId = 40122; - spellId2 = 40121; - break; - case FORM_SPIRITOFREDEMPTION: - spellId = 27792; - spellId2 = 27795; // must be second, this important at aura remove to prevent to early iterator invalidation. - break; - case FORM_GHOSTWOLF: - case FORM_AMBIENT: - case FORM_GHOUL: - case FORM_SHADOW: - case FORM_STEALTH: - case FORM_CREATURECAT: - case FORM_CREATUREBEAR: - spellId = 0; - break; - } - - uint32 form = GetModifier()->m_miscvalue-1; - - if(apply) - { - if (spellId) m_target->CastSpell(m_target, spellId, true, NULL, this ); - if (spellId2) m_target->CastSpell(m_target, spellId2, true, NULL, this); + if(!Real) + return; - if(m_target->GetTypeId() == TYPEID_PLAYER) - { - const PlayerSpellMap& sp_list = ((Player *)m_target)->GetSpellMap(); - for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr) - { - if(itr->second->state == PLAYERSPELL_REMOVED) continue; - if(itr->first==spellId || itr->first==spellId2) continue; - SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first); - if (!spellInfo || !(spellInfo->Attributes & ((1<<6) | (1<<7)))) continue; - if (spellInfo->Stances & (1<<form)) - m_target->CastSpell(m_target, itr->first, true, NULL, this); - } - //LotP - if (((Player*)m_target)->HasSpell(17007)) - { - SpellEntry const *spellInfo = sSpellStore.LookupEntry(24932); - if (spellInfo && spellInfo->Stances & (1<<form)) - m_target->CastSpell(m_target, 24932, true, NULL, this); - } - // HotW - if (HotWSpellId) - { - Unit::AuraList const& mModTotalStatPct = m_target->GetAurasByType(SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE); - for(Unit::AuraList::const_iterator i = mModTotalStatPct.begin(); i != mModTotalStatPct.end(); ++i) - { - if ((*i)->GetSpellProto()->SpellIconID == 240 && (*i)->GetModifier()->m_miscvalue == 3) - { - int32 HotWMod = (*i)->GetModifier()->m_amount; - if(GetModifier()->m_miscvalue == FORM_CAT) - HotWMod /= 2; + if(!apply && m_target->HasAuraType(SPELL_AURA_ALLOW_ONLY_ABILITY)) + return; - m_target->CastCustomSpell(m_target, HotWSpellId, &HotWMod, NULL, NULL, true, NULL, this); - break; - } - } - } - } - } - else + if(m_target->GetTypeId()==TYPEID_PLAYER) { - m_target->RemoveAurasDueToSpell(spellId); - m_target->RemoveAurasDueToSpell(spellId2); - - Unit::AuraMap& tAuras = m_target->GetAuras(); - for (Unit::AuraMap::iterator itr = tAuras.begin(); itr != tAuras.end();) - { - if (itr->second->IsRemovedOnShapeLost()) - { - m_target->RemoveAurasDueToSpell(itr->second->GetId()); - itr = tAuras.begin(); - } - else - { - ++itr; - } - } + if (apply) + m_target->SetFlag(PLAYER_FLAGS, PLAYER_ALLOW_ONLY_ABILITY); + else + m_target->RemoveFlag(PLAYER_FLAGS, PLAYER_ALLOW_ONLY_ABILITY); } - - /*double healthPercentage = (double)m_target->GetHealth() / (double)m_target->GetMaxHealth(); - m_target->SetHealth(uint32(ceil((double)m_target->GetMaxHealth() * healthPercentage)));*/ } -void Aura::HandleAuraEmpathy(bool apply, bool Real) +void AuraEffect::HandleAuraEmpathy(bool apply, bool Real) { if(m_target->GetTypeId() != TYPEID_UNIT) return; @@ -4998,15 +5360,15 @@ void Aura::HandleAuraEmpathy(bool apply, bool Real) m_target->ApplyModUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_SPECIALINFO, apply); } -void Aura::HandleAuraUntrackable(bool apply, bool Real) +void AuraEffect::HandleAuraUntrackable(bool apply, bool Real) { if(apply) - m_target->SetFlag(UNIT_FIELD_BYTES_1, PLAYER_STATE_FLAG_UNTRACKABLE); + m_target->SetByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_UNTRACKABLE); else - m_target->RemoveFlag(UNIT_FIELD_BYTES_1, PLAYER_STATE_FLAG_UNTRACKABLE); + m_target->RemoveByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_UNTRACKABLE); } -void Aura::HandleAuraModPacify(bool apply, bool Real) +void AuraEffect::HandleAuraModPacify(bool apply, bool Real) { if(m_target->GetTypeId() != TYPEID_PLAYER) return; @@ -5017,13 +5379,13 @@ void Aura::HandleAuraModPacify(bool apply, bool Real) m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED); } -void Aura::HandleAuraModPacifyAndSilence(bool apply, bool Real) +void AuraEffect::HandleAuraModPacifyAndSilence(bool apply, bool Real) { HandleAuraModPacify(apply,Real); HandleAuraModSilence(apply,Real); } -void Aura::HandleAuraGhost(bool apply, bool Real) +void AuraEffect::HandleAuraGhost(bool apply, bool Real) { if(m_target->GetTypeId() != TYPEID_PLAYER) return; @@ -5038,7 +5400,7 @@ void Aura::HandleAuraGhost(bool apply, bool Real) } } -void Aura::HandleAuraAllowFlight(bool apply, bool Real) +void AuraEffect::HandleAuraAllowFlight(bool apply, bool Real) { // all applied/removed only at real aura add/remove if(!Real) @@ -5047,15 +5409,29 @@ void Aura::HandleAuraAllowFlight(bool apply, bool Real) // allow fly WorldPacket data; if(apply) + { data.Initialize(SMSG_MOVE_SET_CAN_FLY, 12); + if(m_target->GetTypeId() == TYPEID_UNIT) + { + m_target->SetByteFlag(UNIT_FIELD_BYTES_1, 3, 0x02); + m_target->AddUnitMovementFlag(MOVEMENTFLAG_FLYING2); + } + } else + { data.Initialize(SMSG_MOVE_UNSET_CAN_FLY, 12); + if(m_target->GetTypeId() == TYPEID_UNIT) + { + m_target->RemoveByteFlag(UNIT_FIELD_BYTES_1, 3, 0x02); + m_target->RemoveUnitMovementFlag(MOVEMENTFLAG_FLYING2); + } + } data.append(m_target->GetPackGUID()); data << uint32(0); // unk m_target->SendMessageToSet(&data, true); } -void Aura::HandleModRating(bool apply, bool Real) +void AuraEffect::HandleModRating(bool apply, bool Real) { // spells required only Real aura add/remove if(!Real) @@ -5065,11 +5441,25 @@ void Aura::HandleModRating(bool apply, bool Real) return; for (uint32 rating = 0; rating < MAX_COMBAT_RATING; ++rating) - if (m_modifier.m_miscvalue & (1 << rating)) - ((Player*)m_target)->ApplyRatingMod(CombatRating(rating), GetModifierValue(), apply); + if (GetMiscValue() & (1 << rating)) + ((Player*)m_target)->ApplyRatingMod(CombatRating(rating), m_amount, apply); +} + +void AuraEffect::HandleModRatingFromStat(bool apply, bool Real) +{ + // spells required only Real aura add/remove + if(!Real) + return; + + if(m_target->GetTypeId() != TYPEID_PLAYER) + return; + // Just recalculate ratings + for (uint32 rating = 0; rating < MAX_COMBAT_RATING; ++rating) + if (GetMiscValue() & (1 << rating)) + ((Player*)m_target)->ApplyRatingMod(CombatRating(rating), 0, apply); } -void Aura::HandleForceMoveForward(bool apply, bool Real) +void AuraEffect::HandleForceMoveForward(bool apply, bool Real) { if(!Real || m_target->GetTypeId() != TYPEID_PLAYER) return; @@ -5079,7 +5469,7 @@ void Aura::HandleForceMoveForward(bool apply, bool Real) m_target->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FORCE_MOVE); } -void Aura::HandleAuraModExpertise(bool /*apply*/, bool Real) +void AuraEffect::HandleAuraModExpertise(bool /*apply*/, bool Real) { if(m_target->GetTypeId() != TYPEID_PLAYER) return; @@ -5088,7 +5478,7 @@ void Aura::HandleAuraModExpertise(bool /*apply*/, bool Real) ((Player*)m_target)->UpdateExpertise(OFF_ATTACK); } -void Aura::HandleModTargetResistance(bool apply, bool Real) +void AuraEffect::HandleModTargetResistance(bool apply, bool Real) { // spells required only Real aura add/remove if(!Real) @@ -5096,25 +5486,25 @@ void Aura::HandleModTargetResistance(bool apply, bool Real) // applied to damage as HandleNoImmediateEffect in Unit::CalcAbsorbResist and Unit::CalcArmorReducedDamage // show armor penetration - if (m_target->GetTypeId() == TYPEID_PLAYER && (m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)) - m_target->ApplyModInt32Value(PLAYER_FIELD_MOD_TARGET_PHYSICAL_RESISTANCE,GetModifierValue(), apply); + if (m_target->GetTypeId() == TYPEID_PLAYER && (GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL)) + m_target->ApplyModInt32Value(PLAYER_FIELD_MOD_TARGET_PHYSICAL_RESISTANCE,m_amount, apply); // show as spell penetration only full spell penetration bonuses (all resistances except armor and holy - if (m_target->GetTypeId() == TYPEID_PLAYER && (m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_SPELL)==SPELL_SCHOOL_MASK_SPELL) - m_target->ApplyModInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE,GetModifierValue(), apply); + if (m_target->GetTypeId() == TYPEID_PLAYER && (GetMiscValue() & SPELL_SCHOOL_MASK_SPELL)==SPELL_SCHOOL_MASK_SPELL) + m_target->ApplyModInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE,m_amount, apply); } -void Aura::HandleShieldBlockValue(bool apply, bool Real) +void AuraEffect::HandleShieldBlockValue(bool apply, bool Real) { BaseModType modType = FLAT_MOD; - if(m_modifier.m_auraname == SPELL_AURA_MOD_SHIELD_BLOCKVALUE_PCT) + if(m_auraName == SPELL_AURA_MOD_SHIELD_BLOCKVALUE_PCT) modType = PCT_MOD; if(m_target->GetTypeId() == TYPEID_PLAYER) - ((Player*)m_target)->HandleBaseModValue(SHIELD_BLOCK_VALUE, modType, float(GetModifierValue()), apply); + ((Player*)m_target)->HandleBaseModValue(SHIELD_BLOCK_VALUE, modType, float(m_amount), apply); } -void Aura::HandleAuraRetainComboPoints(bool apply, bool Real) +void AuraEffect::HandleAuraRetainComboPoints(bool apply, bool Real) { // spells required only Real aura add/remove if(!Real) @@ -5127,23 +5517,26 @@ void Aura::HandleAuraRetainComboPoints(bool apply, bool Real) // combo points was added in SPELL_EFFECT_ADD_COMBO_POINTS handler // remove only if aura expire by time (in case combo points amount change aura removed without combo points lost) - if( !apply && m_duration==0 && target->GetComboTarget()) + if( !apply && GetParentAura()->GetAuraDuration()==0 && target->GetComboTarget()) if(Unit* unit = ObjectAccessor::GetUnit(*m_target,target->GetComboTarget())) - target->AddComboPoints(unit, -GetModifierValue()); + target->AddComboPoints(unit, -m_amount); } -void Aura::HandleModUnattackable( bool Apply, bool Real ) +void AuraEffect::HandleModUnattackable( bool Apply, bool Real ) { - if(Real && Apply) + if(!Real) + return; + + if(Apply) { m_target->CombatStop(); - m_target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_UNATTACKABLE); + m_target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION); } - m_target->ApplyModFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE,Apply); + m_target->ApplyModFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE, Apply); } -void Aura::HandleSpiritOfRedemption( bool apply, bool Real ) +void AuraEffect::HandleSpiritOfRedemption( bool apply, bool Real ) { // spells required only Real aura add/remove if(!Real) @@ -5159,7 +5552,7 @@ void Aura::HandleSpiritOfRedemption( bool apply, bool Real ) // set stand state (expected in this form) if(!m_target->IsStandState()) - m_target->SetStandState(PLAYER_STATE_NONE); + m_target->SetStandState(UNIT_STAND_STATE_STAND); } m_target->SetHealth(1); @@ -5169,36 +5562,7 @@ void Aura::HandleSpiritOfRedemption( bool apply, bool Real ) m_target->setDeathState(JUST_DIED); } -void Aura::CleanupTriggeredSpells() -{ - uint32 tSpellId = m_spellProto->EffectTriggerSpell[GetEffIndex()]; - if(!tSpellId) - return; - - SpellEntry const* tProto = sSpellStore.LookupEntry(tSpellId); - if(!tProto) - return; - - if(GetSpellDuration(tProto) != -1) - return; - - // needed for spell 43680, maybe others - // TODO: is there a spell flag, which can solve this in a more sophisticated way? - if(m_spellProto->EffectApplyAuraName[GetEffIndex()] == SPELL_AURA_PERIODIC_TRIGGER_SPELL && - GetSpellDuration(m_spellProto) == m_spellProto->EffectAmplitude[GetEffIndex()]) - return; - m_target->RemoveAurasDueToSpell(tSpellId); -} - -void Aura::HandleAuraPowerBurn(bool apply, bool Real) -{ - if (m_periodicTimer <= 0) - m_periodicTimer += m_modifier.periodictime; - - m_isPeriodic = apply; -} - -void Aura::HandleSchoolAbsorb(bool apply, bool Real) +void AuraEffect::HandleSchoolAbsorb(bool apply, bool Real) { if(!Real) return; @@ -5212,29 +5576,26 @@ void Aura::HandleSchoolAbsorb(bool apply, bool Real) switch(m_spellProto->SpellFamilyName) { case SPELLFAMILY_PRIEST: - if(m_spellProto->SpellFamilyFlags == 0x1) //PW:S + if(m_spellProto->SpellFamilyFlags.IsEqual(0x1)) //PW:S { //+30% from +healing bonus DoneActualBenefit = caster->SpellBaseHealingBonus(GetSpellSchoolMask(m_spellProto)) * 0.3f; - break; } break; case SPELLFAMILY_MAGE: - if(m_spellProto->SpellFamilyFlags == 0x80100 || m_spellProto->SpellFamilyFlags == 0x8 || m_spellProto->SpellFamilyFlags == 0x100000000LL) + if(m_spellProto->SpellFamilyFlags.IsEqual(0x80100) || m_spellProto->SpellFamilyFlags.IsEqual(0x8) || m_spellProto->SpellFamilyFlags.IsEqual(0,0x1)) { //frost ward, fire ward, ice barrier //+10% from +spd bonus DoneActualBenefit = caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellProto)) * 0.1f; - break; } break; case SPELLFAMILY_WARLOCK: - if(m_spellProto->SpellFamilyFlags == 0x00) + if(m_spellProto->SpellFamilyFlags.IsEqual(0,0,0x40)) { //shadow ward //+10% from +spd bonus DoneActualBenefit = caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellProto)) * 0.1f; - break; } break; default: @@ -5243,17 +5604,17 @@ void Aura::HandleSchoolAbsorb(bool apply, bool Real) DoneActualBenefit *= caster->CalculateLevelPenalty(GetSpellProto()); - m_modifier.m_amount += (int32)DoneActualBenefit; + m_amount += (int32)DoneActualBenefit; } } } -void Aura::PeriodicTick() +void AuraEffect::PeriodicTick() { if(!m_target->isAlive()) return; - switch(m_modifier.m_auraname) + switch(GetAuraName()) { case SPELL_AURA_PERIODIC_DAMAGE: case SPELL_AURA_PERIODIC_DAMAGE_PERCENT: @@ -5271,7 +5632,7 @@ void Aura::PeriodicTick() return; // some auras remove at specific health level or more - if(m_modifier.m_auraname==SPELL_AURA_PERIODIC_DAMAGE) + if(m_auraName==SPELL_AURA_PERIODIC_DAMAGE) { switch(GetId()) { @@ -5298,18 +5659,18 @@ void Aura::PeriodicTick() } case 41337:// aura of anger { - Unit::AuraList const& mMod = m_target->GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE); - for(Unit::AuraList::const_iterator i = mMod.begin(); i != mMod.end(); ++i) + Unit::AuraEffectList const& mMod = m_target->GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE); + for(Unit::AuraEffectList::const_iterator i = mMod.begin(); i != mMod.end(); ++i) { if ((*i)->GetId() == 41337) { (*i)->ApplyModifier(false); - (*i)->GetModifier()->m_amount += 5; + (*i)->SetAmount((*i)->GetAmount()+5); (*i)->ApplyModifier(true); break; } } - m_modifier.m_amount = 100 * m_tickNumber; + m_amount = 100 * m_tickNumber; }break; default: break; @@ -5321,30 +5682,27 @@ void Aura::PeriodicTick() CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL ); // ignore non positive values (can be result apply spellmods to aura damage - uint32 amount = GetModifierValuePerStack() > 0 ? GetModifierValuePerStack() : 0; - - uint32 pdamage; + //uint32 amount = GetModifierValuePerStack() > 0 ? GetModifierValuePerStack() : 0; + uint32 pdamage = GetAmount() > 0 ? GetAmount() : 0; - if(m_modifier.m_auraname == SPELL_AURA_PERIODIC_DAMAGE) + if(GetAuraName() == SPELL_AURA_PERIODIC_DAMAGE) { - pdamage = pCaster->SpellDamageBonus(m_target,GetSpellProto(),amount,DOT); + pdamage = pCaster->SpellDamageBonus(m_target, GetSpellProto(), pdamage, DOT, GetParentAura()->GetStackAmount()); // Calculate armor mitigation if it is a physical spell // But not for bleed mechanic spells if ( GetSpellSchoolMask(GetSpellProto()) & SPELL_SCHOOL_MASK_NORMAL && GetEffectMechanic(GetSpellProto(), m_effIndex) != MECHANIC_BLEED) { - uint32 pdamageReductedArmor = pCaster->CalcArmorReducedDamage(m_target, pdamage); + uint32 pdamageReductedArmor = pCaster->CalcArmorReducedDamage(m_target, pdamage, GetSpellProto()); cleanDamage.damage += pdamage - pdamageReductedArmor; pdamage = pdamageReductedArmor; } - //pdamage = pCaster->SpellDamageBonus(m_target,GetSpellProto(),pdamage,DOT); - // Curse of Agony damage-per-tick calculation - if (GetSpellProto()->SpellFamilyName==SPELLFAMILY_WARLOCK && (GetSpellProto()->SpellFamilyFlags & 0x0000000000000400LL) && GetSpellProto()->SpellIconID==544) + if (GetSpellProto()->SpellFamilyName==SPELLFAMILY_WARLOCK && (GetSpellProto()->SpellFamilyFlags[0] & 0x400) && GetSpellProto()->SpellIconID==544) { - uint32 totalTick = m_maxduration / m_modifier.periodictime; + uint32 totalTick = GetParentAura()->GetAuraMaxDuration() / m_amplitude; // 1..4 ticks, 1/2 from normal tick damage if(m_tickNumber <= totalTick / 3) pdamage = pdamage/2; @@ -5355,16 +5713,14 @@ void Aura::PeriodicTick() } } else - pdamage = uint32(m_target->GetMaxHealth()*amount/100); + pdamage = uint32(m_target->GetMaxHealth()*pdamage/100); //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) pdamage-=((Player*)m_target)->GetDotDamageReduction(pdamage); - pdamage *= GetStackAmount(); - - pCaster->CalcAbsorbResist(m_target, GetSpellSchoolMask(GetSpellProto()), DOT, pdamage, &absorb, &resist); + pCaster->CalcAbsorbResist(m_target, GetSpellSchoolMask(GetSpellProto()), DOT, pdamage, &absorb, &resist, m_spellProto); sLog.outDetail("PeriodicTick: %u (TypeId: %u) attacked %u (TypeId: %u) for %u dmg inflicted by %u abs is %u", GUID_LOPART(GetCasterGUID()), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), pdamage, GetId(),absorb); @@ -5374,8 +5730,9 @@ void Aura::PeriodicTick() data.appendPackGUID(GetCasterGUID()); data << uint32(GetId()); data << uint32(1); - data << uint32(m_modifier.m_auraname); + data << uint32(m_auraName); data << (uint32)pdamage; + data << uint32(0); // overkill data << (uint32)GetSpellSchoolMask(GetSpellProto()); // will be mask in 2.4.x data << (uint32)absorb; data << (uint32)resist; @@ -5396,6 +5753,7 @@ void Aura::PeriodicTick() break; } case SPELL_AURA_PERIODIC_LEECH: + case SPELL_AURA_PERIODIC_HEALTH_FUNNEL: { Unit *pCaster = GetCaster(); if(!pCaster) @@ -5408,7 +5766,7 @@ void Aura::PeriodicTick() pCaster->SpellHitResult(m_target,GetSpellProto(),false)!=SPELL_MISS_NONE) return; - // Check for immune (not use charges) + // Check for immune if(m_target->IsImmunedToDamage(GetSpellSchoolMask(GetSpellProto()))) return; @@ -5416,80 +5774,24 @@ void Aura::PeriodicTick() uint32 resist=0; CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL ); - uint32 pdamage = GetModifierValuePerStack() > 0 ? GetModifierValuePerStack() : 0; - pdamage = pCaster->SpellDamageBonus(m_target,GetSpellProto(),pdamage,DOT); + //uint32 pdamage = GetModifierValuePerStack() > 0 ? GetModifierValuePerStack() : 0; + uint32 pdamage = GetAmount() > 0 ? GetAmount() : 0; + pdamage = pCaster->SpellDamageBonus(m_target, GetSpellProto(), pdamage, DOT, GetParentAura()->GetStackAmount()); //Calculate armor mitigation if it is a physical spell if (GetSpellSchoolMask(GetSpellProto()) & SPELL_SCHOOL_MASK_NORMAL) { - uint32 pdamageReductedArmor = pCaster->CalcArmorReducedDamage(m_target, pdamage); + uint32 pdamageReductedArmor = pCaster->CalcArmorReducedDamage(m_target, pdamage, GetSpellProto()); cleanDamage.damage += pdamage - pdamageReductedArmor; pdamage = pdamageReductedArmor; } - //pdamage = pCaster->SpellDamageBonus(m_target,GetSpellProto(),pdamage,DOT); - - // talent Soul Siphon add bonus to Drain Life spells - if( GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK && (GetSpellProto()->SpellFamilyFlags & 0x8) ) - { - // find talent max bonus percentage - Unit::AuraList const& mClassScriptAuras = pCaster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); - for(Unit::AuraList::const_iterator i = mClassScriptAuras.begin(); i != mClassScriptAuras.end(); ++i) - { - if ((*i)->GetModifier()->m_miscvalue == 4992 || (*i)->GetModifier()->m_miscvalue == 4993) - { - if((*i)->GetEffIndex()!=1) - { - sLog.outError("Expected spell %u structure change, need code update",(*i)->GetId()); - break; - } - - // effect 1 m_amount - int32 maxPercent = (*i)->GetModifier()->m_amount; - // effect 0 m_amount - int32 stepPercent = pCaster->CalculateSpellDamage((*i)->GetSpellProto(),0,(*i)->GetSpellProto()->EffectBasePoints[0],pCaster); - - // count affliction effects and calc additional damage in percentage - int32 modPercent = 0; - Unit::AuraMap const& victimAuras = m_target->GetAuras(); - for (Unit::AuraMap::const_iterator itr = victimAuras.begin(); itr != victimAuras.end(); ++itr) - { - Aura* aura = itr->second; - if (aura->IsPositive())continue; - SpellEntry const* m_spell = aura->GetSpellProto(); - if (m_spell->SpellFamilyName != SPELLFAMILY_WARLOCK) - continue; - - SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(m_spell->Id); - SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(m_spell->Id); - - for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx) - { - if(_spell_idx->second->skillId == SKILL_AFFLICTION) - { - modPercent += stepPercent; - if (modPercent >= maxPercent) - { - modPercent = maxPercent; - break; - } - } - } - } - pdamage += (pdamage*modPercent/100); - break; - } - } - } - //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) pdamage-=((Player*)m_target)->GetDotDamageReduction(pdamage); - pdamage *= GetStackAmount(); - - pCaster->CalcAbsorbResist(m_target, GetSpellSchoolMask(GetSpellProto()), DOT, pdamage, &absorb, &resist); + pCaster->CalcAbsorbResist(m_target, GetSpellSchoolMask(GetSpellProto()), DOT, pdamage, &absorb, &resist, m_spellProto); if(m_target->GetHealth() < pdamage) pdamage = uint32(m_target->GetHealth()); @@ -5499,10 +5801,10 @@ void Aura::PeriodicTick() pCaster->SendSpellNonMeleeDamageLog(m_target, GetId(), pdamage, GetSpellSchoolMask(GetSpellProto()), absorb, resist, false, 0); - Unit* target = m_target; // aura can be deleted in DealDamage SpellEntry const* spellProto = GetSpellProto(); float multiplier = spellProto->EffectMultipleValue[GetEffIndex()] > 0 ? spellProto->EffectMultipleValue[GetEffIndex()] : 1; + int32 stackAmount = GetParentAura()->GetStackAmount(); // Set trigger flag uint32 procAttacker = PROC_FLAG_ON_DO_PERIODIC;// | PROC_FLAG_SUCCESSFUL_HARMFUL_SPELL_HIT; @@ -5526,7 +5828,8 @@ void Aura::PeriodicTick() if(Player *modOwner = pCaster->GetSpellModOwner()) modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_MULTIPLE_VALUE, multiplier); - uint32 heal = pCaster->SpellHealingBonus(spellProto, uint32(new_damage * multiplier), DOT, pCaster); + // Don't apply heal mods for this aura + uint32 heal = uint32(new_damage * multiplier);//pCaster->SpellHealingBonus(pCaster, spellProto, uint32(new_damage * multiplier), DOT, stackAmount); int32 gain = pCaster->ModifyHealth(heal); pCaster->getHostilRefManager().threatAssist(pCaster, gain * 0.5f, spellProto); @@ -5542,22 +5845,20 @@ void Aura::PeriodicTick() return; // heal for caster damage (must be alive) - if(m_target != pCaster && GetSpellProto()->SpellVisual==163 && !pCaster->isAlive()) + if(m_target != pCaster && GetSpellProto()->SpellVisual[0]==163 && !pCaster->isAlive()) return; - // ignore non positive values (can be result apply spellmods to aura damage - uint32 amount = GetModifierValuePerStack() > 0 ? GetModifierValuePerStack() : 0; + if(GetParentAura()->GetAuraDuration() ==-1 && m_target->GetHealth()==m_target->GetMaxHealth()) + return; - uint32 pdamage; + // ignore non positive values (can be result apply spellmods to aura damage + //uint32 amount = GetModifierValuePerStack() > 0 ? GetModifierValuePerStack() : 0; + int32 pdamage = GetAmount() > 0 ? GetAmount() : 0; - if(m_modifier.m_auraname==SPELL_AURA_OBS_MOD_HEALTH) - pdamage = uint32(m_target->GetMaxHealth() * amount/100); + if(m_auraName==SPELL_AURA_OBS_MOD_HEALTH) + pdamage = uint32(m_target->GetMaxHealth() * pdamage * GetParentAura()->GetStackAmount() / 100); else - pdamage = pCaster->SpellHealingBonus(GetSpellProto(), amount, DOT, m_target); - - pdamage *= GetStackAmount(); - - //pdamage = pCaster->SpellHealingBonus(GetSpellProto(), pdamage, DOT, m_target); + pdamage = pCaster->SpellHealingBonus(m_target, GetSpellProto(), pdamage, DOT, GetParentAura()->GetStackAmount()); 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()); @@ -5567,8 +5868,9 @@ void Aura::PeriodicTick() data.appendPackGUID(GetCasterGUID()); data << uint32(GetId()); data << uint32(1); - data << uint32(m_modifier.m_auraname); + data << uint32(m_auraName); data << (uint32)pdamage; + data << uint32(0); // wotlk m_target->SendMessageToSet(&data,true); int32 gain = m_target->ModifyHealth(pdamage); @@ -5579,16 +5881,16 @@ void Aura::PeriodicTick() bg->UpdatePlayerScore(((Player*)pCaster), SCORE_HEALING_DONE, gain); //Do check before because m_modifier.auraName can be invalidate by DealDamage. - bool procSpell = (m_modifier.m_auraname == SPELL_AURA_PERIODIC_HEAL && m_target != pCaster); + bool procSpell = (m_auraName == SPELL_AURA_PERIODIC_HEAL && m_target != pCaster); m_target->getHostilRefManager().threatAssist(pCaster, float(gain) * 0.5f, GetSpellProto()); Unit* target = m_target; // aura can be deleted in DealDamage SpellEntry const* spellProto = GetSpellProto(); - bool haveCastItem = GetCastItemGUID()!=0; + bool haveCastItem = GetParentAura()->GetCastItemGUID()!=0; // heal for caster damage - if(m_target!=pCaster && spellProto->SpellVisual==163) + if(m_target!=pCaster && spellProto->SpellVisual[0]==163) { uint32 dmg = spellProto->manaPerSecond; if(pCaster->GetHealth() <= dmg && pCaster->GetTypeId()==TYPEID_PLAYER) @@ -5624,6 +5926,15 @@ void Aura::PeriodicTick() } case SPELL_AURA_PERIODIC_MANA_LEECH: { + if(GetMiscValue() < 0 || GetMiscValue() >= MAX_POWERS) + return; + + Powers power = Powers(GetMiscValue()); + + // power type might have changed between aura applying and tick (druid's shapeshift) + if(m_target->getPowerType() != power) + return; + Unit *pCaster = GetCaster(); if(!pCaster) return; @@ -5640,20 +5951,22 @@ void Aura::PeriodicTick() return; // ignore non positive values (can be result apply spellmods to aura damage - uint32 pdamage = GetModifierValue() > 0 ? GetModifierValue() : 0; + uint32 pdamage = m_amount > 0 ? m_amount : 0; + + // Special case: draining x% of mana (up to a maximum of 2*x% of the caster's maximum mana) + // It's mana percent cost spells, m_amount is percent drain from target + if (m_spellProto->ManaCostPercentage) + { + // max value + uint32 maxmana = pCaster->GetMaxPower(power) * pdamage * 2 / 100; + pdamage = m_target->GetMaxPower(power) * pdamage / 100; + if(pdamage > maxmana) + pdamage = maxmana; + } sLog.outDetail("PeriodicTick: %u (TypeId: %u) power leech of %u (TypeId: %u) for %u dmg inflicted by %u", GUID_LOPART(GetCasterGUID()), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), pdamage, GetId()); - if(m_modifier.m_miscvalue < 0 || m_modifier.m_miscvalue > 4) - break; - - Powers power = Powers(m_modifier.m_miscvalue); - - // power type might have changed between aura applying and tick (druid's shapeshift) - if(m_target->getPowerType() != power) - break; - int32 drain_amount = m_target->GetPower(power) > pdamage ? pdamage : m_target->GetPower(power); // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4) @@ -5677,7 +5990,7 @@ void Aura::PeriodicTick() data.appendPackGUID(GetCasterGUID()); data << uint32(GetId()); data << uint32(1); - data << uint32(m_modifier.m_auraname); + data << uint32(m_auraName); data << (uint32)power; // power type data << (uint32)drain_amount; data << (float)gain_multiplier; @@ -5696,7 +6009,7 @@ void Aura::PeriodicTick() { m_target->CastSpell(m_target, 31463, true, 0, this); // Remove aura - SetAuraDuration(0); + GetParentAura()->SetAuraDuration(0); } // Mark of Kazzak @@ -5709,67 +6022,78 @@ void Aura::PeriodicTick() { m_target->CastSpell(m_target, 32961, true, 0, this); // Remove aura - SetAuraDuration(0); + GetParentAura()->SetAuraDuration(0); } } break; } - case SPELL_AURA_PERIODIC_ENERGIZE: + case SPELL_AURA_OBS_MOD_ENERGY: { - // ignore non positive values (can be result apply spellmods to aura damage - uint32 pdamage = GetModifierValue() > 0 ? GetModifierValue() : 0; + if(GetMiscValue() < 0) + return; - sLog.outDetail("PeriodicTick: %u (TypeId: %u) energize %u (TypeId: %u) for %u dmg inflicted by %u", - GUID_LOPART(GetCasterGUID()), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), pdamage, GetId()); + Powers power; + if (GetMiscValue() == POWER_ALL) + power = m_target->getPowerType(); + else + power = Powers(GetMiscValue()); - if(m_modifier.m_miscvalue < 0 || m_modifier.m_miscvalue > 4) - break; + if(m_target->GetMaxPower(power) == 0) + return; - Powers power = Powers(m_modifier.m_miscvalue); + if(GetParentAura()->GetAuraDuration() ==-1 && m_target->GetPower(power)==m_target->GetMaxPower(power)) + return; - if(m_target->GetMaxPower(power) == 0) - break; + uint32 amount = m_amount * m_target->GetMaxPower(power) /100; + sLog.outDetail("PeriodicTick: %u (TypeId: %u) energize %u (TypeId: %u) for %u dmg inflicted by %u", + GUID_LOPART(GetCasterGUID()), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), amount, GetId()); WorldPacket data(SMSG_PERIODICAURALOG, (21+16));// we guess size data.append(m_target->GetPackGUID()); data.appendPackGUID(GetCasterGUID()); data << uint32(GetId()); data << uint32(1); - data << uint32(m_modifier.m_auraname); - data << (uint32)power; // power type - data << (uint32)pdamage; + data << uint32(m_auraName); + data << uint32(power); // power type + data << (uint32)amount; m_target->SendMessageToSet(&data,true); - int32 gain = m_target->ModifyPower(power,pdamage); + int32 gain = m_target->ModifyPower(power,amount); if(Unit* pCaster = GetCaster()) m_target->getHostilRefManager().threatAssist(pCaster, float(gain) * 0.5f, GetSpellProto()); break; } - case SPELL_AURA_OBS_MOD_MANA: + case SPELL_AURA_PERIODIC_ENERGIZE: { // ignore non positive values (can be result apply spellmods to aura damage - uint32 amount = GetModifierValue() > 0 ? GetModifierValue() : 0; + if(m_amount < 0 || GetMiscValue() >= MAX_POWERS) + return; - uint32 pdamage = uint32(m_target->GetMaxPower(POWER_MANA) * amount/100); + Powers power = Powers(GetMiscValue()); - sLog.outDetail("PeriodicTick: %u (TypeId: %u) energize %u (TypeId: %u) for %u mana inflicted by %u", - GUID_LOPART(GetCasterGUID()), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), pdamage, GetId()); + if(m_target->GetMaxPower(power) == 0) + return; - if(m_target->GetMaxPower(POWER_MANA) == 0) - break; + if(GetParentAura()->GetAuraDuration() ==-1 && m_target->GetPower(power)==m_target->GetMaxPower(power)) + return; + + uint32 amount = m_amount; WorldPacket data(SMSG_PERIODICAURALOG, (21+16));// we guess size data.append(m_target->GetPackGUID()); data.appendPackGUID(GetCasterGUID()); data << uint32(GetId()); data << uint32(1); - data << uint32(m_modifier.m_auraname); - data << (uint32)0; // ? - data << (uint32)pdamage; + data << uint32(m_auraName); + data << (uint32)power; // power type + data << (uint32)amount; m_target->SendMessageToSet(&data,true); - int32 gain = m_target->ModifyPower(POWER_MANA, pdamage); + sLog.outDetail("PeriodicTick: %u (TypeId: %u) energize %u (TypeId: %u) for %u dmg inflicted by %u", + GUID_LOPART(GetCasterGUID()), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), amount, GetId()); + + int32 gain = m_target->ModifyPower(power,amount); if(Unit* pCaster = GetCaster()) m_target->getHostilRefManager().threatAssist(pCaster, float(gain) * 0.5f, GetSpellProto()); @@ -5785,9 +6109,9 @@ void Aura::PeriodicTick() if(m_target->IsImmunedToDamage(GetSpellSchoolMask(GetSpellProto()))) return; - int32 pdamage = GetModifierValue() > 0 ? GetModifierValue() : 0; + int32 pdamage = m_amount > 0 ? m_amount : 0; - Powers powerType = Powers(m_modifier.m_miscvalue); + Powers powerType = Powers(GetMiscValue()); if(!m_target->isAlive() || m_target->getPowerType() != powerType) return; @@ -5819,127 +6143,143 @@ void Aura::PeriodicTick() pCaster->DealSpellDamage(&damageInfo, true); break; } + case SPELL_AURA_MOD_REGEN: + { + int32 gain = m_target->ModifyHealth(m_amount); + if (Unit *caster = GetCaster()) + m_target->getHostilRefManager().threatAssist(caster, float(gain) * 0.5f, GetSpellProto()); + break; + } + case SPELL_AURA_MOD_POWER_REGEN: + { + Powers pt = m_target->getPowerType(); + if(int32(pt) != GetMiscValue()) + return; + + if ( GetSpellProto()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED ) + { + // eating anim + m_target->HandleEmoteCommand(EMOTE_ONESHOT_EAT); + } + else if( GetId() == 20577 ) + { + // cannibalize anim + m_target->HandleEmoteCommand(EMOTE_STATE_CANNIBALIZE); + } + + // Anger Management + // amount = 1+ 16 = 17 = 3,4*5 = 10,2*5/3 + // so 17 is rounded amount for 5 sec tick grow ~ 1 range grow in 3 sec + if(pt == POWER_RAGE) + m_target->ModifyPower(pt, m_amount*3/5); + break; + } + case SPELL_AURA_DUMMY: + { + // Haunting Spirits + if (GetId() == 7057) + { + m_target->CastSpell((Unit*)NULL , m_amount , true); + m_amplitude = irand (0 , 60 ) + 30; + m_amplitude *= IN_MILISECONDS; + break; + } + break; + } // Here tick dummy auras case SPELL_AURA_PERIODIC_DUMMY: { PeriodicDummyTick(); break; } + case SPELL_AURA_PERIODIC_TRIGGER_SPELL: + { + TriggerSpell(); + break; + } + case SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE: + { + TriggerSpellWithValue(); + break; + } default: break; } } -void Aura::PeriodicDummyTick() +void AuraEffect::PeriodicDummyTick() { + Unit *caster = GetCaster(); SpellEntry const* spell = GetSpellProto(); - switch (spell->Id) - { - // Drink - case 430: - case 431: - case 432: - case 1133: - case 1135: - case 1137: - case 10250: - case 22734: - case 27089: - case 34291: - case 43706: - case 46755: - { - if (m_target->GetTypeId() != TYPEID_PLAYER) - return; - // Search SPELL_AURA_MOD_POWER_REGEN aura for this spell and add bonus - Unit::AuraList const& aura = m_target->GetAurasByType(SPELL_AURA_MOD_POWER_REGEN); - for(Unit::AuraList::const_iterator i = aura.begin(); i != aura.end(); ++i) + switch (spell->SpellFamilyName) + { + case SPELLFAMILY_GENERIC: + switch (spell->Id) + { + // Drink + case 430: + case 431: + case 432: + case 1133: + case 1135: + case 1137: + case 10250: + case 22734: + case 27089: + case 34291: + case 43706: + case 46755: + case 49472: // Drink Coffee + case 61830: { - if ((*i)->GetId() == GetId()) + if (m_target->GetTypeId() != TYPEID_PLAYER) + return; + // Search SPELL_AURA_MOD_POWER_REGEN aura for this spell and add bonus + Unit::AuraEffectList const& aura = m_target->GetAurasByType(SPELL_AURA_MOD_POWER_REGEN); + for(Unit::AuraEffectList::const_iterator i = aura.begin(); i != aura.end(); ++i) { - // Get tick number - int32 tick = (m_maxduration - m_duration) / m_modifier.periodictime; - // Default case (not on arenas) - if (tick == 0) + if ((*i)->GetId() == GetId()) { - (*i)->GetModifier()->m_amount = m_modifier.m_amount; + (*i)->SetAmount(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; } - ((Player*)m_target)->UpdateManaRegen(); - return;*/ } + return; + } + // Forsaken Skills + case 7054: + { + // Possibly need cast one of them (but + // 7038 Forsaken Skill: Swords + // 7039 Forsaken Skill: Axes + // 7040 Forsaken Skill: Daggers + // 7041 Forsaken Skill: Maces + // 7042 Forsaken Skill: Staves + // 7043 Forsaken Skill: Bows + // 7044 Forsaken Skill: Guns + // 7045 Forsaken Skill: 2H Axes + // 7046 Forsaken Skill: 2H Maces + // 7047 Forsaken Skill: 2H Swords + // 7048 Forsaken Skill: Defense + // 7049 Forsaken Skill: Fire + // 7050 Forsaken Skill: Frost + // 7051 Forsaken Skill: Holy + // 7053 Forsaken Skill: Shadow + return; } - return; - } // // Panda // case 19230: break; -// // Master of Subtlety -// case 31666: break; // // Gossip NPC Periodic - Talk // case 33208: break; // // Gossip NPC Periodic - Despawn // case 33209: break; -// // Force of Nature -// case 33831: break; - // Aspect of the Viper - case 34074: - { - if (m_target->GetTypeId() != TYPEID_PLAYER) - return; - // Should be manauser - if (m_target->getPowerType()!=POWER_MANA) - return; - Unit *caster = GetCaster(); - if (!caster) - return; - // Regen amount is max (100% from spell) on 21% or less mana and min on 92.5% or greater mana (20% from spell) - int mana = m_target->GetPower(POWER_MANA); - int max_mana = m_target->GetMaxPower(POWER_MANA); - int32 base_regen = caster->CalculateSpellDamage(m_spellProto, m_effIndex, m_currentBasePoints, m_target); - float regen_pct = 1.20f - 1.1f * mana / max_mana; - if (regen_pct > 1.0f) regen_pct = 1.0f; - else if (regen_pct < 0.2f) regen_pct = 0.2f; - m_modifier.m_amount = int32 (base_regen * regen_pct); - ((Player*)m_target)->UpdateManaRegen(); - return; - } // // Steal Weapon // case 36207: break; // // Simon Game START timer, (DND) // case 39993: break; -// // Harpooner's Mark -// case 40084: break; // // Knockdown Fel Cannon: break; The Aggro Burst // case 40119: break; // // Old Mount Spell @@ -5952,6 +6292,8 @@ void Aura::PeriodicDummyTick() // case 40846: break; // // Copy Weapon // case 41054: break; +// // Dementia +// case 41404: break; // // Ethereal Ring Visual, Lightning Aura // case 41477: break; // // Ethereal Ring Visual, Lightning Aura (Fork) @@ -6000,6 +6342,8 @@ void Aura::PeriodicDummyTick() // case 43310: break; // // Headless Horseman - Maniacal Laugh, Maniacal, Delayed 17 // case 43884: break; +// // Wretched! +// case 43963: break; // // Headless Horseman - Maniacal Laugh, Maniacal, other, Delayed 17 // case 44000: break; // // Energy Feedback @@ -6066,25 +6410,199 @@ void Aura::PeriodicDummyTick() // case 47407: break; // // Mole Machine Port Schedule // case 47489: break; +// case 47941: break; // Crystal Spike +// case 48200: break; // Healer Aura +// case 48630: break; // Summon Gauntlet Mobs Periodic +// case 49313: break; // Proximity Mine Area Aura // // Mole Machine Portal Schedule // case 49466: break; -// // Drink Coffee -// case 49472: break; +// case 49555: break; // Corpse Explode +// case 49592: break; // Temporal Rift +// case 49957: break; // Cutting Laser +// case 50085: break; // Slow Fall // // Listening to Music // case 50493: break; // // Love Rocket Barrage // case 50530: break; +// Exist more after, need add later + default: + break; + } + break; + case SPELLFAMILY_MAGE: + { + // Mirror Image +// if (spell->Id == 55342) +// return; + break; + } + case SPELLFAMILY_WARRIOR: + { + // Armored to the Teeth + if (spell->SpellIconID == 3516) + { + // Increases your attack power by $s1 for every $s2 armor value you have. + // Calculate AP bonus (from 1 efect of this spell) + int32 apBonus = m_amount * m_target->GetArmor() / m_target->CalculateSpellDamage(spell, 1, spell->EffectBasePoints[1], m_target); + m_target->CastCustomSpell(m_target, 61217, &apBonus, &apBonus, 0, true, 0, this); + return; + } + break; + } + case SPELLFAMILY_DRUID: + { + switch (spell->Id) + { + // Frenzied Regeneration + case 22842: + { + // Converts up to 10 rage per second into health for $d. Each point of rage is converted into ${$m2/10}.1% of max health. + // Should be manauser + if (m_target->getPowerType()!=POWER_RAGE) + return; + uint32 rage = m_target->GetPower(POWER_RAGE); + // Nothing todo + if (rage == 0) + return; + int32 mod = (rage < 100) ? rage : 100; + int32 points = m_target->CalculateSpellDamage(spell, 1, spell->EffectBasePoints[1], m_target); + int32 regen = m_target->GetMaxHealth() * (mod * points / 10) / 1000; + m_target->CastCustomSpell(m_target, 22845, ®en, 0, 0, true, 0, this); + m_target->SetPower(POWER_RAGE, rage-mod); + return; + } + // Force of Nature + case 33831: + return; + default: + break; + } + break; + } + case SPELLFAMILY_ROGUE: + { + switch (spell->Id) + { + // Killing Spree +// case 51690: break; + // Overkill + case 58428: + if (!m_target->HasAuraType(SPELL_AURA_MOD_STEALTH)) + m_target->RemoveAurasDueToSpell(58427); + break; + // Master of Subtlety + case 31666: + if (!m_target->HasAuraType(SPELL_AURA_MOD_STEALTH)) + m_target->RemoveAurasDueToSpell(31665); + break; + default: + break; + } + break; + } + case SPELLFAMILY_HUNTER: + { + // Explosive Shot + if (spell->SpellFamilyFlags[1] & 0x80000000) + { + if (!caster) + return; + int32 damage = m_amount; + caster->CastCustomSpell(m_target, 56298, &damage, 0, 0, true, 0, this); + return; + } + switch (spell->Id) + { + // Harpooner's Mark + // case 40084: + // return; + // Feeding Frenzy Rank 1 + case 53511: + if ( m_target->GetHealth() * 100 < m_target->GetMaxHealth() * 35 ) + m_target->CastSpell(m_target, 60096, true, 0, this); + return; + // Feeding Frenzy Rank 2 + case 53512: + if ( m_target->GetHealth() * 100 < m_target->GetMaxHealth() * 35 ) + m_target->CastSpell(m_target, 60097, true, 0, this); + return; + default: + break; + } + break; + } + case SPELLFAMILY_SHAMAN: + { + // Astral Shift + if (spell->Id == 52179) + { + // Periodic need for remove visual on stun/fear/silence lost + if (!(m_target->GetUInt32Value(UNIT_FIELD_FLAGS)&(UNIT_FLAG_STUNNED|UNIT_FLAG_FLEEING|UNIT_FLAG_SILENCED))) + m_target->RemoveAurasDueToSpell(52179); + return; + } + break; + } + case SPELLFAMILY_DEATHKNIGHT: + { + // Death and Decay + if (spell->SpellFamilyFlags[0] & 0x20) + { + if (caster) + caster->CastCustomSpell(m_target, 52212, &m_amount, NULL, NULL, true, 0, this); + return; + } + // Raise Dead +// if (spell->SpellFamilyFlags & 0x0000000000001000LL) +// return; + // Chains of Ice + if (spell->SpellFamilyFlags[1] & 0x00004000) + { + // Get 0 effect aura + AuraEffect *slow = m_target->GetAuraEffect(GetId(), 0); + if (slow) + { + slow->ApplyModifier(false);; + slow->SetAmount(GetAmount()*2); + if (slow->GetAmount() > 0) slow->SetAmount(0); + slow->ApplyModifier(true); + } + return; + } + // Summon Gargoyle +// if (spell->SpellFamilyFlags & 0x0000008000000000LL) +// return; + // Death Rune Mastery +// if (spell->SpellFamilyFlags & 0x0000000000004000LL) +// return; + // Bladed Armor + if (spell->SpellIconID == 2653) + { + // Increases your attack power by $s1 for every $s2 armor value you have. + // Calculate AP bonus (from 1 efect of this spell) + int32 apBonus = m_amount * m_target->GetArmor() / m_target->CalculateSpellDamage(spell, 1, spell->EffectBasePoints[1], m_target); + m_target->CastCustomSpell(m_target, 61217, &apBonus, &apBonus, 0, true, 0, this); + return; + } + // Reaping +// if (spell->SpellIconID == 22) +// return; + // Blood of the North +// if (spell->SpellIconID == 30412) +// return; + break; + } default: break; } } -void Aura::HandlePreventFleeing(bool apply, bool Real) +void AuraEffect::HandlePreventFleeing(bool apply, bool Real) { if(!Real) return; - Unit::AuraList const& fearAuras = m_target->GetAurasByType(SPELL_AURA_MOD_FEAR); + Unit::AuraEffectList const& fearAuras = m_target->GetAurasByType(SPELL_AURA_MOD_FEAR); if( !fearAuras.empty() ) { m_target->SetControlled(!apply, UNIT_STAT_FLEEING); @@ -6095,7 +6613,7 @@ void Aura::HandlePreventFleeing(bool apply, bool Real) } } -void Aura::HandleManaShield(bool apply, bool Real) +void AuraEffect::HandleManaShield(bool apply, bool Real) { if(!Real) return; @@ -6109,7 +6627,7 @@ void Aura::HandleManaShield(bool apply, bool Real) switch(m_spellProto->SpellFamilyName) { case SPELLFAMILY_MAGE: - if(m_spellProto->SpellFamilyFlags & 0x8000) + if(m_spellProto->SpellFamilyFlags[0] & 0x8000) { // Mana Shield // +50% from +spd bonus @@ -6123,12 +6641,12 @@ void Aura::HandleManaShield(bool apply, bool Real) DoneActualBenefit *= caster->CalculateLevelPenalty(GetSpellProto()); - m_modifier.m_amount += (int32)DoneActualBenefit; + m_amount += (int32)DoneActualBenefit; } } } -void Aura::HandleArenaPreparation(bool apply, bool Real) +void AuraEffect::HandleArenaPreparation(bool apply, bool Real) { if(!Real) return; @@ -6139,3 +6657,220 @@ void Aura::HandleArenaPreparation(bool apply, bool Real) m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION); } +void AuraEffect::HandleAuraControlVehicle(bool apply, bool Real) +{ + if(!Real) + return; + + if(m_target->GetTypeId() != TYPEID_PLAYER) + return; + + if(Pet *pet = ((Player*)m_target)->GetPet()) + pet->Remove(PET_SAVE_AS_CURRENT); + + //WorldPacket data(SMSG_ON_CANCEL_EXPECTED_RIDE_VEHICLE_AURA, 0); + //((Player*)m_target)->GetSession()->SendPacket(&data); +} + +void AuraEffect::HandleAuraConvertRune(bool apply, bool Real) +{ + if(!Real) + return; + + if(m_target->GetTypeId() != TYPEID_PLAYER) + return; + + Player *plr = (Player*)m_target; + + if(plr->getClass() != CLASS_DEATH_KNIGHT) + return; + + // how to determine what rune need to be converted? + for(uint32 i = 0; i < MAX_RUNES; ++i) + { + if(apply) + { + if(!plr->GetRuneCooldown(i)) + { + plr->ConvertRune(i, GetSpellProto()->EffectMiscValueB[m_effIndex]); + break; + } + } + else + { + if(plr->GetCurrentRune(i) == GetSpellProto()->EffectMiscValueB[m_effIndex]) + { + plr->ConvertRune(i, plr->GetBaseRune(i)); + break; + } + } + } +} + +// Control Auras + +void AuraEffect::HandleAuraModStun(bool apply, bool Real) +{ + if(!Real) + return; + + m_target->SetControlled(apply, UNIT_STAT_STUNNED); +} + +void AuraEffect::HandleAuraModRoot(bool apply, bool Real) +{ + if(!Real) + return; + + m_target->SetControlled(apply, UNIT_STAT_ROOT); +} + +// Charm Auras + +void AuraEffect::HandleModPossess(bool apply, bool Real) +{ + if(!Real) + return; + + Unit* caster = GetCaster(); + if(caster && caster->GetTypeId() == TYPEID_UNIT) + { + HandleModCharm(apply, Real); + return; + } + + if(apply) + { + if(m_target->getLevel() > m_amount) + return; + + m_target->SetCharmedOrPossessedBy(caster, true); + } + else + m_target->RemoveCharmedOrPossessedBy(caster); +} + +void AuraEffect::HandleModPossessPet(bool apply, bool Real) +{ + if(!Real) + return; + + Unit* caster = GetCaster(); + if(!caster || caster->GetTypeId() != TYPEID_PLAYER) + return; + + if(apply) + { + if(caster->GetGuardianPet() != m_target) + return; + + m_target->SetCharmedOrPossessedBy(caster, true); + } + else + { + m_target->RemoveCharmedOrPossessedBy(caster); + + // Reinitialize the pet bar and make the pet come back to the owner + ((Player*)caster)->PetSpellInitialize(); + if(!m_target->getVictim()) + { + m_target->GetMotionMaster()->MoveFollow(caster, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + m_target->GetCharmInfo()->SetCommandState(COMMAND_FOLLOW); + } + } +} + +void AuraEffect::HandleModCharm(bool apply, bool Real) +{ + if(!Real) + return; + + Unit* caster = GetCaster(); + + if(apply) + { + if(int32(m_target->getLevel()) > m_amount) + return; + + m_target->SetCharmedOrPossessedBy(caster, false); + } + else + m_target->RemoveCharmedOrPossessedBy(caster); +} + +void AuraEffect::HandlePhase(bool apply, bool Real) +{ + if(!Real) + return; + + // always non stackable + if(apply) + { + m_target->RemoveAurasByType(SPELL_AURA_PHASE, NULL, GetParentAura()); + } + + // no-phase is also phase state so same code for apply and remove + + // phase auras normally not expected at BG but anyway better check + if(m_target->GetTypeId()==TYPEID_PLAYER) + { + // drop flag at invisible in bg + if(((Player*)m_target)->InBattleGround()) + if(BattleGround *bg = ((Player*)m_target)->GetBattleGround()) + bg->EventPlayerDroppedFlag((Player*)m_target); + + // GM-mode have mask 0xFFFFFFFF + if(!((Player*)m_target)->isGameMaster()) + m_target->SetPhaseMask(apply ? GetMiscValue() : PHASEMASK_NORMAL,false); + + ((Player*)m_target)->GetSession()->SendSetPhaseShift(apply ? GetMiscValue() : PHASEMASK_NORMAL); + + if(GetEffIndex()==0) + { + SpellAreaForAreaMapBounds saBounds = spellmgr.GetSpellAreaForAuraMapBounds(GetId()); + if(saBounds.first != saBounds.second) + { + uint32 zone, area; + m_target->GetZoneAndAreaId(zone,area); + + for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) + { + // some auras remove at aura remove + if(!itr->second->IsFitToRequirements((Player*)m_target,zone,area)) + m_target->RemoveAurasDueToSpell(itr->second->spellId); + // some auras applied at aura apply + else if(itr->second->autocast) + { + if( !m_target->HasAura(itr->second->spellId,0) ) + m_target->CastSpell(m_target,itr->second->spellId,true); + } + } + } + } + } + else + m_target->SetPhaseMask(apply ? GetMiscValue() : PHASEMASK_NORMAL,false); + + // need triggering visibility update base at phase update of not GM invisible (other GMs anyway see in any phases) + if(m_target->GetVisibility()!=VISIBILITY_OFF) + m_target->SetVisibility(m_target->GetVisibility()); +} + +void Aura::UnregisterSingleCastAura() +{ + if (IsSingleTarget()) + { + Unit* caster = NULL; + caster = GetCaster(); + if(caster) + { + caster->GetSingleCastAuras().remove(this); + } + else + { + sLog.outError("Couldn't find the caster of the single target aura, may crash later!"); + assert(false); + } + m_isSingleTargetAura = false; + } +} diff --git a/src/game/SpellAuras.h b/src/game/SpellAuras.h index 40552a3cb40..14d43070fe1 100644 --- a/src/game/SpellAuras.h +++ b/src/game/SpellAuras.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,23 +22,6 @@ #include "SpellAuraDefines.h" -struct DamageManaShield -{ - uint32 m_spellId; - uint32 m_modType; - int32 m_schoolType; - uint32 m_totalAbsorb; - uint32 m_currAbsorb; -}; - -struct Modifier -{ - AuraType m_auraname; - int32 m_amount; - int32 m_miscvalue; - uint32 periodictime; -}; - class Unit; struct SpellEntry; struct SpellModifier; @@ -46,8 +29,9 @@ struct ProcTriggerSpell; // forward decl class Aura; +class AuraEffect; -typedef void(Aura::*pAuraHandler)(bool Apply, bool Real); +typedef void(AuraEffect::*pAuraHandler)(bool Apply, bool Real); // Real == true at aura add/remove // Real == false at aura mod unapply/reapply; when adding/removing dependent aura/item/stat mods // @@ -63,9 +47,126 @@ typedef void(Aura::*pAuraHandler)(bool Apply, bool Real); class TRINITY_DLL_SPEC Aura { - friend Aura* CreateAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster, Item* castItem); + friend void Player::SendAurasForTarget(Unit *target); + public: + virtual ~Aura(); + Aura(SpellEntry const* spellproto, uint32 effMask, int32 *currentBasePoints, Unit *target, Unit *caster = NULL, Item* castItem = NULL); + + SpellEntry const* GetSpellProto() const { return m_spellProto; } + uint32 GetId() const{ return m_spellProto->Id; } + uint64 GetCastItemGUID() const { return m_castItemGuid; } + + uint64 const& GetCasterGUID() const { return m_caster_guid; } + Unit* GetCaster() const; + Unit* GetTarget() const { return m_target; } + + time_t GetAuraApplyTime() const { return m_applyTime; } + + int32 GetAuraMaxDuration() const { return m_maxduration; } + void SetAuraMaxDuration(int32 duration) { m_maxduration = duration; } + int32 GetAuraDuration() const { return m_duration; } + void SetAuraDuration(int32 duration); + void RefreshAura(){ SetAuraDuration (m_maxduration); } + + void SendAuraUpdate(); + uint8 GetAuraSlot() const { return m_auraSlot; } + void SetAuraSlot(uint8 slot) { m_auraSlot = slot; } + uint8 GetAuraCharges() const { return m_procCharges; } + void SetAuraCharges(uint8 charges); + bool DropAuraCharge(); + void SetProcDamage(uint32 val) { m_procDamage = val; } + uint32 GetProcDamage() const { return m_procDamage; } + + int8 GetStackAmount() const {return m_stackAmount;} + void SetStackAmount(uint8 num); + bool modStackAmount(int32 num); // return true if last charge dropped + uint32 GetAuraStateMask(){return m_auraStateMask;} + void SetAuraState(uint8 num){m_auraStateMask |= 1<<(num-1);} //modifies aura's aura state (not unit!) + + void SetRemoveMode(AuraRemoveMode mode) { m_removeMode = mode; } + uint8 GetRemoveMode() const {return m_removeMode;} + + inline uint8 GetEffectMask() const {return m_auraFlags & 7;} + AuraEffect * GetPartAura (uint8 effIndex) const {return m_partAuras[effIndex];} + bool SetPartAura(AuraEffect* aurEff, uint8 effIndex); + + bool IsPositive() const { return m_positive; } + void SetNegative() { m_positive = false; } + void SetPositive() { m_positive = true; } + bool IsPermanent() const { return m_permanent; } + + bool IsPassive() const { return m_isPassive; } + bool IsDeathPersistent() const { return m_isDeathPersist; } + bool IsRemovedOnShapeLost() const { return m_isRemovedOnShapeLost; } + bool IsUpdated() const { return m_updated; } + bool IsRemoved() const { return m_isRemoved; } + void SetUpdated(bool val) { m_updated = val; } + + bool IsPersistent() const; + bool IsAreaAura() const; + bool IsAuraType(AuraType type) const; + void SetLoadedState(uint64 caster_guid,int32 maxduration,int32 duration,int32 charges, uint8 stackamount, int32 * amount); + bool HasEffect(uint8 effIndex) const {return bool (m_partAuras[effIndex]);} + inline void HandleEffects(bool apply) + { + for (uint8 i = 0; i<MAX_SPELL_EFFECTS;++i) + if (m_partAuras[i]) + m_target->HandleAuraEffect(m_partAuras[i], apply); + } + void ApplyAllModifiers(bool apply, bool Real=false); + + void Update(uint32 diff); + + void _AddAura(); + void _RemoveAura(); + + // Allow Apply Aura Handler to modify and access m_AuraDRGroup + void setDiminishGroup(DiminishingGroup group) { m_AuraDRGroup = group; } + DiminishingGroup getDiminishGroup() const { return m_AuraDRGroup; } + + // Single cast aura helpers + void UnregisterSingleCastAura(); + bool IsSingleTarget() const {return m_isSingleTargetAura;} + void SetIsSingleTarget(bool val) { m_isSingleTargetAura = val;} + + private: + SpellEntry const *m_spellProto; + Unit * const m_target; + uint64 m_caster_guid; + uint64 m_castItemGuid; // it is NOT safe to keep a pointer to the item because it may get deleted + time_t m_applyTime; + + int32 m_maxduration; // Max aura duration + int32 m_duration; // Current time + int32 m_timeCla; // Timer for power per sec calcultion + AuraRemoveMode m_removeMode:8; // Store info for know remove aura reason + DiminishingGroup m_AuraDRGroup:8; // Diminishing + + uint8 m_auraSlot; // Aura slot on unit (for show in client) + uint8 m_auraFlags; // Aura info flag (for send data to client) + uint8 m_auraLevel; // Aura level (store caster level for correct show level dep amount) + uint8 m_procCharges; // Aura charges (0 for infinite) + uint32 m_procDamage; // used in aura proc code + uint8 m_stackAmount; // Aura stack amount + uint32 m_auraStateMask; + AuraEffect * m_partAuras[3]; + + bool m_isDeathPersist:1; + bool m_isRemovedOnShapeLost:1; + bool m_isPassive:1; + bool m_positive:1; + bool m_permanent:1; + bool m_isRemoved:1; + bool m_updated:1; // Prevent remove aura by stack if set + bool m_isSingleTargetAura:1; // true if it's a single target spell and registered at caster - can change at spell steal for example +}; + +class TRINITY_DLL_SPEC AuraEffect +{ public: + friend AuraEffect* CreateAuraEffect(Aura * parentAura, uint32 effIndex, int32 *currentBasePoints, Unit * caster, Item * castItem); + friend void Aura::SetStackAmount(uint8 stackAmount); //aura handlers void HandleNULL(bool, bool) { @@ -100,6 +201,7 @@ class TRINITY_DLL_SPEC Aura void HandleAuraFeatherFall(bool Apply, bool Real); void HandleAuraHover(bool Apply, bool Real); void HandleAddModifier(bool Apply, bool Real); + void HandleAddTargetTrigger(bool Apply, bool Real); void HandleAuraModStun(bool Apply, bool Real); void HandleModDamageDone(bool Apply, bool Real); void HandleAuraUntrackable(bool Apply, bool Real); @@ -111,13 +213,15 @@ class TRINITY_DLL_SPEC Aura void HandleAuraModRegenInterrupt(bool Apply, bool Real); void HandleHaste(bool Apply, bool Real); void HandlePeriodicTriggerSpell(bool Apply, bool Real); + void HandlePeriodicTriggerSpellWithValue(bool apply, bool Real); void HandlePeriodicEnergize(bool Apply, bool Real); void HandleAuraModResistanceExclusive(bool Apply, bool Real); + void HandleAuraModPetTalentsPoints(bool Apply, bool Real); void HandleModStealth(bool Apply, bool Real); void HandleInvisibility(bool Apply, bool Real); void HandleInvisibilityDetect(bool Apply, bool Real); void HandleAuraModTotalHealthPercentRegen(bool Apply, bool Real); - void HandleAuraModTotalManaPercentRegen(bool Apply, bool Real); + void HandleAuraModTotalEnergyPercentRegen(bool Apply, bool Real); void HandleAuraModResistance(bool Apply, bool Real); void HandleAuraModRoot(bool Apply, bool Real); void HandleAuraModSilence(bool Apply, bool Real); @@ -147,6 +251,7 @@ class TRINITY_DLL_SPEC Aura void HandleModSpellHitChance(bool Apply, bool Real); void HandleAuraModScale(bool Apply, bool Real); void HandlePeriodicManaLeech(bool Apply, bool Real); + void HandlePeriodicHealthFunnel(bool apply, bool Real); void HandleModCastingSpeed(bool Apply, bool Real); void HandleAuraMounted(bool Apply, bool Real); void HandleWaterBreathing(bool Apply, bool Real); @@ -165,6 +270,7 @@ class TRINITY_DLL_SPEC Aura void HandleFarSight(bool Apply, bool Real); void HandleModPossessPet(bool Apply, bool Real); void HandleModMechanicImmunity(bool Apply, bool Real); + void HandleModStateImmunityMask(bool apply, bool Real); void HandleAuraModSkill(bool Apply, bool Real); void HandleModDamagePercentDone(bool Apply, bool Real); void HandleModPercentStat(bool Apply, bool Real); @@ -184,10 +290,12 @@ class TRINITY_DLL_SPEC Aura void HandleAuraGhost(bool Apply, bool Real); void HandleAuraAllowFlight(bool Apply, bool Real); void HandleModRating(bool apply, bool Real); + void HandleModRatingFromStat(bool apply, bool Real); void HandleModTargetResistance(bool apply, bool Real); void HandleAuraModAttackPowerPercent(bool apply, bool Real); void HandleAuraModRangedAttackPowerPercent(bool apply, bool Real); void HandleAuraModRangedAttackPowerOfStatPercent(bool apply, bool Real); + void HandleAuraModAttackPowerOfStatPercent(bool apply, bool Real); void HandleSpiritOfRedemption(bool apply, bool Real); void HandleModManaRegen(bool apply, bool Real); void HandleComprehendLanguage(bool apply, bool Real); @@ -197,6 +305,7 @@ class TRINITY_DLL_SPEC Aura void HandleModSpellDamagePercentFromStat(bool apply, bool Real); void HandleModSpellHealingPercentFromStat(bool apply, bool Real); void HandleAuraModDispelResist(bool apply, bool Real); + void HandleAuraControlVehicle(bool apply, bool Real); void HandleModSpellDamagePercentFromAttackPower(bool apply, bool Real); void HandleModSpellHealingPercentFromAttackPower(bool apply, bool Real); void HandleAuraModPacifyAndSilence(bool Apply, bool Real); @@ -209,171 +318,90 @@ class TRINITY_DLL_SPEC Aura void HandlePreventFleeing(bool apply, bool Real); void HandleManaShield(bool apply, bool Real); void HandleArenaPreparation(bool apply, bool Real); + void HandleAuraConvertRune(bool apply, bool Real); + void HandleAuraIncreaseBaseHealthPercent(bool Apply, bool Real); + void HandleNoReagentUseAura(bool Apply, bool Real); + void HandlePhase(bool Apply, bool Real); + void HandleAuraAllowOnlyAbility(bool apply, bool Real); - virtual ~Aura(); + // add/remove SPELL_AURA_MOD_SHAPESHIFT (36) linked auras + void HandleShapeshiftBoosts(bool apply); - void SetModifier(AuraType t, int32 a, uint32 pt, int32 miscValue); - Modifier* GetModifier() {return &m_modifier;} - int32 GetModifierValuePerStack() {return m_modifier.m_amount;} - int32 GetModifierValue() {return m_modifier.m_amount * m_stackAmount;} - int32 GetMiscValue() {return m_spellProto->EffectMiscValue[m_effIndex];} - int32 GetMiscBValue() {return m_spellProto->EffectMiscValueB[m_effIndex];} + inline Unit * GetCaster() const{ return m_parentAura->GetCaster(); } + inline uint64 GetCasterGUID() const{ return m_parentAura->GetCasterGUID(); } + Aura * GetParentAura() const { return m_parentAura; } SpellEntry const* GetSpellProto() const { return m_spellProto; } - uint32 GetId() const{ return m_spellProto->Id; } - uint64 GetCastItemGUID() const { return m_castItemGuid; } - uint32 GetEffIndex() const{ return m_effIndex; } + uint32 GetId() const { return m_spellProto->Id; } + uint32 GetEffIndex() const { return m_effIndex; } int32 GetBasePoints() const { return m_currentBasePoints; } + int32 GetAuraAmplitude(){return m_amplitude;} + virtual void Update(uint32 diff); - int32 GetAuraMaxDuration() const { return m_maxduration; } - void SetAuraMaxDuration(int32 duration) { m_maxduration = duration; } - int32 GetAuraDuration() const { return m_duration; } - void SetAuraDuration(int32 duration) - { - m_duration = duration; - if (duration<0) - m_permanent=true; - else - m_permanent=false; - } - time_t GetAuraApplyTime() { return m_applyTime; } - void UpdateAuraDuration(); - void SendAuraDurationForCaster(Player* caster); - void UpdateSlotCounterAndDuration(); uint32 GetTickNumber() const { return m_tickNumber; } - - uint64 const& GetCasterGUID() const { return m_caster_guid; } - Unit* GetCaster() const; - Unit* GetTarget() const { return m_target; } - void SetTarget(Unit* target) { m_target = target; } - void SetLoadedState(uint64 caster_guid,int32 damage,int32 maxduration,int32 duration,int32 charges) - { - m_caster_guid = caster_guid; - m_modifier.m_amount = damage; - m_maxduration = maxduration; - m_duration = duration; - m_procCharges = charges; - } - - uint8 GetAuraSlot() const { return m_auraSlot; } - void SetAuraSlot(uint8 slot) { m_auraSlot = slot; } - void UpdateAuraCharges() - { - uint8 slot = GetAuraSlot(); - - // only aura in slot with charges and without stack limitation - if (slot < MAX_AURAS && m_procCharges >= 1 && GetSpellProto()->StackAmount==0) - SetAuraApplication(slot, m_procCharges - 1); - } - - bool IsPositive() { return m_positive; } - void SetNegative() { m_positive = false; } - void SetPositive() { m_positive = true; } - - bool IsPermanent() const { return m_permanent; } bool IsAreaAura() const { return m_isAreaAura; } bool IsPeriodic() const { return m_isPeriodic; } - bool IsTrigger() const { return m_isTrigger; } - bool IsPassive() const { return m_isPassive; } bool IsPersistent() const { return m_isPersistent; } - bool IsDeathPersistent() const { return m_isDeathPersist; } - bool IsRemovedOnShapeLost() const { return m_isRemovedOnShapeLost; } - bool IsInUse() const { return m_in_use;} - void CleanupTriggeredSpells(); + bool isAffectedOnSpell(SpellEntry const *spell) const; - virtual void Update(uint32 diff); void ApplyModifier(bool apply, bool Real = false); - - void _AddAura(); - void _RemoveAura(); - - void TriggerSpell(); - - bool IsUpdated() { return m_updated; } - void SetUpdated(bool val) { m_updated = val; } - void SetRemoveMode(AuraRemoveMode mode) { m_removeMode = mode; } - - int32 m_procCharges; - void SetAuraProcCharges(int32 charges) { m_procCharges = charges; } + void HandleAuraEffect(bool apply); + void ApplyAllModifiers(bool apply, bool Real); Unit* GetTriggerTarget() const; - - // add/remove SPELL_AURA_MOD_SHAPESHIFT (36) linked auras - void HandleShapeshiftBoosts(bool apply); - - // Allow Apply Aura Handler to modify and access m_AuraDRGroup - void setDiminishGroup(DiminishingGroup group) { m_AuraDRGroup = group; } - DiminishingGroup getDiminishGroup() const { return m_AuraDRGroup; } - + void TriggerSpell(); + void TriggerSpellWithValue(); void PeriodicTick(); void PeriodicDummyTick(); - int32 GetStackAmount() {return m_stackAmount;} - void SetStackAmount(int32 amount) {m_stackAmount=amount;} + int32 GetMiscBValue() const {return m_spellProto->EffectMiscValueB[m_effIndex];} + int32 GetMiscValue() const {return m_spellProto->EffectMiscValue[m_effIndex];} + uint32 GetAuraName() const {return m_auraName;} + int32 GetAmount() const {return m_amount;} + void SetAmount(int32 amount) { m_amount = amount; } + void CleanupTriggeredSpells(); + protected: - Aura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster = NULL, Item* castItem = NULL); + AuraEffect (Aura * parentAura, uint8 effIndex, int32 * currentBasePoints , Unit * caster,Item * castItem); + Aura * const m_parentAura; + Unit * const m_target; - Modifier m_modifier; - SpellModifier *m_spellmod; - uint32 m_effIndex; SpellEntry const *m_spellProto; - int32 m_currentBasePoints; // cache SpellEntry::EffectBasePoints and use for set custom base points - uint64 m_caster_guid; - Unit* m_target; - int32 m_maxduration; - int32 m_duration; uint32 m_tickNumber; - int32 m_timeCla; - uint64 m_castItemGuid; // it is NOT safe to keep a pointer to the item because it may get deleted - time_t m_applyTime; - AuraRemoveMode m_removeMode; + uint8 m_effIndex; + AuraType m_auraName; + int32 m_currentBasePoints; + int32 m_amount; - uint8 m_auraSlot; + SpellModifier *m_spellmod; + + int32 m_periodicTimer; // Timer for periodic auras + int32 m_amplitude; - bool m_positive:1; - bool m_permanent:1; bool m_isPeriodic:1; - bool m_isTrigger:1; bool m_isAreaAura:1; - bool m_isPassive:1; bool m_isPersistent:1; - bool m_isDeathPersist:1; - bool m_isRemovedOnShapeLost:1; - bool m_updated:1; - bool m_in_use:1; // true while in Aura::ApplyModifier call - - int32 m_periodicTimer; - uint32 m_PeriodicEventId; - DiminishingGroup m_AuraDRGroup; - - int32 m_stackAmount; - private: - void SetAura(uint32 slot, bool remove) { m_target->SetUInt32Value(UNIT_FIELD_AURA + slot, remove ? 0 : GetId()); } - void SetAuraFlag(uint32 slot, bool add); - void SetAuraLevel(uint32 slot, uint32 level); - void SetAuraApplication(uint32 slot, int8 count); }; -class TRINITY_DLL_SPEC AreaAura : public Aura +class TRINITY_DLL_SPEC AreaAuraEffect : public AuraEffect { public: - AreaAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster = NULL, Item* castItem = NULL); - ~AreaAura(); + AreaAuraEffect(Aura * parentAura, uint32 effIndex, int32 * currentBasePoints, Unit * caster=NULL, Item * castItem=NULL); + ~AreaAuraEffect(); void Update(uint32 diff); private: float m_radius; + int32 m_removeTime; AreaAuraType m_areaAuraType; }; -class TRINITY_DLL_SPEC PersistentAreaAura : public Aura +class TRINITY_DLL_SPEC PersistentAreaAuraEffect : public AuraEffect { public: - PersistentAreaAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster = NULL, Item* castItem = NULL); - ~PersistentAreaAura(); + PersistentAreaAuraEffect(Aura * parentAura, uint32 eff, int32 *currentBasePoints, Unit *caster = NULL, Item* castItem = NULL); + ~PersistentAreaAuraEffect(); void Update(uint32 diff); }; - -Aura* CreateAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster = NULL, Item* castItem = NULL); +AuraEffect* CreateAuraEffect(Aura * parentAura, uint32 effIndex, int32 *currentBasePoints, Unit * caster, Item * castItem = NULL); #endif - diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index af7d2ee8a98..053130fed90 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,10 +19,8 @@ */ #include "Common.h" -#include "SharedDefines.h" #include "Database/DatabaseEnv.h" #include "WorldPacket.h" -#include "WorldSession.h" #include "Opcodes.h" #include "Log.h" #include "UpdateMask.h" @@ -32,7 +30,6 @@ #include "Player.h" #include "SkillExtraItems.h" #include "Unit.h" -#include "CreatureAI.h" #include "Spell.h" #include "DynamicObject.h" #include "SpellAuras.h" @@ -47,6 +44,7 @@ #include "Creature.h" #include "Totem.h" #include "CreatureAI.h" +#include "BattleGroundMgr.h" #include "BattleGround.h" #include "BattleGroundEY.h" #include "BattleGroundWS.h" @@ -60,6 +58,9 @@ #include "CellImpl.h" #include "GridNotifiers.h" #include "GridNotifiersImpl.h" +#include "SkillDiscovery.h" +#include "Formulas.h" +#include "Vehicle.h" pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= { @@ -104,8 +105,8 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= &Spell::EffectDispel, // 38 SPELL_EFFECT_DISPEL &Spell::EffectUnused, // 39 SPELL_EFFECT_LANGUAGE &Spell::EffectDualWield, // 40 SPELL_EFFECT_DUAL_WIELD - &Spell::EffectSummonWild, // 41 SPELL_EFFECT_SUMMON_WILD - &Spell::EffectSummonGuardian, // 42 SPELL_EFFECT_SUMMON_GUARDIAN + &Spell::EffectJump, // 41 SPELL_EFFECT_JUMP + &Spell::EffectJump, // 42 SPELL_EFFECT_JUMP2 &Spell::EffectTeleUnitsFaceCaster, // 43 SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER &Spell::EffectLearnSkill, // 44 SPELL_EFFECT_SKILL_STEP &Spell::EffectAddHonor, // 45 SPELL_EFFECT_ADD_HONOR honor/pvp related @@ -128,16 +129,16 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= &Spell::EffectPowerBurn, // 62 SPELL_EFFECT_POWER_BURN &Spell::EffectThreat, // 63 SPELL_EFFECT_THREAT &Spell::EffectTriggerSpell, // 64 SPELL_EFFECT_TRIGGER_SPELL - &Spell::EffectUnused, // 65 SPELL_EFFECT_HEALTH_FUNNEL unused - &Spell::EffectUnused, // 66 SPELL_EFFECT_POWER_FUNNEL unused + &Spell::EffectApplyAreaAura, // 65 SPELL_EFFECT_APPLY_AREA_AURA_RAID + &Spell::EffectUnused, // 66 SPELL_EFFECT_CREATE_MANA_GEM (possibly recharge it, misc - is item ID) &Spell::EffectHealMaxHealth, // 67 SPELL_EFFECT_HEAL_MAX_HEALTH &Spell::EffectInterruptCast, // 68 SPELL_EFFECT_INTERRUPT_CAST &Spell::EffectDistract, // 69 SPELL_EFFECT_DISTRACT &Spell::EffectPull, // 70 SPELL_EFFECT_PULL one spell: Distract Move &Spell::EffectPickPocket, // 71 SPELL_EFFECT_PICKPOCKET &Spell::EffectAddFarsight, // 72 SPELL_EFFECT_ADD_FARSIGHT - &Spell::EffectSummonPossessed, // 73 SPELL_EFFECT_SUMMON_POSSESSED - &Spell::EffectSummonTotem, // 74 SPELL_EFFECT_SUMMON_TOTEM + &Spell::EffectUnused, // 73 SPELL_EFFECT_UNTRAIN_TALENTS + &Spell::EffectApplyGlyph, // 74 SPELL_EFFECT_APPLY_GLYPH &Spell::EffectHealMechanical, // 75 SPELL_EFFECT_HEAL_MECHANICAL one spell: Mechanical Patch Kit &Spell::EffectSummonObjectWild, // 76 SPELL_EFFECT_SUMMON_OBJECT_WILD &Spell::EffectScriptEffect, // 77 SPELL_EFFECT_SCRIPT_EFFECT @@ -150,17 +151,17 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= &Spell::EffectStuck, // 84 SPELL_EFFECT_STUCK &Spell::EffectSummonPlayer, // 85 SPELL_EFFECT_SUMMON_PLAYER &Spell::EffectActivateObject, // 86 SPELL_EFFECT_ACTIVATE_OBJECT - &Spell::EffectSummonTotem, // 87 SPELL_EFFECT_SUMMON_TOTEM_SLOT1 - &Spell::EffectSummonTotem, // 88 SPELL_EFFECT_SUMMON_TOTEM_SLOT2 - &Spell::EffectSummonTotem, // 89 SPELL_EFFECT_SUMMON_TOTEM_SLOT3 - &Spell::EffectSummonTotem, // 90 SPELL_EFFECT_SUMMON_TOTEM_SLOT4 + &Spell::EffectUnused, // 87 SPELL_EFFECT_WMO_DAMAGE + &Spell::EffectUnused, // 88 SPELL_EFFECT_WMO_REPAIR + &Spell::EffectUnused, // 89 SPELL_EFFECT_WMO_CHANGE + &Spell::EffectUnused, // 90 SPELL_EFFECT_KILL_CREDIT &Spell::EffectUnused, // 91 SPELL_EFFECT_THREAT_ALL one spell: zzOLDBrainwash &Spell::EffectEnchantHeldItem, // 92 SPELL_EFFECT_ENCHANT_HELD_ITEM &Spell::EffectUnused, // 93 SPELL_EFFECT_SUMMON_PHANTASM &Spell::EffectSelfResurrect, // 94 SPELL_EFFECT_SELF_RESURRECT &Spell::EffectSkinning, // 95 SPELL_EFFECT_SKINNING &Spell::EffectUnused, // 96 SPELL_EFFECT_CHARGE - &Spell::EffectSummonCritter, // 97 SPELL_EFFECT_SUMMON_CRITTER + &Spell::EffectUnused, // 97 SPELL_EFFECT_97 &Spell::EffectKnockBack, // 98 SPELL_EFFECT_KNOCK_BACK &Spell::EffectDisEnchant, // 99 SPELL_EFFECT_DISENCHANT &Spell::EffectInebriate, //100 SPELL_EFFECT_INEBRIATE @@ -175,7 +176,7 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= &Spell::EffectSummonDeadPet, //109 SPELL_EFFECT_SUMMON_DEAD_PET &Spell::EffectDestroyAllTotems, //110 SPELL_EFFECT_DESTROY_ALL_TOTEMS &Spell::EffectDurabilityDamage, //111 SPELL_EFFECT_DURABILITY_DAMAGE - &Spell::EffectSummonDemon, //112 SPELL_EFFECT_SUMMON_DEMON + &Spell::EffectUnused, //112 SPELL_EFFECT_112 &Spell::EffectResurrectNew, //113 SPELL_EFFECT_RESURRECT_NEW &Spell::EffectTaunt, //114 SPELL_EFFECT_ATTACK_ME &Spell::EffectDurabilityDamagePCT, //115 SPELL_EFFECT_DURABILITY_DAMAGE_PCT @@ -195,21 +196,21 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= &Spell::EffectApplyAreaAura, //129 SPELL_EFFECT_APPLY_AREA_AURA_ENEMY &Spell::EffectRedirectThreat, //130 SPELL_EFFECT_REDIRECT_THREAT &Spell::EffectUnused, //131 SPELL_EFFECT_131 used in some test spells - &Spell::EffectNULL, //132 SPELL_EFFECT_PLAY_MUSIC sound id in misc value + &Spell::EffectNULL, //132 SPELL_EFFECT_PLAY_MUSIC sound id in misc value (SoundEntries.dbc) &Spell::EffectUnlearnSpecialization, //133 SPELL_EFFECT_UNLEARN_SPECIALIZATION unlearn profession specialization &Spell::EffectKillCredit, //134 SPELL_EFFECT_KILL_CREDIT misc value is creature entry &Spell::EffectNULL, //135 SPELL_EFFECT_CALL_PET &Spell::EffectHealPct, //136 SPELL_EFFECT_HEAL_PCT &Spell::EffectEnergisePct, //137 SPELL_EFFECT_ENERGIZE_PCT - &Spell::EffectNULL, //138 SPELL_EFFECT_138 Leap - &Spell::EffectUnused, //139 SPELL_EFFECT_139 unused + &Spell::EffectJump2, //138 SPELL_EFFECT_138 Leap + &Spell::EffectUnused, //139 SPELL_EFFECT_CLEAR_QUEST (misc - is quest ID) &Spell::EffectForceCast, //140 SPELL_EFFECT_FORCE_CAST &Spell::EffectNULL, //141 SPELL_EFFECT_141 damage and reduce speed? &Spell::EffectTriggerSpellWithValue, //142 SPELL_EFFECT_TRIGGER_SPELL_WITH_VALUE &Spell::EffectApplyAreaAura, //143 SPELL_EFFECT_APPLY_AREA_AURA_OWNER &Spell::EffectKnockBack, //144 SPELL_EFFECT_KNOCK_BACK_2 Spectral Blast &Spell::EffectPlayerPull, //145 SPELL_EFFECT_145 Black Hole Effect - &Spell::EffectUnused, //146 SPELL_EFFECT_146 unused + &Spell::EffectActivateRune, //146 SPELL_EFFECT_ACTIVATE_RUNE &Spell::EffectQuestFail, //147 SPELL_EFFECT_QUEST_FAIL quest fail &Spell::EffectUnused, //148 SPELL_EFFECT_148 unused &Spell::EffectNULL, //149 SPELL_EFFECT_149 swoop @@ -217,6 +218,12 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= &Spell::EffectTriggerRitualOfSummoning, //151 SPELL_EFFECT_TRIGGER_SPELL_2 &Spell::EffectNULL, //152 SPELL_EFFECT_152 summon Refer-a-Friend &Spell::EffectNULL, //153 SPELL_EFFECT_CREATE_PET misc value is creature entry + &Spell::EffectNULL, //154 unused + &Spell::EffectTitanGrip, //155 SPELL_EFFECT_TITAN_GRIP Allows you to equip two-handed axes, maces and swords in one hand, but you attack $49152s1% slower than normal. + &Spell::EffectEnchantItemPrismatic, //156 SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC + &Spell::EffectCreateItem2, //157 SPELL_EFFECT_CREATE_ITEM_2 create/learn item/spell for profession + &Spell::EffectMilling, //158 SPELL_EFFECT_MILLING milling + &Spell::EffectRenamePet //159 SPELL_EFFECT_ALLOW_RENAME_PET allow rename pet once again }; void Spell::EffectNULL(uint32 /*i*/) @@ -291,13 +298,13 @@ void Spell::EffectEnvirinmentalDMG(uint32 i) // Note: this hack with damage replace required until GO casting not implemented // environment damage spells already have around enemies targeting but this not help in case not existed GO casting support // currently each enemy selected explicitly and self cast damage, we prevent apply self casted spell bonuses/etc - damage = m_spellInfo->EffectBasePoints[i]+m_spellInfo->EffectBaseDice[i]; + damage = m_spellInfo->CalculateSimpleValue(i); - m_caster->CalcAbsorbResist(m_caster,GetSpellSchoolMask(m_spellInfo), SPELL_DIRECT_DAMAGE, damage, &absorb, &resist); + m_caster->CalcAbsorbResist(m_caster,GetSpellSchoolMask(m_spellInfo), SPELL_DIRECT_DAMAGE, damage, &absorb, &resist, m_spellInfo); m_caster->SendSpellNonMeleeDamageLog(m_caster, m_spellInfo->Id, damage, GetSpellSchoolMask(m_spellInfo), absorb, resist, false, 0, false); if(m_caster->GetTypeId() == TYPEID_PLAYER) - ((Player*)m_caster)->EnvironmentalDamage(m_caster->GetGUID(),DAMAGE_FIRE,damage); + ((Player*)m_caster)->EnvironmentalDamage(DAMAGE_FIRE,damage); } void Spell::EffectSchoolDMG(uint32 effect_idx) @@ -333,7 +340,7 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx) { case 35354: //Hand of Death { - if(unitTarget && unitTarget->HasAura(38528,0)) //Protection of Elune + if(unitTarget && unitTarget->HasAura(38528)) //Protection of Elune { damage = 0; } @@ -347,6 +354,13 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx) damage = 200; break; } + // Intercept (warrior spell trigger) + case 20253: + case 61491: + { + damage+= uint32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * 0.12f); + break; + } // arcane charge. must only affect demons (also undead?) case 45072: { @@ -362,7 +376,7 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx) if(unitTarget->GetGUID() == m_caster->GetGUID() || unitTarget->GetTypeId() != TYPEID_PLAYER) return; - float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[0])); + float radius = GetSpellRadiusForHostile(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[0])); if(!radius) return; float distance = m_caster->GetDistance2d(unitTarget); damage = (distance > radius ) ? 0 : (int32)(m_spellInfo->EffectBasePoints[0]*((radius - distance)/radius)); @@ -374,7 +388,7 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx) case SPELLFAMILY_MAGE: { // Arcane Blast - if(m_spellInfo->SpellFamilyFlags & 0x20000000LL) + if(m_spellInfo->SpellFamilyFlags[0] & 0x20000000) { m_caster->CastSpell(m_caster,36032,true); } @@ -383,109 +397,112 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx) case SPELLFAMILY_WARRIOR: { // Bloodthirst - if(m_spellInfo->SpellFamilyFlags & 0x40000000000LL) + if(m_spellInfo->SpellFamilyFlags[1] & 0x400) { damage = uint32(damage * (m_caster->GetTotalAttackPowerValue(BASE_ATTACK)) / 100); } // Shield Slam - else if(m_spellInfo->SpellFamilyFlags & 0x100000000LL) + else if(m_spellInfo->SpellFamilyFlags[1] & 0x200 && m_spellInfo->Category==1209) damage += int32(m_caster->GetShieldBlockValue()); // Victory Rush - else if(m_spellInfo->SpellFamilyFlags & 0x10000000000LL) + else if(m_spellInfo->SpellFamilyFlags[1] & 0x100) { damage = uint32(damage * m_caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100); m_caster->ModifyAuraState(AURA_STATE_WARRIOR_VICTORY_RUSH, false); } + // Revenge ${$m1+$AP*0.207} to ${$M1+$AP*0.207} + else if(m_spellInfo->SpellFamilyFlags[0] & 0x400) + damage+= uint32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * 0.207f); + // Heroic Throw ${$m1+$AP*.50} + else if(m_spellInfo->SpellFamilyFlags[1] & 0x00000001) + damage+= uint32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * 0.5f); + // Shockwave ${$m3/100*$AP} + else if(m_spellInfo->SpellFamilyFlags[1] & 0x00008000) + { + int32 pct = m_caster->CalculateSpellDamage(m_spellInfo, 2, m_spellInfo->EffectBasePoints[2], unitTarget); + if (pct > 0) + damage+= int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * pct / 100); + break; + } + // Thunder Clap + else if(m_spellInfo->SpellFamilyFlags[0] & 0x80) + { + damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * 12 / 100); + break; + } + // Concussion Blow + else if(m_spellInfo->SpellFamilyFlags[0] & 0x4000000) + { + int32 pct = m_caster->CalculateSpellDamage(m_spellInfo, 2, m_spellInfo->EffectBasePoints[2], unitTarget); + damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * pct / 100); + break; + } break; } case SPELLFAMILY_WARLOCK: { // Incinerate Rank 1 & 2 - if((m_spellInfo->SpellFamilyFlags & 0x00004000000000LL) && m_spellInfo->SpellIconID==2128) + if((m_spellInfo->SpellFamilyFlags[1] & 0x000040) && m_spellInfo->SpellIconID==2128) { // Incinerate does more dmg (dmg*0.25) if the target is Immolated. - if(unitTarget->HasAuraState(AURA_STATE_IMMOLATE)) - damage += int32(damage*0.25); + if(unitTarget->HasAuraState(AURA_STATE_IMMOLATE, m_spellInfo, m_caster)) + damage += int32(damage*0.25f); } - // Conflagrate - consumes immolate if (m_spellInfo->TargetAuraState == AURA_STATE_IMMOLATE) { // for caster applied auras only - Unit::AuraList const &mPeriodic = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE); - for(Unit::AuraList::const_iterator i = mPeriodic.begin(); i != mPeriodic.end(); ++i) + Unit::AuraEffectList const &mPeriodic = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE); + for(Unit::AuraEffectList::const_iterator i = mPeriodic.begin(); i != mPeriodic.end(); ++i) { - if( (*i)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK && ((*i)->GetSpellProto()->SpellFamilyFlags & 4) && + if( (*i)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK && ((*i)->GetSpellProto()->SpellFamilyFlags[0] & 4) && (*i)->GetCasterGUID()==m_caster->GetGUID() ) { - unitTarget->RemoveAurasByCasterSpell((*i)->GetId(), m_caster->GetGUID()); + unitTarget->RemoveAurasDueToSpell((*i)->GetId(), m_caster->GetGUID()); break; } } } break; } + case SPELLFAMILY_PRIEST: + { + // Shadow Word: Death - deals damage equal to damage done to caster + if (m_spellInfo->SpellFamilyFlags[1] & 0x2) + m_caster->CastCustomSpell(m_caster, 32409, &damage, 0, 0, true); + break; + } case SPELLFAMILY_DRUID: { // Ferocious Bite - if((m_spellInfo->SpellFamilyFlags & 0x000800000) && m_spellInfo->SpellVisual==6587) + if(m_caster->GetTypeId()==TYPEID_PLAYER && (m_spellInfo->SpellFamilyFlags[0] & 0x000800000) && m_spellInfo->SpellVisual[0]==6587) { - // converts each extra point of energy into ($f1+$AP/630) additional damage - float multiple = m_caster->GetTotalAttackPowerValue(BASE_ATTACK) / 630 + m_spellInfo->DmgMultiplier[effect_idx]; + // converts each extra point of energy into ($f1+$AP/410) additional damage + float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK); + float multiple = ap / 410 + m_spellInfo->DmgMultiplier[effect_idx]; damage += int32(m_caster->GetPower(POWER_ENERGY) * multiple); + damage += int32(((Player*)m_caster)->GetComboPoints() * ap * 7 / 100); m_caster->SetPower(POWER_ENERGY,0); } // Rake - else if(m_spellInfo->SpellFamilyFlags & 0x0000000000001000LL) + else if(m_spellInfo->SpellFamilyFlags[0] & 0x1000) { damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100); } // Swipe - else if(m_spellInfo->SpellFamilyFlags & 0x0010000000000000LL) + else if(m_spellInfo->SpellFamilyFlags[1] & 0x00100000) { damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.08f); } - // Starfire - else if ( m_spellInfo->SpellFamilyFlags & 0x0004LL ) - { - Unit::AuraList const& m_OverrideClassScript = m_caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); - for(Unit::AuraList::const_iterator i = m_OverrideClassScript.begin(); i != m_OverrideClassScript.end(); ++i) - { - // Starfire Bonus (caster) - switch((*i)->GetModifier()->m_miscvalue) - { - case 5481: // Nordrassil Regalia - bonus - { - Unit::AuraList const& m_periodicDamageAuras = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE); - for(Unit::AuraList::const_iterator itr = m_periodicDamageAuras.begin(); itr != m_periodicDamageAuras.end(); ++itr) - { - // Moonfire or Insect Swarm (target debuff from any casters) - if ( (*itr)->GetSpellProto()->SpellFamilyFlags & 0x00200002LL ) - { - int32 mod = (*i)->GetModifier()->m_amount; - damage += damage*mod/100; - break; - } - } - break; - } - case 5148: //Improved Starfire - Ivory Idol of the Moongoddes Aura - { - damage += (*i)->GetModifier()->m_amount; - break; - } - } - } - } //Mangle Bonus for the initial damage of Lacerate and Rake - if((m_spellInfo->SpellFamilyFlags==0x0000000000001000LL && m_spellInfo->SpellIconID==494) || - (m_spellInfo->SpellFamilyFlags==0x0000010000000000LL && m_spellInfo->SpellIconID==2246)) + if((m_spellInfo->SpellFamilyFlags.IsEqual(0x1000,0,0) && m_spellInfo->SpellIconID==494) || + (m_spellInfo->SpellFamilyFlags.IsEqual(0,0x100,0) && m_spellInfo->SpellIconID==2246)) { - Unit::AuraList const& mDummyAuras = unitTarget->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraList::const_iterator i = mDummyAuras.begin(); i != mDummyAuras.end(); ++i) - if((*i)->GetSpellProto()->SpellFamilyFlags & 0x0000044000000000LL && (*i)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_DRUID) + Unit::AuraEffectList const& mDummyAuras = unitTarget->GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraEffectList::const_iterator i = mDummyAuras.begin(); i != mDummyAuras.end(); ++i) + if((*i)->GetSpellProto()->SpellFamilyFlags[1] & 0x00000440 && (*i)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_DRUID) { - damage = int32(damage*(100.0f+(*i)->GetModifier()->m_amount)/100.0f); + damage = int32(damage*(100.0f+(*i)->GetAmount())/100.0f); break; } } @@ -494,78 +511,97 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx) case SPELLFAMILY_ROGUE: { // Envenom - if(m_caster->GetTypeId()==TYPEID_PLAYER && (m_spellInfo->SpellFamilyFlags & 0x800000000LL)) + if(m_caster->GetTypeId()==TYPEID_PLAYER && (m_spellInfo->SpellFamilyFlags[1] & 0x8)) { // consume from stack dozes not more that have combo-points if(uint32 combo = ((Player*)m_caster)->GetComboPoints()) { - // count consumed deadly poison doses at target - uint32 doses = 0; - - // remove consumed poison doses - Unit::AuraList const& auras = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE); - for(Unit::AuraList::const_iterator itr = auras.begin(); itr!=auras.end() && combo;) - { - // Deadly poison (only attacker applied) - if( (*itr)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_ROGUE && ((*itr)->GetSpellProto()->SpellFamilyFlags & 0x10000) && - (*itr)->GetSpellProto()->SpellVisual==5100 && (*itr)->GetCasterGUID()==m_caster->GetGUID() ) + Aura *poison = 0; + // Lookup for Deadly poison (only attacker applied) + Unit::AuraEffectList const& auras = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE); + for(Unit::AuraEffectList::const_iterator itr = auras.begin(); itr!=auras.end(); ++itr) + if( (*itr)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_ROGUE && + (*itr)->GetSpellProto()->SpellFamilyFlags[0] & 0x10000 && + (*itr)->GetCasterGUID()==m_caster->GetGUID() ) { - --combo; - ++doses; - - unitTarget->RemoveSingleAuraFromStack((*itr)->GetId(), (*itr)->GetEffIndex()); - - itr = auras.begin(); + poison = (*itr)->GetParentAura(); + break; } - else - ++itr; + // count consumed deadly poison doses at target + if (poison) + { + uint32 spellId = poison->GetId(); + uint32 doses = poison->GetStackAmount(); + if (doses > combo) + doses = combo; + for (int i=0; i< doses; i++) + unitTarget->RemoveAuraFromStack(spellId); + damage *= doses; + damage += int32(((Player*)m_caster)->GetTotalAttackPowerValue(BASE_ATTACK) * 0.03f * doses); } - - damage *= doses; - damage += int32(((Player*)m_caster)->GetTotalAttackPowerValue(BASE_ATTACK) * 0.03f * doses); - // Eviscerate and Envenom Bonus Damage (item set effect) if(m_caster->GetDummyAura(37169)) damage += ((Player*)m_caster)->GetComboPoints()*40; } } // Eviscerate - else if((m_spellInfo->SpellFamilyFlags & 0x00020000LL) && m_caster->GetTypeId()==TYPEID_PLAYER) + else if((m_spellInfo->SpellFamilyFlags[0] & 0x00020000) && m_caster->GetTypeId()==TYPEID_PLAYER) { if(uint32 combo = ((Player*)m_caster)->GetComboPoints()) { - damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * combo * 0.03f); + float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK); + damage += irand(int32(ap * combo * 0.03f), int32(ap * combo * 0.07f)); // Eviscerate and Envenom Bonus Damage (item set effect) if(m_caster->GetDummyAura(37169)) damage += combo*40; } } + // Gouge + else if(m_spellInfo->SpellFamilyFlags[0] & 0x8) + { + damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.02f); + } + // Instant Poison + else if(m_spellInfo->SpellFamilyFlags[0] & 0x2000) + { + damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.10f); + } + // Wound Poison + else if(m_spellInfo->SpellFamilyFlags[0] & 0x10000000) + { + damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.04f); + } break; } case SPELLFAMILY_HUNTER: { // Mongoose Bite - if((m_spellInfo->SpellFamilyFlags & 0x000000002) && m_spellInfo->SpellVisual==342) + if((m_spellInfo->SpellFamilyFlags[0] & 0x2) && m_spellInfo->SpellVisual[0]==342) + { + damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.2f); + } + // Counterattack + else if(m_spellInfo->SpellFamilyFlags[1] & 0x00080000) { - damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.2); + damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.2f); } // Arcane Shot - else if((m_spellInfo->SpellFamilyFlags & 0x00000800) && m_spellInfo->maxLevel > 0) + else if((m_spellInfo->SpellFamilyFlags[0] & 0x00000800) && m_spellInfo->maxLevel > 0) { - damage += int32(m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)*0.15); + damage += int32(m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)*0.15f); } // Steady Shot - else if(m_spellInfo->SpellFamilyFlags & 0x100000000LL) + else if(m_spellInfo->SpellFamilyFlags[1] & 0x1) { int32 base = irand((int32)m_caster->GetWeaponDamageRange(RANGED_ATTACK, MINDAMAGE),(int32)m_caster->GetWeaponDamageRange(RANGED_ATTACK, MAXDAMAGE)); - damage += int32(float(base)/m_caster->GetAttackTime(RANGED_ATTACK)*2800 + m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)*0.2f); + damage += int32(float(base)/m_caster->GetAttackTime(RANGED_ATTACK)*2800 + m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)*0.1f); bool found = false; // check dazed affect - Unit::AuraList const& decSpeedList = unitTarget->GetAurasByType(SPELL_AURA_MOD_DECREASE_SPEED); - for(Unit::AuraList::const_iterator iter = decSpeedList.begin(); iter != decSpeedList.end(); ++iter) + Unit::AuraEffectList const& decSpeedList = unitTarget->GetAurasByType(SPELL_AURA_MOD_DECREASE_SPEED); + for(Unit::AuraEffectList::const_iterator iter = decSpeedList.begin(); iter != decSpeedList.end(); ++iter) { if((*iter)->GetSpellProto()->SpellIconID==15 && (*iter)->GetSpellProto()->Dispel==0) { @@ -578,28 +614,59 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx) if(found) damage += m_spellInfo->EffectBasePoints[1]; } - //Explosive Trap Effect - else if(m_spellInfo->SpellFamilyFlags & 0x00000004) + // Explosive Trap Effect + else if(m_spellInfo->SpellFamilyFlags[0] & 0x00000004) { - damage += int32(m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)*0.1); + damage += int32(m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)*0.1f); } break; } case SPELLFAMILY_PALADIN: { - //Judgement of Vengeance - if((m_spellInfo->SpellFamilyFlags & 0x800000000LL) && m_spellInfo->SpellIconID==2292) + // Judgement of Vengeance/Corruption ${1+0.22*$SPH+0.14*$AP} + 10% for each application of Holy Vengeance/Blood Corruption on the target + if((m_spellInfo->SpellFamilyFlags[1] & 0x400000) && m_spellInfo->SpellIconID==2292) { + float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK); + int32 holy = m_caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellInfo)) + + m_caster->SpellBaseDamageBonusForVictim(GetSpellSchoolMask(m_spellInfo), unitTarget); + damage+=int32(ap * 0.14f) + int32(holy * 22 / 100); + // Get stack of Holy Vengeance/Blood Corruption on the target added by caster uint32 stacks = 0; - Unit::AuraList const& auras = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE); - for(Unit::AuraList::const_iterator itr = auras.begin(); itr!=auras.end(); ++itr) - if((*itr)->GetId() == 31803 && (*itr)->GetCasterGUID()==m_caster->GetGUID()) - ++stacks; - if(!stacks) - //No damage if the target isn't affected by this - damage = -1; - else - damage *= stacks; + Unit::AuraEffectList const& auras = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE); + for(Unit::AuraEffectList::const_iterator itr = auras.begin(); itr!=auras.end(); ++itr) + if(((*itr)->GetId() == 31803 || (*itr)->GetId() == 53742) && (*itr)->GetCasterGUID()==m_caster->GetGUID()) + { + stacks = (*itr)->GetParentAura()->GetStackAmount(); + break; + } + // + 10% for each application of Holy Vengeance/Blood Corruption on the target + if(stacks) + damage += damage * stacks * 10 /100; + } + // Avenger's Shield ($m1+0.07*$SPH+0.07*$AP) + else if(m_spellInfo->SpellFamilyFlags[0] & 0x4000) + { + float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK); + damage += int32(ap * 0.07f); + } + // Hammer of Wrath ($m1+0.15*$SPH+0.15*$AP) + else if(m_spellInfo->SpellFamilyFlags[1] & 0x00000080) + { + float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK); + damage += int32(ap * 0.15f); + } + // Hammer of the Righteous + else if(m_spellInfo->SpellFamilyFlags[1]&0x00040000) + { + // Add main hand dps * effect[2] amount + float averange = (m_caster->GetFloatValue(UNIT_FIELD_MINDAMAGE) + m_caster->GetFloatValue(UNIT_FIELD_MAXDAMAGE)) / 2; + int32 count = m_caster->CalculateSpellDamage(m_spellInfo, 2, m_spellInfo->EffectBasePoints[2], unitTarget); + damage += count * int32(averange * IN_MILISECONDS) / m_caster->GetAttackTime(BASE_ATTACK); + } + // Shield of Righteousness + else if(m_spellInfo->SpellFamilyFlags[1]&0x00100000) + { + damage+=int32(m_caster->GetShieldBlockValue()); } break; } @@ -659,7 +726,7 @@ void Spell::EffectDummy(uint32 i) if (!creatureTarget || !pGameObj) return; - if (!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), 181574, creatureTarget->GetMap(), + if (!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), 181574, creatureTarget->GetMap(), creatureTarget->GetPhaseMask(), creatureTarget->GetPositionX(), creatureTarget->GetPositionY(), creatureTarget->GetPositionZ(), creatureTarget->GetOrientation(), 0, 0, 0, 0, 100, 1)) { @@ -767,12 +834,6 @@ void Spell::EffectDummy(uint32 i) m_caster->CastCustomSpell(unitTarget, 12721, &deepWoundsDotBasePoints0, NULL, NULL, true, NULL); return; } - case 12975: //Last Stand - { - int32 healthModSpellBasePoints0 = int32(m_caster->GetMaxHealth()*0.3); - m_caster->CastCustomSpell(m_caster, 12976, &healthModSpellBasePoints0, NULL, NULL, true, NULL); - return; - } case 13120: // net-o-matic { if(!unitTarget) @@ -809,30 +870,6 @@ void Spell::EffectDummy(uint32 i) } return; } - case 14185: // Preparation Rogue - { - if(m_caster->GetTypeId()!=TYPEID_PLAYER) - 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) - { - uint32 classspell = itr->first; - SpellEntry const *spellInfo = sSpellStore.LookupEntry(classspell); - - if (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && (spellInfo->SpellFamilyFlags & 0x26000000860LL)) - { - ((Player*)m_caster)->RemoveSpellCooldown(classspell); - - WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8)); - data << uint32(classspell); - data << uint64(m_caster->GetGUID()); - ((Player*)m_caster)->GetSession()->SendPacket(&data); - } - } - return; - } case 15998: // Capture Worg Pup case 29435: // Capture Female Kaliri Hatchling { @@ -897,19 +934,20 @@ void Spell::EffectDummy(uint32 i) if(creatureTarget->isPet()) return; + GameObject* Crystal_Prison = m_caster->SummonGameObject(179644, creatureTarget->GetPositionX(), creatureTarget->GetPositionY(), creatureTarget->GetPositionZ(), creatureTarget->GetOrientation(), 0, 0, 0, 0, creatureTarget->GetRespawnTime()-time(NULL)); + sLog.outDebug("SummonGameObject at SpellEfects.cpp EffectDummy for Spell 23019"); + creatureTarget->setDeathState(JUST_DIED); creatureTarget->RemoveCorpse(); creatureTarget->SetHealth(0); // just for nice GM-mode view - GameObject* Crystal_Prison = m_caster->SummonGameObject(179644, creatureTarget->GetPositionX(), creatureTarget->GetPositionY(), creatureTarget->GetPositionZ(), creatureTarget->GetOrientation(), 0, 0, 0, 0, creatureTarget->GetRespawnTime()-time(NULL)); - sLog.outDebug("SummonGameObject at SpellEfects.cpp EffectDummy for Spell 23019\n"); WorldPacket data(SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE, 8); data << uint64(Crystal_Prison->GetGUID()); m_caster->SendMessageToSet(&data,true); return; } - case 23074: // Arc. Dragonling + case 23074: // Arcanite Dragonling if (!m_CastItem) return; m_caster->CastSpell(m_caster,19804,true,m_CastItem); return; @@ -957,7 +995,7 @@ void Spell::EffectDummy(uint32 i) float flyspeed = m_caster->GetSpeedRate(MOVE_FLIGHT); float speed = m_caster->GetSpeedRate(MOVE_RUN); - m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); + m_caster->RemoveAurasByType(SPELL_AURA_MOUNTED); //5 different spells used depending on mounted speed and if mount can fly or not if (flyspeed >= 4.1f) @@ -983,7 +1021,7 @@ void Spell::EffectDummy(uint32 i) } case 28730: // Arcane Torrent (Mana) { - Aura * dummy = m_caster->GetDummyAura(28734); + Aura * dummy = m_caster->GetAura(28734, m_caster->GetGUID()); if (dummy) { int32 bp = damage * dummy->GetStackAmount(); @@ -1121,36 +1159,6 @@ void Spell::EffectDummy(uint32 i) m_caster->CastSpell(m_caster,42337,true,NULL); return; } - case 37573: //Temporal Phase Modulator - { - if(!unitTarget) - return; - - TemporarySummon* tempSummon = dynamic_cast<TemporarySummon*>(unitTarget); - if(!tempSummon) - return; - - uint32 health = tempSummon->GetHealth(); - const uint32 entry_list[6] = {21821, 21820, 21817}; - - float x = tempSummon->GetPositionX(); - float y = tempSummon->GetPositionY(); - float z = tempSummon->GetPositionZ(); - float o = tempSummon->GetOrientation(); - - tempSummon->UnSummon(); - - Creature* pCreature = m_caster->SummonCreature(entry_list[urand(0, 2)], x, y, z, o,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,180000); - if (!pCreature) - return; - - pCreature->SetHealth(health); - - if(pCreature->IsAIEnabled) - pCreature->AI()->AttackStart(m_caster); - - return; - } case 34665: //Administer Antidote { if(!unitTarget || m_caster->GetTypeId() != TYPEID_PLAYER ) @@ -1159,7 +1167,7 @@ void Spell::EffectDummy(uint32 i) if(!unitTarget) return; - TemporarySummon* tempSummon = dynamic_cast<TemporarySummon*>(unitTarget); + TempSummon* tempSummon = dynamic_cast<TempSummon*>(unitTarget); if(!tempSummon) return; @@ -1176,7 +1184,7 @@ void Spell::EffectDummy(uint32 i) return; pCreature->SetHealth(health); - ((Player*)m_caster)->KilledMonster(16992,pCreature->GetGUID()); + ((Player*)m_caster)->RewardPlayerAndGroupAtEvent(16992,pCreature); if (pCreature->IsAIEnabled) pCreature->AI()->AttackStart(m_caster); @@ -1226,7 +1234,34 @@ void Spell::EffectDummy(uint32 i) m_caster->CastSpell(m_caster, 30452, true, NULL); return; - } + } + case 52308: + { + switch(i) + { + case 0: + { + uint32 spellID = m_spellInfo->CalculateSimpleValue(0); + uint32 reqAuraID = m_spellInfo->CalculateSimpleValue(1); + + if (m_caster->HasAuraEffect(reqAuraID,0)) + m_caster->CastSpell(m_caster,spellID,true,NULL); + return; + } + case 1: + return; // additional data for dummy[0] + } + return; + } + case 53341: + case 53343: + { + m_caster->CastSpell(m_caster,54586,true); + return; + } + case 58418: // Portal to Orgrimmar + case 58420: // Portal to Stormwind + return; // implemented in EffectScript[0] } //All IconID Check in there @@ -1300,65 +1335,129 @@ void Spell::EffectDummy(uint32 i) break; case SPELLFAMILY_WARRIOR: // Charge - if(m_spellInfo->SpellFamilyFlags & 0x1 && m_spellInfo->SpellVisual == 867) + if(m_spellInfo->SpellFamilyFlags & 0x1 && m_spellInfo->SpellVisual[0] == 867) { int32 chargeBasePoints0 = damage; m_caster->CastCustomSpell(m_caster,34846,&chargeBasePoints0,NULL,NULL,true); return; } + //Slam + if(m_spellInfo->SpellFamilyFlags[0] & 0x200000 && m_spellInfo->SpellIconID == 559) + { + int32 bp0 = damage; + m_caster->CastCustomSpell(unitTarget, 50783, &bp0, NULL, NULL, true, 0); + return; + } // Execute - if(m_spellInfo->SpellFamilyFlags & 0x20000000) + if(m_spellInfo->SpellFamilyFlags[0] & 0x20000000) { if(!unitTarget) return; + uint32 rage = m_caster->GetPower(POWER_RAGE); + // Glyph of Execution bonus + if (AuraEffect *aura = m_caster->GetDummyAura(58367)) + rage+=aura->GetAmount(); + spell_id = 20647; - bp = damage+int32(m_caster->GetPower(POWER_RAGE) * m_spellInfo->DmgMultiplier[i]); - m_caster->SetPower(POWER_RAGE,0); + bp = damage+int32(rage * m_spellInfo->DmgMultiplier[i] + + m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.2f); + // Sudden death cost modifier + if (Aura * aur = m_caster->GetAura(52437)) + { + m_caster->ModifyPower(POWER_RAGE,- m_powerCost); + if (m_caster->GetPower(POWER_RAGE)<100) + m_caster->SetPower(POWER_RAGE,100); + m_caster->RemoveAura(aur); + } + else + m_caster->SetPower(POWER_RAGE,0); break; } - if(m_spellInfo->Id==21977) //Warrior's Wrath + // Slam + if(m_spellInfo->SpellFamilyFlags[0] & 0x200000) { if(!unitTarget) return; - - m_caster->CastSpell(unitTarget,21887,true); // spell mod + m_damage+=m_caster->CalculateDamage(m_attackType, false); + m_damage+=damage; return; } + switch(m_spellInfo->Id) + { + // Warrior's Wrath + case 21977: + { + if(!unitTarget) + return; + m_caster->CastSpell(unitTarget,21887,true); // spell mod + return; + } + // Last Stand + case 12975: + { + int32 healthModSpellBasePoints0 = int32(m_caster->GetMaxHealth()*0.3); + 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: - //Life Tap (only it have this with dummy effect) - if (m_spellInfo->SpellFamilyFlags == 0x40000) + // Life Tap + if (m_spellInfo->SpellFamilyFlags[0] & 0x40000) { - float cost = damage; - - if(Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, cost,this); - - int32 dmg = m_caster->SpellDamageBonus(m_caster, m_spellInfo,uint32(cost > 0 ? cost : 0), SPELL_DIRECT_DAMAGE); - - if(int32(m_caster->GetHealth()) > dmg) + // In 303 exist spirit depend + uint32 spirit = uint32(m_caster->GetStat(STAT_SPIRIT)); + switch (m_spellInfo->Id) + { + case 1454: damage+=spirit; break; + case 1455: damage+=spirit*15/10; break; + case 1456: damage+=spirit*2; break; + case 11687: damage+=spirit*25/10; break; + case 11688: + case 11689: + case 27222: + case 57946: damage+=spirit*3; break; + default: + sLog.outError("Spell::EffectDummy: %u Life Tap need set spirit multipler", m_spellInfo->Id); + return; + } +// Think its not need (also need remove Life Tap from SpellDamageBonus or add new value) +// damage = m_caster->SpellDamageBonus(m_caster, m_spellInfo,uint32(damage > 0 ? damage : 0), SPELL_DIRECT_DAMAGE); + if(int32(unitTarget->GetHealth()) > damage) { // Shouldn't Appear in Combat Log - m_caster->ModifyHealth(-dmg); - - int32 mana = dmg; + unitTarget->ModifyHealth(-damage); - Unit::AuraList const& auraDummy = m_caster->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraList::const_iterator itr = auraDummy.begin(); itr != auraDummy.end(); ++itr) + int32 mana = damage; + // Improved Life Tap mod + Unit::AuraEffectList const& auraDummy = m_caster->GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraEffectList::const_iterator itr = auraDummy.begin(); itr != auraDummy.end(); ++itr) { - // only Imp. Life Tap have this in combination with dummy aura if((*itr)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_WARLOCK && (*itr)->GetSpellProto()->SpellIconID == 208) - mana = ((*itr)->GetModifier()->m_amount + 100)* mana / 100; + mana = ((*itr)->GetAmount() + 100)* mana / 100; } - - m_caster->CastCustomSpell(m_caster,31818,&mana,NULL,NULL,true,NULL); + m_caster->CastCustomSpell(unitTarget, 31818, &mana, NULL, NULL, true); // Mana Feed - int32 manaFeedVal = m_caster->CalculateSpellDamage(m_spellInfo,1, m_spellInfo->EffectBasePoints[1],m_caster); - manaFeedVal = manaFeedVal * mana / 100; + int32 manaFeedVal = 0; + Unit::AuraEffectList const& mod = m_caster->GetAurasByType(SPELL_AURA_ADD_FLAT_MODIFIER); + for(Unit::AuraEffectList::const_iterator itr = mod.begin(); itr != mod.end(); ++itr) + { + if((*itr)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_WARLOCK && (*itr)->GetSpellProto()->SpellIconID == 1982) + manaFeedVal+= (*itr)->GetAmount(); + } if(manaFeedVal > 0) - m_caster->CastCustomSpell(m_caster,32553,&manaFeedVal,NULL,NULL,true,NULL); + { + manaFeedVal = manaFeedVal * mana / 100; + m_caster->CastCustomSpell(m_caster, 32553, &manaFeedVal, NULL, NULL, true, NULL); + } } else SendCastResult(SPELL_FAILED_FIZZLE); @@ -1366,52 +1465,54 @@ void Spell::EffectDummy(uint32 i) } break; case SPELLFAMILY_PRIEST: - switch(m_spellInfo->Id ) + // Penance + if (m_spellInfo->SpellFamilyFlags[1] & 0x00800000) { - case 28598: // Touch of Weakness triggered spell + if (!unitTarget) + return; + + int hurt = 0; + int heal = 0; + switch(m_spellInfo->Id) { - if(!unitTarget || !m_triggeredByAuraSpell) + case 47540: hurt = 47758; heal = 47757; break; + case 53005: hurt = 53001; heal = 52986; break; + case 53006: hurt = 53002; heal = 52987; break; + case 53007: hurt = 53003; heal = 52988; break; + default: + sLog.outError("Spell::EffectDummy: Spell %u Penance need set correct heal/damage spell", m_spellInfo->Id); return; - - uint32 spellid = 0; - switch(m_triggeredByAuraSpell->Id) - { - case 2652: spellid = 2943; break; // Rank 1 - case 19261: spellid = 19249; break; // Rank 2 - case 19262: spellid = 19251; break; // Rank 3 - case 19264: spellid = 19252; break; // Rank 4 - case 19265: spellid = 19253; break; // Rank 5 - case 19266: spellid = 19254; break; // Rank 6 - case 25461: spellid = 25460; break; // Rank 7 - default: - sLog.outError("Spell::EffectDummy: Spell 28598 triggered by unhandled spell %u",m_triggeredByAuraSpell->Id); - return; - } - m_caster->CastSpell(unitTarget, spellid, true, NULL); - return; } + if (m_caster->IsFriendlyTo(unitTarget)) + m_caster->CastSpell(unitTarget, heal, true, 0); + else + m_caster->CastSpell(unitTarget, hurt, true, 0); + return; } break; case SPELLFAMILY_DRUID: - switch(m_spellInfo->Id ) + // Starfall + if (m_spellInfo->SpellFamilyFlags[2] & 0x100) { - case 5420: // Tree of Life passive + //Shapeshifting into an animal form or mounting cancels the effect. + if(m_caster->GetCreatureType() == CREATURE_TYPE_BEAST || m_caster->IsMounted()) { - // 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); + if(m_triggeredByAuraSpell) + m_caster->RemoveAurasDueToSpell(m_triggeredByAuraSpell->Id); return; } + + //Any effect which causes you to lose control of your character will supress the starfall effect. + if(m_caster->hasUnitState(UNIT_STAT_STUNNED | UNIT_STAT_FLEEING | UNIT_STAT_ROOT | UNIT_STAT_CONFUSED)) + return; + + m_caster->CastSpell(unitTarget, damage, true); + return; } break; case SPELLFAMILY_ROGUE: switch(m_spellInfo->Id ) { - case 31231: // Cheat Death - { - m_caster->CastSpell(m_caster,45182,true); - return; - } case 5938: // Shiv { if(m_caster->GetTypeId() != TYPEID_PLAYER) @@ -1447,36 +1548,38 @@ void Spell::EffectDummy(uint32 i) m_caster->CastSpell(unitTarget, 5940, true); return; } - } - break; - case SPELLFAMILY_HUNTER: - // Kill command - if(m_spellInfo->SpellFamilyFlags & 0x00080000000000LL) - { - if(m_caster->getClass()!=CLASS_HUNTER) - return; + case 14185: // Preparation Rogue + { + if(m_caster->GetTypeId()!=TYPEID_PLAYER) + return; - // clear hunter crit aura state - m_caster->ModifyAuraState(AURA_STATE_HUNTER_CRIT_STRIKE,false); + //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) + { + uint32 classspell = itr->first; + SpellEntry const *spellInfo = sSpellStore.LookupEntry(classspell); - // additional damage from pet to pet target - Pet* pet = m_caster->GetPet(); - if(!pet || !pet->getVictim()) - return; + if (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && (spellInfo->SpellFamilyFlags[1] & 0x00000240 || spellInfo->SpellFamilyFlags[0] & 0x00000860)) + { + ((Player*)m_caster)->RemoveSpellCooldown(classspell); - uint32 spell_id = 0; - switch (m_spellInfo->Id) + WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8)); + data << uint32(classspell); + data << uint64(m_caster->GetGUID()); + ((Player*)m_caster)->GetSession()->SendPacket(&data); + } + } + return; + } + case 31231: // Cheat Death { - case 34026: spell_id = 34027; break; // rank 1 - default: - sLog.outError("Spell::EffectDummy: Spell %u not handled in KC",m_spellInfo->Id); + m_caster->CastSpell(m_caster,45182,true); return; } - - pet->CastSpell(pet->getVictim(), spell_id, true); - return; } - + break; + case SPELLFAMILY_HUNTER: switch(m_spellInfo->Id) { case 23989: //Readiness talent @@ -1519,6 +1622,13 @@ void Spell::EffectDummy(uint32 i) case SPELLFAMILY_PALADIN: switch(m_spellInfo->SpellIconID) { + // Divine Storm + if (m_spellInfo->SpellFamilyFlags[1] & 0x20000) + { + int32 damage=m_currentBasePoints[0] * damage /100; + m_caster->CastCustomSpell(unitTarget, 54172, &damage , 0, 0, true); + return; + } case 156: // Holy Shock { if(!unitTarget) @@ -1534,6 +1644,8 @@ void Spell::EffectDummy(uint32 i) case 20930: hurt = 25902; heal = 25903; break; case 27174: hurt = 27176; heal = 27175; break; case 33072: hurt = 33073; heal = 33074; break; + case 48824: hurt = 48822; heal = 48820; break; + case 48825: hurt = 48823; heal = 48821; break; default: sLog.outError("Spell::EffectDummy: Spell %u not handled in HS",m_spellInfo->Id); return; @@ -1546,32 +1658,31 @@ void Spell::EffectDummy(uint32 i) return; } - case 561: // Judgement of command + } + + switch(m_spellInfo->Id) + { + case 20425: // Judgement of command { if(!unitTarget) return; - uint32 spell_id = m_spellInfo->EffectBasePoints[i]+1;//m_currentBasePoints[i]+1; - SpellEntry const* spell_proto = sSpellStore.LookupEntry(spell_id); + SpellEntry const* spell_proto = sSpellStore.LookupEntry(damage); if(!spell_proto) return; - if( !unitTarget->hasUnitState(UNIT_STAT_STUNNED) && m_caster->GetTypeId()==TYPEID_PLAYER) + if(unitTarget->hasUnitState(UNIT_STAT_STUNNED) && m_caster->GetTypeId()==TYPEID_PLAYER) { - // decreased damage (/2) for non-stunned target. + // always critical for stunned target SpellModifier *mod = new SpellModifier; - mod->op = SPELLMOD_DAMAGE; - mod->value = -50; - mod->type = SPELLMOD_PCT; + mod->op = SPELLMOD_CRITICAL_CHANCE; + mod->value = 100; + mod->type = SPELLMOD_FLAT; mod->spellId = m_spellInfo->Id; - mod->effectId = i; - mod->lastAffected = NULL; - mod->mask = 0x0000020000000000LL; - mod->charges = 0; + mod->mask[1] = 0x00000200; ((Player*)m_caster)->AddSpellMod(mod, true); m_caster->CastSpell(unitTarget,spell_proto,true,NULL); - // mod deleted ((Player*)m_caster)->AddSpellMod(mod, false); } else @@ -1579,10 +1690,6 @@ void Spell::EffectDummy(uint32 i) return; } - } - - switch(m_spellInfo->Id) - { case 31789: // Righteous Defense (step 1) { // 31989 -> dummy effect (step 1) + dummy effect (step 2) -> 31709 (taunt like spell for each target) @@ -1649,8 +1756,10 @@ void Spell::EffectDummy(uint32 i) break; case SPELLFAMILY_SHAMAN: //Shaman Rockbiter Weapon - if (m_spellInfo->SpellFamilyFlags == 0x400000) + if (m_spellInfo->SpellFamilyFlags.IsEqual(0x400000)) { + // TODO: use expect spell for enchant (if exist talent) + // In 3.0.3 no mods present for rockbiter uint32 spell_id = 0; switch(m_spellInfo->Id) { @@ -1658,11 +1767,6 @@ void Spell::EffectDummy(uint32 i) case 8018: spell_id = 36750; break; // Rank 2 case 8019: spell_id = 36755; break; // Rank 3 case 10399: spell_id = 36759; break; // Rank 4 - case 16314: spell_id = 36763; break; // Rank 5 - case 16315: spell_id = 36766; break; // Rank 6 - case 16316: spell_id = 36771; break; // Rank 7 - case 25479: spell_id = 36775; break; // Rank 8 - case 25485: spell_id = 36499; break; // Rank 9 default: sLog.outError("Spell::EffectDummy: Spell %u not handled in RW",m_spellInfo->Id); return; @@ -1672,7 +1776,7 @@ void Spell::EffectDummy(uint32 i) if(!spellInfo) { - sLog.outError("WORLD: unknown spell id %i\n", spell_id); + sLog.outError("WORLD: unknown spell id %i", spell_id); return; } @@ -1700,18 +1804,83 @@ void Spell::EffectDummy(uint32 i) } return; } - - if(m_spellInfo->Id == 39610) // Mana-Tide Totem effect + // Healing Stream Totem + if(m_spellInfo->SpellFamilyFlags[0] & 0x2000) + { + m_caster->CastCustomSpell(unitTarget, 52042, &damage, 0, 0, true, 0, 0, m_originalCasterGUID); + return; + } + // Mana Spring Totem + if(m_spellInfo->SpellFamilyFlags[0] & 0x4000) + { + if(unitTarget->getPowerType()!=POWER_MANA) + return; + m_caster->CastCustomSpell(unitTarget, 52032, &damage, 0, 0, true, 0, 0, m_originalCasterGUID); + return; + } + if(m_spellInfo->Id == 39610) // Mana Tide Totem effect { if(!unitTarget || unitTarget->getPowerType() != POWER_MANA) return; - + // Glyph of Mana Tide + Unit *owner = m_caster->GetOwner(); + if (owner) + if (AuraEffect *dummy = owner->GetDummyAura(55441)) + damage+=dummy->GetAmount(); // Regenerate 6% of Total Mana Every 3 secs int32 EffectBasePoints0 = unitTarget->GetMaxPower(POWER_MANA) * damage / 100; m_caster->CastCustomSpell(unitTarget,39609,&EffectBasePoints0,NULL,NULL,true,NULL,NULL,m_originalCasterGUID); return; } + // Lava Lash + if (m_spellInfo->SpellFamilyFlags[2] & 0x00000004) + { + if (m_caster->GetTypeId()!=TYPEID_PLAYER) + return; + Item *item = ((Player*)m_caster)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); + if (item) + { + if (m_caster->GetAura(SPELL_AURA_DUMMY, SPELLFAMILY_SHAMAN, 0x200000)) + { + m_damage += m_damage * damage / 100; + } + } + return; + } + break; + case SPELLFAMILY_DEATHKNIGHT: + // Death strike dummy aura apply + // Used to proc healing later + if (m_spellInfo->SpellFamilyFlags[0] & 0x00000010) + { + spell_id=45469; + m_caster->CastSpell(m_caster,spell_id,true); + return; + } + // Death Coil + else if(m_spellInfo->SpellFamilyFlags[0] & 0x002000) + { + if(m_caster->IsFriendlyTo(unitTarget)) + { + if(unitTarget->GetCreatureType() != CREATURE_TYPE_UNDEAD) + return; + int32 bp = damage * 1.5f; + m_caster->CastCustomSpell(unitTarget,47633,&bp,NULL,NULL,true); + } + else + { + int32 bp = damage; + m_caster->CastCustomSpell(unitTarget,47632,&bp,NULL,NULL,true); + } + return; + } + // Death Grip + else if(m_spellInfo->Id == 49560) + { + unitTarget->CastSpell(m_caster, damage, true); + return; + } break; } @@ -1739,6 +1908,15 @@ void Spell::EffectDummy(uint32 i) m_caster->AddPetAura(petSpell); return; } + + // Script based implementation. Must be used only for not good for implementation in core spell effects + // So called only for not proccessed cases + if(gameObjTarget) + Script->EffectDummyGameObj(m_caster, m_spellInfo->Id, i, gameObjTarget); + else if(unitTarget && unitTarget->GetTypeId()==TYPEID_UNIT) + Script->EffectDummyCreature(m_caster, m_spellInfo->Id, i, (Creature*)unitTarget); + else if(itemTarget) + Script->EffectDummyItem(m_caster, m_spellInfo->Id, i, itemTarget); } void Spell::EffectTriggerSpellWithValue(uint32 i) @@ -1750,7 +1928,7 @@ void Spell::EffectTriggerSpellWithValue(uint32 i) if(!spellInfo) { - sLog.outError("EffectTriggerSpellWithValue of spell %u: triggering unknown spell id %i\n", m_spellInfo->Id,triggered_spell_id); + sLog.outError("EffectTriggerSpellWithValue of spell %u: triggering unknown spell id %i", m_spellInfo->Id,triggered_spell_id); return; } @@ -1776,8 +1954,8 @@ void Spell::EffectTriggerRitualOfSummoning(uint32 i) targets.setUnitTarget( unitTarget); spell->prepare(&targets); - m_caster->SetCurrentCastedSpell(spell); - spell->m_selfContainer = &(m_caster->m_currentSpells[spell->GetCurrentContainer()]); + //m_caster->SetCurrentCastedSpell(spell); + //spell->m_selfContainer = &(m_caster->m_currentSpells[spell->GetCurrentContainer()]); } @@ -1810,9 +1988,9 @@ void Spell::EffectTriggerSpell(uint32 i) // Vanish case 18461: { - m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT); - m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_DECREASE_SPEED); - m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_STALKED); + m_caster->RemoveAurasByType(SPELL_AURA_MOD_ROOT); + m_caster->RemoveAurasByType(SPELL_AURA_MOD_DECREASE_SPEED); + m_caster->RemoveAurasByType(SPELL_AURA_MOD_STALKED); // if this spell is given to NPC it must handle rest by it's own AI if ( m_caster->GetTypeId() != TYPEID_PLAYER ) @@ -1820,6 +1998,7 @@ void Spell::EffectTriggerSpell(uint32 i) // get highest rank of the Stealth spell uint32 spellId = 0; + SpellEntry const *spellInfo; const PlayerSpellMap& sp_list = ((Player*)m_caster)->GetSpellMap(); for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr) { @@ -1827,11 +2006,11 @@ void Spell::EffectTriggerSpell(uint32 i) if(!itr->second->active || itr->second->disabled || itr->second->state == PLAYERSPELL_REMOVED) continue; - SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first); + spellInfo = sSpellStore.LookupEntry(itr->first); if (!spellInfo) continue; - if (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && spellInfo->SpellFamilyFlags & SPELLFAMILYFLAG_ROGUE_STEALTH) + if (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && spellInfo->SpellFamilyFlags[0] & SPELLFAMILYFLAG_ROGUE_STEALTH) { spellId = spellInfo->Id; break; @@ -1846,7 +2025,8 @@ void Spell::EffectTriggerSpell(uint32 i) if(((Player*)m_caster)->HasSpellCooldown(spellId)) ((Player*)m_caster)->RemoveSpellCooldown(spellId); - m_caster->CastSpell(m_caster, spellId, true); + // Push stealth to list because it must be handled after combat remove + m_TriggerSpells.push_back(spellInfo); return; } // just skip @@ -1886,7 +2066,7 @@ void Spell::EffectTriggerSpell(uint32 i) { uint32 dispelMask = GetDispellMask(DISPEL_ALL); Unit::AuraMap& Auras = m_caster->GetAuras(); - for(Unit::AuraMap::iterator iter = Auras.begin(); iter != Auras.end(); ++iter) + for(Unit::AuraMap::iterator iter = Auras.begin(); iter != Auras.end();) { // remove all harmful spells on you... SpellEntry const* spell = iter->second->GetSpellProto(); @@ -1895,16 +2075,17 @@ void Spell::EffectTriggerSpell(uint32 i) // ignore positive and passive auras && !iter->second->IsPositive() && !iter->second->IsPassive()) { - m_caster->RemoveAurasDueToSpell(spell->Id); - iter = Auras.begin(); + m_caster->RemoveAura(iter); } + else + iter++; } return; } // Priest Shadowfiend (34433) need apply mana gain trigger aura on pet case 41967: { - if (Unit *pet = m_caster->GetPet()) + if (Unit *pet = m_caster->GetGuardianPet()) pet->CastSpell(pet, 28305, true); return; } @@ -1988,12 +2169,65 @@ void Spell::EffectTriggerMissileSpell(uint32 effect_idx) if (m_CastItem) DEBUG_LOG("WORLD: cast Item spellId - %i", spellInfo->Id); - Spell *spell = new Spell(m_caster, spellInfo, true, m_originalCasterGUID ); + m_caster->CastSpell(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, spellInfo->Id, true, m_CastItem, 0, m_originalCasterGUID); +} - SpellCastTargets targets; - targets.setDestination(m_targets.m_destX,m_targets.m_destY,m_targets.m_destZ); - spell->m_CastItem = m_CastItem; - spell->prepare(&targets, NULL); +void Spell::EffectJump(uint32 i) +{ + if(m_caster->isInFlight()) + return; + + // Init dest coordinates + float x,y,z,o; + if(m_targets.HasDest()) + { + x = m_targets.m_destX; + y = m_targets.m_destY; + z = m_targets.m_destZ; + + if(m_spellInfo->EffectImplicitTargetA[i] == TARGET_DEST_TARGET_BACK) + { + // explicit cast data from client or server-side cast + // some spell at client send caster + Unit* pTarget = NULL; + if(m_targets.getUnitTarget() && m_targets.getUnitTarget()!=m_caster) + pTarget = m_targets.getUnitTarget(); + else if(unitTarget->getVictim()) + pTarget = m_caster->getVictim(); + else if(m_caster->GetTypeId() == TYPEID_PLAYER) + pTarget = ObjectAccessor::GetUnit(*m_caster, ((Player*)m_caster)->GetSelection()); + + o = pTarget ? pTarget->GetOrientation() : m_caster->GetOrientation(); + } + else + o = m_caster->GetOrientation(); + } + else if(unitTarget) + { + unitTarget->GetContactPoint(m_caster,x,y,z,CONTACT_DISTANCE); + o = m_caster->GetOrientation(); + } + else if(gameObjTarget) + { + gameObjTarget->GetContactPoint(m_caster,x,y,z,CONTACT_DISTANCE); + o = m_caster->GetOrientation(); + } + else + { + sLog.outError( "Spell::EffectJump - unsupported target mode for spell ID %u", m_spellInfo->Id ); + return; + } + + //m_caster->NearTeleportTo(x,y,z,o,true); + float speedZ; + if(m_spellInfo->EffectMiscValue[i]) + speedZ = float(m_spellInfo->EffectMiscValue[i])/10; + else if(m_spellInfo->EffectMiscValueB[i]) + speedZ = float(m_spellInfo->EffectMiscValueB[i])/10; + else + speedZ = 10.0f; + float speedXY = m_caster->GetExactDistance2d(x, y) * 10.0f / speedZ; + m_caster->GetMotionMaster()->MoveJump(x, y, z, speedXY, speedZ); } void Spell::EffectTeleportUnits(uint32 i) @@ -2007,6 +2241,7 @@ void Spell::EffectTeleportUnits(uint32 i) sLog.outError( "Spell::EffectTeleportUnits - does not have destination for spell ID %u\n", m_spellInfo->Id ); return; } + // Init dest coordinates int32 mapid = m_targets.m_mapId; if(mapid < 0) mapid = (int32)unitTarget->GetMapId(); @@ -2015,16 +2250,11 @@ void Spell::EffectTeleportUnits(uint32 i) float z = m_targets.m_destZ; float orientation = m_targets.getUnitTarget() ? m_targets.getUnitTarget()->GetOrientation() : unitTarget->GetOrientation(); sLog.outDebug("Spell::EffectTeleportUnits - teleport unit to %u %f %f %f\n", mapid, x, y, z); - // Teleport - if(unitTarget->GetTypeId() == TYPEID_PLAYER) - ((Player*)unitTarget)->TeleportTo(mapid, x, y, z, orientation, TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget==m_caster ? TELE_TO_SPELL : 0)); - else - { - MapManager::Instance().GetMap(mapid, m_caster)->CreatureRelocation((Creature*)unitTarget, x, y, z, orientation); - WorldPacket data; - unitTarget->BuildTeleportAckMsg(&data, x, y, z, orientation); - unitTarget->SendMessageToSet(&data, false); - } + + if(mapid == unitTarget->GetMapId()) + unitTarget->NearTeleportTo(x, y, z, orientation, unitTarget == m_caster); + else if(unitTarget->GetTypeId() == TYPEID_PLAYER) + ((Player*)unitTarget)->TeleportTo(mapid, x, y, z, orientation, unitTarget==m_caster ? TELE_TO_SPELL : 0); // post effects for TARGET_TABLE_X_Y_Z_COORDINATES switch ( m_spellInfo->Id ) @@ -2125,84 +2355,7 @@ void Spell::EffectTeleportUnits(uint32 i) void Spell::EffectApplyAura(uint32 i) { - if(!unitTarget) - return; - - SpellImmuneList const& list = unitTarget->m_spellImmune[IMMUNITY_STATE]; - for(SpellImmuneList::const_iterator itr = list.begin(); itr != list.end(); ++itr) - if(itr->type == m_spellInfo->EffectApplyAuraName[i]) - return; - - // ghost spell check, allow apply any auras at player loading in ghost mode (will be cleanup after load) - if( !unitTarget->isAlive() && m_spellInfo->Id != 20584 && m_spellInfo->Id != 8326 && - (unitTarget->GetTypeId()!=TYPEID_PLAYER || !((Player*)unitTarget)->GetSession()->PlayerLoading()) ) - return; - - Unit* caster = m_originalCasterGUID ? m_originalCaster : m_caster; - if(!caster) - return; - - sLog.outDebug("Spell: Aura is: %u", m_spellInfo->EffectApplyAuraName[i]); - - Aura* Aur = CreateAura(m_spellInfo, i, &damage, unitTarget, caster, m_CastItem); - - // Now Reduce spell duration using data received at spell hit - int32 duration = Aur->GetAuraMaxDuration(); - unitTarget->ApplyDiminishingToDuration(m_diminishGroup,duration,caster,m_diminishLevel); - Aur->setDiminishGroup(m_diminishGroup); - - // if Aura removed and deleted, do not continue. - if(duration== 0 && !(Aur->IsPermanent())) - { - delete Aur; - return; - } - - if(duration != Aur->GetAuraMaxDuration()) - { - Aur->SetAuraMaxDuration(duration); - Aur->SetAuraDuration(duration); - } - - bool added = unitTarget->AddAura(Aur); - - // Aura not added and deleted in AddAura call; - if (!added) - return; - - // found crash at character loading, broken pointer to Aur... - // Aur was deleted in AddAura()... - if(!Aur) - return; - - // TODO Make a way so it works for every related spell! - if(unitTarget->GetTypeId()==TYPEID_PLAYER ||( unitTarget->GetTypeId()==TYPEID_UNIT && ((Creature*)unitTarget)->isPet() ) ) // Negative buff should only be applied on players - { - uint32 spellId = 0; - if(m_spellInfo->CasterAuraStateNot==AURA_STATE_WEAKENED_SOUL || m_spellInfo->TargetAuraStateNot==AURA_STATE_WEAKENED_SOUL) - spellId = 6788; // Weakened Soul - else if(m_spellInfo->CasterAuraStateNot==AURA_STATE_FORBEARANCE || m_spellInfo->TargetAuraStateNot==AURA_STATE_FORBEARANCE) - spellId = 25771; // Forbearance - else if(m_spellInfo->CasterAuraStateNot==AURA_STATE_HYPOTHERMIA) - spellId = 41425; // Hypothermia - else if (m_spellInfo->Mechanic == MECHANIC_BANDAGE) // Bandages - spellId = 11196; // Recently Bandaged - else if( (m_spellInfo->AttributesEx & 0x20) && (m_spellInfo->AttributesEx2 & 0x20000) ) - spellId = 23230; // Blood Fury - Healing Reduction - - SpellEntry const *AdditionalSpellInfo = sSpellStore.LookupEntry(spellId); - if (AdditionalSpellInfo) - { - // applied at target by target - Aura* AdditionalAura = CreateAura(AdditionalSpellInfo, 0, NULL, unitTarget,unitTarget, 0); - unitTarget->AddAura(AdditionalAura); - sLog.outDebug("Spell: Additional Aura is: %u", AdditionalSpellInfo->EffectApplyAuraName[0]); - } - } - - // Prayer of Mending (jump animation), we need formal caster instead original for correct animation - if( m_spellInfo->SpellFamilyName == SPELLFAMILY_PRIEST && (m_spellInfo->SpellFamilyFlags & 0x00002000000000LL)) - m_caster->CastSpell(unitTarget, 41637, true, NULL, Aur, m_originalCasterGUID); + return; //handled elsewhere } void Spell::EffectUnlearnSpecialization( uint32 i ) @@ -2252,7 +2405,8 @@ void Spell::EffectPowerDrain(uint32 i) unitTarget->ModifyPower(drain_power,-new_damage); - if(drain_power == POWER_MANA) + // Don`t restore from self drain + if(drain_power == POWER_MANA && m_caster != unitTarget) { float manaMultiplier = m_spellInfo->EffectMultipleValue[i]; if(manaMultiplier==0) @@ -2271,60 +2425,9 @@ void Spell::EffectPowerDrain(uint32 i) void Spell::EffectSendEvent(uint32 EffectIndex) { - if (m_caster->GetTypeId() == TYPEID_PLAYER && ((Player*)m_caster)->InBattleGround()) - { - BattleGround* bg = ((Player *)m_caster)->GetBattleGround(); - if(bg && bg->GetStatus() == STATUS_IN_PROGRESS) - { - switch(m_spellInfo->Id) - { - case 23333: // Pickup Horde Flag - /*do not uncomment . - if(bg->GetTypeID()==BATTLEGROUND_WS) - bg->EventPlayerClickedOnFlag((Player*)m_caster, gameObjTarget); - sLog.outDebug("Send Event Horde Flag Picked Up"); - break; - /* not used : - case 23334: // Drop Horde Flag - if(bg->GetTypeID()==BATTLEGROUND_WS) - bg->EventPlayerDroppedFlag((Player*)m_caster); - sLog.outDebug("Drop Horde Flag"); - break; - */ - case 23335: // Pickup Alliance Flag - /*do not uncomment ... (it will cause crash, because of null targetobject!) anyway this is a bad way to call that event, because it would cause recursion - if(bg->GetTypeID()==BATTLEGROUND_WS) - bg->EventPlayerClickedOnFlag((Player*)m_caster, gameObjTarget); - sLog.outDebug("Send Event Alliance Flag Picked Up"); - break; - /* not used : - case 23336: // Drop Alliance Flag - if(bg->GetTypeID()==BATTLEGROUND_WS) - bg->EventPlayerDroppedFlag((Player*)m_caster); - sLog.outDebug("Drop Alliance Flag"); - break; - case 23385: // Alliance Flag Returns - if(bg->GetTypeID()==BATTLEGROUND_WS) - bg->EventPlayerClickedOnFlag((Player*)m_caster, gameObjTarget); - sLog.outDebug("Alliance Flag Returned"); - break; - case 23386: // Horde Flag Returns - if(bg->GetTypeID()==BATTLEGROUND_WS) - bg->EventPlayerClickedOnFlag((Player*)m_caster, gameObjTarget); - sLog.outDebug("Horde Flag Returned"); - break;*/ - case 34976: - /* - if(bg->GetTypeID()==BATTLEGROUND_EY) - bg->EventPlayerClickedOnFlag((Player*)m_caster, gameObjTarget); - */ - break; - default: - sLog.outDebug("Unknown spellid %u in BG event", m_spellInfo->Id); - break; - } - } - } + /* + we do not handle a flag dropping or clicking on flag in battleground by sendevent system + */ sLog.outDebug("Spell ScriptStart %u for spellid %u in EffectSendEvent ", m_spellInfo->EffectMiscValue[EffectIndex], m_spellInfo->Id); sWorld.ScriptsStart(sEventScripts, m_spellInfo->EffectMiscValue[EffectIndex], m_caster, focusObject); } @@ -2345,10 +2448,20 @@ void Spell::EffectPowerBurn(uint32 i) if(damage < 0) return; + Unit* caster = m_originalCaster ? m_originalCaster : m_caster; + + // burn x% of target's mana, up to maximum of 2x% of caster's mana (Mana Burn) + if(m_spellInfo->ManaCostPercentage) + { + uint32 maxdamage = m_caster->GetMaxPower(powertype) * damage * 2 / 100; + damage = unitTarget->GetMaxPower(powertype) * damage / 100; + if(damage > maxdamage) damage = maxdamage; + } + int32 curPower = int32(unitTarget->GetPower(powertype)); - // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4) uint32 power = damage; + // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4) if ( powertype == POWER_MANA && unitTarget->GetTypeId() == TYPEID_PLAYER ) power -= ((Player*)unitTarget)->GetSpellCritDamageReduction(power); @@ -2390,27 +2503,27 @@ void Spell::SpellDamageHeal(uint32 /*i*/) { // Amount of heal - depends from stacked Holy Energy int damageAmount = 0; - Unit::AuraList const& mDummyAuras = m_caster->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i) + Unit::AuraEffectList const& mDummyAuras = m_caster->GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraEffectList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i) if((*i)->GetId() == 45062) - damageAmount+=(*i)->GetModifierValue(); + damageAmount+=(*i)->GetAmount(); if (damageAmount) m_caster->RemoveAurasDueToSpell(45062); addhealth += damageAmount; } // Swiftmend - consumes Regrowth or Rejuvenation - else if (m_spellInfo->TargetAuraState == AURA_STATE_SWIFTMEND && unitTarget->HasAuraState(AURA_STATE_SWIFTMEND)) + else if (m_spellInfo->TargetAuraState == AURA_STATE_SWIFTMEND && unitTarget->HasAuraState(AURA_STATE_SWIFTMEND, m_spellInfo, m_caster)) { - Unit::AuraList const& RejorRegr = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_HEAL); + Unit::AuraEffectList const& RejorRegr = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_HEAL); // find most short by duration - Aura *targetAura = NULL; - for(Unit::AuraList::const_iterator i = RejorRegr.begin(); i != RejorRegr.end(); ++i) + AuraEffect *targetAura = NULL; + for(Unit::AuraEffectList::const_iterator i = RejorRegr.begin(); i != RejorRegr.end(); ++i) { if((*i)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_DRUID - && ((*i)->GetSpellProto()->SpellFamilyFlags == 0x40 || (*i)->GetSpellProto()->SpellFamilyFlags == 0x10) ) + && ((*i)->GetSpellProto()->SpellFamilyFlags.IsEqual(0x40) || (*i)->GetSpellProto()->SpellFamilyFlags.IsEqual(0x10)) ) { - if(!targetAura || (*i)->GetAuraDuration() < targetAura->GetAuraDuration()) + if(!targetAura || (*i)->GetParentAura()->GetAuraDuration() < targetAura->GetParentAura()->GetAuraDuration()) targetAura = *i; } } @@ -2421,29 +2534,43 @@ void Spell::SpellDamageHeal(uint32 /*i*/) return; } - int32 tickheal = targetAura->GetModifierValuePerStack(); + int32 tickheal = targetAura->GetAmount(); if(Unit* auraCaster = targetAura->GetCaster()) - tickheal = auraCaster->SpellHealingBonus(targetAura->GetSpellProto(), tickheal, DOT, unitTarget); + tickheal = auraCaster->SpellHealingBonus(unitTarget, targetAura->GetSpellProto(), tickheal, DOT); //int32 tickheal = targetAura->GetSpellProto()->EffectBasePoints[idx] + 1; //It is said that talent bonus should not be included - //int32 tickheal = targetAura->GetModifierValue(); + int32 tickcount = 0; if(targetAura->GetSpellProto()->SpellFamilyName == SPELLFAMILY_DRUID) { - switch(targetAura->GetSpellProto()->SpellFamilyFlags)//TODO: proper spellfamily for 3.0.x + switch(targetAura->GetSpellProto()->SpellFamilyFlags[0]) { case 0x10: tickcount = 4; break; // Rejuvenation case 0x40: tickcount = 6; break; // Regrowth } } addhealth += tickheal * tickcount; - unitTarget->RemoveAurasByCasterSpell(targetAura->GetId(), targetAura->GetCasterGUID()); + + // Glyph of Swiftmend + if(!caster->GetDummyAura(54824)) + unitTarget->RemoveAura(targetAura->GetId(), targetAura->GetCasterGUID()); //addhealth += tickheal * tickcount; //addhealth = caster->SpellHealingBonus(m_spellInfo, addhealth,HEAL, unitTarget); } + // Riptide - increase healing done by Chain Heal + else if (m_spellInfo->SpellFamilyName==SPELLFAMILY_SHAMAN && m_spellInfo->SpellFamilyFlags[0] & 0x100) + { + if (AuraEffect * aurEff = unitTarget->GetAura(SPELL_AURA_PERIODIC_HEAL, SPELLFAMILY_SHAMAN, 0, 0, 0x10, m_originalCasterGUID)) + { + addhealth = caster->SpellHealingBonus(unitTarget, m_spellInfo, addhealth, HEAL); + addhealth *= 1.25f; + // consume aura + unitTarget->RemoveAura(aurEff->GetParentAura()); + } + } else - addhealth = caster->SpellHealingBonus(m_spellInfo, addhealth,HEAL, unitTarget); + addhealth = caster->SpellHealingBonus(unitTarget, m_spellInfo, addhealth, HEAL); m_damage -= addhealth; } @@ -2461,6 +2588,9 @@ void Spell::EffectHealPct( uint32 /*i*/ ) return; uint32 addhealth = unitTarget->GetMaxHealth() * damage / 100; + if(Player* modOwner = m_caster->GetSpellModOwner()) + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DAMAGE, addhealth, this); + caster->SendHealSpellLog(unitTarget, m_spellInfo->Id, addhealth, false); int32 gain = unitTarget->ModifyHealth( int32(addhealth) ); @@ -2484,7 +2614,7 @@ void Spell::EffectHealMechanical( uint32 /*i*/ ) if (!caster) return; - uint32 addhealth = caster->SpellHealingBonus(m_spellInfo, uint32(damage), HEAL, unitTarget); + uint32 addhealth = caster->SpellHealingBonus(unitTarget, m_spellInfo, uint32(damage), HEAL); caster->SendHealSpellLog(unitTarget, m_spellInfo->Id, addhealth, false); unitTarget->ModifyHealth( int32(damage) ); } @@ -2515,7 +2645,7 @@ void Spell::EffectHealthLeech(uint32 i) if(m_caster->isAlive()) { - new_damage = m_caster->SpellHealingBonus(m_spellInfo, new_damage, HEAL, m_caster); + new_damage = m_caster->SpellHealingBonus(m_caster, m_spellInfo, new_damage, HEAL); m_caster->ModifyHealth(new_damage); @@ -2568,8 +2698,8 @@ void Spell::DoCreateItem(uint32 i, uint32 itemtype) if (num_to_add < 1) num_to_add = 1; - if (num_to_add > pProto->Stackable) - num_to_add = pProto->Stackable; + if (num_to_add > pProto->GetMaxStackSize()) + num_to_add = pProto->GetMaxStackSize(); // init items_count to 1, since 1 item will be created regardless of specialization int items_count=1; @@ -2627,7 +2757,7 @@ void Spell::DoCreateItem(uint32 i, uint32 itemtype) // we succeeded in creating at least one item, so a levelup is possible player->UpdateCraftSkill(m_spellInfo->Id); - } + } } void Spell::EffectCreateItem(uint32 i) @@ -2635,9 +2765,24 @@ void Spell::EffectCreateItem(uint32 i) DoCreateItem(i,m_spellInfo->EffectItemType[i]); } +void Spell::EffectCreateItem2(uint32 i) +{ + // special case: generate using spell_loot_template + if(!m_spellInfo->EffectItemType[i]) + { + if(m_caster->GetTypeId()!=TYPEID_PLAYER) + return; + + // create some random items + ((Player*)m_caster)->AutoStoreLoot(m_spellInfo->Id,LootTemplates_Spell); + return; + } + DoCreateItem(i,m_spellInfo->EffectItemType[i]); +} + void Spell::EffectPersistentAA(uint32 i) { - float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + float radius = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); if(Player* modOwner = m_originalCaster->GetSpellModOwner()) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius); @@ -2666,6 +2811,8 @@ void Spell::EffectEnergize(uint32 i) if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS) return; + Powers power = Powers(m_spellInfo->EffectMiscValue[i]); + // Some level depends spells int multiplier = 0; int level_diff = 0; @@ -2693,11 +2840,18 @@ void Spell::EffectEnergize(uint32 i) if (level_diff > 0) damage -= multiplier * level_diff; + //Judgement of wisdom energize effect + if(m_spellInfo->Id == 20268) + { + if(unitTarget->GetTypeId() == TYPEID_PLAYER) + { + damage = unitTarget->GetCreateMana() * damage / 100; + } + } + if(damage < 0) return; - Powers power = Powers(m_spellInfo->EffectMiscValue[i]); - if(unitTarget->GetMaxPower(power) == 0) return; @@ -2765,7 +2919,7 @@ void Spell::EffectEnergisePct(uint32 i) uint32 gain = damage * maxPower / 100; unitTarget->ModifyPower(power, gain); - m_caster->SendEnergizeSpellLog(unitTarget, m_spellInfo->Id, damage, power); + m_caster->SendEnergizeSpellLog(unitTarget, m_spellInfo->Id, gain, power); } void Spell::SendLoot(uint64 guid, LootType loottype) @@ -2853,7 +3007,7 @@ void Spell::SendLoot(uint64 guid, LootType loottype) player->SendLoot(guid, loottype); } -void Spell::EffectOpenLock(uint32 /*i*/) +void Spell::EffectOpenLock(uint32 effIndex) { if(!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER) { @@ -2863,7 +3017,6 @@ void Spell::EffectOpenLock(uint32 /*i*/) Player* player = (Player*)m_caster; - LootType loottype = LOOT_CORPSE; uint32 lockId = 0; uint64 guid = 0; @@ -2875,7 +3028,7 @@ void Spell::EffectOpenLock(uint32 /*i*/) if( goInfo->type == GAMEOBJECT_TYPE_BUTTON && goInfo->button.noDamageImmune || goInfo->type == GAMEOBJECT_TYPE_GOOBER && goInfo->goober.losOK ) { - //isAllowUseBattleGroundObject() already called in CanCast() + //CanUseBattleGroundObject() already called in CheckCast() // in battleground check if(BattleGround *bg = player->GetBattleGround()) { @@ -2887,7 +3040,7 @@ void Spell::EffectOpenLock(uint32 /*i*/) } else if (goInfo->type == GAMEOBJECT_TYPE_FLAGSTAND) { - //isAllowUseBattleGroundObject() already called in CanCast() + //CanUseBattleGroundObject() already called in CheckCast() // in battleground check if(BattleGround *bg = player->GetBattleGround()) { @@ -2914,93 +3067,39 @@ void Spell::EffectOpenLock(uint32 /*i*/) return; } - if(!lockId) // possible case for GO and maybe for items. - { - SendLoot(guid, loottype); - return; - } + SkillType skillId = SKILL_NONE; + int32 reqSkillValue = 0; + int32 skillValue; - // Get LockInfo - LockEntry const *lockInfo = sLockStore.LookupEntry(lockId); - - if (!lockInfo) + SpellCastResult res = CanOpenLock(effIndex,lockId,skillId,reqSkillValue,skillValue); + if(res != SPELL_CAST_OK) { - sLog.outError( "Spell::EffectOpenLock: %s [guid = %u] has an unknown lockId: %u!", - (gameObjTarget ? "gameobject" : "item"), GUID_LOPART(guid), lockId); - SendCastResult(SPELL_FAILED_BAD_TARGETS); + SendCastResult(res); return; } - // check key - for(int i = 0; i < 5; ++i) - { - // type==1 This means lockInfo->key[i] is an item - if(lockInfo->keytype[i]==LOCK_KEY_ITEM && lockInfo->key[i] && m_CastItem && m_CastItem->GetEntry()==lockInfo->key[i]) - { - SendLoot(guid, loottype); - return; - } - } - - uint32 SkillId = 0; - // Check and skill-up skill - if( m_spellInfo->Effect[1] == SPELL_EFFECT_SKILL ) - SkillId = m_spellInfo->EffectMiscValue[1]; - // pickpocketing spells - else if( m_spellInfo->EffectMiscValue[0] == LOCKTYPE_PICKLOCK ) - SkillId = SKILL_LOCKPICKING; + SendLoot(guid, LOOT_SKINNING); - // skill bonus provided by casting spell (mostly item spells) - uint32 spellSkillBonus = uint32(damage/*m_currentBasePoints[0]+1*/); - - uint32 reqSkillValue = lockInfo->requiredminingskill; - - if(lockInfo->requiredlockskill) // required pick lock skill applying + // not allow use skill grow at item base open + if(!m_CastItem && skillId != SKILL_NONE) { - if(SkillId != SKILL_LOCKPICKING) // wrong skill (cheating?) - { - SendCastResult(SPELL_FAILED_FIZZLE); - return; - } - - reqSkillValue = lockInfo->requiredlockskill; - } - else if(SkillId == SKILL_LOCKPICKING) // apply picklock skill to wrong target - { - SendCastResult(SPELL_FAILED_BAD_TARGETS); - return; - } - - if ( SkillId ) - { - loottype = LOOT_SKINNING; - if ( player->GetSkillValue(SkillId) + spellSkillBonus < reqSkillValue ) - { - SendCastResult(SPELL_FAILED_LOW_CASTLEVEL); - return; - } - // update skill if really known - uint32 SkillValue = player->GetPureSkillValue(SkillId); - if(SkillValue) // non only item base skill + if(uint32 pureSkillValue = player->GetPureSkillValue(skillId)) { if(gameObjTarget) { // Allow one skill-up until respawned if ( !gameObjTarget->IsInSkillupList( player->GetGUIDLow() ) && - player->UpdateGatherSkill(SkillId, SkillValue, reqSkillValue) ) + player->UpdateGatherSkill(skillId, pureSkillValue, reqSkillValue) ) gameObjTarget->AddToSkillupList( player->GetGUIDLow() ); } else if(itemTarget) { // Do one skill-up - uint32 SkillValue = player->GetPureSkillValue(SkillId); - player->UpdateGatherSkill(SkillId, SkillValue, reqSkillValue); + player->UpdateGatherSkill(skillId, pureSkillValue, reqSkillValue); } } } - - SendLoot(guid, loottype); } void Spell::EffectSummonChangeItem(uint32 i) @@ -3126,100 +3225,63 @@ void Spell::EffectProficiency(uint32 /*i*/) void Spell::EffectApplyAreaAura(uint32 i) { - if(!unitTarget) + return; +/* if(!unitTarget) return; if(!unitTarget->isAlive()) return; AreaAura* Aur = new AreaAura(m_spellInfo, i, &damage, unitTarget, m_caster, m_CastItem); - unitTarget->AddAura(Aur); + unitTarget->AddAura(Aur);*/ } void Spell::EffectSummonType(uint32 i) { - switch(m_spellInfo->EffectMiscValueB[i]) - { - case SUMMON_TYPE_GUARDIAN: - EffectSummonGuardian(i); - break; - case SUMMON_TYPE_POSESSED: - case SUMMON_TYPE_POSESSED2: - case SUMMON_TYPE_POSESSED3: - EffectSummonPossessed(i); - break; - case SUMMON_TYPE_WILD: - EffectSummonWild(i); - break; - case SUMMON_TYPE_DEMON: - EffectSummonDemon(i); - break; - case SUMMON_TYPE_SUMMON: - EffectSummon(i); - break; - case SUMMON_TYPE_CRITTER: - case SUMMON_TYPE_CRITTER2: - case SUMMON_TYPE_CRITTER3: - EffectSummonCritter(i); - break; - case SUMMON_TYPE_TOTEM_SLOT1: - case SUMMON_TYPE_TOTEM_SLOT2: - case SUMMON_TYPE_TOTEM_SLOT3: - case SUMMON_TYPE_TOTEM_SLOT4: - case SUMMON_TYPE_TOTEM: - EffectSummonTotem(i); - break; - case SUMMON_TYPE_UNKNOWN1: - case SUMMON_TYPE_UNKNOWN3: - case SUMMON_TYPE_UNKNOWN4: - case SUMMON_TYPE_UNKNOWN5: - break; - default: - sLog.outError("EffectSummonType: Unhandled summon type %u", m_spellInfo->EffectMiscValueB[i]); - break; - } -} - -void Spell::EffectSummon(uint32 i) -{ - uint32 pet_entry = m_spellInfo->EffectMiscValue[i]; - if(!pet_entry) + uint32 entry = m_spellInfo->EffectMiscValue[i]; + if(!entry) return; - if(!m_originalCaster || m_originalCaster->GetTypeId() != TYPEID_PLAYER) + SummonPropertiesEntry const *properties = sSummonPropertiesStore.LookupEntry(m_spellInfo->EffectMiscValueB[i]); + if(!properties) { - EffectSummonWild(i); + sLog.outError("EffectSummonType: Unhandled summon type %u", m_spellInfo->EffectMiscValueB[i]); return; } - Player *owner = (Player*)m_originalCaster; - - if(owner->GetPetGUID()) - return; - - // Summon in dest location - float x,y,z; - if(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) + switch(properties->Category) { - x = m_targets.m_destX; - y = m_targets.m_destY; - z = m_targets.m_destZ; + default: + switch(properties->Type) + { + case SUMMON_TYPE_PET: + case SUMMON_TYPE_GUARDIAN: + case SUMMON_TYPE_MINION: + SummonGuardian(entry, properties); + break; + case SUMMON_TYPE_VEHICLE: + SummonVehicle(entry, properties); + break; + case SUMMON_TYPE_TOTEM: + SummonTotem(entry, properties); + break; + case SUMMON_TYPE_MINIPET: + EffectSummonCritter(i); + break; + default: + EffectSummonWild(i); + break; + } + break; + case SUMMON_CATEGORY_PET: + SummonGuardian(entry, properties); + break; + case SUMMON_CATEGORY_POSSESSED: + SummonPossessed(entry, properties); + break; + case SUMMON_CATEGORY_VEHICLE: + SummonVehicle(entry, properties); + break; } - else - m_caster->GetClosePoint(x,y,z,owner->GetObjectSize()); - - Pet *spawnCreature = owner->SummonPet(pet_entry, x, y, z, m_caster->GetOrientation(), SUMMON_PET, GetSpellDuration(m_spellInfo)); - if(!spawnCreature) - return; - - spawnCreature->SetUInt32Value(UNIT_NPC_FLAGS, 0); - spawnCreature->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, 0); - spawnCreature->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); - - std::string name = owner->GetName(); - name.append(petTypeSuffix[spawnCreature->getPetType()]); - spawnCreature->SetName( name ); - - spawnCreature->SetReactState( REACT_DEFENSIVE ); } void Spell::EffectLearnSpell(uint32 i) @@ -3237,8 +3299,8 @@ void Spell::EffectLearnSpell(uint32 i) Player *player = (Player*)unitTarget; - uint32 spellToLearn = (m_spellInfo->Id==SPELL_ID_GENERIC_LEARN) ? damage : m_spellInfo->EffectTriggerSpell[i]; - player->learnSpell(spellToLearn); + 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() ); } @@ -3248,8 +3310,7 @@ void Spell::EffectDispel(uint32 i) if(!unitTarget) return; - // Fill possible dispel list - std::vector <Aura *> dispel_list; + std::list < Aura * > dispel_list; // Create dispel mask by dispel type uint32 dispel_type = m_spellInfo->EffectMiscValue[i]; @@ -3258,7 +3319,7 @@ void Spell::EffectDispel(uint32 i) for(Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) { Aura *aur = (*itr).second; - if (aur && (1<<aur->GetSpellProto()->Dispel) & dispelMask) + if (aur && ((1<<aur->GetSpellProto()->Dispel) & dispelMask)) { if(aur->GetSpellProto()->Dispel == DISPEL_MAGIC) { @@ -3273,80 +3334,67 @@ void Spell::EffectDispel(uint32 i) if(positive == unitTarget->IsFriendlyTo(m_caster)) continue; } - // Add every aura stack to dispel list - for(uint32 stack_amount = 0; stack_amount < aur->GetStackAmount(); ++stack_amount) + for (uint8 i = aur->GetStackAmount();i!=0;--i) dispel_list.push_back(aur); } } // Ok if exist some buffs for dispel try dispel it - if (!dispel_list.empty()) + if (uint32 list_size = dispel_list.size()) { - std::list < std::pair<uint32,uint64> > success_list;// (spell_id,casterGuid) - std::list < uint32 > fail_list; // spell_id - int32 list_size = dispel_list.size(); + uint32 failCount = 0; + std::list < Aura * > success_list; + WorldPacket dataFail(SMSG_DISPEL_FAILED, 8+8+4+4+damage*4); // dispel N = damage buffs (or while exist buffs for dispel) - for (int32 count=0; count < damage && list_size > 0; ++count) + for (int32 count=0; count < damage && list_size > 0; ++count, list_size = dispel_list.size()) { // Random select buff for dispel - Aura *aur = dispel_list[urand(0, list_size-1)]; - - SpellEntry const* spellInfo = aur->GetSpellProto(); - // Base dispel chance - // TODO: possible chance depend from spell level?? - int32 miss_chance = 0; - // Apply dispel mod from aura caster - if (Unit *caster = aur->GetCaster()) + std::list < Aura * > ::iterator itr = dispel_list.begin(); + for (uint32 i=urand(0, list_size-1);i>0;--i) + itr++; + + if (GetDispelChance((*itr)->GetCaster(), (*itr)->GetId())) { - if ( Player* modOwner = caster->GetSpellModOwner() ) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_RESIST_DISPEL_CHANCE, miss_chance, this); + success_list.push_back(*itr); + dispel_list.erase(itr); } - // Try dispel - if (roll_chance_i(miss_chance)) - fail_list.push_back(aur->GetId()); else - success_list.push_back(std::pair<uint32,uint64>(aur->GetId(),aur->GetCasterGUID())); - // Remove buff from list for prevent doubles - for (std::vector<Aura *>::iterator j = dispel_list.begin(); j != dispel_list.end(); ) { - Aura *dispelled = *j; - if (dispelled->GetId() == aur->GetId() && dispelled->GetCasterGUID() == aur->GetCasterGUID()) + if (!failCount) { - j = dispel_list.erase(j); - --list_size; - break; + // Failed to dispell + dataFail << uint64(m_caster->GetGUID()); // Caster GUID + dataFail << uint64(unitTarget->GetGUID()); // Victim GUID + dataFail << uint32(m_spellInfo->Id); // dispel spell id } - else - ++j; + failCount++; + dataFail << uint32((*itr)->GetId()); // Spell Id } } - // Send success log and really remove auras - if (!success_list.empty()) + if (failCount) + { + m_caster->SendMessageToSet(&dataFail, true); + } + if (success_list.size()) { - int32 count = success_list.size(); - WorldPacket data(SMSG_SPELLDISPELLOG, 8+8+4+1+4+count*5); - data.append(unitTarget->GetPackGUID()); // Victim GUID - data.append(m_caster->GetPackGUID()); // Caster GUID - data << uint32(m_spellInfo->Id); // dispel spell id - data << uint8(0); // not used - data << uint32(count); // count - for (std::list<std::pair<uint32,uint64> >::iterator j = success_list.begin(); j != success_list.end(); ++j) + WorldPacket dataSuccess(SMSG_SPELLDISPELLOG, 8+8+4+1+4+damage*5); + // Send packet header + dataSuccess.append(unitTarget->GetPackGUID()); // Victim GUID + dataSuccess.append(m_caster->GetPackGUID()); // Caster GUID + dataSuccess << uint32(m_spellInfo->Id); // dispel spell id + dataSuccess << uint8(0); // not used + dataSuccess << uint32(success_list.size()); // count + for (std::list < Aura * > ::iterator itr = success_list.begin();itr!=success_list.end();++itr) { - SpellEntry const* spellInfo = sSpellStore.LookupEntry(j->first); - data << uint32(spellInfo->Id); // Spell Id - data << uint8(0); // 0 - dispelled !=0 cleansed - if(spellInfo->StackAmount!= 0) - { - //Why are Aura's Removed by EffIndex? Auras should be removed as a whole..... - unitTarget->RemoveSingleAuraFromStackByDispel(spellInfo->Id); - } - else - unitTarget->RemoveAurasDueToSpellByDispel(spellInfo->Id, j->second, m_caster); - } - m_caster->SendMessageToSet(&data, true); + // Send dispelled spell info + dataSuccess << uint32((*itr)->GetId()); // Spell Id + dataSuccess << uint8(0); // 0 - dispelled !=0 cleansed + unitTarget->RemoveAurasDueToSpellByDispel((*itr)->GetId(), (*itr)->GetCasterGUID(), m_caster); + } + m_caster->SendMessageToSet(&dataSuccess, true); // On succes dispel // Devour Magic - if (m_spellInfo->SpellFamilyName == SPELLFAMILY_WARLOCK && m_spellInfo->Category == 12) + if (m_spellInfo->SpellFamilyName == SPELLFAMILY_WARLOCK && m_spellInfo->Category == SPELLCATEGORY_DEVOUR_MAGIC) { uint32 heal_spell = 0; switch (m_spellInfo->Id) @@ -3357,6 +3405,7 @@ void Spell::EffectDispel(uint32 i) case 19736: heal_spell = 19735; break; case 27276: heal_spell = 27278; break; case 27277: heal_spell = 27279; break; + case 48011: heal_spell = 48010; break; default: sLog.outDebug("Spell for Devour Magic %d not handled in Spell::EffectDispel", m_spellInfo->Id); break; @@ -3365,18 +3414,6 @@ void Spell::EffectDispel(uint32 i) m_caster->CastSpell(m_caster, heal_spell, true); } } - // Send fail log to client - if (!fail_list.empty()) - { - // Failed to dispell - WorldPacket data(SMSG_DISPEL_FAILED, 8+8+4+4*fail_list.size()); - data << uint64(m_caster->GetGUID()); // Caster GUID - data << uint64(unitTarget->GetGUID()); // Victim GUID - data << uint32(m_spellInfo->Id); // dispel spell id - for (std::list< uint32 >::iterator j = fail_list.begin(); j != fail_list.end(); ++j) - data << uint32(*j); // Spell Id - m_caster->SendMessageToSet(&data, true); - } } } @@ -3418,7 +3455,7 @@ void Spell::EffectDistract(uint32 /*i*/) // Set creature Distracted, Stop it, And turn it unitTarget->SetOrientation(angle); unitTarget->StopMoving(); - unitTarget->GetMotionMaster()->MoveDistract(damage*1000); + unitTarget->GetMotionMaster()->MoveDistract(damage*IN_MILISECONDS); } } @@ -3446,8 +3483,7 @@ void Spell::EffectPickPocket(uint32 /*i*/) { // Reveal action + get attack m_caster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TALK); - if (((Creature*)unitTarget)->IsAIEnabled) - ((Creature*)unitTarget)->AI()->AttackStart(m_caster); + m_caster->CombatStart(unitTarget); } } } @@ -3457,7 +3493,7 @@ void Spell::EffectAddFarsight(uint32 i) if (m_caster->GetTypeId() != TYPEID_PLAYER) return; - float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + float radius = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); int32 duration = GetSpellDuration(m_spellInfo); DynamicObject* dynObj = new DynamicObject; if(!dynObj->Create(objmgr.GenerateLowGuid(HIGHGUID_DYNAMICOBJECT), m_caster, m_spellInfo->Id, 4, m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, duration, radius)) @@ -3473,8 +3509,8 @@ void Spell::EffectAddFarsight(uint32 i) dynObj->GetMap()->Add(dynObj); //grid will also be loaded // Need to update visibility of object for client to accept farsight guid + ((Player*)m_caster)->SetViewpoint(dynObj, true); ((Player*)m_caster)->UpdateVisibilityOf(dynObj); - ((Player*)m_caster)->SetFarsightTarget(dynObj); } void Spell::EffectSummonWild(uint32 i) @@ -3499,35 +3535,14 @@ void Spell::EffectSummonWild(uint32 i) } } - // select center of summon position - float center_x = m_targets.m_destX; - float center_y = m_targets.m_destY; - float center_z = m_targets.m_destZ; - - float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + float radius = GetSpellRadiusForHostile(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); int32 amount = damage > 0 ? damage : 1; for(int32 count = 0; count < amount; ++count) { float px, py, pz; - // If dest location if present - if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) - { - // Summon 1 unit in dest location - if (count == 0) - { - px = m_targets.m_destX; - py = m_targets.m_destY; - pz = m_targets.m_destZ; - } - // Summon in random point all other units if location present - else - m_caster->GetRandomPoint(center_x,center_y,center_z,radius,px,py,pz); - } - // Summon if dest location not present near caster - else - m_caster->GetClosePoint(px,py,pz,3.0f); + GetSummonPosition(px, py, pz, radius, count); int32 duration = GetSpellDuration(m_spellInfo); @@ -3540,124 +3555,6 @@ void Spell::EffectSummonWild(uint32 i) } } -void Spell::EffectSummonGuardian(uint32 i) -{ - uint32 pet_entry = m_spellInfo->EffectMiscValue[i]; - if(!pet_entry) - return; - - // Jewelery statue case (totem like) - if(m_spellInfo->SpellIconID==2056) - { - EffectSummonTotem(i); - return; - } - - Player *caster = NULL; - if(m_originalCaster) - { - if(m_originalCaster->GetTypeId() == TYPEID_PLAYER) - caster = (Player*)m_originalCaster; - else if(((Creature*)m_originalCaster)->isTotem()) - caster = m_originalCaster->GetCharmerOrOwnerPlayerOrPlayerItself(); - } - - if(!caster) - { - EffectSummonWild(i); - return; - } - - // set timer for unsummon - int32 duration = GetSpellDuration(m_spellInfo); - - // Search old Guardian only for players (if casted spell not have duration or cooldown) - // FIXME: some guardians have control spell applied and controlled by player and anyway player can't summon in this time - // so this code hack in fact - if(duration <= 0 || GetSpellRecoveryTime(m_spellInfo)==0) - if(caster->HasGuardianWithEntry(pet_entry)) - return; // find old guardian, ignore summon - - // in another case summon new - uint32 level = caster->getLevel(); - - // level of pet summoned using engineering item based at engineering skill level - if(m_CastItem) - { - ItemPrototype const *proto = m_CastItem->GetProto(); - if(proto && proto->RequiredSkill == SKILL_ENGINERING) - { - uint16 skill202 = caster->GetSkillValue(SKILL_ENGINERING); - if(skill202) - { - level = skill202/5; - } - } - } - - // select center of summon position - float center_x = m_targets.m_destX; - float center_y = m_targets.m_destY; - float center_z = m_targets.m_destZ; - - float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); - - int32 amount = damage > 0 ? damage : 1; - - for(int32 count = 0; count < amount; ++count) - { - float px, py, pz; - // If dest location if present - if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) - { - // Summon 1 unit in dest location - if (count == 0) - { - px = m_targets.m_destX; - py = m_targets.m_destY; - pz = m_targets.m_destZ; - } - // Summon in random point all other units if location present - else - m_caster->GetRandomPoint(center_x,center_y,center_z,radius,px,py,pz); - } - // Summon if dest location not present near caster - else - m_caster->GetClosePoint(px,py,pz,m_caster->GetObjectSize()); - - Pet *spawnCreature = caster->SummonPet(m_spellInfo->EffectMiscValue[i], px, py, pz, m_caster->GetOrientation(), GUARDIAN_PET, duration); - if(!spawnCreature) - return; - - spawnCreature->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP,0); - spawnCreature->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); - } -} - -void Spell::EffectSummonPossessed(uint32 i) -{ - uint32 entry = m_spellInfo->EffectMiscValue[i]; - if(!entry) - return; - - if(m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - uint32 level = m_caster->getLevel(); - - float x, y, z; - m_caster->GetClosePoint(x, y, z, DEFAULT_WORLD_OBJECT_SIZE); - - int32 duration = GetSpellDuration(m_spellInfo); - - Pet* pet = ((Player*)m_caster)->SummonPet(entry, x, y, z, m_caster->GetOrientation(), POSSESSED_PET, duration); - if(!pet) - return; - - pet->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); - pet->SetCharmedOrPossessedBy(m_caster, true); -} - void Spell::EffectTeleUnitsFaceCaster(uint32 i) { if(!unitTarget) @@ -3666,16 +3563,12 @@ void Spell::EffectTeleUnitsFaceCaster(uint32 i) if(unitTarget->isInFlight()) return; - uint32 mapid = m_caster->GetMapId(); - float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + float dis = m_caster->GetSpellRadiusForTarget(unitTarget, sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); float fx,fy,fz; m_caster->GetClosePoint(fx,fy,fz,unitTarget->GetObjectSize(),dis); - if(unitTarget->GetTypeId() == TYPEID_PLAYER) - ((Player*)unitTarget)->TeleportTo(mapid, fx, fy, fz, -m_caster->GetOrientation(), TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget==m_caster ? TELE_TO_SPELL : 0)); - else - m_caster->GetMap()->CreatureRelocation((Creature*)m_caster, fx, fy, fz, -m_caster->GetOrientation()); + unitTarget->NearTeleportTo(fx,fy,fz,-m_caster->GetOrientation(),unitTarget==m_caster); } void Spell::EffectLearnSkill(uint32 i) @@ -3696,13 +3589,27 @@ void Spell::EffectAddHonor(uint32 /*i*/) if(unitTarget->GetTypeId() != TYPEID_PLAYER) return; - sLog.outDebug("SpellEffect::AddHonor called for spell_id %u , that rewards %d honor points to player: %u", m_spellInfo->Id, damage, ((Player*)unitTarget)->GetGUIDLow()); - - // TODO: find formula for honor reward based on player's level! + // not scale value for item based reward (/10 value expected) + if(m_CastItem) + { + ((Player*)unitTarget)->RewardHonor(NULL, 1, damage/10); + sLog.outError("SpellEffect::AddHonor (spell_id %u) rewards %d honor points (item %u) for player: %u", m_spellInfo->Id, damage/10, m_CastItem->GetEntry(),((Player*)unitTarget)->GetGUIDLow()); + return; + } - // now fixed only for level 70 players: - if (((Player*)unitTarget)->getLevel() == 70) + // do not allow to add too many honor for player (50 * 21) = 1040 at level 70, or (50 * 31) = 1550 at level 80 + if( damage <= 50) + { + uint32 honor_reward = MaNGOS::Honor::hk_honor_at_level(unitTarget->getLevel(), damage); + ((Player*)unitTarget)->RewardHonor(NULL, 1, honor_reward); + sLog.outDebug("SpellEffect::AddHonor (spell_id %u) rewards %u honor points (scale) to player: %u", m_spellInfo->Id, honor_reward, ((Player*)unitTarget)->GetGUIDLow()); + } + else + { + //maybe we have correct honor_gain in damage already ((Player*)unitTarget)->RewardHonor(NULL, 1, damage); + sLog.outError("SpellEffect::AddHonor (spell_id %u) rewards %u honor points (non scale) for player: %u", m_spellInfo->Id, damage, ((Player*)unitTarget)->GetGUIDLow()); + } } void Spell::EffectTradeSkill(uint32 /*i*/) @@ -3714,7 +3621,7 @@ void Spell::EffectTradeSkill(uint32 /*i*/) // ((Player*)unitTarget)->SetSkill(skillid,skillval?skillval:1,skillmax+75); } -void Spell::EffectEnchantItemPerm(uint32 i) +void Spell::EffectEnchantItemPerm(uint32 effect_idx) { if(m_caster->GetTypeId() != TYPEID_PLAYER) return; @@ -3723,37 +3630,95 @@ void Spell::EffectEnchantItemPerm(uint32 i) Player* p_caster = (Player*)m_caster; + // not grow at item use at item case p_caster->UpdateCraftSkill(m_spellInfo->Id); - if (m_spellInfo->EffectMiscValue[i]) + uint32 enchant_id = m_spellInfo->EffectMiscValue[effect_idx]; + if (!enchant_id) + return; + + SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); + if(!pEnchant) + return; + + // item can be in trade slot and have owner diff. from caster + Player* item_owner = itemTarget->GetOwner(); + if(!item_owner) + return; + + if(item_owner!=p_caster && p_caster->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) ) { - uint32 enchant_id = m_spellInfo->EffectMiscValue[i]; + sLog.outCommand(p_caster->GetSession()->GetAccountId(),"GM %s (Account: %u) enchanting(perm): %s (Entry: %d) for player: %s (Account: %u)", + p_caster->GetName(),p_caster->GetSession()->GetAccountId(), + itemTarget->GetProto()->Name1,itemTarget->GetEntry(), + item_owner->GetName(),item_owner->GetSession()->GetAccountId()); + } - SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); - if(!pEnchant) - return; + // remove old enchanting before applying new if equipped + item_owner->ApplyEnchantment(itemTarget,PERM_ENCHANTMENT_SLOT,false); - // item can be in trade slot and have owner diff. from caster - Player* item_owner = itemTarget->GetOwner(); - if(!item_owner) - return; + itemTarget->SetEnchantment(PERM_ENCHANTMENT_SLOT, enchant_id, 0, 0); + + // add new enchanting if equipped + item_owner->ApplyEnchantment(itemTarget,PERM_ENCHANTMENT_SLOT,true); +} + +void Spell::EffectEnchantItemPrismatic(uint32 effect_idx) +{ + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + if (!itemTarget) + return; - if(item_owner!=p_caster && p_caster->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) ) + Player* p_caster = (Player*)m_caster; + + uint32 enchant_id = m_spellInfo->EffectMiscValue[effect_idx]; + if (!enchant_id) + return; + + SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); + if(!pEnchant) + return; + + // support only enchantings with add socket in this slot + { + bool add_socket = false; + for(int i = 0; i < 3; ++i) { - sLog.outCommand(p_caster->GetSession()->GetAccountId(),"GM %s (Account: %u) enchanting(perm): %s (Entry: %d) for player: %s (Account: %u)", - p_caster->GetName(),p_caster->GetSession()->GetAccountId(), - itemTarget->GetProto()->Name1,itemTarget->GetEntry(), - item_owner->GetName(),item_owner->GetSession()->GetAccountId()); + if(pEnchant->type[i]==ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET) + { + add_socket = true; + break; + } } + if(!add_socket) + { + sLog.outError("Spell::EffectEnchantItemPrismatic: attempt apply enchant spell %u with SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC (%u) but without ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET (u), not suppoted yet.", + m_spellInfo->Id,SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC,ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET); + return; + } + } - // remove old enchanting before applying new if equipped - item_owner->ApplyEnchantment(itemTarget,PERM_ENCHANTMENT_SLOT,false); - - itemTarget->SetEnchantment(PERM_ENCHANTMENT_SLOT, enchant_id, 0, 0); + // item can be in trade slot and have owner diff. from caster + Player* item_owner = itemTarget->GetOwner(); + if(!item_owner) + return; - // add new enchanting if equipped - item_owner->ApplyEnchantment(itemTarget,PERM_ENCHANTMENT_SLOT,true); + if(item_owner!=p_caster && p_caster->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) ) + { + sLog.outCommand(p_caster->GetSession()->GetAccountId(),"GM %s (Account: %u) enchanting(perm): %s (Entry: %d) for player: %s (Account: %u)", + p_caster->GetName(),p_caster->GetSession()->GetAccountId(), + itemTarget->GetProto()->Name1,itemTarget->GetEntry(), + item_owner->GetName(),item_owner->GetSession()->GetAccountId()); } + + // remove old enchanting before applying new if equipped + item_owner->ApplyEnchantment(itemTarget,PRISMATIC_ENCHANTMENT_SLOT,false); + + itemTarget->SetEnchantment(PRISMATIC_ENCHANTMENT_SLOT, enchant_id, 0, 0); + + // add new enchanting if equipped + item_owner->ApplyEnchantment(itemTarget,PRISMATIC_ENCHANTMENT_SLOT,true); } void Spell::EffectEnchantItemTmp(uint32 i) @@ -3848,13 +3813,13 @@ void Spell::EffectEnchantItemTmp(uint32 i) else if(m_spellInfo->SpellFamilyName==SPELLFAMILY_SHAMAN) duration = 1800; // 30 mins // other cases with this SpellVisual already selected - else if(m_spellInfo->SpellVisual==215) + else if(m_spellInfo->SpellVisual[0]==215) duration = 1800; // 30 mins // some fishing pole bonuses - else if(m_spellInfo->SpellVisual==563) + else if(m_spellInfo->SpellVisual[0]==563) duration = 600; // 10 mins // shaman rockbiter enchantments - else if(m_spellInfo->SpellVisual==0) + else if(m_spellInfo->SpellVisual[0]==0) duration = 1800; // 30 mins else if(m_spellInfo->Id==29702) duration = 300; // 5 mins @@ -3917,17 +3882,19 @@ void Spell::EffectTameCreature(uint32 /*i*/) creatureTarget->RemoveCorpse(); creatureTarget->SetHealth(0); // just for nice GM-mode view + uint32 level = (creatureTarget->getLevel() < (m_caster->getLevel() - 5)) ? (m_caster->getLevel() - 5) : creatureTarget->getLevel(); + // prepare visual effect for levelup - pet->SetUInt32Value(UNIT_FIELD_LEVEL,creatureTarget->getLevel()-1); + pet->SetUInt32Value(UNIT_FIELD_LEVEL, level - 1); // add to world pet->GetMap()->Add((Creature*)pet); // visual effect for levelup - pet->SetUInt32Value(UNIT_FIELD_LEVEL,creatureTarget->getLevel()); + pet->SetUInt32Value(UNIT_FIELD_LEVEL, level); // caster have pet now - m_caster->SetPet(pet); + m_caster->SetGuardian(pet, true); if(m_caster->GetTypeId() == TYPEID_PLAYER) { @@ -3993,7 +3960,7 @@ void Spell::EffectSummonPet(uint32 i) Pet* pet = owner->SummonPet(petentry, x, y, z, owner->GetOrientation(), SUMMON_PET, 0); if(!pet) return; - + if(m_caster->GetTypeId() == TYPEID_UNIT) { if ( ((Creature*)m_caster)->isTotem() ) @@ -4005,9 +3972,12 @@ void Spell::EffectSummonPet(uint32 i) pet->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); // this enables popup window (pet dismiss, cancel), hunter pet additional flags set later - pet->SetUInt32Value(UNIT_FIELD_FLAGS,UNIT_FLAG_PVP_ATTACKABLE); + if(m_caster->GetTypeId() == TYPEID_PLAYER) + pet->SetUInt32Value(UNIT_FIELD_FLAGS,UNIT_FLAG_PVP_ATTACKABLE); + pet->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL)); + pet->InitTalentForLevel(); // generate new name for summon pet std::string new_name=objmgr.GeneratePetName(petentry); if(!new_name.empty()) @@ -4031,7 +4001,6 @@ void Spell::EffectLearnPetSpell(uint32 i) if(!learn_spellproto) return; - pet->SetTP(pet->m_TrainingPoints - pet->GetTPForSpell(learn_spellproto->Id)); pet->learnSpell(learn_spellproto->Id); pet->SavePetToDB(PET_SAVE_AS_CURRENT); @@ -4085,7 +4054,6 @@ void Spell::SpellDamageWeaponDmg(uint32 i) } // some spell specific modifiers - //float weaponDamagePercentMod = 1.0f; // applied to weapon damage (and to fixed effect damage bonus if customBonusDamagePercentMod not set float totalDamagePercentMod = 1.0f; // applied to final bonus+weapon damage int32 fixed_bonus = 0; int32 spell_bonus = 0; // bonus specific for spell @@ -4095,23 +4063,18 @@ void Spell::SpellDamageWeaponDmg(uint32 i) case SPELLFAMILY_WARRIOR: { // Devastate bonus and sunder armor refresh - if(m_spellInfo->SpellVisual == 671 && m_spellInfo->SpellIconID == 1508) + if(m_spellInfo->SpellFamilyFlags[1] & 0x40) { + if (m_caster->GetTypeId()!=TYPEID_PLAYER) + return; + SpellEntry const *spellInfo = NULL; uint32 stack = 0; - Unit::AuraList const& list = unitTarget->GetAurasByType(SPELL_AURA_MOD_RESISTANCE); - for(Unit::AuraList::const_iterator itr=list.begin();itr!=list.end();++itr) + if (AuraEffect * aur = unitTarget->GetAura(SPELL_AURA_MOD_RESISTANCE,SPELLFAMILY_WARRIOR,SPELLFAMILYFLAG_WARRIOR_SUNDERARMOR, 0, 0, m_caster->GetGUID())) { - SpellEntry const *proto = (*itr)->GetSpellProto(); - if(proto->SpellFamilyName == SPELLFAMILY_WARRIOR - && proto->SpellFamilyFlags == SPELLFAMILYFLAG_WARRIOR_SUNDERARMOR) - { - int32 duration = GetSpellDuration(proto); - (*itr)->SetAuraDuration(duration); - (*itr)->UpdateAuraDuration(); - stack = (*itr)->GetStackAmount(); - break; - } + aur->GetParentAura()->RefreshAura(); + spellInfo = aur->GetSpellProto(); + stack = aur->GetParentAura()->GetStackAmount(); } for(int j = 0; j < 3; j++) @@ -4123,7 +4086,7 @@ void Spell::SpellDamageWeaponDmg(uint32 i) } } - if(stack < 5) + if(!spellInfo) { // get highest rank of the Sunder Armor spell const PlayerSpellMap& sp_list = ((Player*)m_caster)->GetSpellMap(); @@ -4133,66 +4096,97 @@ void Spell::SpellDamageWeaponDmg(uint32 i) if(!itr->second->active || itr->second->disabled || itr->second->state == PLAYERSPELL_REMOVED) continue; - SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first); - if (!spellInfo) + SpellEntry const *spellProto = sSpellStore.LookupEntry(itr->first); + if (!spellProto) continue; - if (spellInfo->SpellFamilyFlags == SPELLFAMILYFLAG_WARRIOR_SUNDERARMOR - && spellInfo->Id != m_spellInfo->Id - && spellInfo->SpellFamilyName == SPELLFAMILY_WARRIOR) + if (spellProto->SpellFamilyFlags[0] & SPELLFAMILYFLAG_WARRIOR_SUNDERARMOR + && spellProto->Id != m_spellInfo->Id + && spellProto->SpellFamilyName == SPELLFAMILY_WARRIOR) { - m_caster->CastSpell(unitTarget, spellInfo, true); + spellInfo = spellProto; break; } } } + if (!spellInfo) + break; + m_caster->CastSpell(unitTarget, spellInfo, true); + if (stack) + spell_bonus += stack * CalculateDamage(2, unitTarget); } break; } case SPELLFAMILY_ROGUE: { // Hemorrhage - if(m_spellInfo->SpellFamilyFlags & 0x2000000) + if(m_spellInfo->SpellFamilyFlags[0] & 0x2000000) { if(m_caster->GetTypeId()==TYPEID_PLAYER) ((Player*)m_caster)->AddComboPoints(unitTarget, 1); } + // Fan of Knives + else if(m_spellInfo->SpellFamilyFlags[1] & 0x40000) + { + // 50% more damage with daggers + if (Item* item = ((Player*)m_caster)->GetWeaponForAttack(m_attackType)) + if (item->GetProto()->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER) + totalDamagePercentMod *= 1.5f; + } // Mutilate (for each hand) - else if(m_spellInfo->SpellFamilyFlags & 0x600000000LL) + else if(m_spellInfo->SpellFamilyFlags[1] & 0x6) { - Unit::AuraMap const& auras = unitTarget->GetAuras(); - for(Unit::AuraMap::const_iterator itr = auras.begin(); itr!=auras.end(); ++itr) + bool found = false; + // fast check + if(unitTarget->HasAuraState(AURA_STATE_DEADLY_POISON, m_spellInfo, m_caster)) + found = true; + // full aura scan + else { - if(itr->second->GetSpellProto()->Dispel == DISPEL_POISON) + Unit::AuraMap const& auras = unitTarget->GetAuras(); + for(Unit::AuraMap::const_iterator itr = auras.begin(); itr!=auras.end(); ++itr) { - totalDamagePercentMod *= 1.5f; // 150% if poisoned - break; + if(itr->second->GetSpellProto()->Dispel == DISPEL_POISON) + { + found = true; + break; + } } } + + if(found) + totalDamagePercentMod *= 1.2f; // 120% if poisoned } break; } case SPELLFAMILY_PALADIN: { // Seal of Command - receive benefit from Spell Damage and Healing - if(m_spellInfo->SpellFamilyFlags & 0x00000002000000LL) + if(m_spellInfo->SpellFamilyFlags[0] & 0x2000000) { - spell_bonus += int32(0.20f*m_caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellInfo))); + spell_bonus += int32(0.23f*m_caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellInfo))); spell_bonus += int32(0.29f*m_caster->SpellBaseDamageBonusForVictim(GetSpellSchoolMask(m_spellInfo), unitTarget)); } + + // Seal of Command Unleashed + else if(m_spellInfo->Id==20467) + { + spell_bonus += int32(0.16f*m_caster->GetTotalAttackPowerValue(BASE_ATTACK)); + spell_bonus += int32(0.25f*m_caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellInfo))); + } break; } case SPELLFAMILY_SHAMAN: { // Skyshatter Harness item set bonus // Stormstrike - if(m_spellInfo->SpellFamilyFlags & 0x001000000000LL) + if(m_spellInfo->SpellFamilyFlags[1] & 0x0010) { - Unit::AuraList const& m_OverrideClassScript = m_caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); - for(Unit::AuraList::const_iterator i = m_OverrideClassScript.begin(); i != m_OverrideClassScript.end(); ++i) + Unit::AuraEffectList const& m_OverrideClassScript = m_caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); + for(Unit::AuraEffectList::const_iterator i = m_OverrideClassScript.begin(); i != m_OverrideClassScript.end(); ++i) { // Stormstrike AP Buff - if ( (*i)->GetModifier()->m_miscvalue == 5634 ) + if ( (*i)->GetMiscValue() == 5634 ) { m_caster->CastSpell(m_caster,38430,true,NULL,*i); break; @@ -4204,7 +4198,7 @@ void Spell::SpellDamageWeaponDmg(uint32 i) case SPELLFAMILY_DRUID: { // Mangle (Cat): CP - if(m_spellInfo->SpellFamilyFlags==0x0000040000000000LL) + if(m_spellInfo->SpellFamilyFlags.IsEqual(0,0x00000400)) { if(m_caster->GetTypeId()==TYPEID_PLAYER) ((Player*)m_caster)->AddComboPoints(unitTarget,1); @@ -4229,6 +4223,7 @@ void Spell::SpellDamageWeaponDmg(uint32 i) break; case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE: weaponDamagePercentMod *= float(CalculateDamage(j,unitTarget)) / 100.0f; + break; default: break; // not weapon damage effect, just skip } @@ -4289,33 +4284,6 @@ void Spell::SpellDamageWeaponDmg(uint32 i) // Add melee damage bonuses (also check for negative) m_caster->MeleeDamageBonus(unitTarget, &eff_damage, m_attackType, m_spellInfo); m_damage+= eff_damage; - - // take ammo - if(m_attackType == RANGED_ATTACK && m_caster->GetTypeId() == TYPEID_PLAYER) - { - Item *pItem = ((Player*)m_caster)->GetWeaponForAttack( RANGED_ATTACK ); - - // wands don't have ammo - if(!pItem || pItem->IsBroken() || pItem->GetProto()->SubClass==ITEM_SUBCLASS_WEAPON_WAND) - return; - - if( pItem->GetProto()->InventoryType == INVTYPE_THROWN ) - { - if(pItem->GetMaxStackCount()==1) - { - // decrease durability for non-stackable throw weapon - ((Player*)m_caster)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_RANGED); - } - else - { - // decrease items amount for stackable throw weapon - uint32 count = 1; - ((Player*)m_caster)->DestroyItemCount( pItem, count, true); - } - } - else if(uint32 ammo = ((Player*)m_caster)->GetUInt32Value(PLAYER_AMMO_ID)) - ((Player*)m_caster)->DestroyItemCount(ammo, 1, true); - } } void Spell::EffectThreat(uint32 /*i*/) @@ -4360,7 +4328,7 @@ void Spell::EffectInterruptCast(uint32 i) { if(m_originalCaster) { - int32 duration = m_originalCaster->CalculateSpellDuration(m_spellInfo, i, unitTarget); + int32 duration = m_originalCaster->ModSpellDuration(m_spellInfo, unitTarget, m_originalCaster->CalcSpellDuration(m_spellInfo), false); unitTarget->ProhibitSpellScholl(GetSpellSchoolMask(unitTarget->m_currentSpells[i]->m_spellInfo), duration/*GetSpellDuration(m_spellInfo)*/); } unitTarget->InterruptSpell(i,false); @@ -4392,14 +4360,14 @@ void Spell::EffectSummonObjectWild(uint32 i) Map *map = target->GetMap(); if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), gameobject_id, map, - x, y, z, target->GetOrientation(), 0, 0, 0, 0, 100, 1)) + m_caster->GetPhaseMask(), x, y, z, target->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 100, 1)) { delete pGameObj; return; } int32 duration = GetSpellDuration(m_spellInfo); - pGameObj->SetRespawnTime(duration > 0 ? duration/1000 : 0); + pGameObj->SetRespawnTime(duration > 0 ? duration/IN_MILISECONDS : 0); pGameObj->SetSpellId(m_spellInfo->Id); if(pGameObj->GetGoType() != GAMEOBJECT_TYPE_FLAGDROP) // make dropped flag clickable for other players (not set owner guid (created by) for this)... @@ -4440,9 +4408,9 @@ void Spell::EffectSummonObjectWild(uint32 i) { GameObject* linkedGO = new GameObject; if(linkedGO->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), linkedEntry, map, - x, y, z, target->GetOrientation(), 0, 0, 0, 0, 100, 1)) + m_caster->GetPhaseMask(), x, y, z, target->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 100, 1)) { - linkedGO->SetRespawnTime(duration > 0 ? duration/1000 : 0); + linkedGO->SetRespawnTime(duration > 0 ? duration/IN_MILISECONDS : 0); linkedGO->SetSpellId(m_spellInfo->Id); m_caster->AddGameObject(linkedGO); @@ -4461,441 +4429,638 @@ void Spell::EffectScriptEffect(uint32 effIndex) { // TODO: we must implement hunter pet summon at login there (spell 6962) - // by spell id - switch(m_spellInfo->Id) + switch(m_spellInfo->SpellFamilyName) { - // PX-238 Winter Wondervolt TRAP - case 26275: + case SPELLFAMILY_GENERIC: { - if( unitTarget->HasAura(26272,0) - || unitTarget->HasAura(26157,0) - || unitTarget->HasAura(26273,0) - || unitTarget->HasAura(26274,0)) - return; + switch(m_spellInfo->Id) + { + // PX-238 Winter Wondervolt TRAP + case 26275: + { + uint32 spells[4] = { 26272, 26157, 26273, 26274 }; - uint32 iTmpSpellId; + // check presence + for(int j = 0; j < 4; ++j) + if(unitTarget->HasAuraEffect(spells[j],0)) + return; - switch(urand(0,3)) - { - case 0: - iTmpSpellId = 26272; - break; - case 1: - iTmpSpellId = 26157; - break; - case 2: - iTmpSpellId = 26273; - break; - case 3: - iTmpSpellId = 26274; - break; - } + // select spell + uint32 iTmpSpellId = spells[urand(0,3)]; - unitTarget->CastSpell(unitTarget, iTmpSpellId, true); + // cast + unitTarget->CastSpell(unitTarget, iTmpSpellId, true); + return; + } + // Bending Shinbone + case 8856: + { + if(!itemTarget && m_caster->GetTypeId()!=TYPEID_PLAYER) + return; - return; - } + uint32 spell_id = 0; + switch(urand(1,5)) + { + case 1: spell_id = 8854; break; + default: spell_id = 8855; break; + } - // Bending Shinbone - case 8856: - { - if(!itemTarget && m_caster->GetTypeId()!=TYPEID_PLAYER) - return; + m_caster->CastSpell(m_caster,spell_id,true,NULL); + return; + } + // Brittle Armor - need remove one 24575 Brittle Armor aura + case 24590: + unitTarget->RemoveAuraFromStack(24575); + return; + // Mercurial Shield - need remove one 26464 Mercurial Shield aura + case 26465: + unitTarget->RemoveAuraFromStack(26464); + return; + // Orb teleport spells + case 25140: + case 25143: + case 25650: + case 25652: + case 29128: + case 29129: + case 35376: + case 35727: + { + if(!unitTarget) + return; - uint32 spell_id = 0; - switch(urand(1,5)) - { - case 1: spell_id = 8854; break; - default: spell_id = 8855; break; - } + uint32 spellid; + switch(m_spellInfo->Id) + { + case 25140: spellid = 32571; break; + case 25143: spellid = 32572; break; + case 25650: spellid = 30140; break; + case 25652: spellid = 30141; break; + case 29128: spellid = 32568; break; + case 29129: spellid = 32569; break; + case 35376: spellid = 25649; break; + case 35727: spellid = 35730; break; + default: + return; + } - m_caster->CastSpell(m_caster,spell_id,true,NULL); - return; - } + unitTarget->CastSpell(unitTarget,spellid,false); + return; + } + // Shadow Flame (All script effects, not just end ones to prevent player from dodging the last triggered spell) + case 22539: + case 22972: + case 22975: + case 22976: + case 22977: + case 22978: + case 22979: + case 22980: + case 22981: + case 22982: + case 22983: + case 22984: + case 22985: + { + if(!unitTarget || !unitTarget->isAlive()) + return; - // Healthstone creating spells - case 6201: - case 6202: - case 5699: - case 11729: - case 11730: - case 27230: - { - uint32 itemtype; - uint32 rank = 0; - Unit::AuraList const& mDummyAuras = unitTarget->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i) - { - if((*i)->GetId() == 18692) + // Onyxia Scale Cloak + if(unitTarget->GetDummyAura(22683)) + return; + + // Shadow Flame + m_caster->CastSpell(unitTarget, 22682, true); + return; + } + // Summon Black Qiraji Battle Tank + case 26656: { - rank = 1; + if(!unitTarget) + return; + + // Prevent stacking of mounts + unitTarget->RemoveAurasByType(SPELL_AURA_MOUNTED); + + // Two separate mounts depending on area id (allows use both in and out of specific instance) + if (unitTarget->GetAreaId() == 3428) + unitTarget->CastSpell(unitTarget, 25863, false); + else + unitTarget->CastSpell(unitTarget, 26655, false); + return; + } + // Piccolo of the Flaming Fire + case 17512: + { + if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + unitTarget->HandleEmoteCommand(EMOTE_STATE_DANCE); + return; + } + // Escape artist + case 20589: + { + if(!unitTarget) + return; + // It is said that removing effects by script should include dispel resist mods + unitTarget->RemoveAurasByTypeWithDispel(SPELL_AURA_MOD_ROOT, this); + unitTarget->RemoveAurasByTypeWithDispel(SPELL_AURA_MOD_DECREASE_SPEED, this); + return; + } + // Mirren's Drinking Hat + case 29830: + { + uint32 item = 0; + switch ( urand(1,6) ) + { + case 1:case 2:case 3: + item = 23584;break; // Loch Modan Lager + case 4:case 5: + item = 23585;break; // Stouthammer Lite + case 6: + item = 23586;break; // Aerie Peak Pale Ale + } + if (item) + DoCreateItem(effIndex,item); break; } - else if((*i)->GetId() == 18693) + // Improved Sprint + case 30918: { - rank = 2; + // Removes snares and roots. + uint32 mechanic_mask = (1<<MECHANIC_ROOT) | (1<<MECHANIC_SNARE); + Unit::AuraMap& Auras = unitTarget->GetAuras(); + for(Unit::AuraMap::iterator iter = Auras.begin(), next; iter != Auras.end();) + { + Aura *aur = iter->second; + if (!aur->IsPositive()) //only remove negative spells + { + // check for mechanic mask + if(GetAllSpellMechanicMask(aur->GetSpellProto()) & mechanic_mask) + { + unitTarget->RemoveAura(iter); + } + else + iter++; + } + else + iter++; + } break; } - } + /*// Flame Crash + case 41126: + { + if(!unitTarget) + return; - static uint32 const itypes[6][3] = { - { 5512,19004,19005}, // Minor Healthstone - { 5511,19006,19007}, // Lesser Healthstone - { 5509,19008,19009}, // Healthstone - { 5510,19010,19011}, // Greater Healthstone - { 9421,19012,19013}, // Major Healthstone - {22103,22104,22105} // Master Healthstone - }; + unitTarget->CastSpell(unitTarget, 41131, true); + break; + }*/ + // Draw Soul + case 40904: + { + if(!unitTarget) + return; - switch(m_spellInfo->Id) - { - case 6201: itemtype=itypes[0][rank];break; // Minor Healthstone - case 6202: itemtype=itypes[1][rank];break; // Lesser Healthstone - case 5699: itemtype=itypes[2][rank];break; // Healthstone - case 11729: itemtype=itypes[3][rank];break; // Greater Healthstone - case 11730: itemtype=itypes[4][rank];break; // Major Healthstone - case 27230: itemtype=itypes[5][rank];break; // Master Healthstone - default: - return; - } - DoCreateItem( effIndex, itemtype ); - return; - } - // Brittle Armor - need remove one 24575 Brittle Armor aura - case 24590: - unitTarget->RemoveSingleAuraFromStack(24575, 0); - unitTarget->RemoveSingleAuraFromStack(24575, 1); - return; - // Mercurial Shield - need remove one 26464 Mercurial Shield aura - case 26465: - unitTarget->RemoveSingleAuraFromStack(26464, 0); - return; - // Orb teleport spells - case 25140: - case 25143: - case 25650: - case 25652: - case 29128: - case 29129: - case 35376: - case 35727: - { - if(!unitTarget) - return; + unitTarget->CastSpell(m_caster, 40903, true); + break; + } + case 48025: // Headless Horseman's Mount + { + if(!unitTarget) + return; - uint32 spellid; - switch(m_spellInfo->Id) - { - case 25140: spellid = 32571; break; - case 25143: spellid = 32572; break; - case 25650: spellid = 30140; break; - case 25652: spellid = 30141; break; - case 29128: spellid = 32568; break; - case 29129: spellid = 32569; break; - case 35376: spellid = 25649; break; - case 35727: spellid = 35730; break; - default: - return; - } + switch(((Player*)unitTarget)->GetBaseSkillValue(762)) + { + case 75: unitTarget->CastSpell(unitTarget, 51621, true); break;; + case 150: unitTarget->CastSpell(unitTarget, 48024, true); break; + case 225: unitTarget->CastSpell(unitTarget, 51617, true); break; + case 300: unitTarget->CastSpell(unitTarget, 48023, true); break; + default: break; + } + break; + } + case 47977: // Magic Broom + { + if(!unitTarget) + return; - unitTarget->CastSpell(unitTarget,spellid,false); - return; - } + if(unitTarget) + { + switch(((Player*)unitTarget)->GetBaseSkillValue(762)) + { + case 75: unitTarget->CastSpell(unitTarget, 42680, true); break;; + case 150: case 225: case 300: unitTarget->CastSpell(unitTarget, 42683, true); break; + default: break; + } + } + break; + } + case 41931: + { + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; - // Shadow Flame (All script effects, not just end ones to prevent player from dodging the last triggered spell) - case 22539: - case 22972: - case 22975: - case 22976: - case 22977: - case 22978: - case 22979: - case 22980: - case 22981: - case 22982: - case 22983: - case 22984: - case 22985: - { - if(!unitTarget || !unitTarget->isAlive()) - return; + int bag=19; + int slot=0; + Item* item = NULL; - // Onyxia Scale Cloak - if(unitTarget->GetDummyAura(22683)) - return; + while (bag < 256) + { + item = ((Player*)m_caster)->GetItemByPos(bag,slot); + if (item && item->GetEntry() == 38587) break; + slot++; + if (slot == 39) + { + slot = 0; + bag++; + } + } + if (bag < 256) + { + if (((Player*)m_caster)->GetItemByPos(bag,slot)->GetCount() == 1) ((Player*)m_caster)->RemoveItem(bag,slot,true); + else ((Player*)m_caster)->GetItemByPos(bag,slot)->SetCount(((Player*)m_caster)->GetItemByPos(bag,slot)->GetCount()-1); + // Spell 42518 (Braufest - Gratisprobe des Braufest herstellen) + m_caster->CastSpell(m_caster,42518,true); + return; + } + break; + } + // Force Cast - Portal Effect: Sunwell Isle + case 44876: + { + if(!unitTarget) + return; - // Shadow Flame - m_caster->CastSpell(unitTarget, 22682, true); - return; - } - break; + unitTarget->CastSpell(unitTarget, 44870, true); + break; + } + // Goblin Weather Machine + case 46203: + { + if(!unitTarget) + return; - // Summon Black Qiraji Battle Tank - case 26656: - { - if(!unitTarget) - return; + uint32 spellId; + switch(rand()%4) + { + case 0: spellId = 46740; break; + case 1: spellId = 46739; break; + case 2: spellId = 46738; break; + case 3: spellId = 46736; break; + } + unitTarget->CastSpell(unitTarget, spellId, true); + break; + } + //5,000 Gold + case 46642: + { + if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) + return; - // Prevent stacking of mounts - unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); + ((Player*)unitTarget)->ModifyMoney(50000000); - // Two separate mounts depending on area id (allows use both in and out of specific instance) - if (unitTarget->GetAreaId() == 3428) - unitTarget->CastSpell(unitTarget, 25863, false); - else - unitTarget->CastSpell(unitTarget, 26655, false); - break; - } - // Piccolo of the Flaming Fire - case 17512: - { - if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - unitTarget->HandleEmoteCommand(EMOTE_STATE_DANCE); - break; - } - // Netherbloom - case 28702: - { - if(!unitTarget) - return; - // 25% chance of casting a random buff - if(roll_chance_i(75)) - return; + break; + } + // Emblazon Runeblade + case 51770: + { + if(!m_originalCaster) + return; + + m_originalCaster->CastSpell(m_originalCaster, damage, false); + break; + } + // Death Gate + case 52751: + { + if(!unitTarget || unitTarget->getClass() != CLASS_DEATH_KNIGHT) + return; + // triggered spell is stored in m_spellInfo->EffectBasePoints[0] + unitTarget->CastSpell(unitTarget, damage, false); + break; + } + case 58418: // Portal to Orgrimmar + case 58420: // Portal to Stormwind + { + if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER || effIndex!=0) + return; + + uint32 spellID = m_spellInfo->CalculateSimpleValue(0); + uint32 questID = m_spellInfo->CalculateSimpleValue(1); - // triggered spells are 28703 to 28707 - // Note: some sources say, that there was the possibility of - // receiving a debuff. However, this seems to be removed by a patch. - const uint32 spellid = 28703; + if( ((Player*)unitTarget)->GetQuestStatus(questID) == QUEST_STATUS_COMPLETE && !((Player*)unitTarget)->GetQuestRewardStatus (questID) ) + unitTarget->CastSpell(unitTarget, spellID, true); - // don't overwrite an existing aura - for(uint8 i=0; i<5; i++) - if(unitTarget->HasAura(spellid+i, 0)) return; - unitTarget->CastSpell(unitTarget, spellid+urand(0, 4), true); - break; - } + } + // random spell learn instead placeholder + case 60893: // Northrend Alchemy Research + case 61177: // Northrend Inscription Research + case 61288: // Minor Inscription Research + case 61756: // Northrend Inscription Research (FAST QA VERSION) + { + if(!IsExplicitDiscoverySpell(m_spellInfo)) + { + sLog.outError("Wrong explicit discovery spell %u structure, or outdated...",m_spellInfo->Id); + return; + } - // Nightmare Vine - case 28720: - { - if(!unitTarget) - return; - // 25% chance of casting Nightmare Pollen - if(roll_chance_i(75)) - return; - unitTarget->CastSpell(unitTarget, 28721, true); - break; - } + if(m_caster->GetTypeId()!=TYPEID_PLAYER) + return; + Player* player = (Player*)m_caster; - // Mirren's Drinking Hat - case 29830: - { - uint32 item = 0; - switch ( urand(1,6) ) - { - case 1: case 2: case 3: item = 23584; break;// Loch Modan Lager - case 4: case 5: item = 23585; break;// Stouthammer Lite - case 6: item = 23586; break;// Aerie Peak Pale Ale + // need replace effect 0 item by loot + uint32 reagent_id = m_spellInfo->EffectItemType[0]; + + if(!player->HasItemCount(reagent_id,1)) + return; + + // remove reagent + uint32 count = 1; + player->DestroyItemCount (reagent_id,count,true); + + // create some random items + player->AutoStoreLoot(m_spellInfo->Id,LootTemplates_Spell); + + // learn random explicit discovery recipe (if any) + if(uint32 discoveredSpell = GetExplicitDiscoverySpell(m_spellInfo->Id, player)) + player->learnSpell(discoveredSpell,false); + return; + } } - if (item) - DoCreateItem(effIndex,item); break; } - // Improved Sprint - case 30918: + case SPELLFAMILY_WARLOCK: { - // Removes snares and roots. - uint32 mechanic_mask = (1<<MECHANIC_ROOT) | (1<<MECHANIC_SNARE); - Unit::AuraMap& Auras = unitTarget->GetAuras(); - for(Unit::AuraMap::iterator iter = Auras.begin(), next; iter != Auras.end(); iter = next) + switch(m_spellInfo->Id) { - next = iter; - ++next; - Aura *aur = iter->second; - if (!aur->IsPositive()) //only remove negative spells + // Healthstone creating spells + case 6201: + case 6202: + case 5699: + case 11729: + case 11730: + case 27230: + case 47871: + case 47878: { - // check for mechanic mask - if(GetSpellMechanicMask(aur->GetSpellProto(), aur->GetEffIndex()) & mechanic_mask) + uint32 itemtype; + uint32 rank = 0; + Unit::AuraEffectList const& mDummyAuras = unitTarget->GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraEffectList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i) { - unitTarget->RemoveAurasDueToSpell(aur->GetId()); - if(Auras.empty()) + if((*i)->GetId() == 18692) + { + rank = 1; break; - else - next = Auras.begin(); + } + else if((*i)->GetId() == 18693) + { + rank = 2; + break; + } } - } - } - break; - } - - // Goblin Weather Machine - case 46203: - { - if(!unitTarget) - return; - uint32 spellId; - switch(rand()%4) - { - case 0: - spellId=46740; - break; - case 1: - spellId=46739; - break; - case 2: - spellId=46738; - break; - case 3: - spellId=46736; - break; - } - unitTarget->CastSpell(unitTarget, spellId, true); - break; - } - case 48025: // Headless Horseman's Mount - { - if(!unitTarget) - return; + static uint32 const itypes[8][3] = { + { 5512,19004,19005}, // Minor Healthstone + { 5511,19006,19007}, // Lesser Healthstone + { 5509,19008,19009}, // Healthstone + { 5510,19010,19011}, // Greater Healthstone + { 9421,19012,19013}, // Major Healthstone + {22103,22104,22105}, // Master Healthstone + {36889,36890,36891}, // Demonic Healthstone + {36892,36893,36894} // Fel Healthstone + }; - if(unitTarget) - { - switch(((Player*)unitTarget)->GetBaseSkillValue(762)) + switch(m_spellInfo->Id) { - case 75: unitTarget->CastSpell(unitTarget, 51621, true); break;; - case 150: unitTarget->CastSpell(unitTarget, 48024, true); break; - case 225: unitTarget->CastSpell(unitTarget, 51617, true); break; - case 300: unitTarget->CastSpell(unitTarget, 48023, true); break; - default: break; + case 6201: + itemtype=itypes[0][rank];break; // Minor Healthstone + case 6202: + itemtype=itypes[1][rank];break; // Lesser Healthstone + case 5699: + itemtype=itypes[2][rank];break; // Healthstone + case 11729: + itemtype=itypes[3][rank];break; // Greater Healthstone + case 11730: + itemtype=itypes[4][rank];break; // Major Healthstone + case 27230: + itemtype=itypes[5][rank];break; // Master Healthstone + case 47871: + itemtype=itypes[6][rank];break; // Demonic Healthstone + case 47878: + itemtype=itypes[7][rank];break; // Fel Healthstone + default: + return; } + DoCreateItem( effIndex, itemtype ); + return; } - break; + // Everlasting Affliction + case 47422: + // Refresh corruption on target + if (AuraEffect * aur = unitTarget->GetAura(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_WARLOCK, 0x2, 0, 0, m_caster->GetGUID())) + aur->GetParentAura()->RefreshAura(); + return; + } + break; } - case 47977: // Magic Broom + case SPELLFAMILY_PRIEST: { - if(!unitTarget) - return; - - if(unitTarget) + switch(m_spellInfo->Id) { - switch(((Player*)unitTarget)->GetBaseSkillValue(762)) + // Pain and Suffering + case 47948: + { + if (!unitTarget) + return; + // Refresh Shadow Word: Pain on target + if (AuraEffect * aur = unitTarget->GetAura(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_PRIEST, 0x8000, 0, 0, m_caster->GetGUID())) + aur->GetParentAura()->RefreshAura(); + return; + } + // Divine Hymn + case 47951: { - case 75: unitTarget->CastSpell(unitTarget, 42680, true); break;; - case 150: case 225: case 300: unitTarget->CastSpell(unitTarget, 42683, true); break; - default: break; + if (!unitTarget) + return; + Unit * target=NULL; + unitTarget->CastSpell(target, 59600, false); + unitTarget->CastSpell(target, 47953, false); + return; } + default: + break; } break; } - } - - if(!unitTarget || !unitTarget->isAlive()) // can we remove this check? - { - sLog.outError("Spell %u in EffectScriptEffect does not have unitTarget", m_spellInfo->Id); - return; - } - - switch(m_spellInfo->Id) - { - // Dreaming Glory - case 28698: unitTarget->CastSpell(unitTarget, 28694, true); break; - // Needle Spine - //case 39835: unitTarget->CastSpell(unitTarget, 39968, true); break; - // Draw Soul - case 40904: unitTarget->CastSpell(m_caster, 40903, true); break; - // Flame Crash - //case 41126: unitTarget->CastSpell(unitTarget, 41131, true); break; - case 41931: + case SPELLFAMILY_HUNTER: { - int bag=19; - int slot=0; - Item* item = NULL; - - while (bag < 256) + switch(m_spellInfo->Id) { - item = ((Player*)m_caster)->GetItemByPos(bag,slot); - if (item && item->GetEntry() == 38587) break; - slot++; - if (slot == 39) + // Chimera Shot + case 53209: { - slot = 0; - bag++; + uint32 spellId = 0; + int32 basePoint = 0; + Unit::AuraMap& Auras = unitTarget->GetAuras(); + for(Unit::AuraMap::iterator i = Auras.begin(); i != Auras.end(); ++i) + { + Aura *aura = (*i).second; + if (aura->GetCasterGUID() != m_caster->GetGUID()) + continue; + // Search only Serpent Sting, Viper Sting, Scorpid Sting auras + flag96 familyFlag = aura->GetSpellProto()->SpellFamilyFlags; + if (!(familyFlag[1] & 0x00000080 || familyFlag[0] & 0x0000C000)) + continue; + + // Serpent Sting - Instantly deals 40% of the damage done by your Serpent Sting. + if (familyFlag[0] & 0x4000) + { + spellId = 53353; // 53353 Chimera Shot - Serpent + basePoint = aura->GetPartAura(0)->GetAmount() * 5 * 40 / 100; + } + // Viper Sting - Instantly restores mana to you equal to 60% of the total amount drained by your Viper Sting. + if (familyFlag[1] & 0x00000080) + { + int32 tickCount = (aura->GetAuraMaxDuration() - aura->GetAuraDuration()) / aura->GetPartAura(0)->GetAuraAmplitude(); + spellId = 53358; // 53358 Chimera Shot - Viper + // Amount of one aura tick + basePoint = aura->GetPartAura(0)->GetAmount() * aura->GetTarget()->GetMaxPower(POWER_MANA) / 100 ; + int32 casterBasePoint = aura->GetPartAura(0)->GetAmount() * unitTarget->GetMaxPower(POWER_MANA) / 50 ; + if (basePoint > casterBasePoint) + basePoint = casterBasePoint; + basePoint = basePoint * tickCount * 60 / 100; + } + // Scorpid Sting - Attempts to Disarm the target for 10 sec. This effect cannot occur more than once per 1 minute. + if (familyFlag[0] & 0x00008000) + spellId = 53359; // 53359 Chimera Shot - Scorpid + // ?? nothing say in spell desc (possibly need addition check) + //if (familyFlag & 0x0000010000000000LL || // dot + // familyFlag & 0x0000100000000000LL) // stun + //{ + // spellId = 53366; // 53366 Chimera Shot - Wyvern + //} + + // Refresh aura duration + aura->RefreshAura(); + break; + } + if (spellId) + m_caster->CastCustomSpell(unitTarget, spellId, &basePoint, 0, 0, true); + return; } + default: + break; } - if (bag < 256) - { - if (((Player*)m_caster)->GetItemByPos(bag,slot)->GetCount() == 1) ((Player*)m_caster)->RemoveItem(bag,slot,true); - else ((Player*)m_caster)->GetItemByPos(bag,slot)->SetCount(((Player*)m_caster)->GetItemByPos(bag,slot)->GetCount()-1); - // Spell 42518 (Braufest - Gratisprobe des Braufest herstellen) - m_caster->CastSpell(m_caster,42518,true); - return; - } - } - // Force Cast - Portal Effect: Sunwell Isle - case 44876: unitTarget->CastSpell(unitTarget, 44870, true); break; - //5,000 Gold - case 46642: - { - if(unitTarget->GetTypeId() == TYPEID_PLAYER) - ((Player*)unitTarget)->ModifyMoney(50000000); break; } - } - - if( m_spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN ) - { - switch(m_spellInfo->SpellFamilyFlags) + case SPELLFAMILY_PALADIN: { // Judgement - case 0x800000: + if (m_spellInfo->SpellFamilyFlags[0] & 0x800000) { + if(!unitTarget || !unitTarget->isAlive()) + return; + uint32 spellId1 = 0; uint32 spellId2 = 0; - // all seals have aura dummy - Unit::AuraList const& m_dummyAuras = m_caster->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraList::const_iterator itr = m_dummyAuras.begin(); itr != m_dummyAuras.end(); ++itr) + // Judgement self add switch + switch (m_spellInfo->Id) { - SpellEntry const *spellInfo = (*itr)->GetSpellProto(); - - // search seal (all seals have judgement's aura dummy spell id in 2 effect - if ( !spellInfo || !IsSealSpell((*itr)->GetSpellProto()) || (*itr)->GetEffIndex() != 2 ) - continue; - - // must be calculated base at raw base points in spell proto, GetModifier()->m_value for S.Righteousness modified by SPELLMOD_DAMAGE - spellId2 = (*itr)->GetSpellProto()->EffectBasePoints[2]+1; - - if(spellId2 <= 1) - continue; - - // found, remove seal - m_caster->RemoveAurasDueToSpell((*itr)->GetId()); - - // Sanctified Judgement - Unit::AuraList const& m_auras = m_caster->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraList::const_iterator i = m_auras.begin(); i != m_auras.end(); ++i) + case 41467: break; // Judgement + case 53407: spellId1 = 20184; break; // Judgement of Justice + case 20271: // Judgement of Light + case 57774: spellId1 = 20185; break; // Judgement of Light + case 53408: spellId1 = 20186; break; // Judgement of Wisdom + default: + return; + } + // all seals have aura dummy in 2 effect + Unit::AuraMap & sealAuras = m_caster->GetAuras(); + for(Unit::AuraMap::iterator iter = sealAuras.begin(); iter != sealAuras.end();) + { + if (IsSealSpell(iter->second->GetSpellProto())) { - if ((*i)->GetSpellProto()->SpellIconID == 205 && (*i)->GetSpellProto()->Attributes == 0x01D0LL) + if (AuraEffect * aureff = iter->second->GetPartAura(2)) + if (aureff->GetAuraName()==SPELL_AURA_DUMMY) + { + if (sSpellStore.LookupEntry(aureff->GetAmount())) + spellId2 = aureff->GetAmount(); + break; + } + if (!spellId2) { - int32 chance = (*i)->GetModifier()->m_amount; - if ( roll_chance_i(chance) ) + switch (iter->first) { - int32 mana = spellInfo->manaCost; - if ( Player* modOwner = m_caster->GetSpellModOwner() ) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COST, mana); - mana = int32(mana* 0.8f); - m_caster->CastCustomSpell(m_caster,31930,&mana,NULL,NULL,true,NULL,*i); + // Seal of light, wisdom, justice + case 20165: + case 20166: + case 20164: + spellId2 = 54158; } - break; } + break; } + else + ++iter; + } + if (spellId1) + m_caster->CastSpell(unitTarget, spellId1, true); + if (spellId2) + m_caster->CastSpell(unitTarget, spellId2, true); + return; + } + } + case SPELLFAMILY_POTION: + { + switch(m_spellInfo->Id) + { + // Dreaming Glory + case 28698: + { + if(!unitTarget) + return; + unitTarget->CastSpell(unitTarget, 28694, true); + break; + } + // Netherbloom + case 28702: + { + if(!unitTarget) + return; + // 25% chance of casting a random buff + if(roll_chance_i(75)) + return; + + // triggered spells are 28703 to 28707 + // Note: some sources say, that there was the possibility of + // receiving a debuff. However, this seems to be removed by a patch. + const uint32 spellid = 28703; + // don't overwrite an existing aura + for(uint8 i=0; i<5; i++) + if(unitTarget->HasAuraEffect(spellid+i, 0)) + return; + unitTarget->CastSpell(unitTarget, spellid+urand(0, 4), true); break; } - m_caster->CastSpell(unitTarget,spellId2,true); - return; + // Nightmare Vine + case 28720: + { + if(!unitTarget) + return; + // 25% chance of casting Nightmare Pollen + if(roll_chance_i(75)) + return; + unitTarget->CastSpell(unitTarget, 28721, true); + break; + } } + break; } } @@ -4911,7 +5076,7 @@ void Spell::EffectSanctuary(uint32 /*i*/) std::list<Unit*> targets; Trinity::AnyUnfriendlyUnitInObjectRangeCheck u_check(unitTarget, unitTarget, World::GetMaxVisibleDistance()); - Trinity::UnitListSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(targets, u_check); + Trinity::UnitListSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(unitTarget, targets, u_check); unitTarget->VisitNearbyObject(World::GetMaxVisibleDistance(), searcher); for(std::list<Unit*>::iterator iter = targets.begin(); iter != targets.end(); ++iter) { @@ -4931,9 +5096,9 @@ void Spell::EffectSanctuary(uint32 /*i*/) unitTarget->CombatStop(); unitTarget->getHostilRefManager().deleteReferences(); // stop all fighting // Vanish allows to remove all threat and cast regular stealth so other spells can be used - if(m_spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && (m_spellInfo->SpellFamilyFlags & SPELLFAMILYFLAG_ROGUE_VANISH)) + if(m_spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && (m_spellInfo->SpellFamilyFlags[0] & SPELLFAMILYFLAG_ROGUE_VANISH)) { - ((Player *)m_caster)->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT); + ((Player *)m_caster)->RemoveAurasByType(SPELL_AURA_MOD_ROOT); } } @@ -4966,6 +5131,7 @@ void Spell::EffectDuel(uint32 i) // Players can only fight a duel with each other outside (=not inside dungeons and not in capital cities) // Don't have to check the target's map since you cannot challenge someone across maps if(caster->GetMap()->Instanceable()) + //if( mapid != 0 && mapid != 1 && mapid != 530 && mapid != 571 && mapid != 609) { SendCastResult(SPELL_FAILED_NO_DUELING); // Dueling isn't allowed here return; @@ -4991,11 +5157,12 @@ void Spell::EffectDuel(uint32 i) uint32 gameobject_id = m_spellInfo->EffectMiscValue[i]; Map *map = m_caster->GetMap(); - if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), gameobject_id, map, + if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), gameobject_id, + map, m_caster->GetPhaseMask(), m_caster->GetPositionX()+(unitTarget->GetPositionX()-m_caster->GetPositionX())/2 , m_caster->GetPositionY()+(unitTarget->GetPositionY()-m_caster->GetPositionY())/2 , m_caster->GetPositionZ(), - m_caster->GetOrientation(), 0, 0, 0, 0, 0, 1)) + m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0, 1)) { delete pGameObj; return; @@ -5004,7 +5171,7 @@ void Spell::EffectDuel(uint32 i) pGameObj->SetUInt32Value(GAMEOBJECT_FACTION, m_caster->getFaction() ); pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel()+1 ); int32 duration = GetSpellDuration(m_spellInfo); - pGameObj->SetRespawnTime(duration > 0 ? duration/1000 : 0); + pGameObj->SetRespawnTime(duration > 0 ? duration/IN_MILISECONDS : 0); pGameObj->SetSpellId(m_spellInfo->Id); m_caster->AddGameObject(pGameObj); @@ -5081,7 +5248,7 @@ void Spell::EffectSummonPlayer(uint32 /*i*/) WorldPacket data(SMSG_SUMMON_REQUEST, 8+4+4); data << uint64(m_caster->GetGUID()); // summoner guid data << uint32(m_caster->GetZoneId()); // summoner zone - data << uint32(MAX_PLAYER_SUMMON_DELAY*1000); // auto decline after msecs + data << uint32(MAX_PLAYER_SUMMON_DELAY*IN_MILISECONDS); // auto decline after msecs ((Player*)unitTarget)->GetSession()->SendPacket(&data); } @@ -5104,89 +5271,40 @@ void Spell::EffectActivateObject(uint32 effect_idx) sWorld.ScriptCommandStart(activateCommand, delay_secs, m_caster, gameObjTarget); } -void Spell::EffectSummonTotem(uint32 i) +void Spell::EffectApplyGlyph(uint32 i) { - uint8 slot = 0; - switch(m_spellInfo->EffectMiscValueB[i]) - { - case SUMMON_TYPE_TOTEM_SLOT1: slot = 0; break; - case SUMMON_TYPE_TOTEM_SLOT2: slot = 1; break; - case SUMMON_TYPE_TOTEM_SLOT3: slot = 2; break; - case SUMMON_TYPE_TOTEM_SLOT4: slot = 3; break; - // Battle standard case - case SUMMON_TYPE_TOTEM: slot = 254; break; - // jewelery statue case, like totem without slot - case SUMMON_TYPE_GUARDIAN: slot = 255; break; - default: return; - } - - if(slot < MAX_TOTEM) - { - uint64 guid = m_caster->m_TotemSlot[slot]; - if(guid != 0) - { - Creature *OldTotem = ObjectAccessor::GetCreature(*m_caster, guid); - if(OldTotem && OldTotem->isTotem()) - ((Totem*)OldTotem)->UnSummon(); - } - } - - uint32 team = 0; - if (m_caster->GetTypeId()==TYPEID_PLAYER) - team = ((Player*)m_caster)->GetTeam(); - - Totem* pTotem = new Totem; - - if(!pTotem->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), m_caster->GetMap(), m_spellInfo->EffectMiscValue[i], team )) - { - delete pTotem; + if(m_caster->GetTypeId() != TYPEID_PLAYER) return; - } - - float angle = slot < MAX_TOTEM ? M_PI/MAX_TOTEM - (slot*2*M_PI/MAX_TOTEM) : 0; - - float x,y,z; - m_caster->GetClosePoint(x,y,z,pTotem->GetObjectSize(),2.0f,angle); - - // totem must be at same Z in case swimming caster and etc. - if( fabs( z - m_caster->GetPositionZ() ) > 5 ) - z = m_caster->GetPositionZ(); - - pTotem->Relocate(x, y, z, m_caster->GetOrientation()); - - if(slot < MAX_TOTEM) - m_caster->m_TotemSlot[slot] = pTotem->GetGUID(); - pTotem->SetOwner(m_caster->GetGUID()); - pTotem->SetTypeBySummonSpell(m_spellInfo); // must be after Create call where m_spells initilized - - int32 duration=GetSpellDuration(m_spellInfo); - if(Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id,SPELLMOD_DURATION, duration); - pTotem->SetDuration(duration); + Player *player = (Player*)m_caster; - if (damage) // if not spell info, DB values used + // apply new one + if(uint32 glyph = m_spellInfo->EffectMiscValue[i]) { - pTotem->SetMaxHealth(damage); - pTotem->SetHealth(damage); - } - - pTotem->SetUInt32Value(UNIT_CREATED_BY_SPELL,m_spellInfo->Id); - pTotem->SetFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_PVP_ATTACKABLE); - - pTotem->ApplySpellImmune(m_spellInfo->Id,IMMUNITY_STATE,SPELL_AURA_MOD_FEAR,true); - pTotem->ApplySpellImmune(m_spellInfo->Id,IMMUNITY_STATE,SPELL_AURA_TRANSFORM,true); + if(GlyphPropertiesEntry const *gp = sGlyphPropertiesStore.LookupEntry(glyph)) + { + if(GlyphSlotEntry const *gs = sGlyphSlotStore.LookupEntry(player->GetGlyphSlot(m_glyphIndex))) + { + if(gp->TypeFlags != gs->TypeFlags) + { + SendCastResult(SPELL_FAILED_INVALID_GLYPH); + return; // glyph slot mismatch + } + } - pTotem->Summon(m_caster); + // remove old glyph + if(uint32 oldglyph = player->GetGlyph(m_glyphIndex)) + { + if(GlyphPropertiesEntry const *old_gp = sGlyphPropertiesStore.LookupEntry(oldglyph)) + { + player->RemoveAurasDueToSpell(old_gp->SpellId); + player->SetGlyph(m_glyphIndex, 0); + } + } - if(slot < MAX_TOTEM && m_caster->GetTypeId() == TYPEID_PLAYER) - { - WorldPacket data(SMSG_TOTEM_CREATED, 1+8+4+4); - data << uint8(slot); - data << uint64(pTotem->GetGUID()); - data << uint32(duration); - data << uint32(m_spellInfo->Id); - ((Player*)m_caster)->SendDirectMessage(&data); + player->CastSpell(m_caster, gp->SpellId, true); + player->SetGlyph(m_glyphIndex, glyph); + } } } @@ -5227,7 +5345,7 @@ void Spell::EffectEnchantHeldItem(uint32 i) return; // Apply the temporary enchantment - item->SetEnchantment(slot, enchant_id, duration*1000, 0); + item->SetEnchantment(slot, enchant_id, duration*IN_MILISECONDS, 0); item_owner->ApplyEnchantment(item,slot,true); } } @@ -5270,7 +5388,8 @@ void Spell::EffectFeedPet(uint32 i) Player *_player = (Player*)m_caster; - if(!itemTarget) + Item* foodItem = m_targets.getItemTarget(); + if(!foodItem) return; Pet *pet = _player->GetPet(); @@ -5280,15 +5399,15 @@ void Spell::EffectFeedPet(uint32 i) if(!pet->isAlive()) return; - int32 benefit = pet->GetCurrentFoodBenefitLevel(itemTarget->GetProto()->ItemLevel); + int32 benefit = pet->GetCurrentFoodBenefitLevel(foodItem->GetProto()->ItemLevel); if(benefit <= 0) return; uint32 count = 1; - _player->DestroyItemCount(itemTarget,count,true); + _player->DestroyItemCount(foodItem,count,true); // TODO: fix crash when a spell has two effects, both pointed at the same item target - m_caster->CastCustomSpell(m_caster,m_spellInfo->EffectTriggerSpell[i],&benefit,NULL,NULL,true); + m_caster->CastCustomSpell(pet,m_spellInfo->EffectTriggerSpell[i],&benefit,NULL,NULL,true); } void Spell::EffectDismissPet(uint32 /*i*/) @@ -5296,7 +5415,7 @@ void Spell::EffectDismissPet(uint32 /*i*/) if(m_caster->GetTypeId() != TYPEID_PLAYER) return; - Pet* pet = m_caster->GetPet(); + Pet* pet = ((Player*)m_caster)->GetPet(); // not let dismiss dead pet if(!pet||!pet->isAlive()) @@ -5332,9 +5451,6 @@ void Spell::EffectSummonObject(uint32 i) GameObject* pGameObj = new GameObject; - float rot2 = sin(m_caster->GetOrientation()/2); - float rot3 = cos(m_caster->GetOrientation()/2); - float x,y,z; // If dest location if present if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) @@ -5348,7 +5464,8 @@ void Spell::EffectSummonObject(uint32 i) m_caster->GetClosePoint(x,y,z,DEFAULT_WORLD_OBJECT_SIZE); Map *map = m_caster->GetMap(); - if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), go_id, map, x, y, z, m_caster->GetOrientation(), 0, 0, rot2, rot3, 0, 1)) + if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), go_id, map, + m_caster->GetPhaseMask(), x, y, z, m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0, 1)) { delete pGameObj; return; @@ -5356,7 +5473,7 @@ void Spell::EffectSummonObject(uint32 i) //pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL,m_caster->getLevel()); int32 duration = GetSpellDuration(m_spellInfo); - pGameObj->SetRespawnTime(duration > 0 ? duration/1000 : 0); + pGameObj->SetRespawnTime(duration > 0 ? duration/IN_MILISECONDS : 0); pGameObj->SetSpellId(m_spellInfo->Id); m_caster->AddGameObject(pGameObj); @@ -5427,18 +5544,14 @@ void Spell::EffectAddExtraAttacks(uint32 /*i*/) void Spell::EffectParry(uint32 /*i*/) { - if (unitTarget->GetTypeId() == TYPEID_PLAYER) - { + if (unitTarget && unitTarget->GetTypeId() == TYPEID_PLAYER) ((Player*)unitTarget)->SetCanParry(true); - } } void Spell::EffectBlock(uint32 /*i*/) { - if (unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - ((Player*)unitTarget)->SetCanBlock(true); + if (unitTarget && unitTarget->GetTypeId() == TYPEID_PLAYER) + ((Player*)unitTarget)->SetCanBlock(true); } void Spell::EffectMomentMove(uint32 i) @@ -5450,7 +5563,7 @@ void Spell::EffectMomentMove(uint32 i) return; uint32 mapid = m_caster->GetMapId(); - float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + float dis = m_caster->GetSpellRadiusForTarget(unitTarget, sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); // src point float *fx = new float[11], *fy = new float[11], *fz = new float[11]; @@ -5488,26 +5601,9 @@ void Spell::EffectMomentMove(uint32 i) if (hit == false) itr_j = last_valid; - - if (unitTarget->GetTypeId() == TYPEID_PLAYER) - ((Player*)unitTarget)->TeleportTo(mapid, fx[itr_j], fy[itr_j], fz[itr_j] + 0.07531, orientation, TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget == m_caster ? TELE_TO_SPELL : 0)); - else - MapManager::Instance().GetMap(mapid, unitTarget)->CreatureRelocation((Creature*)unitTarget, fx[itr_j], fy[itr_j], fz[itr_j] + 0.07531, orientation); + unitTarget->NearTeleportTo(fx[itr_j], fy[itr_j], fz[itr_j] + 0.07531, orientation, unitTarget==m_caster); delete [] fx; delete [] fy; delete [] fz; - -/* uint32 mapid = unitTarget->GetMapId(); - float ox,oy,oz; - unitTarget->GetPosition(ox,oy,oz); - - float fx,fy,fz; // getObjectHitPos overwrite last args in any result case - if(VMAP::VMapFactory::createOrGetVMapManager()->getObjectHitPos(mapid, ox,oy,oz+0.5, m_targets.m_destX,m_targets.m_destY,oz+0.5,fx,fy,fz, -0.5)) - unitTarget->UpdateGroundPositionZ(fx,fy,fz); - - if(unitTarget->GetTypeId() == TYPEID_PLAYER) - ((Player*)unitTarget)->TeleportTo(mapid, fx, fy, fz, unitTarget->GetOrientation(), TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget==m_caster ? TELE_TO_SPELL : 0)); - else - MapManager::Instance().GetMap(mapid, unitTarget)->CreatureRelocation((Creature*)unitTarget, fx, fy, fz, unitTarget->GetOrientation());*/ } void Spell::EffectReputation(uint32 i) @@ -5526,7 +5622,7 @@ void Spell::EffectReputation(uint32 i) if(!factionEntry) return; - _player->ModifyFactionReputation(factionEntry,rep_change); + _player->GetReputationMgr().ModifyReputation(factionEntry,rep_change); } void Spell::EffectQuestComplete(uint32 i) @@ -5630,76 +5726,32 @@ void Spell::EffectSummonCritter(uint32 i) if(!pet_entry) return; - Pet* old_critter = player->GetMiniPet(); - - // for same pet just despawn - if(old_critter && old_critter->GetEntry() == pet_entry) - { - player->RemoveMiniPet(); - return; - } - - // despawn old pet before summon new - if(old_critter) - player->RemoveMiniPet(); - - // summon new pet - Pet* critter = new Pet(MINI_PET); - - Map *map = m_caster->GetMap(); - uint32 pet_number = objmgr.GeneratePetNumber(); - if(!critter->Create(objmgr.GenerateLowGuid(HIGHGUID_PET), - map, pet_entry, pet_number)) - { - sLog.outError("Spell::EffectSummonCritter, spellid %u: no such creature entry %u", m_spellInfo->Id, pet_entry); - delete critter; - return; - } - float x,y,z; - // If dest location if present - if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) - { - x = m_targets.m_destX; - y = m_targets.m_destY; - z = m_targets.m_destZ; - } - // Summon if dest location not present near caster - else - m_caster->GetClosePoint(x,y,z,critter->GetObjectSize()); + GetSummonPosition(x, y, z); - critter->Relocate(x,y,z,m_caster->GetOrientation()); - - if(!critter->IsPositionValid()) - { - sLog.outError("ERROR: Pet (guidlow %d, entry %d) not summoned. Suggested coordinates isn't valid (X: %f Y: %f)", - critter->GetGUIDLow(), critter->GetEntry(), critter->GetPositionX(), critter->GetPositionY()); - delete critter; + int32 duration = GetSpellDuration(m_spellInfo); + TempSummon *critter = m_caster->GetMap()->SummonCreature(pet_entry, x, y, z, m_caster->GetOrientation(), sSummonPropertiesStore.LookupEntry(m_spellInfo->EffectMiscValueB[i]), duration, m_caster); + if(!critter) return; - } critter->SetOwnerGUID(m_caster->GetGUID()); critter->SetCreatorGUID(m_caster->GetGUID()); critter->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,m_caster->getFaction()); critter->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); - critter->AIM_Initialize(); - critter->InitPetCreateSpells(); // e.g. disgusting oozeling has a create spell as critter... + //critter->InitPetCreateSpells(); // e.g. disgusting oozeling has a create spell as critter... critter->SetMaxHealth(1); critter->SetHealth(1); critter->SetLevel(1); - // set timer for unsummon - int32 duration = GetSpellDuration(m_spellInfo); - if(duration > 0) - critter->SetDuration(duration); + critter->SetReactState(REACT_PASSIVE); + critter->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + critter->GetMotionMaster()->MoveTargetedHome(); std::string name = player->GetName(); - name.append(petTypeSuffix[critter->getPetType()]); + name.append(petTypeSuffix[3]); critter->SetName( name ); - player->SetMiniPet(critter); - - map->Add((Creature*)critter); } void Spell::EffectKnockBack(uint32 i) @@ -5707,10 +5759,6 @@ void Spell::EffectKnockBack(uint32 i) if(!unitTarget) return; - // Effect only works on players - if(unitTarget->GetTypeId()!=TYPEID_PLAYER) - return; - float x, y; if(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) { @@ -5723,31 +5771,29 @@ void Spell::EffectKnockBack(uint32 i) y = m_caster->GetPositionY(); } - float dx = unitTarget->GetPositionX() - x; - float dy = unitTarget->GetPositionY() - y; - float vcos, vsin; - if(dx < 0.001f && dy < 0.001f) + float speedxy = float(m_spellInfo->EffectMiscValue[i])/10; + float speedz = float(damage/10); + + unitTarget->KnockbackFrom(x, y, speedxy, speedz); +} + +void Spell::EffectJump2(uint32 i) +{ + if(!unitTarget) + return; + + float speedxy = float(m_spellInfo->EffectMiscValue[i])/10; + float speedz = float(damage/10); + if(!speedxy) { - float angle = rand_norm()*2*M_PI; - vcos = cos(angle); - vsin = sin(angle); + if(m_targets.getUnitTarget()) + unitTarget->JumpTo(m_targets.getUnitTarget(), speedz); } else { - float dist = sqrt((dx*dx) + (dy*dy)); - vcos = dx / dist; - vsin = dy / dist; + //1891: Disengage + unitTarget->JumpTo(speedxy, speedz, m_spellInfo->SpellIconID != 1891); } - - WorldPacket data(SMSG_MOVE_KNOCK_BACK, (8+4+4+4+4+4)); - data.append(unitTarget->GetPackGUID()); - data << uint32(0); // Sequence - data << float(vcos); // x direction - data << float(vsin); // y direction - data << float(m_spellInfo->EffectMiscValue[i])/10; // Horizontal speed - data << float(damage/-10); // Z Movement speed (vertical) - - ((Player*)unitTarget)->GetSession()->SendPacket(&data); } void Spell::EffectSendTaxi(uint32 i) @@ -5768,17 +5814,26 @@ void Spell::EffectSendTaxi(uint32 i) uint32 mountid = 0; switch(m_spellInfo->Id) { - case 31606: //Stormcrow Amulet + case 31606: //Stormcrow Amulet mountid = 17447; break; - case 45071: //Quest - Sunwell Daily - Dead Scar Bombing Run - case 45113: //Quest - Sunwell Daily - Ship Bombing Run - case 45353: //Quest - Sunwell Daily - Ship Bombing Run Return + case 45071: //Quest - Sunwell Daily - Dead Scar Bombing Run + case 45113: //Quest - Sunwell Daily - Ship Bombing Run + case 45353: //Quest - Sunwell Daily - Ship Bombing Run Return mountid = 22840; break; - case 34905: //Stealth Flight + case 34905: //Stealth Flight mountid = 6851; break; + case 45883: //Amber Ledge to Beryl Point + mountid = 23524; + break; + case 46064: //Amber Ledge to Coldarra + mountid = 6371; + break; + case 53335: //Stormwind Harbor Flight - Peaceful + mountid = 6852; + break; case 41533: //Fly of the Netherwing case 41540: //Fly of the Netherwing mountid = 23468; @@ -5820,22 +5875,24 @@ void Spell::EffectDispelMechanic(uint32 i) uint32 mechanic = m_spellInfo->EffectMiscValue[i]; + std::queue < Aura * > dispel_list; + Unit::AuraMap& Auras = unitTarget->GetAuras(); - for(Unit::AuraMap::iterator iter = Auras.begin(), next; iter != Auras.end(); iter = next) + for(Unit::AuraMap::iterator iter = Auras.begin(); iter != Auras.end(); iter++) { - next = iter; - ++next; - SpellEntry const *spell = sSpellStore.LookupEntry(iter->second->GetSpellProto()->Id); - if(spell->Mechanic == mechanic || spell->EffectMechanic[iter->second->GetEffIndex()] == mechanic) + if(GetAllSpellMechanicMask(iter->second->GetSpellProto()) & (1<<(mechanic))) + { + dispel_list.push(iter->second); + } + } + + for(;dispel_list.size();dispel_list.pop()) + { + if (GetDispelChance(dispel_list.front()->GetCaster(), dispel_list.front()->GetId())) { - unitTarget->RemoveAurasDueToSpell(spell->Id); - if(Auras.empty()) - break; - else - next = Auras.begin(); + unitTarget->RemoveAura(dispel_list.front(), AURA_REMOVE_BY_ENEMY_SPELL); } } - return; } void Spell::EffectSummonDeadPet(uint32 /*i*/) @@ -5850,36 +5907,40 @@ void Spell::EffectSummonDeadPet(uint32 /*i*/) return; if(damage < 0) return; + pet->SetUInt32Value(UNIT_DYNAMIC_FLAGS, 0); pet->RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); pet->setDeathState( ALIVE ); pet->clearUnitState(UNIT_STAT_ALL_STATE); pet->SetHealth( uint32(pet->GetMaxHealth()*(float(damage)/100))); - pet->AIM_Initialize(); - - _player->PetSpellInitialize(); + //pet->AIM_Initialize(); + //_player->PetSpellInitialize(); pet->SavePetToDB(PET_SAVE_AS_CURRENT); } void Spell::EffectDestroyAllTotems(uint32 /*i*/) { float mana = 0; - for(int slot = 0; slot < MAX_TOTEM; ++slot) + for(int slot = SUMMON_SLOT_TOTEM; slot < MAX_TOTEM_SLOT; ++slot) { - if(!m_caster->m_TotemSlot[slot]) + if(!m_caster->m_SummonSlot[slot]) continue; - Creature* totem = ObjectAccessor::GetCreature(*m_caster,m_caster->m_TotemSlot[slot]); + Creature* totem = ObjectAccessor::GetCreature(*m_caster,m_caster->m_SummonSlot[slot]); if(totem && totem->isTotem()) { uint32 spell_id = totem->GetUInt32Value(UNIT_CREATED_BY_SPELL); SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell_id); if(spellInfo) - mana += spellInfo->manaCost * damage / 100; + { + mana += spellInfo->manaCost; + mana += spellInfo->ManaCostPercentage * m_caster->GetCreateMana() / 100; + } ((Totem*)totem)->UnSummon(); } } + mana = mana * damage / 100; int32 gain = m_caster->ModifyPower(POWER_MANA,int32(mana)); m_caster->SendEnergizeSpellLog(m_caster, m_spellInfo->Id, gain, POWER_MANA); @@ -5965,13 +6026,14 @@ void Spell::EffectTransmitted(uint32 effIndex) //FIXME: this can be better check for most objects but still hack else if(m_spellInfo->EffectRadiusIndex[effIndex] && m_spellInfo->speed==0) { - float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[effIndex])); + float dis = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[effIndex])); m_caster->GetClosePoint(fx,fy,fz,DEFAULT_WORLD_OBJECT_SIZE, dis); } else { - float min_dis = GetSpellMinRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); - float max_dis = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); + //GO is always friendly to it's creator, get range for friends + float min_dis = GetSpellMinRangeForFriend(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); + float max_dis = GetSpellMaxRangeForFriend(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); float dis = rand_norm() * (max_dis - min_dis) + min_dis; m_caster->GetClosePoint(fx,fy,fz,DEFAULT_WORLD_OBJECT_SIZE, dis); @@ -6000,7 +6062,7 @@ void Spell::EffectTransmitted(uint32 effIndex) GameObject* pGameObj = new GameObject; if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), name_id, cMap, - fx, fy, fz, m_caster->GetOrientation(), 0, 0, 0, 0, 100, 1)) + m_caster->GetPhaseMask(), fx, fy, fz, m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 100, 1)) { delete pGameObj; return; @@ -6013,10 +6075,6 @@ void Spell::EffectTransmitted(uint32 effIndex) case GAMEOBJECT_TYPE_FISHINGNODE: { m_caster->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT,pGameObj->GetGUID()); - // Orientation3 - pGameObj->SetFloatValue(GAMEOBJECT_ROTATION + 2, 0.88431775569915771 ); - // Orientation4 - pGameObj->SetFloatValue(GAMEOBJECT_ROTATION + 3, -0.4668855369091033 ); m_caster->AddGameObject(pGameObj); // will removed at spell cancel // end time of range when possible catch fish (FISHING_BOBBER_READY_TIME..GetDuration(m_spellInfo)) @@ -6030,7 +6088,7 @@ void Spell::EffectTransmitted(uint32 effIndex) case 3: lastSec = 17; break; } - duration = duration - lastSec*1000 + FISHING_BOBBER_READY_TIME*1000; + duration = duration - lastSec*IN_MILISECONDS + FISHING_BOBBER_READY_TIME*IN_MILISECONDS; break; } case GAMEOBJECT_TYPE_SUMMONING_RITUAL: @@ -6050,14 +6108,14 @@ void Spell::EffectTransmitted(uint32 effIndex) } } - pGameObj->SetRespawnTime(duration > 0 ? duration/1000 : 0); + pGameObj->SetRespawnTime(duration > 0 ? duration/IN_MILISECONDS : 0); pGameObj->SetOwnerGUID(m_caster->GetGUID() ); //pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel() ); pGameObj->SetSpellId(m_spellInfo->Id); - DEBUG_LOG("AddObject at SpellEfects.cpp EffectTransmitted\n"); + DEBUG_LOG("AddObject at SpellEfects.cpp EffectTransmitted"); //m_caster->AddGameObject(pGameObj); //m_ObjToDel.push_back(pGameObj); @@ -6071,9 +6129,9 @@ void Spell::EffectTransmitted(uint32 effIndex) { GameObject* linkedGO = new GameObject; if(linkedGO->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), linkedEntry, cMap, - fx, fy, fz, m_caster->GetOrientation(), 0, 0, 0, 0, 100, 1)) + m_caster->GetPhaseMask(), fx, fy, fz, m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 100, 1)) { - linkedGO->SetRespawnTime(duration > 0 ? duration/1000 : 0); + linkedGO->SetRespawnTime(duration > 0 ? duration/IN_MILISECONDS : 0); //linkedGO->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel() ); linkedGO->SetSpellId(m_spellInfo->Id); linkedGO->SetOwnerGUID(m_caster->GetGUID() ); @@ -6111,6 +6169,28 @@ void Spell::EffectProspecting(uint32 /*i*/) ((Player*)m_caster)->SendLoot(itemTarget->GetGUID(), LOOT_PROSPECTING); } +void Spell::EffectMilling(uint32 /*i*/) +{ + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + Player* p_caster = (Player*)m_caster; + if(!itemTarget || !(itemTarget->GetProto()->BagFamily & BAG_FAMILY_MASK_HERBS)) + return; + + if(itemTarget->GetCount() < 5) + return; + + if( sWorld.getConfig(CONFIG_SKILL_MILLING)) + { + uint32 SkillValue = p_caster->GetPureSkillValue(SKILL_INSCRIPTION); + uint32 reqSkillValue = itemTarget->GetProto()->RequiredSkillRank; + p_caster->UpdateGatherSkill(SKILL_INSCRIPTION, SkillValue, reqSkillValue); + } + + ((Player*)m_caster)->SendLoot(itemTarget->GetGUID(), LOOT_MILLING); +} + void Spell::EffectSkill(uint32 /*i*/) { sLog.outDebug("WORLD: SkillEFFECT"); @@ -6118,26 +6198,34 @@ void Spell::EffectSkill(uint32 /*i*/) void Spell::EffectSummonDemon(uint32 i) { - float px = m_targets.m_destX; - float py = m_targets.m_destY; - float pz = m_targets.m_destZ; + float radius = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); - Creature* Charmed = m_caster->SummonCreature(m_spellInfo->EffectMiscValue[i], px, py, pz, m_caster->GetOrientation(),TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,3600000); - if (!Charmed) - return; + int32 amount = damage > 0 ? damage : 1; + + for(int32 count = 0; count < amount; ++count) + { + float px, py, pz; + GetSummonPosition(px, py, pz, radius, count); - // might not always work correctly, maybe the creature that dies from CoD casts the effect on itself and is therefore the caster? - Charmed->SetLevel(m_caster->getLevel()); + int32 duration = GetSpellDuration(m_spellInfo); - // TODO: Add damage/mana/hp according to level + Creature* Charmed = m_caster->SummonCreature(m_spellInfo->EffectMiscValue[i], px, py, pz, m_caster->GetOrientation(),TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,duration); + if (!Charmed) // something fatal, not attempt more + return; - if (m_spellInfo->EffectMiscValue[i] == 89) // Inferno summon - { - // Enslave demon effect, without mana cost and cooldown - m_caster->CastSpell(Charmed, 20882, true); // FIXME: enslave does not scale with level, level 62+ minions cannot be enslaved + // might not always work correctly, maybe the creature that dies from CoD casts the effect on itself and is therefore the caster? + Charmed->SetLevel(m_caster->getLevel()); - // Inferno effect - Charmed->CastSpell(Charmed, 22703, true, 0); + // TODO: Add damage/mana/hp according to level + + if (m_spellInfo->EffectMiscValue[i] == 89) // Inferno summon + { + // Enslave demon effect, without mana cost and cooldown + m_caster->CastSpell(Charmed, 20882, true); // FIXME: enslave does not scale with level, level 62+ minions cannot be enslaved + + // Inferno effect + Charmed->CastSpell(Charmed, 22703, true, 0); + } } } @@ -6179,7 +6267,7 @@ void Spell::EffectStealBeneficialBuff(uint32 i) if(!unitTarget || unitTarget==m_caster) // can't steal from self return; - std::vector <Aura *> steal_list; + std::list <Aura *> steal_list; // Create dispel mask by dispel type uint32 dispelMask = GetDispellMask( DispelType(m_spellInfo->EffectMiscValue[i]) ); Unit::AuraMap const& auras = unitTarget->GetAuras(); @@ -6189,53 +6277,38 @@ void Spell::EffectStealBeneficialBuff(uint32 i) if (aur && (1<<aur->GetSpellProto()->Dispel) & dispelMask) { // Need check for passive? this - if (aur->IsPositive() && !aur->IsPassive()) + if (aur->IsPositive() && !aur->IsPassive() && !(aur->GetSpellProto()->AttributesEx4 & SPELL_ATTR_EX4_NOT_STEALABLE)) steal_list.push_back(aur); } } // Ok if exist some buffs for dispel try dispel it - if (!steal_list.empty()) + if (uint32 list_size = steal_list.size()) { - std::list < std::pair<uint32,uint64> > success_list; - int32 list_size = steal_list.size(); + std::list < Aura * > success_list; + // dispel N = damage buffs (or while exist buffs for dispel) - for (int32 count=0; count < damage && list_size > 0; ++count) + for (int32 count=0; count < damage && list_size > 0; ++count, list_size = steal_list.size()) { // Random select buff for dispel - Aura *aur = steal_list[urand(0, list_size-1)]; - // Not use chance for steal - // TODO possible need do it - success_list.push_back( std::pair<uint32,uint64>(aur->GetId(),aur->GetCasterGUID())); - - // Remove buff from list for prevent doubles - for (std::vector<Aura *>::iterator j = steal_list.begin(); j != steal_list.end(); ) - { - Aura *stealed = *j; - if (stealed->GetId() == aur->GetId() && stealed->GetCasterGUID() == aur->GetCasterGUID()) - { - j = steal_list.erase(j); - --list_size; - } - else - ++j; - } + std::list < Aura * > ::iterator itr = steal_list.begin(); + for (uint32 i=urand(0, list_size-1);i>0;--i) + itr++; + success_list.push_back(*itr); + steal_list.erase(itr); } - // Really try steal and send log - if (!success_list.empty()) + if (success_list.size()) { - int32 count = success_list.size(); - WorldPacket data(SMSG_SPELLSTEALLOG, 8+8+4+1+4+count*5); + WorldPacket data(SMSG_SPELLSTEALLOG, 8+8+4+1+4+damage*5); data.append(unitTarget->GetPackGUID()); // Victim GUID data.append(m_caster->GetPackGUID()); // Caster GUID data << uint32(m_spellInfo->Id); // dispel spell id data << uint8(0); // not used - data << uint32(count); // count - for (std::list<std::pair<uint32,uint64> >::iterator j = success_list.begin(); j != success_list.end(); ++j) + data << uint32(success_list.size()); // count + for (std::list < Aura * > ::iterator itr = success_list.begin();itr!=success_list.end();++itr) { - SpellEntry const* spellInfo = sSpellStore.LookupEntry(j->first); - data << uint32(spellInfo->Id); // Spell Id + data << uint32((*itr)->GetId()); // Spell Id data << uint8(0); // 0 - steals !=0 transfers - unitTarget->RemoveAurasDueToSpellBySteal(spellInfo->Id, j->second, m_caster); + unitTarget->RemoveAurasDueToSpellBySteal((*itr)->GetId(), (*itr)->GetCasterGUID(), m_caster); } m_caster->SendMessageToSet(&data, true); } @@ -6247,7 +6320,7 @@ void Spell::EffectKillCredit(uint32 i) if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) return; - ((Player*)unitTarget)->KilledMonster(m_spellInfo->EffectMiscValue[i], 0); + ((Player*)unitTarget)->RewardPlayerAndGroupAtEvent(m_spellInfo->EffectMiscValue[i], unitTarget); } void Spell::EffectQuestFail(uint32 i) @@ -6258,9 +6331,221 @@ void Spell::EffectQuestFail(uint32 i) ((Player*)unitTarget)->FailQuest(m_spellInfo->EffectMiscValue[i]); } +void Spell::EffectActivateRune(uint32 eff_idx) +{ + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + Player *plr = (Player*)m_caster; + + if(plr->getClass() != CLASS_DEATH_KNIGHT) + return; + + for(uint32 j = 0; j < MAX_RUNES; ++j) + { + if(plr->GetRuneCooldown(j) && plr->GetCurrentRune(j) == m_spellInfo->EffectMiscValue[eff_idx]) + { + plr->SetRuneCooldown(j, 0); + } + } +} + +void Spell::EffectTitanGrip(uint32 /*eff_idx*/) +{ + if (unitTarget && unitTarget->GetTypeId() == TYPEID_PLAYER) + ((Player*)unitTarget)->SetCanTitanGrip(true); +} + void Spell::EffectRedirectThreat(uint32 /*i*/) { if(unitTarget) m_caster->SetReducedThreatPercent((uint32)damage, unitTarget->GetGUID()); } +void Spell::SummonTotem(uint32 entry, SummonPropertiesEntry const *properties) +{ + int8 slot = (int8)properties->Slot; + + if(slot >= SUMMON_SLOT_TOTEM && slot < MAX_TOTEM_SLOT) + { + uint64 guid = m_caster->m_SummonSlot[slot]; + if(guid != 0) + { + Creature *OldTotem = ObjectAccessor::GetCreature(*m_caster, guid); + if(OldTotem && OldTotem->isTotem()) + ((Totem*)OldTotem)->UnSummon(); + } + } + + uint32 team = 0; + if (m_caster->GetTypeId()==TYPEID_PLAYER) + team = ((Player*)m_caster)->GetTeam(); + + Totem* pTotem = new Totem(properties, m_caster); + + if(!pTotem->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), m_caster->GetMap(), m_caster->GetPhaseMask(), + entry, team )) + { + delete pTotem; + return; + } + + float x,y,z; + GetSummonPosition(x, y, z); + + // totem must be at same Z in case swimming caster and etc. + if( fabs( z - m_caster->GetPositionZ() ) > 5 ) + z = m_caster->GetPositionZ(); + + pTotem->Relocate(x, y, z, m_caster->GetOrientation()); + + if(slot >= SUMMON_SLOT_TOTEM && slot < MAX_TOTEM_SLOT) + m_caster->m_SummonSlot[slot] = pTotem->GetGUID(); + + pTotem->SetOwner(m_caster->GetGUID()); + pTotem->SetTypeBySummonSpell(m_spellInfo); // must be after Create call where m_spells initilized + + int32 duration=GetSpellDuration(m_spellInfo); + if(Player* modOwner = m_caster->GetSpellModOwner()) + modOwner->ApplySpellMod(m_spellInfo->Id,SPELLMOD_DURATION, duration); + pTotem->SetDuration(duration); + + if (damage) // if not spell info, DB values used + { + pTotem->SetMaxHealth(damage); + pTotem->SetHealth(damage); + } + + pTotem->SetUInt32Value(UNIT_CREATED_BY_SPELL,m_spellInfo->Id); + + if(m_caster->GetTypeId() == TYPEID_PLAYER) + pTotem->SetFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_PVP_ATTACKABLE); + + pTotem->Summon(m_caster); + + if(slot >= SUMMON_SLOT_TOTEM && slot < MAX_TOTEM_SLOT && m_caster->GetTypeId() == TYPEID_PLAYER) + { + WorldPacket data(SMSG_TOTEM_CREATED, 1+8+4+4); + data << uint8(slot-1); + data << uint64(pTotem->GetGUID()); + data << uint32(duration); + data << uint32(m_spellInfo->Id); + ((Player*)m_caster)->SendDirectMessage(&data); + } +} + +void Spell::SummonGuardian(uint32 entry, SummonPropertiesEntry const *properties) +{ + Unit *caster = m_originalCaster; + if(caster && caster->GetTypeId() == TYPEID_UNIT && ((Creature*)caster)->isTotem()) + caster = caster->GetOwner(); + if(!caster) + return; + + // in another case summon new + uint32 level = caster->getLevel(); + + // level of pet summoned using engineering item based at engineering skill level + if(m_CastItem && caster->GetTypeId() == TYPEID_PLAYER) + { + ItemPrototype const *proto = m_CastItem->GetProto(); + if(proto && proto->RequiredSkill == SKILL_ENGINERING) + { + uint16 skill202 = ((Player*)caster)->GetSkillValue(SKILL_ENGINERING); + if(skill202) + { + level = skill202/5; + } + } + } + + //float radius = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + float radius = 5.0f; + int32 amount = damage > 0 ? damage : 1; + int32 duration = GetSpellDuration(m_spellInfo); + TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN; + Map *map = caster->GetMap(); + + for(int32 count = 0; count < amount; ++count) + { + float px, py, pz; + GetSummonPosition(px, py, pz, radius, count); + + TempSummon *summon = map->SummonCreature(entry, px, py, pz, m_caster->GetOrientation(), properties, duration, caster); + if(!summon) + return; + + if(summon->HasSummonMask(SUMMON_MASK_GUARDIAN)) + ((Guardian*)summon)->InitStatsForLevel(level); + + summon->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); + } +} + +void Spell::SummonPossessed(uint32 entry, SummonPropertiesEntry const *properties) +{ + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + uint32 level = m_caster->getLevel(); + + float x, y, z; + m_caster->GetClosePoint(x, y, z, DEFAULT_WORLD_OBJECT_SIZE); + + int32 duration = GetSpellDuration(m_spellInfo); + + Pet* pet = ((Player*)m_caster)->SummonPet(entry, x, y, z, m_caster->GetOrientation(), POSSESSED_PET, duration); + if(!pet) + return; + + pet->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); + pet->SetCharmedOrPossessedBy(m_caster, true); +} + +void Spell::SummonVehicle(uint32 entry, SummonPropertiesEntry const *properties) +{ + float x, y, z; + m_caster->GetClosePoint(x, y, z, DEFAULT_WORLD_OBJECT_SIZE); + Vehicle *vehicle = m_caster->SummonVehicle(entry, x, y, z, m_caster->GetOrientation()); + if(!vehicle) + return; + + vehicle->SetOwnerGUID(m_caster->GetGUID()); + vehicle->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); + + if(damage) + { + m_caster->CastSpell(vehicle, damage, true); + if(m_caster->GetTypeId() == TYPEID_PLAYER) + ((Player*)m_caster)->EnterVehicle(vehicle); + } +} + +void Spell::GetSummonPosition(float &x, float &y, float &z, float radius, uint32 count) +{ + if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) + { + // Summon 1 unit in dest location + if (count == 0) + { + x = m_targets.m_destX; + y = m_targets.m_destY; + z = m_targets.m_destZ; + } + // Summon in random point all other units if location present + else + m_caster->GetRandomPoint(m_targets.m_destX,m_targets.m_destY,m_targets.m_destZ,radius,x,y,z); + } + // Summon if dest location not present near caster + else + m_caster->GetClosePoint(x,y,z,3.0f); +} + +void Spell::EffectRenamePet(uint32 /*eff_idx*/) +{ + if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT || + !((Creature*)unitTarget)->isPet() || ((Pet*)unitTarget)->getPetType() != HUNTER_PET) + return; + + unitTarget->SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_ALLOWED); +} diff --git a/src/game/SpellHandler.cpp b/src/game/SpellHandler.cpp index 3da05d37f3d..19a33dff75a 100644 --- a/src/game/SpellHandler.cpp +++ b/src/game/SpellHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,36 +19,40 @@ */ #include "Common.h" -#include "Database/DBCStores.h" +#include "DBCStores.h" #include "WorldPacket.h" #include "WorldSession.h" -#include "World.h" #include "ObjectMgr.h" #include "SpellMgr.h" #include "Log.h" #include "Opcodes.h" #include "Spell.h" -#include "SpellAuras.h" -#include "BattleGround.h" -#include "MapManager.h" #include "ScriptCalls.h" #include "Totem.h" #include "TemporarySummon.h" +#include "SpellAuras.h" void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket) { // TODO: add targets.read() check - CHECK_PACKET_SIZE(recvPacket,1+1+1+1+8); + CHECK_PACKET_SIZE(recvPacket,1+1+1+4+8+4+1); Player* pUser = _player; + + // ignore for remote control state + if(pUser->m_mover != pUser) + return; + uint8 bagIndex, slot; - uint8 spell_count; // number of spells at item, not used + uint8 unk_flags; // flags (if 0x02 - some additional data are received) uint8 cast_count; // next cast if exists (single or not) uint64 item_guid; + uint32 glyphIndex; // something to do with glyphs? + uint32 spellid; // casted spell id - recvPacket >> bagIndex >> slot >> spell_count >> cast_count >> item_guid; + recvPacket >> bagIndex >> slot >> cast_count >> spellid >> item_guid >> glyphIndex >> unk_flags; - Item *pItem = pUser->GetItemByPos(bagIndex, slot); + Item *pItem = pUser->GetUseableItemByPos(bagIndex, slot); if(!pItem) { pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL ); @@ -61,7 +65,7 @@ void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket) return; } - sLog.outDetail("WORLD: CMSG_USE_ITEM packet, bagIndex: %u, slot: %u, spell_count: %u , cast_count: %u, Item: %u, data length = %i", bagIndex, slot, spell_count, cast_count, pItem->GetEntry(), recvPacket.size()); + sLog.outDetail("WORLD: CMSG_USE_ITEM packet, bagIndex: %u, slot: %u, cast_count: %u, spellid: %u, Item: %u, glyphIndex: %u, unk_flags: %u, data length = %i", bagIndex, slot, cast_count, spellid, pItem->GetEntry(), glyphIndex, unk_flags, (uint32)recvPacket.size()); ItemPrototype const *proto = pItem->GetProto(); if(!proto) @@ -95,7 +99,7 @@ void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket) if (pUser->isInCombat()) { - for(int i = 0; i < 5; ++i) + for(int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) { if (SpellEntry const *spellInfo = sSpellStore.LookupEntry(proto->Spells[i].SpellId)) { @@ -126,57 +130,7 @@ void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket) if(!Script->ItemUse(pUser,pItem,targets)) { // no script or script not process request by self - - // special learning case - if(pItem->GetProto()->Spells[0].SpellId==SPELL_ID_GENERIC_LEARN) - { - uint32 learning_spell_id = pItem->GetProto()->Spells[1].SpellId; - - SpellEntry const *spellInfo = sSpellStore.LookupEntry(SPELL_ID_GENERIC_LEARN); - if(!spellInfo) - { - sLog.outError("Item (Entry: %u) in have wrong spell id %u, ignoring ",proto->ItemId, SPELL_ID_GENERIC_LEARN); - pUser->SendEquipError(EQUIP_ERR_NONE,pItem,NULL); - return; - } - - Spell *spell = new Spell(pUser, spellInfo, false); - spell->m_CastItem = pItem; - spell->m_cast_count = cast_count; //set count of casts - spell->m_currentBasePoints[0] = learning_spell_id; - spell->prepare(&targets); - return; - } - - // use triggered flag only for items with many spell casts and for not first cast - int count = 0; - - for(int i = 0; i < 5; ++i) - { - _Spell const& spellData = pItem->GetProto()->Spells[i]; - - // no spell - if(!spellData.SpellId) - continue; - - // wrong triggering type - if( spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_USE && spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_NO_DELAY_USE) - continue; - - SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellData.SpellId); - if(!spellInfo) - { - sLog.outError("Item (Entry: %u) in have wrong spell id %u, ignoring ",proto->ItemId, spellData.SpellId); - continue; - } - - Spell *spell = new Spell(pUser, spellInfo, (count > 0)); - spell->m_CastItem = pItem; - spell->m_cast_count = cast_count; //set count of casts - spell->prepare(&targets); - - ++count; - } + pUser->CastItemUseSpell(pItem,targets,cast_count,glyphIndex); } } @@ -190,9 +144,14 @@ void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket) { CHECK_PACKET_SIZE(recvPacket,1+1); - sLog.outDetail("WORLD: CMSG_OPEN_ITEM packet, data length = %i",recvPacket.size()); + sLog.outDetail("WORLD: CMSG_OPEN_ITEM packet, data length = %i",(uint32)recvPacket.size()); Player* pUser = _player; + + // ignore for remote control state + if(pUser->m_mover != pUser) + return; + uint8 bagIndex, slot; recvPacket >> bagIndex >> slot; @@ -227,7 +186,7 @@ void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket) } // required picklocking - if(lockInfo->requiredlockskill || lockInfo->requiredminingskill) + if(lockInfo->Skill[1] || lockInfo->Skill[0]) { pUser->SendEquipError(EQUIP_ERR_ITEM_LOCKED, pItem, NULL ); return; @@ -270,6 +229,11 @@ void WorldSession::HandleGameObjectUseOpcode( WorldPacket & recv_data ) recv_data >> guid; sLog.outDebug( "WORLD: Recvd CMSG_GAMEOBJ_USE Message [guid=%u]", GUID_LOPART(guid)); + + // ignore for remote control state + if(_player->m_mover != _player) + return; + GameObject *obj = ObjectAccessor::GetGameObject(*_player, guid); if(!obj) @@ -281,17 +245,46 @@ void WorldSession::HandleGameObjectUseOpcode( WorldPacket & recv_data ) obj->Use(_player); } +void WorldSession::HandleGameobjectReportUse(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket,8); + + uint64 guid; + recvPacket >> guid; + + sLog.outDebug( "WORLD: Recvd CMSG_GAMEOBJ_REPORT_USE Message [in game guid: %u]", GUID_LOPART(guid)); + + // ignore for remote control state + if(_player->m_mover != _player) + return; + + GameObject* go = ObjectAccessor::GetGameObject(*_player,guid); + if(!go) + return; + + if(!go->IsWithinDistInMap(_player,INTERACTION_DISTANCE)) + return; + + _player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT, go->GetEntry()); +} + void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) { - CHECK_PACKET_SIZE(recvPacket,4+1+2); + CHECK_PACKET_SIZE(recvPacket,1+4+1); uint32 spellId; - uint8 cast_count; - recvPacket >> spellId; + uint8 cast_count, unk_flags; recvPacket >> cast_count; + recvPacket >> spellId; + recvPacket >> unk_flags; // flags (if 0x02 - some additional data are received) + + // ignore for remote control state (for player case) + Unit* mover = _player->m_mover; + if(mover != _player && mover->GetTypeId()==TYPEID_PLAYER) + return; - sLog.outDebug("WORLD: got cast spell packet, spellId - %u, cast_count: %u data length = %i", - spellId, cast_count, recvPacket.size()); + sLog.outDebug("WORLD: got cast spell packet, spellId - %u, cast_count: %u, unk_flags %u, data length = %i", + spellId, cast_count, unk_flags, (uint32)recvPacket.size()); SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId ); @@ -301,11 +294,23 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) return; } - // not have spell or spell passive and not casted by client - if ( !_player->HasSpell (spellId) || IsPassiveSpell(spellId) ) + if(mover->GetTypeId()==TYPEID_PLAYER) { - //cheater? kick? ban? - return; + // not have spell in spellbook or spell passive and not casted by client + if (!((Player*)mover)->HasActiveSpell (spellId) || IsPassiveSpell(spellId) ) + { + //cheater? kick? ban? + return; + } + } + else + { + // not have spell in spellbook or spell passive and not casted by client + if (!((Creature*)mover)->HasSpell(spellId) || IsPassiveSpell(spellId) ) + { + //cheater? kick? ban? + return; + } } // can't use our own spells when we're in possession of another unit, @@ -314,7 +319,7 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) // client provided targets SpellCastTargets targets; - if(!targets.read(&recvPacket,_player)) + if(!targets.read(&recvPacket,mover)) return; // auto-selection buff level base at target level (in spellInfo) @@ -327,30 +332,42 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) spellInfo = actualSpellInfo; } - Spell *spell = new Spell(_player, spellInfo, false); + Spell *spell = new Spell(mover, spellInfo, false); spell->m_cast_count = cast_count; // set count of casts spell->prepare(&targets); } void WorldSession::HandleCancelCastOpcode(WorldPacket& recvPacket) { - CHECK_PACKET_SIZE(recvPacket,4); + CHECK_PACKET_SIZE(recvPacket,5); + + // ignore for remote control state (for player case) + Unit* mover = _player->m_mover; + if(mover != _player && mover->GetTypeId()==TYPEID_PLAYER) + return; + // increments with every CANCEL packet, don't use for now + uint8 counter; uint32 spellId; + recvPacket >> counter; recvPacket >> spellId; //FIXME: hack, ignore unexpected client cancel Deadly Throw cast if(spellId==26679) return; - if(_player->IsNonMeleeSpellCasted(false)) - _player->InterruptNonMeleeSpells(false,spellId); + if(mover->IsNonMeleeSpellCasted(false)) + mover->InterruptNonMeleeSpells(false,spellId); } 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; @@ -362,49 +379,27 @@ void WorldSession::HandleCancelAuraOpcode( WorldPacket& recvPacket) if(!IsPositiveSpell(spellId) || (spellInfo->Attributes & SPELL_ATTR_CANT_CANCEL)) return; - // lifebloom must delete final heal effect - if (spellInfo->SpellFamilyName == SPELLFAMILY_DRUID && (spellInfo->SpellFamilyFlags & 0x1000000000LL) ) - { - Unit::AuraMap::iterator iter; - while((iter = _player->m_Auras.find(Unit::spellEffectPair(spellId, 1))) != _player->m_Auras.end()) - { - _player->m_modAuras[SPELL_AURA_DUMMY].remove(iter->second); - - Aura* Aur = iter->second; - _player->m_Auras.erase(iter); - ++_player->m_removedAuras; // internal count used by unit update - - delete Aur; - - if( _player->m_Auras.empty() ) - iter = _player->m_Auras.end(); - else - iter = _player->m_Auras.begin(); - - } - } - // channeled spell case (it currently casted then) - if(IsChanneledSpell(spellInfo)) + if (IsChanneledSpell(spellInfo)) { - if(Spell* spell = _player->m_currentSpells[CURRENT_CHANNELED_SPELL]) - { - if(spell->m_spellInfo->Id==spellId) - { - spell->cancel(); - } - } + if (_player->m_currentSpells[CURRENT_CHANNELED_SPELL] && + _player->m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo->Id==spellId) + _player->InterruptSpell(CURRENT_CHANNELED_SPELL); return; } // non channeled case - _player->RemoveAurasDueToSpellByCancel(spellId); + _player->RemoveAurasDueToSpell(spellId, _player->GetGUID(), AURA_REMOVE_BY_CANCEL); } void WorldSession::HandlePetCancelAuraOpcode( WorldPacket& recvPacket) { CHECK_PACKET_SIZE(recvPacket, 8+4); + // ignore for remote control state + if(_player->m_mover != _player) + return; + uint64 guid; uint32 spellId; @@ -418,7 +413,7 @@ void WorldSession::HandlePetCancelAuraOpcode( WorldPacket& recvPacket) return; } - Creature* pet=ObjectAccessor::GetCreatureOrPet(*_player,guid); + Creature* pet=ObjectAccessor::GetCreatureOrPetOrVehicle(*_player,guid); if(!pet) { @@ -426,7 +421,7 @@ void WorldSession::HandlePetCancelAuraOpcode( WorldPacket& recvPacket) return; } - if(pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharm()) + if(pet != GetPlayer()->GetGuardianPet() && pet != GetPlayer()->GetCharm()) { sLog.outError( "HandlePetCancelAura.Pet %u isn't pet of player %s", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); return; @@ -452,7 +447,7 @@ void WorldSession::HandleCancelAutoRepeatSpellOpcode( WorldPacket& /*recvPacket* { // may be better send SMSG_CANCEL_AUTO_REPEAT? // cancel and prepare for deleting - _player->InterruptSpell(CURRENT_AUTOREPEAT_SPELL); + _player->m_mover->InterruptSpell(CURRENT_AUTOREPEAT_SPELL); } /// \todo Complete HandleCancelChanneling function @@ -470,17 +465,22 @@ void WorldSession::HandleTotemDestroy( WorldPacket& recvPacket) { CHECK_PACKET_SIZE(recvPacket, 1); + // ignore for remote control state + if(_player->m_mover != _player) + return; + uint8 slotId; recvPacket >> slotId; - if (slotId >= MAX_TOTEM) + ++slotId; + if (slotId >= MAX_TOTEM_SLOT) return; - if(!_player->m_TotemSlot[slotId]) + if(!_player->m_SummonSlot[slotId]) return; - Creature* totem = ObjectAccessor::GetCreature(*_player,_player->m_TotemSlot[slotId]); + Creature* totem = ObjectAccessor::GetCreature(*_player,_player->m_SummonSlot[slotId]); // Don't unsummon sentry totem if(totem && totem->isTotem() && totem->GetEntry() != SENTRY_TOTEM_ENTRY) ((Totem*)totem)->UnSummon(); diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp index 30112caa75a..af8a04740f8 100644 --- a/src/game/SpellMgr.cpp +++ b/src/game/SpellMgr.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,10 +22,11 @@ #include "ObjectMgr.h" #include "SpellAuraDefines.h" #include "ProgressBar.h" -#include "Database/DBCStores.h" +#include "DBCStores.h" #include "World.h" #include "Chat.h" #include "Spell.h" +#include "BattleGroundMgr.h" bool IsAreaEffectTarget[TOTAL_SPELL_TARGETS]; @@ -38,25 +39,17 @@ SpellMgr::SpellMgr() case SPELL_EFFECT_PERSISTENT_AREA_AURA: //27 case SPELL_EFFECT_SUMMON: //28 case SPELL_EFFECT_TRIGGER_MISSILE: //32 - case SPELL_EFFECT_SUMMON_WILD: //41 - case SPELL_EFFECT_SUMMON_GUARDIAN: //42 case SPELL_EFFECT_TRANS_DOOR: //50 summon object case SPELL_EFFECT_SUMMON_PET: //56 case SPELL_EFFECT_ADD_FARSIGHT: //72 - case SPELL_EFFECT_SUMMON_POSSESSED: //73 - case SPELL_EFFECT_SUMMON_TOTEM: //74 case SPELL_EFFECT_SUMMON_OBJECT_WILD: //76 - case SPELL_EFFECT_SUMMON_TOTEM_SLOT1: //87 - case SPELL_EFFECT_SUMMON_TOTEM_SLOT2: //88 - case SPELL_EFFECT_SUMMON_TOTEM_SLOT3: //89 - case SPELL_EFFECT_SUMMON_TOTEM_SLOT4: //90 - case SPELL_EFFECT_SUMMON_CRITTER: //97 + //case SPELL_EFFECT_SUMMON_CRITTER: //97 not 303 case SPELL_EFFECT_SUMMON_OBJECT_SLOT1: //104 case SPELL_EFFECT_SUMMON_OBJECT_SLOT2: //105 case SPELL_EFFECT_SUMMON_OBJECT_SLOT3: //106 case SPELL_EFFECT_SUMMON_OBJECT_SLOT4: //107 case SPELL_EFFECT_SUMMON_DEAD_PET: //109 - case SPELL_EFFECT_SUMMON_DEMON: //112 + //case SPELL_EFFECT_SUMMON_DEMON: //112 not 303 case SPELL_EFFECT_TRIGGER_SPELL_2: //151 ritual of summon EffectTargetType[i] = SPELL_REQUIRE_DEST; break; @@ -73,6 +66,8 @@ SpellMgr::SpellMgr() case SPELL_EFFECT_DISENCHANT: case SPELL_EFFECT_FEED_PET: case SPELL_EFFECT_PROSPECTING: + case SPELL_EFFECT_MILLING: + case SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC: EffectTargetType[i] = SPELL_REQUIRE_ITEM; break; //caster must be pushed otherwise no sound @@ -81,6 +76,7 @@ SpellMgr::SpellMgr() case SPELL_EFFECT_APPLY_AREA_AURA_ENEMY: case SPELL_EFFECT_APPLY_AREA_AURA_PET: case SPELL_EFFECT_APPLY_AREA_AURA_OWNER: + case SPELL_EFFECT_JUMP2: //42 EffectTargetType[i] = SPELL_REQUIRE_CASTER; break; default: @@ -105,7 +101,6 @@ SpellMgr::SpellMgr() case TARGET_UNIT_TARGET_ALLY: case TARGET_UNIT_TARGET_RAID: case TARGET_UNIT_TARGET_ANY: - case TARGET_UNIT_SINGLE_UNKNOWN: case TARGET_UNIT_TARGET_ENEMY: case TARGET_UNIT_TARGET_PARTY: case TARGET_UNIT_PARTY_TARGET: @@ -235,6 +230,21 @@ int32 GetSpellMaxDuration(SpellEntry const *spellInfo) return (du->Duration[2] == -1) ? -1 : abs(du->Duration[2]); } +bool GetDispelChance(Unit* caster, uint32 spellId) +{ + // we assume that aura dispel chance is 100% on start + // need formula for level difference based chance + int32 miss_chance = 0; + // Apply dispel mod from aura caster + if (caster) + { + if ( Player* modOwner = caster->GetSpellModOwner() ) + modOwner->ApplySpellMod(spellId, SPELLMOD_RESIST_DISPEL_CHANCE, miss_chance); + } + // Try dispel + return !roll_chance_i(miss_chance); +} + uint32 GetSpellCastTime(SpellEntry const* spellInfo, Spell const* spell) { SpellCastTimesEntry const *spellCastTimeEntry = sSpellCastTimesStore.LookupEntry(spellInfo->CastingTimeIndex); @@ -245,19 +255,8 @@ uint32 GetSpellCastTime(SpellEntry const* spellInfo, Spell const* spell) int32 castTime = spellCastTimeEntry->CastTime; - if (spell) - { - if(Player* modOwner = spell->GetCaster()->GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CASTING_TIME, castTime, spell); - - if( !(spellInfo->Attributes & (SPELL_ATTR_UNK4|SPELL_ATTR_UNK5)) ) - castTime = int32(castTime * spell->GetCaster()->GetFloatValue(UNIT_MOD_CAST_SPEED)); - else - { - if (spell->IsRangedSpell() && !spell->IsAutoRepeat()) - castTime = int32(castTime * spell->GetCaster()->m_modAttackSpeedPct[RANGED_ATTACK]); - } - } + if (spell && spell->GetCaster()) + spell->GetCaster()->ModSpellCastTime(spellInfo, castTime, spell); if (spellInfo->Attributes & SPELL_ATTR_RANGED && (!spell || !(spell->IsAutoRepeat()))) castTime += 500; @@ -273,32 +272,21 @@ bool IsPassiveSpell(uint32 spellId) return (spellInfo->Attributes & SPELL_ATTR_PASSIVE) != 0; } -/*bool IsNoStackAuraDueToAura(uint32 spellId_1, uint32 effIndex_1, uint32 spellId_2, uint32 effIndex_2) +bool IsAutocastableSpell(uint32 spellId) { - SpellEntry const *spellInfo_1 = sSpellStore.LookupEntry(spellId_1); - SpellEntry const *spellInfo_2 = sSpellStore.LookupEntry(spellId_2); - if(!spellInfo_1 || !spellInfo_2) return false; - if(spellInfo_1->Id == spellId_2) return false; - - if (spellInfo_1->Effect[effIndex_1] != spellInfo_2->Effect[effIndex_2] || - spellInfo_1->EffectItemType[effIndex_1] != spellInfo_2->EffectItemType[effIndex_2] || - spellInfo_1->EffectMiscValue[effIndex_1] != spellInfo_2->EffectMiscValue[effIndex_2] || - spellInfo_1->EffectApplyAuraName[effIndex_1] != spellInfo_2->EffectApplyAuraName[effIndex_2]) + SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId); + if(!spellInfo) + return false; + if(spellInfo->Attributes & SPELL_ATTR_PASSIVE) + return false; + if(spellInfo->AttributesEx & SPELL_ATTR_EX_UNAUTOCASTABLE_BY_PET) return false; - return true; -}*/ +} -int32 CompareAuraRanks(uint32 spellId_1, uint32 effIndex_1, uint32 spellId_2, uint32 effIndex_2) +bool IsHigherHankOfSpell(uint32 spellId_1, uint32 spellId_2) { - SpellEntry const*spellInfo_1 = sSpellStore.LookupEntry(spellId_1); - SpellEntry const*spellInfo_2 = sSpellStore.LookupEntry(spellId_2); - if(!spellInfo_1 || !spellInfo_2) return 0; - if (spellId_1 == spellId_2) return 0; - - int32 diff = spellInfo_1->EffectBasePoints[effIndex_1] - spellInfo_2->EffectBasePoints[effIndex_2]; - if (spellInfo_1->EffectBasePoints[effIndex_1]+1 < 0 && spellInfo_2->EffectBasePoints[effIndex_2]+1 < 0) return -diff; - else return diff; + return spellmgr.GetSpellRank(spellId_1)<spellmgr.GetSpellRank(spellId_2); } SpellSpecific GetSpellSpecific(uint32 spellId) @@ -315,31 +303,54 @@ SpellSpecific GetSpellSpecific(uint32 spellId) if (spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) { for(int i = 0; i < 3; i++) - if( spellInfo->EffectApplyAuraName[i]==SPELL_AURA_MOD_POWER_REGEN) + if( spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_POWER_REGEN + || spellInfo->EffectApplyAuraName[i] == SPELL_AURA_OBS_MOD_ENERGY) return SPELL_DRINK; - else if ( spellInfo->EffectApplyAuraName[i]==SPELL_AURA_MOD_REGEN) + else if ( spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_REGEN + || spellInfo->EffectApplyAuraName[i] == SPELL_AURA_OBS_MOD_HEALTH) return SPELL_FOOD; } // this may be a hack else if((spellInfo->AttributesEx2 & SPELL_ATTR_EX2_FOOD) && !spellInfo->Category) return SPELL_WELL_FED; - break; + // scrolls effects + else + { + uint32 firstSpell = spellmgr.GetFirstSpellInChain(spellInfo->Id); + switch (firstSpell) + { + // Strength + case 8118: + // Stamina + case 8099: + // Spirit + case 8112: + //Intellect + case 8096: + // Agility + case 8115: + // Armor + case 8091: + return SPELL_SCROLL; + } + break; + } } case SPELLFAMILY_MAGE: { // family flags 18(Molten), 25(Frost/Ice), 28(Mage) - if (spellInfo->SpellFamilyFlags & 0x12040000) + if (spellInfo->SpellFamilyFlags[0] & 0x12040000) return SPELL_MAGE_ARMOR; - if ((spellInfo->SpellFamilyFlags & 0x1000000) && spellInfo->EffectApplyAuraName[0]==SPELL_AURA_MOD_CONFUSE) + if ((spellInfo->SpellFamilyFlags[0] & 0x1000000) && spellInfo->EffectApplyAuraName[0]==SPELL_AURA_MOD_CONFUSE) return SPELL_MAGE_POLYMORPH; break; } case SPELLFAMILY_WARRIOR: { - if (spellInfo->SpellFamilyFlags & 0x00008000010000LL) + if (spellInfo->SpellFamilyFlags[1] & 0x000080 || spellInfo->SpellFamilyFlags[0] & 0x10000LL) return SPELL_POSITIVE_SHOUT; break; @@ -350,12 +361,12 @@ SpellSpecific GetSpellSpecific(uint32 spellId) if (spellInfo->Dispel == DISPEL_CURSE) return SPELL_CURSE; - // family flag 37 (only part spells have family name) - if (spellInfo->SpellFamilyFlags & 0x2000000000LL) + // Warlock (Demon Armor | Demon Skin | Fel Armor) + if (spellInfo->SpellFamilyFlags[1] & 0x20000020 || spellInfo->SpellFamilyFlags[2] & 0x00000010) return SPELL_WARLOCK_ARMOR; //seed of corruption and corruption - if (spellInfo->SpellFamilyFlags & 0x1000000002LL) + if (spellInfo->SpellFamilyFlags[1] & 0x10 || spellInfo->SpellFamilyFlags[0] & 0x2) return SPELL_WARLOCK_CORRUPTION; break; } @@ -365,6 +376,10 @@ SpellSpecific GetSpellSpecific(uint32 spellId) if (spellInfo->Dispel == DISPEL_POISON) return SPELL_STING; + // only hunter aspects have this + if( spellInfo->SpellFamilyFlags[1] & 0x00440000 || spellInfo->SpellFamilyFlags[0] & 0x00380000 || spellInfo->SpellFamilyFlags[2] & 0x00001010) + return SPELL_ASPECT; + break; } case SPELLFAMILY_PALADIN: @@ -372,16 +387,16 @@ SpellSpecific GetSpellSpecific(uint32 spellId) if (IsSealSpell(spellInfo)) return SPELL_SEAL; - if (spellInfo->SpellFamilyFlags & 0x10000100LL) + if (spellInfo->SpellFamilyFlags[0] & 0x11010002) return SPELL_BLESSING; - if ((spellInfo->SpellFamilyFlags & 0x00000820180400LL) && (spellInfo->AttributesEx3 & 0x200)) + if ((spellInfo->SpellFamilyFlags[1] & 0x000008 || spellInfo->SpellFamilyFlags[0] & 0x20180400) && (spellInfo->AttributesEx3 & 0x200)) return SPELL_JUDGEMENT; for (int i = 0; i < 3; i++) { - // only paladin auras have this - if (spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_PARTY) + // only paladin auras have this (for palaldin class family) + if (spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_RAID) return SPELL_AURA; } break; @@ -396,19 +411,11 @@ SpellSpecific GetSpellSpecific(uint32 spellId) case SPELLFAMILY_POTION: return spellmgr.GetSpellElixirSpecific(spellInfo->Id); - } - - // only warlock armor/skin have this (in additional to family cases) - if( spellInfo->SpellVisual == 130 && spellInfo->SpellIconID == 89) - { - return SPELL_WARLOCK_ARMOR; - } - // only hunter aspects have this (but not all aspects in hunter family) - if( spellInfo->activeIconID == 122 && (GetSpellSchoolMask(spellInfo) & SPELL_SCHOOL_MASK_NATURE) && - (spellInfo->Attributes & 0x50000) != 0 && (spellInfo->Attributes & 0x9000010) == 0) - { - return SPELL_ASPECT; + case SPELLFAMILY_DEATHKNIGHT: + if ((spellInfo->Attributes & 0x10) && (spellInfo->AttributesEx2 & 0x10) && (spellInfo->AttributesEx4 & 0x200000)) + return SPELL_PRESENCE; + break; } for(int i = 0; i < 3; ++i) @@ -417,18 +424,17 @@ SpellSpecific GetSpellSpecific(uint32 spellId) { switch(spellInfo->EffectApplyAuraName[i]) { - case SPELL_AURA_TRACK_CREATURES: - case SPELL_AURA_TRACK_RESOURCES: - case SPELL_AURA_TRACK_STEALTHED: - return SPELL_TRACKER; case SPELL_AURA_MOD_CHARM: case SPELL_AURA_MOD_POSSESS_PET: case SPELL_AURA_MOD_POSSESS: return SPELL_CHARM; + case SPELL_AURA_TRACK_CREATURES: + case SPELL_AURA_TRACK_RESOURCES: + case SPELL_AURA_TRACK_STEALTHED: + return SPELL_TRACKER; } } } - // elixirs can have different families, but potion most ofc. if(SpellSpecific sp = spellmgr.GetSpellElixirSpecific(spellInfo->Id)) return sp; @@ -464,10 +470,12 @@ bool IsSingleFromSpellSpecificPerTarget(uint32 spellSpec1,uint32 spellSpec2) case SPELL_MAGE_ARMOR: case SPELL_ELEMENTAL_SHIELD: case SPELL_MAGE_POLYMORPH: + case SPELL_PRESENCE: case SPELL_WELL_FED: case SPELL_DRINK: case SPELL_FOOD: case SPELL_CHARM: + case SPELL_SCROLL: return spellSpec1==spellSpec2; case SPELL_BATTLE_ELIXIR: return spellSpec2==SPELL_BATTLE_ELIXIR @@ -497,8 +505,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; } @@ -507,20 +513,13 @@ bool IsPositiveTarget(uint32 targetA, uint32 targetB) return true; } -bool IsPositiveEffect(uint32 spellId, uint32 effIndex) +bool IsPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) { SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId); if (!spellproto) return false; switch(spellId) { - case 23333: // BG spell - case 23335: // BG spell - case 34976: // BG spell - case 31579: // Arcane Empowerment Rank1 talent aura with one positive and one negative (check not needed in wotlk) - case 31582: // Arcane Empowerment Rank2 - case 31583: // Arcane Empowerment Rank3 - return true; case 28441: // not positive dummy spell case 37675: // Chaos Blast case 41519: // Mark of Stormrage @@ -528,6 +527,16 @@ bool IsPositiveEffect(uint32 spellId, uint32 effIndex) case 34700: // Allergic Reaction case 31719: // Suspension return false; + case 12042: // Arcane Power + return true; + } + + switch(spellproto->Mechanic) + { + case MECHANIC_IMMUNE_SHIELD: + return true; + default: + break; } switch(spellproto->Effect[effIndex]) @@ -553,6 +562,7 @@ bool IsPositiveEffect(uint32 spellId, uint32 effIndex) { case 13139: // net-o-matic special effect case 23445: // evil twin + case 35679: // Protectorate Demolitionist case 38637: // Nether Exhaustion (red) case 38638: // Nether Exhaustion (green) case 38639: // Nether Exhaustion (blue) @@ -564,15 +574,16 @@ bool IsPositiveEffect(uint32 spellId, uint32 effIndex) case SPELL_AURA_MOD_STAT: case SPELL_AURA_MOD_DAMAGE_DONE: // dependent from bas point sign (negative -> negative) case SPELL_AURA_MOD_HEALING_DONE: + case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE: { - if(spellproto->EffectBasePoints[effIndex]+int32(spellproto->EffectBaseDice[effIndex]) < 0) + if(spellproto->CalculateSimpleValue(effIndex) < 0) return false; break; } case SPELL_AURA_ADD_TARGET_TRIGGER: return true; case SPELL_AURA_PERIODIC_TRIGGER_SPELL: - if(spellId != spellproto->EffectTriggerSpell[effIndex]) + if(!deep) { uint32 spellTriggeredId = spellproto->EffectTriggerSpell[effIndex]; SpellEntry const *spellTriggeredProto = sSpellStore.LookupEntry(spellTriggeredId); @@ -582,14 +593,15 @@ bool IsPositiveEffect(uint32 spellId, uint32 effIndex) // non-positive targets of main spell return early for(int i = 0; i < 3; ++i) { + if (!spellTriggeredProto->Effect[i]) + continue; // if non-positive trigger cast targeted to positive target this main cast is non-positive // this will place this spell auras as debuffs - if(IsPositiveTarget(spellTriggeredProto->EffectImplicitTargetA[effIndex],spellTriggeredProto->EffectImplicitTargetB[effIndex]) && !IsPositiveEffect(spellTriggeredId,i)) + if(IsPositiveTarget(spellTriggeredProto->EffectImplicitTargetA[effIndex],spellTriggeredProto->EffectImplicitTargetB[effIndex]) && !IsPositiveEffect(spellTriggeredId,i, true)) return false; } } } - break; case SPELL_AURA_PROC_TRIGGER_SPELL: // many positive auras have negative triggered spells at damage for example and this not make it negative (it can be canceled for example) break; @@ -663,19 +675,35 @@ bool IsPositiveEffect(uint32 spellId, uint32 effIndex) switch(spellproto->EffectMiscValue[effIndex]) { case SPELLMOD_COST: // dependent from bas point sign (negative -> positive) - if(spellproto->EffectBasePoints[effIndex]+int32(spellproto->EffectBaseDice[effIndex]) > 0) - return false; + if(spellproto->CalculateSimpleValue(effIndex) > 0) + { + if (!deep) + { + bool negative = true; + for (uint8 i=0;i<MAX_SPELL_EFFECTS;++i) + { + if (i != effIndex) + if (IsPositiveEffect(spellId, i, true)) + { + negative = false; + break; + } + } + if (negative) + return false; + } + } break; default: break; } } break; case SPELL_AURA_MOD_HEALING_PCT: - if(spellproto->EffectBasePoints[effIndex]+int32(spellproto->EffectBaseDice[effIndex]) < 0) + if(spellproto->CalculateSimpleValue(effIndex) < 0) return false; break; case SPELL_AURA_MOD_SKILL: - if(spellproto->EffectBasePoints[effIndex]+int32(spellproto->EffectBaseDice[effIndex]) < 0) + if(spellproto->CalculateSimpleValue(effIndex) < 0) return false; break; case SPELL_AURA_FORCE_REACTION: @@ -699,11 +727,17 @@ bool IsPositiveEffect(uint32 spellId, uint32 effIndex) if(spellproto->AttributesEx & SPELL_ATTR_EX_NEGATIVE) return false; + if (!deep && spellproto->EffectTriggerSpell[effIndex] + && !spellproto->EffectApplyAuraName[effIndex] + && IsPositiveTarget(spellproto->EffectImplicitTargetA[effIndex],spellproto->EffectImplicitTargetB[effIndex]) + && !IsPositiveSpell(spellproto->EffectTriggerSpell[effIndex], true)) + return false; + // ok, positive return true; } -bool IsPositiveSpell(uint32 spellId) +bool IsPositiveSpell(uint32 spellId, bool deep) { SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId); if (!spellproto) return false; @@ -711,7 +745,7 @@ bool IsPositiveSpell(uint32 spellId) // spells with at least one negative effect are considered negative // some self-applied spells have negative effects but in self casting case negative check ignored. for (int i = 0; i < 3; i++) - if (!IsPositiveEffect(spellId, i)) + if (!IsPositiveEffect(spellId, i, deep)) return false; return true; } @@ -772,13 +806,13 @@ bool IsAuraAddedBySpell(uint32 auraType, uint32 spellId) return false; } -uint8 GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form) +SpellCastResult GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form) { // talents that learn spells can have stance requirements that need ignore // (this requirement only for client-side stance show in talent description) if( GetTalentSpellCost(spellInfo->Id) > 0 && (spellInfo->Effect[0]==SPELL_EFFECT_LEARN_SPELL || spellInfo->Effect[1]==SPELL_EFFECT_LEARN_SPELL || spellInfo->Effect[2]==SPELL_EFFECT_LEARN_SPELL) ) - return 0; + return SPELL_CAST_OK; uint32 stanceMask = (form ? 1 << (form - 1) : 0); @@ -786,7 +820,7 @@ uint8 GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form) return SPELL_FAILED_NOT_SHAPESHIFT; if (stanceMask & spellInfo->Stances) // can explicitly be casted in this stance - return 0; + return SPELL_CAST_OK; bool actAsShifted = false; if (form > 0) @@ -795,7 +829,7 @@ uint8 GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form) if (!shapeInfo) { sLog.outError("GetErrorAtShapeshiftedCast: unknown shapeshift %u", form); - return 0; + return SPELL_CAST_OK; } actAsShifted = !(shapeInfo->flags1 & 1); // shapeshift acts as normal form for spells } @@ -814,7 +848,7 @@ uint8 GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form) return SPELL_FAILED_ONLY_SHAPESHIFT; } - return 0; + return SPELL_CAST_OK; } void SpellMgr::LoadSpellTargetPositions() @@ -908,8 +942,8 @@ void SpellMgr::LoadSpellAffects() uint32 count = 0; - // 0 1 2 - QueryResult *result = WorldDatabase.Query("SELECT entry, effectId, SpellFamilyMask FROM spell_affect"); + // 0 1 2 3 4 + QueryResult *result = WorldDatabase.Query("SELECT entry, effectId, SpellClassMask0, SpellClassMask1, SpellClassMask2 FROM spell_affect"); if( !result ) { @@ -956,26 +990,22 @@ void SpellMgr::LoadSpellAffects() continue; } - uint64 spellAffectMask = fields[2].GetUInt64(); + flag96 affect(fields[2].GetUInt32(), fields[3].GetUInt32(), fields[4].GetUInt32()); - // Spell.dbc have own data for low part of SpellFamilyMask - if( spellInfo->EffectItemType[effectId]) - { - if(spellInfo->EffectItemType[effectId] == spellAffectMask) - { - sLog.outErrorDb("Spell %u listed in `spell_affect` have redundant (same with EffectItemType%d) data for effect index (%u) and not needed, skipped.", entry,effectId+1,effectId); - continue; - } + // Spell.dbc have own data + if (effectId>3) + continue; - // 24429 have wrong data in EffectItemType and overwrites by DB, possible bug in client - if(spellInfo->Id!=24429 && spellInfo->EffectItemType[effectId] != spellAffectMask) - { - sLog.outErrorDb("Spell %u listed in `spell_affect` have different low part from EffectItemType%d for effect index (%u) and not needed, skipped.", entry,effectId+1,effectId); - continue; - } + flag96 dbc_affect; + dbc_affect = spellInfo->EffectSpellClassMask[effectId]; + if(dbc_affect[0] == affect[0] || dbc_affect[1] == affect[1] || dbc_affect[2] == affect[2]) + { + char text[]="ABC"; + sLog.outErrorDb("Spell %u listed in `spell_affect` have redundant (same with EffectSpellClassMask%c) data for effect index (%u) and not needed, skipped.", entry, text[effectId], effectId); + continue; } - mSpellAffectMap.insert(SpellAffectMap::value_type((entry<<8) + effectId,spellAffectMask)); + mSpellAffectMap[(entry<<8) + effectId] = affect; ++count; } while( result->NextRow() ); @@ -983,7 +1013,7 @@ void SpellMgr::LoadSpellAffects() delete result; sLog.outString(); - sLog.outString( ">> Loaded %u spell affect definitions", count ); + sLog.outString( ">> Loaded %u custom spell affect definitions", count ); for (uint32 id = 0; id < sSpellStore.GetNumRows(); ++id) { @@ -999,7 +1029,9 @@ void SpellMgr::LoadSpellAffects() spellInfo->EffectApplyAuraName[effectId] != SPELL_AURA_ADD_TARGET_TRIGGER) ) continue; - if(spellInfo->EffectItemType[effectId] != 0) + flag96 dbc_affect; + dbc_affect = spellInfo->EffectSpellClassMask[effectId]; + if(dbc_affect) continue; if(mSpellAffectMap.find((id<<8) + effectId) != mSpellAffectMap.end()) @@ -1010,33 +1042,19 @@ void SpellMgr::LoadSpellAffects() } } -bool SpellMgr::IsAffectedBySpell(SpellEntry const *spellInfo, uint32 spellId, uint8 effectId, uint64 familyFlags) const +bool SpellMgr::IsAffectedByMod(SpellEntry const *spellInfo, SpellModifier *mod) const { // false for spellInfo == NULL - if (!spellInfo) + if (!spellInfo || !mod) return false; - SpellEntry const *affect_spell = sSpellStore.LookupEntry(spellId); - // false for affect_spell == NULL - if (!affect_spell) + SpellEntry const *affect_spell = sSpellStore.LookupEntry(mod->spellId); + // False if affect_spell == NULL or spellFamily not equal + if (!affect_spell || affect_spell->SpellFamilyName != spellInfo->SpellFamilyName) return false; - // False if spellFamily not equal - if (affect_spell->SpellFamilyName != spellInfo->SpellFamilyName) - return false; - - // If familyFlags == 0 - if (!familyFlags) - { - // Get it from spellAffect table - familyFlags = GetSpellAffectMask(spellId,effectId); - // false if familyFlags == 0 - if (!familyFlags) - return false; - } - // true - if (familyFlags & spellInfo->SpellFamilyFlags) + if (mod->mask & spellInfo->SpellFamilyFlags) return true; return false; @@ -1048,15 +1066,12 @@ void SpellMgr::LoadSpellProcEvents() uint32 count = 0; - // 0 1 2 3 4 5 6 7 8 - QueryResult *result = WorldDatabase.Query("SELECT entry, SchoolMask, SpellFamilyName, SpellFamilyMask, procFlags, procEx, ppmRate, CustomChance, Cooldown FROM spell_proc_event"); + // 0 1 2 3 4 5 6 7 8 9 10 + QueryResult *result = WorldDatabase.Query("SELECT entry, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, procFlags, procEx, ppmRate, CustomChance, Cooldown FROM spell_proc_event"); if( !result ) { - barGoLink bar( 1 ); - bar.step(); - sLog.outString(); sLog.outString( ">> Loaded %u spell proc event conditions", count ); return; @@ -1070,7 +1085,7 @@ void SpellMgr::LoadSpellProcEvents() bar.step(); - uint16 entry = fields[0].GetUInt16(); + uint32 entry = fields[0].GetUInt32(); const SpellEntry *spell = sSpellStore.LookupEntry(entry); if (!spell) @@ -1083,12 +1098,14 @@ void SpellMgr::LoadSpellProcEvents() spe.schoolMask = fields[1].GetUInt32(); spe.spellFamilyName = fields[2].GetUInt32(); - spe.spellFamilyMask = fields[3].GetUInt64(); - spe.procFlags = fields[4].GetUInt32(); - spe.procEx = fields[5].GetUInt32(); - spe.ppmRate = fields[6].GetFloat(); - spe.customChance = fields[7].GetFloat(); - spe.cooldown = fields[8].GetUInt32(); + spe.spellFamilyMask[0] = fields[3].GetUInt32(); + spe.spellFamilyMask[1] = fields[4].GetUInt32(); + spe.spellFamilyMask[2] = fields[5].GetUInt32(); + spe.procFlags = fields[6].GetUInt32(); + spe.procEx = fields[7].GetUInt32(); + spe.ppmRate = fields[8].GetFloat(); + spe.customChance = fields[9].GetFloat(); + spe.cooldown = fields[10].GetUInt32(); mSpellProcEventMap[entry] = spe; @@ -1108,82 +1125,56 @@ void SpellMgr::LoadSpellProcEvents() sLog.outString(); if (customProc) - sLog.outString( ">> Loaded %u custom spell proc event conditions +%u custom", count, customProc ); + sLog.outString( ">> Loaded %u extra spell proc event conditions +%u custom", count, customProc ); else - sLog.outString( ">> Loaded %u spell proc event conditions", count ); + sLog.outString( ">> Loaded %u extra spell proc event conditions", count ); +} - /* - // Commented for now, as it still produces many errors (still quite many spells miss spell_proc_event) - for (uint32 id = 0; id < sSpellStore.GetNumRows(); ++id) +void SpellMgr::LoadSpellBonusess() +{ + mSpellBonusMap.clear(); // need for reload case + uint32 count = 0; + // 0 1 2 3 + QueryResult *result = WorldDatabase.Query("SELECT entry, direct_bonus, dot_bonus, ap_bonus FROM spell_bonus_data"); + if( !result ) { - SpellEntry const* spellInfo = sSpellStore.LookupEntry(id); - if (!spellInfo) - continue; - - bool found = false; - for (int effectId = 0; effectId < 3; ++effectId) - { - // at this moment check only SPELL_AURA_PROC_TRIGGER_SPELL - if( spellInfo->EffectApplyAuraName[effectId] == SPELL_AURA_PROC_TRIGGER_SPELL ) - { - found = true; - break; - } - } + barGoLink bar( 1 ); + bar.step(); + sLog.outString(); + sLog.outString( ">> Loaded %u spell bonus data", count); + return; + } - if(!found) - continue; + barGoLink bar( result->GetRowCount() ); + do + { + Field *fields = result->Fetch(); + bar.step(); + uint32 entry = fields[0].GetUInt32(); - if(GetSpellProcEvent(id)) + const SpellEntry *spell = sSpellStore.LookupEntry(entry); + if (!spell) + { + sLog.outErrorDb("Spell %u listed in `spell_bonus_data` does not exist", entry); continue; + } - sLog.outErrorDb("Spell %u (%s) misses spell_proc_event",id,spellInfo->SpellName[sWorld.GetDBClang()]); - } - */ -} - -/* -bool SpellMgr::IsSpellProcEventCanTriggeredBy( SpellProcEventEntry const * spellProcEvent, SpellEntry const * procSpell, uint32 procFlags ) -{ - if((procFlags & spellProcEvent->procFlags) == 0) - return false; + SpellBonusEntry sbe; - // Additional checks in case spell cast/hit/crit is the event - // Check (if set) school, category, skill line, spell talent mask - if(spellProcEvent->schoolMask && (!procSpell || (GetSpellSchoolMask(procSpell) & spellProcEvent->schoolMask) == 0)) - return false; - if(spellProcEvent->category && (!procSpell || procSpell->Category != spellProcEvent->category)) - return false; - if(spellProcEvent->skillId) - { - if (!procSpell) - return false; + sbe.direct_damage = fields[1].GetFloat(); + sbe.dot_damage = fields[2].GetFloat(); + sbe.ap_bonus = fields[3].GetFloat(); - SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(procSpell->Id); - SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(procSpell->Id); + mSpellBonusMap[entry] = sbe; + } while( result->NextRow() ); - bool found = false; - for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx) - { - if(_spell_idx->second->skillId == spellProcEvent->skillId) - { - found = true; - break; - } - } - if (!found) - return false; - } - if(spellProcEvent->spellFamilyName && (!procSpell || spellProcEvent->spellFamilyName != procSpell->SpellFamilyName)) - return false; - if(spellProcEvent->spellFamilyMask && (!procSpell || (spellProcEvent->spellFamilyMask & procSpell->SpellFamilyFlags) == 0)) - return false; + delete result; - return true; + sLog.outString(); + sLog.outString( ">> Loaded %u extra spell bonus data", count); } -*/ -bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const * spellProcEvent, uint32 EventProcFlag, SpellEntry const * procSpell, uint32 procFlags, uint32 procExtra, bool active) +bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const* spellProcEvent, uint32 EventProcFlag, SpellEntry const * procSpell, uint32 procFlags, uint32 procExtra, bool active) { // No extra req need uint32 procEvent_procEx = PROC_EX_NONE; @@ -1193,9 +1184,8 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const * spellP return false; // Always trigger for this - if (EventProcFlag & (PROC_FLAG_KILLED | PROC_FLAG_KILL_AND_GET_XP)) + if (EventProcFlag & (PROC_FLAG_KILLED | PROC_FLAG_KILL | PROC_FLAG_ON_TRAP_ACTIVATION)) return true; - if (spellProcEvent) // Exist event data { // Store extra req @@ -1221,7 +1211,7 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const * spellP // spellFamilyName is Ok need check for spellFamilyMask if present if(spellProcEvent->spellFamilyMask) { - if ((spellProcEvent->spellFamilyMask & procSpell->SpellFamilyFlags) == 0) + if ((spellProcEvent->spellFamilyMask & procSpell->SpellFamilyFlags ) == 0) return false; active = true; // Spell added manualy -> so its active spell } @@ -1320,36 +1310,44 @@ 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(IsProfessionSpell(spellInfo->Id)) + 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_PARTY) - 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; } bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2, bool sameCaster) const { - //if(spellId_1 == spellId_2) // auras due to the same spell - // return false; SpellEntry const *spellInfo_1 = sSpellStore.LookupEntry(spellId_1); SpellEntry const *spellInfo_2 = sSpellStore.LookupEntry(spellId_2); @@ -1363,29 +1361,9 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2, bool ||(IsSingleFromSpellSpecificPerCaster(spellId_spec_1, spellId_spec_2) && sameCaster)) return true; - // spells with different specific always stack - if(spellId_spec_1 || spellId_spec_2) - return false; - if(spellInfo_1->SpellFamilyName != spellInfo_2->SpellFamilyName) return false; - // generic spells - if(!spellInfo_1->SpellFamilyName) - { - if(!spellInfo_1->SpellIconID - || spellInfo_1->SpellIconID == 1 - || spellInfo_1->SpellIconID != spellInfo_2->SpellIconID) - return false; - } - - // check for class spells - else - { - if (spellInfo_1->SpellFamilyFlags != spellInfo_2->SpellFamilyFlags) - return false; - } - if(!sameCaster) { for(uint32 i = 0; i < 3; ++i) @@ -1402,7 +1380,7 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2, bool case SPELL_AURA_PERIODIC_MANA_LEECH: case SPELL_AURA_PERIODIC_LEECH: case SPELL_AURA_POWER_BURN_MANA: - case SPELL_AURA_OBS_MOD_MANA: + case SPELL_AURA_OBS_MOD_ENERGY: case SPELL_AURA_OBS_MOD_HEALTH: return false; default: @@ -1410,23 +1388,61 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2, bool } } -// not needed now because we compare effects last rank of spells -// if(spellInfo_1->SpellFamilyName && IsRankSpellDueToSpell(spellInfo_1, spellId_2)) -// return true; + // hack for Incanter's Absorption + if (spellInfo_1->Id==44413 && spellInfo_2->Id==44413) + return false; + + spellId_2 = GetLastSpellInChain(spellId_2); + spellId_1 = GetLastSpellInChain(spellId_1); + if (spellId_1 == spellId_2) + return true; + + // generic spells + if(!spellInfo_1->SpellFamilyName) + { + if(!spellInfo_1->SpellIconID + || spellInfo_1->SpellIconID == 1 + || spellInfo_1->SpellIconID != spellInfo_2->SpellIconID) + return false; + } + // check for class spells + else + { + if (spellInfo_1->SpellFamilyFlags != spellInfo_2->SpellFamilyFlags) + return false; + } //use data of highest rank spell(needed for spells which ranks have different effects) - spellInfo_1=sSpellStore.LookupEntry(GetLastSpellInChain(spellId_1)); - spellInfo_2=sSpellStore.LookupEntry(GetLastSpellInChain(spellId_2)); + spellInfo_1=sSpellStore.LookupEntry(spellId_1); + spellInfo_2=sSpellStore.LookupEntry(spellId_2); //if spells have exactly the same effect they cannot stack for(uint32 i = 0; i < 3; ++i) if(spellInfo_1->Effect[i] != spellInfo_2->Effect[i] + // Allow dummy auras stack (needed by 31666 and 58428) + || spellInfo_1->EffectApplyAuraName[i] == SPELL_AURA_DUMMY + || spellInfo_1->EffectApplyAuraName[i] == SPELL_AURA_PERIODIC_DUMMY || spellInfo_1->EffectApplyAuraName[i] != spellInfo_2->EffectApplyAuraName[i] || spellInfo_1->EffectMiscValue[i] != spellInfo_2->EffectMiscValue[i]) // paladin resist aura return false; // need itemtype check? need an example to add that check return true; } + +bool SpellMgr::IsProfessionOrRidingSpell(uint32 spellId) +{ + SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId); + if(!spellInfo) + return false; + + if(spellInfo->Effect[1] != SPELL_EFFECT_SKILL) + return false; + + uint32 skill = spellInfo->EffectMiscValue[1]; + + return IsProfessionOrRidingSkill(skill); +} + bool SpellMgr::IsProfessionSpell(uint32 spellId) { SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId); @@ -1460,6 +1476,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 @@ -1471,7 +1505,8 @@ SpellEntry const* SpellMgr::SelectAuraRankForPlayerLevel(SpellEntry const* spell { if( IsPositiveEffect(spellInfo->Id, i) && ( spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA || - spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_PARTY + spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_PARTY || + spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_RAID ) ) { needRankSelection = true; @@ -1546,21 +1581,31 @@ struct SpellRankEntry uint32 RangeIndex; uint32 SpellVisual; uint32 ProcFlags; - uint64 SpellFamilyFlags; + flag96 SpellFamilyFlags; uint32 TargetAuraState; uint32 ManaCost; - - bool operator()(const SpellRankEntry & _Left,const SpellRankEntry & _Right)const - { - return (_Left.SkillId != _Right.SkillId ? _Left.SkillId < _Right.SkillId - : _Left.SpellName!=_Right.SpellName ? _Left.SpellName < _Right.SpellName - : _Left.ProcFlags!=_Right.ProcFlags ? _Left.ProcFlags < _Right.ProcFlags - : _Left.SpellFamilyFlags!=_Right.SpellFamilyFlags ? _Left.SpellFamilyFlags < _Right.SpellFamilyFlags - : (_Left.SpellVisual!=_Right.SpellVisual) && (!_Left.SpellVisual || !_Right.SpellVisual) ? _Left.SpellVisual < _Right.SpellVisual - : (_Left.ManaCost!=_Right.ManaCost) && (!_Left.ManaCost || !_Right.ManaCost) ? _Left.ManaCost < _Right.ManaCost - : (_Left.DurationIndex!=_Right.DurationIndex) && (!_Left.DurationIndex || !_Right.DurationIndex)? _Left.DurationIndex < _Right.DurationIndex - : (_Left.RangeIndex!=_Right.RangeIndex) && (!_Left.RangeIndex || !_Right.RangeIndex || _Left.RangeIndex==1 || !_Right.RangeIndex==1) ? _Left.RangeIndex < _Right.RangeIndex - : _Left.TargetAuraState < _Right.TargetAuraState + uint32 CastingTimeIndex; + flag96 Effect; + flag96 Aura; + uint16 TalentID; + + bool operator < (const SpellRankEntry & _Right) const + { + return (SkillId != _Right.SkillId ? SkillId < _Right.SkillId + : SpellName!=_Right.SpellName ? SpellName < _Right.SpellName + : ProcFlags!=_Right.ProcFlags ? ProcFlags < _Right.ProcFlags + + : Effect!=_Right.Effect ? Effect < _Right.Effect + : Aura!=_Right.Aura ? Aura < _Right.Aura + : TalentID!=_Right.TalentID ? TalentID < _Right.TalentID + : (CastingTimeIndex!=_Right.CastingTimeIndex) && (!CastingTimeIndex || !_Right.CastingTimeIndex || CastingTimeIndex==1 || !_Right.CastingTimeIndex==1) ? CastingTimeIndex < _Right.CastingTimeIndex + + : SpellFamilyFlags!=_Right.SpellFamilyFlags ? SpellFamilyFlags < _Right.SpellFamilyFlags + : (SpellVisual!=_Right.SpellVisual) && (!SpellVisual || !_Right.SpellVisual) ? SpellVisual < _Right.SpellVisual + : (ManaCost!=_Right.ManaCost) && (!ManaCost || !_Right.ManaCost) ? ManaCost < _Right.ManaCost + : (DurationIndex!=_Right.DurationIndex) && (!DurationIndex || !_Right.DurationIndex)? DurationIndex < _Right.DurationIndex + : (RangeIndex!=_Right.RangeIndex) && (!RangeIndex || !_Right.RangeIndex || RangeIndex==1 || !_Right.RangeIndex==1) ? RangeIndex < _Right.RangeIndex + : TargetAuraState < _Right.TargetAuraState ); } }; @@ -1569,6 +1614,7 @@ struct SpellRankValue { uint32 Id; char const *Rank; + bool strict; }; void SpellMgr::LoadSpellChains() @@ -1581,14 +1627,12 @@ void SpellMgr::LoadSpellChains() SkillLineAbilityEntry const *AbilityInfo=sSkillLineAbilityStore.LookupEntry(ability_id); if (!AbilityInfo) continue; - if (AbilityInfo->spellId==20154) //exception to these rules (not needed in 3.0.3) - continue; if (!AbilityInfo->forward_spellid) continue; ChainedSpells.push_back(AbilityInfo->forward_spellid); } - std::multimap<SpellRankEntry, SpellRankValue,SpellRankEntry> RankMap; + std::multimap<SpellRankEntry, SpellRankValue> RankMap; for (uint32 ability_id=0;ability_id<sSkillLineAbilityStore.GetNumRows();ability_id++) { @@ -1598,8 +1642,6 @@ void SpellMgr::LoadSpellChains() //get only spell with lowest ability_id to prevent doubles uint32 spell_id=AbilityInfo->spellId; - if (spell_id==20154) //exception to these rules (not needed in 3.0.3) - continue; bool found=false; for (uint32 i=0; i<ChainedSpells.size(); i++) { @@ -1618,7 +1660,7 @@ void SpellMgr::LoadSpellChains() if(sRank.empty()) continue; //exception to polymorph spells-make pig and turtle other chain than sheep - if ((SpellInfo->SpellFamilyName==SPELLFAMILY_MAGE) && (SpellInfo->SpellFamilyFlags & 0x1000000) && (SpellInfo->SpellIconID!=82)) + if ((SpellInfo->SpellFamilyName==SPELLFAMILY_MAGE) && (SpellInfo->SpellFamilyFlags[0] & 0x1000000) && (SpellInfo->SpellIconID!=82)) continue; SpellRankEntry entry; @@ -1630,14 +1672,16 @@ void SpellMgr::LoadSpellChains() entry.ProcFlags=SpellInfo->procFlags; entry.SpellFamilyFlags=SpellInfo->SpellFamilyFlags; entry.TargetAuraState=SpellInfo->TargetAuraState; - entry.SpellVisual=SpellInfo->SpellVisual; + entry.SpellVisual=SpellInfo->SpellVisual[0]; entry.ManaCost=SpellInfo->manaCost; - + entry.CastingTimeIndex=0; + entry.TalentID=0; for (;;) { AbilityInfo=mSkillLineAbilityMap.lower_bound(spell_id)->second; value.Id=spell_id; value.Rank=SpellInfo->Rank[sWorld.GetDefaultDbcLocale()]; + value.strict=false; RankMap.insert(std::pair<SpellRankEntry, SpellRankValue>(entry,value)); spell_id=AbilityInfo->forward_spellid; SpellInfo=sSpellStore.LookupEntry(spell_id); @@ -1650,48 +1694,109 @@ void SpellMgr::LoadSpellChains() uint32 count=0; - for (std::multimap<SpellRankEntry, SpellRankValue,SpellRankEntry>::iterator itr = RankMap.begin();itr!=RankMap.end();) + for (std::multimap<SpellRankEntry, SpellRankValue>::iterator itr = RankMap.begin();itr!=RankMap.end();) { SpellRankEntry entry=itr->first; //trac errors in extracted data - std::multimap<char const *, std::multimap<SpellRankEntry, SpellRankValue,SpellRankEntry>::iterator> RankErrorMap; - for (std::multimap<SpellRankEntry, SpellRankValue,SpellRankEntry>::iterator itr2 = RankMap.lower_bound(entry);itr2!=RankMap.upper_bound(entry);itr2++) + std::multimap<char const *, std::multimap<SpellRankEntry, SpellRankValue>::iterator> RankErrorMap; + for (std::multimap<SpellRankEntry, SpellRankValue>::iterator itr2 = RankMap.lower_bound(entry);itr2!=RankMap.upper_bound(entry);itr2++) { bar.step(); - RankErrorMap.insert(std::pair<char const *, std::multimap<SpellRankEntry, SpellRankValue,SpellRankEntry>::iterator>(itr2->second.Rank,itr2)); + RankErrorMap.insert(std::pair<char const *, std::multimap<SpellRankEntry, SpellRankValue>::iterator>(itr2->second.Rank,itr2)); } - for (std::multimap<char const *, std::multimap<SpellRankEntry, SpellRankValue,SpellRankEntry>::iterator>::iterator itr2 = RankErrorMap.begin();itr2!=RankErrorMap.end();) - { - char const * err_entry=itr2->first; - uint32 rank_count=RankErrorMap.count(itr2->first); - if (rank_count>1) - for (itr2 = RankErrorMap.lower_bound(err_entry);itr2!=RankErrorMap.upper_bound(err_entry);itr2++) + + bool error=false; + //if strict == true strict check is not needed + if (!itr->second.strict) + //check for rank duplicates, if there are any do strict check + for (std::multimap<char const *, std::multimap<SpellRankEntry, SpellRankValue>::iterator>::iterator itr2 = RankErrorMap.begin();itr2!=RankErrorMap.end();) { - sLog.outDebug("There is a duplicate rank entry (%s) for spell: %u",itr2->first,itr2->second->second.Id); - sLog.outDebug("Spell %u removed from chain data.",itr2->second->second.Id); - RankMap.erase(itr2->second); - itr=RankMap.lower_bound(entry); + char const * err_entry=itr2->first; + uint32 rank_count=RankErrorMap.count(itr2->first); + if (rank_count>1) + { + error=true; + break; + } + else + itr2++; } - else - itr2++; - } - //do not proceed for spells with less than 2 ranks - uint32 spell_max_rank=RankMap.count(entry); - if (spell_max_rank<2) + bool allHaveTalents=true; + if (error) { - itr=RankMap.upper_bound(entry); + std::list<uint32> ConflictedSpells; + for (std::multimap<SpellRankEntry, SpellRankValue>::iterator itr2 = RankMap.lower_bound(entry);itr2!=RankMap.upper_bound(entry);itr2=RankMap.lower_bound(entry)) + { + ConflictedSpells.push_back(itr2->second.Id); + if (!GetTalentSpellPos(itr2->second.Id)) + allHaveTalents=false; + RankMap.erase(itr2); + } + SpellRankEntry nextEntry, currEntry; + for (;!ConflictedSpells.empty();ConflictedSpells.pop_front()) + { + SpellEntry const *SpellInfo=sSpellStore.LookupEntry(ConflictedSpells.front()); + currEntry.SkillId=entry.SkillId; + currEntry.SpellName=SpellInfo->SpellName[sWorld.GetDefaultDbcLocale()]; + currEntry.DurationIndex=SpellInfo->DurationIndex; + currEntry.RangeIndex=SpellInfo->rangeIndex; + currEntry.ProcFlags=SpellInfo->procFlags; + currEntry.SpellFamilyFlags=SpellInfo->SpellFamilyFlags; + //compare talents only when all spells from chain have entry + //to prevent wrong results with spells which have first rank talented and other not + if (allHaveTalents) + currEntry.TalentID=GetTalentSpellPos(ConflictedSpells.front())->talent_id; + else + currEntry.TalentID=0; + currEntry.TargetAuraState=SpellInfo->TargetAuraState; + currEntry.SpellVisual=SpellInfo->SpellVisual[0]; + currEntry.ManaCost=SpellInfo->manaCost; + + //compare effects and casting time + currEntry.CastingTimeIndex=SpellInfo->CastingTimeIndex; + currEntry.Effect[0]=SpellInfo->Effect[0]; + currEntry.Effect[1]=SpellInfo->Effect[1]; + currEntry.Effect[2]=SpellInfo->Effect[2]; + + currEntry.Aura[0]=SpellInfo->EffectApplyAuraName[0]; + currEntry.Aura[1]=SpellInfo->EffectApplyAuraName[1]; + currEntry.Aura[2]=SpellInfo->EffectApplyAuraName[2]; + + SpellRankValue currValue; + currValue.Id=ConflictedSpells.front(); + currValue.Rank=SpellInfo->Rank[sWorld.GetDefaultDbcLocale()]; + currValue.strict=true; + RankMap.insert(std::pair<SpellRankEntry, SpellRankValue>(currEntry,currValue)); + } + itr=RankMap.begin(); continue; } + else + for (std::multimap<char const *, std::multimap<SpellRankEntry, SpellRankValue>::iterator>::iterator itr2 = RankErrorMap.begin();itr2!=RankErrorMap.end();) + { + char const * err_entry=itr2->first; + uint32 rank_count=RankErrorMap.count(itr2->first); + if (rank_count>1) + for (itr2 = RankErrorMap.lower_bound(err_entry);itr2!=RankErrorMap.upper_bound(err_entry);itr2++) + { + sLog.outDebug("There is a duplicate rank entry (%s) for spell: %u",itr2->first,itr2->second->second.Id); + if (!(itr2->second->second.Id==52375 || itr2->second->second.Id==45902)) + { + sLog.outDebug("Spell %u removed from chain data.",itr2->second->second.Id); + RankMap.erase(itr2->second); + } + } + else + itr2++; + } - itr=RankMap.upper_bound(entry); - - //order spells by spells by spellLevel + //order spells by spellLevel std::list<uint32> RankedSpells; uint32 min_spell_lvl=0; - std::multimap<SpellRankEntry, SpellRankValue,SpellRankEntry>::iterator min_itr; + std::multimap<SpellRankEntry, SpellRankValue>::iterator min_itr; for (;RankMap.count(entry);) { - for (std::multimap<SpellRankEntry, SpellRankValue,SpellRankEntry>::iterator itr2 = RankMap.lower_bound(entry);itr2!=RankMap.upper_bound(entry);itr2++) + for (std::multimap<SpellRankEntry, SpellRankValue>::iterator itr2 = RankMap.lower_bound(entry);itr2!=RankMap.upper_bound(entry);itr2++) { SpellEntry const *SpellInfo=sSpellStore.LookupEntry(itr2->second.Id); if (SpellInfo->spellLevel<min_spell_lvl || itr2==RankMap.lower_bound(entry)) @@ -1727,9 +1832,13 @@ void SpellMgr::LoadSpellChains() } } + //do not proceed for spells with less than 2 ranks + itr=RankMap.begin(); + if (RankedSpells.size()<2) + continue; + count++; - itr=RankMap.upper_bound(entry); uint32 spell_rank=1; for(std::list<uint32>::iterator itr2 = RankedSpells.begin();itr2!=RankedSpells.end();spell_rank++) { @@ -1753,8 +1862,8 @@ void SpellMgr::LoadSpellChains() } //uncomment these two lines to print yourself list of spell_chains on startup -// for (UNORDERED_MAP<uint32, SpellChainNode>::iterator itr=mSpellChains.begin();itr!=mSpellChains.end();itr++) -// sLog.outString( "Id: %u, Rank: %d , %s",itr->first,itr->second.rank, sSpellStore.LookupEntry(itr->first)->Rank[sWorld.GetDefaultDbcLocale()]); + //for (UNORDERED_MAP<uint32, SpellChainNode>::iterator itr=mSpellChains.begin();itr!=mSpellChains.end();itr++) + //sLog.outString( "Id: %u, Rank: %d , %s, %u, %u, %u, %u",itr->first,itr->second.rank, sSpellStore.LookupEntry(itr->first)->Rank[sWorld.GetDefaultDbcLocale()], itr->second.first, itr->second.last,itr->second.next ,itr->second.prev); sLog.outString(); sLog.outString( ">> Loaded %u spell chains",count); @@ -1766,8 +1875,10 @@ void SpellMgr::LoadSpellLearnSkills() // search auto-learned skills and add its to map also for use in unlearn spells/talents uint32 dbc_count = 0; + barGoLink bar( sSpellStore.GetNumRows() ); for(uint32 spell = 0; spell < sSpellStore.GetNumRows(); ++spell) { + bar.step(); SpellEntry const* entry = sSpellStore.LookupEntry(spell); if(!entry) @@ -1780,10 +1891,10 @@ void SpellMgr::LoadSpellLearnSkills() SpellLearnSkillNode dbc_node; dbc_node.skill = entry->EffectMiscValue[i]; if ( dbc_node.skill != SKILL_RIDING ) - dbc_node.value = 1; + dbc_node.value = 1; else - dbc_node.value = (entry->EffectBasePoints[i]+1)*75; - dbc_node.maxvalue = (entry->EffectBasePoints[i]+1)*75; + dbc_node.value = entry->CalculateSimpleValue(i)*75; + dbc_node.maxvalue = entry->CalculateSimpleValue(i)*75; SpellLearnSkillNode const* db_node = GetSpellLearnSkill(spell); @@ -1802,7 +1913,8 @@ void SpellMgr::LoadSpellLearnSpells() { mSpellLearnSpells.clear(); // need for reload case - QueryResult *result = WorldDatabase.Query("SELECT entry, SpellID FROM spell_learn_spell"); + // 0 1 2 + QueryResult *result = WorldDatabase.Query("SELECT entry, SpellID, Active FROM spell_learn_spell"); if(!result) { barGoLink bar( 1 ); @@ -1826,6 +1938,7 @@ void SpellMgr::LoadSpellLearnSpells() SpellLearnSpellNode node; node.spell = fields[1].GetUInt32(); + node.active = fields[2].GetBool(); node.autoLearned= false; if(!sSpellStore.LookupEntry(spell_id)) @@ -1862,7 +1975,16 @@ void SpellMgr::LoadSpellLearnSpells() { SpellLearnSpellNode dbc_node; dbc_node.spell = entry->EffectTriggerSpell[i]; - dbc_node.autoLearned = true; + dbc_node.active = true; // all dbc based learned spells is active (show in spell book or hide by client itself) + + // ignore learning not existed spells (broken/outdated/or generic learnig spell 483 + if(!sSpellStore.LookupEntry(dbc_node.spell)) + continue; + + // talent or passive spells or skill-step spells auto-casted and not need dependent learning, + // pet teaching spells don't must be dependent learning (casted) + // other required explicit dependent learning + dbc_node.autoLearned = entry->EffectImplicitTargetA[i]==TARGET_PET || GetTalentSpellCost(spell) > 0 || IsPassiveSpell(spell) || IsSpellHaveEffect(entry,SPELL_EFFECT_SKILL_STEP); SpellLearnSpellMap::const_iterator db_node_begin = GetBeginSpellLearnSpell(spell); SpellLearnSpellMap::const_iterator db_node_end = GetEndSpellLearnSpell(spell); @@ -2094,7 +2216,7 @@ void SpellMgr::LoadSpellPetAuras() continue; } - PetAura pa(pet, aura, spellInfo->EffectImplicitTargetA[i] == TARGET_PET, spellInfo->EffectBasePoints[i] + spellInfo->EffectBaseDice[i]); + PetAura pa(pet, aura, spellInfo->EffectImplicitTargetA[i] == TARGET_PET, spellInfo->CalculateSimpleValue(i)); mSpellPetAuraMap[spell] = pa; } @@ -2179,9 +2301,17 @@ void SpellMgr::LoadSpellCustomAttr() spellInfo->Effect[j] = SPELL_EFFECT_TRIGGER_MISSILE; break; } + + switch(SpellTargetType[spellInfo->EffectImplicitTargetA[j]]) + { + case TARGET_TYPE_UNIT_TARGET: + case TARGET_TYPE_DEST_TARGET: + spellInfo->Targets |= TARGET_FLAG_UNIT; + break; + } } - if(spellInfo->SpellVisual == 3879) + if(spellInfo->SpellVisual[0] == 3879) mSpellCustomAttr[i] |= SPELL_ATTR_CU_CONE_BACK; switch(i) @@ -2209,7 +2339,7 @@ void SpellMgr::LoadSpellCustomAttr() case 45027: // Revitalize case 45976: // Muru Portal Channel case 39365: // Thundering Storm - case 41071: // Raise Dead + case 41071: // Raise Dead (HACK) spellInfo->MaxAffectedTargets = 1; break; case 41376: // Spite @@ -2220,6 +2350,7 @@ void SpellMgr::LoadSpellCustomAttr() case 46771: //Flame Sear case 45248: //Shadow Blades case 41303: // Soul Drain + case 54172: // Divine Storm (heal) spellInfo->MaxAffectedTargets = 3; break; case 38310: //Multi-Shot @@ -2246,10 +2377,45 @@ void SpellMgr::LoadSpellCustomAttr() spellInfo->MaxAffectedTargets = 1; spellInfo->EffectTriggerSpell[0] = 33760; break; + case 1122: // Inferno + case 18662: // Curse of Doom + spellInfo->EffectBasePoints[0] = 0; //prevent summon too many of them + break; + case 17941: // Shadow Trance + case 22008: // Netherwind Focus + case 31834: // Light's Grace + case 34754: // Clearcasting + case 34936: // Backlash + case 48108: // Hot Streak + case 51124: // Killing Machine + case 54741: // Firestarter + case 57761: // Fireball! + case 39805: // Lightning Overload + case 52437: // Sudden Death + spellInfo->procCharges=1; + break; + case 44544: // Fingers of Frost + spellInfo->procCharges=2; + break; + case 28200: // Ascendance (Talisman of Ascendance trinket) + spellInfo->procCharges=6; + break; default: break; } + + switch(spellInfo->SpellFamilyName) + { + case SPELLFAMILY_DRUID: + //starfall + if(spellInfo->SpellFamilyFlags[2] & 0x100) + spellInfo->MaxAffectedTargets = 2; + break; + } } + + SummonPropertiesEntry *properties = const_cast<SummonPropertiesEntry*>(sSummonPropertiesStore.LookupEntry(121)); + properties->Type = SUMMON_TYPE_TOTEM; } void SpellMgr::LoadSpellLinked() @@ -2326,6 +2492,48 @@ void SpellMgr::LoadSpellLinked() } /// Some checks for spells, to prevent adding depricated/broken spells for trainers, spell book, etc +void SpellMgr::LoadPetLevelupSpellMap() +{ + mPetLevelupSpellMap.clear(); // need for reload case + + uint32 count=0; + for (uint32 i = 0; i < sCreatureFamilyStore.GetNumRows(); ++i) + { + CreatureFamilyEntry const *creatureFamily=sCreatureFamilyStore.LookupEntry(i); + if(!creatureFamily) // not exist + continue; + + for (uint8 j = 0; j < 2; ++j) + { + if (creatureFamily->skillLine[j]==0) + continue; + + for (uint32 k=0;k<sSkillLineAbilityStore.GetNumRows();++k) + { + SkillLineAbilityEntry const *skillLine = sSkillLineAbilityStore.LookupEntry(k); + if (!skillLine) + continue; + if (creatureFamily->skillLine[j]!=skillLine->skillId) + continue; + SpellEntry const *spell = sSpellStore.LookupEntry(skillLine->spellId); + // not exist + if(!spell) + continue; + if (!spell->spellLevel) + continue; + if (!spell->SpellFamilyName) + continue; + mPetLevelupSpellMap.insert(PetLevelupSpellMap::value_type(creatureFamily->ID, std::make_pair(spell->spellLevel , spell->Id ))); + count++; + } + } + } + + sLog.outString(); + sLog.outString( ">> Loaded %u pet levelup spells", count ); +} + +/// Some checks for spells, to prevent adding deprecated/broken spells for trainers, spell book, etc bool SpellMgr::IsSpellValid(SpellEntry const* spellInfo, Player* pl, bool msg) { // not exist @@ -2400,82 +2608,326 @@ bool SpellMgr::IsSpellValid(SpellEntry const* spellInfo, Player* pl, bool msg) return true; } -bool IsSpellAllowedInLocation(SpellEntry const *spellInfo,uint32 map_id,uint32 zone_id,uint32 area_id) +void SpellMgr::LoadSpellAreas() { - // normal case - if( spellInfo->AreaId && spellInfo->AreaId != zone_id && spellInfo->AreaId != area_id ) - return false; + mSpellAreaMap.clear(); // need for reload case + mSpellAreaForQuestMap.clear(); + mSpellAreaForActiveQuestMap.clear(); + mSpellAreaForQuestEndMap.clear(); + mSpellAreaForAuraMap.clear(); + + uint32 count = 0; + + // 0 1 2 3 4 5 6 7 8 + QueryResult *result = WorldDatabase.Query("SELECT spell, area, quest_start, quest_start_active, quest_end, aura_spell, racemask, gender, autocast FROM spell_area"); + + if( !result ) + { + barGoLink bar( 1 ); + + bar.step(); + + sLog.outString(); + sLog.outString( ">> Loaded %u spell area requirements", count ); + return; + } - // elixirs (all area dependent elixirs have family SPELLFAMILY_POTION, use this for speedup) - if(spellInfo->SpellFamilyName==SPELLFAMILY_POTION) + barGoLink bar( result->GetRowCount() ); + + do { - if(uint32 mask = spellmgr.GetSpellElixirMask(spellInfo->Id)) + Field *fields = result->Fetch(); + + bar.step(); + + uint32 spell = fields[0].GetUInt32(); + SpellArea spellArea; + spellArea.spellId = spell; + spellArea.areaId = fields[1].GetUInt32(); + spellArea.questStart = fields[2].GetUInt32(); + spellArea.questStartCanActive = fields[3].GetBool(); + spellArea.questEnd = fields[4].GetUInt32(); + spellArea.auraSpell = fields[5].GetInt32(); + spellArea.raceMask = fields[6].GetUInt32(); + spellArea.gender = Gender(fields[7].GetUInt8()); + spellArea.autocast = fields[8].GetBool(); + + if(!sSpellStore.LookupEntry(spell)) + { + sLog.outErrorDb("Spell %u listed in `spell_area` does not exist", spell); + continue; + } + { - if(mask & ELIXIR_BATTLE_MASK) + bool ok = true; + SpellAreaMapBounds sa_bounds = GetSpellAreaMapBounds(spellArea.spellId); + for(SpellAreaMap::const_iterator itr = sa_bounds.first; itr != sa_bounds.second; ++itr) + { + if(spellArea.spellId && itr->second.spellId && spellArea.spellId != itr->second.spellId) + continue; + if(spellArea.areaId && itr->second.areaId && spellArea.areaId!= itr->second.areaId) + continue; + if(spellArea.questStart && itr->second.questStart && spellArea.questStart!= itr->second.questStart) + continue; + if(spellArea.auraSpell && itr->second.auraSpell && spellArea.auraSpell!= itr->second.auraSpell) + continue; + if(spellArea.raceMask && itr->second.raceMask && (spellArea.raceMask & itr->second.raceMask)==0) + continue; + if(spellArea.gender != GENDER_NONE && itr->second.gender != GENDER_NONE && spellArea.gender!= itr->second.gender) + continue; + + // duplicate by requirements + ok =false; + break; + } + + if(!ok) { - if(spellInfo->Id==45373) // Bloodberry Elixir - return zone_id==4075; + sLog.outErrorDb("Spell %u listed in `spell_area` already listed with similar requirements.", spell); + continue; } - if(mask & ELIXIR_UNSTABLE_MASK) + + } + + if(spellArea.areaId && !GetAreaEntryByAreaID(spellArea.areaId)) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have wrong area (%u) requirement", spell,spellArea.areaId); + continue; + } + + if(spellArea.questStart && !objmgr.GetQuestTemplate(spellArea.questStart)) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have wrong start quest (%u) requirement", spell,spellArea.questStart); + continue; + } + + if(spellArea.questEnd) + { + if(!objmgr.GetQuestTemplate(spellArea.questEnd)) { - // in the Blade's Edge Mountains Plateaus and Gruul's Lair. - return zone_id ==3522 || map_id==565; + sLog.outErrorDb("Spell %u listed in `spell_area` have wrong end quest (%u) requirement", spell,spellArea.questEnd); + continue; } - if(mask & ELIXIR_SHATTRATH_MASK) + + if(spellArea.questEnd==spellArea.questStart && !spellArea.questStartCanActive) { - // in Tempest Keep, Serpentshrine Cavern, Caverns of Time: Mount Hyjal, Black Temple, Sunwell Plateau - if(zone_id ==3607 || map_id==534 || map_id==564 || zone_id==4075) - return true; + sLog.outErrorDb("Spell %u listed in `spell_area` have quest (%u) requirement for start and end in same time", spell,spellArea.questEnd); + continue; + } + } - MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); - if(!mapEntry) - return false; + if(spellArea.auraSpell) + { + SpellEntry const* spellInfo = sSpellStore.LookupEntry(abs(spellArea.auraSpell)); + if(!spellInfo) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have wrong aura spell (%u) requirement", spell,abs(spellArea.auraSpell)); + continue; + } - return mapEntry->multimap_id==206; + if(spellInfo->EffectApplyAuraName[0]!=SPELL_AURA_DUMMY && spellInfo->EffectApplyAuraName[0]!=SPELL_AURA_PHASE) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell requirement (%u) without dummy/phase aura in effect 0", spell,abs(spellArea.auraSpell)); + continue; } - // elixirs not have another limitations - return true; + if(abs(spellArea.auraSpell)==spellArea.spellId) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell (%u) requirement for itself", spell,abs(spellArea.auraSpell)); + continue; + } + + // not allow autocast chains by auraSpell field (but allow use as alternative if not present) + if(spellArea.autocast && spellArea.auraSpell > 0) + { + bool chain = false; + SpellAreaForAuraMapBounds saBound = GetSpellAreaForAuraMapBounds(spellArea.spellId); + for(SpellAreaForAuraMap::const_iterator itr = saBound.first; itr != saBound.second; ++itr) + { + if(itr->second->autocast && itr->second->auraSpell > 0) + { + chain = true; + break; + } + } + + if(chain) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell (%u) requirement that itself autocast from aura", spell,spellArea.auraSpell); + continue; + } + + SpellAreaMapBounds saBound2 = GetSpellAreaMapBounds(spellArea.auraSpell); + for(SpellAreaMap::const_iterator itr2 = saBound2.first; itr2 != saBound2.second; ++itr2) + { + if(itr2->second.autocast && itr2->second.auraSpell > 0) + { + chain = true; + break; + } + } + + if(chain) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell (%u) requirement that itself autocast from aura", spell,spellArea.auraSpell); + continue; + } + } + } + + if(spellArea.raceMask && (spellArea.raceMask & RACEMASK_ALL_PLAYABLE)==0) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have wrong race mask (%u) requirement", spell,spellArea.raceMask); + continue; + } + + if(spellArea.gender!=GENDER_NONE && spellArea.gender!=GENDER_FEMALE && spellArea.gender!=GENDER_MALE) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have wrong gender (%u) requirement", spell,spellArea.gender); + continue; + } + + SpellArea const* sa = &mSpellAreaMap.insert(SpellAreaMap::value_type(spell,spellArea))->second; + + // for search by current zone/subzone at zone/subzone change + if(spellArea.areaId) + mSpellAreaForAreaMap.insert(SpellAreaForAreaMap::value_type(spellArea.areaId,sa)); + + // for search at quest start/reward + if(spellArea.questStart) + { + if(spellArea.questStartCanActive) + mSpellAreaForActiveQuestMap.insert(SpellAreaForQuestMap::value_type(spellArea.questStart,sa)); + else + mSpellAreaForQuestMap.insert(SpellAreaForQuestMap::value_type(spellArea.questStart,sa)); } + + // for search at quest start/reward + if(spellArea.questEnd) + mSpellAreaForQuestEndMap.insert(SpellAreaForQuestMap::value_type(spellArea.questEnd,sa)); + + // for search at aura apply + if(spellArea.auraSpell) + mSpellAreaForAuraMap.insert(SpellAreaForAuraMap::value_type(abs(spellArea.auraSpell),sa)); + + ++count; + } while( result->NextRow() ); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u spell area requirements", count ); +} + +SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spellInfo, uint32 map_id, uint32 zone_id, uint32 area_id, Player const* player) +{ + // normal case + if( spellInfo->AreaGroupId > 0) + { + bool found = false; + + AreaGroupEntry const* groupEntry = sAreaGroupStore.LookupEntry(spellInfo->AreaGroupId); + if(groupEntry) + { + for (uint8 i=0; i<7; i++) + if( groupEntry->AreaId[i] == zone_id || groupEntry->AreaId[i] == area_id ) + found = true; + } + + if(!found) + return SPELL_FAILED_INCORRECT_AREA; } - // special cases zone check (maps checked by multimap common id) + // DB base check (if non empty then must fit at least single for allow) + SpellAreaMapBounds saBounds = spellmgr.GetSpellAreaMapBounds(spellInfo->Id); + if(saBounds.first != saBounds.second) + { + for(SpellAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) + { + if(itr->second.IsFitToRequirements(player,zone_id,area_id)) + return SPELL_CAST_OK; + } + return SPELL_FAILED_INCORRECT_AREA; + } + + // bg spell checks switch(spellInfo->Id) { - case 41618: // Bottled Nethergon Energy - case 41620: // Bottled Nethergon Vapor + case 23333: // Warsong Flag + case 23335: // Silverwing Flag + return map_id == 489 && player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; + case 34976: // Netherstorm Flag + return map_id == 566 && player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; + case 2584: // Waiting to Resurrect + case 22011: // Spirit Heal Channel + case 22012: // Spirit Heal + case 24171: // Resurrection Impact Visual + case 42792: // Recently Dropped Flag + case 43681: // Inactive + case 44535: // Spirit Heal (mana) { MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); if(!mapEntry) - return false; + return SPELL_FAILED_INCORRECT_AREA; - return mapEntry->multimap_id==206; + return mapEntry->IsBattleGround() && player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; } - case 41617: // Cenarion Mana Salve - case 41619: // Cenarion Healing Salve + case 44521: // Preparation { + if(!player) + return SPELL_FAILED_REQUIRES_AREA; + MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); if(!mapEntry) - return false; + return SPELL_FAILED_INCORRECT_AREA; + + if(!mapEntry->IsBattleGround()) + return SPELL_FAILED_REQUIRES_AREA; - return mapEntry->multimap_id==207; + BattleGround* bg = player->GetBattleGround(); + return bg && bg->GetStatus()==STATUS_WAIT_JOIN ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; + } + case 32724: // Gold Team (Alliance) + case 32725: // Green Team (Alliance) + case 35774: // Gold Team (Horde) + case 35775: // Green Team (Horde) + { + MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); + if(!mapEntry) + return SPELL_FAILED_INCORRECT_AREA; + + return mapEntry->IsBattleArena() && player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; + } + case 32727: // Arena Preparation + { + if(!player) + return SPELL_FAILED_REQUIRES_AREA; + + MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); + if(!mapEntry) + return SPELL_FAILED_INCORRECT_AREA; + + if(!mapEntry->IsBattleArena()) + return SPELL_FAILED_REQUIRES_AREA; + + BattleGround* bg = player->GetBattleGround(); + return bg && bg->GetStatus()==STATUS_WAIT_JOIN ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; } - case 40216: // Dragonmaw Illusion - case 42016: // Dragonmaw Illusion - return area_id == 3759 || area_id == 3966 || area_id == 3939; } - return true; + return SPELL_CAST_OK; } void SpellMgr::LoadSkillLineAbilityMap() { mSkillLineAbilityMap.clear(); + barGoLink bar( sSkillLineAbilityStore.GetNumRows() ); uint32 count = 0; for (uint32 i = 0; i < sSkillLineAbilityStore.GetNumRows(); i++) { + bar.step(); SkillLineAbilityEntry const *SkillInfo = sSkillLineAbilityStore.LookupEntry(i); if(!SkillInfo) continue; @@ -2485,7 +2937,7 @@ void SpellMgr::LoadSkillLineAbilityMap() } sLog.outString(); - sLog.outString(">> Loaded %u SkillLineAbility MultiMap", count); + sLog.outString(">> Loaded %u SkillLineAbility MultiMap Data", count); } DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto, bool triggered) @@ -2493,56 +2945,49 @@ DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto // Explicit Diminishing Groups switch(spellproto->SpellFamilyName) { - case SPELLFAMILY_MAGE: - { - // Polymorph - if ((spellproto->SpellFamilyFlags & 0x00001000000LL) && spellproto->EffectApplyAuraName[0]==SPELL_AURA_MOD_CONFUSE) - return DIMINISHING_POLYMORPH; - break; - } case SPELLFAMILY_ROGUE: { // Kidney Shot - if (spellproto->SpellFamilyFlags & 0x00000200000LL) + if (spellproto->SpellFamilyFlags[0] & 0x200000) return DIMINISHING_KIDNEYSHOT; // Sap - else if (spellproto->SpellFamilyFlags & 0x00000000080LL) + else if (spellproto->SpellFamilyFlags[0] & 0x80) return DIMINISHING_POLYMORPH; // Gouge - else if (spellproto->SpellFamilyFlags & 0x00000000008LL) + else if (spellproto->SpellFamilyFlags[0] & 0x8) return DIMINISHING_POLYMORPH; // Blind - else if (spellproto->SpellFamilyFlags & 0x00001000000LL) + else if (spellproto->SpellFamilyFlags[0] & 0x00001000000) return DIMINISHING_BLIND_CYCLONE; break; } case SPELLFAMILY_WARLOCK: { // Death Coil - if (spellproto->SpellFamilyFlags & 0x00000080000LL) + if (spellproto->SpellFamilyFlags[0] & 0x00000080000) return DIMINISHING_DEATHCOIL; // Seduction - if (spellproto->SpellFamilyFlags & 0x00040000000LL) + if (spellproto->SpellFamilyFlags[0] & 0x00040000000) return DIMINISHING_FEAR; // Fear //else if (spellproto->SpellFamilyFlags & 0x40840000000LL) // return DIMINISHING_WARLOCK_FEAR; // Curses/etc - else if (spellproto->SpellFamilyFlags & 0x00080000000LL) + else if (spellproto->SpellFamilyFlags[0] & 0x80000000) return DIMINISHING_LIMITONLY; break; } case SPELLFAMILY_DRUID: { // Cyclone - if (spellproto->SpellFamilyFlags & 0x02000000000LL) + if (spellproto->SpellFamilyFlags[1] & 0x020) return DIMINISHING_BLIND_CYCLONE; break; } case SPELLFAMILY_WARRIOR: { // Hamstring - limit duration to 10s in PvP - if (spellproto->SpellFamilyFlags & 0x00000000002LL) + if (spellproto->SpellFamilyFlags[0] & 0x00000000002) return DIMINISHING_LIMITONLY; break; } @@ -2551,30 +2996,21 @@ DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto } // Get by mechanic - for (uint8 i=0;i<3;++i) - { - if (spellproto->Mechanic == MECHANIC_STUN || spellproto->EffectMechanic[i] == MECHANIC_STUN) - return triggered ? DIMINISHING_TRIGGER_STUN : DIMINISHING_CONTROL_STUN; - else if (spellproto->Mechanic == MECHANIC_SLEEP || spellproto->EffectMechanic[i] == MECHANIC_SLEEP) - return DIMINISHING_SLEEP; - else if (spellproto->Mechanic == MECHANIC_ROOT || spellproto->EffectMechanic[i] == MECHANIC_ROOT) - return triggered ? DIMINISHING_TRIGGER_ROOT : DIMINISHING_CONTROL_ROOT; - else if (spellproto->Mechanic == MECHANIC_FEAR || spellproto->EffectMechanic[i] == MECHANIC_FEAR) - return DIMINISHING_FEAR; - else if (spellproto->Mechanic == MECHANIC_CHARM || spellproto->EffectMechanic[i] == MECHANIC_CHARM) - return DIMINISHING_CHARM; - else if (spellproto->Mechanic == MECHANIC_SILENCE || spellproto->EffectMechanic[i] == MECHANIC_SILENCE) - return DIMINISHING_SILENCE; - else if (spellproto->Mechanic == MECHANIC_DISARM || spellproto->EffectMechanic[i] == MECHANIC_DISARM) - return DIMINISHING_DISARM; - else if (spellproto->Mechanic == MECHANIC_FREEZE || spellproto->EffectMechanic[i] == MECHANIC_FREEZE) - return DIMINISHING_FREEZE; - else if (spellproto->Mechanic == MECHANIC_KNOCKOUT|| spellproto->EffectMechanic[i] == MECHANIC_KNOCKOUT || - spellproto->Mechanic == MECHANIC_SAPPED || spellproto->EffectMechanic[i] == MECHANIC_SAPPED) - return DIMINISHING_KNOCKOUT; - else if (spellproto->Mechanic == MECHANIC_BANISH || spellproto->EffectMechanic[i] == MECHANIC_BANISH) - return DIMINISHING_BANISH; - } + uint32 mechanic = GetAllSpellMechanicMask(spellproto); + if (mechanic == MECHANIC_NONE) return DIMINISHING_NONE; + if (mechanic & (1<<MECHANIC_STUN)) return triggered ? DIMINISHING_TRIGGER_STUN : DIMINISHING_CONTROL_STUN; + if (mechanic & (1<<MECHANIC_SLEEP)) return DIMINISHING_SLEEP; + if (mechanic & (1<<MECHANIC_POLYMORPH)) return DIMINISHING_POLYMORPH; + if (mechanic & (1<<MECHANIC_ROOT)) return triggered ? DIMINISHING_TRIGGER_ROOT : DIMINISHING_CONTROL_ROOT; + if (mechanic & (1<<MECHANIC_FEAR)) return DIMINISHING_FEAR; + if (mechanic & (1<<MECHANIC_CHARM)) return DIMINISHING_CHARM; + if (mechanic & (1<<MECHANIC_SILENCE)) return DIMINISHING_SILENCE; + if (mechanic & (1<<MECHANIC_DISARM)) return DIMINISHING_DISARM; + if (mechanic & (1<<MECHANIC_FREEZE)) return DIMINISHING_FREEZE; + if (mechanic & ((1<<MECHANIC_KNOCKOUT) | (1<<MECHANIC_SAPPED))) return DIMINISHING_KNOCKOUT; + if (mechanic & (1<<MECHANIC_BANISH)) return DIMINISHING_BANISH; + if (mechanic & (1<<MECHANIC_HORROR)) return DIMINISHING_DEATHCOIL; + return DIMINISHING_NONE; } @@ -2631,3 +3067,55 @@ DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group) return DRTYPE_NONE; } +bool SpellArea::IsFitToRequirements(Player const* player, uint32 newZone, uint32 newArea) const +{ + if(gender!=GENDER_NONE) + { + // not in expected gender + if(!player || gender != player->getGender()) + return false; + } + + if(raceMask) + { + // not in expected race + if(!player || !(raceMask & player->getRaceMask())) + return false; + } + + if(areaId) + { + // not in expected zone + if(newZone!=areaId && newArea!=areaId) + return false; + } + + if(questStart) + { + // not in expected required quest state + if(!player || (!questStartCanActive || !player->IsActiveQuest(questStart)) && !player->GetQuestRewardStatus(questStart)) + return false; + } + + if(questEnd) + { + // not in expected forbidden quest state + if(!player || player->GetQuestRewardStatus(questEnd)) + return false; + } + + if(auraSpell) + { + // not have expected aura + if(!player) + return false; + if(auraSpell > 0) + // have expected aura + return player->HasAura(auraSpell); + else + // not have expected aura + return !player->HasAura(-auraSpell); + } + + return true; +} diff --git a/src/game/SpellMgr.h b/src/game/SpellMgr.h index 35d867dce2b..41a6cf41cf3 100644 --- a/src/game/SpellMgr.h +++ b/src/game/SpellMgr.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -10,12 +10,12 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _SPELLMGR_H @@ -25,10 +25,13 @@ // For more high level function for sSpellStore data #include "SharedDefines.h" -#include "Database/DBCStructure.h" +#include "DBCStructure.h" #include "Database/SQLStorage.h" #include "Utilities/UnorderedMap.h" + +#include "Player.h" + #include <map> class Player; @@ -36,177 +39,11 @@ class Spell; extern SQLStorage sSpellThreatStore; -enum SpellFailedReason -{ - SPELL_FAILED_AFFECTING_COMBAT = 0x00, - SPELL_FAILED_ALREADY_AT_FULL_HEALTH = 0x01, - SPELL_FAILED_ALREADY_AT_FULL_MANA = 0x02, - SPELL_FAILED_ALREADY_AT_FULL_POWER = 0x03, - SPELL_FAILED_ALREADY_BEING_TAMED = 0x04, - SPELL_FAILED_ALREADY_HAVE_CHARM = 0x05, - SPELL_FAILED_ALREADY_HAVE_SUMMON = 0x06, - SPELL_FAILED_ALREADY_OPEN = 0x07, - SPELL_FAILED_AURA_BOUNCED = 0x08, - SPELL_FAILED_AUTOTRACK_INTERRUPTED = 0x09, - SPELL_FAILED_BAD_IMPLICIT_TARGETS = 0x0A, - SPELL_FAILED_BAD_TARGETS = 0x0B, - SPELL_FAILED_CANT_BE_CHARMED = 0x0C, - SPELL_FAILED_CANT_BE_DISENCHANTED = 0x0D, - SPELL_FAILED_CANT_BE_DISENCHANTED_SKILL = 0x0E, - SPELL_FAILED_CANT_BE_PROSPECTED = 0x0F, - SPELL_FAILED_CANT_CAST_ON_TAPPED = 0x10, - SPELL_FAILED_CANT_DUEL_WHILE_INVISIBLE = 0x11, - SPELL_FAILED_CANT_DUEL_WHILE_STEALTHED = 0x12, - SPELL_FAILED_CANT_STEALTH = 0x13, - SPELL_FAILED_CASTER_AURASTATE = 0x14, - SPELL_FAILED_CASTER_DEAD = 0x15, - SPELL_FAILED_CHARMED = 0x16, - SPELL_FAILED_CHEST_IN_USE = 0x17, - SPELL_FAILED_CONFUSED = 0x18, - SPELL_FAILED_DONT_REPORT = 0x19, - SPELL_FAILED_EQUIPPED_ITEM = 0x1A, - SPELL_FAILED_EQUIPPED_ITEM_CLASS = 0x1B, - SPELL_FAILED_EQUIPPED_ITEM_CLASS_MAINHAND = 0x1C, - SPELL_FAILED_EQUIPPED_ITEM_CLASS_OFFHAND = 0x1D, - SPELL_FAILED_ERROR = 0x1E, - SPELL_FAILED_FIZZLE = 0x1F, - SPELL_FAILED_FLEEING = 0x20, - SPELL_FAILED_FOOD_LOWLEVEL = 0x21, - SPELL_FAILED_HIGHLEVEL = 0x22, - SPELL_FAILED_HUNGER_SATIATED = 0x23, - SPELL_FAILED_IMMUNE = 0x24, - SPELL_FAILED_INTERRUPTED = 0x25, - SPELL_FAILED_INTERRUPTED_COMBAT = 0x26, - SPELL_FAILED_ITEM_ALREADY_ENCHANTED = 0x27, - SPELL_FAILED_ITEM_GONE = 0x28, - SPELL_FAILED_ITEM_NOT_FOUND = 0x29, - SPELL_FAILED_ITEM_NOT_READY = 0x2A, - SPELL_FAILED_LEVEL_REQUIREMENT = 0x2B, - SPELL_FAILED_LINE_OF_SIGHT = 0x2C, - SPELL_FAILED_LOWLEVEL = 0x2D, - SPELL_FAILED_LOW_CASTLEVEL = 0x2E, - SPELL_FAILED_MAINHAND_EMPTY = 0x2F, - SPELL_FAILED_MOVING = 0x30, - SPELL_FAILED_NEED_AMMO = 0x31, - SPELL_FAILED_NEED_AMMO_POUCH = 0x32, - SPELL_FAILED_NEED_EXOTIC_AMMO = 0x33, - SPELL_FAILED_NOPATH = 0x34, - SPELL_FAILED_NOT_BEHIND = 0x35, - SPELL_FAILED_NOT_FISHABLE = 0x36, - SPELL_FAILED_NOT_FLYING = 0x37, - SPELL_FAILED_NOT_HERE = 0x38, - SPELL_FAILED_NOT_INFRONT = 0x39, - SPELL_FAILED_NOT_IN_CONTROL = 0x3A, - SPELL_FAILED_NOT_KNOWN = 0x3B, - SPELL_FAILED_NOT_MOUNTED = 0x3C, - SPELL_FAILED_NOT_ON_TAXI = 0x3D, - SPELL_FAILED_NOT_ON_TRANSPORT = 0x3E, - SPELL_FAILED_NOT_READY = 0x3F, - SPELL_FAILED_NOT_SHAPESHIFT = 0x40, - SPELL_FAILED_NOT_STANDING = 0x41, - SPELL_FAILED_NOT_TRADEABLE = 0x42, - SPELL_FAILED_NOT_TRADING = 0x43, - SPELL_FAILED_NOT_UNSHEATHED = 0x44, - SPELL_FAILED_NOT_WHILE_GHOST = 0x45, - SPELL_FAILED_NO_AMMO = 0x46, - SPELL_FAILED_NO_CHARGES_REMAIN = 0x47, - SPELL_FAILED_NO_CHAMPION = 0x48, - SPELL_FAILED_NO_COMBO_POINTS = 0x49, - SPELL_FAILED_NO_DUELING = 0x4A, - SPELL_FAILED_NO_ENDURANCE = 0x4B, - SPELL_FAILED_NO_FISH = 0x4C, - SPELL_FAILED_NO_ITEMS_WHILE_SHAPESHIFTED = 0x4D, - SPELL_FAILED_NO_MOUNTS_ALLOWED = 0x4E, - SPELL_FAILED_NO_PET = 0x4F, - SPELL_FAILED_NO_POWER = 0x50, - SPELL_FAILED_NOTHING_TO_DISPEL = 0x51, - SPELL_FAILED_NOTHING_TO_STEAL = 0x52, - SPELL_FAILED_ONLY_ABOVEWATER = 0x53, - SPELL_FAILED_ONLY_DAYTIME = 0x54, - SPELL_FAILED_ONLY_INDOORS = 0x55, - SPELL_FAILED_ONLY_MOUNTED = 0x56, - SPELL_FAILED_ONLY_NIGHTTIME = 0x57, - SPELL_FAILED_ONLY_OUTDOORS = 0x58, - SPELL_FAILED_ONLY_SHAPESHIFT = 0x59, - SPELL_FAILED_ONLY_STEALTHED = 0x5A, - SPELL_FAILED_ONLY_UNDERWATER = 0x5B, - SPELL_FAILED_OUT_OF_RANGE = 0x5C, - SPELL_FAILED_PACIFIED = 0x5D, - SPELL_FAILED_POSSESSED = 0x5E, - SPELL_FAILED_REAGENTS = 0x5F, - SPELL_FAILED_REQUIRES_AREA = 0x60, - SPELL_FAILED_REQUIRES_SPELL_FOCUS = 0x61, - SPELL_FAILED_ROOTED = 0x62, - SPELL_FAILED_SILENCED = 0x63, - SPELL_FAILED_SPELL_IN_PROGRESS = 0x64, - SPELL_FAILED_SPELL_LEARNED = 0x65, - SPELL_FAILED_SPELL_UNAVAILABLE = 0x66, - SPELL_FAILED_STUNNED = 0x67, - SPELL_FAILED_TARGETS_DEAD = 0x68, - SPELL_FAILED_TARGET_AFFECTING_COMBAT = 0x69, - SPELL_FAILED_TARGET_AURASTATE = 0x6A, - SPELL_FAILED_TARGET_DUELING = 0x6B, - SPELL_FAILED_TARGET_ENEMY = 0x6C, - SPELL_FAILED_TARGET_ENRAGED = 0x6D, - SPELL_FAILED_TARGET_FRIENDLY = 0x6E, - SPELL_FAILED_TARGET_IN_COMBAT = 0x6F, - SPELL_FAILED_TARGET_IS_PLAYER = 0x70, - SPELL_FAILED_TARGET_IS_PLAYER_CONTROLLED = 0x71, - SPELL_FAILED_TARGET_NOT_DEAD = 0x72, - SPELL_FAILED_TARGET_NOT_IN_PARTY = 0x73, - SPELL_FAILED_TARGET_NOT_LOOTED = 0x74, - SPELL_FAILED_TARGET_NOT_PLAYER = 0x75, - SPELL_FAILED_TARGET_NO_POCKETS = 0x76, - SPELL_FAILED_TARGET_NO_WEAPONS = 0x77, - SPELL_FAILED_TARGET_UNSKINNABLE = 0x78, - SPELL_FAILED_THIRST_SATIATED = 0x79, - SPELL_FAILED_TOO_CLOSE = 0x7A, - SPELL_FAILED_TOO_MANY_OF_ITEM = 0x7B, - SPELL_FAILED_TOTEM_CATEGORY = 0x7C, - SPELL_FAILED_TOTEMS = 0x7D, - SPELL_FAILED_TRAINING_POINTS = 0x7E, - SPELL_FAILED_TRY_AGAIN = 0x7F, - SPELL_FAILED_UNIT_NOT_BEHIND = 0x80, - SPELL_FAILED_UNIT_NOT_INFRONT = 0x81, - SPELL_FAILED_WRONG_PET_FOOD = 0x82, - SPELL_FAILED_NOT_WHILE_FATIGUED = 0x83, - SPELL_FAILED_TARGET_NOT_IN_INSTANCE = 0x84, - SPELL_FAILED_NOT_WHILE_TRADING = 0x85, - SPELL_FAILED_TARGET_NOT_IN_RAID = 0x86, - SPELL_FAILED_DISENCHANT_WHILE_LOOTING = 0x87, - SPELL_FAILED_PROSPECT_WHILE_LOOTING = 0x88, - SPELL_FAILED_PROSPECT_NEED_MORE = 0x89, - SPELL_FAILED_TARGET_FREEFORALL = 0x8A, - SPELL_FAILED_NO_EDIBLE_CORPSES = 0x8B, - SPELL_FAILED_ONLY_BATTLEGROUNDS = 0x8C, - SPELL_FAILED_TARGET_NOT_GHOST = 0x8D, - SPELL_FAILED_TOO_MANY_SKILLS = 0x8E, - SPELL_FAILED_TRANSFORM_UNUSABLE = 0x8F, - SPELL_FAILED_WRONG_WEATHER = 0x90, - SPELL_FAILED_DAMAGE_IMMUNE = 0x91, - SPELL_FAILED_PREVENTED_BY_MECHANIC = 0x92, - SPELL_FAILED_PLAY_TIME = 0x93, - SPELL_FAILED_REPUTATION = 0x94, - SPELL_FAILED_MIN_SKILL = 0x95, - SPELL_FAILED_NOT_IN_ARENA = 0x96, - SPELL_FAILED_NOT_ON_SHAPESHIFT = 0x97, - SPELL_FAILED_NOT_ON_STEALTHED = 0x98, - SPELL_FAILED_NOT_ON_DAMAGE_IMMUNE = 0x99, - SPELL_FAILED_NOT_ON_MOUNTED = 0x9A, - SPELL_FAILED_TOO_SHALLOW = 0x9B, - SPELL_FAILED_TARGET_NOT_IN_SANCTUARY = 0x9C, - SPELL_FAILED_TARGET_IS_TRIVIAL = 0x9D, - SPELL_FAILED_BM_OR_INVISGOD = 0x9E, - SPELL_FAILED_EXPERT_RIDING_REQUIREMENT = 0x9F, - SPELL_FAILED_ARTISAN_RIDING_REQUIREMENT = 0xA0, - SPELL_FAILED_NOT_IDLE = 0xA1, - SPELL_FAILED_NOT_INACTIVE = 0xA2, - SPELL_FAILED_PARTIAL_PLAYTIME = 0xA3, - SPELL_FAILED_NO_PLAYTIME = 0xA4, - SPELL_FAILED_NOT_IN_BATTLEGROUND = 0xA5, - SPELL_FAILED_ONLY_IN_ARENA = 0xA6, - SPELL_FAILED_TARGET_LOCKED_TO_RAID_INSTANCE = 0xA7, - SPELL_FAILED_UNKNOWN = 0xA8, +// only used in code +enum SpellCategories +{ + SPELLCATEGORY_HEALTH_MANA_POTIONS = 4, + SPELLCATEGORY_DEVOUR_MAGIC = 12 }; enum SpellFamilyNames @@ -223,8 +60,12 @@ enum SpellFamilyNames SPELLFAMILY_HUNTER = 9, SPELLFAMILY_PALADIN = 10, SPELLFAMILY_SHAMAN = 11, - SPELLFAMILY_UNK2 = 12, - SPELLFAMILY_POTION = 13 + SPELLFAMILY_UNK2 = 12, // 2 spells (silence resistance) + SPELLFAMILY_POTION = 13, + // 14 - unused + SPELLFAMILY_DEATHKNIGHT = 15, + // 16 - unused + SPELLFAMILY_PET = 17 }; enum SpellDisableTypes @@ -256,15 +97,15 @@ enum SpellSelectTargetTypes }; //Some SpellFamilyFlags -#define SPELLFAMILYFLAG_ROGUE_VANISH 0x000000800LL -#define SPELLFAMILYFLAG_ROGUE_STEALTH 0x000400000LL -#define SPELLFAMILYFLAG_ROGUE_BACKSTAB 0x000800004LL -#define SPELLFAMILYFLAG_ROGUE_SAP 0x000000080LL -#define SPELLFAMILYFLAG_ROGUE_FEINT 0x008000000LL -#define SPELLFAMILYFLAG_ROGUE_KIDNEYSHOT 0x000200000LL -#define SPELLFAMILYFLAG_ROGUE__FINISHING_MOVE 0x9003E0000LL -#define SPELLFAMILYFLAG_WARRIOR_SUNDERARMOR 0x000004000LL -#define SPELLFAMILYFLAG_SHAMAN_FROST_SHOCK 0x080000000LL +#define SPELLFAMILYFLAG_ROGUE_VANISH 0x00000800 +#define SPELLFAMILYFLAG_ROGUE_STEALTH 0x00400000 +#define SPELLFAMILYFLAG_ROGUE_BACKSTAB 0x00800004 +#define SPELLFAMILYFLAG_ROGUE_SAP 0x00000080 +#define SPELLFAMILYFLAG_ROGUE_FEINT 0x08000000 +#define SPELLFAMILYFLAG_ROGUE_KIDNEYSHOT 0x00200000 +//#define SPELLFAMILYFLAG_ROGUE__FINISHING_MOVE 0x9003E0000LL +#define SPELLFAMILYFLAG_WARRIOR_SUNDERARMOR 0x00004000 +#define SPELLFAMILYFLAG_SHAMAN_FROST_SHOCK 0x80000000 // Spell clasification enum SpellSpecific @@ -290,7 +131,9 @@ enum SpellSpecific SPELL_WELL_FED = 18, SPELL_DRINK = 19, SPELL_FOOD = 20, - SPELL_CHARM = 21, + SPELL_PRESENCE = 21, + SPELL_CHARM = 22, + SPELL_SCROLL = 23 }; #define SPELL_LINKED_MAX_SPELLS 200000 @@ -306,19 +149,36 @@ enum SpellLinkedType SpellSpecific GetSpellSpecific(uint32 spellId); // Different spell properties -inline float GetSpellRadius(SpellRadiusEntry const *radius) { return (radius ? radius->Radius : 0); } +inline float GetSpellRadiusForHostile(SpellRadiusEntry const *radius) { return (radius ? radius->radiusHostile : 0); } +inline float GetSpellRadiusForFriend(SpellRadiusEntry const *radius) { return (radius ? radius->radiusFriend : 0); } uint32 GetSpellCastTime(SpellEntry const* spellInfo, Spell const* spell = NULL); -inline float GetSpellMinRange(SpellRangeEntry const *range) { return (range ? range->minRange : 0); } -inline float GetSpellMaxRange(SpellRangeEntry const *range) { return (range ? range->maxRange : 0); } +bool GetDispelChance(Unit* caster, uint32 spellId); +inline float GetSpellMinRangeForHostile(SpellRangeEntry const *range) { return (range ? range->minRangeHostile : 0); } +inline float GetSpellMaxRangeForHostile(SpellRangeEntry const *range) { return (range ? range->maxRangeHostile : 0); } +inline float GetSpellMinRangeForFriend(SpellRangeEntry const *range) { return (range ? range->minRangeFriend : 0); } +inline float GetSpellMaxRangeForFriend(SpellRangeEntry const *range) { return (range ? range->maxRangeFriend : 0); } inline uint32 GetSpellRangeType(SpellRangeEntry const *range) { return (range ? range->type : 0); } inline uint32 GetSpellRecoveryTime(SpellEntry const *spellInfo) { return spellInfo->RecoveryTime > spellInfo->CategoryRecoveryTime ? spellInfo->RecoveryTime : spellInfo->CategoryRecoveryTime; } int32 GetSpellDuration(SpellEntry const *spellInfo); int32 GetSpellMaxDuration(SpellEntry const *spellInfo); +/*struct DispelEntry +{ + uint64 casterGuid; + uint32 spellId; + Unit * caster; + uint8 stackAmount; + + bool operator < (const DispelEntry & _Right) const + { + return (spellId != _Right.spellId ? spellId < _Right.spellId : casterGuid < _Right.casterGuid); + } +};*/ + inline bool IsSpellHaveEffect(SpellEntry const *spellInfo, SpellEffects effect) { for(int i= 0; i < 3; ++i) - if(spellInfo->Effect[i]==effect) + if(SpellEffects(spellInfo->Effect[i])==effect) return true; return false; } @@ -329,30 +189,46 @@ inline bool IsSealSpell(SpellEntry const *spellInfo) { //Collection of all the seal family flags. No other paladin spell has any of those. return spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN && - ( spellInfo->SpellFamilyFlags & 0x4000A000200LL ); + ( spellInfo->SpellFamilyFlags[1] & 0x26000C00 + || spellInfo->SpellFamilyFlags[0] & 0x0A000000 ); } inline bool IsElementalShield(SpellEntry const *spellInfo) { // family flags 10 (Lightning), 42 (Earth), 37 (Water), proc shield from T2 8 pieces bonus - return (spellInfo->SpellFamilyFlags & 0x42000000400LL) || spellInfo->Id == 23552; + return (spellInfo->SpellFamilyFlags[1] & 0x420 + || spellInfo->SpellFamilyFlags[0] & 0x00000400) + || spellInfo->Id == 23552; +} + +inline bool IsExplicitDiscoverySpell(SpellEntry const *spellInfo) +{ + return spellInfo->Effect[0]==SPELL_EFFECT_CREATE_ITEM_2 && spellInfo->Effect[1]==SPELL_EFFECT_SCRIPT_EFFECT; } -int32 CompareAuraRanks(uint32 spellId_1, uint32 effIndex_1, uint32 spellId_2, uint32 effIndex_2); +inline bool IsLootCraftingSpell(SpellEntry const *spellInfo) +{ + return spellInfo->Effect[0]==SPELL_EFFECT_CREATE_ITEM_2 && + (spellInfo->Effect[1]==SPELL_EFFECT_SCRIPT_EFFECT || !spellInfo->EffectItemType[0]); +} + +bool IsHigherHankOfSpell(uint32 spellId_1,uint32 spellId_2); bool IsSingleFromSpellSpecificPerCaster(uint32 spellSpec1, uint32 spellSpec2); bool IsSingleFromSpellSpecificPerTarget(uint32 spellSpec1, uint32 spellSpec2); bool IsPassiveSpell(uint32 spellId); +bool IsAutocastableSpell(uint32 spellId); -inline bool IsDeathPersistentSpell(SpellEntry const *spellInfo) +inline bool IsPassiveSpellStackableWithRanks(SpellEntry const* spellProto) { - switch(spellInfo->Id) - { - case 40214: // Dragonmaw Illusion - case 35480: case 35481: case 35482: // Human Illusion - case 35483: case 39824: // Human Illusion - return true; - } + if(!IsPassiveSpell(spellProto->Id)) + return false; + + return !IsSpellHaveEffect(spellProto,SPELL_EFFECT_APPLY_AURA); +} + +inline bool IsDeathPersistentSpell(SpellEntry const *spellInfo) +{ return spellInfo->AttributesEx3 & SPELL_ATTR_EX3_DEATH_PERSISTENT; } @@ -361,8 +237,8 @@ inline bool IsNonCombatSpell(SpellEntry const *spellInfo) return (spellInfo->Attributes & SPELL_ATTR_CANT_USED_IN_COMBAT) != 0; } -bool IsPositiveSpell(uint32 spellId); -bool IsPositiveEffect(uint32 spellId, uint32 effIndex); +bool IsPositiveSpell(uint32 spellId, bool deep = false); +bool IsPositiveEffect(uint32 spellId, uint32 effIndex, bool deep = false); bool IsPositiveTarget(uint32 targetA, uint32 targetB); bool IsSingleTargetSpell(SpellEntry const *spellInfo); @@ -370,9 +246,8 @@ bool IsSingleTargetSpells(SpellEntry const *spellInfo1, SpellEntry const *spellI bool IsAuraAddedBySpell(uint32 auraType, uint32 spellId); -bool IsSpellAllowedInLocation(SpellEntry const *spellInfo,uint32 map_id,uint32 zone_id,uint32 area_id); - extern bool IsAreaEffectTarget[TOTAL_SPELL_TARGETS]; + inline bool IsAreaOfEffectSpell(SpellEntry const *spellInfo) { if(IsAreaEffectTarget[spellInfo->EffectImplicitTargetA[0]] || IsAreaEffectTarget[spellInfo->EffectImplicitTargetB[0]]) @@ -387,6 +262,7 @@ inline bool IsAreaOfEffectSpell(SpellEntry const *spellInfo) inline bool IsAreaAuraEffect(uint32 effect) { if( effect == SPELL_EFFECT_APPLY_AREA_AURA_PARTY || + effect == SPELL_EFFECT_APPLY_AREA_AURA_RAID || effect == SPELL_EFFECT_APPLY_AREA_AURA_FRIEND || effect == SPELL_EFFECT_APPLY_AREA_AURA_ENEMY || effect == SPELL_EFFECT_APPLY_AREA_AURA_PET || @@ -419,7 +295,12 @@ inline bool isSpellBreakStealth(SpellEntry const* spellInfo) return !(spellInfo->AttributesEx & SPELL_ATTR_EX_NOT_BREAK_STEALTH); } -uint8 GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form); +inline bool IsAutoRepeatRangedSpell(SpellEntry const* spellInfo) +{ + return (spellInfo->Attributes & SPELL_ATTR_RANGED) && (spellInfo->AttributesEx2 & SPELL_ATTR_EX2_AUTOREPEAT_FLAG); +} + +SpellCastResult GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form); inline bool IsChanneledSpell(SpellEntry const* spellInfo) { @@ -446,6 +327,17 @@ inline uint32 GetSpellMechanicMask(SpellEntry const* spellInfo, int32 effect) return mask; } +inline uint32 GetAllSpellMechanicMask(SpellEntry const* spellInfo) +{ + uint32 mask = 0; + if (spellInfo->Mechanic) + mask |= 1<<spellInfo->Mechanic; + for (int i=0; i< 3; ++i) + if (spellInfo->EffectMechanic[i]) + mask |= 1<<spellInfo->EffectMechanic[i]; + return mask; +} + inline Mechanics GetEffectMechanic(SpellEntry const* spellInfo, int32 effect) { if (spellInfo->EffectMechanic[effect]) @@ -470,7 +362,7 @@ bool IsDiminishingReturnsGroupDurationLimited(DiminishingGroup group); DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group); // Spell affects related declarations (accessed using SpellMgr functions) -typedef std::map<uint32, uint64> SpellAffectMap; +typedef UNORDERED_MAP<uint32, flag96> SpellAffectMap; // Spell proc event related declarations (accessed using SpellMgr functions) enum ProcFlags @@ -478,16 +370,16 @@ enum ProcFlags PROC_FLAG_NONE = 0x00000000, PROC_FLAG_KILLED = 0x00000001, // 00 Killed by agressor - PROC_FLAG_KILL_AND_GET_XP = 0x00000002, // 01 Kill that yields experience or honor + PROC_FLAG_KILL = 0x00000002, // 01 Kill target (in most cases need XP/Honor reward) - PROC_FLAG_SUCCESSFUL_MILEE_HIT = 0x00000004, // 02 Successful melee attack - PROC_FLAG_TAKEN_MELEE_HIT = 0x00000008, // 03 Taken damage from melee strike hit + PROC_FLAG_SUCCESSFUL_MILEE_HIT = 0x00000004, // 02 Successful melee auto attack + PROC_FLAG_TAKEN_MELEE_HIT = 0x00000008, // 03 Taken damage from melee auto attack hit PROC_FLAG_SUCCESSFUL_MELEE_SPELL_HIT = 0x00000010, // 04 Successful attack by Spell that use melee weapon PROC_FLAG_TAKEN_MELEE_SPELL_HIT = 0x00000020, // 05 Taken damage by Spell that use melee weapon - PROC_FLAG_SUCCESSFUL_RANGED_HIT = 0x00000040, // 06 Successful Ranged attack (all ranged attack deal as spell so newer set :( ) - PROC_FLAG_TAKEN_RANGED_HIT = 0x00000080, // 07 Taken damage from ranged attack (all ranged attack deal as spell so newer set :( ) + PROC_FLAG_SUCCESSFUL_RANGED_HIT = 0x00000040, // 06 Successful Ranged auto attack + PROC_FLAG_TAKEN_RANGED_HIT = 0x00000080, // 07 Taken damage from ranged auto attack PROC_FLAG_SUCCESSFUL_RANGED_SPELL_HIT = 0x00000100, // 08 Successful Ranged attack by Spell that use ranged weapon PROC_FLAG_TAKEN_RANGED_SPELL_HIT = 0x00000200, // 09 Taken damage by Spell that use ranged weapon @@ -512,6 +404,7 @@ enum ProcFlags PROC_FLAG_TAKEN_OFFHAND_HIT = 0x00400000, // 22 Taken off-hand melee attacks(not used) PROC_FLAG_SUCCESSFUL_OFFHAND_HIT = 0x00800000 // 23 Successful off-hand melee attacks +// PROC_FLAG_DEATH = 0x01000000 }; #define MELEE_BASED_TRIGGER_MASK (PROC_FLAG_SUCCESSFUL_MILEE_HIT | \ @@ -538,19 +431,20 @@ enum ProcFlagsEx PROC_EX_DEFLECT = 0x0000200, PROC_EX_ABSORB = 0x0000400, PROC_EX_REFLECT = 0x0000800, - PROC_EX_INTERRUPT = 0x0001000, // Melle hit result can be Interrupt (not used) - PROC_EX_RESERVED1 = 0x0002000, - PROC_EX_RESERVED2 = 0x0004000, - PROC_EX_RESERVED3 = 0x0008000, + PROC_EX_INTERRUPT = 0x0001000, // Melee hit result can be Interrupt (not used) + PROC_EX_AURA_REMOVE_DESTROY = 0x0002000, // aura absorb destroy or dispel + PROC_EX_AURA_REMOVE_EXPIRE = 0x0004000, // aura remove by default and by cancel PROC_EX_EX_TRIGGER_ALWAYS = 0x0010000, // If set trigger always ( no matter another flags) used for drop charges - PROC_EX_EX_ONE_TIME_TRIGGER = 0x0020000 // If set trigger always but only one time + PROC_EX_EX_ONE_TIME_TRIGGER = 0x0020000 // If set trigger always but only one time (not used) }; +#define AURA_REMOVE_PROC_EX_MASK \ + (PROC_EX_AURA_REMOVE_DESTROY | PROC_EX_AURA_REMOVE_EXPIRE) struct SpellProcEventEntry { uint32 schoolMask; // if nonzero - bit mask for matching proc condition based on spell candidate's school: Fire=2, Mask=1<<(2-1)=2 uint32 spellFamilyName; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyNamer value - uint64 spellFamilyMask; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyFlags (like auras 107 and 108 do) + flag96 spellFamilyMask; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyFlags (like auras 107 and 108 do) uint32 procFlags; // bitmask for matching proc event uint32 procEx; // proc Extend info (see ProcFlagsEx) float ppmRate; // for melee (ranged?) damage spells - proc rate per minute. if zero, falls back to flat chance from Spell.dbc @@ -558,7 +452,15 @@ struct SpellProcEventEntry uint32 cooldown; // hidden cooldown used for some spell proc events, applied to _triggered_spell_ }; +struct SpellBonusEntry +{ + float direct_damage; + float dot_damage; + float ap_bonus; +}; + typedef UNORDERED_MAP<uint32, SpellProcEventEntry> SpellProcEventMap; +typedef UNORDERED_MAP<uint32, SpellBonusEntry> SpellBonusMap; #define ELIXIR_BATTLE_MASK 0x1 #define ELIXIR_GUARDIAN_MASK 0x2 @@ -651,6 +553,32 @@ class PetAura }; typedef std::map<uint16, PetAura> SpellPetAuraMap; +struct SpellArea +{ + uint32 spellId; + uint32 areaId; // zone/subzone/or 0 is not limited to zone + uint32 questStart; // quest start (quest must be active or rewarded for spell apply) + uint32 questEnd; // quest end (quest don't must be rewarded for spell apply) + int32 auraSpell; // spell aura must be applied for spell apply )if possitive) and it don't must be applied in other case + uint32 raceMask; // can be applied only to races + Gender gender; // can be applied only to gender + bool questStartCanActive; // if true then quest start can be active (not only rewarded) + bool autocast; // if true then auto applied at area enter, in other case just allowed to cast + + // helpers + bool IsFitToRequirements(Player const* player, uint32 newZone, uint32 newArea) const; +}; + +typedef std::multimap<uint32,SpellArea> SpellAreaMap; +typedef std::multimap<uint32,SpellArea const*> SpellAreaForQuestMap; +typedef std::multimap<uint32,SpellArea const*> SpellAreaForAuraMap; +typedef std::multimap<uint32,SpellArea const*> SpellAreaForAreaMap; +typedef std::pair<SpellAreaMap::const_iterator,SpellAreaMap::const_iterator> SpellAreaMapBounds; +typedef std::pair<SpellAreaForQuestMap::const_iterator,SpellAreaForQuestMap::const_iterator> SpellAreaForQuestMapBounds; +typedef std::pair<SpellAreaForAuraMap::const_iterator, SpellAreaForAuraMap::const_iterator> SpellAreaForAuraMapBounds; +typedef std::pair<SpellAreaForAreaMap::const_iterator, SpellAreaForAreaMap::const_iterator> SpellAreaForAreaMapBounds; + + // Spell rank chain (accessed using SpellMgr functions) struct SpellChainNode { @@ -681,6 +609,7 @@ typedef std::map<uint32, SpellLearnSkillNode> SpellLearnSkillMap; struct SpellLearnSpellNode { uint32 spell; + bool active; // show in spellbook or not bool autoLearned; }; @@ -688,6 +617,9 @@ typedef std::multimap<uint32, SpellLearnSpellNode> SpellLearnSpellMap; typedef std::multimap<uint32, SkillLineAbilityEntry const*> SkillLineAbilityMap; +typedef std::multimap<uint32, std::pair < uint32,uint32 > >PetLevelupSpellMap; +//typedef std::map<uint32, PetLevelupSpellSet> PetLevelupSpellMap; + inline bool IsPrimaryProfessionSkill(uint32 skill) { SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(skill); @@ -725,6 +657,11 @@ typedef std::vector<uint32> SpellCustomAttribute; typedef std::map<int32, std::vector<int32> > SpellLinkedMap; +inline bool IsProfessionOrRidingSkill(uint32 skill) +{ + return IsProfessionSkill(skill) || skill == SKILL_RIDING; +} + class SpellMgr { // Constructors @@ -732,18 +669,18 @@ class SpellMgr SpellMgr(); ~SpellMgr(); - // Accessors (const or static functions) + // Accessors (const or static functions) public: // Spell affects - uint64 GetSpellAffectMask(uint16 spellId, uint8 effectId) const + flag96 const*GetSpellAffect(uint16 spellId, uint8 effectId) const { SpellAffectMap::const_iterator itr = mSpellAffectMap.find((spellId<<8) + effectId); if( itr != mSpellAffectMap.end( ) ) - return itr->second; + return &itr->second; return 0; } - bool IsAffectedBySpell(SpellEntry const *spellInfo, uint32 spellId, uint8 effectId, uint64 familyFlags) const; + bool IsAffectedByMod(SpellEntry const *spellInfo, SpellModifier *mod) const; SpellElixirMap const& GetSpellElixirMap() const { return mSpellElixirs; } @@ -778,7 +715,24 @@ class SpellMgr return NULL; } - static bool IsSpellProcEventCanTriggeredBy( SpellProcEventEntry const * spellProcEvent, uint32 EventProcFlag, SpellEntry const * procSpell, uint32 procFlags, uint32 procExtra, bool active); + bool IsSpellProcEventCanTriggeredBy( SpellProcEventEntry const * spellProcEvent, uint32 EventProcFlag, SpellEntry const * procSpell, uint32 procFlags, uint32 procExtra, bool active); + + // Spell bonus data + SpellBonusEntry const* GetSpellBonusData(uint32 spellId) const + { + // Lookup data + SpellBonusMap::const_iterator itr = mSpellBonusMap.find(spellId); + if( itr != mSpellBonusMap.end( ) ) + return &itr->second; + // Not found, try lookup for 1 spell rank if exist + if (uint32 rank_1 = GetFirstSpellInChain(spellId)) + { + SpellBonusMap::const_iterator itr = mSpellBonusMap.find(rank_1); + if( itr != mSpellBonusMap.end( ) ) + return &itr->second; + } + return NULL; + } // Spell target coordinates SpellTargetPosition const* GetSpellTargetPosition(uint32 spell_id) const @@ -824,6 +778,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 @@ -880,7 +842,7 @@ class SpellMgr bool IsSpellLearnSpell(uint32 spell_id) const { - return mSpellLearnSpells.count(spell_id)!=0; + return mSpellLearnSpells.find(spell_id) != mSpellLearnSpells.end(); } SpellLearnSpellMap::const_iterator GetBeginSpellLearnSpell(uint32 spell_id) const @@ -903,10 +865,14 @@ class SpellMgr return false; } + static bool IsProfessionOrRidingSpell(uint32 spellId); static bool IsProfessionSpell(uint32 spellId); 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 { @@ -946,11 +912,6 @@ class SpellMgr return 0; else return mSpellCustomAttr[spell_id]; - /*SpellCustomAttrMap::const_iterator itr = mSpellCustomAttrMap.find(spell_id); - if(itr != mSpellCustomAttrMap.end()) - return itr->second; - else - return 0;*/ } const std::vector<int32> *GetSpellLinked(int32 spell_id) const @@ -962,7 +923,42 @@ class SpellMgr SpellEffectTargetTypes EffectTargetType[TOTAL_SPELL_EFFECTS]; SpellSelectTargetTypes SpellTargetType[TOTAL_SPELL_TARGETS]; - // Modifiers + SpellCastResult GetSpellAllowedInLocationError(SpellEntry const *spellInfo, uint32 map_id, uint32 zone_id, uint32 area_id, Player const* player = NULL); + + SpellAreaMapBounds GetSpellAreaMapBounds(uint32 spell_id) const + { + return SpellAreaMapBounds(mSpellAreaMap.lower_bound(spell_id),mSpellAreaMap.upper_bound(spell_id)); + } + + SpellAreaForQuestMapBounds GetSpellAreaForQuestMapBounds(uint32 quest_id, bool active) const + { + if(active) + return SpellAreaForQuestMapBounds(mSpellAreaForActiveQuestMap.lower_bound(quest_id),mSpellAreaForActiveQuestMap.upper_bound(quest_id)); + else + return SpellAreaForQuestMapBounds(mSpellAreaForQuestMap.lower_bound(quest_id),mSpellAreaForQuestMap.upper_bound(quest_id)); + } + + SpellAreaForQuestMapBounds GetSpellAreaForQuestEndMapBounds(uint32 quest_id) const + { + return SpellAreaForQuestMapBounds(mSpellAreaForQuestEndMap.lower_bound(quest_id),mSpellAreaForQuestEndMap.upper_bound(quest_id)); + } + + SpellAreaForAuraMapBounds GetSpellAreaForAuraMapBounds(uint32 spell_id) const + { + return SpellAreaForAuraMapBounds(mSpellAreaForAuraMap.lower_bound(spell_id),mSpellAreaForAuraMap.upper_bound(spell_id)); + } + + SpellAreaForAreaMapBounds GetSpellAreaForAreaMapBounds(uint32 area_id) const + { + return SpellAreaForAreaMapBounds(mSpellAreaForAreaMap.lower_bound(area_id),mSpellAreaForAreaMap.upper_bound(area_id)); + } + + PetLevelupSpellMap const * GetPetLevelupSpellMap() const + { + return &mPetLevelupSpellMap; + } + + // Modifiers public: static SpellMgr& Instance(); @@ -975,12 +971,15 @@ class SpellMgr void LoadSpellAffects(); void LoadSpellElixirs(); void LoadSpellProcEvents(); + void LoadSpellBonusess(); void LoadSpellTargetPositions(); void LoadSpellThreats(); void LoadSkillLineAbilityMap(); void LoadSpellPetAuras(); void LoadSpellCustomAttr(); void LoadSpellLinked(); + void LoadPetLevelupSpellMap(); + void LoadSpellAreas(); private: SpellScriptTarget mSpellScriptTarget; @@ -993,10 +992,18 @@ class SpellMgr SpellAffectMap mSpellAffectMap; SpellElixirMap mSpellElixirs; SpellProcEventMap mSpellProcEventMap; + SpellBonusMap mSpellBonusMap; SkillLineAbilityMap mSkillLineAbilityMap; SpellPetAuraMap mSpellPetAuraMap; SpellCustomAttribute mSpellCustomAttr; SpellLinkedMap mSpellLinkedMap; + PetLevelupSpellMap mPetLevelupSpellMap; + SpellAreaMap mSpellAreaMap; + SpellAreaForQuestMap mSpellAreaForQuestMap; + SpellAreaForQuestMap mSpellAreaForActiveQuestMap; + SpellAreaForQuestMap mSpellAreaForQuestEndMap; + SpellAreaForAuraMap mSpellAreaForAuraMap; + SpellAreaForAreaMap mSpellAreaForAreaMap; }; #define spellmgr SpellMgr::Instance() diff --git a/src/game/StatSystem.cpp b/src/game/StatSystem.cpp index ae96953e337..f9d237b9d5e 100644 --- a/src/game/StatSystem.cpp +++ b/src/game/StatSystem.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -51,24 +51,17 @@ bool Player::UpdateStats(Stats stat) switch(stat) { case STAT_STRENGTH: - UpdateAttackPowerAndDamage(); UpdateShieldBlockValue(); break; case STAT_AGILITY: UpdateArmor(); - UpdateAttackPowerAndDamage(true); - if(getClass() == CLASS_ROGUE || getClass() == CLASS_HUNTER || getClass() == CLASS_DRUID && m_form==FORM_CAT) - UpdateAttackPowerAndDamage(); - UpdateAllCritPercentages(); UpdateDodgePercentage(); break; - case STAT_STAMINA: UpdateMaxHealth(); break; case STAT_INTELLECT: UpdateMaxPower(POWER_MANA); UpdateAllSpellCritChances(); - UpdateAttackPowerAndDamage(true); //SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT, only intellect currently UpdateArmor(); //SPELL_AURA_MOD_RESISTANCE_OF_INTELLECT_PERCENT, only armor currently break; @@ -78,11 +71,60 @@ bool Player::UpdateStats(Stats stat) default: break; } + + if (stat == STAT_STRENGTH) + { + UpdateAttackPowerAndDamage(false); + if (HasAuraTypeWithMiscvalue(SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT, stat)) + UpdateAttackPowerAndDamage(true); + } + else if (stat == STAT_AGILITY) + { + UpdateAttackPowerAndDamage(false); + UpdateAttackPowerAndDamage(true); + } + else + { + // Need update (exist AP from stat auras) + if (HasAuraTypeWithMiscvalue(SPELL_AURA_MOD_ATTACK_POWER_OF_STAT_PERCENT, stat)) + UpdateAttackPowerAndDamage(false); + if (HasAuraTypeWithMiscvalue(SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT, stat)) + UpdateAttackPowerAndDamage(true); + } + UpdateSpellDamageAndHealingBonus(); UpdateManaRegen(); + + // Update ratings in exist SPELL_AURA_MOD_RATING_FROM_STAT and only depends from stat + uint32 mask = 0; + AuraEffectList const& modRatingFromStat = GetAurasByType(SPELL_AURA_MOD_RATING_FROM_STAT); + for(AuraEffectList::const_iterator i = modRatingFromStat.begin();i != modRatingFromStat.end(); ++i) + if (Stats((*i)->GetMiscBValue()) == stat) + mask |= (*i)->GetMiscValue(); + if (mask) + { + for (uint32 rating = 0; rating < MAX_COMBAT_RATING; ++rating) + if (mask & (1 << rating)) + ApplyRatingMod(CombatRating(rating), 0, true); + } return true; } +void Player::ApplySpellDamageBonus(int32 amount, bool apply) +{ + m_baseSpellDamage+=apply?amount:-amount; + // For speed just update for client + for(int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; i++) + ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS+i, amount, apply); +} + +void Player::ApplySpellHealingBonus(int32 amount, bool apply) +{ + m_baseSpellHealing+=apply?amount:-amount; + // For speed just update for client + ApplyModUInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS, amount, apply); +} + void Player::UpdateSpellDamageAndHealingBonus() { // Magic damage modifiers implemented in Unit::SpellDamageBonus @@ -150,12 +192,11 @@ void Player::UpdateArmor() value += GetModifierValue(unitMod, TOTAL_VALUE); //add dynamic flat mods - AuraList const& mResbyIntellect = GetAurasByType(SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT); - for(AuraList::const_iterator i = mResbyIntellect.begin();i != mResbyIntellect.end(); ++i) + AuraEffectList const& mResbyIntellect = GetAurasByType(SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT); + for(AuraEffectList::const_iterator i = mResbyIntellect.begin();i != mResbyIntellect.end(); ++i) { - Modifier* mod = (*i)->GetModifier(); - if(mod->m_miscvalue & SPELL_SCHOOL_MASK_NORMAL) - value += int32(GetStat(Stats((*i)->GetMiscBValue())) * (*i)->GetModifierValue() / 100.0f); + if((*i)->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) + value += int32(GetStat(Stats((*i)->GetMiscBValue())) * (*i)->GetAmount() / 100.0f); } value *= GetModifierValue(unitMod, TOTAL_PCT); @@ -213,6 +254,12 @@ void Player::UpdateMaxPower(Powers power) SetMaxPower(power, uint32(value)); } +void Player::ApplyFeralAPBonus(int32 amount, bool apply) +{ + m_baseFeralAP+= apply ? amount:-amount; + UpdateAttackPowerAndDamage(); +} + void Player::UpdateAttackPowerAndDamage(bool ranged ) { float val2 = 0.0f; @@ -253,11 +300,12 @@ void Player::UpdateAttackPowerAndDamage(bool ranged ) { switch(getClass()) { - case CLASS_WARRIOR: val2 = level*3.0f + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break; - case CLASS_PALADIN: val2 = level*3.0f + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break; - case CLASS_ROGUE: val2 = level*2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f; break; - case CLASS_HUNTER: val2 = level*2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f; break; - case CLASS_SHAMAN: val2 = level*2.0f + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break; + case CLASS_WARRIOR: val2 = level*3.0f + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break; + case CLASS_PALADIN: val2 = level*3.0f + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break; + case CLASS_DEATH_KNIGHT: val2 = level*3.0f + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break; + case CLASS_ROGUE: val2 = level*2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f; break; + case CLASS_HUNTER: val2 = level*2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f; break; + case CLASS_SHAMAN: val2 = level*2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f; break; case CLASS_DRUID: { //Check if Predatory Strikes is skilled @@ -269,13 +317,13 @@ void Player::UpdateAttackPowerAndDamage(bool ranged ) case FORM_DIREBEAR: case FORM_MOONKIN: { - Unit::AuraList const& mDummy = GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraList::const_iterator itr = mDummy.begin(); itr != mDummy.end(); ++itr) + Unit::AuraEffectList const& mDummy = GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraEffectList::const_iterator itr = mDummy.begin(); itr != mDummy.end(); ++itr) { // Predatory Strikes if ((*itr)->GetSpellProto()->SpellIconID == 1563) { - mLevelMult = (*itr)->GetModifier()->m_amount / 100.0f; + mLevelMult = (*itr)->GetAmount() / 100.0f; break; } } @@ -286,12 +334,12 @@ void Player::UpdateAttackPowerAndDamage(bool ranged ) switch(m_form) { case FORM_CAT: - val2 = getLevel()*(mLevelMult+2.0f) + GetStat(STAT_STRENGTH)*2.0f + GetStat(STAT_AGILITY) - 20.0f; break; + val2 = getLevel()*(mLevelMult+2.0f) + GetStat(STAT_STRENGTH)*2.0f + GetStat(STAT_AGILITY) - 20.0f + m_baseFeralAP; break; case FORM_BEAR: case FORM_DIREBEAR: - val2 = getLevel()*(mLevelMult+3.0f) + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break; + val2 = getLevel()*(mLevelMult+3.0f) + GetStat(STAT_STRENGTH)*2.0f - 20.0f + m_baseFeralAP; break; case FORM_MOONKIN: - val2 = getLevel()*(mLevelMult+1.5f) + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break; + val2 = getLevel()*(mLevelMult+1.5f) + GetStat(STAT_STRENGTH)*2.0f - 20.0f + m_baseFeralAP; break; default: val2 = GetStat(STAT_STRENGTH)*2.0f - 20.0f; break; } @@ -309,17 +357,26 @@ void Player::UpdateAttackPowerAndDamage(bool ranged ) float attPowerMod = GetModifierValue(unitMod, TOTAL_VALUE); //add dynamic flat mods - if( ranged && (getClassMask() & CLASSMASK_WAND_USERS)==0) + if( ranged ) + { + if ((getClassMask() & CLASSMASK_WAND_USERS)==0) + { + AuraEffectList const& mRAPbyStat = GetAurasByType(SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT); + for(AuraEffectList::const_iterator i = mRAPbyStat.begin();i != mRAPbyStat.end(); ++i) + attPowerMod += int32(GetStat(Stats((*i)->GetMiscValue())) * (*i)->GetAmount() / 100.0f); + } + } + else { - AuraList const& mRAPbyIntellect = GetAurasByType(SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT); - for(AuraList::const_iterator i = mRAPbyIntellect.begin();i != mRAPbyIntellect.end(); ++i) - attPowerMod += int32(GetStat(Stats((*i)->GetModifier()->m_miscvalue)) * (*i)->GetModifierValue() / 100.0f); + AuraEffectList const& mAPbyStat = GetAurasByType(SPELL_AURA_MOD_ATTACK_POWER_OF_STAT_PERCENT); + for(AuraEffectList::const_iterator i = mAPbyStat.begin();i != mAPbyStat.end(); ++i) + attPowerMod += int32(GetStat(Stats((*i)->GetMiscValue())) * (*i)->GetAmount() / 100.0f); } float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f; - SetUInt32Value(index, (uint32)base_attPower); //UNIT_FIELD_(RANGED)_ATTACK_POWER field - SetUInt32Value(index_mod, (uint32)attPowerMod); //UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field + SetInt32Value(index, (uint32)base_attPower); //UNIT_FIELD_(RANGED)_ATTACK_POWER field + SetInt32Value(index_mod, (uint32)attPowerMod); //UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field SetFloatValue(index_mult, attPowerMultiplier); //UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field //automatically update weapon damage after attack power modification @@ -386,8 +443,15 @@ void Player::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, fl weapon_mindamage = lvl*0.85*att_speed; weapon_maxdamage = lvl*1.25*att_speed; } - else if(!IsUseEquipedWeapon(attType==BASE_ATTACK)) //check if player not in form but still can't use weapon (broken/etc) + else if(!CanUseAttackType(attType)) //check if player not in form but still can't use (disarm case) { + //cannot use ranged/off attack, set values to 0 + if (attType != BASE_ATTACK) + { + min_damage=0; + max_damage=0; + return; + } weapon_mindamage = BASE_MINDAMAGE; weapon_maxdamage = BASE_MAXDAMAGE; } @@ -554,6 +618,24 @@ void Player::UpdateSpellCritChance(uint32 school) SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1 + school, crit); } +void Player::UpdateMeleeHitChances() +{ + m_modMeleeHitChance = GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE); + m_modMeleeHitChance+= GetRatingBonusValue(CR_HIT_MELEE); +} + +void Player::UpdateRangedHitChances() +{ + m_modRangedHitChance = GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE); + m_modRangedHitChance+= GetRatingBonusValue(CR_HIT_RANGED); +} + +void Player::UpdateSpellHitChances() +{ + m_modSpellHitChance = GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_HIT_CHANCE); + m_modSpellHitChance+= GetRatingBonusValue(CR_HIT_SPELL); +} + void Player::UpdateAllSpellCritChances() { for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++) @@ -569,15 +651,15 @@ void Player::UpdateExpertise(WeaponAttackType attack) Item *weapon = GetWeaponForAttack(attack); - AuraList const& expAuras = GetAurasByType(SPELL_AURA_MOD_EXPERTISE); - for(AuraList::const_iterator itr = expAuras.begin(); itr != expAuras.end(); ++itr) + AuraEffectList const& expAuras = GetAurasByType(SPELL_AURA_MOD_EXPERTISE); + for(AuraEffectList::const_iterator itr = expAuras.begin(); itr != expAuras.end(); ++itr) { // item neutral spell if((*itr)->GetSpellProto()->EquippedItemClass == -1) - expertise += (*itr)->GetModifierValue(); + expertise += (*itr)->GetAmount(); // item dependent spell else if(weapon && weapon->IsFitToSpellRequirements((*itr)->GetSpellProto())) - expertise += (*itr)->GetModifierValue(); + expertise += (*itr)->GetAmount(); } if(expertise < 0) @@ -591,6 +673,12 @@ void Player::UpdateExpertise(WeaponAttackType attack) } } +void Player::ApplyManaRegenBonus(int32 amount, bool apply) +{ + m_baseManaRegen+= apply ? amount : -amount; + UpdateManaRegen(); +} + void Player::UpdateManaRegen() { float Intellect = GetStat(STAT_INTELLECT); @@ -600,33 +688,22 @@ void Player::UpdateManaRegen() power_regen *= GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_POWER_REGEN_PERCENT, POWER_MANA); // Mana regen from SPELL_AURA_MOD_POWER_REGEN aura - float power_regen_mp5 = GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, POWER_MANA) / 5.0f; + float power_regen_mp5 = (GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, POWER_MANA) + m_baseManaRegen) / 5.0f; // Get bonus from SPELL_AURA_MOD_MANA_REGEN_FROM_STAT aura - AuraList const& regenAura = GetAurasByType(SPELL_AURA_MOD_MANA_REGEN_FROM_STAT); - for(AuraList::const_iterator i = regenAura.begin();i != regenAura.end(); ++i) + AuraEffectList const& regenAura = GetAurasByType(SPELL_AURA_MOD_MANA_REGEN_FROM_STAT); + for(AuraEffectList::const_iterator i = regenAura.begin();i != regenAura.end(); ++i) { - Modifier* mod = (*i)->GetModifier(); - power_regen_mp5 += GetStat(Stats(mod->m_miscvalue)) * (*i)->GetModifierValue() / 500.0f; + power_regen_mp5 += GetStat(Stats((*i)->GetMiscValue())) * (*i)->GetAmount() / 500.0f; } - // Bonus from some dummy auras - AuraList const& mDummyAuras = GetAurasByType(SPELL_AURA_PERIODIC_DUMMY); - for(AuraList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i) - if((*i)->GetId() == 34074) // Aspect of the Viper - { - power_regen_mp5 += (*i)->GetModifier()->m_amount * Intellect / 500.0f; - // Add regen bonus from level in this dummy - power_regen_mp5 += getLevel() * 35 / 100; - } - // Set regen rate in cast state apply only on spirit based regen int32 modManaRegenInterrupt = GetTotalAuraModifier(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT); if (modManaRegenInterrupt > 100) modManaRegenInterrupt = 100; - SetStatFloatValue(PLAYER_FIELD_MOD_MANA_REGEN_INTERRUPT, power_regen_mp5 + power_regen * modManaRegenInterrupt / 100.0f); + SetStatFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER, (power_regen_mp5 + power_regen) * modManaRegenInterrupt / 100.0f); - SetStatFloatValue(PLAYER_FIELD_MOD_MANA_REGEN, power_regen_mp5 + power_regen); + SetStatFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER, power_regen_mp5 + power_regen); } void Player::_ApplyAllStatBonuses() @@ -712,6 +789,27 @@ void Creature::UpdateMaxPower(Powers power) void Creature::UpdateAttackPowerAndDamage(bool ranged) { + UnitMods unitMod = ranged ? UNIT_MOD_ATTACK_POWER_RANGED : UNIT_MOD_ATTACK_POWER; + + uint16 index = UNIT_FIELD_ATTACK_POWER; + uint16 index_mod = UNIT_FIELD_ATTACK_POWER_MODS; + uint16 index_mult = UNIT_FIELD_ATTACK_POWER_MULTIPLIER; + + if(ranged) + { + index = UNIT_FIELD_RANGED_ATTACK_POWER; + index_mod = UNIT_FIELD_RANGED_ATTACK_POWER_MODS; + index_mult = UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER; + } + + float base_attPower = GetModifierValue(unitMod, BASE_VALUE) * GetModifierValue(unitMod, BASE_PCT); + float attPowerMod = GetModifierValue(unitMod, TOTAL_VALUE); + float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f; + + SetInt32Value(index, (uint32)base_attPower); //UNIT_FIELD_(RANGED)_ATTACK_POWER field + SetInt32Value(index_mod, (uint32)attPowerMod); //UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field + SetFloatValue(index_mult, attPowerMultiplier); //UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field + //automatically update weapon damage after attack power modification if(ranged) UpdateDamagePhysical(RANGED_ATTACK); @@ -755,7 +853,7 @@ void Creature::UpdateDamagePhysical(WeaponAttackType attType) float total_value = GetModifierValue(unitMod, TOTAL_VALUE); float total_pct = GetModifierValue(unitMod, TOTAL_PCT); - if(attType == BASE_ATTACK && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED)) + if(!CanUseAttackType(attType)) { weapon_mindamage = 0; weapon_maxdamage = 0; @@ -923,7 +1021,7 @@ void Pet::UpdateAttackPowerAndDamage(bool ranged) if(getPetType() == HUNTER_PET) //hunter pets benefit from owner's attack power { bonusAP = owner->GetTotalAttackPowerValue(RANGED_ATTACK) * 0.22f; - SetBonusDamage( int32(owner->GetTotalAttackPowerValue(RANGED_ATTACK) * 0.125f)); + SetBonusDamage( int32(owner->GetTotalAttackPowerValue(RANGED_ATTACK) * 0.1287f)); } //demons benefit from warlocks shadow or fire damage else if(getPetType() == SUMMON_PET && owner->getClass() == CLASS_WARLOCK) @@ -954,9 +1052,9 @@ void Pet::UpdateAttackPowerAndDamage(bool ranged) float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f; //UNIT_FIELD_(RANGED)_ATTACK_POWER field - SetUInt32Value(UNIT_FIELD_ATTACK_POWER, (uint32)base_attPower); + SetInt32Value(UNIT_FIELD_ATTACK_POWER, (int32)base_attPower); //UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field - SetUInt32Value(UNIT_FIELD_ATTACK_POWER_MODS, (uint32)attPowerMod); + SetInt32Value(UNIT_FIELD_ATTACK_POWER_MODS, (int32)attPowerMod); //UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER, attPowerMultiplier); diff --git a/src/game/TargetedMovementGenerator.cpp b/src/game/TargetedMovementGenerator.cpp index acdf7762b71..6aa4be19765 100644 --- a/src/game/TargetedMovementGenerator.cpp +++ b/src/game/TargetedMovementGenerator.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,7 +22,6 @@ #include "TargetedMovementGenerator.h" #include "Errors.h" #include "Creature.h" -#include "MapManager.h" #include "DestinationHolderImp.h" #include "World.h" @@ -95,8 +94,6 @@ template<class T> void TargetedMovementGenerator<T>::Initialize(T &owner) { - if(!&owner) - return; owner.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); if (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->canFly()) diff --git a/src/game/TargetedMovementGenerator.h b/src/game/TargetedMovementGenerator.h index 609e517078c..2083dd95369 100644 --- a/src/game/TargetedMovementGenerator.h +++ b/src/game/TargetedMovementGenerator.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/TaxiHandler.cpp b/src/game/TaxiHandler.cpp index c65a4214892..c6e68ad2cbd 100644 --- a/src/game/TaxiHandler.cpp +++ b/src/game/TaxiHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,7 +24,6 @@ #include "WorldSession.h" #include "Opcodes.h" #include "Log.h" -#include "World.h" #include "ObjectMgr.h" #include "Player.h" #include "UpdateMask.h" @@ -56,7 +55,7 @@ void WorldSession::SendTaxiStatus( uint64 guid ) return; } - uint32 curloc = objmgr.GetNearestTaxiNode(unit->GetPositionX(),unit->GetPositionY(),unit->GetPositionZ(),unit->GetMapId()); + uint32 curloc = objmgr.GetNearestTaxiNode(unit->GetPositionX(),unit->GetPositionY(),unit->GetPositionZ(),unit->GetMapId(),GetPlayer( )->GetTeam()); // not found nearest if(curloc == 0) @@ -71,7 +70,7 @@ void WorldSession::SendTaxiStatus( uint64 guid ) sLog.outDebug( "WORLD: Sent SMSG_TAXINODE_STATUS" ); } -void WorldSession::HandleTaxiQueryAvailableNodesOpcode( WorldPacket & recv_data ) +void WorldSession::HandleTaxiQueryAvailableNodes( WorldPacket & recv_data ) { CHECK_PACKET_SIZE(recv_data,8); @@ -90,7 +89,7 @@ void WorldSession::HandleTaxiQueryAvailableNodesOpcode( WorldPacket & recv_data // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); // unknown taxi node case if( SendLearnNewTaxiNode(unit) ) @@ -103,7 +102,7 @@ void WorldSession::HandleTaxiQueryAvailableNodesOpcode( WorldPacket & recv_data void WorldSession::SendTaxiMenu( Creature* unit ) { // find current node - uint32 curloc = objmgr.GetNearestTaxiNode(unit->GetPositionX(),unit->GetPositionY(),unit->GetPositionZ(),unit->GetMapId()); + uint32 curloc = objmgr.GetNearestTaxiNode(unit->GetPositionX(),unit->GetPositionY(),unit->GetPositionZ(),unit->GetMapId(),GetPlayer( )->GetTeam()); if ( curloc == 0 ) return; @@ -124,7 +123,7 @@ void WorldSession::SendDoFlight( uint16 MountId, uint32 path, uint32 pathNode ) { // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); while(GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType()==FLIGHT_MOTION_TYPE) GetPlayer()->GetMotionMaster()->MovementExpired(false); @@ -136,7 +135,7 @@ void WorldSession::SendDoFlight( uint16 MountId, uint32 path, uint32 pathNode ) bool WorldSession::SendLearnNewTaxiNode( Creature* unit ) { // find current node - uint32 curloc = objmgr.GetNearestTaxiNode(unit->GetPositionX(),unit->GetPositionY(),unit->GetPositionZ(),unit->GetMapId()); + uint32 curloc = objmgr.GetNearestTaxiNode(unit->GetPositionX(),unit->GetPositionY(),unit->GetPositionZ(),unit->GetMapId(),GetPlayer( )->GetTeam()); if ( curloc == 0 ) return true; // `true` send to avoid WorldSession::SendTaxiMenu call with one more curlock seartch with same false result. diff --git a/src/game/TemporarySummon.cpp b/src/game/TemporarySummon.cpp index bbe8fa9c104..c87ad91ff90 100644 --- a/src/game/TemporarySummon.cpp +++ b/src/game/TemporarySummon.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,19 +18,26 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "TemporarySummon.h" -#include "WorldPacket.h" -#include "MapManager.h" #include "Log.h" #include "ObjectAccessor.h" #include "CreatureAI.h" +#include "ObjectMgr.h" +#include "TemporarySummon.h" -TemporarySummon::TemporarySummon( uint64 summoner ) : -Creature(), m_type(TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN), m_timer(0), m_lifetime(0), m_summoner(summoner) +TempSummon::TempSummon(SummonPropertiesEntry const *properties, Unit *owner) : +Creature(), m_type(TEMPSUMMON_MANUAL_DESPAWN), m_timer(0), m_lifetime(0) +, m_Properties(properties) { + m_summonerGUID = owner ? owner->GetGUID() : 0; + m_summonMask |= SUMMON_MASK_SUMMON; } -void TemporarySummon::Update( uint32 diff ) +Unit* TempSummon::GetSummoner() const +{ + return m_summonerGUID ? ObjectAccessor::GetUnit(*this, m_summonerGUID) : NULL; +} + +void TempSummon::Update( uint32 diff ) { if (m_deathState == DEAD) { @@ -159,30 +166,125 @@ void TemporarySummon::Update( uint32 diff ) Creature::Update( diff ); } -void TemporarySummon::Summon(TempSummonType type, uint32 lifetime) +void TempSummon::InitSummon(uint32 duration) { - m_type = type; - m_timer = lifetime; - m_lifetime = lifetime; + assert(!isPet()); + + m_timer = duration; + m_lifetime = duration; + + if(m_type == TEMPSUMMON_MANUAL_DESPAWN) + m_type = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN; + + Unit* owner = GetSummoner(); + if(owner) + { + if(owner->GetTypeId()==TYPEID_UNIT && ((Creature*)owner)->IsAIEnabled) + ((Creature*)owner)->AI()->JustSummoned(this); + + if(GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_TRIGGER && m_spells[0]) + { + setFaction(owner->getFaction()); + CastSpell(this, m_spells[0], false, 0, 0, m_summonerGUID); + } + } - MapManager::Instance().GetMap(GetMapId(), this)->Add((Creature*)this); + if(!m_Properties) + return; + + if(uint32 slot = m_Properties->Slot) + { + if(owner) + { + if(owner->m_SummonSlot[slot] && owner->m_SummonSlot[slot] != GetGUID()) + { + Creature *OldTotem = ObjectAccessor::GetCreature(*this, owner->m_SummonSlot[slot]); + if(OldTotem && OldTotem->isSummon()) + ((TempSummon*)OldTotem)->UnSummon(); + } + owner->m_SummonSlot[slot] = GetGUID(); + } + } - AIM_Initialize(); + if(m_Properties->Faction) + setFaction(m_Properties->Faction); } -void TemporarySummon::UnSummon() +void TempSummon::SetTempSummonType(TempSummonType type) { + m_type = type; +} + +void TempSummon::UnSummon() +{ + assert(!isPet()); + + Unit* owner = GetSummoner(); + if(owner && owner->GetTypeId() == TYPEID_UNIT && ((Creature*)owner)->IsAIEnabled) + ((Creature*)owner)->AI()->SummonedCreatureDespawn(this); + CleanupsBeforeDelete(); AddObjectToRemoveList(); +} + +void TempSummon::RemoveFromWorld() +{ + if(!IsInWorld()) + return; - Unit* sum = m_summoner ? ObjectAccessor::GetUnit(*this, m_summoner) : NULL; - if (sum && sum->GetTypeId() == TYPEID_UNIT && ((Creature*)sum)->IsAIEnabled) + if(m_Properties) { - ((Creature*)sum)->AI()->SummonedCreatureDespawn(this); + if(uint32 slot = m_Properties->Slot) + { + if(Unit* owner = GetSummoner()) + { + if(owner->m_SummonSlot[slot] = GetGUID()) + owner->m_SummonSlot[slot] = 0; + } + } } + + if(GetOwnerGUID()) + sLog.outError("Unit %u has owner guid when removed from world", GetEntry()); + + Creature::RemoveFromWorld(); } -void TemporarySummon::SaveToDB() +void TempSummon::SaveToDB() { } +Guardian::Guardian(SummonPropertiesEntry const *properties, Unit *owner) : TempSummon(properties, owner) +, m_owner(owner), m_bonusdamage(0) +{ + m_summonMask |= SUMMON_MASK_GUARDIAN; + InitCharmInfo(); +} + +void Guardian::InitSummon(uint32 duration) +{ + TempSummon::InitSummon(duration); + + SetReactState(REACT_AGGRESSIVE); + + SetCreatorGUID(m_owner->GetGUID()); + setFaction(m_owner->getFaction()); + + if(m_owner->GetTypeId() == TYPEID_PLAYER) + { + m_charmInfo->InitCharmCreateSpells(); + //((Player*)m_owner)->CharmSpellInitialize(); + } + + m_owner->SetGuardian(this, true); +} + +void Guardian::RemoveFromWorld() +{ + if(!IsInWorld()) + return; + + m_owner->SetGuardian(this, false); + TempSummon::RemoveFromWorld(); +} + diff --git a/src/game/TemporarySummon.h b/src/game/TemporarySummon.h index fe9763f5301..cb5651d3194 100644 --- a/src/game/TemporarySummon.h +++ b/src/game/TemporarySummon.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,23 +22,45 @@ #define TRINITYCORE_TEMPSUMMON_H #include "Creature.h" -#include "ObjectAccessor.h" -class TemporarySummon : public Creature +class TempSummon : public Creature { public: - explicit TemporarySummon(uint64 summoner = 0); - virtual ~TemporarySummon(){}; + explicit TempSummon(SummonPropertiesEntry const *properties, Unit *owner); + virtual ~TempSummon(){}; void Update(uint32 time); - void Summon(TempSummonType type, uint32 lifetime); + virtual void InitSummon(uint32 lifetime); void UnSummon(); + void RemoveFromWorld(); + void SetTempSummonType(TempSummonType type); void SaveToDB(); - Unit* GetSummoner() const { return m_summoner ? ObjectAccessor::GetUnit(*this, m_summoner) : NULL; } + Unit* GetSummoner() const; + + SummonPropertiesEntry const *m_Properties; private: TempSummonType m_type; uint32 m_timer; uint32 m_lifetime; - uint64 m_summoner; + uint64 m_summonerGUID; +}; + +class Guardian : public TempSummon +{ + public: + Guardian(SummonPropertiesEntry const *properties, Unit *owner); + bool Create(uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, uint32 pet_number); + void InitSummon(uint32 duration); + void RemoveFromWorld(); + bool InitStatsForLevel(uint32 level); + + int32 GetBonusDamage() { return m_bonusdamage; } + void SetBonusDamage(int32 damage) { m_bonusdamage = damage; } + + Unit *GetOwner() { return m_owner; } + protected: + Unit *m_owner; + int32 m_bonusdamage; }; + #endif diff --git a/src/game/ThreatManager.cpp b/src/game/ThreatManager.cpp index ac124da6951..336f02c4470 100644 --- a/src/game/ThreatManager.cpp +++ b/src/game/ThreatManager.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,7 +23,6 @@ #include "Creature.h" #include "CreatureAI.h" #include "Map.h" -#include "MapManager.h" #include "Player.h" #include "ObjectAccessor.h" #include "UnitEvents.h" @@ -84,7 +83,7 @@ void HostilReference::sourceObjectDestroyLink() //============================================================ // Inform the source, that the status of the reference changed -void HostilReference::fireStatusChanged(const ThreatRefStatusChangeEvent& pThreatRefStatusChangeEvent) +void HostilReference::fireStatusChanged(ThreatRefStatusChangeEvent& pThreatRefStatusChangeEvent) { if(getSource()) getSource()->processThreatEvent(&pThreatRefStatusChangeEvent); @@ -100,7 +99,11 @@ void HostilReference::addThreat(float pMod) if(!isOnline()) updateOnlineStatus(); if(pMod != 0.0f) - fireStatusChanged(ThreatRefStatusChangeEvent(UEV_THREAT_REF_THREAT_CHANGE, this, pMod)); + { + ThreatRefStatusChangeEvent event(UEV_THREAT_REF_THREAT_CHANGE, this, pMod); + fireStatusChanged(event); + } + if(isValid() && pMod >= 0) { Unit* victim_owner = getTarget()->GetCharmerOrOwner(); @@ -155,8 +158,10 @@ void HostilReference::setOnlineOfflineState(bool pIsOnline) { iOnline = pIsOnline; if(!iOnline) - setAccessibleState(false); // if not online that not accessible as well - fireStatusChanged(ThreatRefStatusChangeEvent(UEV_THREAT_REF_ONLINE_STATUS, this)); + setAccessibleState(false); // if not online that not accessable as well + + ThreatRefStatusChangeEvent event(UEV_THREAT_REF_ONLINE_STATUS, this); + fireStatusChanged(event); } } @@ -167,7 +172,9 @@ void HostilReference::setAccessibleState(bool pIsAccessible) if(iAccessible != pIsAccessible) { iAccessible = pIsAccessible; - fireStatusChanged(ThreatRefStatusChangeEvent(UEV_THREAT_REF_ASSECCIBLE_STATUS, this)); + + ThreatRefStatusChangeEvent event(UEV_THREAT_REF_ASSECCIBLE_STATUS, this); + fireStatusChanged(event); } } @@ -178,7 +185,9 @@ void HostilReference::setAccessibleState(bool pIsAccessible) void HostilReference::removeReference() { invalidate(); - fireStatusChanged(ThreatRefStatusChangeEvent(UEV_THREAT_REF_REMOVE_FROM_LIST, this)); + + ThreatRefStatusChangeEvent event(UEV_THREAT_REF_REMOVE_FROM_LIST, this); + fireStatusChanged(event); } //============================================================ @@ -267,26 +276,36 @@ HostilReference* ThreatContainer::selectNextVictim(Creature* pAttacker, HostilRe { HostilReference* currentRef = NULL; bool found = false; + bool noPriorityTargetFound = false; std::list<HostilReference*>::iterator lastRef = iThreatList.end(); lastRef--; - for(std::list<HostilReference*>::iterator iter = iThreatList.begin(); iter != iThreatList.end() && !found; ++iter) + for(std::list<HostilReference*>::iterator iter = iThreatList.begin(); iter != iThreatList.end() && !found;) { currentRef = (*iter); Unit* target = currentRef->getTarget(); assert(target); // if the ref has status online the target must be there ! - // some units are preferred in comparison to others - if(iter != lastRef && (target->IsImmunedToDamage(pAttacker->GetMeleeDamageSchoolMask(), false) || - target->hasUnitState(UNIT_STAT_CONFUSED) - ) ) + // some units are prefered in comparison to others + if(!noPriorityTargetFound && (target->IsImmunedToDamage(pAttacker->GetMeleeDamageSchoolMask()) || target->hasNegativeAuraWithInterruptFlag(AURA_INTERRUPT_FLAG_DAMAGE)) ) { - // current victim is a second choice target, so don't compare threat with it below - if(currentRef == pCurrentVictim) - pCurrentVictim = NULL; - continue; + if(iter != lastRef) + { + // current victim is a second choice target, so don't compare threat with it below + if(currentRef == pCurrentVictim) + pCurrentVictim = NULL; + ++iter; + continue; + } + else + { + // if we reached to this point, everyone in the threatlist is a second choice target. In such a situation the target with the highest threat should be attacked. + noPriorityTargetFound = true; + iter = iThreatList.begin(); + continue; + } } if(!pAttacker->IsOutOfThreatArea(target)) // skip non attackable currently targets @@ -314,6 +333,7 @@ HostilReference* ThreatContainer::selectNextVictim(Creature* pAttacker, HostilRe break; } } + ++iter; } if(!found) currentRef = NULL; @@ -347,12 +367,18 @@ void ThreatManager::addThreat(Unit* pVictim, float pThreat, SpellSchoolMask scho //players and pets have only InHateListOf //HateOfflineList is used co contain unattackable victims (in-flight, in-water, GM etc.) - if (pVictim == getOwner()) // only for same creatures :) + // not to self + if (pVictim == getOwner()) return; + // not to GM if(!pVictim || (pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster()) ) return; + // not to dead and not for dead + if(!pVictim->isAlive() || !getOwner()->isAlive() ) + return; + assert(getOwner()->GetTypeId()== TYPEID_UNIT); float threat = ThreatCalcHelper::calcThreat(pVictim, iOwner, pThreat, schoolMask, pThreatSpell); @@ -450,18 +476,13 @@ void ThreatManager::setCurrentVictim(HostilReference* pHostilReference) // The hated unit is gone, dead or deleted // return true, if the event is consumed -bool ThreatManager::processThreatEvent(const UnitBaseEvent* pUnitBaseEvent) +void ThreatManager::processThreatEvent(ThreatRefStatusChangeEvent* threatRefStatusChangeEvent) { - bool consumed = false; - - ThreatRefStatusChangeEvent* threatRefStatusChangeEvent; - HostilReference* hostilReference; - - threatRefStatusChangeEvent = (ThreatRefStatusChangeEvent*) pUnitBaseEvent; threatRefStatusChangeEvent->setThreatManager(this); // now we can set the threat manager - hostilReference = threatRefStatusChangeEvent->getReference(); - switch(pUnitBaseEvent->getType()) + HostilReference* hostilReference = threatRefStatusChangeEvent->getReference(); + + switch(threatRefStatusChangeEvent->getType()) { case UEV_THREAT_REF_THREAT_CHANGE: if((getCurrentVictim() == hostilReference && threatRefStatusChangeEvent->getFValue()<0.0f) || @@ -499,6 +520,5 @@ bool ThreatManager::processThreatEvent(const UnitBaseEvent* pUnitBaseEvent) iThreatOfflineContainer.remove(hostilReference); break; } - return consumed; } diff --git a/src/game/ThreatManager.h b/src/game/ThreatManager.h index 56281b9f8f3..aa84cb7d52c 100644 --- a/src/game/ThreatManager.h +++ b/src/game/ThreatManager.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -45,20 +45,8 @@ class ThreatCalcHelper }; //============================================================== - class TRINITY_DLL_SPEC HostilReference : public Reference<Unit, ThreatManager> { - private: - float iThreat; - float iTempThreatModifyer; // used for taunt - uint64 iUnitGuid; - bool iOnline; - bool iAccessible; - private: - // Inform the source, that the status of that reference was changed - void fireStatusChanged(const ThreatRefStatusChangeEvent& pThreatRefStatusChangeEvent); - - Unit* getSourceUnit(); public: HostilReference(Unit* pUnit, ThreatManager *pThreatManager, float pThreat); @@ -125,6 +113,17 @@ class TRINITY_DLL_SPEC HostilReference : public Reference<Unit, ThreatManager> // Tell our refFrom (source) object, that the link is cut (Target destroyed) void sourceObjectDestroyLink(); + private: + // Inform the source, that the status of that reference was changed + void fireStatusChanged(ThreatRefStatusChangeEvent& pThreatRefStatusChangeEvent); + + Unit* getSourceUnit(); + private: + float iThreat; + float iTempThreatModifyer; // used for taunt + uint64 iUnitGuid; + bool iOnline; + bool iAccessible; }; //============================================================== @@ -170,14 +169,9 @@ class TRINITY_DLL_SPEC ThreatContainer class TRINITY_DLL_SPEC ThreatManager { - private: - HostilReference* iCurrentVictim; - Unit* iOwner; - ThreatContainer iThreatContainer; - ThreatContainer iThreatOfflineContainer; - - void _addThreat(Unit* target, float threat); public: + friend class HostilReference; + explicit ThreatManager(Unit *pOwner); ~ThreatManager() { clearReferences(); } @@ -191,7 +185,7 @@ class TRINITY_DLL_SPEC ThreatManager bool isThreatListEmpty() { return iThreatContainer.empty();} - bool processThreatEvent(const UnitBaseEvent* pUnitBaseEvent); + void processThreatEvent(ThreatRefStatusChangeEvent* threatRefStatusChangeEvent); HostilReference* getCurrentVictim() { return iCurrentVictim; } @@ -208,10 +202,17 @@ class TRINITY_DLL_SPEC ThreatManager // methods to access the lists from the outside to do sume dirty manipulation (scriping and such) // I hope they are used as little as possible. - inline std::list<HostilReference*>& getThreatList() { return iThreatContainer.getThreatList(); } - inline std::list<HostilReference*>& getOfflieThreatList() { return iThreatOfflineContainer.getThreatList(); } - inline ThreatContainer& getOnlineContainer() { return iThreatContainer; } - inline ThreatContainer& getOfflineContainer() { return iThreatOfflineContainer; } + std::list<HostilReference*>& getThreatList() { return iThreatContainer.getThreatList(); } + std::list<HostilReference*>& getOfflieThreatList() { return iThreatOfflineContainer.getThreatList(); } + ThreatContainer& getOnlineContainer() { return iThreatContainer; } + ThreatContainer& getOfflineContainer() { return iThreatOfflineContainer; } + private: + void _addThreat(Unit *pVictim, float threat); + + HostilReference* iCurrentVictim; + Unit* iOwner; + ThreatContainer iThreatContainer; + ThreatContainer iThreatOfflineContainer; }; //================================================= diff --git a/src/game/TicketHandler.cpp b/src/game/TicketHandler.cpp index 6a4606aadca..f4af95bb169 100644 --- a/src/game/TicketHandler.cpp +++ b/src/game/TicketHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS + * Copyright (C) 2005-2009 MaNGOS * - * Copyright (C) 2008 Trinity + * Copyright (C) 2008-2009 Trinity * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -60,6 +60,11 @@ void WorldSession::HandleGMTicketCreateOpcode( WorldPacket & recv_data ) ticket->guid = ticketmgr.GenerateTicketID(); ticket->playerGuid = GetPlayer()->GetGUID(); ticket->message = ticketText; + ticket->createtime = time(NULL); + ticket->map = map; + ticket->pos_x = x; + ticket->pos_y = y; + ticket->pos_z = z; ticket->timestamp = time(NULL); ticket->closed = 0; ticket->assignedToGM = 0; @@ -188,4 +193,3 @@ void WorldSession::HandleGMTicketSystemStatusOpcode( WorldPacket & /*recv_data*/ // Send Packet SendPacket(&data); } - diff --git a/src/game/TicketMgr.cpp b/src/game/TicketMgr.cpp index e8b75c9f284..7335b3a3067 100644 --- a/src/game/TicketMgr.cpp +++ b/src/game/TicketMgr.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS + * Copyright (C) 2005-2009 MaNGOS * - * Copyright (C) 2008 Trinity + * Copyright (C) 2008-2009 Trinity * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -92,13 +92,9 @@ void TicketMgr::DeleteGMTicketPermanently(uint64 ticketGuid) for(GmTicketList::iterator i = GM_TicketList.begin(); i != GM_TicketList.end();) { if((*i)->guid == ticketGuid) - { i = GM_TicketList.erase(i); - } else - { ++i; - } } // delete database record @@ -108,10 +104,9 @@ void TicketMgr::DeleteGMTicketPermanently(uint64 ticketGuid) void TicketMgr::LoadGMTickets() { - InitTicketID(); // Delete all out of object holder GM_TicketList.clear(); - QueryResult *result = CharacterDatabase.Query( "SELECT `guid`, `playerGuid`, `name`, `message`, `timestamp`, `closed`, `assignedto`, `comment` FROM `gm_tickets`" ); + QueryResult *result = CharacterDatabase.Query( "SELECT `guid`, `playerGuid`, `name`, `message`, `createtime`, `map`, `posX`, `posY`, `posZ`, `timestamp`, `closed`, `assignedto`, `comment` FROM `gm_tickets`" ); GM_Ticket *ticket; if(!result) @@ -126,10 +121,14 @@ void TicketMgr::LoadGMTickets() ticket->playerGuid = fields[1].GetUInt64(); ticket->name = fields[2].GetString(); ticket->message = fields[3].GetString(); - ticket->timestamp = fields[4].GetUInt32(); - ticket->closed = fields[5].GetUInt16(); - ticket->assignedToGM = fields[6].GetUInt64(); - ticket->comment = fields[7].GetString(); + ticket->createtime = fields[4].GetUInt64(); + ticket->pos_x = fields[5].GetFloat(); + ticket->pos_y = fields[6].GetFloat(); + ticket->pos_z = fields[7].GetFloat(); + ticket->timestamp = fields[8].GetUInt64(); + ticket->closed = fields[9].GetUInt64(); + ticket->assignedToGM = fields[10].GetUInt64(); + ticket->comment = fields[11].GetString(); AddGMTicket(ticket, true); @@ -172,11 +171,16 @@ void TicketMgr::SaveGMTicket(GM_Ticket* ticket) std::string msg = ticket->message; CharacterDatabase.escape_string(msg); std::stringstream ss; - ss << "REPLACE INTO `gm_tickets` (`guid`, `playerGuid`, `name`, `message`, `timestamp`, `closed`, `assignedto`, `comment`) VALUES('"; + ss << "REPLACE INTO `gm_tickets` (`guid`, `playerGuid`, `name`, `message`, `createtime`, `map`, `posX`, `posY`, `posZ`, `timestamp`, `closed`, `assignedto`, `comment`) VALUES('"; ss << ticket->guid << "', '"; ss << ticket->playerGuid << "', '"; ss << ticket->name << "', '"; ss << msg << "', '" ; + ss << ticket->createtime << "', '"; + ss << ticket->map << "', '"; + ss << ticket->pos_x << "', '"; + ss << ticket->pos_y << "', '"; + ss << ticket->pos_z << "', '"; ss << ticket->timestamp << "', '"; ss << ticket->closed << "', '"; ss << ticket->assignedToGM << "', '"; diff --git a/src/game/TicketMgr.h b/src/game/TicketMgr.h index f3fa7d98f7d..491e6657f40 100644 --- a/src/game/TicketMgr.h +++ b/src/game/TicketMgr.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS + * Copyright (C) 2005-2009 MaNGOS * - * Copyright (C) 2008 Trinity + * Copyright (C) 2008-2009 Trinity * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,9 +32,14 @@ struct GM_Ticket uint64 guid; uint64 playerGuid; std::string name; + float pos_x; + float pos_y; + float pos_z; + uint32 map; std::string message; + uint64 createtime; uint64 timestamp; - uint16 closed; + uint64 closed; uint64 assignedToGM; std::string comment; }; @@ -45,7 +50,7 @@ typedef std::list<GM_Ticket*> GmTicketList class TicketMgr { public: - TicketMgr(){m_ticketid = 1;} //constructor + TicketMgr(){ InitTicketID();} //constructor ~TicketMgr(){} //destructor // Object Holder diff --git a/src/game/Tools.cpp b/src/game/Tools.cpp index e553b86f8e9..d1728c78c9e 100644 --- a/src/game/Tools.cpp +++ b/src/game/Tools.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/Tools.h b/src/game/Tools.h index 2a71121129b..bdb3ad8e762 100644 --- a/src/game/Tools.h +++ b/src/game/Tools.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/Totem.cpp b/src/game/Totem.cpp index 2889f6f9188..3b1879feca7 100644 --- a/src/game/Totem.cpp +++ b/src/game/Totem.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,16 +20,15 @@ #include "Totem.h" #include "WorldPacket.h" -#include "MapManager.h" #include "Log.h" #include "Group.h" #include "Player.h" #include "ObjectMgr.h" #include "SpellMgr.h" -Totem::Totem() : Creature() +Totem::Totem(SummonPropertiesEntry const *properties, Unit *owner) : TempSummon(properties, owner) { - m_isTotem = true; + m_summonMask |= SUMMON_MASK_TOTEM; m_duration = 0; m_type = TOTEM_PASSIVE; } @@ -89,8 +88,6 @@ void Totem::Summon(Unit* owner) data << GetGUID(); SendMessageToSet(&data,true); - AIM_Initialize(); - switch(m_type) { case TOTEM_PASSIVE: CastSpell(this, GetSpell(), true); break; @@ -112,11 +109,11 @@ void Totem::UnSummon() if (owner) { // clear owenr's totem slot - for(int i = 0; i < MAX_TOTEM; ++i) + for(int i = SUMMON_SLOT_TOTEM; i < MAX_TOTEM_SLOT; ++i) { - if(owner->m_TotemSlot[i]==GetGUID()) + if(owner->m_SummonSlot[i]==GetGUID()) { - owner->m_TotemSlot[i] = 0; + owner->m_SummonSlot[i] = 0; break; } } @@ -178,19 +175,19 @@ void Totem::SetTypeBySummonSpell(SpellEntry const * spellProto) m_type = TOTEM_STATUE; //Jewelery statue } -bool Totem::IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges) +bool Totem::IsImmunedToSpellEffect(SpellEntry const* spellInfo, uint32 index) const { -/* for (int i=0;i<3;i++) + // TODO: possibly all negative auras immuned? + switch(spellInfo->EffectApplyAuraName[index]) { - switch(spellInfo->EffectApplyAuraName[i]) - { - case SPELL_AURA_PERIODIC_DAMAGE: - case SPELL_AURA_PERIODIC_LEECH: - return true; - default: - continue; - } - }*/ - return Creature::IsImmunedToSpell(spellInfo, useCharges); + case SPELL_AURA_PERIODIC_DAMAGE: + case SPELL_AURA_PERIODIC_LEECH: + case SPELL_AURA_MOD_FEAR: + case SPELL_AURA_TRANSFORM: + return true; + default: + break; + } + return Creature::IsImmunedToSpellEffect(spellInfo, index); } diff --git a/src/game/Totem.h b/src/game/Totem.h index 82b183d5d62..40e2744bbeb 100644 --- a/src/game/Totem.h +++ b/src/game/Totem.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,7 +21,7 @@ #ifndef TRINITYCORE_TOTEM_H #define TRINITYCORE_TOTEM_H -#include "Creature.h" +#include "TemporarySummon.h" enum TotemType { @@ -32,10 +32,10 @@ enum TotemType #define SENTRY_TOTEM_ENTRY 3968 -class Totem : public Creature +class Totem : public TempSummon { public: - explicit Totem(); + explicit Totem(SummonPropertiesEntry const *properties, Unit *owner); virtual ~Totem(){}; void Update( uint32 time ); void Summon(Unit* owner); @@ -57,7 +57,7 @@ class Totem : public Creature void UpdateAttackPowerAndDamage(bool /*ranged*/ ) {} void UpdateDamagePhysical(WeaponAttackType /*attType*/) {} - bool IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges = false); + bool IsImmunedToSpellEffect(SpellEntry const* spellInfo, uint32 index) const; protected: TotemType m_type; diff --git a/src/game/TotemAI.cpp b/src/game/TotemAI.cpp index 3593f8396a1..4efced3bc3a 100644 --- a/src/game/TotemAI.cpp +++ b/src/game/TotemAI.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,9 +21,7 @@ #include "TotemAI.h" #include "Totem.h" #include "Creature.h" -#include "Player.h" -#include "Database/DBCStores.h" -#include "MapManager.h" +#include "DBCStores.h" #include "ObjectAccessor.h" #include "SpellMgr.h" @@ -40,8 +38,9 @@ TotemAI::Permissible(const Creature *creature) return PERMIT_BASE_NO; } -TotemAI::TotemAI(Creature *c) : CreatureAI(c), i_totem(static_cast<Totem&>(*c)), i_victimGuid(0) +TotemAI::TotemAI(Creature *c) : CreatureAI(c), i_victimGuid(0) { + assert(c->isTotem()); } void @@ -51,52 +50,41 @@ TotemAI::MoveInLineOfSight(Unit *) void TotemAI::EnterEvadeMode() { - i_totem.CombatStop(); + m_creature->CombatStop(); } void TotemAI::UpdateAI(const uint32 /*diff*/) { - if (i_totem.GetTotemType() != TOTEM_ACTIVE) + if (((Totem*)m_creature)->GetTotemType() != TOTEM_ACTIVE) return; - if (!i_totem.isAlive() || i_totem.IsNonMeleeSpellCasted(false)) + if (!m_creature->isAlive() || m_creature->IsNonMeleeSpellCasted(false)) return; // Search spell - SpellEntry const *spellInfo = sSpellStore.LookupEntry(i_totem.GetSpell()); + SpellEntry const *spellInfo = sSpellStore.LookupEntry(((Totem*)m_creature)->GetSpell()); if (!spellInfo) return; - // Get spell rangy + // Get spell range SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex); - float max_range = GetSpellMaxRange(srange); + float max_range = GetSpellMaxRangeForHostile(srange); // SPELLMOD_RANGE not applied in this place just because not existence range mods for attacking totems // pointer to appropriate target if found any - Unit* victim = i_victimGuid ? ObjectAccessor::GetUnit(i_totem, i_victimGuid) : NULL; + Unit* victim = i_victimGuid ? ObjectAccessor::GetUnit(*m_creature, i_victimGuid) : NULL; // Search victim if no, not attackable, or out of range, or friendly (possible in case duel end) if( !victim || - !victim->isTargetableForAttack() || !i_totem.IsWithinDistInMap(victim, max_range) || - i_totem.IsFriendlyTo(victim) || !victim->isVisibleForOrDetect(&i_totem,false) ) + !victim->isTargetableForAttack() || !m_creature->IsWithinDistInMap(victim, max_range) || + m_creature->IsFriendlyTo(victim) || !victim->isVisibleForOrDetect(m_creature,false) ) { - CellPair p(Trinity::ComputeCellPair(i_totem.GetPositionX(),i_totem.GetPositionY())); - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - victim = NULL; - - Trinity::NearestAttackableUnitInObjectRangeCheck u_check(&i_totem, &i_totem, max_range); - Trinity::UnitLastSearcher<Trinity::NearestAttackableUnitInObjectRangeCheck> checker(victim, u_check); - - TypeContainerVisitor<Trinity::UnitLastSearcher<Trinity::NearestAttackableUnitInObjectRangeCheck>, GridTypeMapContainer > grid_object_checker(checker); - TypeContainerVisitor<Trinity::UnitLastSearcher<Trinity::NearestAttackableUnitInObjectRangeCheck>, WorldTypeMapContainer > world_object_checker(checker); - - CellLock<GridReadGuard> cell_lock(cell, p); - cell_lock->Visit(cell_lock, grid_object_checker, *MapManager::Instance().GetMap(i_totem.GetMapId(), &i_totem)); - cell_lock->Visit(cell_lock, world_object_checker, *MapManager::Instance().GetMap(i_totem.GetMapId(), &i_totem)); + Trinity::NearestAttackableUnitInObjectRangeCheck u_check(m_creature, m_creature, max_range); + Trinity::UnitLastSearcher<Trinity::NearestAttackableUnitInObjectRangeCheck> checker(m_creature, victim, u_check); + m_creature->VisitNearbyObject(max_range, checker); } // If have target @@ -106,30 +94,23 @@ TotemAI::UpdateAI(const uint32 /*diff*/) i_victimGuid = victim->GetGUID(); // attack - i_totem.SetInFront(victim); // client change orientation by self - i_totem.CastSpell(victim, i_totem.GetSpell(), false); + m_creature->SetInFront(victim); // client change orientation by self + m_creature->CastSpell(victim, ((Totem*)m_creature)->GetSpell(), false); } else i_victimGuid = 0; } -bool -TotemAI::IsVisible(Unit *) const -{ - return false; -} - void TotemAI::AttackStart(Unit *) { // Sentry totem sends ping on attack - if (i_totem.GetEntry() == SENTRY_TOTEM_ENTRY && i_totem.GetOwner()->GetTypeId() == TYPEID_PLAYER) + if (m_creature->GetEntry() == SENTRY_TOTEM_ENTRY && m_creature->GetOwner()->GetTypeId() == TYPEID_PLAYER) { WorldPacket data(MSG_MINIMAP_PING, (8+4+4)); - data << i_totem.GetGUID(); - data << i_totem.GetPositionX(); - data << i_totem.GetPositionY(); - ((Player*)i_totem.GetOwner())->GetSession()->SendPacket(&data); + data << m_creature->GetGUID(); + data << m_creature->GetPositionX(); + data << m_creature->GetPositionY(); + ((Player*)m_creature->GetOwner())->GetSession()->SendPacket(&data); } } - diff --git a/src/game/TotemAI.h b/src/game/TotemAI.h index 50ea764abfd..003f5d5ca13 100644 --- a/src/game/TotemAI.h +++ b/src/game/TotemAI.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,18 +31,16 @@ class TRINITY_DLL_DECL TotemAI : public CreatureAI { public: - TotemAI(Creature *c); + explicit TotemAI(Creature *c); void MoveInLineOfSight(Unit *); void AttackStart(Unit *); void EnterEvadeMode(); - bool IsVisible(Unit *) const; void UpdateAI(const uint32); static int Permissible(const Creature *); private: - Totem &i_totem; uint64 i_victimGuid; }; #endif diff --git a/src/game/TradeHandler.cpp b/src/game/TradeHandler.cpp index 278bb37c81c..cc3bcab62d2 100644 --- a/src/game/TradeHandler.cpp +++ b/src/game/TradeHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/Transports.cpp b/src/game/Transports.cpp index df2ad178550..c8f89b337bc 100644 --- a/src/game/Transports.cpp +++ b/src/game/Transports.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,7 +26,7 @@ #include "Path.h" #include "WorldPacket.h" -#include "Database/DBCStores.h" +#include "DBCStores.h" #include "ProgressBar.h" #include "World.h" @@ -89,8 +89,6 @@ void MapManager::LoadTransports() continue; } - t->m_name = goinfo->name; - float x, y, z, o; uint32 mapid; x = t->m_WayPoints[0].x; y = t->m_WayPoints[0].y; z = t->m_WayPoints[0].z; mapid = t->m_WayPoints[0].mapid; o = 1; @@ -139,7 +137,7 @@ void MapManager::LoadTransports() Transport::Transport() : GameObject() { // 2.3.2 - 0x5A - m_updateFlag = (UPDATEFLAG_TRANSPORT | UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID | UPDATEFLAG_HASPOSITION); + m_updateFlag = (UPDATEFLAG_TRANSPORT | UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID | UPDATEFLAG_HAS_POSITION); } bool Transport::Create(uint32 guidlow, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress, uint32 dynflags) @@ -147,10 +145,11 @@ bool Transport::Create(uint32 guidlow, uint32 mapid, float x, float y, float z, Relocate(x,y,z,ang); SetMapId(mapid); + // instance id and phaseMask isn't set to values different from std. if(!IsPositionValid()) { - sLog.outError("ERROR: Transport (GUID: %u) not created. Suggested coordinates isn't valid (X: %f Y: %f)", + sLog.outError("Transport (GUID: %u) not created. Suggested coordinates isn't valid (X: %f Y: %f)", guidlow,x,y); return false; } @@ -170,9 +169,10 @@ bool Transport::Create(uint32 guidlow, uint32 mapid, float x, float y, float z, SetFloatValue(OBJECT_FIELD_SCALE_X, goinfo->size); SetUInt32Value(GAMEOBJECT_FACTION, goinfo->faction); - SetUInt32Value(GAMEOBJECT_FLAGS, goinfo->flags); - - SetUInt32Value(OBJECT_FIELD_ENTRY, goinfo->id); + //SetUInt32Value(GAMEOBJECT_FLAGS, goinfo->flags); + SetUInt32Value(GAMEOBJECT_FLAGS, MAKE_PAIR32(0x28, 0x64)); + SetUInt32Value(GAMEOBJECT_LEVEL, m_period); + SetEntry(goinfo->id); SetUInt32Value(GAMEOBJECT_DISPLAYID, goinfo->displayId); @@ -181,7 +181,9 @@ bool Transport::Create(uint32 guidlow, uint32 mapid, float x, float y, float z, SetGoAnimProgress(animprogress); if(dynflags) - SetUInt32Value(GAMEOBJECT_DYN_FLAGS, dynflags); + SetUInt32Value(GAMEOBJECT_DYNAMIC, MAKE_PAIR32(0, dynflags)); + + SetName(goinfo->name); return true; } @@ -414,7 +416,7 @@ bool Transport::GenerateWaypoints(uint32 pathid, std::set<uint32> &mapids) uint32 timer = t; - // sLog.outDetail(" Generated %d waypoints, total time %u.", m_WayPoints.size(), timer); + // sLog.outDetail(" Generated %lu waypoints, total time %u.", (unsigned long)m_WayPoints.size(), timer); m_curr = m_WayPoints.begin(); m_curr = GetNextWayPoint(); @@ -437,11 +439,9 @@ Transport::WayPointMap::iterator Transport::GetNextWayPoint() void Transport::TeleportTransport(uint32 newMapid, float x, float y, float z) { - //MapManager::Instance().GetMap(oldMapid)->Remove((GameObject *)this, false); + Map const* oldMap = GetMap(); SetMapId(newMapid); - //MapManager::Instance().LoadGrid(newMapid,x,y,true); Relocate(x, y, z); - //MapManager::Instance().GetMap(newMapid)->Add<GameObject>((GameObject *)this); for(PlayerSet::iterator itr = m_passengers.begin(); itr != m_passengers.end();) { @@ -465,13 +465,21 @@ void Transport::TeleportTransport(uint32 newMapid, float x, float y, float z) //data << uint32(0); //plr->GetSession()->SendPacket(&data); } + + Map const* newMap = GetMap(); + + if(oldMap != newMap) + { + UpdateForMap(oldMap); + UpdateForMap(newMap); + } } bool Transport::AddPassenger(Player* passenger) { if (m_passengers.find(passenger) == m_passengers.end()) { - sLog.outDetail("Player %s boarded transport %s.", passenger->GetName(), this->m_name.c_str()); + sLog.outDetail("Player %s boarded transport %s.", passenger->GetName(), GetName()); m_passengers.insert(passenger); } return true; @@ -479,11 +487,8 @@ bool Transport::AddPassenger(Player* passenger) bool Transport::RemovePassenger(Player* passenger) { - if (m_passengers.find(passenger) != m_passengers.end()) - { - sLog.outDetail("Player %s removed from transport %s.", passenger->GetName(), this->m_name.c_str()); - m_passengers.erase(passenger); - } + if (m_passengers.erase(passenger)) + sLog.outDetail("Player %s removed from transport %s.", passenger->GetName(), GetName()); return true; } @@ -512,7 +517,6 @@ void Transport::Update(uint32 /*p_time*/) } else { - //MapManager::Instance().GetMap(m_curr->second.mapid)->GameobjectRelocation((GameObject *)this, m_curr->second.x, m_curr->second.y, m_curr->second.z, this->m_orientation); Relocate(m_curr->second.x, m_curr->second.y, m_curr->second.z); } /* @@ -550,10 +554,6 @@ void Transport::Update(uint32 /*p_time*/) if (m_curr == m_WayPoints.begin() && (sLog.getLogFilter() & LOG_FILTER_TRANSPORT_MOVES)==0) sLog.outDetail(" ************ BEGIN ************** %s", this->m_name.c_str()); - // MapManager::Instance().GetMap(m_curr->second.mapid)->Add(&this); // -> // ->Add(t); - //MapManager::Instance().GetMap(m_curr->second.mapid)->Remove((GameObject *)this, false); // -> // ->Add(t); - //MapManager::Instance().GetMap(m_curr->second.mapid)->Add((GameObject *)this); // -> // ->Add(t); - if ((sLog.getLogFilter() & LOG_FILTER_TRANSPORT_MOVES)==0) sLog.outDetail("%s moved to %d %f %f %f %d", this->m_name.c_str(), m_curr->second.id, m_curr->second.x, m_curr->second.y, m_curr->second.z, m_curr->second.mapid); @@ -562,3 +562,36 @@ void Transport::Update(uint32 /*p_time*/) } } +void Transport::UpdateForMap(Map const* targetMap) +{ + Map::PlayerList const& pl = targetMap->GetPlayers(); + if(pl.isEmpty()) + return; + + if(GetMapId()==targetMap->GetId()) + { + for(Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr) + { + if(this != itr->getSource()->GetTransport()) + { + UpdateData transData; + BuildCreateUpdateBlockForPlayer(&transData, itr->getSource()); + WorldPacket packet; + transData.BuildPacket(&packet, true); + itr->getSource()->SendDirectMessage(&packet); + } + } + } + else + { + UpdateData transData; + BuildOutOfRangeUpdateBlock(&transData); + WorldPacket out_packet; + transData.BuildPacket(&out_packet, true); + + for(Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr) + if(this != itr->getSource()->GetTransport()) + itr->getSource()->SendDirectMessage(&out_packet); + } +} + diff --git a/src/game/Transports.h b/src/game/Transports.h index a814a0f70b9..5e0f954c3b7 100644 --- a/src/game/Transports.h +++ b/src/game/Transports.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -38,16 +38,16 @@ class TransportPath uint32 delay; }; - inline void SetLength(const unsigned int sz) + void SetLength(const unsigned int sz) { i_nodes.resize( sz ); } - inline unsigned int Size(void) const { return i_nodes.size(); } - inline bool Empty(void) const { return i_nodes.empty(); } - inline void Resize(unsigned int sz) { i_nodes.resize(sz); } - inline void Clear(void) { i_nodes.clear(); } - inline PathNode* GetNodes(void) { return static_cast<PathNode *>(&i_nodes[0]); } + unsigned int Size(void) const { return i_nodes.size(); } + bool Empty(void) const { return i_nodes.empty(); } + void Resize(unsigned int sz) { i_nodes.resize(sz); } + void Clear(void) { i_nodes.clear(); } + PathNode* GetNodes(void) { return static_cast<PathNode *>(&i_nodes[0]); } PathNode& operator[](const unsigned int idx) { return i_nodes[idx]; } const PathNode& operator()(const unsigned int idx) const { return i_nodes[idx]; } @@ -56,7 +56,7 @@ class TransportPath std::vector<PathNode> i_nodes; }; -class Transport : private GameObject +class Transport : protected GameObject { public: explicit Transport(); @@ -83,7 +83,6 @@ class Transport : private GameObject typedef std::set<Player*> PlayerSet; PlayerSet const& GetPassengers() const { return m_passengers; } - std::string m_name; private: struct WayPoint { @@ -114,6 +113,7 @@ class Transport : private GameObject private: void TeleportTransport(uint32 newMapid, float x, float y, float z); + void UpdateForMap(Map const* map); WayPointMap::iterator GetNextWayPoint(); }; #endif diff --git a/src/game/Traveller.h b/src/game/Traveller.h index b8865282672..702cc6413b0 100644 --- a/src/game/Traveller.h +++ b/src/game/Traveller.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,11 +21,9 @@ #ifndef TRINITY_TRAVELLER_H #define TRINITY_TRAVELLER_H -#include "MapManager.h" #include "Creature.h" #include "Player.h" #include <cassert> -#include "CreatureGroups.h" /** Traveller is a wrapper for units (creatures or players) that * travel from point A to point B using the destination holder. @@ -47,22 +45,39 @@ struct TRINITY_DLL_DECL Traveller operator T&(void) { return i_traveller; } operator const T&(void) { return i_traveller; } - inline float GetPositionX() const { return i_traveller.GetPositionX(); } - inline float GetPositionY() const { return i_traveller.GetPositionY(); } - inline float GetPositionZ() const { return i_traveller.GetPositionZ(); } - inline T& GetTraveller(void) { return i_traveller; } + float GetPositionX() const { return i_traveller.GetPositionX(); } + float GetPositionY() const { return i_traveller.GetPositionY(); } + float GetPositionZ() const { return i_traveller.GetPositionZ(); } + T& GetTraveller(void) { return i_traveller; } float Speed(void) { assert(false); return 0.0f; } + float GetMoveDestinationTo(float x, float y, float z); + uint32 GetTotalTrevelTimeTo(float x, float y, float z); + void Relocation(float x, float y, float z, float orientation) {} void Relocation(float x, float y, float z) { Relocation(x, y, z, i_traveller.GetOrientation()); } void MoveTo(float x, float y, float z, uint32 t) {} }; +template<class T> +inline uint32 Traveller<T>::GetTotalTrevelTimeTo(float x, float y, float z) +{ + float dist = GetMoveDestinationTo(x,y,z); + float speed = 0.001f; + if (Speed() <= 0.0f) + return 0xfffffffe; // almost infinity-unit should stop + else + speed *= Speed(); // speed is in seconds so convert from second to millisecond + return static_cast<uint32>(dist/speed); +} + // specialization for creatures template<> inline float Traveller<Creature>::Speed() { - if(i_traveller.HasUnitMovementFlag(MOVEMENTFLAG_WALK_MODE)) + if(i_traveller.hasUnitState(UNIT_STAT_CHARGING)) + return i_traveller.m_TempSpeed; + else if(i_traveller.HasUnitMovementFlag(MOVEMENTFLAG_WALK_MODE)) return i_traveller.GetSpeed(MOVE_WALK); else if(i_traveller.HasUnitMovementFlag(MOVEMENTFLAG_FLYING2)) return i_traveller.GetSpeed(MOVE_FLIGHT); @@ -73,34 +88,59 @@ inline float Traveller<Creature>::Speed() template<> inline void Traveller<Creature>::Relocation(float x, float y, float z, float orientation) { - MapManager::Instance().GetMap(i_traveller.GetMapId(), &i_traveller)->CreatureRelocation(&i_traveller, x, y, z, orientation); + i_traveller.GetMap()->CreatureRelocation(&i_traveller, x, y, z, orientation); } template<> -inline void Traveller<Creature>::MoveTo(float x, float y, float z, uint32 t) +inline float Traveller<Creature>::GetMoveDestinationTo(float x, float y, float z) { - //Call for creature group update - if(i_traveller.IsFormationLeader() && !i_traveller.isInCombat() && - CreatureGroupHolder.find(i_traveller.GetFormationID()) != CreatureGroupHolder.end()) - CreatureGroupHolder[i_traveller.GetFormationID()]->LeaderMovedInEvade(); + float dx = x - GetPositionX(); + float dy = y - GetPositionY(); + float dz = z - GetPositionZ(); - i_traveller.AI_SendMoveToPacket(x, y, z, t, i_traveller.GetUnitMovementFlags(), 0); + if(i_traveller.HasUnitMovementFlag(MOVEMENTFLAG_FLYING2)) + return sqrt((dx*dx) + (dy*dy) + (dz*dz)); + else //Walking on the ground + return sqrt((dx*dx) + (dy*dy)); +} + + +template<> +inline void Traveller<Creature>::MoveTo(float x, float y, float z, uint32 t) +{ + //i_traveller.AI_SendMoveToPacket(x, y, z, t, i_traveller.GetUnitMovementFlags(), 0); + i_traveller.SendMonsterMove(x, y, z, t); } // specialization for players template<> inline float Traveller<Player>::Speed() { - if (i_traveller.isInFlight()) + if(i_traveller.hasUnitState(UNIT_STAT_CHARGING)) + return i_traveller.m_TempSpeed; + else if(i_traveller.isInFlight()) return PLAYER_FLIGHT_SPEED; else return i_traveller.GetSpeed(i_traveller.HasUnitMovementFlag(MOVEMENTFLAG_WALK_MODE) ? MOVE_WALK : MOVE_RUN); } template<> +inline float Traveller<Player>::GetMoveDestinationTo(float x, float y, float z) +{ + float dx = x - GetPositionX(); + float dy = y - GetPositionY(); + float dz = z - GetPositionZ(); + + if (i_traveller.isInFlight()) + return sqrt((dx*dx) + (dy*dy) + (dz*dz)); + else //Walking on the ground + return sqrt((dx*dx) + (dy*dy)); +} + +template<> inline void Traveller<Player>::Relocation(float x, float y, float z, float orientation) { - MapManager::Instance().GetMap(i_traveller.GetMapId(), &i_traveller)->PlayerRelocation(&i_traveller, x, y, z, orientation); + i_traveller.GetMap()->PlayerRelocation(&i_traveller, x, y, z, orientation); } template<> diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index 6e7a2302c9f..0ee053745aa 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -49,6 +49,8 @@ #include "CreatureGroups.h" #include "PetAI.h" #include "NullCreatureAI.h" +#include "Traveller.h" +#include "TemporarySummon.h" #include <math.h> @@ -62,92 +64,17 @@ float baseMoveSpeed[MAX_MOVE_TYPE] = 3.141594f, // MOVE_TURN_RATE 7.0f, // MOVE_FLIGHT 4.5f, // MOVE_FLIGHT_BACK + 3.14f // MOVE_PITCH_RATE }; -void InitTriggerAuraData(); - -// auraTypes contains attacker auras capable of proc'ing cast auras -static Unit::AuraTypeSet GenerateAttakerProcCastAuraTypes() -{ - static Unit::AuraTypeSet auraTypes; - auraTypes.insert(SPELL_AURA_DUMMY); - auraTypes.insert(SPELL_AURA_PROC_TRIGGER_SPELL); - auraTypes.insert(SPELL_AURA_MOD_HASTE); - auraTypes.insert(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); - return auraTypes; -} - -// auraTypes contains victim auras capable of proc'ing cast auras -static Unit::AuraTypeSet GenerateVictimProcCastAuraTypes() -{ - static Unit::AuraTypeSet auraTypes; - auraTypes.insert(SPELL_AURA_DUMMY); - auraTypes.insert(SPELL_AURA_PRAYER_OF_MENDING); - auraTypes.insert(SPELL_AURA_PROC_TRIGGER_SPELL); - return auraTypes; -} - -// auraTypes contains auras capable of proc effect/damage (but not cast) for attacker -static Unit::AuraTypeSet GenerateAttakerProcEffectAuraTypes() -{ - static Unit::AuraTypeSet auraTypes; - auraTypes.insert(SPELL_AURA_MOD_DAMAGE_DONE); - auraTypes.insert(SPELL_AURA_PROC_TRIGGER_DAMAGE); - auraTypes.insert(SPELL_AURA_MOD_CASTING_SPEED); - auraTypes.insert(SPELL_AURA_MOD_RATING); - return auraTypes; -} - -// auraTypes contains auras capable of proc effect/damage (but not cast) for victim -static Unit::AuraTypeSet GenerateVictimProcEffectAuraTypes() -{ - static Unit::AuraTypeSet auraTypes; - auraTypes.insert(SPELL_AURA_MOD_RESISTANCE); - auraTypes.insert(SPELL_AURA_PROC_TRIGGER_DAMAGE); - auraTypes.insert(SPELL_AURA_MOD_PARRY_PERCENT); - auraTypes.insert(SPELL_AURA_MOD_BLOCK_PERCENT); - auraTypes.insert(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN); - return auraTypes; -} - -static Unit::AuraTypeSet attackerProcCastAuraTypes = GenerateAttakerProcCastAuraTypes(); -static Unit::AuraTypeSet attackerProcEffectAuraTypes = GenerateAttakerProcEffectAuraTypes(); - -static Unit::AuraTypeSet victimProcCastAuraTypes = GenerateVictimProcCastAuraTypes(); -static Unit::AuraTypeSet victimProcEffectAuraTypes = GenerateVictimProcEffectAuraTypes(); - -// auraTypes contains auras capable of proc'ing for attacker and victim -static Unit::AuraTypeSet GenerateProcAuraTypes() -{ - InitTriggerAuraData(); - - Unit::AuraTypeSet auraTypes; - auraTypes.insert(attackerProcCastAuraTypes.begin(),attackerProcCastAuraTypes.end()); - auraTypes.insert(attackerProcEffectAuraTypes.begin(),attackerProcEffectAuraTypes.end()); - auraTypes.insert(victimProcCastAuraTypes.begin(),victimProcCastAuraTypes.end()); - auraTypes.insert(victimProcEffectAuraTypes.begin(),victimProcEffectAuraTypes.end()); - return auraTypes; -} - -static Unit::AuraTypeSet procAuraTypes = GenerateProcAuraTypes(); - -bool IsPassiveStackableSpell( uint32 spellId ) -{ - if(!IsPassiveSpell(spellId)) - return false; - - SpellEntry const* spellProto = sSpellStore.LookupEntry(spellId); - if(!spellProto) - return false; - - for(int j = 0; j < 3; ++j) - { - if(std::find(procAuraTypes.begin(),procAuraTypes.end(),spellProto->EffectApplyAuraName[j])!=procAuraTypes.end()) - return false; - } - - return true; -} +// Used for prepare can/can`t triggr aura +static bool InitTriggerAuraData(); +// Define can trigger auras +static bool isTriggerAura[TOTAL_AURAS]; +// Define can`t trigger auras (need for disable second trigger) +static bool isNonTriggerAura[TOTAL_AURAS]; +// Prepare lists +static bool procPrepared = InitTriggerAuraData(); Unit::Unit() : WorldObject(), i_motionMaster(this), m_ThreatManager(this), m_HostilRefManager(this) @@ -157,7 +84,7 @@ Unit::Unit() m_objectType |= TYPEMASK_UNIT; m_objectTypeId = TYPEID_UNIT; // 2.3.2 - 0x70 - m_updateFlag = (UPDATEFLAG_HIGHGUID | UPDATEFLAG_LIVING | UPDATEFLAG_HASPOSITION); + m_updateFlag = (UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID | UPDATEFLAG_LIVING | UPDATEFLAG_HAS_POSITION); m_attackTimer[BASE_ATTACK] = 0; m_attackTimer[OFF_ATTACK] = 0; @@ -178,15 +105,14 @@ Unit::Unit() m_addDmgOnce = 0; - for(int i = 0; i < MAX_TOTEM; ++i) - m_TotemSlot[i] = 0; + for(int i = 0; i < MAX_SUMMON_SLOT; ++i) + m_SummonSlot[i] = 0; m_ObjectSlot[0] = m_ObjectSlot[1] = m_ObjectSlot[2] = m_ObjectSlot[3] = 0; //m_Aura = NULL; //m_AurasCheck = 2000; //m_removeAuraTimer = 4; //tmpAura = NULL; - waterbreath = false; m_Visibility = VISIBILITY_ON; @@ -209,7 +135,7 @@ Unit::Unit() // implement 50% base damage from offhand m_auraModifiersGroup[UNIT_MOD_DAMAGE_OFFHAND][TOTAL_PCT] = 0.5f; - for (int i = 0; i < 3; i++) + for (int i = 0; i < MAX_ATTACK; ++i) { m_weaponDamage[i][MINDAMAGE] = BASE_MINDAMAGE; m_weaponDamage[i][MAXDAMAGE] = BASE_MAXDAMAGE; @@ -233,7 +159,6 @@ Unit::Unit() for (int i = 0; i < MAX_MOVE_TYPE; ++i) m_speed_rate[i] = 1.0f; - m_removedAuras = 0; m_charmInfo = NULL; m_unit_movement_flags = 0; m_reducedThreatPercent = 0; @@ -264,6 +189,7 @@ Unit::~Unit() assert(!m_attacking); assert(m_attackers.empty()); assert(m_sharedVision.empty()); + assert(m_Controlled.empty()); } void Unit::Update( uint32 p_time ) @@ -313,6 +239,7 @@ void Unit::Update( uint32 p_time ) ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, GetHealth() < GetMaxHealth()*0.20f); ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, GetHealth() < GetMaxHealth()*0.35f); + ModifyAuraState(AURA_STATE_HEALTH_ABOVE_75_PERCENT, GetHealth() > GetMaxHealth()*0.75f); i_motionMaster.UpdateMotion(p_time); } @@ -329,28 +256,23 @@ void Unit::SendMonsterMoveWithSpeedToCurrentDestination(Player* player) { float x, y, z; if(GetMotionMaster()->GetDestination(x, y, z)) - SendMonsterMoveWithSpeed(x, y, z, GetUnitMovementFlags(), 0, player); + SendMonsterMoveWithSpeed(x, y, z, 0, player); } -void Unit::SendMonsterMoveWithSpeed(float x, float y, float z, uint32 MovementFlags, uint32 transitTime, Player* player) +void Unit::SendMonsterMoveWithSpeed(float x, float y, float z, uint32 transitTime, Player* player) { if (!transitTime) { - float dx = x - GetPositionX(); - float dy = y - GetPositionY(); - float dz = z - GetPositionZ(); - - float dist = ((dx*dx) + (dy*dy) + (dz*dz)); - if(dist<0) - dist = 0; + if(GetTypeId()==TYPEID_PLAYER) + { + Traveller<Player> traveller(*(Player*)this); + transitTime = traveller.GetTotalTrevelTimeTo(x,y,z); + } else - dist = sqrt(dist); - - double speed = GetSpeed((MovementFlags & MOVEMENTFLAG_WALK_MODE) ? MOVE_WALK : MOVE_RUN); - if(speed<=0) - speed = 2.5f; - speed *= 0.001f; - transitTime = static_cast<uint32>(dist / speed + 0.5); + { + Traveller<Creature> traveller(*(Creature*)this); + transitTime = traveller.GetTotalTrevelTimeTo(x,y,z); + } } //float orientation = (float)atan2((double)dy, (double)dx); SendMonsterMove(x, y, z, transitTime, player); @@ -368,7 +290,7 @@ void Unit::SendMonsterStop() void Unit::SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint32 Time, Player* player) { - WorldPacket data( SMSG_MONSTER_MOVE, (41 + GetPackGUID().size()) ); + WorldPacket data( SMSG_MONSTER_MOVE, 12+4+1+4+4+4+12+GetPackGUID().size()); data.append(GetPackGUID()); data << GetPositionX() << GetPositionY() << GetPositionZ(); @@ -387,38 +309,66 @@ void Unit::SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint32 T SendMessageToSet( &data, true ); } +void Unit::SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint32 MoveFlags, uint32 time, float speedZ, Player *player) +{ + WorldPacket data( SMSG_MONSTER_MOVE, 12+4+1+4+4+4+12+GetPackGUID().size()); + data.append(GetPackGUID()); + + data << GetPositionX() << GetPositionY() << GetPositionZ(); + data << getMSTime(); + + data << uint8(0); + data << MoveFlags; + + if(MoveFlags & MOVEFLAG_JUMP) + { + data << time; + data << speedZ; + data << (uint32)0; // walk time after jump + } + else + data << time; + + data << uint32(1); // 1 single waypoint + data << NewPosX << NewPosY << NewPosZ; // the single waypoint Point B + + if(player) + player->GetSession()->SendPacket(&data); + else + SendMessageToSet( &data, true ); +} + /*void Unit::SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint8 type, uint32 MovementFlags, uint32 Time, Player* player) { WorldPacket data( SMSG_MONSTER_MOVE, (41 + GetPackGUID().size()) ); data.append(GetPackGUID()); - // Point A, starting location data << GetPositionX() << GetPositionY() << GetPositionZ(); - // unknown field - unrelated to orientation - // seems to increment about 1000 for every 1.7 seconds - // for now, we'll just use mstime - data << getMSTime(); + data << uint32(getMSTime()); data << uint8(type); // unknown switch(type) { case 0: // normal packet break; - case 1: // stop packet + case 1: // stop packet (raw pos?) SendMessageToSet( &data, true ); return; + case 2: // facing spot, not used currently + data << float(0); + data << float(0); + data << float(0); + break; case 3: // not used currently - data << uint64(0); // probably target guid + data << uint64(0); // probably target guid (facing target?) break; case 4: // not used currently - data << float(0); // probably orientation + data << float(0); // facing angle break; } - //Movement Flags (0x0 = walk, 0x100 = run, 0x200 = fly/swim) - data << uint32((MovementFlags & MOVEMENTFLAG_LEVITATING) ? MOVEFLAG_FLY : MOVEFLAG_WALK); - - data << Time; // Time in between points + data << uint32(MovementFlags); + data << uint32(Time); // Time in between points data << uint32(1); // 1 single waypoint data << NewPosX << NewPosY << NewPosZ; // the single waypoint Point B @@ -439,16 +389,12 @@ void Unit::SendMonsterMoveByPath(Path const& path, uint32 start, uint32 end) data << GetPositionX(); data << GetPositionY(); data << GetPositionZ(); - data << getMSTime(); - data << uint8( 0 ); data << uint32(((GetUnitMovementFlags() & MOVEMENTFLAG_LEVITATING) || isInFlight())? (MOVEFLAG_FLY|MOVEFLAG_WALK) : MOVEFLAG_WALK); data << uint32( traveltime ); data << uint32( pathSize ); data.append( (char*)path.GetNodes(start), pathSize * 4 * 3 ); - - //WPAssert( data.size() == 37 + pathnodes.Size( ) * 4 * 3 ); SendMessageToSet(&data, true); } @@ -503,71 +449,22 @@ void Unit::GetRandomContactPoint( const Unit* obj, float &x, float &y, float &z, , GetAngle(obj) + (attacker_number ? (M_PI/2 - M_PI * rand_norm()) * (float)attacker_number / combat_reach / 3 : 0)); } -void Unit::RemoveSpellsCausingAura(AuraType auraType) -{ - if (auraType >= TOTAL_AURAS) return; - AuraList::iterator iter, next; - for (iter = m_modAuras[auraType].begin(); iter != m_modAuras[auraType].end(); iter = next) - { - next = iter; - ++next; - - if (*iter) - { - RemoveAurasDueToSpell((*iter)->GetId()); - if (!m_modAuras[auraType].empty()) - next = m_modAuras[auraType].begin(); - else - return; - } - } -} - -void Unit::RemoveAuraTypeByCaster(AuraType auraType, uint64 casterGUID) -{ - if (auraType >= TOTAL_AURAS) return; - AuraList::iterator iter, next; - for(iter = m_modAuras[auraType].begin(); iter != m_modAuras[auraType].end(); iter = next) - { - next = iter; - ++next; - - if (*iter) - { - RemoveAurasByCasterSpell((*iter)->GetId(), casterGUID); - if (!m_modAuras[auraType].empty()) - next = m_modAuras[auraType].begin(); - else - return; - } - } -} - void Unit::RemoveAurasWithInterruptFlags(uint32 flag, uint32 except) { if(!(m_interruptMask & flag)) return; // interrupt auras - AuraList::iterator iter, next; - for (iter = m_interruptableAuras.begin(); iter != m_interruptableAuras.end(); iter = next) + for (AuraList::iterator iter = m_interruptableAuras.begin(); iter != m_interruptableAuras.end();) { - next = iter; - ++next; - - //sLog.outDetail("auraflag:%u flag:%u = %u",(*iter)->GetSpellProto()->AuraInterruptFlags,flag,(*iter)->GetSpellProto()->AuraInterruptFlags & flag); - if(*iter && ((*iter)->GetSpellProto()->AuraInterruptFlags & flag)) + Aura * aur = *iter; + ++iter; + if ((aur->GetSpellProto()->AuraInterruptFlags & flag) && (!except || aur->GetId() != except)) { - if((*iter)->IsInUse()) - sLog.outError("Aura %u is trying to remove itself! Flag %u. May cause crash!", (*iter)->GetId(), flag); - else if(!except || (*iter)->GetId() != except) - { - RemoveAurasDueToSpell((*iter)->GetId()); - if (!m_interruptableAuras.empty()) - next = m_interruptableAuras.begin(); - else - break; - } + uint32 removedAuras = m_removedAuras.size(); + RemoveAura(aur); + if (removedAuras+1<m_removedAuras.size()) + iter=m_interruptableAuras.begin(); } } @@ -594,11 +491,6 @@ void Unit::UpdateInterruptMask() m_interruptMask |= spell->m_spellInfo->ChannelInterruptFlags; } -bool Unit::HasAuraType(AuraType auraType) const -{ - return (!m_modAuras[auraType].empty()); -} - /* Called by DealDamage for auras that have a chance to be dispelled on damage taken. */ void Unit::RemoveSpellbyDamageTaken(uint32 damage, uint32 spell) { @@ -606,26 +498,24 @@ void Unit::RemoveSpellbyDamageTaken(uint32 damage, uint32 spell) uint32 max_dmg = getLevel() > 8 ? 25 * getLevel() - 150 : 50; float chance = float(damage) / max_dmg * 100.0f; - AuraList::iterator i, next; - for(i = m_ccAuras.begin(); i != m_ccAuras.end(); i = next) + // interrupt auras + for (AuraList::iterator iter = m_ccAuras.begin(); iter != m_ccAuras.end();) { - next = i; - ++next; - - if(*i && (!spell || (*i)->GetId() != spell) && roll_chance_f(chance)) + Aura * aur = *iter; + ++iter; + if ((!spell || aur->GetId() != spell) && roll_chance_f(chance)) { - RemoveAurasDueToSpell((*i)->GetId()); - if (!m_ccAuras.empty()) - next = m_ccAuras.begin(); - else - return; + uint32 removedAuras = m_removedAuras.size(); + RemoveAura(aur); + if (removedAuras+1<m_removedAuras.size()) + iter=m_ccAuras.begin(); } } } uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellEntry const *spellProto, bool durabilityLoss) { - if (!pVictim->isAlive() || pVictim->isInFlight() || pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode()) + if (!pVictim->isAlive() || pVictim->hasUnitState(UNIT_STAT_UNATTACKABLE) || pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode()) return 0; //You don't lose health from damage taken from another player while in a sanctuary @@ -684,30 +574,27 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa return 0; } - if(pVictim->GetTypeId() != TYPEID_PLAYER) + // no xp,health if type 8 /critters/ + if(pVictim->GetTypeId() != TYPEID_PLAYER && pVictim->GetCreatureType() == CREATURE_TYPE_CRITTER) { - // no xp,health if type 8 /critters/ - if ( pVictim->GetCreatureType() == CREATURE_TYPE_CRITTER) + // allow loot only if has loot_id in creature_template + if(damage >= pVictim->GetHealth()) { - // allow loot only if has loot_id in creature_template - if(damage >= pVictim->GetHealth()) - { - pVictim->setDeathState(JUST_DIED); - pVictim->SetHealth(0); + pVictim->setDeathState(JUST_DIED); + pVictim->SetHealth(0); - CreatureInfo const* cInfo = ((Creature*)pVictim)->GetCreatureInfo(); - if(cInfo && cInfo->lootid) - pVictim->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + CreatureInfo const* cInfo = ((Creature*)pVictim)->GetCreatureInfo(); + if(cInfo && cInfo->lootid) + pVictim->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); - // some critters required for quests - if(GetTypeId() == TYPEID_PLAYER) - ((Player*)this)->KilledMonster(pVictim->GetEntry(),pVictim->GetGUID()); - } - else - pVictim->ModifyHealth(- (int32)damage); - - return damage; + // some critters required for quests + if(GetTypeId() == TYPEID_PLAYER) + ((Player*)this)->KilledMonster(pVictim->GetEntry(),pVictim->GetGUID()); } + else + pVictim->ModifyHealth(- (int32)damage); + + return damage; } DEBUG_LOG("DealDamageStart"); @@ -777,29 +664,174 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa { DEBUG_LOG("DealDamage: victim just died"); Kill(pVictim, durabilityLoss); - } - else // if (health <= damage) - { - DEBUG_LOG("DealDamageAlive"); - pVictim->ModifyHealth(- (int32)damage); + /*// find player: owner of controlled `this` or `this` itself maybe + Player *player = GetCharmerOrOwnerPlayerOrPlayerItself(); + + if(pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->GetLootRecipient()) + player = ((Creature*)pVictim)->GetLootRecipient(); + // Reward player, his pets, and group/raid members + // call kill spell proc event (before real die and combat stop to triggering auras removed at death/combat stop) + if(player && player!=pVictim) + { + player->RewardPlayerAndGroupAtKill(pVictim); + player->ProcDamageAndSpell(pVictim, PROC_FLAG_KILL, PROC_FLAG_KILLED, PROC_EX_NONE, 0); + } + + DEBUG_LOG("DealDamageAttackStop"); + + // stop combat + pVictim->CombatStop(); + pVictim->getHostilRefManager().deleteReferences(); + + bool damageFromSpiritOfRedemtionTalent = spellProto && spellProto->Id == 27795; + + // if talent known but not triggered (check priest class for speedup check) + Aura* spiritOfRedemtionTalentReady = NULL; + if( !damageFromSpiritOfRedemtionTalent && // not called from SPELL_AURA_SPIRIT_OF_REDEMPTION + pVictim->GetTypeId()==TYPEID_PLAYER && pVictim->getClass()==CLASS_PRIEST ) + { + AuraList const& vDummyAuras = pVictim->GetAurasByType(SPELL_AURA_DUMMY); + for(AuraList::const_iterator itr = vDummyAuras.begin(); itr != vDummyAuras.end(); ++itr) + { + if((*itr)->GetSpellProto()->SpellIconID==1654) + { + spiritOfRedemtionTalentReady = *itr; + break; + } + } + } + + DEBUG_LOG("SET JUST_DIED"); + if(!spiritOfRedemtionTalentReady) + pVictim->setDeathState(JUST_DIED); + + DEBUG_LOG("DealDamageHealth1"); + + if(spiritOfRedemtionTalentReady) + { + // save value before aura remove + uint32 ressSpellId = pVictim->GetUInt32Value(PLAYER_SELF_RES_SPELL); + if(!ressSpellId) + ressSpellId = ((Player*)pVictim)->GetResurrectionSpellId(); + + //Remove all expected to remove at death auras (most important negative case like DoT or periodic triggers) + pVictim->RemoveAllAurasOnDeath(); + + // restore for use at real death + pVictim->SetUInt32Value(PLAYER_SELF_RES_SPELL,ressSpellId); + + // FORM_SPIRITOFREDEMPTION and related auras + pVictim->CastSpell(pVictim,27827,true,NULL,spiritOfRedemtionTalentReady); + } + else + pVictim->SetHealth(0); + + // remember victim PvP death for corpse type and corpse reclaim delay + // at original death (not at SpiritOfRedemtionTalent timeout) + if( pVictim->GetTypeId()==TYPEID_PLAYER && !damageFromSpiritOfRedemtionTalent ) + ((Player*)pVictim)->SetPvPDeath(player!=NULL); - if(damagetype != DOT) + // Call KilledUnit for creatures + if (GetTypeId() == TYPEID_UNIT && ((Creature*)this)->AI()) + ((Creature*)this)->AI()->KilledUnit(pVictim); + + // achievement stuff + if (pVictim->GetTypeId() == TYPEID_PLAYER) { - if(!getVictim()) - /*{ - // if have target and damage pVictim just call AI reaction - if(pVictim != getVictim() && pVictim->GetTypeId()==TYPEID_UNIT && ((Creature*)pVictim)->IsAIEnabled) - ((Creature*)pVictim)->AI()->AttackedBy(this); + if (GetTypeId() == TYPEID_UNIT) + ((Player*)pVictim)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE, GetEntry()); + else if(GetTypeId() == TYPEID_PLAYER && pVictim != this) + ((Player*)pVictim)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER, 1, ((Player*)this)->GetTeam()); + } + + // 10% durability loss on death + // clean InHateListOf + if (pVictim->GetTypeId() == TYPEID_PLAYER) + { + // only if not player and not controlled by player pet. And not at BG + if (durabilityLoss && !player && !((Player*)pVictim)->InBattleGround()) + { + DEBUG_LOG("We are dead, loosing 10 percents durability"); + ((Player*)pVictim)->DurabilityLossAll(0.10f,false); + // durability lost message + WorldPacket data(SMSG_DURABILITY_DAMAGE_DEATH, 0); + ((Player*)pVictim)->GetSession()->SendPacket(&data); } - else*/ + } + else // creature died + { + DEBUG_LOG("DealDamageNotPlayer"); + Creature *cVictim = (Creature*)pVictim; + + if(!cVictim->isPet()) { - // if not have main target then attack state with target (including AI call) - //start melee attacks only after melee hit - Attack(pVictim,(damagetype == DIRECT_DAMAGE)); + cVictim->DeleteThreatList(); + cVictim->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + } + // Call creature just died function + if (cVictim->AI()) + cVictim->AI()->JustDied(this); + + // Dungeon specific stuff, only applies to players killing creatures + if(cVictim->GetInstanceId()) + { + Map *m = cVictim->GetMap(); + Player *creditedPlayer = GetCharmerOrOwnerPlayerOrPlayerItself(); + // TODO: do instance binding anyway if the charmer/owner is offline + + if(m->IsDungeon() && creditedPlayer) + { + if(m->IsRaid() || m->IsHeroic()) + { + if(cVictim->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_INSTANCE_BIND) + ((InstanceMap *)m)->PermBindAllPlayers(creditedPlayer); + } + else + { + // the reset time is set but not added to the scheduler + // until the players leave the instance + time_t resettime = cVictim->GetRespawnTimeEx() + 2 * HOUR; + if(InstanceSave *save = sInstanceSaveManager.GetInstanceSave(cVictim->GetInstanceId())) + if(save->GetResetTime() < resettime) save->SetResetTime(resettime); + } + } } } + // last damage from non duel opponent or opponent controlled creature + if(duel_hasEnded) + { + assert(pVictim->GetTypeId()==TYPEID_PLAYER); + Player *he = (Player*)pVictim; + + assert(he->duel); + + he->duel->opponent->CombatStopWithPets(true); + he->CombatStopWithPets(true); + + he->DuelComplete(DUEL_INTERUPTED); + } + + // battleground things (do this at the end, so the death state flag will be properly set to handle in the bg->handlekill) + if(pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->InBattleGround()) + { + Player *killed = ((Player*)pVictim); + if(BattleGround *bg = killed->GetBattleGround()) + if(player) + bg->HandleKillPlayer(killed, player); + //later we can add support for creature->player kills here i'm + //not sure, but i guess those kills also get counted in av + //else if(GetTypeId() == TYPEID_UNIT) + // bg->HandleKillPlayer(killed,(Creature*)this); + }*/ + } + else // if (health <= damage) + { + DEBUG_LOG("DealDamageAlive"); + + pVictim->ModifyHealth(- (int32)damage); + if(damagetype == DIRECT_DAMAGE || damagetype == SPELL_DIRECT_DAMAGE) { //TODO: This is from procflag, I do not know which spell needs this @@ -807,7 +839,6 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa //if (!spellProto || !(spellProto->AuraInterruptFlags&AURA_INTERRUPT_FLAG_DIRECT_DAMAGE)) pVictim->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_DIRECT_DAMAGE, spellProto ? spellProto->Id : 0); } - if (pVictim->GetTypeId() != TYPEID_PLAYER) { if(spellProto && IsDamageToThreatSpell(spellProto)) @@ -874,7 +905,7 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa if(spell->getState() == SPELL_STATE_PREPARING) { uint32 interruptFlags = spell->m_spellInfo->InterruptFlags; - if(interruptFlags & SPELL_INTERRUPT_FLAG_DAMAGE) + if(interruptFlags & SPELL_INTERRUPT_FLAG_ABORT_ON_DMG) pVictim->InterruptNonMeleeSpells(false); else if(interruptFlags & SPELL_INTERRUPT_FLAG_PUSH_BACK) spell->Delayed(); @@ -924,7 +955,7 @@ void Unit::CastStop(uint32 except_spellid) InterruptSpell(i,false); } -void Unit::CastSpell(Unit* Victim, uint32 spellId, bool triggered, Item *castItem, Aura* triggeredByAura, uint64 originalCaster) +void Unit::CastSpell(Unit* Victim, uint32 spellId, bool triggered, Item *castItem, AuraEffect* triggeredByAura, uint64 originalCaster) { SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId ); @@ -937,7 +968,7 @@ void Unit::CastSpell(Unit* Victim, uint32 spellId, bool triggered, Item *castIte CastSpell(Victim,spellInfo,triggered,castItem,triggeredByAura, originalCaster); } -void Unit::CastSpell(Unit* Victim,SpellEntry const *spellInfo, bool triggered, Item *castItem, Aura* triggeredByAura, uint64 originalCaster) +void Unit::CastSpell(Unit* Victim,SpellEntry const *spellInfo, bool triggered, Item *castItem, AuraEffect* triggeredByAura, uint64 originalCaster) { if(!spellInfo) { @@ -991,20 +1022,20 @@ void Unit::CastSpell(Unit* Victim,SpellEntry const *spellInfo, bool triggered, I spell->prepare(&targets, triggeredByAura); } -void Unit::CastCustomSpell(Unit* Victim,uint32 spellId, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem, Aura* triggeredByAura, uint64 originalCaster) +void Unit::CastCustomSpell(Unit* Victim,uint32 spellId, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem, AuraEffect* triggeredByAura, uint64 originalCaster) { SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId ); if(!spellInfo) { - sLog.outError("CastCustomSpell: unknown spell id %i\n", spellId); + sLog.outError("CastCustomSpell: unknown spell id %i", spellId); return; } CastCustomSpell(Victim,spellInfo,bp0,bp1,bp2,triggered,castItem,triggeredByAura, originalCaster); } -void Unit::CastCustomSpell(Unit* Victim,SpellEntry const *spellInfo, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem, Aura* triggeredByAura, uint64 originalCaster) +void Unit::CastCustomSpell(Unit* Victim,SpellEntry const *spellInfo, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem, AuraEffect* triggeredByAura, uint64 originalCaster) { if(!spellInfo) { @@ -1055,7 +1086,7 @@ void Unit::CastCustomSpell(Unit* Victim,SpellEntry const *spellInfo, int32 const } // used for scripting -void Unit::CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item *castItem, Aura* triggeredByAura, uint64 originalCaster) +void Unit::CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item *castItem, AuraEffect* triggeredByAura, uint64 originalCaster) { SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId ); @@ -1080,7 +1111,7 @@ void Unit::CastSpell(float x, float y, float z, uint32 spellId, bool triggered, } // used for scripting -void Unit::CastSpell(GameObject *go, uint32 spellId, bool triggered, Item *castItem, Aura* triggeredByAura, uint64 originalCaster) +void Unit::CastSpell(GameObject *go, uint32 spellId, bool triggered, Item *castItem, AuraEffect* triggeredByAura, uint64 originalCaster) { if(!go) return; @@ -1187,6 +1218,9 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage *damageInfo, int32 dama if (blocked) { damageInfo->blocked = uint32(pVictim->GetShieldBlockValue()); + //double blocked amount if block is critical + if (isBlockCritical()) + damageInfo->blocked+=damageInfo->blocked; if (damage < damageInfo->blocked) damageInfo->blocked = damage; damage-=damageInfo->blocked; @@ -1201,7 +1235,7 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage *damageInfo, int32 dama if (crit) { damageInfo->HitInfo|= SPELL_HIT_TYPE_CRIT; - damage = SpellCriticalBonus(spellInfo, damage, pVictim); + damage = SpellCriticalDamageBonus(spellInfo, damage, pVictim); // Resilience - reduce crit damage if (pVictim->GetTypeId()==TYPEID_PLAYER) damage -= ((Player*)pVictim)->GetSpellCritDamageReduction(damage); @@ -1211,12 +1245,12 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage *damageInfo, int32 dama } if( damageSchoolMask & SPELL_SCHOOL_MASK_NORMAL ) - damage = CalcArmorReducedDamage(pVictim, damage); + damage = CalcArmorReducedDamage(pVictim, damage, spellInfo, attackType); // Calculate absorb resist if(damage > 0) { - CalcAbsorbResist(pVictim, damageSchoolMask, SPELL_DIRECT_DAMAGE, damage, &damageInfo->absorb, &damageInfo->resist); + CalcAbsorbResist(pVictim, damageSchoolMask, SPELL_DIRECT_DAMAGE, damage, &damageInfo->absorb, &damageInfo->resist, spellInfo); damage-= damageInfo->absorb + damageInfo->resist; } else @@ -1224,6 +1258,37 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage *damageInfo, int32 dama damageInfo->damage = damage; } +int32 Unit::GetIgnoredArmorMultiplier(SpellEntry const *spellInfo, WeaponAttackType attackType) +{ + if (GetTypeId() != TYPEID_PLAYER) + return 0; + //check if spell uses weapon + if (!spellInfo || spellInfo->EquippedItemClass!=ITEM_CLASS_WEAPON) + return 0; + Item *item = NULL; + if(attackType == BASE_ATTACK) + item = ((Player*)this)->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); + else if (attackType == OFF_ATTACK) + item = ((Player*)this)->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); + else if (attackType == RANGED_ATTACK) + item = ((Player*)this)->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED); + if (!item) + return 0; + + AuraEffectList const& armAuras = GetAurasByType(SPELL_AURA_MOD_WEAPONTYPE_IGNORE_TARGET_RESISTANCE); + int32 armorIgnored = 0; + for(AuraEffectList::const_iterator i = armAuras.begin();i != armAuras.end(); ++i) + { + if (!((*i)->GetSpellProto()->EquippedItemClass==item->GetProto()->Class + && (*i)->GetSpellProto()->EquippedItemSubClassMask & (1<<item->GetProto()->SubClass))) + continue; + + if((*i)->GetAmount()) + armorIgnored += (*i)->GetAmount(); + } + return (-armorIgnored); +} + void Unit::DealSpellDamage(SpellNonMeleeDamage *damageInfo, bool durabilityLoss) { if (damageInfo==0) @@ -1234,7 +1299,7 @@ void Unit::DealSpellDamage(SpellNonMeleeDamage *damageInfo, bool durabilityLoss) if(!this || !pVictim) return; - if (!pVictim->isAlive() || pVictim->isInFlight() || pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode()) + if (!pVictim->isAlive() || pVictim->hasUnitState(UNIT_STAT_UNATTACKABLE) || pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode()) return; SpellEntry const *spellProto = sSpellStore.LookupEntry(damageInfo->SpellID); @@ -1253,20 +1318,6 @@ void Unit::DealSpellDamage(SpellNonMeleeDamage *damageInfo, bool durabilityLoss) return; } - // update at damage Judgement aura duration that applied by attacker at victim - if(damageInfo->damage && spellProto->Id == 35395) - { - AuraMap& vAuras = pVictim->GetAuras(); - for(AuraMap::iterator itr = vAuras.begin(); itr != vAuras.end(); ++itr) - { - SpellEntry const *spellInfo = (*itr).second->GetSpellProto(); - if( spellInfo->AttributesEx3 & 0x40000 && spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN && ((*itr).second->GetCasterGUID() == GetGUID())) - { - (*itr).second->SetAuraDuration((*itr).second->GetAuraMaxDuration()); - (*itr).second->UpdateAuraDuration(); - } - } - } // Call default DealDamage CleanDamage cleanDamage(damageInfo->cleanDamage, BASE_ATTACK, MELEE_HIT_NORMAL); DealDamage(pVictim, damageInfo->damage, &cleanDamage, SPELL_DIRECT_DAMAGE, SpellSchoolMask(damageInfo->schoolMask), spellProto, durabilityLoss); @@ -1320,7 +1371,7 @@ void Unit::CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *da } // Physical Immune check - if(damageInfo->target->IsImmunedToDamage(SpellSchoolMask(damageInfo->damageSchoolMask),true)) + if(damageInfo->target->IsImmunedToDamage(SpellSchoolMask(damageInfo->damageSchoolMask))) { damageInfo->HitInfo |= HITINFO_NORMALSWING; damageInfo->TargetState = VICTIMSTATE_IS_IMMUNE; @@ -1334,7 +1385,7 @@ void Unit::CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *da // Add melee damage bonus MeleeDamageBonus(damageInfo->target, &damage, damageInfo->attackType); // Calculate armor reduction - damageInfo->damage = CalcArmorReducedDamage(damageInfo->target, damage); + damageInfo->damage = CalcArmorReducedDamage(damageInfo->target, damage, NULL , damageInfo->attackType); damageInfo->cleanDamage += damage - damageInfo->damage; damageInfo->hitOutCome = RollMeleeOutcomeAgainst(damageInfo->target, damageInfo->attackType); @@ -1422,8 +1473,12 @@ void Unit::CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *da case MELEE_HIT_BLOCK: { damageInfo->TargetState = VICTIMSTATE_NORMAL; + damageInfo->HitInfo |= HITINFO_BLOCK; damageInfo->procEx|=PROC_EX_BLOCK; damageInfo->blocked_amount = damageInfo->target->GetShieldBlockValue(); + //double blocked amount if block is critical + if (isBlockCritical()) + damageInfo->blocked_amount+=damageInfo->blocked_amount; if (damageInfo->blocked_amount >= damageInfo->damage) { damageInfo->TargetState = VICTIMSTATE_BLOCKS; @@ -1487,7 +1542,7 @@ void Unit::DealMeleeDamage(CalcDamageInfo *damageInfo, bool durabilityLoss) if(!this || !pVictim) return; - if (!pVictim->isAlive() || pVictim->isInFlight() || pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode()) + if (!pVictim->isAlive() || pVictim->hasUnitState(UNIT_STAT_UNATTACKABLE) || pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode()) return; //You don't lose health from damage taken from another player while in a sanctuary @@ -1570,41 +1625,26 @@ void Unit::DealMeleeDamage(CalcDamageInfo *damageInfo, bool durabilityLoss) CastSpell(pVictim, 1604, true); } - // update at damage Judgement aura duration that applied by attacker at victim - if(damageInfo->damage) - { - AuraMap& vAuras = pVictim->GetAuras(); - for(AuraMap::iterator itr = vAuras.begin(); itr != vAuras.end(); ++itr) - { - SpellEntry const *spellInfo = (*itr).second->GetSpellProto(); - if( spellInfo->AttributesEx3 & 0x40000 && spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN && ((*itr).second->GetCasterGUID() == GetGUID())) - { - (*itr).second->SetAuraDuration((*itr).second->GetAuraMaxDuration()); - (*itr).second->UpdateAuraDuration(); - } - } - } - // If not miss if (!(damageInfo->HitInfo & HITINFO_MISS)) { if(GetTypeId() == TYPEID_PLAYER && pVictim->isAlive()) { for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++) - ((Player*)this)->CastItemCombatSpell(((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0,i), pVictim, damageInfo->attackType); + ((Player*)this)->CastItemCombatSpell(((Player*)this)->GetUseableItemByPos(INVENTORY_SLOT_BAG_0,i), pVictim, damageInfo->attackType); } // victim's damage shield - std::set<Aura*> alreadyDone; - uint32 removedAuras = pVictim->m_removedAuras; - AuraList const& vDamageShields = pVictim->GetAurasByType(SPELL_AURA_DAMAGE_SHIELD); - for(AuraList::const_iterator i = vDamageShields.begin(), next = vDamageShields.begin(); i != vDamageShields.end(); i = next) + std::set<AuraEffect*> alreadyDone; + uint32 removedAuras = pVictim->m_removedAuras.size(); + AuraEffectList const& vDamageShields = pVictim->GetAurasByType(SPELL_AURA_DAMAGE_SHIELD); + for(AuraEffectList::const_iterator i = vDamageShields.begin(), next = vDamageShields.begin(); i != vDamageShields.end(); i = next) { next++; if (alreadyDone.find(*i) == alreadyDone.end()) { alreadyDone.insert(*i); - uint32 damage=(*i)->GetModifier()->m_amount; + uint32 damage=(*i)->GetAmount(); SpellEntry const *spellProto = sSpellStore.LookupEntry((*i)->GetId()); if(!spellProto) continue; @@ -1614,18 +1654,20 @@ void Unit::DealMeleeDamage(CalcDamageInfo *damageInfo, bool durabilityLoss) //CalcAbsorbResist(pVictim, SpellSchools(spellProto->School), SPELL_DIRECT_DAMAGE, damage, &absorb, &resist); //damage-=absorb + resist; - WorldPacket data(SMSG_SPELLDAMAGESHIELD,(8+8+4+4)); + WorldPacket data(SMSG_SPELLDAMAGESHIELD,(8+8+4+4+4+4)); data << uint64(pVictim->GetGUID()); data << uint64(GetGUID()); + data << uint32(spellProto->Id); + data << uint32(damage); // Damage + data << uint32(0); // Overkill data << uint32(spellProto->SchoolMask); - data << uint32(damage); pVictim->SendMessageToSet(&data, true ); pVictim->DealDamage(this, damage, 0, SPELL_DIRECT_DAMAGE, GetSpellSchoolMask(spellProto), spellProto, true); - if (pVictim->m_removedAuras > removedAuras) + if (pVictim->m_removedAuras.size() > removedAuras) { - removedAuras = pVictim->m_removedAuras; + removedAuras = pVictim->m_removedAuras.size(); next = vDamageShields.begin(); } } @@ -1637,28 +1679,50 @@ void Unit::DealMeleeDamage(CalcDamageInfo *damageInfo, bool durabilityLoss) void Unit::HandleEmoteCommand(uint32 anim_id) { WorldPacket data( SMSG_EMOTE, 12 ); - data << anim_id << GetGUID(); - WPAssert(data.size() == 12); - + data << uint32(anim_id); + data << uint64(GetGUID()); SendMessageToSet(&data, true); } -uint32 Unit::CalcArmorReducedDamage(Unit* pVictim, const uint32 damage) +uint32 Unit::CalcArmorReducedDamage(Unit* pVictim, const uint32 damage, SpellEntry const *spellInfo, WeaponAttackType attackType) { uint32 newdamage = 0; float armor = pVictim->GetArmor(); // Ignore enemy armor by SPELL_AURA_MOD_TARGET_RESISTANCE aura armor += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, SPELL_SCHOOL_MASK_NORMAL); - if (armor<0.0f) armor=0.0f; + armor *= float((GetIgnoredArmorMultiplier(spellInfo, attackType)+100.0f)/100.0f); + if(spellInfo) + if(Player *modOwner = GetSpellModOwner()) + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_IGNORE_ARMOR, armor); - float tmpvalue = 0.0f; - if(getLevel() <= 59) //Level 1-59 - tmpvalue = armor / (armor + 400.0f + 85.0f * getLevel()); - else if(getLevel() < 70) //Level 60-69 - tmpvalue = armor / (armor - 22167.5f + 467.5f * getLevel()); - else //Level 70+ - tmpvalue = armor / (armor + 10557.5f); + AuraEffectList const& ResIgnoreAurasAb = GetAurasByType(SPELL_AURA_MOD_ABILITY_IGNORE_TARGET_RESIST); + for(AuraEffectList::const_iterator j = ResIgnoreAurasAb.begin();j != ResIgnoreAurasAb.end(); ++j) + { + if( (*j)->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL + && (*j)->isAffectedOnSpell(spellInfo)) + armor= int32(float(armor) * (float(100-(*j)->GetAmount())/100.0f)); + } + + AuraEffectList const& ResIgnoreAuras = GetAurasByType(SPELL_AURA_MOD_IGNORE_TARGET_RESIST); + for(AuraEffectList::const_iterator j = ResIgnoreAuras.begin();j != ResIgnoreAuras.end(); ++j) + { + if( (*j)->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) + armor= int32(float(armor) * (float(100-(*j)->GetAmount())/100.0f)); + } + + // Apply Player CR_ARMOR_PENETRATION rating + if (GetTypeId()==TYPEID_PLAYER) + armor *= 1.0f - ((Player*)this)->GetRatingBonusValue(CR_ARMOR_PENETRATION) / 100.0f; + + if (armor < 0.0f) armor=0.0f; + + float levelModifier = getLevel(); + if ( levelModifier > 59 ) + levelModifier = levelModifier + (4.5f * (levelModifier-59)); + + float tmpvalue = 0.1f * armor / (8.5f * levelModifier + 40); + tmpvalue = tmpvalue/(1.0f + tmpvalue); if(tmpvalue < 0.0f) tmpvalue = 0.0f; @@ -1669,7 +1733,7 @@ uint32 Unit::CalcArmorReducedDamage(Unit* pVictim, const uint32 damage) return (newdamage > 1) ? newdamage : 1; } -void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffectType damagetype, const uint32 damage, uint32 *absorb, uint32 *resist) +void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffectType damagetype, const uint32 damage, uint32 *absorb, uint32 *resist, SpellEntry const *spellInfo) { if(!pVictim || !pVictim->isAlive() || !damage) return; @@ -1705,124 +1769,336 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe *resist += uint32(damage * m / 4); if(*resist > damage) *resist = damage; + + AuraEffectList const& ResIgnoreAurasAb = GetAurasByType(SPELL_AURA_MOD_ABILITY_IGNORE_TARGET_RESIST); + for(AuraEffectList::const_iterator j = ResIgnoreAurasAb.begin();j != ResIgnoreAurasAb.end(); ++j) + { + if( (*j)->GetMiscValue() & schoolMask + && (*j)->isAffectedOnSpell(spellInfo)) + *resist= int32(float(*resist) * (float(100-(*j)->GetAmount())/100.0f)); + } + + AuraEffectList const& ResIgnoreAuras = GetAurasByType(SPELL_AURA_MOD_IGNORE_TARGET_RESIST); + for(AuraEffectList::const_iterator j = ResIgnoreAuras.begin();j != ResIgnoreAuras.end(); ++j) + { + if( (*j)->GetMiscValue() & schoolMask) + *resist= int32(float(*resist) * (float(100-(*j)->GetAmount())/100.0f)); + } } else *resist = 0; int32 RemainingDamage = damage - *resist; - + int32 TotalAbsorb = RemainingDamage; + // Get unit state (need for some absorb check) + uint32 unitflag = pVictim->GetUInt32Value(UNIT_FIELD_FLAGS); + // Reflect damage spells (not cast any damage spell in aura lookup) + uint32 reflectSpell = 0; + int32 reflectDamage = 0; + uint32 healSpell = 0; + int32 healAmount = 0; + Unit * healCaster = NULL; + // Need remove expired auras after + bool existExpired = false; // absorb without mana cost - int32 reflectDamage = 0; - Aura* reflectAura = NULL; - AuraList const& vSchoolAbsorb = pVictim->GetAurasByType(SPELL_AURA_SCHOOL_ABSORB); - for(AuraList::const_iterator i = vSchoolAbsorb.begin(), next; i != vSchoolAbsorb.end() && RemainingDamage > 0; i = next) + AuraEffectList const& vSchoolAbsorb = pVictim->GetAurasByType(SPELL_AURA_SCHOOL_ABSORB); + for(AuraEffectList::const_iterator i = vSchoolAbsorb.begin(); i != vSchoolAbsorb.end() && RemainingDamage > 0; ++i) { - next = i; ++next; - - if (((*i)->GetModifier()->m_miscvalue & schoolMask)==0) + if (!((*i)->GetMiscValue() & schoolMask)) continue; - // Cheat Death - if((*i)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_ROGUE && (*i)->GetSpellProto()->SpellIconID == 2109) + SpellEntry const* spellProto = (*i)->GetSpellProto(); + + // Max Amount can be absorbed by this aura + int32 currentAbsorb = (*i)->GetAmount(); + + // Found empty aura (impossible but..) + if (currentAbsorb <=0) { - if (((Player*)pVictim)->HasSpellCooldown(31231)) - continue; - if (pVictim->GetHealth() <= RemainingDamage) + existExpired = true; + continue; + } + // Handle custom absorb auras + // TODO: try find better way + switch(spellProto->SpellFamilyName) + { + case SPELLFAMILY_GENERIC: { - int32 chance = (*i)->GetModifier()->m_amount; - if (roll_chance_i(chance)) + // Astral Shift + if (spellProto->SpellIconID == 3066) { - pVictim->CastSpell(pVictim,31231,true); - ((Player*)pVictim)->AddSpellCooldown(31231,0,time(NULL)+60); - - // with health > 10% lost health until health==10%, in other case no losses - uint32 health10 = pVictim->GetMaxHealth()/10; - RemainingDamage = pVictim->GetHealth() > health10 ? pVictim->GetHealth() - health10 : 0; + //reduces all damage taken while stun, fear or silence + if (unitflag & (UNIT_FLAG_STUNNED|UNIT_FLAG_FLEEING|UNIT_FLAG_SILENCED)) + RemainingDamage -= RemainingDamage * currentAbsorb / 100; + continue; } + // Nerves of Steel + if (spellProto->SpellIconID == 2115) + { + // while affected by Stun and Fear + if (unitflag&(UNIT_FLAG_STUNNED|UNIT_FLAG_FLEEING)) + RemainingDamage -= RemainingDamage * currentAbsorb / 100; + continue; + } + // Spell Deflection + if (spellProto->SpellIconID == 3006) + { + // You have a chance equal to your Parry chance + if (damagetype == DIRECT_DAMAGE && // Only for direct damage + roll_chance_f(pVictim->GetUnitParryChance())) // Roll chance + RemainingDamage -= RemainingDamage * currentAbsorb / 100; + continue; + } + // Reflective Shield (Lady Malande boss) + if (spellProto->Id == 41475) + { + if(RemainingDamage < currentAbsorb) + reflectDamage = RemainingDamage / 2; + else + reflectDamage = currentAbsorb / 2; + reflectSpell = 33619; + break; + } + if (spellProto->Id == 39228 || // Argussian Compass + spellProto->Id == 60218) // Essence of Gossamer + { + // Max absorb stored in 1 dummy effect + if (spellProto->EffectBasePoints[1] < currentAbsorb) + currentAbsorb = spellProto->EffectBasePoints[1]; + break; + } + break; } - continue; - } - - int32 currentAbsorb; - - //Reflective Shield - if ((pVictim != this) && (*i)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_PRIEST && (*i)->GetSpellProto()->SpellFamilyFlags == 0x1) - { - if(Unit* caster = (*i)->GetCaster()) + case SPELLFAMILY_DRUID: { - AuraList const& vOverRideCS = caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); - for(AuraList::const_iterator k = vOverRideCS.begin(); k != vOverRideCS.end(); ++k) + // Primal Tenacity + if (spellProto->SpellIconID == 2253) { - switch((*k)->GetModifier()->m_miscvalue) + //reduces all damage taken while Stunned + if (unitflag & UNIT_FLAG_STUNNED) + RemainingDamage -= RemainingDamage * currentAbsorb / 100; + continue; + } + break; + } + case SPELLFAMILY_ROGUE: + { + // Cheat Death + if(spellProto->SpellIconID == 2109) + { + if (pVictim->GetTypeId()==TYPEID_PLAYER && // Only players + pVictim->GetHealth() <= RemainingDamage && // Only if damage kill + !((Player*)pVictim)->HasSpellCooldown(31231) && // Only if no cooldown + roll_chance_i(currentAbsorb)) // Only if roll { - case 5065: // Rank 1 - case 5064: // Rank 2 - case 5063: // Rank 3 - case 5062: // Rank 4 - case 5061: // Rank 5 - { - if(RemainingDamage >= (*i)->GetModifier()->m_amount) - reflectDamage = (*i)->GetModifier()->m_amount * (*k)->GetModifier()->m_amount/100; - else - reflectDamage = (*k)->GetModifier()->m_amount * RemainingDamage/100; - reflectAura = *i; - - } break; - default: break; + pVictim->CastSpell(pVictim,31231,true); + ((Player*)pVictim)->AddSpellCooldown(31231,0,time(NULL)+60); + // with health > 10% lost health until health==10%, in other case no losses + uint32 health10 = pVictim->GetMaxHealth()/10; + RemainingDamage = pVictim->GetHealth() > health10 ? pVictim->GetHealth() - health10 : 0; + } + continue; + } + break; + } + case SPELLFAMILY_PRIEST: + { + // Guardian Spirit + if (spellProto->Id==47788) + { + if (pVictim->GetHealth() <= RemainingDamage) // Killing Blow + { + healAmount = pVictim->GetMaxHealth()/2; + healCaster = pVictim; + healSpell = 48153; + (*i)->SetAmount(0); + RemainingDamage=0; } + continue; + } - if(reflectDamage) + // Reflective Shield + if (spellProto->SpellFamilyFlags.IsEqual(0x1, 0, 0x400)) + { + if (pVictim == this) + break; + Unit* caster = (*i)->GetCaster(); + if (!caster) break; + AuraEffectList const& vOverRideCS = caster->GetAurasByType(SPELL_AURA_DUMMY); + for(AuraEffectList::const_iterator k = vOverRideCS.begin(); k != vOverRideCS.end(); ++k) + { + if((*k)->GetSpellProto()->SpellFamilyName != SPELLFAMILY_PRIEST) + continue; + switch((*k)->GetMiscValue()) + { + case 5065: // Rank 1 + case 5064: // Rank 2 + case 5063: // Rank 3 + { + if(RemainingDamage >= currentAbsorb) + reflectDamage = (*k)->GetAmount() * currentAbsorb/100; + else + reflectDamage = (*k)->GetAmount() * RemainingDamage/100; + reflectSpell = 33619; + } break; + } + } } + // no break here, rapture can proc with reflective shield + // Rapture + if (spellProto->SpellFamilyFlags.HasFlag(0x1, 0x1000000)) + { + if (pVictim == this) + break; + healCaster = (*i)->GetCaster(); + if (!healCaster) + break; + + AuraEffectList const& lOverRideCS = pVictim->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); + for(AuraEffectList::const_iterator k = lOverRideCS.begin(); k != lOverRideCS.end(); ++k) + { + switch((*k)->GetMiscValue()) + { + case 7556: // Rank 1 + case 7555: // Rank 2 + case 7554: // Rank 3 + case 7553: // Rank 4 + case 7552: // Rank 5 + { + healSpell = 47755; + healAmount += ((pVictim->getLevel() * (-0.2) + 18) / 1000000) * currentAbsorb * pVictim->GetMaxPower(POWER_MANA); + // limitation based on aura amount + if(healAmount > pVictim->GetMaxPower(POWER_MANA) * (*k)->GetAmount() / 1000) + healAmount = pVictim->GetMaxPower(POWER_MANA) * (*k)->GetAmount() / 1000; + } break; + } + } + } + break; } + case SPELLFAMILY_SHAMAN: + { + // Astral Shift + if (spellProto->SpellIconID == 3066) + { + //reduces all damage taken while stun, fear or silence + if (unitflag & (UNIT_FLAG_STUNNED|UNIT_FLAG_FLEEING|UNIT_FLAG_SILENCED)) + RemainingDamage -= RemainingDamage * currentAbsorb / 100; + continue; + } + break; + } + case SPELLFAMILY_DEATHKNIGHT: + { + // Shadow of Death + if (spellProto->SpellIconID == 1958) + { + // TODO: absorb only while transform + continue; + } + // Anti-Magic Shell (on self) + if (spellProto->Id == 48707) + { + // damage absorbed by Anti-Magic Shell energizes the DK with additional runic power. + // This, if I'm not mistaken, shows that we get back ~2% of the absorbed damage as runic power. + int32 absorbed = RemainingDamage * currentAbsorb / 100; + int32 regen = absorbed * 2 / 10; + pVictim->CastCustomSpell(pVictim, 49088, ®en, 0, 0, true, 0, (*i)); + RemainingDamage -= absorbed; + continue; + } + // Anti-Magic Shell (on single party/raid member) + if (spellProto->Id == 50462) + { + RemainingDamage -= RemainingDamage * currentAbsorb / 100; + continue; + } + // Anti-Magic Zone + if (spellProto->Id == 50461) + { + Unit* caster = (*i)->GetCaster(); + if (!caster) + continue; + int32 absorbed = RemainingDamage * currentAbsorb / 100; + int32 canabsorb = caster->GetHealth(); + if (canabsorb < absorbed) + absorbed = canabsorb; + DealDamage(caster, absorbed, NULL, damagetype, schoolMask, 0, false); + RemainingDamage -= absorbed; + continue; + } + break; + } + default: + break; } - if (RemainingDamage >= (*i)->GetModifier()->m_amount) - { - currentAbsorb = (*i)->GetModifier()->m_amount; - pVictim->RemoveAurasDueToSpell((*i)->GetId()); - next = vSchoolAbsorb.begin(); - } - else - { + // currentAbsorb - damage can be absorbed by shield + // If need absorb less damage + if (RemainingDamage < currentAbsorb) currentAbsorb = RemainingDamage; - (*i)->GetModifier()->m_amount -= RemainingDamage; - } RemainingDamage -= currentAbsorb; + + // Reduce shield amount + (*i)->SetAmount((*i)->GetAmount() -currentAbsorb); + // Need remove it later + if ((*i)->GetAmount()<=0) + existExpired = true; + } + if(healSpell && healCaster) + healCaster->CastCustomSpell(healCaster, healSpell, &healAmount, NULL, NULL, true); + + // Remove all expired absorb auras + if (existExpired) + { + for(AuraEffectList::const_iterator i = vSchoolAbsorb.begin(); i != vSchoolAbsorb.end();) + { + Aura *aura=(*i)->GetParentAura(); + AuraEffect *auraeff =(*i); + ++i; + if (auraeff->GetAmount()<=0) + { + uint32 removedAuras = pVictim->m_removedAuras.size(); + pVictim->RemoveAura(aura, AURA_REMOVE_BY_ENEMY_SPELL); + if (removedAuras+1<pVictim->m_removedAuras.size()) + i=vSchoolAbsorb.begin(); + } + } } - // do not cast spells while looping auras; auras can get invalid otherwise - if (reflectDamage) - pVictim->CastCustomSpell(this, 33619, &reflectDamage, NULL, NULL, true, NULL, reflectAura); + // Cast back reflect damage spell + if (reflectSpell) + pVictim->CastCustomSpell(this, reflectSpell, &reflectDamage, NULL, NULL, true); // absorb by mana cost - AuraList const& vManaShield = pVictim->GetAurasByType(SPELL_AURA_MANA_SHIELD); - for(AuraList::const_iterator i = vManaShield.begin(), next; i != vManaShield.end() && RemainingDamage > 0; i = next) + AuraEffectList const& vManaShield = pVictim->GetAurasByType(SPELL_AURA_MANA_SHIELD); + for(AuraEffectList::const_iterator i = vManaShield.begin(), next; i != vManaShield.end() && RemainingDamage > 0; i = next) { next = i; ++next; // check damage school mask - if(((*i)->GetModifier()->m_miscvalue & schoolMask)==0) + if(((*i)->GetMiscValue() & schoolMask)==0) continue; int32 currentAbsorb; - if (RemainingDamage >= (*i)->GetModifier()->m_amount) - currentAbsorb = (*i)->GetModifier()->m_amount; + if (RemainingDamage >= (*i)->GetAmount()) + currentAbsorb = (*i)->GetAmount(); else currentAbsorb = RemainingDamage; float manaMultiplier = (*i)->GetSpellProto()->EffectMultipleValue[(*i)->GetEffIndex()]; - if(Player *modOwner = GetSpellModOwner()) + if(Player *modOwner = pVictim->GetSpellModOwner()) modOwner->ApplySpellMod((*i)->GetId(), SPELLMOD_MULTIPLE_VALUE, manaMultiplier); int32 maxAbsorb = int32(pVictim->GetPower(POWER_MANA) / manaMultiplier); if (currentAbsorb > maxAbsorb) currentAbsorb = maxAbsorb; - (*i)->GetModifier()->m_amount -= currentAbsorb; - if((*i)->GetModifier()->m_amount <= 0) + (*i)->SetAmount((*i)->GetAmount()-currentAbsorb); + if((*i)->GetAmount() <= 0) { - pVictim->RemoveAurasDueToSpell((*i)->GetId()); + pVictim->RemoveAura((*i)->GetParentAura(), AURA_REMOVE_BY_ENEMY_SPELL); next = vManaShield.begin(); } @@ -1835,13 +2111,13 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe // only split damage if not damaging yourself if(pVictim != this) { - AuraList const& vSplitDamageFlat = pVictim->GetAurasByType(SPELL_AURA_SPLIT_DAMAGE_FLAT); - for(AuraList::const_iterator i = vSplitDamageFlat.begin(), next; i != vSplitDamageFlat.end() && RemainingDamage >= 0; i = next) + AuraEffectList const& vSplitDamageFlat = pVictim->GetAurasByType(SPELL_AURA_SPLIT_DAMAGE_FLAT); + for(AuraEffectList::const_iterator i = vSplitDamageFlat.begin(), next; i != vSplitDamageFlat.end() && RemainingDamage >= 0; i = next) { next = i; ++next; // check damage school mask - if(((*i)->GetModifier()->m_miscvalue & schoolMask)==0) + if(((*i)->GetMiscValue() & schoolMask)==0) continue; // Damage can be splitted only if aura has an alive caster @@ -1850,8 +2126,8 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe continue; int32 currentAbsorb; - if (RemainingDamage >= (*i)->GetModifier()->m_amount) - currentAbsorb = (*i)->GetModifier()->m_amount; + if (RemainingDamage >= (*i)->GetAmount()) + currentAbsorb = (*i)->GetAmount(); else currentAbsorb = RemainingDamage; @@ -1863,13 +2139,13 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe DealDamage(caster, currentAbsorb, &cleanDamage, DIRECT_DAMAGE, schoolMask, (*i)->GetSpellProto(), false); } - AuraList const& vSplitDamagePct = pVictim->GetAurasByType(SPELL_AURA_SPLIT_DAMAGE_PCT); - for(AuraList::const_iterator i = vSplitDamagePct.begin(), next; i != vSplitDamagePct.end() && RemainingDamage >= 0; i = next) + AuraEffectList const& vSplitDamagePct = pVictim->GetAurasByType(SPELL_AURA_SPLIT_DAMAGE_PCT); + for(AuraEffectList::const_iterator i = vSplitDamagePct.begin(), next; i != vSplitDamagePct.end() && RemainingDamage >= 0; i = next) { next = i; ++next; // check damage school mask - if(((*i)->GetModifier()->m_miscvalue & schoolMask)==0) + if(((*i)->GetMiscValue() & schoolMask)==0) continue; // Damage can be splitted only if aura has an alive caster @@ -1877,7 +2153,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe if(!caster || caster == pVictim || !caster->IsInWorld() || !caster->isAlive()) continue; - int32 splitted = int32(RemainingDamage * (*i)->GetModifier()->m_amount / 100.0f); + int32 splitted = int32(RemainingDamage * (*i)->GetAmount() / 100.0f); RemainingDamage -= splitted; @@ -1888,409 +2164,53 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe } } - *absorb = damage - RemainingDamage - *resist; -} - -/* -void Unit::DoAttackDamage (Unit *pVictim, uint32 *damage, CleanDamage *cleanDamage, uint32 *blocked_amount, SpellSchoolMask damageSchoolMask, uint32 *hitInfo, VictimState *victimState, uint32 *absorbDamage, uint32 *resistDamage, WeaponAttackType attType, SpellEntry const *spellCasted, bool isTriggeredSpell) -{ - MeleeHitOutcome outcome; - - // If is casted Melee spell, calculate like physical - if(!spellCasted) - outcome = RollMeleeOutcomeAgainst (pVictim, attType); - else - outcome = RollPhysicalOutcomeAgainst (pVictim, attType, spellCasted); - - if(outcome == MELEE_HIT_MISS ||outcome == MELEE_HIT_DODGE ||outcome == MELEE_HIT_BLOCK ||outcome == MELEE_HIT_PARRY) - pVictim->AddThreat(this, 0.0f); - switch(outcome) - { - case MELEE_HIT_EVADE: - { - *hitInfo |= HITINFO_MISS; - *damage = 0; - cleanDamage->damage = 0; - return; - } - case MELEE_HIT_MISS: - { - *hitInfo |= HITINFO_MISS; - *damage = 0; - cleanDamage->damage = 0; - if(GetTypeId()== TYPEID_PLAYER) - ((Player*)this)->UpdateWeaponSkill(attType); - return; - } - } - - /// If this is a creature and it attacks from behind it has a probability to daze it's victim - if( (outcome==MELEE_HIT_CRIT || outcome==MELEE_HIT_CRUSHING || outcome==MELEE_HIT_NORMAL || outcome==MELEE_HIT_GLANCING) && - GetTypeId() != TYPEID_PLAYER && !((Creature*)this)->GetCharmerOrOwnerGUID() && !pVictim->HasInArc(M_PI, this) - && pVictim->GetTypeId() == TYPEID_PLAYER) - { - // -probability is between 0% and 40% - // 20% base chance - float Probability = 20; - - //there is a newbie protection, at level 10 just 7% base chance; assuming linear function - if( pVictim->getLevel() < 30 ) - Probability = 0.65f*pVictim->getLevel()+0.5; - - uint32 VictimDefense=pVictim->GetDefenseSkillValue(this); - uint32 AttackerMeleeSkill=GetUnitMeleeSkill(pVictim); - - Probability *= AttackerMeleeSkill/(float)VictimDefense; - - if(Probability > 40.0f) - Probability = 40.0f; - - if(roll_chance_f(Probability)) - CastSpell(pVictim, 1604, true); - } - - //Calculate the damage after armor mitigation if SPELL_SCHOOL_NORMAL - if (damageSchoolMask & SPELL_SCHOOL_MASK_NORMAL) + TotalAbsorb = (TotalAbsorb - RemainingDamage > 0) ? TotalAbsorb - RemainingDamage : 0; + // TODO: School should be checked for absorbing auras or for attacks? + int32 auraAbsorbMod = GetMaxPositiveAuraModifier(SPELL_AURA_MOD_TARGET_ABSORB_SCHOOL); + AuraEffectList const& AbsIgnoreAurasAb = GetAurasByType(SPELL_AURA_MOD_TARGET_ABILITY_ABSORB_SCHOOL); + for(AuraEffectList::const_iterator i = AbsIgnoreAurasAb.begin();i != AbsIgnoreAurasAb.end(); ++i) { - uint32 damageAfterArmor = CalcArmorReducedDamage(pVictim, *damage); - - // random durability for main hand weapon (ABSORB) - if(damageAfterArmor < *damage) - if(pVictim->GetTypeId() == TYPEID_PLAYER) - if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_ABSORB))) - ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EquipmentSlots(urand(EQUIPMENT_SLOT_START,EQUIPMENT_SLOT_BACK))); - - cleanDamage->damage += *damage - damageAfterArmor; - *damage = damageAfterArmor; + if ((*i)->GetAmount() > auraAbsorbMod + && (*i)->isAffectedOnSpell(spellInfo)) + auraAbsorbMod = (*i)->GetAmount(); } - if(GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() != TYPEID_PLAYER && pVictim->GetCreatureType() != CREATURE_TYPE_CRITTER ) - ((Player*)this)->UpdateCombatSkills(pVictim, attType, outcome, false); + // Ignore absorb - add reduced amount again to damage + RemainingDamage += auraAbsorbMod * TotalAbsorb / 100; - if(GetTypeId() != TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER) - ((Player*)pVictim)->UpdateCombatSkills(this, attType, outcome, true); + *absorb = damage - RemainingDamage - *resist; - switch (outcome) + if (*absorb) { - case MELEE_HIT_BLOCK_CRIT: - case MELEE_HIT_CRIT: + bool found = false; + int32 spell_dmg=0; + // Incanter's Absorption + AuraEffectList const& DummmyAuras = pVictim->GetAurasByType(SPELL_AURA_DUMMY); + for(AuraEffectList::const_iterator i = DummmyAuras.begin(); i != DummmyAuras.end(); ++i) { - //*hitInfo = 0xEA; - // 0xEA - *hitInfo = HITINFO_CRITICALHIT | HITINFO_NORMALSWING2 | 0x8; - - // Crit bonus calc - uint32 crit_bonus; - crit_bonus = *damage; - - // Apply crit_damage bonus for melee spells - if (spellCasted) + SpellEntry const* spellInfo = (*i)->GetSpellProto(); + if (spellmgr.GetFirstSpellInChain(spellInfo->Id) == 44394) { - if(Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellCasted->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus); - - uint32 creatureTypeMask = pVictim->GetCreatureTypeMask(); - AuraList const& mDamageDoneVersus = GetAurasByType(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS); - for(AuraList::const_iterator i = mDamageDoneVersus.begin();i != mDamageDoneVersus.end(); ++i) - if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue)) - crit_bonus = uint32(crit_bonus * ((*i)->GetModifierValue()+100.0f)/100.0f); - } - - *damage += crit_bonus; - - uint32 resilienceReduction = 0; - - if(attType == RANGED_ATTACK) - { - int32 mod = pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE); - *damage = int32((*damage) * float((100.0f + mod)/100.0f)); - // Resilience - reduce crit damage - if (pVictim->GetTypeId()==TYPEID_PLAYER) - resilienceReduction = ((Player*)pVictim)->GetRangedCritDamageReduction(*damage); - } - else - { - int32 mod = pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE); - mod += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_DAMAGE_BONUS_MELEE); - *damage = int32((*damage) * float((100.0f + mod)/100.0f)); - // Resilience - reduce crit damage - if (pVictim->GetTypeId()==TYPEID_PLAYER) - resilienceReduction = ((Player*)pVictim)->GetMeleeCritDamageReduction(*damage); - } - - *damage -= resilienceReduction; - cleanDamage->damage += resilienceReduction; - - if(GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() != TYPEID_PLAYER && pVictim->GetCreatureType() != CREATURE_TYPE_CRITTER ) - ((Player*)this)->UpdateWeaponSkill(attType); - - ModifyAuraState(AURA_STATE_CRIT, true); - StartReactiveTimer( REACTIVE_CRIT ); - - if(getClass()==CLASS_HUNTER) - { - ModifyAuraState(AURA_STATE_HUNTER_CRIT_STRIKE, true); - StartReactiveTimer( REACTIVE_HUNTER_CRIT ); - } - - if ( outcome == MELEE_HIT_BLOCK_CRIT ) - { - *blocked_amount = pVictim->GetShieldBlockValue(); - - if (pVictim->GetUnitBlockChance()) - pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYSHIELD); - else - pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED); - - //Only set VICTIMSTATE_BLOCK on a full block - if (*blocked_amount >= uint32(*damage)) - { - *victimState = VICTIMSTATE_BLOCKS; - *blocked_amount = uint32(*damage); - } - - if(pVictim->GetTypeId() == TYPEID_PLAYER) - { - // Update defense - ((Player*)pVictim)->UpdateDefense(); - - // random durability for main hand weapon (BLOCK) - if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_BLOCK))) - ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_OFFHAND); - } - - pVictim->ModifyAuraState(AURA_STATE_DEFENSE,true); - pVictim->StartReactiveTimer( REACTIVE_DEFENSE ); - break; - } - - pVictim->HandleEmoteCommand(EMOTE_ONESHOT_WOUNDCRITICAL); - break; - } - case MELEE_HIT_PARRY: - { - if(attType == RANGED_ATTACK) //range attack - no parry - { - outcome = MELEE_HIT_NORMAL; - break; - } - - cleanDamage->damage += *damage; - *damage = 0; - *victimState = VICTIMSTATE_PARRY; - - // instant (maybe with small delay) counter attack - { - float offtime = float(pVictim->getAttackTimer(OFF_ATTACK)); - float basetime = float(pVictim->getAttackTimer(BASE_ATTACK)); - - // after parry nearest next attack time will reduced at %40 from full attack time. - // The delay cannot be reduced to less than 20% of your weapon base swing delay. - if (pVictim->haveOffhandWeapon() && offtime < basetime) - { - float percent20 = pVictim->GetAttackTime(OFF_ATTACK)*0.20; - float percent60 = 3*percent20; - // set to 20% if in range 20%...20+40% of full time - if(offtime > percent20 && offtime <= percent60) - { - pVictim->setAttackTimer(OFF_ATTACK,uint32(percent20)); - } - // decrease at %40 from full time - else if(offtime > percent60) - { - offtime -= 2*percent20; - pVictim->setAttackTimer(OFF_ATTACK,uint32(offtime)); - } - // ELSE not changed - } - else + int32 total_dmg=0; + // Get total bonus from auras + AuraEffectList const& BonusAuras = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE); + for(AuraEffectList::const_iterator itr = BonusAuras.begin(); itr != BonusAuras.end(); ++itr) { - float percent20 = pVictim->GetAttackTime(BASE_ATTACK)*0.20; - float percent60 = 3*percent20; - // set to 20% if in range 20%...20+40% of full time - if(basetime > percent20 && basetime <= percent60) - { - pVictim->setAttackTimer(BASE_ATTACK,uint32(percent20)); - } - // decrease at %40 from full time - else if(basetime > percent60) - { - basetime -= 2*percent20; - pVictim->setAttackTimer(BASE_ATTACK,uint32(basetime)); - } - // ELSE not changed + if ((*itr)->GetId()==44413) + total_dmg += (*itr)->GetMiscValue(); } - } - - if(pVictim->GetTypeId() == TYPEID_PLAYER) - { - // Update victim defense ? - ((Player*)pVictim)->UpdateDefense(); - - // random durability for main hand weapon (PARRY) - if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_PARRY))) - ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_MAINHAND); - } - - pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED); - - if (pVictim->getClass() == CLASS_HUNTER) - { - pVictim->ModifyAuraState(AURA_STATE_HUNTER_PARRY,true); - pVictim->StartReactiveTimer( REACTIVE_HUNTER_PARRY ); - } - else - { - pVictim->ModifyAuraState(AURA_STATE_DEFENSE, true); - pVictim->StartReactiveTimer( REACTIVE_DEFENSE ); - } - - CastMeleeProcDamageAndSpell(pVictim, 0, damageSchoolMask, attType, outcome, spellCasted, isTriggeredSpell); - return; - } - case MELEE_HIT_DODGE: - { - if(attType == RANGED_ATTACK) //range attack - no dodge - { - outcome = MELEE_HIT_NORMAL; + spell_dmg = int32(*absorb * (*i)->GetAmount() / 100); + // Do not apply more auras if more than 5% hp + if(total_dmg+spell_dmg > int32(GetMaxHealth() * 5 / 100)) + break; + found=true; break; - } - - cleanDamage->damage += *damage; - *damage = 0; - *victimState = VICTIMSTATE_DODGE; - - if(pVictim->GetTypeId() == TYPEID_PLAYER) - ((Player*)pVictim)->UpdateDefense(); - - pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED); - - if (pVictim->getClass() != CLASS_ROGUE) // Riposte - { - pVictim->ModifyAuraState(AURA_STATE_DEFENSE, true); - pVictim->StartReactiveTimer( REACTIVE_DEFENSE ); - } - - // Overpower - if (GetTypeId() == TYPEID_PLAYER && getClass() == CLASS_WARRIOR) - { - ((Player*)this)->AddComboPoints(pVictim, 1); - StartReactiveTimer( REACTIVE_OVERPOWER ); - } - - CastMeleeProcDamageAndSpell(pVictim, 0, damageSchoolMask, attType, outcome, spellCasted, isTriggeredSpell); - return; - } - case MELEE_HIT_BLOCK: - { - *blocked_amount = pVictim->GetShieldBlockValue(); - - if (pVictim->GetUnitBlockChance()) - pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYSHIELD); - else - pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED); - - //Only set VICTIMSTATE_BLOCK on a full block - if (*blocked_amount >= uint32(*damage)) - { - *victimState = VICTIMSTATE_BLOCKS; - *blocked_amount = uint32(*damage); - } - - if(pVictim->GetTypeId() == TYPEID_PLAYER) - { - // Update defense - ((Player*)pVictim)->UpdateDefense(); - - // random durability for main hand weapon (BLOCK) - if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_BLOCK))) - ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_OFFHAND); - } - - pVictim->ModifyAuraState(AURA_STATE_DEFENSE,true); - pVictim->StartReactiveTimer( REACTIVE_DEFENSE ); - - break; - } - case MELEE_HIT_GLANCING: - { - int32 leveldif = int32(pVictim->getLevel()) - int32(getLevel()); - if (leveldif > 3) leveldif = 3; - *damage *= (1 - leveldif * 0.1f); - cleanDamage->damage = *damage; - *hitInfo |= HITINFO_GLANCING; - break; - } - case MELEE_HIT_CRUSHING: - { - // 150% normal damage - *damage += (*damage / 2); - cleanDamage->damage = *damage; - *hitInfo |= HITINFO_CRUSHING; - // TODO: victimState, victim animation? - break; - } - default: - break; - } - - // apply melee damage bonus and absorb only if base damage not fully blocked to prevent negative damage or damage with full block - if(*victimState != VICTIMSTATE_BLOCKS) - { - MeleeDamageBonus(pVictim, damage,attType,spellCasted); - CalcAbsorbResist(pVictim, damageSchoolMask, DIRECT_DAMAGE, *damage-*blocked_amount, absorbDamage, resistDamage); - } - - if (*absorbDamage) *hitInfo |= HITINFO_ABSORB; - if (*resistDamage) *hitInfo |= HITINFO_RESIST; - - cleanDamage->damage += *blocked_amount; - - if (*damage <= *absorbDamage + *resistDamage + *blocked_amount) - { - //*hitInfo = 0x00010020; - //*hitInfo |= HITINFO_SWINGNOHITSOUND; - //*damageType = 0; - CastMeleeProcDamageAndSpell(pVictim, 0, damageSchoolMask, attType, outcome, spellCasted, isTriggeredSpell); - return; - } - - // update at damage Judgement aura duration that applied by attacker at victim - if(*damage) - { - AuraMap const& vAuras = pVictim->GetAuras(); - for(AuraMap::const_iterator itr = vAuras.begin(); itr != vAuras.end(); ++itr) - { - SpellEntry const *spellInfo = (*itr).second->GetSpellProto(); - if( (spellInfo->AttributesEx3 & 0x40000) && spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN && - ((*itr).second->GetCasterGUID() == GetGUID() && (!spellCasted || spellCasted->Id == 35395)) ) - { - (*itr).second->SetAuraDuration((*itr).second->GetAuraMaxDuration()); - (*itr).second->UpdateAuraDuration(); - } - } - } - - CastMeleeProcDamageAndSpell(pVictim, (*damage - *absorbDamage pVictim->SpellNonMeleeDamageLog(this, (*i)->GetId(), (*i)->GetModifier()->m_amount, false, false);eld - // yet another hack to fix crashes related to the aura getting removed during iteration - std::set<Aura*> alreadyDone; - uint32 removedAuras = pVictim->m_removedAuras; - AuraList const& vDamageShields = pVictim->GetAurasByType(SPELL_AURA_DAMAGE_SHIELD); - for(AuraList::const_iterator i = vDamageShields.begin(), next = vDamageShields.begin(); i != vDamageShields.end(); i = next) - { - ++next; - if (alreadyDone.find(*i) == alreadyDone.end()) - { - alreadyDone.insert(*i); - pVictim->SpellNonMeleeDamageLog(this, (*i)->GetId(), (*i)->GetModifierValue(), false, false); - if (pVictim->m_removedAuras > removedAuras) - { - removedAuras = pVictim->m_removedAuras; - next = vDamageShields.begin(); - } + } } + if (found) + pVictim->CastCustomSpell(pVictim, 44413, &spell_dmg, NULL, NULL, false); } -}*/ +} void Unit::AttackerStateUpdate (Unit *pVictim, WeaponAttackType attType, bool extra ) { @@ -2329,6 +2249,10 @@ void Unit::AttackerStateUpdate (Unit *pVictim, WeaponAttackType attType, bool ex } } + // if damage pVictim call AI reaction + //if(pVictim->GetTypeId()==TYPEID_UNIT && ((Creature*)pVictim)->AI()) + // ((Creature*)pVictim)->AI()->AttackedBy(this); + return; } @@ -2356,68 +2280,11 @@ void Unit::AttackerStateUpdate (Unit *pVictim, WeaponAttackType attType, bool ex --m_extraAttacks; } } -} -/* -MeleeHitOutcome Unit::RollPhysicalOutcomeAgainst (Unit const *pVictim, WeaponAttackType attType, SpellEntry const *spellInfo) -{ - // Miss chance based on melee - float miss_chance = MeleeMissChanceCalc(pVictim, attType); - - // Critical hit chance - float crit_chance = GetUnitCriticalChance(attType, pVictim); - // this is to avoid compiler issue when declaring variables inside if - float block_chance, parry_chance, dodge_chance; - - // cannot be dodged/parried/blocked - if(spellInfo->Attributes & SPELL_ATTR_IMPOSSIBLE_DODGE_PARRY_BLOCK) - { - block_chance = 0.0f; - parry_chance = 0.0f; - dodge_chance = 0.0f; - } - else - { - // parry can be avoided only by some abilities - parry_chance = pVictim->GetUnitParryChance(); - // block might be bypassed by it as well - block_chance = pVictim->GetUnitBlockChance(); - // stunned target cannot dodge and this is check in GetUnitDodgeChance() - dodge_chance = pVictim->GetUnitDodgeChance(); - } - - // Only players can have Talent&Spell bonuses - if (GetTypeId() == TYPEID_PLAYER) - { - // Increase from SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL aura - crit_chance += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, spellInfo->SchoolMask); - - if( dodge_chance != 0.0f ) // if dodge chance is already 0, ignore talents for speed - { - AuraList const& mCanNotBeDodge = GetAurasByType(SPELL_AURA_IGNORE_COMBAT_RESULT); - for(AuraList::const_iterator i = mCanNotBeDodge.begin(); i != mCanNotBeDodge.end(); ++i) - { - // can't be dodged rogue finishing move - if((*i)->GetModifier()->m_miscvalue == VICTIMSTATE_DODGE) - { - if(spellInfo->SpellFamilyName==SPELLFAMILY_ROGUE && (spellInfo->SpellFamilyFlags & SPELLFAMILYFLAG_ROGUE__FINISHING_MOVE)) - { - dodge_chance = 0.0f; - break; - } - } - } - } - } - - // Spellmods - if(Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CRITICAL_CHANCE, crit_chance); - - DEBUG_LOG("PHYSICAL OUTCOME: miss %f crit %f dodge %f parry %f block %f",miss_chance,crit_chance,dodge_chance,parry_chance, block_chance); - - return RollMeleeOutcomeAgainst(pVictim, attType, int32(crit_chance*100), int32(miss_chance*100),int32(dodge_chance*100),int32(parry_chance*100),int32(block_chance*100), true); -}*/ + // if damage pVictim call AI reaction + //if(pVictim->GetTypeId()==TYPEID_UNIT && ((Creature*)pVictim)->AI()) + // ((Creature*)pVictim)->AI()->AttackedBy(this); +} MeleeHitOutcome Unit::RollMeleeOutcomeAgainst(const Unit *pVictim, WeaponAttackType attType) const { @@ -2438,10 +2305,10 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst(const Unit *pVictim, WeaponAttackT // Useful if want to specify crit & miss chances for melee, else it could be removed DEBUG_LOG("MELEE OUTCOME: miss %f crit %f dodge %f parry %f block %f", miss_chance,crit_chance,dodge_chance,parry_chance,block_chance); - return RollMeleeOutcomeAgainst(pVictim, attType, int32(crit_chance*100), int32(miss_chance*100), int32(dodge_chance*100),int32(parry_chance*100),int32(block_chance*100), false); + return RollMeleeOutcomeAgainst(pVictim, attType, int32(crit_chance*100), int32(miss_chance*100), int32(dodge_chance*100),int32(parry_chance*100),int32(block_chance*100)); } -MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttackType attType, int32 crit_chance, int32 miss_chance, int32 dodge_chance, int32 parry_chance, int32 block_chance, bool SpellCasted ) const +MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttackType attType, int32 crit_chance, int32 miss_chance, int32 dodge_chance, int32 parry_chance, int32 block_chance) const { if(pVictim->GetTypeId()==TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode()) return MELEE_HIT_EVADE; @@ -2454,7 +2321,6 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttack // bonus from skills is 0.04% int32 skillBonus = 4 * ( attackerWeaponSkill - victimMaxSkillValueForLevel ); - int32 skillBonus2 = 4 * ( attackerMaxSkillValueForLevel - victimDefenseSkill ); int32 sum = 0, tmp = 0; int32 roll = urand (0, 10000); @@ -2492,6 +2358,7 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttack // Modify dodge chance by attacker SPELL_AURA_MOD_COMBAT_RESULT_CHANCE dodge_chance+= GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_COMBAT_RESULT_CHANCE, VICTIMSTATE_DODGE); + dodge_chance = int32 (float (dodge_chance) * GetTotalAuraMultiplier(SPELL_AURA_MOD_ENEMY_DODGE)); tmp = dodge_chance; if ( (tmp > 0) // check if unit _can_ dodge @@ -2535,16 +2402,6 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttack && ((tmp -= skillBonus) > 0) && (roll < (sum += tmp))) { - // Critical chance - tmp = crit_chance + skillBonus2; - if ( GetTypeId() == TYPEID_PLAYER && SpellCasted && tmp > 0 ) - { - if ( roll_chance_i(tmp/100)) - { - DEBUG_LOG ("RollMeleeOutcomeAgainst: BLOCKED CRIT"); - return MELEE_HIT_BLOCK_CRIT; - } - } DEBUG_LOG ("RollMeleeOutcomeAgainst: BLOCK <%d, %d)", sum-tmp, sum); return MELEE_HIT_BLOCK; } @@ -2552,7 +2409,7 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttack } // Critical chance - tmp = crit_chance + skillBonus2; + tmp = crit_chance; if (tmp > 0 && roll < (sum += tmp)) { @@ -2564,7 +2421,7 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttack } // Max 40% chance to score a glancing blow against mobs that are higher level (can do only players and pets and not with ranged weapon) - if( attType != RANGED_ATTACK && !SpellCasted && + if( attType != RANGED_ATTACK && (GetTypeId() == TYPEID_PLAYER || ((Creature*)this)->isPet()) && pVictim->GetTypeId() != TYPEID_PLAYER && !((Creature*)pVictim)->isPet() && getLevel() < pVictim->getLevelForTarget(this) ) @@ -2583,10 +2440,14 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttack } } - if(GetTypeId()!=TYPEID_PLAYER && !(((Creature*)this)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_CRUSH) && !((Creature*)this)->isPet() && !SpellCasted /*Only autoattack can be crushing blow*/ ) + // mobs can score crushing blows if they're 4 or more levels above victim + if (getLevelForTarget(pVictim) >= pVictim->getLevelForTarget(this) + 4 && + // can be from by creature (if can) or from controlled player that considered as creature + (GetTypeId()!=TYPEID_PLAYER && !((Creature*)this)->isPet() && + !(((Creature*)this)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_CRUSH) || + GetTypeId()==TYPEID_PLAYER && GetCharmerOrOwnerGUID())) { - // mobs can score crushing blows if they're 3 or more levels above victim - // or when their weapon skill is 15 or more above victim's defense skill + // when their weapon skill is 15 or more above victim's defense skill tmp = victimDefenseSkill; int32 tmpmax = victimMaxSkillValueForLevel; // having defense above your maximum (from items, talents etc.) has no effect @@ -2696,32 +2557,52 @@ bool Unit::isSpellBlocked(Unit *pVictim, SpellEntry const *spellProto, WeaponAtt { if (pVictim->HasInArc(M_PI,this)) { - float blockChance = GetUnitBlockChance(); - blockChance += (GetWeaponSkillValue(attackType) - pVictim->GetMaxSkillValueForLevel() )*0.04; + /* Currently not exist spells with ignore block + // Ignore combat result aura (parry/dodge check on prepare) + AuraList const& ignore = GetAurasByType(SPELL_AURA_IGNORE_COMBAT_RESULT); + for(AuraList::const_iterator i = ignore.begin(); i != ignore.end(); ++i) + { + if (!(*i)->isAffectedOnSpell(spellProto)) + continue; + if ((*i)->GetMiscValue() == ) + return false; + } + */ + + // Check creatures flags_extra for disable block + if(pVictim->GetTypeId()==TYPEID_UNIT && + ((Creature*)pVictim)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_BLOCK ) + return false; + + float blockChance = pVictim->GetUnitBlockChance(); + blockChance += (int32(GetWeaponSkillValue(attackType)) - int32(pVictim->GetMaxSkillValueForLevel()))*0.04f; if (roll_chance_f(blockChance)) return true; } return false; } +bool Unit::isBlockCritical() +{ + if (roll_chance_i(GetTotalAuraModifier(SPELL_AURA_MOD_BLOCK_CRIT_CHANCE))) + return true; + return false; +} + // Melee based spells can be miss, parry or dodge on this step // Crit or block - determined on damage calculation phase! (and can be both in some time) -float Unit::MeleeSpellMissChance(const Unit *pVictim, WeaponAttackType attType, int32 skillDiff, uint32 spellId) const +/*float Unit::MeleeSpellMissChance(Unit *pVictim, WeaponAttackType attType, int32 skillDiff, SpellEntry const *spell) { // Calculate hit chance (more correct for chance mod) int32 HitChance; // PvP - PvE melee chances - /*int32 lchance = pVictim->GetTypeId() == TYPEID_PLAYER ? 5 : 7; + int32 lchance = pVictim->GetTypeId() == TYPEID_PLAYER ? 5 : 7; int32 leveldif = pVictim->getLevelForTarget(this) - getLevelForTarget(pVictim); if(leveldif < 3) HitChance = 95 - leveldif; else - HitChance = 93 - (leveldif - 2) * lchance;*/ - if (spellId || attType == RANGED_ATTACK || !haveOffhandWeapon()) - HitChance = 95.0f; - else - HitChance = 76.0f; + HitChance = 93 - (leveldif - 2) * lchance; // Hit chance depends from victim auras if(attType == RANGED_ATTACK) @@ -2730,11 +2611,8 @@ float Unit::MeleeSpellMissChance(const Unit *pVictim, WeaponAttackType attType, HitChance += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE); // Spellmod from SPELLMOD_RESIST_MISS_CHANCE - if(spellId) - { - if(Player *modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellId, SPELLMOD_RESIST_MISS_CHANCE, HitChance); - } + if(Player *modOwner = GetSpellModOwner()) + modOwner->ApplySpellMod(spell->Id, SPELLMOD_RESIST_MISS_CHANCE, HitChance); // Miss = 100 - hit float miss_chance= 100.0f - HitChance; @@ -2746,12 +2624,7 @@ float Unit::MeleeSpellMissChance(const Unit *pVictim, WeaponAttackType attType, miss_chance -= m_modMeleeHitChance; // bonus from skills is 0.04% - //miss_chance -= skillDiff * 0.04f; - int32 diff = -skillDiff; - if(pVictim->GetTypeId()==TYPEID_PLAYER) - miss_chance += diff > 0 ? diff * 0.04 : diff * 0.02; - else - miss_chance += diff > 10 ? 2 + (diff - 10) * 0.4 : diff * 0.1; + miss_chance -= skillDiff * 0.04f; // Limit miss chance from 0 to 60% if (miss_chance < 0.0f) @@ -2759,7 +2632,7 @@ float Unit::MeleeSpellMissChance(const Unit *pVictim, WeaponAttackType attType, if (miss_chance > 60.0f) return 60.0f; return miss_chance; -} +}*/ // Melee based spells hit result calculations SpellMissInfo Unit::MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell) @@ -2775,68 +2648,117 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell) int32 fullSkillDiff = attackerWeaponSkill - int32(pVictim->GetDefenseSkillValue(this)); uint32 roll = urand (0, 10000); - uint32 missChance = uint32(MeleeSpellMissChance(pVictim, attType, fullSkillDiff, spell->Id)*100.0f); + uint32 missChance = uint32(MeleeSpellMissChance(pVictim, attType, fullSkillDiff, spell->Id)*100.0f); // Roll miss uint32 tmp = missChance; if (roll < tmp) return SPELL_MISS_MISS; + // Chance resist mechanic (select max value from every mechanic spell effect) + int32 resist_mech = 0; + // Get effects mechanic and chance + for(int eff = 0; eff < 3; ++eff) + { + int32 effect_mech = GetEffectMechanic(spell, eff); + if (effect_mech) + { + int32 temp = pVictim->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_MECHANIC_RESISTANCE, effect_mech); + if (resist_mech < temp*100) + resist_mech = temp*100; + } + } + // Roll chance + tmp += resist_mech; + if (roll < tmp) + return SPELL_MISS_RESIST; + + bool canDodge = true; + bool canParry = true; + // Same spells cannot be parry/dodge if (spell->Attributes & SPELL_ATTR_IMPOSSIBLE_DODGE_PARRY_BLOCK) return SPELL_MISS_NONE; - // Ranged attack can`t miss too + // Ranged attack cannot be parry/dodge only deflect if (attType == RANGED_ATTACK) + { + // only if in front + if (pVictim->HasInArc(M_PI,this)) + { + int32 deflect_chance = pVictim->GetTotalAuraModifier(SPELL_AURA_DEFLECT_SPELLS)*100; + tmp+=deflect_chance; + if (roll < tmp) + return SPELL_MISS_DEFLECT; + } return SPELL_MISS_NONE; + } - bool attackFromBehind = !pVictim->HasInArc(M_PI,this); - - // Roll dodge - int32 dodgeChance = int32(pVictim->GetUnitDodgeChance()*100.0f) - skillDiff * 4; - // Reduce enemy dodge chance by SPELL_AURA_MOD_COMBAT_RESULT_CHANCE - dodgeChance+= GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_COMBAT_RESULT_CHANCE, VICTIMSTATE_DODGE); - - // Reduce dodge chance by attacker expertise rating - if (GetTypeId() == TYPEID_PLAYER) - dodgeChance-=int32(((Player*)this)->GetExpertiseDodgeOrParryReduction(attType) * 100.0f); - if (dodgeChance < 0) - dodgeChance = 0; - - // Can`t dodge from behind in PvP (but its possible in PvE) - if (GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER && attackFromBehind) - dodgeChance = 0; - - // Rogue talent`s cant be dodged - AuraList const& mCanNotBeDodge = GetAurasByType(SPELL_AURA_IGNORE_COMBAT_RESULT); - for(AuraList::const_iterator i = mCanNotBeDodge.begin(); i != mCanNotBeDodge.end(); ++i) + // Check for attack from behind + if (!pVictim->HasInArc(M_PI,this)) + { + // Can`t dodge from behind in PvP (but its possible in PvE) + if (GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER) + canDodge = false; + // Can`t parry + canParry = false; + } + // Check creatures flags_extra for disable parry + if(pVictim->GetTypeId()==TYPEID_UNIT) { - if((*i)->GetModifier()->m_miscvalue == VICTIMSTATE_DODGE) // can't be dodged rogue finishing move + uint32 flagEx = ((Creature*)pVictim)->GetCreatureInfo()->flags_extra; + if( flagEx & CREATURE_FLAG_EXTRA_NO_PARRY ) + canParry = false; + } + // Ignore combat result aura + AuraEffectList const& ignore = GetAurasByType(SPELL_AURA_IGNORE_COMBAT_RESULT); + for(AuraEffectList::const_iterator i = ignore.begin(); i != ignore.end(); ++i) + { + if (!(*i)->isAffectedOnSpell(spell)) + continue; + switch((*i)->GetMiscValue()) { - if(spell->SpellFamilyName==SPELLFAMILY_ROGUE && (spell->SpellFamilyFlags & SPELLFAMILYFLAG_ROGUE__FINISHING_MOVE)) - { - dodgeChance = 0; + case MELEE_HIT_DODGE: canDodge = false; break; + case MELEE_HIT_BLOCK: break; // Block check in hit step + case MELEE_HIT_PARRY: canParry = false; break; + default: + DEBUG_LOG("Spell %u SPELL_AURA_IGNORE_COMBAT_RESULT have unhandled state %d", (*i)->GetId(), (*i)->GetMiscValue()); break; - } } } - tmp += dodgeChance; - if (roll < tmp) - return SPELL_MISS_DODGE; + if (canDodge) + { + // Roll dodge + int32 dodgeChance = int32(pVictim->GetUnitDodgeChance()*100.0f) - skillDiff * 4; + // Reduce enemy dodge chance by SPELL_AURA_MOD_COMBAT_RESULT_CHANCE + dodgeChance+= GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_COMBAT_RESULT_CHANCE, VICTIMSTATE_DODGE)*100; + dodgeChance = int32 (float (dodgeChance) * GetTotalAuraMultiplier(SPELL_AURA_MOD_ENEMY_DODGE)); + // Reduce dodge chance by attacker expertise rating + if (GetTypeId() == TYPEID_PLAYER) + dodgeChance-=int32(((Player*)this)->GetExpertiseDodgeOrParryReduction(attType) * 100.0f); + if (dodgeChance < 0) + dodgeChance = 0; - // Roll parry - int32 parryChance = int32(pVictim->GetUnitParryChance()*100.0f) - skillDiff * 4; - // Reduce parry chance by attacker expertise rating - if (GetTypeId() == TYPEID_PLAYER) - parryChance-=int32(((Player*)this)->GetExpertiseDodgeOrParryReduction(attType) * 100.0f); - // Can`t parry from behind - if (parryChance < 0 || attackFromBehind) - parryChance = 0; + tmp += dodgeChance; + if (roll < tmp) + return SPELL_MISS_DODGE; + } - tmp += parryChance; - if (roll < tmp) - return SPELL_MISS_PARRY; + if (canParry) + { + // Roll parry + int32 parryChance = int32(pVictim->GetUnitParryChance()*100.0f) - skillDiff * 4; + // Reduce parry chance by attacker expertise rating + if (GetTypeId() == TYPEID_PLAYER) + parryChance-=int32(((Player*)this)->GetExpertiseDodgeOrParryReduction(attType) * 100.0f); + if (parryChance < 0) + parryChance = 0; + + tmp += parryChance; + if (roll < tmp) + return SPELL_MISS_PARRY; + } return SPELL_MISS_NONE; } @@ -2901,11 +2823,24 @@ SpellMissInfo Unit::MagicSpellHitResult(Unit *pVictim, SpellEntry const *spell) HitChance -= int32(((Player*)pVictim)->GetRatingBonusValue(CR_HIT_TAKEN_SPELL)*100.0f); if (HitChance < 100) HitChance = 100; - if (HitChance > 9900) HitChance = 9900; + if (HitChance > 10000) HitChance = 10000; + + int32 tmp = 10000 - HitChance; uint32 rand = urand(0,10000); - if (rand > HitChance) + + if (rand < tmp) return SPELL_MISS_RESIST; + + // cast by caster in front of victim + if (pVictim->HasInArc(M_PI,this)) + { + int32 deflect_chance = pVictim->GetTotalAuraModifier(SPELL_AURA_DEFLECT_SPELLS)*100; + tmp+=deflect_chance; + if (rand < tmp) + return SPELL_MISS_DEFLECT; + } + return SPELL_MISS_NONE; } @@ -2927,8 +2862,8 @@ SpellMissInfo Unit::SpellHitResult(Unit *pVictim, SpellEntry const *spell, bool if (spell->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY) return SPELL_MISS_NONE; - // Check for immune (use charges) - if (pVictim->IsImmunedToSpell(spell,true)) + // Check for immune + if (pVictim->IsImmunedToSpell(spell)) return SPELL_MISS_IMMUNE; // All positive spells can`t miss @@ -2937,8 +2872,8 @@ SpellMissInfo Unit::SpellHitResult(Unit *pVictim, SpellEntry const *spell, bool &&(!IsHostileTo(pVictim))) //prevent from affecting enemy by "positive" spell return SPELL_MISS_NONE; - // Check for immune (use charges) - if (pVictim->IsImmunedToDamage(GetSpellSchoolMask(spell),true)) + // Check for immune + if (pVictim->IsImmunedToDamage(GetSpellSchoolMask(spell))) return SPELL_MISS_IMMUNE; if(this == pVictim) @@ -2948,10 +2883,10 @@ SpellMissInfo Unit::SpellHitResult(Unit *pVictim, SpellEntry const *spell, bool if (CanReflect) { int32 reflectchance = pVictim->GetTotalAuraModifier(SPELL_AURA_REFLECT_SPELLS); - Unit::AuraList const& mReflectSpellsSchool = pVictim->GetAurasByType(SPELL_AURA_REFLECT_SPELLS_SCHOOL); - for(Unit::AuraList::const_iterator i = mReflectSpellsSchool.begin(); i != mReflectSpellsSchool.end(); ++i) - if((*i)->GetModifier()->m_miscvalue & GetSpellSchoolMask(spell)) - reflectchance = (*i)->GetModifierValue(); + Unit::AuraEffectList const& mReflectSpellsSchool = pVictim->GetAurasByType(SPELL_AURA_REFLECT_SPELLS_SCHOOL); + for(Unit::AuraEffectList::const_iterator i = mReflectSpellsSchool.begin(); i != mReflectSpellsSchool.end(); ++i) + if((*i)->GetMiscValue() & GetSpellSchoolMask(spell)) + reflectchance += (*i)->GetAmount(); if (reflectchance > 0 && roll_chance_i(reflectchance)) { // Start triggers for remove charges if need (trigger only for victim, and mark as active spell) @@ -3126,7 +3061,7 @@ float Unit::GetUnitBlockChance() const Player const* player = (Player const*)this; if(player->CanBlock() ) { - Item *tmpitem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); + Item *tmpitem = player->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); if(tmpitem && !tmpitem->IsBroken() && tmpitem->GetProto()->Block) return GetFloatValue(PLAYER_BLOCK_PERCENTAGE); } @@ -3192,6 +3127,9 @@ float Unit::GetUnitCriticalChance(WeaponAttackType attackType, const Unit *pVict crit -= ((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_MELEE); } + // Apply crit chance from defence skill + crit += (int32(GetMaxSkillValueForLevel(pVictim)) - int32(pVictim->GetDefenseSkillValue(this))) * 0.04f; + if (crit < 0.0f) crit = 0.0f; return crit; @@ -3212,7 +3150,7 @@ uint32 Unit::GetWeaponSkillValue (WeaponAttackType attType, Unit const* target) return 0; } - if(((Player*)this)->IsInFeralForm()) + if(IsInFeralForm()) return GetMaxSkillValueForLevel(); // always maximized SKILL_FERAL_COMBAT in fact // weapon skill or (unarmed for base attack) @@ -3236,6 +3174,67 @@ uint32 Unit::GetWeaponSkillValue (WeaponAttackType attType, Unit const* target) return value; } +void Unit::_DeleteAuras() +{ + for (AuraList::iterator i = m_removedAuras.begin(); i != m_removedAuras.end();i = m_removedAuras.begin()) + { + Aura * Aur = *i; + SpellEntry const* AurSpellInfo = Aur->GetSpellProto(); + // Statue unsummoned at aura delete + Totem* statue = NULL; + if(Aur->GetAuraDuration() && !Aur->IsPersistent() && IsChanneledSpell(Aur->GetSpellProto())) + { + Unit* caster = Aur->GetCaster(); + if(caster && caster->isAlive()) + { + // stop caster chanelling state + if(caster->m_currentSpells[CURRENT_CHANNELED_SPELL]) + { + // same spell + if (AurSpellInfo == caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo + //prevent recurential call + && caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->getState() != SPELL_STATE_FINISHED) + { + if (caster==this || !IsAreaOfEffectSpell(AurSpellInfo)) + { + // remove auras only for non-aoe spells or when chanelled aura is removed + // because aoe spells don't require aura on target to continue + caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->cancel(); + } + + if(caster->GetTypeId()==TYPEID_UNIT && ((Creature*)caster)->isTotem() && ((Totem*)caster)->GetTotemType()==TOTEM_STATUE) + statue = ((Totem*)caster); + } + } + + // Unsummon summon as possessed creatures on spell cancel + if(caster->GetTypeId() == TYPEID_PLAYER) + { + for(int i = 0; i < 3; ++i) + { + if(AurSpellInfo->Effect[i] == SPELL_EFFECT_SUMMON) + if(SummonPropertiesEntry const *SummonProperties = sSummonPropertiesStore.LookupEntry(AurSpellInfo->EffectMiscValueB[i])) + if(SummonProperties->Category == SUMMON_CATEGORY_POSSESSED) + { + ((Player*)caster)->StopCastingCharm(); + break; + } + } + } + } + } + if(statue) + { + sLog.outDebug("Statue %d is unsummoned by aura %d delete from unit %d", statue->GetGUIDLow(), Aur->GetId(),GetGUIDLow()); + statue->UnSummon(); + } + + sLog.outDebug("Aura %d is deleted from unit %d", Aur->GetId(), GetGUIDLow()); + m_removedAuras.pop_front(); + delete (Aur); + } +} + void Unit::_UpdateSpells( uint32 time ) { if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]) @@ -3252,54 +3251,45 @@ void Unit::_UpdateSpells( uint32 time ) } // TODO: Find a better way to prevent crash when multiple auras are removed. - m_removedAuras = 0; for (AuraMap::iterator i = m_Auras.begin(); i != m_Auras.end(); ++i) - if ((*i).second) - (*i).second->SetUpdated(false); + i->second->SetUpdated(false); - for (AuraMap::iterator i = m_Auras.begin(), next; i != m_Auras.end(); i = next) + for(AuraMap::iterator i = m_Auras.begin(); i != m_Auras.end();) { - next = i; - ++next; - if ((*i).second) + Aura *aur = i->second; + + // prevent double update + if(aur->IsUpdated()) { - // prevent double update - if ((*i).second->IsUpdated()) - continue; - (*i).second->SetUpdated(true); - (*i).second->Update( time ); - // several auras can be deleted due to update - if (m_removedAuras) - { - if (m_Auras.empty()) break; - next = m_Auras.begin(); - m_removedAuras = 0; - } + ++i; + continue; } + + aur->SetUpdated(true); + + uint32 removedAuras = m_removedAuras.size(); + aur->Update( time ); + + // several auras can be deleted due to update + if(removedAuras < m_removedAuras.size()) + i = m_Auras.begin(); + else + ++i; } - for (AuraMap::iterator i = m_Auras.begin(); i != m_Auras.end();) + for(AuraMap::iterator i = m_Auras.begin(); i != m_Auras.end();) { - if ((*i).second) - { - if ( !(*i).second->GetAuraDuration() && !((*i).second->IsPermanent() || ((*i).second->IsPassive())) ) - { - RemoveAura(i); - } - else - { - ++i; - } - } + if(!i->second->GetAuraDuration() && !(i->second->IsPermanent() || (i->second->IsPassive()))) + RemoveAura(i, AURA_REMOVE_BY_EXPIRE); else - { ++i; - } } + _DeleteAuras(); + if(!m_gameObj.empty()) { - std::list<GameObject*>::iterator ite1, dnext1; + GameObjectList::iterator ite1, dnext1; for (ite1 = m_gameObj.begin(); ite1 != m_gameObj.end(); ite1 = dnext1) { dnext1 = ite1; @@ -3323,7 +3313,7 @@ void Unit::_UpdateAutoRepeatSpell() if ( (GetTypeId() == TYPEID_PLAYER && ((Player*)this)->isMoving()) || IsNonMeleeSpellCasted(false,false,true) ) { // cancel wand shoot - if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Category == 351) + if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Id != SPELL_ID_AUTOSHOT) InterruptSpell(CURRENT_AUTOREPEAT_SPELL); m_AutoRepeatFirstCast = true; return; @@ -3338,7 +3328,7 @@ void Unit::_UpdateAutoRepeatSpell() if (isAttackReady(RANGED_ATTACK)) { // Check if able to cast - if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->CanCast(true)) + if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->CheckCast(true) != SPELL_CAST_OK) { InterruptSpell(CURRENT_AUTOREPEAT_SPELL); return; @@ -3376,7 +3366,7 @@ void Unit::SetCurrentCastedSpell( Spell * pSpell ) if ( m_currentSpells[CURRENT_AUTOREPEAT_SPELL] ) { // break autorepeat if not Auto Shot - if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Category == 351) + if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Id != SPELL_ID_AUTOSHOT) InterruptSpell(CURRENT_AUTOREPEAT_SPELL); m_AutoRepeatFirstCast = true; } @@ -3391,7 +3381,7 @@ void Unit::SetCurrentCastedSpell( Spell * pSpell ) // it also does break autorepeat if not Auto Shot if ( m_currentSpells[CURRENT_AUTOREPEAT_SPELL] && - m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Category == 351 ) + m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Id != SPELL_ID_AUTOSHOT ) InterruptSpell(CURRENT_AUTOREPEAT_SPELL); addUnitState(UNIT_STAT_CASTING); } break; @@ -3399,7 +3389,7 @@ void Unit::SetCurrentCastedSpell( Spell * pSpell ) case CURRENT_AUTOREPEAT_SPELL: { // only Auto Shoot does not break anything - if (pSpell->m_spellInfo->Category == 351) + if (pSpell->m_spellInfo->Id != SPELL_ID_AUTOSHOT) { // generic autorepeats break generic non-delayed and channeled non-delayed spells InterruptSpell(CURRENT_GENERIC_SPELL,false); @@ -3476,40 +3466,16 @@ bool Unit::IsNonMeleeSpellCasted(bool withDelayed, bool skipChanneled, bool skip void Unit::InterruptNonMeleeSpells(bool withDelayed, uint32 spell_id) { // generic spells are interrupted if they are not finished or delayed - Spell *spell = m_currentSpells[CURRENT_GENERIC_SPELL]; - if (spell && (!spell_id || spell->m_spellInfo->Id==spell_id)) - { - m_currentSpells[CURRENT_GENERIC_SPELL] = NULL; - - if ( (spell->getState() != SPELL_STATE_FINISHED) && - (withDelayed || spell->getState() != SPELL_STATE_DELAYED) ) - spell->cancel(); - spell->SetReferencedFromCurrent(false); - } + if (m_currentSpells[CURRENT_GENERIC_SPELL] && (!spell_id || m_currentSpells[CURRENT_GENERIC_SPELL]->m_spellInfo->Id==spell_id)) + InterruptSpell(CURRENT_GENERIC_SPELL,withDelayed); - spell = m_currentSpells[CURRENT_AUTOREPEAT_SPELL]; // autorepeat spells are interrupted if they are not finished or delayed - if (spell && (!spell_id || spell->m_spellInfo->Id==spell_id)) - { - m_currentSpells[CURRENT_AUTOREPEAT_SPELL] = NULL; - // send disable autorepeat packet in any case - if(GetTypeId()==TYPEID_PLAYER) - ((Player*)this)->SendAutoRepeatCancel(); - if ( (spell->getState() != SPELL_STATE_FINISHED) && - (withDelayed || spell->getState() != SPELL_STATE_DELAYED) ) - spell->cancel(); - spell->SetReferencedFromCurrent(false); - } + if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL] && (!spell_id || m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Id==spell_id)) + InterruptSpell(CURRENT_AUTOREPEAT_SPELL,withDelayed); // channeled spells are interrupted if they are not finished, even if they are delayed - spell = m_currentSpells[CURRENT_CHANNELED_SPELL]; - if (spell && (!spell_id || spell->m_spellInfo->Id==spell_id)) - { - m_currentSpells[CURRENT_CHANNELED_SPELL] = NULL; - if (spell->getState() != SPELL_STATE_FINISHED) - spell->cancel(); - spell->SetReferencedFromCurrent(false); - } + if (m_currentSpells[CURRENT_CHANNELED_SPELL] && (!spell_id || m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo->Id==spell_id)) + InterruptSpell(CURRENT_CHANNELED_SPELL,true); } Spell* Unit::FindCurrentSpellBySpellId(uint32 spell_id) const @@ -3571,9 +3537,9 @@ int32 Unit::GetTotalAuraModifier(AuraType auratype) const { int32 modifier = 0; - AuraList const& mTotalAuraList = GetAurasByType(auratype); - for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i) - modifier += (*i)->GetModifierValue(); + AuraEffectList const& mTotalAuraList = GetAurasByType(auratype); + for(AuraEffectList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i) + modifier += (*i)->GetAmount(); return modifier; } @@ -3582,23 +3548,22 @@ float Unit::GetTotalAuraMultiplier(AuraType auratype) const { float multiplier = 1.0f; - AuraList const& mTotalAuraList = GetAurasByType(auratype); - for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i) - multiplier *= (100.0f + (*i)->GetModifierValue())/100.0f; + AuraEffectList const& mTotalAuraList = GetAurasByType(auratype); + for(AuraEffectList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i) + multiplier *= (100.0f + (*i)->GetAmount())/100.0f; return multiplier; } -int32 Unit::GetMaxPositiveAuraModifier(AuraType auratype) const +int32 Unit::GetMaxPositiveAuraModifier(AuraType auratype) { int32 modifier = 0; - AuraList const& mTotalAuraList = GetAurasByType(auratype); - for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i) + AuraEffectList const& mTotalAuraList = GetAurasByType(auratype); + for(AuraEffectList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i) { - int32 amount = (*i)->GetModifierValue(); - if (amount > modifier) - modifier = amount; + if ((*i)->GetAmount() > modifier) + modifier = (*i)->GetAmount(); } return modifier; @@ -3608,13 +3573,10 @@ int32 Unit::GetMaxNegativeAuraModifier(AuraType auratype) const { int32 modifier = 0; - AuraList const& mTotalAuraList = GetAurasByType(auratype); - for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i) - { - int32 amount = (*i)->GetModifierValue(); - if (amount < modifier) - modifier = amount; - } + AuraEffectList const& mTotalAuraList = GetAurasByType(auratype); + for(AuraEffectList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i) + if ((*i)->GetAmount() < modifier) + modifier = (*i)->GetAmount(); return modifier; } @@ -3623,12 +3585,11 @@ int32 Unit::GetTotalAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) { int32 modifier = 0; - AuraList const& mTotalAuraList = GetAurasByType(auratype); - for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i) + AuraEffectList const& mTotalAuraList = GetAurasByType(auratype); + for(AuraEffectList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i) { - Modifier* mod = (*i)->GetModifier(); - if (mod->m_miscvalue & misc_mask) - modifier += (*i)->GetModifierValue(); + if ((*i)->GetMiscValue()& misc_mask) + modifier += (*i)->GetAmount(); } return modifier; } @@ -3637,12 +3598,11 @@ float Unit::GetTotalAuraMultiplierByMiscMask(AuraType auratype, uint32 misc_mask { float multiplier = 1.0f; - AuraList const& mTotalAuraList = GetAurasByType(auratype); - for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i) + AuraEffectList const& mTotalAuraList = GetAurasByType(auratype); + for(AuraEffectList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i) { - Modifier* mod = (*i)->GetModifier(); - if (mod->m_miscvalue & misc_mask) - multiplier *= (100.0f + (*i)->GetModifierValue())/100.0f; + if ((*i)->GetMiscValue()& misc_mask) + multiplier *= (100.0f + (*i)->GetAmount())/100.0f; } return multiplier; } @@ -3651,13 +3611,11 @@ int32 Unit::GetMaxPositiveAuraModifierByMiscMask(AuraType auratype, uint32 misc_ { int32 modifier = 0; - AuraList const& mTotalAuraList = GetAurasByType(auratype); - for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i) + AuraEffectList const& mTotalAuraList = GetAurasByType(auratype); + for(AuraEffectList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i) { - Modifier* mod = (*i)->GetModifier(); - int32 amount = (*i)->GetModifierValue(); - if (mod->m_miscvalue & misc_mask && amount > modifier) - modifier = amount; + if ((*i)->GetMiscValue()& misc_mask && (*i)->GetAmount() > modifier) + modifier = (*i)->GetAmount(); } return modifier; @@ -3667,13 +3625,11 @@ int32 Unit::GetMaxNegativeAuraModifierByMiscMask(AuraType auratype, uint32 misc_ { int32 modifier = 0; - AuraList const& mTotalAuraList = GetAurasByType(auratype); - for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i) + AuraEffectList const& mTotalAuraList = GetAurasByType(auratype); + for(AuraEffectList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i) { - Modifier* mod = (*i)->GetModifier(); - int32 amount = (*i)->GetModifierValue(); - if (mod->m_miscvalue & misc_mask && amount < modifier) - modifier = amount; + if ((*i)->GetMiscValue()& misc_mask && (*i)->GetAmount() < modifier) + modifier = (*i)->GetAmount(); } return modifier; @@ -3683,12 +3639,11 @@ int32 Unit::GetTotalAuraModifierByMiscValue(AuraType auratype, int32 misc_value) { int32 modifier = 0; - AuraList const& mTotalAuraList = GetAurasByType(auratype); - for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i) + AuraEffectList const& mTotalAuraList = GetAurasByType(auratype); + for(AuraEffectList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i) { - Modifier* mod = (*i)->GetModifier(); - if (mod->m_miscvalue == misc_value) - modifier += (*i)->GetModifierValue(); + if ((*i)->GetMiscValue()== misc_value) + modifier += (*i)->GetAmount(); } return modifier; } @@ -3697,12 +3652,11 @@ float Unit::GetTotalAuraMultiplierByMiscValue(AuraType auratype, int32 misc_valu { float multiplier = 1.0f; - AuraList const& mTotalAuraList = GetAurasByType(auratype); - for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i) + AuraEffectList const& mTotalAuraList = GetAurasByType(auratype); + for(AuraEffectList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i) { - Modifier* mod = (*i)->GetModifier(); - if (mod->m_miscvalue == misc_value) - multiplier *= (100.0f + (*i)->GetModifierValue())/100.0f; + if ((*i)->GetMiscValue()== misc_value) + multiplier *= (100.0f + (*i)->GetAmount())/100.0f; } return multiplier; } @@ -3711,13 +3665,11 @@ int32 Unit::GetMaxPositiveAuraModifierByMiscValue(AuraType auratype, int32 misc_ { int32 modifier = 0; - AuraList const& mTotalAuraList = GetAurasByType(auratype); - for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i) + AuraEffectList const& mTotalAuraList = GetAurasByType(auratype); + for(AuraEffectList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i) { - Modifier* mod = (*i)->GetModifier(); - int32 amount = (*i)->GetModifierValue(); - if (mod->m_miscvalue == misc_value && amount > modifier) - modifier = amount; + if ((*i)->GetMiscValue()== misc_value && (*i)->GetAmount() > modifier) + modifier = (*i)->GetAmount(); } return modifier; @@ -3727,13 +3679,11 @@ int32 Unit::GetMaxNegativeAuraModifierByMiscValue(AuraType auratype, int32 misc_ { int32 modifier = 0; - AuraList const& mTotalAuraList = GetAurasByType(auratype); - for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i) + AuraEffectList const& mTotalAuraList = GetAurasByType(auratype); + for(AuraEffectList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i) { - Modifier* mod = (*i)->GetModifier(); - int32 amount = (*i)->GetModifierValue(); - if (mod->m_miscvalue == misc_value && amount < modifier) - modifier = amount; + if ((*i)->GetMiscValue()== misc_value && (*i)->GetAmount() < modifier) + modifier = (*i)->GetAmount(); } return modifier; @@ -3741,6 +3691,13 @@ int32 Unit::GetMaxNegativeAuraModifierByMiscValue(AuraType auratype, int32 misc_ bool Unit::AddAura(Aura *Aur) { + // aura doesn't apply effects-return + if (!Aur->GetEffectMask()) + { + delete Aur; + return false; + } + // ghost spell check, allow apply any auras at player loading in ghost mode (will be cleanup after load) if( !isAlive() && Aur->GetId() != 20584 && Aur->GetId() != 8326 && Aur->GetId() != 2584 && (GetTypeId()!=TYPEID_PLAYER || !((Player*)this)->GetSession()->PlayerLoading()) ) @@ -3751,8 +3708,8 @@ bool Unit::AddAura(Aura *Aur) if(Aur->GetTarget() != this) { - sLog.outError("Aura (spell %u eff %u) add to aura list of %s (lowguid: %u) but Aura target is %s (lowguid: %u)", - Aur->GetId(),Aur->GetEffIndex(),(GetTypeId()==TYPEID_PLAYER?"player":"creature"),GetGUIDLow(), + sLog.outError("Aura (spell %u) add to aura list of %s (lowguid: %u) but Aura target is %s (lowguid: %u)", + Aur->GetId(),(GetTypeId()==TYPEID_PLAYER?"player":"creature"),GetGUIDLow(), (Aur->GetTarget()->GetTypeId()==TYPEID_PLAYER?"player":"creature"),Aur->GetTarget()->GetGUIDLow()); delete Aur; return false; @@ -3760,66 +3717,30 @@ bool Unit::AddAura(Aura *Aur) SpellEntry const* aurSpellInfo = Aur->GetSpellProto(); - spellEffectPair spair = spellEffectPair(Aur->GetId(), Aur->GetEffIndex()); - - bool stackModified=false; // passive and persistent auras can stack with themselves any number of times - if (!Aur->IsPassive() && !Aur->IsPersistent()) + if (!Aur->IsPassive() && !Aur->IsPersistent() && aurSpellInfo->StackAmount>1) { - for(AuraMap::iterator i2 = m_Auras.lower_bound(spair); i2 != m_Auras.upper_bound(spair);) + // find current aura from spell and change it's stackamount + if (Aura * foundAura = GetAura(aurSpellInfo->Id,Aur->GetCasterGUID())) { - if(i2->second->GetCasterGUID()==Aur->GetCasterGUID()) - { - if (!stackModified) - { - // replace aura if next will > spell StackAmount - if(aurSpellInfo->StackAmount) - { - // prevent adding stack more than once - stackModified=true; - Aur->SetStackAmount(i2->second->GetStackAmount()); - if(Aur->GetStackAmount() < aurSpellInfo->StackAmount) - Aur->SetStackAmount(Aur->GetStackAmount()+1); - } - RemoveAura(i2,AURA_REMOVE_BY_STACK); - i2=m_Auras.lower_bound(spair); - continue; - } - } - switch(aurSpellInfo->EffectApplyAuraName[Aur->GetEffIndex()]) - { - // DOT or HOT from different casters will stack - case SPELL_AURA_PERIODIC_DAMAGE: - case SPELL_AURA_PERIODIC_HEAL: - case SPELL_AURA_PERIODIC_TRIGGER_SPELL: - case SPELL_AURA_PERIODIC_ENERGIZE: - case SPELL_AURA_PERIODIC_MANA_LEECH: - case SPELL_AURA_PERIODIC_LEECH: - case SPELL_AURA_POWER_BURN_MANA: - case SPELL_AURA_OBS_MOD_MANA: - case SPELL_AURA_OBS_MOD_HEALTH: - ++i2; - continue; - } - RemoveAura(i2,AURA_REMOVE_BY_STACK); - i2=m_Auras.lower_bound(spair); - continue; + if(foundAura->GetStackAmount() < aurSpellInfo->StackAmount) + foundAura->SetStackAmount(foundAura->GetStackAmount()+1); + else + foundAura->RefreshAura(); + delete Aur; + return false; } } - // passive auras stack with all (except passive spell proc auras) - if ((!Aur->IsPassive() || !IsPassiveStackableSpell(Aur->GetId())) && - !(Aur->GetId() == 20584 || Aur->GetId() == 8326)) + // passive auras not stacable with other ranks + if (!RemoveNoStackAurasDueToAura(Aur)) { - if (!RemoveNoStackAurasDueToAura(Aur)) - { - delete Aur; - return false; // couldn't remove conflicting aura with higher rank - } + delete Aur; + return false; // couldn't remove conflicting aura with higher rank } // update single target auras list (before aura add to aura list, to prevent unexpected remove recently added aura) - if (IsSingleTargetSpell(aurSpellInfo) && Aur->GetTarget()) + if (Aur->IsSingleTarget() && Aur->GetTarget()) { // caster pointer can be deleted in time aura remove, find it by guid at each iteration for(;;) @@ -3835,12 +3756,7 @@ bool Unit::AddAura(Aura *Aur) if( (*itr)->GetTarget() != Aur->GetTarget() && IsSingleTargetSpells((*itr)->GetSpellProto(),aurSpellInfo) ) { - if ((*itr)->IsInUse()) - { - sLog.outError("Aura (Spell %u Effect %u) is in process but attempt removed at aura (Spell %u Effect %u) adding, need add stack rule for IsSingleTargetSpell", (*itr)->GetId(), (*itr)->GetEffIndex(),Aur->GetId(), Aur->GetEffIndex()); - continue; - } - (*itr)->GetTarget()->RemoveAura((*itr)->GetId(), (*itr)->GetEffIndex()); + (*itr)->GetTarget()->RemoveAurasDueToSpell((*itr)->GetId(), caster->GetGUID(), AURA_REMOVE_BY_STACK); restart = true; break; } @@ -3857,63 +3773,24 @@ bool Unit::AddAura(Aura *Aur) // add aura, register in lists and arrays Aur->_AddAura(); - m_Auras.insert(AuraMap::value_type(spellEffectPair(Aur->GetId(), Aur->GetEffIndex()), Aur)); - if (Aur->GetModifier()->m_auraname < TOTAL_AURAS) + m_Auras.insert(AuraMap::value_type(Aur->GetId(), Aur)); + + if(Aur->GetSpellProto()->AuraInterruptFlags) { - m_modAuras[Aur->GetModifier()->m_auraname].push_back(Aur); - if(Aur->GetSpellProto()->AuraInterruptFlags) - { - m_interruptableAuras.push_back(Aur); - AddInterruptMask(Aur->GetSpellProto()->AuraInterruptFlags); - } - if((Aur->GetSpellProto()->Attributes & SPELL_ATTR_BREAKABLE_BY_DAMAGE) - && (Aur->GetModifier()->m_auraname != SPELL_AURA_MOD_POSSESS)) //only dummy aura is breakable - { - m_ccAuras.push_back(Aur); - } + m_interruptableAuras.push_back(Aur); + AddInterruptMask(Aur->GetSpellProto()->AuraInterruptFlags); } - - Aur->ApplyModifier(true,true); - - uint32 id = Aur->GetId(); - if(spellmgr.GetSpellCustomAttr(id) & SPELL_ATTR_CU_LINK_AURA) + if((Aur->GetSpellProto()->Attributes & SPELL_ATTR_BREAKABLE_BY_DAMAGE + && !Aur->IsAuraType(SPELL_AURA_MOD_POSSESS)) //only dummy aura is breakable + || ((GetAllSpellMechanicMask(Aur->GetSpellProto()) & 1<<MECHANIC_KNOCKOUT) && Aur->IsAuraType(SPELL_AURA_MOD_STUN))) { - if(const std::vector<int32> *spell_triggered = spellmgr.GetSpellLinked(id + SPELL_LINK_AURA)) - for(std::vector<int32>::const_iterator itr = spell_triggered->begin(); itr != spell_triggered->end(); ++itr) - if(*itr < 0) - ApplySpellImmune(id, IMMUNITY_ID, -(*itr), true); - else if(Unit* caster = Aur->GetCaster()) - caster->AddAura(*itr, this); + m_ccAuras.push_back(Aur); } - sLog.outDebug("Aura %u now is in use", Aur->GetModifier()->m_auraname); - return true; -} - -void Unit::RemoveRankAurasDueToSpell(uint32 spellId) -{ - SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId); - if(!spellInfo) - return; - AuraMap::iterator i,next; - for (i = m_Auras.begin(); i != m_Auras.end(); i = next) - { - next = i; - ++next; - uint32 i_spellId = (*i).second->GetId(); - if((*i).second && i_spellId && i_spellId != spellId) - { - if(spellmgr.IsRankSpellDueToSpell(spellInfo,i_spellId)) - { - RemoveAurasDueToSpell(i_spellId); + Aur->HandleEffects(true); - if( m_Auras.empty() ) - break; - else - next = m_Auras.begin(); - } - } - } + sLog.outDebug("Aura %u now is in use", Aur->GetId()); + return true; } bool Unit::RemoveNoStackAurasDueToAura(Aura *Aur) @@ -3922,34 +3799,34 @@ bool Unit::RemoveNoStackAurasDueToAura(Aura *Aur) return false; SpellEntry const* spellProto = Aur->GetSpellProto(); - if (!spellProto) - return false; uint32 spellId = Aur->GetId(); - uint32 effIndex = Aur->GetEffIndex(); + + // passive spell special case (only non stackable with ranks) + if(IsPassiveSpell(spellId)) + { + if(IsPassiveSpellStackableWithRanks(spellProto)) + return true; + } SpellSpecific spellId_spec = GetSpellSpecific(spellId); + //bool linked = spellmgr.GetSpellCustomAttr(spellId) & SPELL_ATTR_CU_LINK_AURA? true : false; + AuraMap::iterator i,next; for (i = m_Auras.begin(); i != m_Auras.end(); i = next) { next = i; ++next; - if (!(*i).second) continue; SpellEntry const* i_spellProto = (*i).second->GetSpellProto(); - if (!i_spellProto) - continue; - uint32 i_spellId = i_spellProto->Id; - if (spellId==i_spellId) - continue; - if(IsPassiveSpell(i_spellId)) { - if(IsPassiveStackableSpell(i_spellId)) + // passive non-stackable spells not stackable only for same caster + if(Aur->GetCasterGUID()!=i->second->GetCasterGUID()) continue; // passive non-stackable spells not stackable only with another rank of same spell @@ -3957,180 +3834,133 @@ bool Unit::RemoveNoStackAurasDueToAura(Aura *Aur) continue; } - uint32 i_effIndex = (*i).second->GetEffIndex(); - bool is_triggered_by_spell = false; // prevent triggered aura of removing aura that triggered it for(int j = 0; j < 3; ++j) if (i_spellProto->EffectTriggerSpell[j] == spellProto->Id) is_triggered_by_spell = true; - if (is_triggered_by_spell) continue; - for(int j = 0; j < 3; ++j) - { - // prevent remove dummy triggered spells at next effect aura add - switch(spellProto->Effect[j]) // main spell auras added added after triggered spell - { - case SPELL_EFFECT_DUMMY: - switch(spellId) - { - case 5420: if(i_spellId==34123) is_triggered_by_spell = true; break; - } - break; - } - - if(is_triggered_by_spell) - break; + // check if they can stack + bool sameCaster = Aur->GetCasterGUID() == (*i).second->GetCasterGUID(); - // prevent remove form main spell by triggered passive spells - switch(i_spellProto->EffectApplyAuraName[j]) // main aura added before triggered spell - { - case SPELL_AURA_MOD_SHAPESHIFT: - switch(i_spellId) + /*// Dont remove by stack with linked auras + // Not needed for now + if(sameCaster && linked) + { + if(const std::vector<int32> *spell_triggered = spellmgr.GetSpellLinked(spellId + SPELL_LINK_AURA)) + for(std::vector<int32>::const_iterator itr = spell_triggered->begin(); itr != spell_triggered->end(); ++itr) + if(*itr>0 && *itr==i_spellId) { - case 24858: if(spellId==24905) is_triggered_by_spell = true; break; - case 33891: if(spellId==5420 || spellId==34123) is_triggered_by_spell = true; break; - case 34551: if(spellId==22688) is_triggered_by_spell = true; break; + is_triggered_by_spell=true; + break; } - break; - } - } + }*/ - if(!is_triggered_by_spell) - { - bool sameCaster = Aur->GetCasterGUID() == (*i).second->GetCasterGUID(); - if( spellmgr.IsNoStackSpellDueToSpell(spellId, i_spellId, sameCaster) ) - { - //some spells should be not removed by lower rank of them (totem, paladin aura) - if (!sameCaster - &&(spellProto->Effect[effIndex]==SPELL_EFFECT_APPLY_AREA_AURA_PARTY) - &&(spellProto->DurationIndex==21) - &&(spellmgr.IsRankSpellDueToSpell(spellProto, i_spellId)) - &&(CompareAuraRanks(spellId, effIndex, i_spellId, i_effIndex) < 0)) - return false; + if (is_triggered_by_spell) + continue; - // Its a parent aura (create this aura in ApplyModifier) - if ((*i).second->IsInUse()) - { - sLog.outError("Aura (Spell %u Effect %u) is in process but attempt removed at aura (Spell %u Effect %u) adding, need add stack rule for Unit::RemoveNoStackAurasDueToAura", i->second->GetId(), i->second->GetEffIndex(),Aur->GetId(), Aur->GetEffIndex()); - continue; - } + if(!spellmgr.IsNoStackSpellDueToSpell(spellId, i_spellId, sameCaster)) + continue; - uint64 caster = (*i).second->GetCasterGUID(); - // Remove all auras by aura caster - for (uint8 a=0;a<3;++a) - { - spellEffectPair spair = spellEffectPair(i_spellId, a); - for(AuraMap::iterator iter = m_Auras.lower_bound(spair); iter != m_Auras.upper_bound(spair);) - { - if(iter->second->GetCasterGUID()==caster) - { - RemoveAura(iter, AURA_REMOVE_BY_STACK); - iter = m_Auras.lower_bound(spair); - } - else - ++iter; - } - } + //some spells should be not removed by lower rank of them (totem, paladin aura) + if (!sameCaster + &&(Aur->IsAreaAura()) + &&(spellProto->DurationIndex==21) + &&(spellmgr.IsRankSpellDueToSpell(spellProto, i_spellId)) + &&(IsHigherHankOfSpell(spellId,i_spellId))) + return false; - if( m_Auras.empty() ) - break; - else - next = m_Auras.begin(); - } - } + // Remove all auras by aura caster + RemoveAura(i, AURA_REMOVE_BY_STACK); + next=i; } return true; } -void Unit::RemoveAura(uint32 spellId, uint32 effindex, Aura* except) +void Unit::RemoveAura(uint32 spellId, uint64 caster ,AuraRemoveMode removeMode) { - spellEffectPair spair = spellEffectPair(spellId, effindex); - for(AuraMap::iterator iter = m_Auras.lower_bound(spair); iter != m_Auras.upper_bound(spair);) + for(AuraMap::iterator iter = m_Auras.lower_bound(spellId); iter != m_Auras.upper_bound(spellId);) { - if(iter->second!=except) + if (!caster || iter->second->GetCasterGUID()==caster) { - RemoveAura(iter); - iter = m_Auras.lower_bound(spair); + RemoveAura(iter, removeMode); + return; } else ++iter; } } -void Unit::RemoveAurasByCasterSpell(uint32 spellId, uint64 casterGUID) +void Unit::RemoveAura(Aura * aur ,AuraRemoveMode mode) { - for(int k = 0; k < 3; ++k) + // no need to remove + if (!aur || aur->IsRemoved()) + return; + for(AuraMap::iterator iter = m_Auras.lower_bound(aur->GetId()); iter != m_Auras.upper_bound(aur->GetId());) { - spellEffectPair spair = spellEffectPair(spellId, k); - for (AuraMap::iterator iter = m_Auras.lower_bound(spair); iter != m_Auras.upper_bound(spair);) + if (aur == iter->second) { - if (iter->second->GetCasterGUID() == casterGUID) - { - RemoveAura(iter); - iter = m_Auras.upper_bound(spair); // overwrite by more appropriate - } - else - ++iter; + RemoveAura(iter, mode); + return; } + else + ++iter; } } -void Unit::SetAurasDurationByCasterSpell(uint32 spellId, uint64 casterGUID, int32 duration) +void Unit::RemoveAurasDueToSpell(uint32 spellId, uint64 caster ,AuraRemoveMode removeMode) { - for(uint8 i = 0; i < 3; ++i) + for(AuraMap::iterator iter = m_Auras.lower_bound(spellId); iter != m_Auras.upper_bound(spellId);) { - spellEffectPair spair = spellEffectPair(spellId, i); - for(AuraMap::const_iterator itr = GetAuras().lower_bound(spair); itr != GetAuras().upper_bound(spair); ++itr) + if (!caster || iter->second->GetCasterGUID()==caster) { - if(itr->second->GetCasterGUID()==casterGUID) - { - itr->second->SetAuraDuration(duration); - break; - } + RemoveAura(iter, removeMode); + iter = m_Auras.lower_bound(spellId); } + else + ++iter; } } -Aura* Unit::GetAuraByCasterSpell(uint32 spellId, uint64 casterGUID) +void Unit::RemoveAuraFromStack(uint32 spellId, uint64 caster ,AuraRemoveMode removeMode) { - // Returns first found aura from spell-use only in cases where effindex of spell doesn't matter! - for(uint8 i = 0; i < 3; ++i) + for(AuraMap::iterator iter = m_Auras.lower_bound(spellId); iter != m_Auras.upper_bound(spellId);) { - spellEffectPair spair = spellEffectPair(spellId, i); - for(AuraMap::const_iterator itr = GetAuras().lower_bound(spair); itr != GetAuras().upper_bound(spair); ++itr) + if (!caster || iter->second->GetCasterGUID()==caster) { - if(itr->second->GetCasterGUID()==casterGUID) - return itr->second; + RemoveAuraFromStack(iter, removeMode); + return; } + else + ++iter; } - return NULL; +} + +inline void Unit::RemoveAuraFromStack(AuraMap::iterator &iter,AuraRemoveMode removeMode) +{ + if (iter->second->modStackAmount(-1)) + RemoveAura(iter, removeMode); } void Unit::RemoveAurasDueToSpellByDispel(uint32 spellId, uint64 casterGUID, Unit *dispeler) { - for (AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end(); ) + for(AuraMap::iterator iter = m_Auras.lower_bound(spellId); iter != m_Auras.upper_bound(spellId);) { - Aura *aur = iter->second; - if (aur->GetId() == spellId && aur->GetCasterGUID() == casterGUID) + Aura * aur= iter->second; + if (casterGUID == aur->GetCasterGUID()) { - // Custom dispel case // Unstable Affliction - if (aur->GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK && (aur->GetSpellProto()->SpellFamilyFlags & 0x010000000000LL)) + if (aur->GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK && (aur->GetSpellProto()->SpellFamilyFlags[1] & 0x0100)) { - int32 damage = aur->GetModifier()->m_amount*9; + int32 damage = aur->GetPartAura(0)->GetAmount()*9; uint64 caster_guid = aur->GetCasterGUID(); - - // Remove aura - RemoveAura(iter, AURA_REMOVE_BY_DISPEL); - + RemoveAuraFromStack(iter, AURA_REMOVE_BY_ENEMY_SPELL); // backfire damage and silence dispeler->CastCustomSpell(dispeler, 31117, &damage, NULL, NULL, true, NULL, NULL,caster_guid); - - iter = m_Auras.begin(); // iterator can be invalidate at cast if self-dispel + return; } - else - RemoveAura(iter, AURA_REMOVE_BY_DISPEL); + RemoveAuraFromStack(iter, AURA_REMOVE_BY_ENEMY_SPELL); + return; } else ++iter; @@ -4139,130 +3969,83 @@ void Unit::RemoveAurasDueToSpellByDispel(uint32 spellId, uint64 casterGUID, Unit void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, uint64 casterGUID, Unit *stealer) { - for (AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end(); ) + if (casterGUID == stealer->GetGUID()) + return; + + for(AuraMap::iterator iter = m_Auras.lower_bound(spellId); iter != m_Auras.upper_bound(spellId);) { - Aura *aur = iter->second; - if (aur->GetId() == spellId && aur->GetCasterGUID() == casterGUID) + Aura * aur= iter->second; + if (casterGUID == aur->GetCasterGUID()) { - int32 basePoints = aur->GetBasePoints(); - // construct the new aura for the attacker - Aura * new_aur = CreateAura(aur->GetSpellProto(), aur->GetEffIndex(), NULL/*&basePoints*/, stealer); - if(!new_aur) - continue; - - // set its duration and maximum duration - // max duration 2 minutes (in msecs) - int32 dur = aur->GetAuraDuration(); - const int32 max_dur = 2*MINUTE*1000; - new_aur->SetAuraMaxDuration( max_dur > dur ? dur : max_dur ); - new_aur->SetAuraDuration( max_dur > dur ? dur : max_dur ); + int32 damage[3]; + for (uint8 i=0;i<3;++i) + { + if (aur->GetPartAura(i)) + damage[i]=aur->GetPartAura(i)->GetAmount(); + else + damage[i]=NULL; + } + int32 dur = 2*MINUTE*IN_MILISECONDS < aur->GetAuraDuration() ? 2*MINUTE*IN_MILISECONDS : aur->GetAuraDuration(); + Aura * new_aur = new Aura(aur->GetSpellProto(),aur->GetEffectMask(), NULL, stealer, stealer, NULL); + new_aur->SetLoadedState(aur->GetCasterGUID(), dur, dur, aur->GetAuraCharges(), aur->GetStackAmount(), &damage[0]); - // add the new aura to stealer + // Unregister _before_ adding to stealer + aur->UnregisterSingleCastAura(); + // strange but intended behaviour: Stolen single target auras won't be treated as single targeted + new_aur->SetIsSingleTarget(false); stealer->AddAura(new_aur); - - // Remove aura as dispel - RemoveAura(iter, AURA_REMOVE_BY_DISPEL); + RemoveAuraFromStack(iter, AURA_REMOVE_BY_ENEMY_SPELL); + return; } else ++iter; } } -void Unit::RemoveAurasDueToSpellByCancel(uint32 spellId) -{ - for (AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end(); ) - { - if (iter->second->GetId() == spellId) - RemoveAura(iter, AURA_REMOVE_BY_CANCEL); - else - ++iter; - } -} - -void Unit::RemoveAurasWithDispelType( DispelType type ) -{ - // Create dispel mask by dispel type - uint32 dispelMask = GetDispellMask(type); - // Dispel all existing auras vs current dispel type - AuraMap& auras = GetAuras(); - for(AuraMap::iterator itr = auras.begin(); itr != auras.end(); ) - { - SpellEntry const* spell = itr->second->GetSpellProto(); - if( (1<<spell->Dispel) & dispelMask ) - { - // Dispel aura - RemoveAurasDueToSpell(spell->Id); - itr = auras.begin(); - } - else - ++itr; - } -} - -void Unit::RemoveSingleAuraFromStackByDispel(uint32 spellId) +void Unit::RemoveAurasDueToItemSpell(Item* castItem,uint32 spellId) { - for (AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end(); ) + for (AuraMap::iterator iter = m_Auras.lower_bound(spellId); iter != m_Auras.upper_bound(spellId);) { - Aura *aur = iter->second; - if (aur->GetId() == spellId) + if (!castItem || iter->second->GetCastItemGUID()==castItem->GetGUID()) { - if(iter->second->GetStackAmount() > 1) - { - // reapply modifier with reduced stack amount - iter->second->ApplyModifier(false,true); - iter->second->SetStackAmount(iter->second->GetStackAmount()-1); - iter->second->ApplyModifier(true,true); - - iter->second->UpdateSlotCounterAndDuration(); - return; // not remove aura if stack amount > 1 - } - else - RemoveAura(iter,AURA_REMOVE_BY_DISPEL); + RemoveAura(iter); + iter = m_Auras.upper_bound(spellId); // overwrite by more appropriate } else ++iter; } } -void Unit::RemoveSingleAuraFromStack(uint32 spellId, uint32 effindex) +void Unit::RemoveAurasByType(AuraType auraType, uint64 casterGUID, Aura * except) { - AuraMap::iterator iter = m_Auras.find(spellEffectPair(spellId, effindex)); - if(iter != m_Auras.end()) + if (auraType >= TOTAL_AURAS) return; + for (AuraEffectList::iterator iter = m_modAuras[auraType].begin(); iter != m_modAuras[auraType].end();) { - if(iter->second->GetStackAmount() > 1) + Aura * aur = (*iter)->GetParentAura(); + ++iter; + if (aur != except && (!casterGUID || aur->GetCasterGUID()==casterGUID)) { - // reapply modifier with reduced stack amount - iter->second->ApplyModifier(false,true); - iter->second->SetStackAmount(iter->second->GetStackAmount()-1); - iter->second->ApplyModifier(true,true); - - iter->second->UpdateSlotCounterAndDuration(); - return; // not remove aura if stack amount > 1 + uint32 removedAuras = m_removedAuras.size(); + RemoveAura(aur); + if (removedAuras+1<m_removedAuras.size()) + iter=m_modAuras[auraType].begin(); } - RemoveAura(iter); } } -void Unit::RemoveAurasDueToSpell(uint32 spellId, Aura* except) -{ - for (int i = 0; i < 3; ++i) - RemoveAura(spellId,i,except); -} - -void Unit::RemoveAurasDueToItemSpell(Item* castItem,uint32 spellId) +void Unit::RemoveAurasByTypeWithDispel(AuraType auraType, Spell * spell) { - for (int k=0; k < 3; ++k) + if (auraType >= TOTAL_AURAS) return; + for (AuraEffectList::iterator iter = m_modAuras[auraType].begin(); iter != m_modAuras[auraType].end();) { - spellEffectPair spair = spellEffectPair(spellId, k); - for (AuraMap::iterator iter = m_Auras.lower_bound(spair); iter != m_Auras.upper_bound(spair);) + Aura * aur = (*iter)->GetParentAura(); + ++iter; + if (GetDispelChance(aur->GetCaster(), aur->GetId())) { - if (iter->second->GetCastItemGUID() == castItem->GetGUID()) - { - RemoveAura(iter); - iter = m_Auras.upper_bound(spair); // overwrite by more appropriate - } - else - ++iter; + uint32 removedAuras = m_removedAuras.size(); + RemoveAura(aur, AURA_REMOVE_BY_ENEMY_SPELL); + if (removedAuras+1<m_removedAuras.size()) + iter=m_modAuras[auraType].begin(); } } } @@ -4280,182 +4063,62 @@ void Unit::RemoveNotOwnSingleTargetAuras() // single target auras at other targets AuraList& scAuras = GetSingleCastAuras(); - for (AuraList::iterator iter = scAuras.begin(); iter != scAuras.end(); ) + for (AuraList::iterator iter = scAuras.begin(); iter != scAuras.end();) { - Aura* aura = *iter; - if (aura->GetTarget()!=this) + Aura * aur=*iter; + ++iter; + if (aur->GetTarget()!=this) { - scAuras.erase(iter); // explicitly remove, instead waiting remove in RemoveAura - aura->GetTarget()->RemoveAura(aura->GetId(),aura->GetEffIndex()); - iter = scAuras.begin(); + uint32 removedAuras = m_removedAuras.size(); + aur->GetTarget()->RemoveAura(aur->GetId(),aur->GetCasterGUID()); + if (removedAuras+1<m_removedAuras.size()) + iter=scAuras.begin(); } - else - ++iter; } - } void Unit::RemoveAura(AuraMap::iterator &i, AuraRemoveMode mode) { Aura* Aur = i->second; - SpellEntry const* AurSpellInfo = Aur->GetSpellProto(); // some ShapeshiftBoosts at remove trigger removing other auras including parent Shapeshift aura // remove aura from list before to prevent deleting it before m_Auras.erase(i); - ++m_removedAuras; // internal count used by unit update - Unit* caster = NULL; - if (IsSingleTargetSpell(AurSpellInfo)) + Aur->UnregisterSingleCastAura(); + + if(Aur->GetSpellProto()->AuraInterruptFlags) { - caster = Aur->GetCaster(); - if(caster) - { - AuraList& scAuras = caster->GetSingleCastAuras(); - scAuras.remove(Aur); - } - else - { - sLog.outError("Couldn't find the caster of the single target aura, may crash later!"); - assert(false); - } + m_interruptableAuras.remove(Aur); + UpdateInterruptMask(); } - // remove from list before mods removing (prevent cyclic calls, mods added before including to aura list - use reverse order) - if (Aur->GetModifier()->m_auraname < TOTAL_AURAS) + if((Aur->GetSpellProto()->Attributes & SPELL_ATTR_BREAKABLE_BY_DAMAGE + && !Aur->IsAuraType(SPELL_AURA_MOD_POSSESS)) //only dummy aura is breakable + || ((GetAllSpellMechanicMask(Aur->GetSpellProto()) & 1<<MECHANIC_KNOCKOUT) && Aur->IsAuraType(SPELL_AURA_MOD_STUN))) { - m_modAuras[Aur->GetModifier()->m_auraname].remove(Aur); - - if(Aur->GetSpellProto()->AuraInterruptFlags) - { - m_interruptableAuras.remove(Aur); - UpdateInterruptMask(); - } - - if((Aur->GetSpellProto()->Attributes & SPELL_ATTR_BREAKABLE_BY_DAMAGE) - && (Aur->GetModifier()->m_auraname != SPELL_AURA_MOD_POSSESS)) //only dummy aura is breakable - { - m_ccAuras.remove(Aur); - } + m_ccAuras.remove(Aur); } - // Set remove mode Aur->SetRemoveMode(mode); - // Statue unsummoned at aura remove - Totem* statue = NULL; - bool channeled = false; - if(Aur->GetAuraDuration() && !Aur->IsPersistent() && IsChanneledSpell(AurSpellInfo)) - { - if(!caster) // can be already located for IsSingleTargetSpell case - caster = Aur->GetCaster(); - - if(caster && caster->isAlive()) - { - // stop caster chanelling state - if(caster->m_currentSpells[CURRENT_CHANNELED_SPELL] - //prevent recurential call - && caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->getState() != SPELL_STATE_FINISHED) - { - if (caster==this || !IsAreaOfEffectSpell(AurSpellInfo)) - { - // remove auras only for non-aoe spells or when chanelled aura is removed - // because aoe spells don't require aura on target to continue - if (AurSpellInfo->EffectApplyAuraName[Aur->GetEffIndex()]!=SPELL_AURA_PERIODIC_DUMMY - && AurSpellInfo->EffectApplyAuraName[Aur->GetEffIndex()]!= SPELL_AURA_DUMMY) - //don't stop channeling of scripted spells (this is actually a hack) - { - caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->cancel(); - caster->m_currentSpells[CURRENT_CHANNELED_SPELL]=NULL; - - } - } - - if(caster->GetTypeId()==TYPEID_UNIT && ((Creature*)caster)->isTotem() && ((Totem*)caster)->GetTotemType()==TOTEM_STATUE) - statue = ((Totem*)caster); - } - - // Unsummon summon as possessed creatures on spell cancel - if(caster->GetTypeId() == TYPEID_PLAYER) - { - for(int i = 0; i < 3; ++i) - { - if(AurSpellInfo->Effect[i] == SPELL_EFFECT_SUMMON && - (AurSpellInfo->EffectMiscValueB[i] == SUMMON_TYPE_POSESSED || - AurSpellInfo->EffectMiscValueB[i] == SUMMON_TYPE_POSESSED2 || - AurSpellInfo->EffectMiscValueB[i] == SUMMON_TYPE_POSESSED3)) - { - ((Player*)caster)->StopCastingCharm(); - break; - } - } - } - } - } - - sLog.outDebug("Aura %u (%u) now is remove mode %d", Aur->GetId(), Aur->GetModifier()->m_auraname, mode); - assert(!Aur->IsInUse()); - Aur->ApplyModifier(false,true); + sLog.outDebug("Aura %u now is remove mode %d", Aur->GetId(), mode); + Aur->HandleEffects(false); - Aur->SetStackAmount(0); + // set aura to be removed during unit::_updatespells + m_removedAuras.push_back(Aur); Aur->_RemoveAura(); - bool stack = false; - spellEffectPair spair = spellEffectPair(Aur->GetId(), Aur->GetEffIndex()); - for(AuraMap::const_iterator itr = GetAuras().lower_bound(spair); itr != GetAuras().upper_bound(spair); ++itr) - { - if (itr->second->GetCasterGUID()==GetGUID()) - { - stack = true; - } - } - if (!stack) - { - // Remove all triggered by aura spells vs unlimited duration - Aur->CleanupTriggeredSpells(); - - // Remove Linked Auras - uint32 id = Aur->GetId(); - if(spellmgr.GetSpellCustomAttr(id) & SPELL_ATTR_CU_LINK_REMOVE) - { - if(const std::vector<int32> *spell_triggered = spellmgr.GetSpellLinked(-(int32)id)) - for(std::vector<int32>::const_iterator itr = spell_triggered->begin(); itr != spell_triggered->end(); ++itr) - if(*itr < 0) - RemoveAurasDueToSpell(-(*itr)); - else if(Unit* caster = Aur->GetCaster()) - CastSpell(this, *itr, true, 0, 0, caster->GetGUID()); - } - if(spellmgr.GetSpellCustomAttr(id) & SPELL_ATTR_CU_LINK_AURA) - { - if(const std::vector<int32> *spell_triggered = spellmgr.GetSpellLinked(id + SPELL_LINK_AURA)) - for(std::vector<int32>::const_iterator itr = spell_triggered->begin(); itr != spell_triggered->end(); ++itr) - if(*itr < 0) - ApplySpellImmune(id, IMMUNITY_ID, -(*itr), false); - else - RemoveAurasDueToSpell(*itr); - } - } - - delete Aur; - - if(statue) - statue->UnSummon(); - // only way correctly remove all auras from list - if( m_Auras.empty() ) - i = m_Auras.end(); - else - i = m_Auras.begin(); + i = m_Auras.begin(); } void Unit::RemoveAllAuras() { + AuraMap::iterator iter = m_Auras.begin(); while (!m_Auras.empty()) - { - AuraMap::iterator iter = m_Auras.begin(); RemoveAura(iter); - } } void Unit::RemoveArenaAuras(bool onleave) @@ -4487,17 +4150,17 @@ void Unit::RemoveAllAurasOnDeath() } } -void Unit::DelayAura(uint32 spellId, uint32 effindex, int32 delaytime) +void Unit::DelayAura(uint32 spellId, uint64 caster, int32 delaytime) { - AuraMap::iterator iter = m_Auras.find(spellEffectPair(spellId, effindex)); - if (iter != m_Auras.end()) + if (Aura * aur = GetAura(spellId, caster)) { - if (iter->second->GetAuraDuration() < delaytime) - iter->second->SetAuraDuration(0); + if (aur->GetAuraDuration() < delaytime) + aur->SetAuraDuration(0); else - iter->second->SetAuraDuration(iter->second->GetAuraDuration() - delaytime); - iter->second->UpdateAuraDuration(); - sLog.outDebug("Aura %u partially interrupted on unit %u, new duration: %u ms",iter->second->GetModifier()->m_auraname, GetGUIDLow(), iter->second->GetAuraDuration()); + aur->SetAuraDuration(aur->GetAuraDuration() - delaytime); + // update for out of range group members (on 1 slot use) + UpdateAuraForGroup(aur->GetAuraSlot()); + sLog.outDebug("Aura %u partially interrupted on unit %u, new duration: %u ms",aur->GetId(), GetGUIDLow(), aur->GetAuraDuration()); } } @@ -4505,7 +4168,7 @@ void Unit::_RemoveAllAuraMods() { for (AuraMap::iterator i = m_Auras.begin(); i != m_Auras.end(); ++i) { - (*i).second->ApplyModifier(false); + (*i).second->ApplyAllModifiers(false); } } @@ -4513,15 +4176,94 @@ void Unit::_ApplyAllAuraMods() { for (AuraMap::iterator i = m_Auras.begin(); i != m_Auras.end(); ++i) { - (*i).second->ApplyModifier(true); + (*i).second->ApplyAllModifiers(true); } } -Aura* Unit::GetAura(uint32 spellId, uint32 effindex) +bool Unit::HasAuraTypeWithMiscvalue(AuraType auratype, uint32 miscvalue) const +{ + AuraEffectList const& mTotalAuraList = GetAurasByType(auratype); + for(AuraEffectList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i) + if (miscvalue == (*i)->GetMiscValue()) + return true; + return false; +} + +bool Unit::HasAuraType(AuraType auraType) const +{ + return (!m_modAuras[auraType].empty()); +} + +bool Unit::HasAura(uint32 spellId, uint64 caster) const { - AuraMap::iterator iter = m_Auras.find(spellEffectPair(spellId, effindex)); - if (iter != m_Auras.end()) - return iter->second; + //Special case for non existing spell + if (spellId==61988) + return HasAura(61987, caster) || HasAura(25771, caster); + + if (Aura * aur = GetAura(spellId, caster)) + return true; + return false; +} + +bool Unit::HasAura(Aura * aur) const +{ + // no need to find aura + if (!aur || aur->IsRemoved()) + return false; + for(AuraMap::const_iterator iter = m_Auras.lower_bound(aur->GetId()); iter != m_Auras.upper_bound(aur->GetId());) + { + if (aur == iter->second) + return true; + else + ++iter; + } + return false; +} + +bool Unit::HasAuraEffect(uint32 spellId, uint8 effIndex, uint64 caster) const +{ + if (Aura * aur = GetAura(spellId, caster)) + return bool(aur->HasEffect(effIndex)); + return false; +} + +Aura * Unit::GetAura(uint32 spellId, uint64 caster) const +{ + if (!caster) + { + AuraMap::const_iterator itr = m_Auras.find(spellId); + return itr != m_Auras.end() ? itr->second : NULL; + } + else + { + AuraMap const& auras = GetAuras(); + for(AuraMap::const_iterator itr = auras.lower_bound(spellId); itr != auras.upper_bound(spellId); ++itr) + if(itr->second->GetCasterGUID()==caster) + return itr->second; + return NULL; + } +} + +AuraEffect * Unit::GetAuraEffect(uint32 spellId, uint8 effIndex, uint64 caster) const +{ + if (Aura * aur = GetAura(spellId, caster)) + return aur->GetPartAura(effIndex); + return false; +} + +AuraEffect* Unit::GetAura(AuraType type, uint32 family, uint32 familyFlag1, uint32 familyFlag2, uint32 familyFlag3, uint64 casterGUID) +{ + AuraEffectList const& auras = GetAurasByType(type); + for(AuraEffectList::const_iterator i = auras.begin();i != auras.end(); ++i) + { + SpellEntry const *spell = (*i)->GetSpellProto(); + if (spell->SpellFamilyName == family && spell->SpellFamilyFlags.HasFlag(familyFlag1, familyFlag2, familyFlag3)) + { + if (casterGUID && (*i)->GetCasterGUID()!=casterGUID) + continue; + return (*i); + } + } return NULL; } @@ -4598,27 +4340,54 @@ DynamicObject * Unit::GetDynObject(uint32 spellId) return NULL; } +GameObject* Unit::GetGameObject(uint32 spellId) const +{ + for (GameObjectList::const_iterator i = m_gameObj.begin(); i != m_gameObj.end();) + if ((*i)->GetSpellId() == spellId) + return *i; + + return NULL; +} + void Unit::AddGameObject(GameObject* gameObj) { assert(gameObj && gameObj->GetOwnerGUID()==0); m_gameObj.push_back(gameObj); gameObj->SetOwnerGUID(GetGUID()); + + if ( GetTypeId()==TYPEID_PLAYER && gameObj->GetSpellId() ) + { + SpellEntry const* createBySpell = sSpellStore.LookupEntry(gameObj->GetSpellId()); + // Need disable spell use for owner + if (createBySpell && createBySpell->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE) + // note: item based cooldowns and cooldown spell mods with charges ignored (unknown existed cases) + ((Player*)this)->AddSpellAndCategoryCooldowns(createBySpell,0,NULL,true); + } } void Unit::RemoveGameObject(GameObject* gameObj, bool del) { assert(gameObj && gameObj->GetOwnerGUID()==GetGUID()); + gameObj->SetOwnerGUID(0); + // GO created by some spell - if ( GetTypeId()==TYPEID_PLAYER && gameObj->GetSpellId() ) + if (uint32 spellid = gameObj->GetSpellId()) { - SpellEntry const* createBySpell = sSpellStore.LookupEntry(gameObj->GetSpellId()); - // Need activate spell use for owner - if (createBySpell && createBySpell->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE) - ((Player*)this)->SendCooldownEvent(createBySpell); + RemoveAurasDueToSpell(spellid); + + if (GetTypeId()==TYPEID_PLAYER) + { + SpellEntry const* createBySpell = sSpellStore.LookupEntry(spellid ); + // Need activate spell use for owner + if (createBySpell && createBySpell->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE) + // note: item based cooldowns and cooldown spell mods with charges ignored (unknown existed cases) + ((Player*)this)->SendCooldownEvent(createBySpell); + } } - gameObj->SetOwnerGUID(0); + m_gameObj.remove(gameObj); + if(del) { gameObj->SetRespawnTime(0); @@ -4630,7 +4399,7 @@ void Unit::RemoveGameObject(uint32 spellid, bool del) { if(m_gameObj.empty()) return; - std::list<GameObject*>::iterator i, next; + GameObjectList::iterator i, next; for (i = m_gameObj.begin(); i != m_gameObj.end(); i = next) { next = i; @@ -4653,7 +4422,7 @@ void Unit::RemoveGameObject(uint32 spellid, bool del) void Unit::RemoveAllGameObjects() { // remove references to unit - for(std::list<GameObject*>::iterator i = m_gameObj.begin(); i != m_gameObj.end();) + for(GameObjectList::iterator i = m_gameObj.begin(); i != m_gameObj.end();) { (*i)->SetOwnerGUID(0); (*i)->SetRespawnTime(0); @@ -4669,6 +4438,7 @@ void Unit::SendSpellNonMeleeDamageLog(SpellNonMeleeDamage *log) data.append(log->attacker->GetPackGUID()); data << uint32(log->SpellID); data << uint32(log->damage); //damage amount + data << uint32(0); data << uint8 (log->schoolMask); //damage school data << uint32(log->absorb); //AbsorbedDamage data << uint32(log->resist); //resist @@ -4688,6 +4458,7 @@ void Unit::SendSpellNonMeleeDamageLog(Unit *target,uint32 SpellID,uint32 Damage, data.append(GetPackGUID()); data << uint32(SpellID); data << uint32(Damage-AbsorbedDamage-Resist-Blocked); + data << uint32(0); // wotlk data << uint8(damageSchoolMask); // spell school data << uint32(AbsorbedDamage); // AbsorbedDamage data << uint32(Resist); // resist @@ -4726,25 +4497,65 @@ void Unit::SendSpellMiss(Unit *target, uint32 spellID, SpellMissInfo missInfo) void Unit::SendAttackStateUpdate(CalcDamageInfo *damageInfo) { - WorldPacket data(SMSG_ATTACKERSTATEUPDATE, (16+84)); // we guess size + uint32 count = 1; + WorldPacket data(SMSG_ATTACKERSTATEUPDATE, (16+45)); // we guess size data << (uint32)damageInfo->HitInfo; data.append(GetPackGUID()); data.append(damageInfo->target->GetPackGUID()); data << (uint32)(damageInfo->damage); // Full damage + data << uint32(0); // overkill value + + data << (uint8)count; // Sub damage count + + for(int i = 0; i < count; ++i) + { + data << (uint32)(damageInfo->damageSchoolMask); // School of sub damage + data << (float)damageInfo->damage; // sub damage + data << (uint32)damageInfo->damage; // Sub Damage + } - data << (uint8)1; // Sub damage count - //=== Sub damage description - data << (uint32)(damageInfo->damageSchoolMask); // School of sub damage - data << (float)damageInfo->damage; // sub damage - data << (uint32)damageInfo->damage; // Sub Damage - data << (uint32)damageInfo->absorb; // Absorb - data << (uint32)damageInfo->resist; // Resist - //================================================= - data << (uint32)damageInfo->TargetState; + if(damageInfo->HitInfo & (HITINFO_ABSORB | HITINFO_ABSORB2)) + { + for(int i = 0; i < count; ++i) + data << (uint32)damageInfo->absorb; // Absorb + } + + if(damageInfo->HitInfo & (HITINFO_RESIST | HITINFO_RESIST2)) + { + for(int i = 0; i < count; ++i) + data << (uint32)damageInfo->resist; // Resist + } + + data << (uint8)damageInfo->TargetState; data << (uint32)0; data << (uint32)0; - data << (uint32)damageInfo->blocked_amount; - SendMessageToSet( &data, true );/**/ + + if(damageInfo->HitInfo & HITINFO_BLOCK) + data << (uint32)damageInfo->blocked_amount; + + if(damageInfo->HitInfo & HITINFO_UNK3) + data << uint32(0); + + if(damageInfo->HitInfo & HITINFO_UNK1) + { + data << uint32(0); + data << float(0); + data << float(0); + data << float(0); + data << float(0); + data << float(0); + data << float(0); + data << float(0); + data << float(0); + for(uint8 i = 0; i < 5; ++i) + { + data << float(0); + data << float(0); + } + data << uint32(0); + } + + SendMessageToSet( &data, true ); } void Unit::SendAttackStateUpdate(uint32 HitInfo, Unit *target, uint8 SwingType, SpellSchoolMask damageSchoolMask, uint32 Damage, uint32 AbsorbDamage, uint32 Resist, VictimState TargetState, uint32 BlockedAmount) @@ -4752,176 +4563,76 @@ void Unit::SendAttackStateUpdate(uint32 HitInfo, Unit *target, uint8 SwingType, sLog.outDebug("WORLD: Sending SMSG_ATTACKERSTATEUPDATE"); WorldPacket data(SMSG_ATTACKERSTATEUPDATE, (16+45)); // we guess size - data << (uint32)HitInfo; + data << uint32(HitInfo); // flags data.append(GetPackGUID()); data.append(target->GetPackGUID()); - data << (uint32)(Damage-AbsorbDamage-Resist-BlockedAmount); + data << uint32(Damage-AbsorbDamage-Resist-BlockedAmount);// damage + data << uint32(0); // overkill value data << (uint8)SwingType; // count? // for(i = 0; i < SwingType; ++i) data << (uint32)damageSchoolMask; data << (float)(Damage-AbsorbDamage-Resist-BlockedAmount); - // still need to double check damage data << (uint32)(Damage-AbsorbDamage-Resist-BlockedAmount); - data << (uint32)AbsorbDamage; - data << (uint32)Resist; // end loop - data << (uint32)TargetState; - - if( AbsorbDamage == 0 ) //also 0x3E8 = 0x3E8, check when that happens - data << (uint32)0; - else - data << (uint32)-1; - - data << (uint32)0; - data << (uint32)BlockedAmount; - - SendMessageToSet( &data, true ); -} -/* -void Unit::ProcDamageAndSpell(Unit *pVictim, uint32 procAttacker, uint32 procVictim, uint32 damage, SpellSchoolMask damageSchoolMask, SpellEntry const *procSpell, bool isTriggeredSpell, WeaponAttackType attType) -{ - sLog.outDebug("ProcDamageAndSpell: attacker flags are 0x%x, victim flags 0x%x", procAttacker, procVictim); - if(procSpell) - sLog.outDebug("ProcDamageAndSpell: invoked due to spell id %u %s", procSpell->Id, (isTriggeredSpell?"(triggered)":"")); - - // Assign melee/ranged proc flags for magic attacks, that are actually melee/ranged abilities - // not assign for spell proc triggered spell to prevent infinity (or unexpected 2-3 times) melee damage spell proc call with melee damage effect - // That is the question though if it's fully correct - if(procSpell && !isTriggeredSpell) + if(HitInfo & (HITINFO_ABSORB | HITINFO_ABSORB2)) { - if(procSpell->DmgClass == SPELL_DAMAGE_CLASS_MELEE) - { - if(procAttacker & PROC_FLAG_HIT_SPELL) procAttacker |= PROC_FLAG_HIT_MELEE; - if(procAttacker & PROC_FLAG_CRIT_SPELL) procAttacker |= PROC_FLAG_CRIT_MELEE; - if(procVictim & PROC_FLAG_STRUCK_SPELL) procVictim |= PROC_FLAG_STRUCK_MELEE; - if(procVictim & PROC_FLAG_STRUCK_CRIT_SPELL) procVictim |= PROC_FLAG_STRUCK_CRIT_MELEE; - attType = BASE_ATTACK; // Melee abilities are assumed to be dealt with mainhand weapon - } - else if (procSpell->DmgClass == SPELL_DAMAGE_CLASS_RANGED) - { - if(procAttacker & PROC_FLAG_HIT_SPELL) procAttacker |= PROC_FLAG_HIT_RANGED; - if(procAttacker & PROC_FLAG_CRIT_SPELL) procAttacker |= PROC_FLAG_CRIT_RANGED; - if(procVictim & PROC_FLAG_STRUCK_SPELL) procVictim |= PROC_FLAG_STRUCK_RANGED; - if(procVictim & PROC_FLAG_STRUCK_CRIT_SPELL) procVictim |= PROC_FLAG_STRUCK_CRIT_RANGED; - attType = RANGED_ATTACK; - } + // for(i = 0; i < SwingType; ++i) + data << uint32(AbsorbDamage); + // end loop } - if(damage && (procVictim & (PROC_FLAG_STRUCK_MELEE|PROC_FLAG_STRUCK_RANGED|PROC_FLAG_STRUCK_SPELL))) - procVictim |= (PROC_FLAG_TAKE_DAMAGE|PROC_FLAG_TOUCH); - // Not much to do if no flags are set. - if (procAttacker) + if(HitInfo & (HITINFO_RESIST | HITINFO_RESIST2)) { - // processing auras that not generate casts at proc event before auras that generate casts to prevent proc aura added at prev. proc aura execute in set - ProcDamageAndSpellFor(false,pVictim,procAttacker,attackerProcEffectAuraTypes,attType, procSpell, damage, damageSchoolMask); - ProcDamageAndSpellFor(false,pVictim,procAttacker,attackerProcCastAuraTypes,attType, procSpell, damage, damageSchoolMask); + // for(i = 0; i < SwingType; ++i) + data << uint32(Resist); + // end loop } - // Now go on with a victim's events'n'auras - // Not much to do if no flags are set or there is no victim - if(pVictim && pVictim->isAlive() && procVictim) + data << (uint8)TargetState; + data << (uint32)0; + data << (uint32)0; + + if(HitInfo & HITINFO_BLOCK) { - // processing auras that not generate casts at proc event before auras that generate casts to prevent proc aura added at prev. proc aura execute in set - pVictim->ProcDamageAndSpellFor(true,this,procVictim,victimProcEffectAuraTypes,attType,procSpell, damage, damageSchoolMask); - pVictim->ProcDamageAndSpellFor(true,this,procVictim,victimProcCastAuraTypes,attType,procSpell, damage, damageSchoolMask); + data << uint32(BlockedAmount); } -} - -void Unit::CastMeleeProcDamageAndSpell(Unit* pVictim, uint32 damage, SpellSchoolMask damageSchoolMask, WeaponAttackType attType, MeleeHitOutcome outcome, SpellEntry const *spellCasted, bool isTriggeredSpell) -{ - if(!pVictim) - return; - uint32 procAttacker = PROC_FLAG_NONE; - uint32 procVictim = PROC_FLAG_NONE; - - switch(outcome) + if(HitInfo & HITINFO_UNK3) { - case MELEE_HIT_EVADE: - return; - case MELEE_HIT_MISS: - if(attType == BASE_ATTACK || attType == OFF_ATTACK) - { - procAttacker = PROC_FLAG_MISS; - } - break; - case MELEE_HIT_BLOCK_CRIT: - case MELEE_HIT_CRIT: - if(spellCasted && attType == BASE_ATTACK) - { - procAttacker |= PROC_FLAG_CRIT_SPELL; - procVictim |= PROC_FLAG_STRUCK_CRIT_SPELL; - if ( outcome == MELEE_HIT_BLOCK_CRIT ) - { - procVictim |= PROC_FLAG_BLOCK; - procAttacker |= PROC_FLAG_TARGET_BLOCK; - } - } - else if(attType == BASE_ATTACK || attType == OFF_ATTACK) - { - procAttacker = PROC_FLAG_HIT_MELEE | PROC_FLAG_CRIT_MELEE; - procVictim = PROC_FLAG_STRUCK_MELEE | PROC_FLAG_STRUCK_CRIT_MELEE; - } - else - { - procAttacker = PROC_FLAG_HIT_RANGED | PROC_FLAG_CRIT_RANGED; - procVictim = PROC_FLAG_STRUCK_RANGED | PROC_FLAG_STRUCK_CRIT_RANGED; - } - break; - case MELEE_HIT_PARRY: - procAttacker = PROC_FLAG_TARGET_DODGE_OR_PARRY; - procVictim = PROC_FLAG_PARRY; - break; - case MELEE_HIT_BLOCK: - procAttacker = PROC_FLAG_TARGET_BLOCK; - procVictim = PROC_FLAG_BLOCK; - break; - case MELEE_HIT_DODGE: - procAttacker = PROC_FLAG_TARGET_DODGE_OR_PARRY; - procVictim = PROC_FLAG_DODGE; - break; - case MELEE_HIT_CRUSHING: - if(attType == BASE_ATTACK || attType == OFF_ATTACK) - { - procAttacker = PROC_FLAG_HIT_MELEE | PROC_FLAG_CRIT_MELEE; - procVictim = PROC_FLAG_STRUCK_MELEE | PROC_FLAG_STRUCK_CRIT_MELEE; - } - else - { - procAttacker = PROC_FLAG_HIT_RANGED | PROC_FLAG_CRIT_RANGED; - procVictim = PROC_FLAG_STRUCK_RANGED | PROC_FLAG_STRUCK_CRIT_RANGED; - } - break; - default: - if(attType == BASE_ATTACK || attType == OFF_ATTACK) - { - procAttacker = PROC_FLAG_HIT_MELEE; - procVictim = PROC_FLAG_STRUCK_MELEE; - } - else - { - procAttacker = PROC_FLAG_HIT_RANGED; - procVictim = PROC_FLAG_STRUCK_RANGED; - } - break; + data << uint32(0); } - if(damage > 0) - procVictim |= PROC_FLAG_TAKE_DAMAGE; + if(HitInfo & HITINFO_UNK1) + { + data << uint32(0); + data << float(0); + data << float(0); + data << float(0); + data << float(0); + data << float(0); + data << float(0); + data << float(0); + data << float(0); + for(uint8 i = 0; i < 5; ++i) + { + data << float(0); + data << float(0); + } + data << uint32(0); + } - if(procAttacker != PROC_FLAG_NONE || procVictim != PROC_FLAG_NONE) - ProcDamageAndSpell(pVictim, procAttacker, procVictim, damage, damageSchoolMask, spellCasted, isTriggeredSpell, attType); -}*/ + SendMessageToSet( &data, true ); +} -bool Unit::HandleHasteAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAura, SpellEntry const * procSpell, uint32 /*procFlag*/, uint32 /*procEx*/, uint32 cooldown) +bool Unit::HandleHasteAuraProc(Unit *pVictim, uint32 damage, AuraEffect* triggeredByAura, SpellEntry const * procSpell, uint32 /*procFlag*/, uint32 /*procEx*/, uint32 cooldown) { SpellEntry const *hasteSpell = triggeredByAura->GetSpellProto(); - Item* castItem = triggeredByAura->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER - ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetCastItemGUID()) : NULL; + Item* castItem = triggeredByAura->GetParentAura()->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER + ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetParentAura()->GetCastItemGUID()) : NULL; uint32 triggered_spell_id = 0; Unit* target = pVictim; @@ -4979,34 +4690,59 @@ bool Unit::HandleHasteAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu return true; } -bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAura, SpellEntry const * procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown) +bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* 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->GetAmount(); - Item* castItem = triggeredByAura->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER - ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetCastItemGUID()) : NULL; + Item* castItem = triggeredByAura->GetParentAura()->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER + ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetParentAura()->GetCastItemGUID()) : NULL; uint32 triggered_spell_id = 0; Unit* target = pVictim; int32 basepoints0 = 0; + // Master of subtlety (checked here because ranks have different spellfamilynames) + if (dummySpell->Id == 31223 || dummySpell->Id == 31221 || dummySpell->Id == 31222) + { + if (procEx & AURA_REMOVE_PROC_EX_MASK) + triggered_spell_id = 31666; + else + { + triggered_spell_id = 31665; + basepoints0 = triggerAmount; + } + } switch(dummySpell->SpellFamilyName) { case SPELLFAMILY_GENERIC: { switch (dummySpell->Id) { - // Eye of Eye + // Improved Divine Spirit + case 33174: + case 33182: + { + // Tricky thing here, we find current aura from spell by caster and change its modifier value + int32 spelldmg = CalculateSpellDamage(procSpell, 0, procSpell->EffectBasePoints[0],pVictim); + if (AuraEffect * Aur = pVictim->GetAuraEffect(procSpell->Id, effIndex+1, triggeredByAura->GetCasterGUID())) + { + // Remove aura mods + Aur->ApplyModifier(false); + Aur->SetAmount(Aur->GetAmount() + spelldmg * triggerAmount / 100); + // Apply extended aura mods + Aur->ApplyModifier(true); + return true; + } + return false; + } + // Eye for an Eye case 9799: case 25988: { - // prevent damage back from weapon special attacks - if (!procSpell || procSpell->DmgClass != SPELL_DAMAGE_CLASS_MAGIC ) - 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; @@ -5035,15 +4771,14 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu if (!procSpell || procSpell->Id == 24659) return false; // Need remove one 24659 aura - RemoveSingleAuraFromStack(24659, 0); - RemoveSingleAuraFromStack(24659, 1); + RemoveAuraFromStack(24659); return true; } // Restless Strength case 24661: { // Need remove one 24662 aura - RemoveSingleAuraFromStack(24662, 0); + RemoveAuraFromStack(24662); return true; } // Adaptive Warding (Frostfire Regalia set) @@ -5054,12 +4789,12 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu // find Mage Armor bool found = false; - AuraList const& mRegenInterupt = GetAurasByType(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT); - for(AuraList::const_iterator iter = mRegenInterupt.begin(); iter != mRegenInterupt.end(); ++iter) + AuraEffectList const& mRegenInterupt = GetAurasByType(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT); + for(AuraEffectList::const_iterator iter = mRegenInterupt.begin(); iter != mRegenInterupt.end(); ++iter) { if(SpellEntry const* iterSpellProto = (*iter)->GetSpellProto()) { - if(iterSpellProto->SpellFamilyName==SPELLFAMILY_MAGE && (iterSpellProto->SpellFamilyFlags & 0x10000000)) + if(iterSpellProto->SpellFamilyName==SPELLFAMILY_MAGE && (iterSpellProto->SpellFamilyFlags[0] & 0x10000000)) { found=true; break; @@ -5117,15 +4852,26 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu if(!target) return false; - basepoints0 = int32(damage * 2.5f); // manaregen triggered_spell_id = 34650; break; } + // Overkill + case 58426: + { + if (procEx & AURA_REMOVE_PROC_EX_MASK) + triggered_spell_id = 58428; + else + { + basepoints0 = -triggerAmount; + triggered_spell_id = 58427; + } + break; + } // Mark of Malice case 33493: { // Cast finish spell at last charge - if (triggeredByAura->m_procCharges > 1) + if (triggeredByAura->GetParentAura()->GetAuraCharges() > 1) return false; target = this; @@ -5328,6 +5074,57 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu } return false; } + // Living Seed + case 48504: + { + triggered_spell_id = 48503; + basepoints0 = triggerAmount; + target = this; + break; + } + // Kill command + case 58914: + { + // Remove aura stack from pet + RemoveAuraFromStack(58914); + Unit* owner = GetOwner(); + if(!owner) + return true; + // reduce the owner's aura stack + owner->RemoveAuraFromStack(34027); + return true; + } + // Vampiric Touch (generic, used by some boss) + case 52723: + case 60501: + { + triggered_spell_id = 52724; + basepoints0 = damage / 2; + target = this; + break; + } + // Divine purpose + case 31871: + case 31872: + { + // Roll chane + if (!roll_chance_i(triggerAmount)) + return false; + + // Remove any stun effect on target + AuraMap& Auras = pVictim->GetAuras(); + for(AuraMap::iterator iter = Auras.begin(); iter != Auras.end();) + { + SpellEntry const *spell = iter->second->GetSpellProto(); + if( GetAllSpellMechanicMask(spell) & 1<<(MECHANIC_STUN)) + { + pVictim->RemoveAura(iter); + } + else + ++iter; + } + return true; + } } break; } @@ -5340,7 +5137,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; @@ -5352,7 +5149,8 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu return false; // mana cost save - basepoints0 = procSpell->manaCost * triggeredByAura->GetModifier()->m_amount/100; + int32 cost = procSpell->manaCost + procSpell->ManaCostPercentage * GetCreateMana() / 100; + basepoints0 = cost * triggerAmount/100; if( basepoints0 <=0 ) return false; @@ -5360,8 +5158,53 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu triggered_spell_id = 29077; break; } + // Shattered Barrier + if (dummySpell->SpellIconID == 2945) + { + // only on dispel/remove aura by destroy + target = NULL; + triggered_spell_id = 55080; + CastSpell(target, triggered_spell_id, true); + return true; + } + // Hot Streak + if (dummySpell->SpellIconID == 2999) + { + if (effIndex!=0) + return false; + AuraEffect *counter = GetAuraEffect(triggeredByAura->GetId(), 1); + if (!counter) + return true; + + // Count spell criticals in a row in second aura + if (procEx & PROC_EX_CRITICAL_HIT) + { + counter->SetAmount(counter->GetAmount()*2); + if (counter->GetAmount() < 100) // not enough + return true; + // Crititcal counted -> roll chance + if (roll_chance_i(triggerAmount)) + CastSpell(this, 48108, true, castItem, triggeredByAura); + } + counter->SetAmount(25); + return true; + } + // Burnout + if (dummySpell->SpellIconID == 2998) + { + if(!procSpell) + return false; + + int32 cost = procSpell->manaCost + procSpell->ManaCostPercentage * GetCreateMana() / 100; + basepoints0 = cost * triggerAmount/100; + if( basepoints0 <=0 ) + return false; + triggered_spell_id = 44450; + target = this; + break; + } // Incanter's Regalia set (add trigger chance to Mana Shield) - if (dummySpell->SpellFamilyFlags & 0x0000000000008000LL) + if (dummySpell->SpellFamilyFlags[0] & 0x8000) { if(GetTypeId() != TYPEID_PLAYER) return false; @@ -5398,7 +5241,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu case 11129: { //last charge and crit - if (triggeredByAura->m_procCharges <= 1 && (procEx & PROC_EX_CRITICAL_HIT) ) + if (triggeredByAura->GetParentAura()->GetAuraCharges() <= 1 && (procEx & PROC_EX_CRITICAL_HIT) ) { RemoveAurasDueToSpell(28682); //-> remove Combustion auras return true; // charge counting (will removed) @@ -5413,7 +5256,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu case SPELLFAMILY_WARRIOR: { // Retaliation - if(dummySpell->SpellFamilyFlags==0x0000000800000000LL) + if(dummySpell->SpellFamilyFlags.IsEqual(0, 0x8, 0)) { // check attack comes not from behind if (!HasInArc(M_PI, pVictim)) @@ -5422,26 +5265,51 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu triggered_spell_id = 22858; break; } - else if (dummySpell->SpellIconID == 1697) // Second Wind + // Glyph of Devastate + if(dummySpell->Id==58388) + { + // get highest rank of the Sunder Armor spell + if (GetTypeId()!=TYPEID_PLAYER) + return false; + const PlayerSpellMap& sp_list = ((Player*)this)->GetSpellMap(); + for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr) + { + // only highest rank is shown in spell book, so simply check if shown in spell book + if(!itr->second->active || itr->second->disabled || itr->second->state == PLAYERSPELL_REMOVED) + continue; + + SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first); + if (!spellInfo) + continue; + + if (spellInfo->SpellFamilyFlags.IsEqual(SPELLFAMILYFLAG_WARRIOR_SUNDERARMOR) + && spellInfo->SpellFamilyName == SPELLFAMILY_WARRIOR) + { + triggered_spell_id = spellInfo->Id; + break; + } + } + if (!triggered_spell_id) + return false; + for (int32 value = CalculateSpellDamage(dummySpell, 0 , dummySpell->EffectBasePoints[0], pVictim);value>0;value--) + CastSpell(target,triggered_spell_id,true); + return true; + } + // Second Wind + if (dummySpell->SpellIconID == 1697) { // only for spells and hit/crit (trigger start always) and not start from self casted spells (5530 Mace Stun Effect for example) if (procSpell == 0 || !(procEx & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) || this == pVictim) return false; // Need stun or root mechanic - if (procSpell->Mechanic != MECHANIC_ROOT && procSpell->Mechanic != MECHANIC_STUN) - { - int32 i; - for (i=0; i<3; i++) - if (procSpell->EffectMechanic[i] == MECHANIC_ROOT || procSpell->EffectMechanic[i] == MECHANIC_STUN) - break; - if (i == 3) - return false; - } + if (!(GetAllSpellMechanicMask(procSpell) & ((1<<MECHANIC_ROOT)|(1<<MECHANIC_STUN)))) + return false; switch (dummySpell->Id) { case 29838: triggered_spell_id=29842; break; case 29834: triggered_spell_id=29841; break; + case 42770: triggered_spell_id=42771; break; default: sLog.outError("Unit::HandleDummyAuraProc: non handled spell id: %u (SW)",dummySpell->Id); return false; @@ -5450,16 +5318,23 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu target = this; break; } + // Damage Shield + if (dummySpell->SpellIconID == 3214) + { + triggered_spell_id = 59653; + // % of amount blocked + basepoints0 = GetShieldBlockValue() * triggerAmount / 100; + break; + } break; } case SPELLFAMILY_WARLOCK: { // Seed of Corruption - if (dummySpell->SpellFamilyFlags & 0x0000001000000000LL) + if (dummySpell->SpellFamilyFlags[1] & 0x00000010) { - Modifier* mod = triggeredByAura->GetModifier(); // if damage is more than need or target die from damage deal finish spell - if( mod->m_amount <= damage || GetHealth() <= damage ) + if( triggeredByAura->GetAmount() <= damage || GetHealth() <= damage ) { // remember guid before aura delete uint64 casterGuid = triggeredByAura->GetCasterGUID(); @@ -5474,15 +5349,14 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu } // Damage counting - mod->m_amount-=damage; + triggeredByAura->SetAmount(triggeredByAura->GetAmount() - damage); return true; } // Seed of Corruption (Mobs cast) - no die req - if (dummySpell->SpellFamilyFlags == 0x00LL && dummySpell->SpellIconID == 1932) + if (dummySpell->SpellFamilyFlags.IsEqual(0,0,0) && dummySpell->SpellIconID == 1932) { - Modifier* mod = triggeredByAura->GetModifier(); // if damage is more than need deal finish spell - if( mod->m_amount <= damage ) + if( triggeredByAura->GetAmount() <= damage ) { // remember guid before aura delete uint64 casterGuid = triggeredByAura->GetCasterGUID(); @@ -5496,9 +5370,19 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu return true; // no hidden cooldown } // Damage counting - mod->m_amount-=damage; + triggeredByAura->SetAmount(triggeredByAura->GetAmount() - damage); return true; } + // Fel Synergy + if (dummySpell->SpellIconID == 3222) + { + target = GetGuardianPet(); + if (!target) + return false; + triggered_spell_id = 54181; + basepoints0 = damage * 15 / 100; + break; + } switch(dummySpell->Id) { // Nightfall @@ -5514,12 +5398,45 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu case 30295: case 30296: { + // Improved Soul Leech + AuraEffectList const& SoulLeechAuras = GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraEffectList::const_iterator i = SoulLeechAuras.begin();i != SoulLeechAuras.end(); ++i) + { + if ((*i)->GetId()==54117 || (*i)->GetId()==54118) + { + basepoints0 = int32((*i)->GetAmount()); + if (target = GetGuardianPet()) + { + // regen mana for pet + CastCustomSpell(target,54607,&basepoints0,NULL,NULL,true,castItem,triggeredByAura); + } + // regen mana for caster + CastCustomSpell(this,59117,&basepoints0,NULL,NULL,true,castItem,triggeredByAura); + break; + } + } // health - basepoints0 = int32(damage*triggeredByAura->GetModifier()->m_amount/100); + basepoints0 = int32(damage*triggerAmount/100); target = this; triggered_spell_id = 30294; break; } + // Improved Fear + case 53754: + { + if(!pVictim || !pVictim->isAlive()) + return false; + pVictim->CastSpell(pVictim, 60946,true); + return true; + } + // Improved Fear (Rank 2) + case 53759: + { + if(!pVictim || !pVictim->isAlive()) + return false; + pVictim->CastSpell(pVictim, 60947,true); + return true; + } // Shadowflame (Voidheart Raiment set bonus) case 37377: { @@ -5529,12 +5446,12 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu // Pet Healing (Corruptor Raiment or Rift Stalker Armor) case 37381: { - target = GetPet(); + target = GetGuardianPet(); if(!target) return false; // heal amount - basepoints0 = damage * triggeredByAura->GetModifier()->m_amount/100; + basepoints0 = damage * triggerAmount/100; triggered_spell_id = 37382; break; } @@ -5550,7 +5467,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu case SPELLFAMILY_PRIEST: { // Vampiric Touch - if( dummySpell->SpellFamilyFlags & 0x0000040000000000LL ) + if( dummySpell->SpellFamilyFlags[1] & 0x00000400 ) { if(!pVictim || !pVictim->isAlive()) return false; @@ -5560,10 +5477,17 @@ 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 * triggerAmount/100; + triggered_spell_id = 47753; + break; + } switch(dummySpell->Id) { // Vampiric Embrace @@ -5577,18 +5501,19 @@ 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) case 40438: { // Shadow Word: Pain - if( procSpell->SpellFamilyFlags & 0x0000000000008000LL ) + if( procSpell->SpellFamilyFlags[0] & 0x8000 ) triggered_spell_id = 40441; // Renew - else if( procSpell->SpellFamilyFlags & 0x0000000000000010LL ) + else if( procSpell->SpellFamilyFlags[0] & 0x10 ) triggered_spell_id = 40440; else return false; @@ -5596,6 +5521,39 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu target = this; break; } + // Glyph of Power Word: Shield + case 55672: + { + basepoints0 = damage * triggerAmount/100; + triggered_spell_id = 56160; + break; + } + // Psychic Horror + case 47571: + { + if(!pVictim || !pVictim->isAlive()) + return false; + pVictim->CastSpell(pVictim, 59980,true); + return true; + } + // Psychic Horror (Rank 2) + case 47572: + { + if(!pVictim || !pVictim->isAlive()) + return false; + pVictim->CastSpell(pVictim, 59981,true); + return true; + } + // Glyph of Dispel Magic + case 55677: + { + if(!target->IsFriendlyTo(this)) + return false; + + basepoints0 = int32(target->GetMaxHealth() * triggerAmount / 100); + triggered_spell_id = 56131; + break; + } // Oracle Healing Bonus ("Garments of the Oracle" set) case 26169: { @@ -5612,12 +5570,12 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu return false; // heal amount - basepoints0 = int32(damage * 2 / 100); + basepoints0 = damage * triggerAmount/100; target = this; triggered_spell_id = 39373; break; } - // Vestments of Faith (Priest Tier 3) - 4 pieces bonus + // Greater Heal (Vestments of Faith (Priest Tier 3) - 4 pieces bonus) case 28809: { triggered_spell_id = 28810; @@ -5630,6 +5588,20 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu { switch(dummySpell->Id) { + // Leader of the Pack + case 24932: + { + if (triggerAmount <= 0) + return false; + basepoints0 = triggerAmount * GetMaxHealth() / 100; + target = this; + triggered_spell_id = 34299; + if (triggeredByAura->GetCaster() != this) + break; + int32 basepoints1 = triggerAmount * 2; + CastCustomSpell(this,60889,&basepoints1,0,0,true,0,triggeredByAura); + break; + } // Healing Touch (Dreamwalker Raiment set) case 28719: { @@ -5660,19 +5632,19 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu float chance; // Starfire - if( procSpell->SpellFamilyFlags & 0x0000000000000004LL ) + if( procSpell->SpellFamilyFlags[0] & 0x4 ) { triggered_spell_id = 40445; chance = 25.f; } // Rejuvenation - else if( procSpell->SpellFamilyFlags & 0x0000000000000010LL ) + else if( procSpell->SpellFamilyFlags[0] & 0x10 ) { triggered_spell_id = 40446; chance = 25.f; } // Mangle (cat/bear) - else if( procSpell->SpellFamilyFlags & 0x0000044000000000LL ) + else if( procSpell->SpellFamilyFlags[1] & 0x00000440) { triggered_spell_id = 40452; chance = 40.f; @@ -5694,6 +5666,39 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu break; } } + // Eclipse + if (dummySpell->SpellIconID == 2856) + { + if (!procSpell) + return false; + // Only 0 aura can proc + if (effIndex!=0) + return false; + // Wrath crit + if (procSpell->SpellFamilyFlags[0] & 0x1) + { + if (!roll_chance_i(60)) + return false; + triggered_spell_id = 48518; + target = this; + break; + } + // Starfire crit + if (procSpell->SpellFamilyFlags[0] & 0x4) + { + triggered_spell_id = 48517; + target = this; + break; + } + return false; + } + // Living Seed + else if (dummySpell->SpellIconID == 2860) + { + triggered_spell_id = 48504; + basepoints0 = triggerAmount * damage / 100; + break; + } break; } case SPELLFAMILY_ROGUE: @@ -5711,19 +5716,32 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu break; } } + // Cut to the Chase + if( dummySpell->SpellIconID == 2909 ) + { + // "refresh your Slice and Dice duration to its 5 combo point maximum" + // lookup Slice and Dice + if (AuraEffect * aur = GetAura(SPELL_AURA_MOD_HASTE, SPELLFAMILY_ROGUE,0x40000, 0, 0)) + { + aur->GetParentAura()->SetAuraDuration(GetSpellMaxDuration(aur->GetSpellProto())); + return true; + } + return false; + } + // Deadly Brew + else if( dummySpell->SpellIconID == 2963 ) + { + triggered_spell_id = 25809; + break; + } // Quick Recovery - if( dummySpell->SpellIconID == 2116 ) + else if( dummySpell->SpellIconID == 2116 ) { if(!procSpell) return false; - // only rogue's finishing moves (maybe need additional checks) - if( procSpell->SpellFamilyName!=SPELLFAMILY_ROGUE || - (procSpell->SpellFamilyFlags & SPELLFAMILYFLAG_ROGUE__FINISHING_MOVE) == 0) - return false; - // energy cost save - basepoints0 = procSpell->manaCost * triggeredByAura->GetModifier()->m_amount/100; + basepoints0 = procSpell->manaCost * triggerAmount/100; if(basepoints0 <= 0) return false; @@ -5742,7 +5760,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; @@ -5750,75 +5769,142 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu triggered_spell_id = 34720; break; } + // Hunting Party + if ( dummySpell->SpellIconID == 3406 ) + { + triggered_spell_id = 57669; + target = this; + break; + } + // Lock and Load + if ( dummySpell->SpellIconID == 3579 ) + { + // Proc only from periodic (from trap activation proc another aura of this spell) + if (!(procFlag & PROC_FLAG_ON_DO_PERIODIC) || !roll_chance_i(triggerAmount)) + return false; + triggered_spell_id = 56453; + target = this; + break; + } + // Rapid Recuperation + if ( dummySpell->SpellIconID == 3560 ) + { + // This effect only from Rapid Killing (mana regen) + if (!(procSpell->SpellFamilyFlags[1] & 0x01000000)) + return false; + triggered_spell_id = 56654; + target = this; + break; + } break; } case SPELLFAMILY_PALADIN: { - // Seal of Righteousness - melee proc dummy - if (dummySpell->SpellFamilyFlags&0x000000008000000LL && triggeredByAura->GetEffIndex()==0) + // Seal of Righteousness - melee proc dummy (addition ${$MWS*(0.022*$AP+0.044*$SPH)} damage) + if (dummySpell->SpellFamilyFlags[0]&0x8000000) { - if(GetTypeId() != TYPEID_PLAYER) + if (effIndex!=0) return false; - - uint32 spellId; - switch (triggeredByAura->GetId()) - { - case 21084: spellId = 25742; break; // Rank 1 - case 20287: spellId = 25740; break; // Rank 2 - case 20288: spellId = 25739; break; // Rank 3 - case 20289: spellId = 25738; break; // Rank 4 - case 20290: spellId = 25737; break; // Rank 5 - case 20291: spellId = 25736; break; // Rank 6 - case 20292: spellId = 25735; break; // Rank 7 - case 20293: spellId = 25713; break; // Rank 8 - case 27155: spellId = 27156; break; // Rank 9 - default: - sLog.outError("Unit::HandleDummyAuraProc: non handled possibly SoR (Id = %u)", triggeredByAura->GetId()); - return false; - } - Item *item = ((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); - float speed = (item ? item->GetProto()->Delay : BASE_ATTACK_TIME)/1000.0f; - - float damageBasePoints; - if(item && item->GetProto()->InventoryType == INVTYPE_2HWEAPON) - // two hand weapon - damageBasePoints=1.20f*triggeredByAura->GetModifier()->m_amount * 1.2f * 1.03f * speed/100.0f + 1; - else - // one hand weapon/no weapon - damageBasePoints=0.85f*ceil(triggeredByAura->GetModifier()->m_amount * 1.2f * 1.03f * speed/100.0f) - 1; - - int32 damagePoint = int32(damageBasePoints + 0.03f * (GetWeaponDamageRange(BASE_ATTACK,MINDAMAGE)+GetWeaponDamageRange(BASE_ATTACK,MAXDAMAGE))/2.0f) + 1; - - // apply damage bonuses manually - if(damagePoint >= 0) - damagePoint = SpellDamageBonus(pVictim, dummySpell, damagePoint, SPELL_DIRECT_DAMAGE); - - CastCustomSpell(pVictim,spellId,&damagePoint,NULL,NULL,true,NULL, triggeredByAura); - return true; // no hidden cooldown + triggered_spell_id = 25742; + float ap = GetTotalAttackPowerValue(BASE_ATTACK); + int32 holy = SpellBaseDamageBonus(SPELL_SCHOOL_MASK_HOLY) + + SpellBaseDamageBonusForVictim(SPELL_SCHOOL_MASK_HOLY, pVictim); + basepoints0 = GetAttackTime(BASE_ATTACK) * int32(ap*0.022f + 0.044f * holy) / 1000; + break; } - // Seal of Blood do damage trigger - if(dummySpell->SpellFamilyFlags & 0x0000040000000000LL) + // Light's Beacon - Beacon of Light + if ( dummySpell->Id == 53651 ) { - switch(triggeredByAura->GetEffIndex()) + if (Unit * caster = triggeredByAura->GetCaster()) { - case 0: - triggered_spell_id = 31893; - break; - case 1: + // do not proc when target of beacon of light is healed + if (caster == pVictim) + return false; + if (Aura * aur = caster->GetAura(53563)) { - // damage - damage += CalculateDamage(BASE_ATTACK, false) * 35 / 100; // add spell damage from prev effect (35%) - basepoints0 = triggeredByAura->GetModifier()->m_amount * damage / 100; - - target = this; - triggered_spell_id = 32221; - break; + if (Unit * paladin = aur->GetCaster()) + { + if (paladin != this) + return false; + basepoints0 = damage; + triggered_spell_id = 53654; + target = caster; + break; + } + else + { + pVictim->RemoveAura(triggeredByAura->GetParentAura()); + return false; + } } } + else return false; + } + // Judgements of the Wise + if (dummySpell->SpellIconID == 3017) + { + // hardcoded amount + basepoints0 = 15 * GetCreatePowers(POWER_MANA)/100; + target = this; + triggered_spell_id = 31930; + // replenishment + CastSpell(this,57669,true); + break; + } + // Sanctified Wrath + if (dummySpell->SpellIconID == 3029) + { + triggered_spell_id = 57318; + target = this; + basepoints0 = triggerAmount; + CastCustomSpell(target,triggered_spell_id,&basepoints0,&basepoints0,NULL,true,castItem,triggeredByAura); + return true; + } + // Sacred Shield + if (dummySpell->SpellFamilyFlags[1]&0x00080000) + { + triggered_spell_id = 58597; + target = this; + break; + } + // Righteous Vengeance + if (dummySpell->SpellIconID == 3025) + { + // 4 damage tick + basepoints0 = triggerAmount*damage/400; + triggered_spell_id = 61840; + break; + } + // Sheath of Light + if (dummySpell->SpellIconID == 3030) + { + // 4 healing tick + basepoints0 = triggerAmount*damage/400; + triggered_spell_id = 54203; + break; } - switch(dummySpell->Id) { + // Judgement of Light + case 20185: + { + // Get judgement caster + Unit *caster = triggeredByAura->GetCaster(); + if (!caster) + return false; + float ap = caster->GetTotalAttackPowerValue(BASE_ATTACK); + int32 holy = caster->SpellBaseDamageBonus(SPELL_SCHOOL_MASK_HOLY) + + caster->SpellBaseDamageBonusForVictim(SPELL_SCHOOL_MASK_HOLY, this); + basepoints0 = int32(ap*0.10f + 0.10f*holy); + pVictim->CastCustomSpell(pVictim, 20267, &basepoints0, 0, 0, true, 0, triggeredByAura); + return true; + } + // Judgement of Wisdom + case 20186: + { + pVictim->CastSpell(pVictim, 20268, true, NULL, triggeredByAura); + return true; + } // Holy Power (Redemption Armor set) case 28789: { @@ -5850,7 +5936,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu } break; } - //Seal of Vengeance + // Seal of Vengeance (damage calc on apply aura) case 31801: { if(effIndex != 0) // effect 1,2 used by seal unleashing code @@ -5859,7 +5945,16 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu triggered_spell_id = 31803; break; } - // Spiritual Att. + // Seal of Corruption + case 53736: + { + if(effIndex != 0) // effect 1,2 used by seal unleashing code + return false; + + triggered_spell_id = 53742; + break; + } + // Spiritual Attunement case 31785: case 33776: { @@ -5868,11 +5963,45 @@ 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; } + // Seal of Blood do damage trigger + case 31892: + { + if (effIndex == 0) // 0 effect - is proc on enemy + triggered_spell_id = 31893; + 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 = triggerAmount * damage / 100; + target = this; + triggered_spell_id = 32221; + } + else + return true; + break; + } + // Seal of the Martyr do damage trigger + case 53720: + { + if (effIndex == 0) // 0 effect - is proc on enemy + triggered_spell_id = 53719; + 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 = triggerAmount * damage / 100; + target = this; + triggered_spell_id = 53718; + } + else + return true; + break; + } // Paladin Tier 6 Trinket (Ashtongue Talisman of Zeal) case 40470: { @@ -5882,13 +6011,13 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu float chance; // Flash of light/Holy light - if( procSpell->SpellFamilyFlags & 0x00000000C0000000LL) + if( procSpell->SpellFamilyFlags[0] & 0xC0000000) { triggered_spell_id = 40471; chance = 15.f; } // Judgement - else if( procSpell->SpellFamilyFlags & 0x0000000000800000LL ) + else if( procSpell->SpellFamilyFlags[0] & 0x800000 ) { triggered_spell_id = 40472; chance = 50.f; @@ -5901,6 +6030,33 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu break; } + // Glyph of Divinity + case 54939: + { + // Lookup base amount mana restore + for (int i=0; i<3;i++) + if (procSpell->Effect[i] == SPELL_EFFECT_ENERGIZE) + { + int32 mana = procSpell->EffectBasePoints[i]; + CastCustomSpell(this, 54986, 0, &mana, 0, true, castItem, triggeredByAura); + break; + } + return true; + } + // Glyph of Flash of Light + case 54936: + { + triggered_spell_id = 54957; + basepoints0 = triggerAmount*damage/100; + break; + } + // Glyph of Holy Light + case 54937: + { + triggered_spell_id = 54968; + basepoints0 = triggerAmount*damage/100; + break; + } } break; } @@ -5908,6 +6064,20 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu { switch(dummySpell->Id) { + // Improved fire nova totem + case 16544: + { + triggered_spell_id = 16086; + break; + } + // Tidal Force + case 55198: + { + // Remove aura stack from caster + RemoveAuraFromStack(55166); + // drop charges + return false; + } // Totemic Power (The Earthshatterer set) case 28823: { @@ -5959,14 +6129,19 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu if( cooldown && ((Player*)this)->HasSpellCooldown(dummySpell->Id)) return false; + // Now amount of extra power stored in 1 effect of Enchant spell + // Get it by item enchant id uint32 spellId; switch (castItem->GetEnchantmentId(EnchantmentSlot(TEMP_ENCHANTMENT_SLOT))) { - case 283: spellId = 33757; break; //1 Rank - case 284: spellId = 33756; break; //2 Rank - case 525: spellId = 33755; break; //3 Rank - case 1669:spellId = 33754; break; //4 Rank - case 2636:spellId = 33727; break; //5 Rank + case 283: spellId = 8232; break; // 1 Rank + case 284: spellId = 8235; break; // 2 Rank + case 525: spellId = 10486; break; // 3 Rank + case 1669:spellId = 16362; break; // 4 Rank + case 2636:spellId = 25505; break; // 5 Rank + case 3785:spellId = 58801; break; // 6 Rank + case 3786:spellId = 58803; break; // 7 Rank + case 3787:spellId = 58804; break; // 8 Rank default: { sLog.outError("Unit::HandleDummyAuraProc: non handled item enchantment (rank?) %u for spell id: %u (Windfury)", @@ -5982,7 +6157,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu return false; } - int32 extra_attack_power = CalculateSpellDamage(windfurySpellEntry,0,windfurySpellEntry->EffectBasePoints[0],pVictim); + int32 extra_attack_power = CalculateSpellDamage(windfurySpellEntry, 1, windfurySpellEntry->EffectBasePoints[1], pVictim); // Off-Hand case if ( castItem->GetSlot() == EQUIPMENT_SLOT_OFFHAND ) @@ -6016,17 +6191,17 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu return false; float chance; - if (procSpell->SpellFamilyFlags & 0x0000000000000001LL) + if (procSpell->SpellFamilyFlags[0] & 0x1) { triggered_spell_id = 40465; // Lightning Bolt chance = 15.f; } - else if (procSpell->SpellFamilyFlags & 0x0000000000000080LL) + else if (procSpell->SpellFamilyFlags[0] & 0x80) { triggered_spell_id = 40465; // Lesser Healing Wave chance = 10.f; } - else if (procSpell->SpellFamilyFlags & 0x0000001000000000LL) + else if (procSpell->SpellFamilyFlags[1] & 0x00000010) { triggered_spell_id = 40466; // Stormstrike chance = 50.f; @@ -6040,20 +6215,69 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu target = this; break; } + // Glyph of Healing Wave + case 55440: + { + // Not proc from self heals + if (this==pVictim) + return false; + basepoints0 = triggerAmount * damage / 100; + target = this; + triggered_spell_id = 55533; + break; + } + // Spirit Hunt + case 58877: + { + // Cast on owner + target = GetOwner(); + if(!target) + return false; + basepoints0 = triggerAmount * damage / 100; + triggered_spell_id = 58879; + break; + } + } + // Ancestral Awakening + if (dummySpell->SpellIconID == 3065) + { + // TODO: frite dummy fot triggered spell + triggered_spell_id = 52759; + basepoints0 = triggerAmount * damage / 100; + target = this; + break; } - // Earth Shield - if(dummySpell->SpellFamilyFlags==0x40000000000LL) + if(dummySpell->SpellFamilyFlags[1] & 0x00000400) { - if(GetTypeId() != TYPEID_PLAYER) - return false; - - // heal - basepoints0 = triggeredByAura->GetModifier()->m_amount; + basepoints0 = triggerAmount; target = this; triggered_spell_id = 379; break; } + // Improved Water Shield + if (dummySpell->SpellIconID == 2287) + { + // Lesser Healing Wave need aditional 60% roll + if (procSpell->SpellFamilyFlags[0] & 0x80 && !roll_chance_i(60)) + return false; + // lookup water shield + AuraEffectList const& vs = GetAurasByType(SPELL_AURA_PROC_TRIGGER_SPELL); + for(AuraEffectList::const_iterator itr = vs.begin(); itr != vs.end(); ++itr) + { + if( (*itr)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_SHAMAN && + (*itr)->GetSpellProto()->SpellFamilyFlags[1] & 0x00000020) + { + uint32 spell = (*itr)->GetSpellProto()->EffectTriggerSpell[(*itr)->GetEffIndex()]; + CastSpell(this, spell, true, castItem, triggeredByAura); + if ((*itr)->GetParentAura()->DropAuraCharge()) + RemoveAurasDueToSpell((*itr)->GetId()); + return true; + } + } + return false; + break; + } // Lightning Overload if (dummySpell->SpellIconID == 2018) // only this spell have SpellFamily Shaman SpellIconID == 2018 and dummy aura { @@ -6081,6 +6305,8 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu case 15208: spellId = 45294; break; // Rank 10 case 25448: spellId = 45295; break; // Rank 11 case 25449: spellId = 45296; break; // Rank 12 + case 49237: spellId = 49239; break; // Rank 13 + case 49238: spellId = 49240; break; // Rank 14 // Chain Lightning case 421: spellId = 45297; break; // Rank 1 case 930: spellId = 45298; break; // Rank 2 @@ -6088,40 +6314,148 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu case 10605: spellId = 45300; break; // Rank 4 case 25439: spellId = 45301; break; // Rank 5 case 25442: spellId = 45302; break; // Rank 6 + case 49268: spellId = 49270; break; // Rank 7 + case 49269: spellId = 49271; break; // Rank 8 default: sLog.outError("Unit::HandleDummyAuraProc: non handled spell id: %u (LO)", procSpell->Id); return false; } - // No thread generated mod - SpellModifier *mod = new SpellModifier; - mod->op = SPELLMOD_THREAT; - mod->value = -100; - mod->type = SPELLMOD_PCT; - mod->spellId = dummySpell->Id; - mod->effectId = 0; - mod->lastAffected = NULL; - mod->mask = 0x0000000000000003LL; - mod->charges = 0; - ((Player*)this)->AddSpellMod(mod, true); - - // Remove cooldown (Chain Lightning - have Category Recovery time) - if (procSpell->SpellFamilyFlags & 0x0000000000000002LL) - ((Player*)this)->RemoveSpellCooldown(spellId); - - // Hmmm.. in most case spells already set half basepoints but... - // Lightning Bolt (2-10 rank) have full basepoint and half bonus from level - // As on wiki: - // BUG: Rank 2 to 10 (and maybe 11) of Lightning Bolt will proc another Bolt with FULL damage (not halved). This bug is known and will probably be fixed soon. - // So - no add changes :) - CastSpell(pVictim, spellId, true, castItem, triggeredByAura); - - ((Player*)this)->AddSpellMod(mod, false); + // Apply spellmod + CastSpell(this, 39805, true, castItem, triggeredByAura); - if( cooldown && GetTypeId()==TYPEID_PLAYER ) - ((Player*)this)->AddSpellCooldown(dummySpell->Id,0,time(NULL) + cooldown); + CastSpell(pVictim, spellId, true, castItem, triggeredByAura); return true; } + // Static Shock + if(dummySpell->SpellIconID == 3059) + { + // lookup Lightning Shield + AuraEffectList const& vs = GetAurasByType(SPELL_AURA_PROC_TRIGGER_SPELL); + for(AuraEffectList::const_iterator itr = vs.begin(); itr != vs.end(); ++itr) + { + if( (*itr)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_SHAMAN && + (*itr)->GetSpellProto()->SpellFamilyFlags[0] & 0x400) + { + uint32 spell = 0; + switch ((*itr)->GetId()) + { + case 324: spell = 26364; break; + case 325: spell = 26365; break; + case 905: spell = 26366; break; + case 945: spell = 26367; break; + case 8134: spell = 26369; break; + case 10431: spell = 26370; break; + case 10432: spell = 26363; break; + case 25469: spell = 26371; break; + case 25472: spell = 26372; break; + case 49280: spell = 49278; break; + case 49281: spell = 49279; break; + default: + return false; + } + CastSpell(this, spell, true, castItem, triggeredByAura); + if ((*itr)->GetParentAura()->DropAuraCharge()) + RemoveAurasDueToSpell((*itr)->GetId()); + return true; + } + } + return false; + break; + } + break; + } + case SPELLFAMILY_DEATHKNIGHT: + { + // Blood Aura + if (dummySpell->SpellIconID == 2636) + { + if (GetTypeId() != TYPEID_PLAYER || !((Player*)this)->isHonorOrXPTarget(pVictim)) + return false; + basepoints0 = triggerAmount * damage / 100; + triggered_spell_id = 53168; + break; + } + // Butchery + if (dummySpell->SpellIconID == 2664) + { + basepoints0 = triggerAmount; + triggered_spell_id = 50163; + target = this; + break; + } + // Dancing Rune Weapon + if (dummySpell->Id == 49028) + { + // 1 dummy aura for dismiss rune blade + 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 + return false; + } + // Mark of Blood + if (dummySpell->Id == 49005) + { + // TODO: need more info (cooldowns/PPM) + triggered_spell_id = 50424; + break; + } + // Vendetta + if (dummySpell->SpellFamilyFlags[0] & 0x10000) + { + basepoints0 = triggerAmount * GetMaxHealth() / 100; + triggered_spell_id = 50181; + target = this; + break; + } + // Necrosis + if (dummySpell->SpellIconID == 2709) + { + basepoints0 = triggerAmount * damage / 100; + triggered_spell_id = 51460; + break; + } + // Runic Power Back on Snare/Root + if (dummySpell->Id == 61257) + { + // only for spells and hit/crit (trigger start always) and not start from self casted spells + if (procSpell == 0 || !(procEx & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) || this == pVictim) + return false; + // Need snare or root mechanic + if (!(GetAllSpellMechanicMask(procSpell) & ((1<<MECHANIC_ROOT)|(1<<MECHANIC_SNARE)))) + return false; + triggered_spell_id = 61258; + target = this; + break; + } + // Wandering Plague + if (dummySpell->SpellIconID == 1614) + { + if (!roll_chance_f(GetUnitCriticalChance(BASE_ATTACK, pVictim))) + return false; + basepoints0 = triggerAmount * damage / 100; + triggered_spell_id = 50526; + break; + } + // Death Strike healing effect + if (dummySpell->Id == 45469) + { + uint8 n=0; + Unit::AuraEffectList const& decSpeedList = pVictim->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE); + for(Unit::AuraEffectList::const_iterator iter = decSpeedList.begin(); iter != decSpeedList.end(); ++iter) + { + if((*iter)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_DEATHKNIGHT + && (*iter)->GetCasterGUID() == GetGUID() + && (*iter)->GetSpellProto()->Dispel == DISPEL_DISEASE) + { + n++; + } + } + int32 heal=0.5f*n*damage+damage; + CastCustomSpell(this,45470,&heal,NULL,NULL,true); + return true; + } break; } case SPELLFAMILY_POTION: @@ -6140,13 +6474,35 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu { triggered_spell_id = 21400; } - else continue; + else + continue; + basepoints0 = CalculateSpellDamage(procSpell,i,procSpell->EffectBasePoints[i],this) * 0.4f; CastCustomSpell(this,triggered_spell_id,&basepoints0,NULL,NULL,true,castItem,triggeredByAura); } return true; } } + break; + } + case SPELLFAMILY_PET: + { + // improved cower + if (dummySpell->SpellIconID == 958 && procSpell->SpellIconID == 958) + { + triggered_spell_id = dummySpell->Id == 53180 ? 54200 : 54201; + target = this; + break; + } + // guard dog + if (dummySpell->SpellIconID == 201 && procSpell->SpellIconID == 201) + { + triggered_spell_id = 54445; + target = this; + pVictim->AddThreat(this,procSpell->EffectBasePoints[0]*triggerAmount/100); + break; + } + break; } default: break; @@ -6181,1172 +6537,570 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu return true; } -/* -bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, Aura* triggeredByAura, SpellEntry const *procSpell, uint32 procFlags,WeaponAttackType attackType, uint32 cooldown) + +bool Unit::HandleObsModEnergyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* triggeredByAura, SpellEntry const * procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown) { - SpellEntry const* auraSpellInfo = triggeredByAura->GetSpellProto(); + SpellEntry const *dummySpell = triggeredByAura->GetSpellProto (); + uint32 effIndex = triggeredByAura->GetEffIndex(); + int32 triggerAmount = triggeredByAura->GetAmount(); - Item* castItem = triggeredByAura->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER - ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetCastItemGUID()) : NULL; + Item* castItem = triggeredByAura->GetParentAura()->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER + ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetParentAura()->GetCastItemGUID()) : NULL; - uint32 triggered_spell_id = auraSpellInfo->EffectTriggerSpell[triggeredByAura->GetEffIndex()]; - Unit* target = !(procFlags & PROC_FLAG_HEAL) && IsPositiveSpell(triggered_spell_id) ? this : pVictim; + uint32 triggered_spell_id = 0; + Unit* target = pVictim; int32 basepoints0 = 0; - switch(auraSpellInfo->SpellFamilyName) + switch(dummySpell->SpellFamilyName) { - case SPELLFAMILY_GENERIC: + case SPELLFAMILY_HUNTER: { - switch(auraSpellInfo->Id) + // Aspect of the Viper + if ( dummySpell->SpellFamilyFlags[1] & 0x40000 ) { - // Aegis of Preservation - case 23780: - //Aegis Heal (instead non-existed triggered spell) - triggered_spell_id = 23781; - target = this; - break; - // Elune's Touch (moonkin mana restore) - case 24905: - { - // Elune's Touch (instead non-existed triggered spell) - triggered_spell_id = 33926; - basepoints0 = int32(0.3f * GetTotalAttackPowerValue(BASE_ATTACK)); - target = this; + uint32 maxmana = GetMaxPower(POWER_MANA); + basepoints0 = maxmana* GetAttackTime(RANGED_ATTACK)/1000.0f/100.0f; + + target = this; + triggered_spell_id = 34075; + break; + } + break; + } + } + // processed charge only counting case + if(!triggered_spell_id) + return true; + + SpellEntry const* triggerEntry = sSpellStore.LookupEntry(triggered_spell_id); + + if(!triggerEntry) + { + sLog.outError("Unit::HandleDummyAuraProc: Spell %u have not existed triggered spell %u",dummySpell->Id,triggered_spell_id); + return false; + } + + // default case + if(!target || target!=this && !target->isAlive()) + return false; + + if( cooldown && GetTypeId()==TYPEID_PLAYER && ((Player*)this)->HasSpellCooldown(triggered_spell_id)) + return false; + + if(basepoints0) + CastCustomSpell(target,triggered_spell_id,&basepoints0,NULL,NULL,true,castItem,triggeredByAura); + else + CastSpell(target,triggered_spell_id,true,castItem,triggeredByAura); + + if( cooldown && GetTypeId()==TYPEID_PLAYER ) + ((Player*)this)->AddSpellCooldown(triggered_spell_id,0,time(NULL) + cooldown); + + return true; +} + +bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* triggeredByAura, SpellEntry const *procSpell, uint32 procFlags, uint32 procEx, uint32 cooldown) +{ + // Get triggered aura spell info + SpellEntry const* auraSpellInfo = triggeredByAura->GetSpellProto(); + + // Basepoints of trigger aura + int32 triggerAmount = triggeredByAura->GetAmount(); + + // Set trigger spell id, target, custom basepoints + uint32 trigger_spell_id = auraSpellInfo->EffectTriggerSpell[triggeredByAura->GetEffIndex()]; + Unit* target = NULL; + int32 basepoints0 = 0; + + if(triggeredByAura->GetAuraName() == SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE) + basepoints0 = triggerAmount; + + Item* castItem = triggeredByAura->GetParentAura()->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER + ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetParentAura()->GetCastItemGUID()) : NULL; + + // Try handle unknown trigger spells + if (sSpellStore.LookupEntry(trigger_spell_id)==NULL) + { + switch (auraSpellInfo->SpellFamilyName) + { + case SPELLFAMILY_GENERIC: + //if (auraSpellInfo->Id==59532) // Abandon Passengers on Poly + //if (auraSpellInfo->Id==54775) // Abandon Vehicle on Poly + //if (auraSpellInfo->Id==34082) // Advantaged State (DND) + if (auraSpellInfo->Id == 23780) // Aegis of Preservation (Aegis of Preservation trinket) + trigger_spell_id = 23781; + //else if (auraSpellInfo->Id==43504) // Alterac Valley OnKill Proc Aura + //else if (auraSpellInfo->Id == 48876) // Beast's Mark + //{ + // trigger_spell_id = 48877; + //} + //else if (auraSpellInfo->Id == 59237) // Beast's Mark + //{ + // trigger_spell_id = 59233; + //} + //else if (auraSpellInfo->Id==46939) // Black Bow of the Betrayer + //{ + // trigger_spell_id = 29471; // gain mana + // 27526; // drain mana if possible + //} + //else if (auraSpellInfo->Id==50844) // Blood Mirror + //else if (auraSpellInfo->Id==54476) // Blood Presence + //else if (auraSpellInfo->Id==50689) // Blood Presence (Rank 1) + //else if (auraSpellInfo->Id==37030) // Chaotic Temperament + //else if (auraSpellInfo->Id==52856) // Charge + else if (auraSpellInfo->Id==43820) // Charm of the Witch Doctor (Amani Charm of the Witch Doctor trinket) + { + // Pct value stored in dummy + basepoints0 = pVictim->GetCreateHealth() * auraSpellInfo->EffectBasePoints[1] / 100; + target = pVictim; break; } - // Enlightenment - case 29601: - { - // only for cast with mana price - if(!procSpell || procSpell->powerType!=POWER_MANA || procSpell->manaCost==0 && procSpell->ManaCostPercentage==0 && procSpell->manaCostPerlevel==0) - return false; - break; // fall through to normal cast + //else if (auraSpellInfo->Id==41248) // Consuming Strikes + // trigger_spell_id = 41249; + //else if (auraSpellInfo->Id==45205) // Copy Offhand Weapon + //else if (auraSpellInfo->Id==57594) // Copy Ranged Weapon + //else if (auraSpellInfo->Id==41054) // Copy Weapon + // trigger_spell_id = 41055; + //else if (auraSpellInfo->Id==45343) // Dark Flame Aura + //else if (auraSpellInfo->Id==47300) // Dark Flame Aura + else if (auraSpellInfo->Id==57345) // Darkmoon Card: Greatness + { + float stat = 0.0f; + // strength + if (GetStat(STAT_STRENGTH) > stat) { trigger_spell_id = 60229;stat = GetStat(STAT_STRENGTH); } + // agility + if (GetStat(STAT_AGILITY) > stat) { trigger_spell_id = 60233;stat = GetStat(STAT_AGILITY); } + // intellect + if (GetStat(STAT_INTELLECT)> stat) { trigger_spell_id = 60234;stat = GetStat(STAT_INTELLECT);} + // spirit + if (GetStat(STAT_SPIRIT) > stat) { trigger_spell_id = 60235;stat = GetStat(STAT_SPIRIT); } } - // Health Restore - case 33510: + //else if (auraSpellInfo->Id==31255) // Deadly Swiftness (Rank 1) + //else if (auraSpellInfo->Id==5301) // Defensive State (DND) + //else if (auraSpellInfo->Id==13358) // Defensive State (DND) + //else if (auraSpellInfo->Id==16092) // Defensive State (DND) + //else if (auraSpellInfo->Id==24949) // Defensive State 2 (DND) + //else if (auraSpellInfo->Id==40329) // Demo Shout Sensor + else if (auraSpellInfo->Id == 33896) // Desperate Defense (Stonescythe Whelp, Stonescythe Alpha, Stonescythe Ambusher) + trigger_spell_id = 33898; + //else if (auraSpellInfo->Id==18943) // Double Attack + //else if (auraSpellInfo->Id==19194) // Double Attack + //else if (auraSpellInfo->Id==19817) // Double Attack + //else if (auraSpellInfo->Id==19818) // Double Attack + //else if (auraSpellInfo->Id==22835) // Drunken Rage + // trigger_spell_id = 14822; + /* + else if (auraSpellInfo->SpellIconID==191) // Elemental Response { - // at melee hit call std triggered spell - if(procFlags & PROC_FLAG_HIT_MELEE) - break; // fall through to normal cast - - // Mark of Conquest - else (at range hit) called custom case - triggered_spell_id = 39557; - target = this; - break; + switch (auraSpellInfo->Id && auraSpellInfo->AttributesEx==0) + { + case 34191: + case 34329: + case 34524: + case 34582: + case 36733: + break; + default: + sLog.outError("Unit::HandleProcTriggerSpell: Spell %u miss posibly Elemental Response",auraSpellInfo->Id); + return false; + } + //This generic aura self-triggers a different spell for each school of magic that lands on the wearer: + switch (procSpell->School) + { + case SPELL_SCHOOL_FIRE: trigger_spell_id = 34192; break; + case SPELL_SCHOOL_FROST: trigger_spell_id = 34193; break; + case SPELL_SCHOOL_ARCANE:trigger_spell_id = 34194; break; + case SPELL_SCHOOL_NATURE:trigger_spell_id = 34195; break; + case SPELL_SCHOOL_SHADOW:trigger_spell_id = 34196; break; + case SPELL_SCHOOL_HOLY: trigger_spell_id = 34197; break; + case SPELL_SCHOOL_NORMAL:trigger_spell_id = 34198; break; + default: + sLog.outError("Unit::HandleProcTriggerSpell: Spell %u Elemental Response wrong school",auraSpellInfo->Id); + return false; + } } - // Shaleskin - case 36576: - return true; // nothing to do - // Forgotten Knowledge (Blade of Wizardry) - case 38319: - // only for harmful enemy targeted spell - if(!pVictim || pVictim==this || !procSpell || IsPositiveSpell(procSpell->Id)) - return false; - break; // fall through to normal cast - // Aura of Wrath (Darkmoon Card: Wrath trinket bonus) - case 39442: - { - // proc only at non-crit hits - if(procFlags & (PROC_FLAG_CRIT_MELEE|PROC_FLAG_CRIT_RANGED|PROC_FLAG_CRIT_SPELL)) - return false; - break; // fall through to normal cast + */ + //else if (auraSpellInfo->Id==40364) // Entangling Roots Sensor + //else if (auraSpellInfo->Id==33207) // Gossip NPC Periodic - Fidget + //else if (auraSpellInfo->Id==50051) // Ethereal Pet Aura + //else if (auraSpellInfo->Id==35321) // Gushing Wound + //else if (auraSpellInfo->Id==38363) // Gushing Wound + //else if (auraSpellInfo->Id==39215) // Gushing Wound + //else if (auraSpellInfo->Id==44527) // Hate Monster (Spar Buddy) (30 sec) + //else if (auraSpellInfo->Id==44819) // Hate Monster (Spar Buddy) (>30% Health) + //else if (auraSpellInfo->Id==44526) // Hate Monster (Spar) (30 sec) + //else if (auraSpellInfo->Id==44820) // Hate Monster (Spar) (<30%) + //else if (auraSpellInfo->Id==49059) // Horde, Hate Monster (Spar Buddy) (>30% Health) + //else if (auraSpellInfo->Id==40250) // Improved Duration + //else if (auraSpellInfo->Id==59288) // Infra-Green Shield + //else if (auraSpellInfo->Id==54072) // Knockback Ball Passive + else if (auraSpellInfo->Id==27522 || auraSpellInfo->Id==40336) + // Mana Drain Trigger + { + // On successful melee or ranged attack gain $29471s1 mana and if possible drain $27526s1 mana from the target. + if (this && this->isAlive()) + CastSpell(this, 29471, true, castItem, triggeredByAura); + if (pVictim && pVictim->isAlive()) + CastSpell(pVictim, 27526, true, castItem, triggeredByAura); + return true; } - // Augment Pain (Timbal's Focusing Crystal trinket bonus) - case 45054: + //else if (auraSpellInfo->Id==55580) // Mana Link + //else if (auraSpellInfo->Id==45903) // Offensive State + //else if (auraSpellInfo->Id==44326) // Pure Energy Passive + //else if (auraSpellInfo->Id==43453) // Rune Ward + //else if (auraSpellInfo->Id== 7137) // Shadow Charge (Rank 1) + //else if (auraSpellInfo->Id==36576) // Shaleskin (Shaleskin Flayer, Shaleskin Ripper) 30023 trigger + //else if (auraSpellInfo->Id==34783) // Spell Reflection + //else if (auraSpellInfo->Id==36096) // Spell Reflection + //else if (auraSpellInfo->Id==57587) // Steal Ranged () + //else if (auraSpellInfo->Id==36207) // Steal Weapon + //else if (auraSpellInfo->Id== 7377) // Take Immune Periodic Damage <Not Working> + //else if (auraSpellInfo->Id==35205) // Vanish + //else if (auraSpellInfo->Id==42730) // Woe Strike + //else if (auraSpellInfo->Id==59735) // Woe Strike + //else if (auraSpellInfo->Id==46146) // [PH] Ahune Spanky Hands + break; + case SPELLFAMILY_MAGE: + if (auraSpellInfo->SpellIconID == 2127) // Blazing Speed { - if(!procSpell) - return false; - - //only periodic damage can trigger spell - bool found = false; - for(int j = 0; j < 3; ++j) + switch (auraSpellInfo->Id) { - if( procSpell->EffectApplyAuraName[j]==SPELL_AURA_PERIODIC_DAMAGE || - procSpell->EffectApplyAuraName[j]==SPELL_AURA_PERIODIC_DAMAGE_PERCENT || - procSpell->EffectApplyAuraName[j]==SPELL_AURA_PERIODIC_LEECH ) - { - found = true; + case 31641: // Rank 1 + case 31642: // Rank 2 + trigger_spell_id = 31643; break; - } + default: + sLog.outError("Unit::HandleProcTriggerSpell: Spell %u miss posibly Blazing Speed",auraSpellInfo->Id); + return false; } - if(!found) - return false; - - break; // fall through to normal cast } - // Evasive Maneuvers (Commendation of Kael'thas) - case 45057: + break; + case SPELLFAMILY_WARRIOR: + if (auraSpellInfo->Id == 50421) // Scent of Blood + trigger_spell_id = 50422; + break; + case SPELLFAMILY_WARLOCK: + { + // Pyroclasm + if (auraSpellInfo->SpellIconID == 1137) { - // damage taken that reduces below 35% health - // does NOT mean you must have been >= 35% before - if (int32(GetHealth())-int32(damage) >= int32(GetMaxHealth()*0.35f)) + if(!pVictim || !pVictim->isAlive() || pVictim == this || procSpell == NULL) + return false; + // Calculate spell tick count for spells + uint32 tick = 1; // Default tick = 1 + + // Hellfire have 15 tick + if (procSpell->SpellFamilyFlags[0]&0x40) + tick = 15; + // Rain of Fire have 4 tick + else if (procSpell->SpellFamilyFlags[0]&0x20) + tick = 4; + else return false; - break; // fall through to normal cast - } - } - switch(triggered_spell_id) - { - // Setup - case 15250: - { - // applied only for main target - if(!pVictim || pVictim != getVictim()) + // Calculate chance = baseChance / tick + float chance = 0; + switch (auraSpellInfo->Id) + { + case 18096: chance = 13.0f / tick; break; + case 18073: chance = 26.0f / tick; break; + } + // Roll chance + if (!roll_chance_f(chance)) return false; - // continue normal case - break; + trigger_spell_id = 18093; } - // Shamanistic Rage triggered spell - case 30824: - basepoints0 = int32(GetTotalAttackPowerValue(BASE_ATTACK)*triggeredByAura->GetModifier()->m_amount/100); - break; - } - break; - } - case SPELLFAMILY_MAGE: - { - switch(auraSpellInfo->SpellIconID) - { - // Blazing Speed - case 2127: - //Blazing Speed (instead non-existed triggered spell) - triggered_spell_id = 31643; - target = this; - break; - } - switch(auraSpellInfo->Id) - { - // Persistent Shield (Scarab Brooch) - case 26467: - basepoints0 = int32(damage * 0.15f); - break; - } - break; - } - case SPELLFAMILY_WARRIOR: - { - //Rampage - if((auraSpellInfo->SpellFamilyFlags & 0x100000) && auraSpellInfo->SpellIconID==2006) - { - //all ranks have effect[0]==AURA (Proc Trigger Spell, non-existed) - //and effect[1]==TriggerSpell - if(auraSpellInfo->Effect[1]!=SPELL_EFFECT_TRIGGER_SPELL) + // Drain Soul + else if (auraSpellInfo->SpellFamilyFlags[0] & 0x4000) { - sLog.outError("Unit::HandleProcTriggerSpell: Spell %u have wrong effect in RM",triggeredByAura->GetSpellProto()->Id); - return false; - } - triggered_spell_id = auraSpellInfo->EffectTriggerSpell[1]; - break; // fall through to normal cast - } - break; - } - case SPELLFAMILY_WARLOCK: - { - // Pyroclasm - if(auraSpellInfo->SpellFamilyFlags == 0x0000000000000000 && auraSpellInfo->SpellIconID==1137) - { - // last case for Hellfire that damage caster also but don't must stun caster - if( pVictim == this ) + Unit::AuraEffectList const& mAddFlatModifier = GetAurasByType(SPELL_AURA_ADD_FLAT_MODIFIER); + for(Unit::AuraEffectList::const_iterator i = mAddFlatModifier.begin(); i != mAddFlatModifier.end(); ++i) + { + if ((*i)->GetMiscValue() == SPELLMOD_CHANCE_OF_SUCCESS && (*i)->GetSpellProto()->SpellIconID == 113) + { + int32 value2 = CalculateSpellDamage((*i)->GetSpellProto(),2,(*i)->GetSpellProto()->EffectBasePoints[2],this); + // Drain Soul + CastCustomSpell(this, 18371, &basepoints0, NULL, NULL, true, castItem, triggeredByAura); + break; + } + } + // Not remove charge (aura removed on death in any cases) + // Need for correct work Drain Soul SPELL_AURA_CHANNEL_DEATH_ITEM aura return false; - - // custom chance - float chance = 0; - switch (triggeredByAura->GetId()) - { - case 18096: chance = 13.0f; break; - case 18073: chance = 26.0f; break; } - if (!roll_chance_f(chance)) - return false; - - // Pyroclasm (instead non-existed triggered spell) - triggered_spell_id = 18093; - target = pVictim; - break; - } - // Drain Soul - if(auraSpellInfo->SpellFamilyFlags & 0x0000000000004000) - { - bool found = false; - Unit::AuraList const& mAddFlatModifier = GetAurasByType(SPELL_AURA_ADD_FLAT_MODIFIER); - for(Unit::AuraList::const_iterator i = mAddFlatModifier.begin(); i != mAddFlatModifier.end(); ++i) + // Nether Protection + else if (auraSpellInfo->SpellIconID == 1985) { - //Improved Drain Soul - if ((*i)->GetModifier()->m_miscvalue == SPELLMOD_CHANCE_OF_SUCCESS && (*i)->GetSpellProto()->SpellIconID == 113) + if (!procSpell) + return false; + switch(GetFirstSchoolInMask(GetSpellSchoolMask(procSpell))) { - int32 value2 = CalculateSpellDamage((*i)->GetSpellProto(),2,(*i)->GetSpellProto()->EffectBasePoints[2],this); - basepoints0 = value2 * GetMaxPower(POWER_MANA) / 100; - // Drain Soul - CastCustomSpell(this, 18371, &basepoints0, NULL, NULL, true, castItem, triggeredByAura); - break; + case SPELL_SCHOOL_NORMAL: + case SPELL_SCHOOL_HOLY: + return false; // ignore + case SPELL_SCHOOL_FIRE: trigger_spell_id = 54371; break; + case SPELL_SCHOOL_NATURE: trigger_spell_id = 54375; break; + case SPELL_SCHOOL_FROST: trigger_spell_id = 54372; break; + case SPELL_SCHOOL_SHADOW: trigger_spell_id = 54374; break; + case SPELL_SCHOOL_ARCANE: trigger_spell_id = 54373; break; + default: + return false; } } - // Not remove charge (aura removed on death in any cases) - // Need for correct work Drain Soul SPELL_AURA_CHANNEL_DEATH_ITEM aura - return false; + break; } - break; - } - case SPELLFAMILY_PRIEST: - { - //Blessed Recovery - if(auraSpellInfo->SpellFamilyFlags == 0x00000000LL && auraSpellInfo->SpellIconID==1875) + case SPELLFAMILY_PRIEST: { - switch (triggeredByAura->GetSpellProto()->Id) + // Greater Heal Refund + if (auraSpellInfo->Id==37594) + trigger_spell_id = 37595; + // Blessed Recovery + else if (auraSpellInfo->SpellIconID == 1875) { - case 27811: triggered_spell_id = 27813; break; - case 27815: triggered_spell_id = 27817; break; - case 27816: triggered_spell_id = 27818; break; - default: - sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in BR",triggeredByAura->GetSpellProto()->Id); - return false; - } - - int32 heal_amount = damage * triggeredByAura->GetModifier()->m_amount / 100; - basepoints0 = heal_amount/3; - target = this; - break; - } - // Shadowguard - if((auraSpellInfo->SpellFamilyFlags & 0x80000000LL) && auraSpellInfo->SpellVisual==7958) - { - switch(triggeredByAura->GetSpellProto()->Id) - { - case 18137: - triggered_spell_id = 28377; break; // Rank 1 - case 19308: - triggered_spell_id = 28378; break; // Rank 2 - case 19309: - triggered_spell_id = 28379; break; // Rank 3 - case 19310: - triggered_spell_id = 28380; break; // Rank 4 - case 19311: - triggered_spell_id = 28381; break; // Rank 5 - case 19312: - triggered_spell_id = 28382; break; // Rank 6 - case 25477: - triggered_spell_id = 28385; break; // Rank 7 - default: - sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in SG",triggeredByAura->GetSpellProto()->Id); + switch (auraSpellInfo->Id) + { + case 27811: trigger_spell_id = 27813; break; + case 27815: trigger_spell_id = 27817; break; + case 27816: trigger_spell_id = 27818; break; + default: + sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in BR", auraSpellInfo->Id); return false; + } + basepoints0 = damage * triggerAmount / 100 / 3; + target = this; } - target = pVictim; break; } - break; - } - case SPELLFAMILY_DRUID: - { - switch(auraSpellInfo->Id) + case SPELLFAMILY_DRUID: { - // Leader of the Pack (triggering Improved Leader of the Pack heal) - case 24932: - { - if (triggeredByAura->GetModifier()->m_amount == 0) - return false; - basepoints0 = triggeredByAura->GetModifier()->m_amount * GetMaxHealth() / 100; - triggered_spell_id = 34299; - break; - }; - // Druid Forms Trinket (Druid Tier5 Trinket, triggers different spells per Form) - case 37336: + // Druid Forms Trinket + if (auraSpellInfo->Id==37336) { switch(m_form) { + case FORM_NONE: trigger_spell_id = 37344;break; + case FORM_CAT: trigger_spell_id = 37341;break; case FORM_BEAR: - case FORM_DIREBEAR: - triggered_spell_id=37340; break;// Ursine Blessing - case FORM_CAT: - triggered_spell_id=37341; break;// Feline Blessing - case FORM_TREE: - triggered_spell_id=37342; break;// Slyvan Blessing - case FORM_MOONKIN: - triggered_spell_id=37343; break;// Lunar Blessing - case FORM_NONE: - triggered_spell_id=37344; break;// Cenarion Blessing (for caster form, except FORM_MOONKIN) + case FORM_DIREBEAR: trigger_spell_id = 37340;break; + case FORM_TREE: trigger_spell_id = 37342;break; + case FORM_MOONKIN: trigger_spell_id = 37343;break; default: return false; } - - target = this; - break; } + //else if (auraSpellInfo->Id==40363)// Entangling Roots () + // trigger_spell_id = ????; + break; } - break; - } - case SPELLFAMILY_ROGUE: - { - if(auraSpellInfo->SpellFamilyFlags == 0x0000000000000000LL) + case SPELLFAMILY_HUNTER: + break; + case SPELLFAMILY_PALADIN: { - switch(auraSpellInfo->SpellIconID) + /* + // Blessed Life + if (auraSpellInfo->SpellIconID == 2137) { - // Combat Potency - case 2260: + switch (auraSpellInfo->Id) { - // skip non offhand attacks - if(attackType!=OFF_ATTACK) + case 31828: // Rank 1 + case 31829: // Rank 2 + case 31830: // Rank 3 + break; + default: + sLog.outError("Unit::HandleProcTriggerSpell: Spell %u miss posibly Blessed Life", auraSpellInfo->Id); return false; - break; // fall through to normal cast } } - } - break; - } - case SPELLFAMILY_PALADIN: - { - if(auraSpellInfo->SpellFamilyFlags == 0x00000000LL) - { - switch(auraSpellInfo->Id) + */ + // Healing Discount + if (auraSpellInfo->Id==37705) { - // Lightning Capacitor - case 37657: - { - // trinket ProcTriggerSpell but for safe checks for player - if(!castItem || !pVictim || !pVictim->isAlive() || GetTypeId()!=TYPEID_PLAYER) - return false; - - if(((Player*)this)->HasSpellCooldown(37657)) - return false; - - // stacking - CastSpell(this, 37658, true, castItem, triggeredByAura); - // 2.5s cooldown before it can stack again, current system allow 1 sec step in cooldown - ((Player*)this)->AddSpellCooldown(37657,0,time(NULL)+(roll_chance_i(50) ? 2 : 3)); - - // counting - Aura * dummy = GetDummyAura(37658); - if (!dummy) - return false; - - // release at 3 aura in stack - if(dummy->GetStackAmount() <= 2) - return true; // main triggered spell casted anyway - - RemoveAurasDueToSpell(37658); - CastSpell(pVictim, 37661, true, castItem, triggeredByAura); - return true; - } - // Healing Discount - case 37705: - // Healing Trance (instead non-existed triggered spell) - triggered_spell_id = 37706; - target = this; - break; - // HoTs on Heals (Fel Reaver's Piston trinket) - case 38299: - { - // at direct heal effect - if(!procSpell || !IsSpellHaveEffect(procSpell,SPELL_EFFECT_HEAL)) - return false; - - // single proc at time - AuraList const& scAuras = GetSingleCastAuras(); - for(AuraList::const_iterator itr = scAuras.begin(); itr != scAuras.end(); ++itr) - if((*itr)->GetId()==triggered_spell_id) - return false; - - // positive cast at victim instead self - target = pVictim; - break; - } + trigger_spell_id = 37706; + target = this; } - switch(auraSpellInfo->SpellIconID) + // Soul Preserver + if (auraSpellInfo->Id==60510) { - case 241: - { - switch(auraSpellInfo->EffectTriggerSpell[0]) - { - //Illumination - case 18350: - { - if(!procSpell) - return false; - - // procspell is triggered spell but we need mana cost of original casted spell - uint32 originalSpellId = procSpell->Id; - - // Holy Shock - if(procSpell->SpellFamilyName == SPELLFAMILY_PALADIN) - { - if(procSpell->SpellFamilyFlags & 0x0001000000000000LL) - { - switch(procSpell->Id) - { - case 25914: originalSpellId = 20473; break; - case 25913: originalSpellId = 20929; break; - case 25903: originalSpellId = 20930; break; - case 27175: originalSpellId = 27174; break; - case 33074: originalSpellId = 33072; break; - default: - sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in HShock",procSpell->Id); - return false; - } - } - } - - SpellEntry const *originalSpell = sSpellStore.LookupEntry(originalSpellId); - if(!originalSpell) - { - sLog.outError("Unit::HandleProcTriggerSpell: Spell %u unknown but selected as original in Illu",originalSpellId); - return false; - } - - // percent stored in effect 1 (class scripts) base points - int32 percent = auraSpellInfo->EffectBasePoints[1]+1; - - basepoints0 = originalSpell->manaCost*percent/100; - triggered_spell_id = 20272; - target = this; - break; - } - } - break; - } + trigger_spell_id = 60515; + target = this; } - } - if(auraSpellInfo->SpellFamilyFlags & 0x00080000) - { - switch(auraSpellInfo->SpellIconID) + // Illumination + else if (auraSpellInfo->SpellIconID==241) { - //Judgement of Wisdom (overwrite non existing triggered spell call in spell.dbc - case 206: + if(!procSpell) + return false; + // procspell is triggered spell but we need mana cost of original casted spell + uint32 originalSpellId = procSpell->Id; + // Holy Shock heal + if(procSpell->SpellFamilyFlags[1] & 0x00010000) { - if(!pVictim || !pVictim->isAlive()) - return false; - - switch(triggeredByAura->GetSpellProto()->Id) + switch(procSpell->Id) { - case 20186: - triggered_spell_id = 20268; // Rank 1 - break; - case 20354: - triggered_spell_id = 20352; // Rank 2 - break; - case 20355: - triggered_spell_id = 20353; // Rank 3 - break; - case 27164: - triggered_spell_id = 27165; // Rank 4 - break; + case 25914: originalSpellId = 20473; break; + case 25913: originalSpellId = 20929; break; + case 25903: originalSpellId = 20930; break; + case 27175: originalSpellId = 27174; break; + case 33074: originalSpellId = 33072; break; + case 48820: originalSpellId = 48824; break; + case 48821: originalSpellId = 48825; break; default: - sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in JoW",triggeredByAura->GetSpellProto()->Id); - return false; + sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in HShock",procSpell->Id); + return false; } - - pVictim->CastSpell(pVictim,triggered_spell_id,true,castItem,triggeredByAura,GetGUID()); - return true; // no hidden cooldown } - //Judgement of Light - case 299: + SpellEntry const *originalSpell = sSpellStore.LookupEntry(originalSpellId); + if(!originalSpell) { - if(!pVictim || !pVictim->isAlive()) - return false; - - // overwrite non existing triggered spell call in spell.dbc - switch(triggeredByAura->GetSpellProto()->Id) - { - case 20185: - triggered_spell_id = 20267; // Rank 1 - break; - case 20344: - triggered_spell_id = 20341; // Rank 2 - break; - case 20345: - triggered_spell_id = 20342; // Rank 3 - break; - case 20346: - triggered_spell_id = 20343; // Rank 4 - break; - case 27162: - triggered_spell_id = 27163; // Rank 5 - break; - default: - sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in JoL",triggeredByAura->GetSpellProto()->Id); - return false; - } - pVictim->CastSpell(pVictim,triggered_spell_id,true,castItem,triggeredByAura,GetGUID()); - return true; // no hidden cooldown + sLog.outError("Unit::HandleProcTriggerSpell: Spell %u unknown but selected as original in Illu",originalSpellId); + return false; } + // percent stored in effect 1 (class scripts) base points + int32 cost = originalSpell->manaCost + originalSpell->ManaCostPercentage * GetCreateMana() / 100; + basepoints0 = cost*auraSpellInfo->CalculateSimpleValue(1)/100; + trigger_spell_id = 20272; + target = this; } - } - // custom check for proc spell - switch(auraSpellInfo->Id) - { - // Bonus Healing (item spell) - case 40971: + // Lightning Capacitor + else if (auraSpellInfo->Id==37657) { if(!pVictim || !pVictim->isAlive()) return false; + // stacking + CastSpell(this, 37658, true, NULL, triggeredByAura); - // bonus if health < 50% - if(pVictim->GetHealth() >= pVictim->GetMaxHealth()*triggeredByAura->GetModifier()->m_amount/100) + AuraEffect * dummy = GetDummyAura(37658); + // release at 3 aura in stack (cont contain in basepoint of trigger aura) + if(!dummy || dummy->GetParentAura()->GetStackAmount() < triggerAmount) return false; - // cast at target positive spell + RemoveAurasDueToSpell(37658); + trigger_spell_id = 37661; target = pVictim; - break; } - } - switch(triggered_spell_id) - { - // Seal of Command - case 20424: - // prevent chain of triggered spell from same triggered spell - if(procSpell && procSpell->Id==20424) + // Thunder Capacitor + else if (auraSpellInfo->Id == 54841) + { + if(!pVictim || !pVictim->isAlive()) return false; - break; + // stacking + CastSpell(this, 54842, true, NULL, triggeredByAura); + + // counting + AuraEffect * dummy = GetDummyAura(54842); + // release at 3 aura in stack (cont contain in basepoint of trigger aura) + if(!dummy || dummy->GetParentAura()->GetStackAmount() < triggerAmount) + return false; + + RemoveAurasDueToSpell(54842); + trigger_spell_id = 54843; + target = pVictim; + } + break; } - break; - } - case SPELLFAMILY_SHAMAN: - { - if(auraSpellInfo->SpellFamilyFlags == 0x0000000000000000) + case SPELLFAMILY_SHAMAN: { - switch(auraSpellInfo->SpellIconID) + // Lightning Shield (overwrite non existing triggered spell call in spell.dbc + if(auraSpellInfo->SpellFamilyFlags[0] & 0x400) { - case 19: - { - switch(auraSpellInfo->Id) - { - case 23551: // Lightning Shield - Tier2: 8 pieces proc shield - { - // Lightning Shield (overwrite non existing triggered spell call in spell.dbc) - triggered_spell_id = 23552; - target = pVictim; - break; - } - case 23552: // Lightning Shield - trigger shield damage - { - // Lightning Shield (overwrite non existing triggered spell call in spell.dbc) - triggered_spell_id = 27635; - target = pVictim; - break; - } - } - break; - } - // Mana Surge (Shaman T1 bonus) - case 87: + switch(auraSpellInfo->Id) { - if(!procSpell) - return false; - - basepoints0 = procSpell->manaCost * 35/100; - triggered_spell_id = 23571; - target = this; - break; + case 324: // Rank 1 + trigger_spell_id = 26364; break; + case 325: // Rank 2 + trigger_spell_id = 26365; break; + case 905: // Rank 3 + trigger_spell_id = 26366; break; + case 945: // Rank 4 + trigger_spell_id = 26367; break; + case 8134: // Rank 5 + trigger_spell_id = 26369; break; + case 10431: // Rank 6 + trigger_spell_id = 26370; break; + case 10432: // Rank 7 + trigger_spell_id = 26363; break; + case 25469: // Rank 8 + trigger_spell_id = 26371; break; + case 25472: // Rank 9 + trigger_spell_id = 26372; break; + case 49280: // Rank 10 + trigger_spell_id = 49278; break; + case 49281: // Rank 11 + trigger_spell_id = 49279; break; + default: + sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in LShield", auraSpellInfo->Id); + return false; } - //Nature's Guardian - case 2013: - { - if(GetTypeId()!=TYPEID_PLAYER) - return false; - - // damage taken that reduces below 30% health - // does NOT mean you must have been >= 30% before - if (10*(int32(GetHealth())-int32(damage)) >= 3*GetMaxHealth()) - return false; - - triggered_spell_id = 31616; + } + // Lightning Shield (The Ten Storms set) + else if (auraSpellInfo->Id == 23551) + { + trigger_spell_id = 23552; + target = pVictim; + } + // Damage from Lightning Shield (The Ten Storms set) + else if (auraSpellInfo->Id == 23552) + trigger_spell_id = 27635; + // Mana Surge (The Earthfury set) + else if (auraSpellInfo->Id == 23572) + { + if(!procSpell) + return false; + basepoints0 = procSpell->manaCost * 35 / 100; + trigger_spell_id = 23571; + target = this; + } + // Nature's Guardian + else if (auraSpellInfo->SpellIconID == 2013) + { + // Check health condition - should drop to less 30% (damage deal after this!) + if (!(10*(int32(GetHealth() - damage)) < 3 * GetMaxHealth())) + return false; - // need check cooldown now - if( cooldown && ((Player*)this)->HasSpellCooldown(triggered_spell_id)) - return false; + if(pVictim && pVictim->isAlive()) + pVictim->getThreatManager().modifyThreatPercent(this,-10); - basepoints0 = triggeredByAura->GetModifier()->m_amount * GetMaxHealth() / 100; - target = this; - if(pVictim && pVictim->isAlive()) - pVictim->getThreatManager().modifyThreatPercent(this,-10); - break; - } + basepoints0 = triggerAmount * GetMaxHealth() / 100; + trigger_spell_id = 31616; + target = this; } - } - - // Water Shield (we can't set cooldown for main spell - it's player casted spell - if((auraSpellInfo->SpellFamilyFlags & 0x0000002000000000LL) && auraSpellInfo->SpellVisual==7358) - { - target = this; break; } - - // Lightning Shield - if((auraSpellInfo->SpellFamilyFlags & 0x00000400) && auraSpellInfo->SpellVisual==37) - { - // overwrite non existing triggered spell call in spell.dbc - switch(triggeredByAura->GetSpellProto()->Id) - { - case 324: - triggered_spell_id = 26364; break; // Rank 1 - case 325: - triggered_spell_id = 26365; break; // Rank 2 - case 905: - triggered_spell_id = 26366; break; // Rank 3 - case 945: - triggered_spell_id = 26367; break; // Rank 4 - case 8134: - triggered_spell_id = 26369; break; // Rank 5 - case 10431: - triggered_spell_id = 26370; break; // Rank 6 - case 10432: - triggered_spell_id = 26363; break; // Rank 7 - case 25469: - triggered_spell_id = 26371; break; // Rank 8 - case 25472: - triggered_spell_id = 26372; break; // Rank 9 - default: - sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in LShield",triggeredByAura->GetSpellProto()->Id); + case SPELLFAMILY_DEATHKNIGHT: + { + // Acclimation + if (auraSpellInfo->SpellIconID == 1930) + { + if (!procSpell) + return false; + switch(GetFirstSchoolInMask(GetSpellSchoolMask(procSpell))) + { + case SPELL_SCHOOL_NORMAL: + return false; // ignore + case SPELL_SCHOOL_HOLY: trigger_spell_id = 50490; break; + case SPELL_SCHOOL_FIRE: trigger_spell_id = 50362; break; + case SPELL_SCHOOL_NATURE: trigger_spell_id = 50488; break; + case SPELL_SCHOOL_FROST: trigger_spell_id = 50485; break; + case SPELL_SCHOOL_SHADOW: trigger_spell_id = 50489; break; + case SPELL_SCHOOL_ARCANE: trigger_spell_id = 54373; break; + default: + return false; + } + } + // Blood Presence + else if (auraSpellInfo->Id == 48266) + { + if (GetTypeId() != TYPEID_PLAYER) + return false; + if (!((Player*)this)->isHonorOrXPTarget(pVictim)) return false; + trigger_spell_id = 50475; + basepoints0 = damage * triggerAmount / 100; } - - target = pVictim; break; } - break; - } - } - - // standard non-dummy case - if(!triggered_spell_id) - { - sLog.outError("Unit::HandleProcTriggerSpell: Spell %u have 0 in EffectTriggered[%d], not handled custom case?",auraSpellInfo->Id,triggeredByAura->GetEffIndex()); - return false; - } - - SpellEntry const* triggerEntry = sSpellStore.LookupEntry(triggered_spell_id); - - if(!triggerEntry) - { - sLog.outError("Unit::HandleProcTriggerSpell: Spell %u have not existed EffectTriggered[%d]=%u, not handled custom case?",auraSpellInfo->Id,triggeredByAura->GetEffIndex(),triggered_spell_id); - return false; - } - - // not allow proc extra attack spell at extra attack - if( m_extraAttacks && IsSpellHaveEffect(triggerEntry,SPELL_EFFECT_ADD_EXTRA_ATTACKS) ) - return false; - - if( cooldown && GetTypeId()==TYPEID_PLAYER && ((Player*)this)->HasSpellCooldown(triggered_spell_id)) - return false; - - // default case - if(!target || target!=this && !target->isAlive()) - return false; - - if(basepoints0) - CastCustomSpell(target,triggered_spell_id,&basepoints0,NULL,NULL,true,castItem,triggeredByAura); - else - CastSpell(target,triggered_spell_id,true,castItem,triggeredByAura); - - if( cooldown && GetTypeId()==TYPEID_PLAYER ) - ((Player*)this)->AddSpellCooldown(triggered_spell_id,0,time(NULL) + cooldown); - - return true; -} -*/ - -bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, Aura* triggeredByAura, SpellEntry const *procSpell, uint32 procFlags, uint32 procEx, uint32 cooldown) -{ - // Get triggered aura spell info - SpellEntry const* auraSpellInfo = triggeredByAura->GetSpellProto(); - - // Basepoints of trigger aura - int32 triggerAmount = triggeredByAura->GetModifier()->m_amount; - - // Set trigger spell id, target, custom basepoints - uint32 trigger_spell_id = auraSpellInfo->EffectTriggerSpell[triggeredByAura->GetEffIndex()]; - Unit* target = NULL; - int32 basepoints0 = 0; - - Item* castItem = triggeredByAura->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER - ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetCastItemGUID()) : NULL; - - // Try handle uncnown trigger spells - if (sSpellStore.LookupEntry(trigger_spell_id)==NULL) - switch (auraSpellInfo->SpellFamilyName) - { - //===================================================================== - // Generic class - // ==================================================================== - // ..... - //===================================================================== - case SPELLFAMILY_GENERIC: -// if (auraSpellInfo->Id==34082) // Advantaged State (DND) -// trigger_spell_id = ???; - if (auraSpellInfo->Id == 23780) // Aegis of Preservation (Aegis of Preservation trinket) - trigger_spell_id = 23781; -// else if (auraSpellInfo->Id==43504) // Alterac Valley OnKill Proc Aura -// trigger_spell_id = ; -// else if (auraSpellInfo->Id==37030) // Chaotic Temperament -// trigger_spell_id = ; - else if (auraSpellInfo->Id==43820) // Charm of the Witch Doctor (Amani Charm of the Witch Doctor trinket) - { - // Pct value stored in dummy - basepoints0 = pVictim->GetCreateHealth() * auraSpellInfo->EffectBasePoints[1] / 100; - target = pVictim; - break; - } -// else if (auraSpellInfo->Id==41248) // Consuming Strikes -// trigger_spell_id = 41249; -// else if (auraSpellInfo->Id==41054) // Copy Weapon -// trigger_spell_id = 41055; -// else if (auraSpellInfo->Id==31255) // Deadly Swiftness (Rank 1) -// trigger_spell_id = ; -// else if (auraSpellInfo->Id==5301) // Defensive State (DND) -// trigger_spell_id = ; -// else if (auraSpellInfo->Id==13358) // Defensive State (DND) -// trigger_spell_id = ; -// else if (auraSpellInfo->Id==16092) // Defensive State (DND) -// trigger_spell_id = ; -// else if (auraSpellInfo->Id==24949) // Defensive State 2 (DND) -// trigger_spell_id = ; -// else if (auraSpellInfo->Id==40329) // Demo Shout Sensor -// trigger_spell_id = ; - // Desperate Defense (Stonescythe Whelp, Stonescythe Alpha, Stonescythe Ambusher) - else if (auraSpellInfo->Id == 33896) - trigger_spell_id = 33898; -// else if (auraSpellInfo->Id==18943) // Double Attack -// trigger_spell_id = ; -// else if (auraSpellInfo->Id==19194) // Double Attack -// trigger_spell_id = ; -// else if (auraSpellInfo->Id==19817) // Double Attack -// trigger_spell_id = ; -// else if (auraSpellInfo->Id==19818) // Double Attack -// trigger_spell_id = ; -// else if (auraSpellInfo->Id==22835) // Drunken Rage -// trigger_spell_id = 14822; - /* - else if (auraSpellInfo->SpellIconID==191) // Elemental Response - { - switch (auraSpellInfo->Id && auraSpellInfo->AttributesEx==0) - { - case 34191: - case 34329: - case 34524: - case 34582: - case 36733:break; - default: - sLog.outError("Unit::HandleProcTriggerSpell: Spell %u miss posibly Elemental Response",auraSpellInfo->Id); - return false; - } - //This generic aura self-triggers a different spell for each school of magic that lands on the wearer: - switch (procSpell->School) - { - case SPELL_SCHOOL_FIRE: trigger_spell_id = 34192;break;//Fire: 34192 - case SPELL_SCHOOL_FROST: trigger_spell_id = 34193;break;//Frost: 34193 - case SPELL_SCHOOL_ARCANE: trigger_spell_id = 34194;break;//Arcane: 34194 - case SPELL_SCHOOL_NATURE: trigger_spell_id = 34195;break;//Nature: 34195 - case SPELL_SCHOOL_SHADOW: trigger_spell_id = 34196;break;//Shadow: 34196 - case SPELL_SCHOOL_HOLY: trigger_spell_id = 34197;break;//Holy: 34197 - case SPELL_SCHOOL_NORMAL: trigger_spell_id = 34198;break;//Physical: 34198 - default: - sLog.outError("Unit::HandleProcTriggerSpell: Spell %u Elemental Response wrong school",auraSpellInfo->Id); - return false; - } - }*/ -// else if (auraSpellInfo->Id==6542) // Enraged Defense -// trigger_spell_id = ; -// else if (auraSpellInfo->Id==40364) // Entangling Roots Sensor -// trigger_spell_id = ; -// else if (auraSpellInfo->Id==33207) // Gossip NPC Periodic - Fidget -// trigger_spell_id = ; -// else if (auraSpellInfo->Id==35321) // Gushing Wound -// trigger_spell_id = ; -// else if (auraSpellInfo->Id==38363) // Gushing Wound -// trigger_spell_id = ; -// else if (auraSpellInfo->Id==39215) // Gushing Wound -// trigger_spell_id = ; -// else if (auraSpellInfo->Id==40250) // Improved Duration -// trigger_spell_id = ; - else if (auraSpellInfo->Id==27522) // Mana Drain Trigger - { - // On successful melee or ranged attack gain $29471s1 mana and if possible drain $27526s1 mana from the target. - if (this && this->isAlive()) - CastSpell(this, 29471, true, castItem, triggeredByAura); - if (pVictim && pVictim->isAlive()) - CastSpell(pVictim, 27526, true, castItem, triggeredByAura); - return true; - } - else if (auraSpellInfo->Id==24905) // Moonkin Form (Passive) - { - // Elune's Touch (instead non-existed triggered spell) 30% from AP - trigger_spell_id = 33926; - basepoints0 = GetTotalAttackPowerValue(BASE_ATTACK) * 30 / 100; - target = this; - } -// else if (auraSpellInfo->Id==43453) // Rune Ward -// trigger_spell_id = ; -// else if (auraSpellInfo->Id==7137) // Shadow Charge (Rank 1) -// trigger_spell_id = ; - // Shaleskin (Shaleskin Flayer, Shaleskin Ripper) 30023 trigger -// else if (auraSpellInfo->Id==36576) -// trigger_spell_id = ; -// else if (auraSpellInfo->Id==34783) // Spell Reflection -// trigger_spell_id = ; -// else if (auraSpellInfo->Id==36096) // Spell Reflection -// trigger_spell_id = ; -// else if (auraSpellInfo->Id==36207) // Steal Weapon -// trigger_spell_id = ; -// else if (auraSpellInfo->Id==35205) // Vanish - break; - //===================================================================== - // Mage - //===================================================================== - // Blazing Speed (Rank 1,2) trigger = 18350 - //===================================================================== - case SPELLFAMILY_MAGE: - // Blazing Speed - if (auraSpellInfo->SpellIconID == 2127) - { - switch (auraSpellInfo->Id) - { - case 31641: // Rank 1 - case 31642: // Rank 2 - trigger_spell_id = 31643; - break; - default: - sLog.outError("Unit::HandleProcTriggerSpell: Spell %u miss posibly Blazing Speed",auraSpellInfo->Id); - return false; - } - } - break; - //===================================================================== - // Warrior - //===================================================================== - // Rampage (Rank 1-3) trigger = 18350 - //===================================================================== - case SPELLFAMILY_WARRIOR: - // Rampage - if (auraSpellInfo->SpellIconID == 2006 && auraSpellInfo->SpellFamilyFlags==0x100000) - { - switch(auraSpellInfo->Id) - { - case 29801: trigger_spell_id = 30029; break; // Rank 1 - case 30030: trigger_spell_id = 30031; break; // Rank 2 - case 30033: trigger_spell_id = 30032; break; // Rank 3 - default: - sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in Rampage",auraSpellInfo->Id); - return false; - } - } - break; - //===================================================================== - // Warlock - //===================================================================== - // Pyroclasm trigger = 18350 - // Drain Soul (Rank 1-5) trigger = 0 - //===================================================================== - case SPELLFAMILY_WARLOCK: - { - // Pyroclasm - if (auraSpellInfo->SpellIconID == 1137) - { - if(!pVictim || !pVictim->isAlive() || pVictim == this || procSpell == NULL) - return false; - // Calculate spell tick count for spells - uint32 tick = 1; // Default tick = 1 - - // Hellfire have 15 tick - if (procSpell->SpellFamilyFlags&0x0000000000000040LL) - tick = 15; - // Rain of Fire have 4 tick - else if (procSpell->SpellFamilyFlags&0x0000000000000020LL) - tick = 4; - else - return false; - - // Calculate chance = baseChance / tick - float chance = 0; - switch (auraSpellInfo->Id) - { - case 18096: chance = 13.0f / tick; break; - case 18073: chance = 26.0f / tick; break; - } - // Roll chance - if (!roll_chance_f(chance)) - return false; - - trigger_spell_id = 18093; - } - // Drain Soul - else if (auraSpellInfo->SpellFamilyFlags & 0x0000000000004000LL) - { - Unit::AuraList const& mAddFlatModifier = GetAurasByType(SPELL_AURA_ADD_FLAT_MODIFIER); - for(Unit::AuraList::const_iterator i = mAddFlatModifier.begin(); i != mAddFlatModifier.end(); ++i) - { - if ((*i)->GetModifier()->m_miscvalue == SPELLMOD_CHANCE_OF_SUCCESS && (*i)->GetSpellProto()->SpellIconID == 113) - { - int32 value2 = CalculateSpellDamage((*i)->GetSpellProto(),2,(*i)->GetSpellProto()->EffectBasePoints[2],this); - basepoints0 = value2 * GetMaxPower(POWER_MANA) / 100; - } - } - if ( basepoints0 == 0 ) - return false; - trigger_spell_id = 18371; - } - break; - } - //===================================================================== - // Priest - //===================================================================== - // Greater Heal Refund trigger = 18350 - // Blessed Recovery (Rank 1-3) trigger = 18350 - // Shadowguard (1-7) trigger = 28376 - //===================================================================== - case SPELLFAMILY_PRIEST: - { - // Greater Heal Refund - if (auraSpellInfo->Id==37594) - trigger_spell_id = 37595; - // Shadowguard - else if(auraSpellInfo->SpellFamilyFlags==0x100080000000LL && auraSpellInfo->SpellVisual==7958) - { - switch(auraSpellInfo->Id) - { - case 18137: trigger_spell_id = 28377; break; // Rank 1 - case 19308: trigger_spell_id = 28378; break; // Rank 2 - case 19309: trigger_spell_id = 28379; break; // Rank 3 - case 19310: trigger_spell_id = 28380; break; // Rank 4 - case 19311: trigger_spell_id = 28381; break; // Rank 5 - case 19312: trigger_spell_id = 28382; break; // Rank 6 - case 25477: trigger_spell_id = 28385; break; // Rank 7 - default: - sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in SG", auraSpellInfo->Id); - return false; - } - } - // Blessed Recovery - else if (auraSpellInfo->SpellIconID == 1875) - { - switch (auraSpellInfo->Id) - { - case 27811: trigger_spell_id = 27813; break; - case 27815: trigger_spell_id = 27817; break; - case 27816: trigger_spell_id = 27818; break; - default: - sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in BR", auraSpellInfo->Id); - return false; - } - basepoints0 = damage * triggerAmount / 100 / 3; - target = this; - } - break; - } - //===================================================================== - // Druid - // ==================================================================== - // Druid Forms Trinket trigger = 18350 - // Entangling Roots trigger = 30023 - // Leader of the Pack trigger = 18350 - //===================================================================== - case SPELLFAMILY_DRUID: - { - // Druid Forms Trinket - if (auraSpellInfo->Id==37336) - { - switch(m_form) - { - case 0: trigger_spell_id = 37344;break; - case FORM_CAT: trigger_spell_id = 37341;break; - case FORM_BEAR: - case FORM_DIREBEAR: trigger_spell_id = 37340;break; - case FORM_TREE: trigger_spell_id = 37342;break; - case FORM_MOONKIN: trigger_spell_id = 37343;break; - default: - return false; - } - } -// else if (auraSpellInfo->Id==40363)// Entangling Roots () -// trigger_spell_id = ????; - // Leader of the Pack - else if (auraSpellInfo->Id == 24932) - { - if (triggerAmount == 0) - return false; - basepoints0 = triggerAmount * GetMaxHealth() / 100; - trigger_spell_id = 34299; - } - break; - } - //===================================================================== - // Hunter - // ==================================================================== - // ...... - //===================================================================== - case SPELLFAMILY_HUNTER: - break; - //===================================================================== - // Paladin - // ==================================================================== - // Blessed Life trigger = 31934 - // Healing Discount trigger = 18350 - // Illumination (Rank 1-5) trigger = 18350 - // Judgement of Light (Rank 1-5) trigger = 5373 - // Judgement of Wisdom (Rank 1-4) trigger = 1826 - // Lightning Capacitor trigger = 18350 - //===================================================================== - case SPELLFAMILY_PALADIN: - { - /* // Blessed Life - if (auraSpellInfo->SpellIconID == 2137) - { - switch (auraSpellInfo->Id) - { - case 31828: // Rank 1 - case 31829: // Rank 2 - case 31830: // Rank 3 + default: break; - default: - sLog.outError("Unit::HandleProcTriggerSpell: Spell %u miss posibly Blessed Life", auraSpellInfo->Id); - return false; - } - }*/ - // Healing Discount - if (auraSpellInfo->Id==37705) - { - trigger_spell_id = 37706; - target = this; - } - // Judgement of Light and Judgement of Wisdom - else if (auraSpellInfo->SpellFamilyFlags & 0x0000000000080000LL) - { - switch (auraSpellInfo->Id) - { - // Judgement of Light - case 20185: trigger_spell_id = 20267;break; // Rank 1 - case 20344: trigger_spell_id = 20341;break; // Rank 2 - case 20345: trigger_spell_id = 20342;break; // Rank 3 - case 20346: trigger_spell_id = 20343;break; // Rank 4 - case 27162: trigger_spell_id = 27163;break; // Rank 5 - // Judgement of Wisdom - case 20186: trigger_spell_id = 20268;break; // Rank 1 - case 20354: trigger_spell_id = 20352;break; // Rank 2 - case 20355: trigger_spell_id = 20353;break; // Rank 3 - case 27164: trigger_spell_id = 27165;break; // Rank 4 - default: - sLog.outError("Unit::HandleProcTriggerSpell: Spell %u miss posibly Judgement of Light/Wisdom", auraSpellInfo->Id); - return false; - } - pVictim->CastSpell(pVictim, trigger_spell_id, true, castItem, triggeredByAura); - return true; // no hidden cooldown - } - // Illumination - else if (auraSpellInfo->SpellIconID==241) - { - if(!procSpell) - return false; - // procspell is triggered spell but we need mana cost of original casted spell - uint32 originalSpellId = procSpell->Id; - // Holy Shock - if(procSpell->SpellFamilyFlags & 0x00200000) - { - switch(procSpell->Id) - { - case 25914: originalSpellId = 20473; break; - case 25913: originalSpellId = 20929; break; - case 25903: originalSpellId = 20930; break; - case 27175: originalSpellId = 27174; break; - case 33074: originalSpellId = 33072; break; - default: - sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in HShock",procSpell->Id); - return false; - } - } - SpellEntry const *originalSpell = sSpellStore.LookupEntry(originalSpellId); - if(!originalSpell) - { - sLog.outError("Unit::HandleProcTriggerSpell: Spell %u unknown but selected as original in Illu",originalSpellId); - return false; - } - // percent stored in effect 1 (class scripts) base points - basepoints0 = originalSpell->manaCost*(auraSpellInfo->EffectBasePoints[1]+1)/100; - trigger_spell_id = 20272; - target = this; - } - // Lightning Capacitor - else if (auraSpellInfo->Id==37657) - { - if(!pVictim || !pVictim->isAlive()) - return false; - // stacking - CastSpell(this, 37658, true, NULL, triggeredByAura); - // counting - Aura * dummy = GetDummyAura(37658); - if (!dummy) - return false; - // release at 3 aura in stack (cont contain in basepoint of trigger aura) - if(dummy->GetStackAmount() <= 2) - return false; - - RemoveAurasDueToSpell(37658); - trigger_spell_id = 37661; - target = pVictim; - } - break; - } - //===================================================================== - // Shaman - //==================================================================== - // Lightning Shield trigger = 18350 - // Mana Surge trigger = 18350 - // Nature's Guardian (Rank 1-5) trigger = 18350 - //===================================================================== - case SPELLFAMILY_SHAMAN: - { - //Lightning Shield (overwrite non existing triggered spell call in spell.dbc - if(auraSpellInfo->SpellFamilyFlags==0x00000400 && auraSpellInfo->SpellVisual==37) - { - switch(auraSpellInfo->Id) - { - case 324: trigger_spell_id = 26364; break; // Rank 1 - case 325: trigger_spell_id = 26365; break; // Rank 2 - case 905: trigger_spell_id = 26366; break; // Rank 3 - case 945: trigger_spell_id = 26367; break; // Rank 4 - case 8134: trigger_spell_id = 26369; break; // Rank 5 - case 10431: trigger_spell_id = 26370; break; // Rank 6 - case 10432: trigger_spell_id = 26363; break; // Rank 7 - case 25469: trigger_spell_id = 26371; break; // Rank 8 - case 25472: trigger_spell_id = 26372; break; // Rank 9 - default: - sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in LShield", auraSpellInfo->Id); - return false; - } - } - // Lightning Shield (The Ten Storms set) - else if (auraSpellInfo->Id == 23551) - { - trigger_spell_id = 23552; - target = pVictim; - } - // Damage from Lightning Shield (The Ten Storms set) - else if (auraSpellInfo->Id == 23552) - trigger_spell_id = 27635; - // Mana Surge (The Earthfury set) - else if (auraSpellInfo->Id == 23572) - { - if(!procSpell) - return false; - basepoints0 = procSpell->manaCost * 35 / 100; - trigger_spell_id = 23571; - target = this; - } - else if (auraSpellInfo->SpellIconID == 2013) //Nature's Guardian - { - // Check health condition - should drop to less 30% (damage deal after this!) - if (!(10*(int32(GetHealth() - damage)) < 3 * GetMaxHealth())) - return false; - - if(pVictim && pVictim->isAlive()) - pVictim->getThreatManager().modifyThreatPercent(this,-10); - - basepoints0 = triggerAmount * GetMaxHealth() / 100; - trigger_spell_id = 31616; - target = this; - } - break; - } - // default - default: - break; + } } // All ok. Check current trigger spell @@ -7420,6 +7174,15 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, Aura* triggeredB return false; break; } + // Rapid Recuperation + case 53228: + case 53232: + { + // This effect only from Rapid Fire (ability cast) + if (!(procSpell->SpellFamilyFlags[0] & 0x20)) + return false; + break; + } } // Costum basepoints/target for exist spell @@ -7449,11 +7212,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 / 100); + 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) @@ -7463,6 +7231,59 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, Aura* triggeredB return false; break; } + // Brain Freeze + case 57761: + { + if(!procSpell) + return false; + // For trigger from Blizzard need exist Improved Blizzard + if (procSpell->SpellFamilyName==SPELLFAMILY_MAGE && procSpell->SpellFamilyFlags[0] & 0x80) + { + bool found = false; + AuraEffectList const& mOverrideClassScript = GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); + for(AuraEffectList::const_iterator i = mOverrideClassScript.begin(); i != mOverrideClassScript.end(); ++i) + { + int32 script = (*i)->GetMiscValue(); + if(script==836 || script==988 || script==989) + { + found=true; + break; + } + } + if(!found) + return false; + } + break; + } + // Astral Shift + case 52179: + { + if (procSpell == 0 || !(procEx & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) || this == pVictim) + return false; + + // Need stun, fear or silence mechanic + if (!(GetAllSpellMechanicMask(procSpell) & ((1<<MECHANIC_SILENCE)|(1<<MECHANIC_STUN)|(1<<MECHANIC_FEAR)))) + return false; + break; + } + // Burning Determination + case 54748: + { + if(!procSpell) + return false; + // Need Interrupt or Silenced mechanic + if (!(GetAllSpellMechanicMask(procSpell) & ((1<<MECHANIC_INTERRUPT)|(1<<MECHANIC_SILENCE)))) + 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)) @@ -7489,15 +7310,15 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, Aura* triggeredB return true; } -bool Unit::HandleOverrideClassScriptAuraProc(Unit *pVictim, Aura *triggeredByAura, SpellEntry const *procSpell, uint32 cooldown) +bool Unit::HandleOverrideClassScriptAuraProc(Unit *pVictim, uint32 damage, AuraEffect *triggeredByAura, SpellEntry const *procSpell, uint32 cooldown) { - int32 scriptId = triggeredByAura->GetModifier()->m_miscvalue; + int32 scriptId = triggeredByAura->GetMiscValue(); if(!pVictim || !pVictim->isAlive()) return false; - Item* castItem = triggeredByAura->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER - ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetCastItemGUID()) : NULL; + Item* castItem = triggeredByAura->GetParentAura()->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER + ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetParentAura()->GetCastItemGUID()) : NULL; uint32 triggered_spell_id = 0; @@ -7505,21 +7326,21 @@ bool Unit::HandleOverrideClassScriptAuraProc(Unit *pVictim, Aura *triggeredByAur { case 836: // Improved Blizzard (Rank 1) { - if (!procSpell || procSpell->SpellVisual!=9487) + if (!procSpell || procSpell->SpellVisual[0]!=9487) return false; triggered_spell_id = 12484; break; } case 988: // Improved Blizzard (Rank 2) { - if (!procSpell || procSpell->SpellVisual!=9487) + if (!procSpell || procSpell->SpellVisual[0]!=9487) return false; triggered_spell_id = 12485; break; } case 989: // Improved Blizzard (Rank 3) { - if (!procSpell || procSpell->SpellVisual!=9487) + if (!procSpell || procSpell->SpellVisual[0]!=9487) return false; triggered_spell_id = 12486; break; @@ -7556,6 +7377,29 @@ bool Unit::HandleOverrideClassScriptAuraProc(Unit *pVictim, Aura *triggeredByAur case 5497: // Improved Mana Gems (Serpent-Coil Braid) triggered_spell_id = 37445; // Mana Surge break; + case 8152: // Serendipity + { + // if heal your target over maximum health + if (pVictim->GetHealth() + damage < pVictim->GetMaxHealth()) + return false; + int32 cost = procSpell->manaCost + procSpell->ManaCostPercentage * GetCreateMana() / 100; + int32 basepoints0 = cost * triggeredByAura->GetAmount()/100; + CastCustomSpell(this, 47762, &basepoints0, 0, 0, true, 0, triggeredByAura); + return true; + } + // Rapture + case 7556: // Rank 1 + case 7555: // Rank 2 + case 7554: // Rank 3 + case 7553: // Rank 4 + case 7552: // Rank 5 + { + int32 basepoints0 = ((getLevel() * (-0.2) + 18) / 1000000) * damage * GetMaxPower(POWER_MANA); + if(basepoints0 > (GetMaxPower(POWER_MANA) / 100) * (triggeredByAura->GetAmount() / 10)) + basepoints0 = (GetMaxPower(POWER_MANA) / 100) * (triggeredByAura->GetAmount() / 10); + CastCustomSpell(this, 47755, &basepoints0, 0, 0, true, 0, triggeredByAura); + return true; + } } // not processed @@ -7647,6 +7491,8 @@ FactionTemplateEntry const* Unit::getFactionTemplateEntry() const bool Unit::IsHostileTo(Unit const* unit) const { + if(!unit) + return false; // always non-hostile to self if(unit==this) return false; @@ -7697,11 +7543,11 @@ bool Unit::IsHostileTo(Unit const* unit) const return false; // Sanctuary - if(pTarget->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY) && pTester->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY)) + if(pTarget->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY) && pTester->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY)) return false; // PvP FFA state - if(pTester->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP) && pTarget->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP)) + if(pTester->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP) && pTarget->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP)) return true; //= PvP states @@ -7726,28 +7572,31 @@ bool Unit::IsHostileTo(Unit const* unit) const if(tester->GetTypeId()==TYPEID_PLAYER) { // forced reaction - ForcedReactions::const_iterator forceItr = ((Player*)tester)->m_forcedReactions.find(target_faction->faction); - if(forceItr!=((Player*)tester)->m_forcedReactions.end()) - return forceItr->second <= REP_HOSTILE; - - // if faction have reputation then hostile state for tester at 100% dependent from at_war state - if(FactionEntry const* raw_target_faction = sFactionStore.LookupEntry(target_faction->faction)) - if(raw_target_faction->reputationListID >=0) - if(FactionState const* factionState = ((Player*)tester)->GetFactionState(raw_target_faction)) + if(target_faction->faction) + { + if(ReputationRank const* force =((Player*)tester)->GetReputationMgr().GetForcedRankIfAny(target_faction)) + return *force <= REP_HOSTILE; + + // if faction have reputation then hostile state for tester at 100% dependent from at_war state + if(FactionEntry const* raw_target_faction = sFactionStore.LookupEntry(target_faction->faction)) + if(FactionState const* factionState = ((Player*)tester)->GetReputationMgr().GetState(raw_target_faction)) return (factionState->Flags & FACTION_FLAG_AT_WAR); + } } // CvP forced reaction and reputation case else if(target->GetTypeId()==TYPEID_PLAYER) { // forced reaction - ForcedReactions::const_iterator forceItr = ((Player const*)target)->m_forcedReactions.find(tester_faction->faction); - if(forceItr!=((Player const*)target)->m_forcedReactions.end()) - return forceItr->second <= REP_HOSTILE; + if(tester_faction->faction) + { + if(ReputationRank const* force = ((Player*)target)->GetReputationMgr().GetForcedRankIfAny(tester_faction)) + return *force <= REP_HOSTILE; - // apply reputation state - FactionEntry const* raw_tester_faction = sFactionStore.LookupEntry(tester_faction->faction); - if(raw_tester_faction && raw_tester_faction->reputationListID >=0 ) - return ((Player const*)target)->GetReputationRank(raw_tester_faction) <= REP_HOSTILE; + // apply reputation state + FactionEntry const* raw_tester_faction = sFactionStore.LookupEntry(tester_faction->faction); + if(raw_tester_faction && raw_tester_faction->reputationListID >=0 ) + return ((Player const*)target)->GetReputationMgr().GetRank(raw_tester_faction) <= REP_HOSTILE; + } } // common faction based case (CvC,PvC,CvP) @@ -7806,11 +7655,11 @@ bool Unit::IsFriendlyTo(Unit const* unit) const return true; // Sanctuary - if(pTarget->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY) && pTester->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY)) + if(pTarget->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY) && pTester->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY)) return true; // PvP FFA state - if(pTester->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP) && pTarget->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP)) + if(pTester->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP) && pTarget->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP)) return false; //= PvP states @@ -7835,28 +7684,31 @@ bool Unit::IsFriendlyTo(Unit const* unit) const if(tester->GetTypeId()==TYPEID_PLAYER) { // forced reaction - ForcedReactions::const_iterator forceItr = ((Player const*)tester)->m_forcedReactions.find(target_faction->faction); - if(forceItr!=((Player const*)tester)->m_forcedReactions.end()) - return forceItr->second >= REP_FRIENDLY; + if(target_faction->faction) + { + if(ReputationRank const* force =((Player*)tester)->GetReputationMgr().GetForcedRankIfAny(target_faction)) + return *force >= REP_FRIENDLY; - // if faction have reputation then friendly state for tester at 100% dependent from at_war state - if(FactionEntry const* raw_target_faction = sFactionStore.LookupEntry(target_faction->faction)) - if(raw_target_faction->reputationListID >=0) - if(FactionState const* FactionState = ((Player*)tester)->GetFactionState(raw_target_faction)) - return !(FactionState->Flags & FACTION_FLAG_AT_WAR); + // if faction have reputation then friendly state for tester at 100% dependent from at_war state + if(FactionEntry const* raw_target_faction = sFactionStore.LookupEntry(target_faction->faction)) + if(FactionState const* factionState = ((Player*)tester)->GetReputationMgr().GetState(raw_target_faction)) + return !(factionState->Flags & FACTION_FLAG_AT_WAR); + } } // CvP forced reaction and reputation case else if(target->GetTypeId()==TYPEID_PLAYER) { // forced reaction - ForcedReactions::const_iterator forceItr = ((Player const*)target)->m_forcedReactions.find(tester_faction->faction); - if(forceItr!=((Player const*)target)->m_forcedReactions.end()) - return forceItr->second >= REP_FRIENDLY; + if(tester_faction->faction) + { + if(ReputationRank const* force =((Player*)target)->GetReputationMgr().GetForcedRankIfAny(tester_faction)) + return *force >= REP_FRIENDLY; - // apply reputation state - if(FactionEntry const* raw_tester_faction = sFactionStore.LookupEntry(tester_faction->faction)) - if(raw_tester_faction->reputationListID >=0 ) - return ((Player const*)target)->GetReputationRank(raw_tester_faction) >= REP_FRIENDLY; + // apply reputation state + if(FactionEntry const* raw_tester_faction = sFactionStore.LookupEntry(tester_faction->faction)) + if(raw_tester_faction->reputationListID >=0 ) + return ((Player const*)target)->GetReputationMgr().GetRank(raw_tester_faction) >= REP_FRIENDLY; + } } // common faction based case (CvC,PvC,CvP) @@ -7866,7 +7718,7 @@ bool Unit::IsFriendlyTo(Unit const* unit) const bool Unit::IsHostileToPlayers() const { FactionTemplateEntry const* my_faction = getFactionTemplateEntry(); - if(!my_faction) + if(!my_faction || !my_faction->faction) return false; FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->faction); @@ -7879,7 +7731,7 @@ bool Unit::IsHostileToPlayers() const bool Unit::IsNeutralToAll() const { FactionTemplateEntry const* my_faction = getFactionTemplateEntry(); - if(!my_faction) + if(!my_faction || !my_faction->faction) return true; FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->faction); @@ -7916,7 +7768,7 @@ bool Unit::Attack(Unit *victim, bool meleeAttack) // remove SPELL_AURA_MOD_UNATTACKABLE at attack (in case non-interruptible spells stun aura applied also that not let attack) if(HasAuraType(SPELL_AURA_MOD_UNATTACKABLE)) - RemoveSpellsCausingAura(SPELL_AURA_MOD_UNATTACKABLE); + RemoveAurasByType(SPELL_AURA_MOD_UNATTACKABLE); if (m_attacking) { @@ -7931,34 +7783,43 @@ bool Unit::Attack(Unit *victim, bool meleeAttack) } return false; } - AttackStop(); + + //switch target + InterruptSpell(CURRENT_MELEE_SPELL); + if(!meleeAttack) + clearUnitState(UNIT_STAT_MELEE_ATTACKING); } + if(m_attacking) + m_attacking->_removeAttacker(this); + + m_attacking = victim; + m_attacking->_addAttacker(this); + //Set our target SetUInt64Value(UNIT_FIELD_TARGET, victim->GetGUID()); if(meleeAttack) addUnitState(UNIT_STAT_MELEE_ATTACKING); - m_attacking = victim; - m_attacking->_addAttacker(this); - //if(m_attacking->GetTypeId()==TYPEID_UNIT && ((Creature*)m_attacking)->IsAIEnabled) - // ((Creature*)m_attacking)->AI()->AttackedBy(this); + // set position before any AI calls/assistance + //if(GetTypeId()==TYPEID_UNIT) + // ((Creature*)this)->SetCombatStartPosition(GetPositionX(), GetPositionY(), GetPositionZ()); if(GetTypeId()==TYPEID_UNIT) { + // should not let player enter combat by right clicking target + SetInCombatWith(victim); + if(victim->GetTypeId() == TYPEID_PLAYER) + victim->SetInCombatWith(this); + AddThreat(victim, 0.0f); + WorldPacket data(SMSG_AI_REACTION, 12); data << uint64(GetGUID()); data << uint32(AI_REACTION_AGGRO); // Aggro sound ((WorldObject*)this)->SendMessageToSet(&data, true); ((Creature*)this)->CallAssistance(); - - // should not let player enter combat by right clicking target - SetInCombatWith(victim); - if(victim->GetTypeId() == TYPEID_PLAYER) - victim->SetInCombatWith(this); - AddThreat(victim, 0.0f); } // delay offhand weapon attack to next attack time @@ -8014,17 +7875,9 @@ void Unit::CombatStop(bool cast) void Unit::CombatStopWithPets(bool cast) { CombatStop(cast); - if(Pet* pet = GetPet()) - pet->CombatStop(cast); - if(Unit* charm = GetCharm()) - charm->CombatStop(cast); - if(GetTypeId()==TYPEID_PLAYER) - { - GuardianPetList const& guardians = ((Player*)this)->GetGuardians(); - for(GuardianPetList::const_iterator itr = guardians.begin(); itr != guardians.end(); ++itr) - if(Unit* guardian = Unit::GetUnit(*this,*itr)) - guardian->CombatStop(cast); - } + + for(ControlList::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr) + (*itr)->CombatStop(cast); } bool Unit::isAttackingPlayer() const @@ -8032,23 +7885,15 @@ bool Unit::isAttackingPlayer() const if(hasUnitState(UNIT_STAT_ATTACK_PLAYER)) return true; - Pet* pet = GetPet(); - if(pet && pet->isAttackingPlayer()) - return true; - - Unit* charmed = GetCharm(); - if(charmed && charmed->isAttackingPlayer()) - return true; + for(ControlList::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr) + if((*itr)->isAttackingPlayer()) + return true; - for (int8 i = 0; i < MAX_TOTEM; i++) - { - if(m_TotemSlot[i]) - { - Creature *totem = ObjectAccessor::GetCreature(*this, m_TotemSlot[i]); - if(totem && totem->isAttackingPlayer()) - return true; - } - } + for(int8 i = 0; i < MAX_SUMMON_SLOT; ++i) + if(m_SummonSlot[i]) + if(Creature *summon = ObjectAccessor::GetCreature(*this, m_SummonSlot[i])) + if(summon->isAttackingPlayer()) + return true; return false; } @@ -8068,51 +7913,19 @@ void Unit::RemoveAllAttackers() void Unit::ModifyAuraState(AuraState flag, bool apply) { - if (apply) - { - if (!HasFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1))) - { - SetFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1)); - if(GetTypeId() == TYPEID_PLAYER) - { - const PlayerSpellMap& sp_list = ((Player*)this)->GetSpellMap(); - for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr) - { - if(itr->second->state == PLAYERSPELL_REMOVED) continue; - SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first); - if (!spellInfo || !IsPassiveSpell(itr->first)) continue; - if (spellInfo->CasterAuraState == flag) - CastSpell(this, itr->first, true, NULL); - } - } - } - } - else - { - if (HasFlag(UNIT_FIELD_AURASTATE,1<<(flag-1))) - { - RemoveFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1)); - Unit::AuraMap& tAuras = GetAuras(); - for (Unit::AuraMap::iterator itr = tAuras.begin(); itr != tAuras.end();) - { - SpellEntry const* spellProto = (*itr).second->GetSpellProto(); - if (spellProto->CasterAuraState == flag) - { - // exceptions (applied at state but not removed at state change) - // Rampage - if(spellProto->SpellIconID==2006 && spellProto->SpellFamilyName==SPELLFAMILY_WARRIOR && spellProto->SpellFamilyFlags==0x100000) - { - ++itr; - continue; - } + ApplyModFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1), apply); +} - RemoveAura(itr); - } - else - ++itr; - } - } +bool Unit::HasAuraState(AuraState flag, SpellEntry const *spellProto, Unit * Caster) const +{ + if (Caster && spellProto) + { + AuraEffectList const& stateAuras = Caster->GetAurasByType(SPELL_AURA_ABILITY_IGNORE_AURASTATE); + for(AuraEffectList::const_iterator j = stateAuras.begin();j != stateAuras.end(); ++j) + if((*j)->isAffectedOnSpell(spellProto)) + return true; } + return HasFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1)); } Unit *Unit::GetOwner() const @@ -8139,15 +7952,16 @@ Player* Unit::GetCharmerOrOwnerPlayerOrPlayerItself() const return GetTypeId()==TYPEID_PLAYER ? (Player*)this : NULL; } -Pet* Unit::GetPet() const +Guardian* Unit::GetGuardianPet() const { if(uint64 pet_guid = GetPetGUID()) { - if(Pet* pet = ObjectAccessor::GetPet(pet_guid)) - return pet; + if(Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, pet_guid)) + if(pet->HasSummonMask(SUMMON_MASK_GUARDIAN)) + return (Guardian*)pet; - sLog.outError("Unit::GetPet: Pet %u not exist.",GUID_LOPART(pet_guid)); - const_cast<Unit*>(this)->SetPet(0); + sLog.outError("Unit::GetGuardianPet: Pet %u not exist.",GUID_LOPART(pet_guid)); + const_cast<Unit*>(this)->SetPetGUID(0); } return NULL; @@ -8161,28 +7975,236 @@ Unit* Unit::GetCharm() const return pet; sLog.outError("Unit::GetCharm: Charmed creature %u not exist.",GUID_LOPART(charm_guid)); - const_cast<Unit*>(this)->SetCharm(0); + const_cast<Unit*>(this)->SetUInt64Value(UNIT_FIELD_CHARM, 0); } return NULL; } -void Unit::SetPet(Pet* pet) +void Unit::SetGuardian(Guardian* guardian, bool apply) { - SetUInt64Value(UNIT_FIELD_SUMMON, pet ? pet->GetGUID() : 0); + sLog.outDebug("SetGuardian %u for %u, apply %u", guardian->GetEntry(), GetEntry(), apply); + + if(apply) + { + if(!guardian->AddUInt64Value(UNIT_FIELD_SUMMONEDBY, GetGUID())) + { + sLog.outCrash("Guardian %u is summoned by %u but it already has a owner", guardian->GetEntry(), GetEntry()); + return; + } + + m_Controlled.insert(guardian); + + if(guardian->isPet() || guardian->m_Properties && guardian->m_Properties->Category == SUMMON_CATEGORY_PET) + { + if(Guardian* oldPet = GetGuardianPet()) + { + if(oldPet != guardian && (oldPet->isPet() || guardian->isPet() || oldPet->GetEntry() != guardian->GetEntry())) + { + // remove existing guardian pet + if(oldPet->isPet()) + ((Pet*)oldPet)->Remove(PET_SAVE_AS_CURRENT); + else + oldPet->UnSummon(); + SetPetGUID(guardian->GetGUID()); + SetGuardianGUID(0); + } + } + else + { + SetPetGUID(guardian->GetGUID()); + SetGuardianGUID(0); + } + } - // FIXME: hack, speed must be set only at follow - if(pet) - for(int i = 0; i < MAX_MOVE_TYPE; ++i) - pet->SetSpeed(UnitMoveType(i), m_speed_rate[i], true); + if(AddUInt64Value(UNIT_FIELD_SUMMON, guardian->GetGUID())) + { + if(GetTypeId() == TYPEID_PLAYER && !GetCharmGUID()) + { + if(guardian->isPet()) + ((Player*)this)->PetSpellInitialize(); + else + ((Player*)this)->CharmSpellInitialize(); + } + } + + // FIXME: hack, speed must be set only at follow + if(GetTypeId() == TYPEID_PLAYER && guardian->HasSummonMask(SUMMON_MASK_PET)) + for(int i = 0; i < MAX_MOVE_TYPE; ++i) + guardian->SetSpeed(UnitMoveType(i), m_speed_rate[i], true); + } + else + { + if(!guardian->RemoveUInt64Value(UNIT_FIELD_SUMMONEDBY, GetGUID())) + { + sLog.outCrash("Pet %u is unsummoned by %u but it has another owner", guardian->GetEntry(), GetEntry()); + return; + } + + m_Controlled.erase(guardian); + + if(guardian->isPet() || guardian->m_Properties && guardian->m_Properties->Category == SUMMON_CATEGORY_PET) + { + if(GetPetGUID() == guardian->GetGUID()) + SetPetGUID(0); + } + + if(RemoveUInt64Value(UNIT_FIELD_SUMMON, guardian->GetGUID())) + { + //Check if there is another guardian + for(ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr) + { + if(GetCharmGUID() == (*itr)->GetGUID()) + continue; + + assert((*itr)->GetOwnerGUID() == GetGUID()); + assert((*itr)->GetTypeId() == TYPEID_UNIT); + if(AddUInt64Value(UNIT_FIELD_SUMMON, (*itr)->GetGUID())) + { + //show another pet bar if there is no charm bar + if(GetTypeId() == TYPEID_PLAYER && !GetCharmGUID()) + { + if(((Creature*)(*itr))->isPet()) + ((Player*)this)->PetSpellInitialize(); + else + ((Player*)this)->CharmSpellInitialize(); + } + } + break; + } + } + } +} + +void Unit::SetCharm(Unit* charm, bool apply) +{ + if(apply) + { + if(GetTypeId() == TYPEID_PLAYER) + { + if(!AddUInt64Value(UNIT_FIELD_CHARM, charm->GetGUID())) + sLog.outCrash("Player %s is trying to charm unit %u, but it already has a charmed unit %u", GetName(), charm->GetEntry(), GetCharmGUID()); + } + + if(!charm->AddUInt64Value(UNIT_FIELD_CHARMEDBY, GetGUID())) + sLog.outCrash("Unit %u is being charmed, but it already has a charmer %u", charm->GetEntry(), charm->GetCharmerGUID()); + + m_Controlled.insert(charm); + } + else + { + if(GetTypeId() == TYPEID_PLAYER) + { + if(!RemoveUInt64Value(UNIT_FIELD_CHARM, charm->GetGUID())) + sLog.outCrash("Player %s is trying to uncharm unit %u, but it has another charmed unit %u", GetName(), charm->GetEntry(), GetCharmGUID()); + } + + if(!charm->RemoveUInt64Value(UNIT_FIELD_CHARMEDBY, GetGUID())) + sLog.outCrash("Unit %u is being uncharmed, but it has another charmer %u", charm->GetEntry(), charm->GetCharmerGUID()); + + m_Controlled.erase(charm); + } } -void Unit::SetCharm(Unit* pet) +Unit* Unit::GetFirstControlled() const { + //Sequence: charmed, pet, other guardians + Unit *unit = GetCharm(); + if(!unit) + { + if(uint64 guid = GetUInt64Value(UNIT_FIELD_SUMMON)) + unit = ObjectAccessor::GetUnit(*this, guid); + } + return unit; +} + +void Unit::RemoveAllControlled() +{ + //possessed pet and vehicle + if(GetTypeId() == TYPEID_PLAYER) + ((Player*)this)->StopCastingCharm(); + + while(!m_Controlled.empty()) + { + Unit *target = *m_Controlled.begin(); + m_Controlled.erase(m_Controlled.begin()); + if(target->GetCharmerGUID() == GetGUID()) + { + target->RemoveCharmAuras(); + } + else if(target->GetOwnerGUID() == GetGUID() + && target->GetTypeId() == TYPEID_UNIT + && ((Creature*)target)->HasSummonMask(SUMMON_MASK_SUMMON)) + { + + if(!((TempSummon*)target)->isPet()) + ((TempSummon*)target)->UnSummon(); + } + else + { + sLog.outError("Unit %u is trying to release unit %u which is neither charmed nor owned by it", GetEntry(), target->GetEntry()); + } + } + if(GetPetGUID() != GetUInt64Value(UNIT_FIELD_SUMMON)) + sLog.outCrash("Unit %u is not able to release its summon %u", GetEntry(), GetPetGUID()); + if(GetCharmGUID()) + sLog.outCrash("Unit %u is not able to release its charm %u", GetEntry(), GetCharmGUID()); +} + +Unit* Unit::GetNextRandomRaidMemberOrPet(float radius) +{ + Player* player = NULL; if(GetTypeId() == TYPEID_PLAYER) - SetUInt64Value(UNIT_FIELD_CHARM, pet ? pet->GetGUID() : 0); + player = (Player*)this; + // Should we enable this also for charmed units? + else if (GetTypeId() == TYPEID_UNIT && ((Creature*)this)->isPet()) + player=(Player*)GetOwner(); + + if (!player) + return NULL; + Group *pGroup = player->GetGroup(); + //When there is no group check pet presence + if (!pGroup) + { + // We are pet now, return owner + if(player!=this) + return IsWithinDistInMap(player, radius) ? player : NULL; + Unit * pet = GetGuardianPet(); + //No pet, no group, nothing to return + if (!pet) + return NULL; + // We are owner now, return pet + return IsWithinDistInMap(pet, radius) ? pet : NULL; + } + + std::vector<Unit*> nearMembers; + //reserve place for players and pets because resizing vector every unit push is unefficient (vector is reallocated then) + nearMembers.reserve(pGroup->GetMembersCount()*2); + + for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player* Target = itr->getSource(); + + // IsHostileTo check duel and controlled by enemy + if( Target && Target !=this && Target->isAlive() && IsWithinDistInMap(Target, radius) && + !IsHostileTo(Target) ) + nearMembers.push_back(Target); + + // Push player's pet to vector + Unit * pet = Target->GetGuardianPet(); + if (pet && pet !=this && pet->isAlive() && IsWithinDistInMap(pet, radius) && + !IsHostileTo(pet) ) + nearMembers.push_back(pet); + } + + if (nearMembers.empty()) + return NULL; + + uint32 randTarget = urand(0,nearMembers.size()-1); + return nearMembers[randTarget]; } +//only called in Player::SetSeer void Unit::AddPlayerToVision(Player* plr) { if(m_sharedVision.empty()) @@ -8191,9 +8213,9 @@ void Unit::AddPlayerToVision(Player* plr) SetWorldObject(true); } m_sharedVision.push_back(plr); - plr->SetFarsightTarget(this); } +//only called in Player::SetSeer void Unit::RemovePlayerFromVision(Player* plr) { m_sharedVision.remove(plr); @@ -8202,31 +8224,30 @@ void Unit::RemovePlayerFromVision(Player* plr) setActive(false); SetWorldObject(false); } - plr->ClearFarsight(); } void Unit::RemoveBindSightAuras() { - RemoveSpellsCausingAura(SPELL_AURA_BIND_SIGHT); + RemoveAurasByType(SPELL_AURA_BIND_SIGHT); } void Unit::RemoveCharmAuras() { - RemoveSpellsCausingAura(SPELL_AURA_MOD_CHARM); - RemoveSpellsCausingAura(SPELL_AURA_MOD_POSSESS_PET); - RemoveSpellsCausingAura(SPELL_AURA_MOD_POSSESS); + RemoveAurasByType(SPELL_AURA_MOD_CHARM); + RemoveAurasByType(SPELL_AURA_MOD_POSSESS_PET); + RemoveAurasByType(SPELL_AURA_MOD_POSSESS); } void Unit::UnsummonAllTotems() { - for (int8 i = 0; i < MAX_TOTEM; ++i) + for (int8 i = 0; i < MAX_SUMMON_SLOT; ++i) { - if(!m_TotemSlot[i]) + if(!m_SummonSlot[i]) continue; - Creature *OldTotem = ObjectAccessor::GetCreature(*this, m_TotemSlot[i]); - if (OldTotem && OldTotem->isTotem()) - ((Totem*)OldTotem)->UnSummon(); + Creature *OldTotem = ObjectAccessor::GetCreature(*this, m_SummonSlot[i]); + if(OldTotem && OldTotem->isSummon()) + ((TempSummon*)OldTotem)->UnSummon(); } } @@ -8238,6 +8259,7 @@ void Unit::SendHealSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage, bool c data.append(GetPackGUID()); data << uint32(SpellID); data << uint32(Damage); + data << uint32(0); // over healing? data << uint8(critical ? 1 : 0); data << uint8(0); // unused in client? SendMessageToSet(&data, true); @@ -8254,426 +8276,319 @@ void Unit::SendEnergizeSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage, Po SendMessageToSet(&data, true); } -uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint32 pdamage, DamageEffectType damagetype) +uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint32 pdamage, DamageEffectType damagetype, uint32 stack) { if(!spellProto || !pVictim || damagetype==DIRECT_DAMAGE ) return pdamage; - //if(spellProto->SchoolMask == SPELL_SCHOOL_MASK_NORMAL) - // return pdamage; - //damage = CalcArmorReducedDamage(pVictim, damage); - - int32 BonusDamage = 0; - if( GetTypeId()==TYPEID_UNIT ) - { - // Pets just add their bonus damage to their spell damage - // note that their spell damage is just gain of their own auras - if (((Creature*)this)->isPet()) - { - BonusDamage = ((Pet*)this)->GetBonusDamage(); - } - // For totems get damage bonus from owner (statue isn't totem in fact) - else if (((Creature*)this)->isTotem() && ((Totem*)this)->GetTotemType()!=TOTEM_STATUE) - { - if(Unit* owner = GetOwner()) - return owner->SpellDamageBonus(pVictim, spellProto, pdamage, damagetype); - } - } - - // Damage Done - uint32 CastingTime = !IsChanneledSpell(spellProto) ? GetSpellCastTime(spellProto) : GetSpellDuration(spellProto); - - // Taken/Done fixed damage bonus auras - int32 DoneAdvertisedBenefit = SpellBaseDamageBonus(GetSpellSchoolMask(spellProto))+BonusDamage; - int32 TakenAdvertisedBenefit = SpellBaseDamageBonusForVictim(GetSpellSchoolMask(spellProto), pVictim); - - // Damage over Time spells bonus calculation - float DotFactor = 1.0f; - if(damagetype == DOT) + // For totems get damage bonus from owner (statue isn't totem in fact) + if( GetTypeId()==TYPEID_UNIT && ((Creature*)this)->isTotem() && ((Totem*)this)->GetTotemType()!=TOTEM_STATUE) { - int32 DotDuration = GetSpellDuration(spellProto); - // 200% limit - if(DotDuration > 0) - { - if(DotDuration > 30000) DotDuration = 30000; - if(!IsChanneledSpell(spellProto)) DotFactor = DotDuration / 15000.0f; - int x = 0; - for(int j = 0; j < 3; j++) - { - if( spellProto->Effect[j] == SPELL_EFFECT_APPLY_AURA && ( - spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_DAMAGE || - spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_LEECH) ) - { - x = j; - break; - } - } - int DotTicks = 6; - if(spellProto->EffectAmplitude[x] != 0) - DotTicks = DotDuration / spellProto->EffectAmplitude[x]; - if(DotTicks) - { - DoneAdvertisedBenefit /= DotTicks; - TakenAdvertisedBenefit /= DotTicks; - } - } + if(Unit* owner = GetOwner()) + return owner->SpellDamageBonus(pVictim, spellProto, pdamage, damagetype); } // Taken/Done total percent damage auras float DoneTotalMod = 1.0f; float TakenTotalMod = 1.0f; + int32 DoneTotal = 0; + int32 TakenTotal = 0; // ..done - AuraList const& mModDamagePercentDone = GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE); - for(AuraList::const_iterator i = mModDamagePercentDone.begin(); i != mModDamagePercentDone.end(); ++i) + // Pet damage + if( GetTypeId() == TYPEID_UNIT && !((Creature*)this)->isPet() ) + DoneTotalMod *= ((Creature*)this)->GetSpellDamageMod(((Creature*)this)->GetCreatureInfo()->rank); + + AuraEffectList const& mModDamagePercentDone = GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE); + for(AuraEffectList::const_iterator i = mModDamagePercentDone.begin(); i != mModDamagePercentDone.end(); ++i) { - if( ((*i)->GetModifier()->m_miscvalue & GetSpellSchoolMask(spellProto)) && + if( ((*i)->GetMiscValue() & GetSpellSchoolMask(spellProto)) && (*i)->GetSpellProto()->EquippedItemClass == -1 && // -1 == any item class (not wand then) (*i)->GetSpellProto()->EquippedItemInventoryTypeMask == 0 ) // 0 == any inventory type (not wand then) { - DoneTotalMod *= ((*i)->GetModifierValue() +100.0f)/100.0f; + DoneTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f; } } uint32 creatureTypeMask = pVictim->GetCreatureTypeMask(); - AuraList const& mDamageDoneVersus = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS); - for(AuraList::const_iterator i = mDamageDoneVersus.begin();i != mDamageDoneVersus.end(); ++i) - if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue)) - DoneTotalMod *= ((*i)->GetModifierValue() +100.0f)/100.0f; - - // ..taken - AuraList const& mModDamagePercentTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN); - for(AuraList::const_iterator i = mModDamagePercentTaken.begin(); i != mModDamagePercentTaken.end(); ++i) - if( (*i)->GetModifier()->m_miscvalue & GetSpellSchoolMask(spellProto) ) - TakenTotalMod *= ((*i)->GetModifierValue() +100.0f)/100.0f; - - // .. taken pct: scripted (increases damage of * against targets *) - AuraList const& mOverrideClassScript = GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); - for(AuraList::const_iterator i = mOverrideClassScript.begin(); i != mOverrideClassScript.end(); ++i) - { - switch((*i)->GetModifier()->m_miscvalue) - { - //Molten Fury - case 4920: case 4919: - if(pVictim->HasAuraState(AURA_STATE_HEALTHLESS_20_PERCENT)) - TakenTotalMod *= (100.0f+(*i)->GetModifier()->m_amount)/100.0f; break; - } - } - - bool hasmangle=false; - // .. taken pct: dummy auras - AuraList const& mDummyAuras = pVictim->GetAurasByType(SPELL_AURA_DUMMY); - for(AuraList::const_iterator i = mDummyAuras.begin(); i != mDummyAuras.end(); ++i) - { - switch((*i)->GetSpellProto()->SpellIconID) + // Add flat bonus from spell damage versus + DoneTotal += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_FLAT_SPELL_DAMAGE_VERSUS, creatureTypeMask); + AuraEffectList const& mDamageDoneVersus = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS); + for(AuraEffectList::const_iterator i = mDamageDoneVersus.begin();i != mDamageDoneVersus.end(); ++i) + if(creatureTypeMask & uint32((*i)->GetMiscValue())) + DoneTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f; + + // done scripted mod (take it from owner) + Unit *owner = GetOwner(); + if (!owner) owner = this; + AuraEffectList const& mOverrideClassScript= owner->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); + for(AuraEffectList::const_iterator i = mOverrideClassScript.begin(); i != mOverrideClassScript.end(); ++i) + { + if (!(*i)->isAffectedOnSpell(spellProto)) + continue; + switch((*i)->GetMiscValue()) { - //Cheat Death - case 2109: - if( ((*i)->GetModifier()->m_miscvalue & GetSpellSchoolMask(spellProto)) ) - { - if(pVictim->GetTypeId() != TYPEID_PLAYER) - continue; - float mod = -((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_SPELL)*2*4; - if (mod < (*i)->GetModifier()->m_amount) - mod = (*i)->GetModifier()->m_amount; - TakenTotalMod *= (mod+100.0f)/100.0f; - } + case 4920: // Molten Fury + case 4919: + case 6917: // Death's Embrace + case 6926: + case 6928: + { + if(pVictim->HasAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, spellProto, this)) + DoneTotalMod *= (100.0f+(*i)->GetAmount())/100.0f; break; - //This is changed in WLK, using aura 255 - //Mangle - case 2312: - case 44955: - // don't apply mod twice - if (hasmangle) - break; - hasmangle=true; - for(int j=0;j<3;j++) - { - if(GetEffectMechanic(spellProto, j)==MECHANIC_BLEED) + } + // Soul Siphon + case 4992: + case 4993: + { + // effect 1 m_amount + int32 maxPercent = (*i)->GetAmount(); + // effect 0 m_amount + int32 stepPercent = CalculateSpellDamage((*i)->GetSpellProto(), 0, (*i)->GetSpellProto()->EffectBasePoints[0], this); + // count affliction effects and calc additional damage in percentage + int32 modPercent = 0; + AuraMap const& victimAuras = pVictim->GetAuras(); + for (AuraMap::const_iterator itr = victimAuras.begin(); itr != victimAuras.end(); ++itr) + { + SpellEntry const* m_spell = itr->second->GetSpellProto(); + if (m_spell->SpellFamilyName != SPELLFAMILY_WARLOCK || !(m_spell->SpellFamilyFlags[1] & 0x0004071B || m_spell->SpellFamilyFlags[0] & 0x8044C402)) + continue; + modPercent += stepPercent * itr->second->GetStackAmount(); + if (modPercent >= maxPercent) { - TakenTotalMod *= (100.0f+(*i)->GetModifier()->m_amount)/100.0f; + modPercent = maxPercent; break; } } + DoneTotalMod *= (modPercent+100.0f)/100.0f; break; - } - } - - // Distribute Damage over multiple effects, reduce by AoE - CastingTime = GetCastingTimeForBonus( spellProto, damagetype, CastingTime ); - - // 50% for damage and healing spells for leech spells from damage bonus and 0% from healing - for(int j = 0; j < 3; ++j) - { - if( spellProto->Effect[j] == SPELL_EFFECT_HEALTH_LEECH || - spellProto->Effect[j] == SPELL_EFFECT_APPLY_AURA && spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_LEECH ) - { - CastingTime /= 2; - break; - } - } - - switch(spellProto->SpellFamilyName) - { - case SPELLFAMILY_GENERIC: - // Siphon Essence - 0% - if(spellProto->AttributesEx == 268435456 && spellProto->SpellIconID == 2027) - { - CastingTime = 0; - } - // Goblin Rocket Launcher - 0% - else if (spellProto->SpellIconID == 184 && spellProto->Attributes == 4259840) - { - CastingTime = 0; - } - // Darkmoon Card: Vengeance - 0.1% - else if (spellProto->SpellVisual == 9850 && spellProto->SpellIconID == 2230) - { - CastingTime = 3.5; - } - case SPELLFAMILY_MAGE: - // Ignite - do not modify, it is (8*Rank)% damage of procing Spell - if(spellProto->Id==12654) - { - return pdamage; - } - // Ice Lance - else if((spellProto->SpellFamilyFlags & 0x20000LL) && spellProto->SpellIconID == 186) - { - CastingTime /= 3; // applied 1/3 bonuses in case generic target - if(pVictim->isFrozen()) // and compensate this for frozen target. - TakenTotalMod *= 3.0f; - } - // Pyroblast - 115% of Fire Damage, DoT - 20% of Fire Damage - else if((spellProto->SpellFamilyFlags & 0x400000LL) && spellProto->SpellIconID == 184 ) - { - DotFactor = damagetype == DOT ? 0.2f : 1.0f; - CastingTime = damagetype == DOT ? 3500 : 4025; - } - // Fireball - 100% of Fire Damage, DoT - 0% of Fire Damage - else if((spellProto->SpellFamilyFlags & 0x1LL) && spellProto->SpellIconID == 185) - { - CastingTime = 3500; - DotFactor = damagetype == DOT ? 0.0f : 1.0f; - } - // Molten armor - else if (spellProto->SpellFamilyFlags & 0x0000000800000000LL) - { - CastingTime = 0; - } - // Arcane Missiles triggered spell - else if ((spellProto->SpellFamilyFlags & 0x200000LL) && spellProto->SpellIconID == 225) - { - CastingTime = 1000; - } - // Blizzard triggered spell - else if ((spellProto->SpellFamilyFlags & 0x80080LL) && spellProto->SpellIconID == 285) - { - CastingTime = 500; } - break; - case SPELLFAMILY_WARLOCK: - // Life Tap - if((spellProto->SpellFamilyFlags & 0x40000LL) && spellProto->SpellIconID == 208) - { - CastingTime = 2800; // 80% from +shadow damage - DoneTotalMod = 1.0f; - TakenTotalMod = 1.0f; - } - // Dark Pact - else if((spellProto->SpellFamilyFlags & 0x80000000LL) && spellProto->SpellIconID == 154 && GetPetGUID()) - { - CastingTime = 3360; // 96% from +shadow damage - DoneTotalMod = 1.0f; - TakenTotalMod = 1.0f; - } - // Soul Fire - 115% of Fire Damage - else if((spellProto->SpellFamilyFlags & 0x8000000000LL) && spellProto->SpellIconID == 184) - { - CastingTime = 4025; - } - // Curse of Agony - 120% of Shadow Damage - else if((spellProto->SpellFamilyFlags & 0x0000000400LL) && spellProto->SpellIconID == 544) - { - DotFactor = 1.2f; - } - // Drain Mana - 0% of Shadow Damage - else if((spellProto->SpellFamilyFlags & 0x10LL) && spellProto->SpellIconID == 548) + case 6916: // Death's Embrace + case 6925: + case 6927: + if (HasAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, spellProto, this)) + DoneTotalMod *= (100.0f+(*i)->GetAmount())/100.0f; + break; + case 5481: // Starfire Bonus { - CastingTime = 0; + if (pVictim->GetAura(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_DRUID, 0x200002, 0)) + DoneTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f; + break; } - // Drain Soul 214.3% - else if ((spellProto->SpellFamilyFlags & 0x4000LL) && spellProto->SpellIconID == 113 ) + case 4418: // Increased Shock Damage + case 4554: // Increased Lightning Damage + case 4555: // Improved Moonfire + case 5142: // Increased Lightning Damage + case 5147: // Improved Consecration / Libram of Resurgence + case 5148: // Idol of the Shooting Star + case 6008: // Increased Lightning Damage / Totem of Hex { - CastingTime = 7500; + DoneTotal+=(*i)->GetAmount(); + break; } - // Hellfire - else if ((spellProto->SpellFamilyFlags & 0x40LL) && spellProto->SpellIconID == 937) + // Tundra Stalker + // Merciless Combat + case 7277: { - CastingTime = damagetype == DOT ? 5000 : 500; // self damage seems to be so + // Merciless Combat + if ((*i)->GetSpellProto()->SpellIconID == 2656) + { + if(pVictim->HasAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, spellProto, this)) + DoneTotalMod *= (100.0f+(*i)->GetAmount())/100.0f; + } + else // Tundra Stalker + { + if (pVictim->GetAura(SPELL_AURA_DUMMY, SPELLFAMILY_DEATHKNIGHT,0, 0x04000000,0)) + DoneTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f; + break; + } + break; } - // Unstable Affliction - 180% - else if (spellProto->Id == 31117 && spellProto->SpellIconID == 232) + case 7293: // Rage of Rivendare { - CastingTime = 6300; + if (pVictim->GetAura(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_DEATHKNIGHT, 0,0x02000000,0)) + DoneTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f; + break; } - // Corruption 93% - else if ((spellProto->SpellFamilyFlags & 0x2LL) && spellProto->SpellIconID == 313) + // Twisted Faith + case 7377: { - DotFactor = 0.93f; + if (pVictim->GetAura(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_PRIEST, 0x8000, 0,0, GetGUID())) + DoneTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f; + break; } - break; - case SPELLFAMILY_PALADIN: - // Consecration - 95% of Holy Damage - if((spellProto->SpellFamilyFlags & 0x20LL) && spellProto->SpellIconID == 51) + // Marked for Death + case 7598: + case 7599: + case 7600: + case 7601: + case 7602: { - DotFactor = 0.95f; - CastingTime = 3500; + if (pVictim->GetAura(SPELL_AURA_MOD_STALKED, SPELLFAMILY_HUNTER, 0x400, 0)) + DoneTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f; + break; } - // Seal of Righteousness - 10.2%/9.8% ( based on weapon type ) of Holy Damage, multiplied by weapon speed - else if((spellProto->SpellFamilyFlags & 0x8000000LL) && spellProto->SpellIconID == 25) - { - Item *item = ((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); - float wspeed = GetAttackTime(BASE_ATTACK)/1000.0f; + } + } - if( item && item->GetProto()->InventoryType == INVTYPE_2HWEAPON) - CastingTime = uint32(wspeed*3500*0.102f); - else - CastingTime = uint32(wspeed*3500*0.098f); - } - // Judgement of Righteousness - 73% - else if ((spellProto->SpellFamilyFlags & 1024) && spellProto->SpellIconID == 25) - { - CastingTime = 2555; - } - // Seal of Vengeance - 17% per Fully Stacked Tick - 5 Applications - else if ((spellProto->SpellFamilyFlags & 0x80000000000LL) && spellProto->SpellIconID == 2292) - { - DotFactor = 0.85f; - CastingTime = 3500; - } - // Holy shield - 5% of Holy Damage - else if ((spellProto->SpellFamilyFlags & 0x4000000000LL) && spellProto->SpellIconID == 453) - { - CastingTime = 175; - } - // Blessing of Sanctuary - 0% - else if ((spellProto->SpellFamilyFlags & 0x10000000LL) && spellProto->SpellIconID == 29) - { - CastingTime = 0; - } - // Seal of Righteousness trigger - already computed for parent spell - else if ( spellProto->SpellFamilyName==SPELLFAMILY_PALADIN && spellProto->SpellIconID==25 && spellProto->AttributesEx4 & 0x00800000LL ) - { - return pdamage; - } - break; - case SPELLFAMILY_SHAMAN: - // totem attack - if (spellProto->SpellFamilyFlags & 0x000040000000LL) - { - if (spellProto->SpellIconID == 33) // Fire Nova totem attack must be 21.4%(untested) - CastingTime = 749; // ignore CastingTime and use as modifier - else if (spellProto->SpellIconID == 680) // Searing Totem attack 8% - CastingTime = 280; // ignore CastingTime and use as modifier - else if (spellProto->SpellIconID == 37) // Magma totem attack must be 6.67%(untested) - CastingTime = 234; // ignore CastingTimePenalty and use as modifier - } - // Lightning Shield (and proc shield from T2 8 pieces bonus ) 33% per charge - else if( (spellProto->SpellFamilyFlags & 0x00000000400LL) || spellProto->Id == 23552) - CastingTime = 1155; // ignore CastingTimePenalty and use as modifier - break; - case SPELLFAMILY_PRIEST: - // Mana Burn - 0% of Shadow Damage - if((spellProto->SpellFamilyFlags & 0x10LL) && spellProto->SpellIconID == 212) - { - CastingTime = 0; - } - // Mind Flay - 59% of Shadow Damage - else if((spellProto->SpellFamilyFlags & 0x800000LL) && spellProto->SpellIconID == 548) - { - CastingTime = 2065; - } - // Holy Fire - 86.71%, DoT - 16.5% - else if ((spellProto->SpellFamilyFlags & 0x100000LL) && spellProto->SpellIconID == 156) - { - DotFactor = damagetype == DOT ? 0.165f : 1.0f; - CastingTime = damagetype == DOT ? 3500 : 3035; - } - // Shadowguard - 28% per charge - else if ((spellProto->SpellFamilyFlags & 0x2000000LL) && spellProto->SpellIconID == 19) - { - CastingTime = 980; - } - // Touch of Weakeness - 10% - else if ((spellProto->SpellFamilyFlags & 0x80000LL) && spellProto->SpellIconID == 1591) - { - CastingTime = 350; - } - // Reflective Shield (back damage) - 0% (other spells fit to check not have damage effects/auras) - else if (spellProto->SpellFamilyFlags == 0 && spellProto->SpellIconID == 566) - { - CastingTime = 0; - } - // Holy Nova - 14% - else if ((spellProto->SpellFamilyFlags & 0x400000LL) && spellProto->SpellIconID == 1874) - { - CastingTime = 500; - } - break; - case SPELLFAMILY_DRUID: - // Hurricane triggered spell - if((spellProto->SpellFamilyFlags & 0x400000LL) && spellProto->SpellIconID == 220) + // Custom scripted damage + // Ice Lance + if (spellProto->SpellFamilyName == SPELLFAMILY_MAGE && spellProto->SpellIconID == 186) + { + if (pVictim->HasAuraState(AURA_STATE_FROZEN, spellProto, this)) + DoneTotalMod *= 3.0f; + } + + // Torment the weak + if (spellProto->SpellFamilyName== SPELLFAMILY_MAGE && (spellProto->SpellFamilyFlags[0]&0x20200021 || spellProto->SpellFamilyFlags[1]& 0x9000)) + { + if(pVictim->HasAuraType(SPELL_AURA_MOD_DECREASE_SPEED)) + { + AuraEffectList const& mDumyAuras = GetAurasByType(SPELL_AURA_DUMMY); + for(AuraEffectList::const_iterator i = mDumyAuras.begin(); i != mDumyAuras.end(); ++i) { - CastingTime = 500; + if ((*i)->GetSpellProto()->SpellIconID == 3263) + { + DoneTotalMod *=float((*i)->GetAmount() + 100.f) / 100.f; + break; + } } - break; - case SPELLFAMILY_WARRIOR: - case SPELLFAMILY_HUNTER: - case SPELLFAMILY_ROGUE: - CastingTime = 0; - break; - default: - break; + } } - float LvlPenalty = CalculateLevelPenalty(spellProto); - - // Spellmod SpellDamage - //float SpellModSpellDamage = 100.0f; - float CoefficientPtc = DotFactor * 100.0f; - if(spellProto->SchoolMask != SPELL_SCHOOL_MASK_NORMAL) - CoefficientPtc *= ((float)CastingTime/3500.0f); + // ..taken + AuraEffectList const& mModDamagePercentTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN); + for(AuraEffectList::const_iterator i = mModDamagePercentTaken.begin(); i != mModDamagePercentTaken.end(); ++i) + if( (*i)->GetMiscValue() & GetSpellSchoolMask(spellProto) ) + TakenTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f; - if(Player* modOwner = GetSpellModOwner()) - //modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_SPELL_BONUS_DAMAGE,SpellModSpellDamage); - modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_SPELL_BONUS_DAMAGE,CoefficientPtc); + // .. taken pct: dummy auras + if (pVictim->GetTypeId() == TYPEID_PLAYER) + { + //Cheat Death + if (AuraEffect *dummy = pVictim->GetDummyAura(45182)) + { + float mod = -((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_SPELL)*2*4; + if (mod < dummy->GetAmount()) + mod = dummy->GetAmount(); + TakenTotalMod *= (mod+100.0f)/100.0f; + } + } - //SpellModSpellDamage /= 100.0f; - CoefficientPtc /= 100.0f; + // From caster spells + AuraEffectList const& mOwnerTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_FROM_CASTER); + for(AuraEffectList::const_iterator i = mOwnerTaken.begin(); i != mOwnerTaken.end(); ++i) + if( (*i)->GetCasterGUID() == GetGUID() && (*i)->isAffectedOnSpell(spellProto)) + TakenTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f; - //float DoneActualBenefit = DoneAdvertisedBenefit * (CastingTime / 3500.0f) * DotFactor * SpellModSpellDamage * LvlPenalty; + // Mod damage from spell mechanic + uint32 mechanicMask = GetAllSpellMechanicMask(spellProto); + if (mechanicMask) + { + AuraEffectList const& mDamageDoneMechanic = pVictim->GetAurasByType(SPELL_AURA_MOD_MECHANIC_DAMAGE_TAKEN_PERCENT); + for(AuraEffectList::const_iterator i = mDamageDoneMechanic.begin();i != mDamageDoneMechanic.end(); ++i) + if(mechanicMask & uint32(1<<((*i)->GetMiscValue()))) + TakenTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f; + } - float DoneActualBenefit = DoneAdvertisedBenefit * CoefficientPtc * LvlPenalty; - float TakenActualBenefit = TakenAdvertisedBenefit * DotFactor * LvlPenalty; - if(spellProto->SpellFamilyName && spellProto->SchoolMask != SPELL_SCHOOL_MASK_NORMAL) - TakenActualBenefit *= ((float)CastingTime / 3500.0f); + // Taken/Done fixed damage bonus auras + int32 DoneAdvertisedBenefit = SpellBaseDamageBonus(GetSpellSchoolMask(spellProto)); + int32 TakenAdvertisedBenefit = SpellBaseDamageBonusForVictim(GetSpellSchoolMask(spellProto), pVictim); + // Pets just add their bonus damage to their spell damage + // note that their spell damage is just gain of their own auras + if (GetTypeId() == TYPEID_UNIT && ((Creature*)this)->HasSummonMask(SUMMON_MASK_GUARDIAN)) + DoneAdvertisedBenefit += ((Guardian*)this)->GetBonusDamage(); + + // Check for table values + float coeff; + SpellBonusEntry const* bonus = spellmgr.GetSpellBonusData(spellProto->Id); + if (bonus) + { + if (damagetype == DOT) + coeff = bonus->dot_damage; + else + coeff = bonus->direct_damage; + if (bonus->ap_bonus) + DoneTotal+=bonus->ap_bonus * GetTotalAttackPowerValue(BASE_ATTACK) * stack; + } + // Default calculation + if (DoneAdvertisedBenefit || TakenAdvertisedBenefit) + { + if(!bonus) + { + // Damage Done from spell damage bonus + int32 CastingTime = !IsChanneledSpell(spellProto) ? GetSpellCastTime(spellProto) : GetSpellDuration(spellProto); + // Damage over Time spells bonus calculation + float DotFactor = 1.0f; + if(damagetype == DOT) + { + int32 DotDuration = GetSpellDuration(spellProto); + // 200% limit + if(DotDuration > 0) + { + if(DotDuration > 30000) DotDuration = 30000; + if(!IsChanneledSpell(spellProto)) DotFactor = DotDuration / 15000.0f; + int x = 0; + for(int j = 0; j < 3; j++) + { + if( spellProto->Effect[j] == SPELL_EFFECT_APPLY_AURA && ( + spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_DAMAGE || + spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_LEECH) ) + { + x = j; + break; + } + } + int32 DotTicks = 6; + if(spellProto->EffectAmplitude[x] != 0) + DotTicks = DotDuration / spellProto->EffectAmplitude[x]; + if(DotTicks) + { + DoneAdvertisedBenefit /= DotTicks; + TakenAdvertisedBenefit /= DotTicks; + } + } + } + // Distribute Damage over multiple effects, reduce by AoE + CastingTime = GetCastingTimeForBonus( spellProto, damagetype, CastingTime ); - float tmpDamage = (float(pdamage)+DoneActualBenefit)*DoneTotalMod; + // 50% for damage and healing spells for leech spells from damage bonus and 0% from healing + for(int j = 0; j < 3; ++j) + { + if( spellProto->Effect[j] == SPELL_EFFECT_HEALTH_LEECH || + spellProto->Effect[j] == SPELL_EFFECT_APPLY_AURA && spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_LEECH ) + { + CastingTime /= 2; + break; + } + } + if(spellProto->SchoolMask != SPELL_SCHOOL_MASK_NORMAL) + coeff = (CastingTime / 3500.0f) * DotFactor; + else + coeff = DotFactor; + } - // Add flat bonus from spell damage versus - tmpDamage += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_FLAT_SPELL_DAMAGE_VERSUS, creatureTypeMask); + float coeff2 = CalculateLevelPenalty(spellProto) * stack; + if(spellProto->SpellFamilyName) //TODO: fix this + TakenTotal+= TakenAdvertisedBenefit * coeff * coeff2; + if(Player* modOwner = GetSpellModOwner()) + { + coeff *= 100.0f; + modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_SPELL_BONUS_DAMAGE, coeff); + coeff /= 100.0f; + } + DoneTotal += DoneAdvertisedBenefit * coeff * coeff2; + } - // apply spellmod to Done damage + float tmpDamage = (pdamage + DoneTotal) * DoneTotalMod; + // apply spellmod to Done damage (flat and pct) if(Player* modOwner = GetSpellModOwner()) modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, tmpDamage); - tmpDamage = (tmpDamage+TakenActualBenefit)*TakenTotalMod; - - if( GetTypeId() == TYPEID_UNIT && !((Creature*)this)->isPet() ) - tmpDamage *= ((Creature*)this)->GetSpellDamageMod(((Creature*)this)->GetCreatureInfo()->rank); + tmpDamage = (tmpDamage + TakenTotal) * TakenTotalMod; return tmpDamage > 0 ? uint32(tmpDamage) : 0; } @@ -8683,39 +8598,36 @@ int32 Unit::SpellBaseDamageBonus(SpellSchoolMask schoolMask) int32 DoneAdvertisedBenefit = 0; // ..done - AuraList const& mDamageDone = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE); - for(AuraList::const_iterator i = mDamageDone.begin();i != mDamageDone.end(); ++i) - if(((*i)->GetModifier()->m_miscvalue & schoolMask) != 0 && + AuraEffectList const& mDamageDone = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE); + for(AuraEffectList::const_iterator i = mDamageDone.begin();i != mDamageDone.end(); ++i) + if(((*i)->GetMiscValue() & schoolMask) != 0 && (*i)->GetSpellProto()->EquippedItemClass == -1 && // -1 == any item class (not wand then) (*i)->GetSpellProto()->EquippedItemInventoryTypeMask == 0 ) // 0 == any inventory type (not wand then) - DoneAdvertisedBenefit += (*i)->GetModifierValue(); + DoneAdvertisedBenefit += (*i)->GetAmount(); if (GetTypeId() == TYPEID_PLAYER) { + // Base value + DoneAdvertisedBenefit +=((Player*)this)->GetBaseSpellDamageBonus(); + // Damage bonus from stats - AuraList const& mDamageDoneOfStatPercent = GetAurasByType(SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT); - for(AuraList::const_iterator i = mDamageDoneOfStatPercent.begin();i != mDamageDoneOfStatPercent.end(); ++i) + AuraEffectList const& mDamageDoneOfStatPercent = GetAurasByType(SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT); + for(AuraEffectList::const_iterator i = mDamageDoneOfStatPercent.begin();i != mDamageDoneOfStatPercent.end(); ++i) { - if((*i)->GetModifier()->m_miscvalue & schoolMask) + if((*i)->GetMiscValue() & schoolMask) { - SpellEntry const* iSpellProto = (*i)->GetSpellProto(); - uint8 eff = (*i)->GetEffIndex(); - - // stat used dependent from next effect aura SPELL_AURA_MOD_SPELL_HEALING presence and misc value (stat index) - Stats usedStat = STAT_INTELLECT; - if(eff < 2 && iSpellProto->EffectApplyAuraName[eff+1]==SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT) - usedStat = Stats(iSpellProto->EffectMiscValue[eff+1]); - - DoneAdvertisedBenefit += int32(GetStat(usedStat) * (*i)->GetModifierValue() / 100.0f); + // stat used stored in miscValueB for this aura + Stats usedStat = Stats((*i)->GetMiscBValue()); + DoneAdvertisedBenefit += int32(GetStat(usedStat) * (*i)->GetAmount() / 100.0f); } } // ... and attack power - AuraList const& mDamageDonebyAP = GetAurasByType(SPELL_AURA_MOD_SPELL_DAMAGE_OF_ATTACK_POWER); - for(AuraList::const_iterator i =mDamageDonebyAP.begin();i != mDamageDonebyAP.end(); ++i) - if ((*i)->GetModifier()->m_miscvalue & schoolMask) - DoneAdvertisedBenefit += int32(GetTotalAttackPowerValue(BASE_ATTACK) * (*i)->GetModifierValue() / 100.0f); + AuraEffectList const& mDamageDonebyAP = GetAurasByType(SPELL_AURA_MOD_SPELL_DAMAGE_OF_ATTACK_POWER); + for(AuraEffectList::const_iterator i =mDamageDonebyAP.begin();i != mDamageDonebyAP.end(); ++i) + if ((*i)->GetMiscValue() & schoolMask) + DoneAdvertisedBenefit += int32(GetTotalAttackPowerValue(BASE_ATTACK) * (*i)->GetAmount() / 100.0f); } return DoneAdvertisedBenefit; @@ -8727,16 +8639,16 @@ int32 Unit::SpellBaseDamageBonusForVictim(SpellSchoolMask schoolMask, Unit *pVic int32 TakenAdvertisedBenefit = 0; // ..done (for creature type by mask) in taken - AuraList const& mDamageDoneCreature = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE_CREATURE); - for(AuraList::const_iterator i = mDamageDoneCreature.begin();i != mDamageDoneCreature.end(); ++i) - if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue)) - TakenAdvertisedBenefit += (*i)->GetModifierValue(); + AuraEffectList const& mDamageDoneCreature = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE_CREATURE); + for(AuraEffectList::const_iterator i = mDamageDoneCreature.begin();i != mDamageDoneCreature.end(); ++i) + if(creatureTypeMask & uint32((*i)->GetMiscValue())) + TakenAdvertisedBenefit += (*i)->GetAmount(); // ..taken - AuraList const& mDamageTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_TAKEN); - for(AuraList::const_iterator i = mDamageTaken.begin();i != mDamageTaken.end(); ++i) - if(((*i)->GetModifier()->m_miscvalue & schoolMask) != 0) - TakenAdvertisedBenefit += (*i)->GetModifierValue(); + AuraEffectList const& mDamageTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_TAKEN); + for(AuraEffectList::const_iterator i = mDamageTaken.begin();i != mDamageTaken.end(); ++i) + if(((*i)->GetMiscValue() & schoolMask) != 0) + TakenAdvertisedBenefit += (*i)->GetAmount(); return TakenAdvertisedBenefit; } @@ -8765,30 +8677,87 @@ bool Unit::isSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolM crit_chance += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, schoolMask); } // taken - if (pVictim && !IsPositiveSpell(spellProto->Id)) - { - // Modify critical chance by victim SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE - crit_chance += pVictim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE, schoolMask); - // Modify critical chance by victim SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE - crit_chance += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE); - // Modify by player victim resilience - if (pVictim->GetTypeId() == TYPEID_PLAYER) - crit_chance -= ((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_SPELL); + if (pVictim) + { + if (!IsPositiveSpell(spellProto->Id)) + { + // Modify critical chance by victim SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE + crit_chance += pVictim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE, schoolMask); + // Modify critical chance by victim SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE + crit_chance += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE); + // Modify by player victim resilience + if (pVictim->GetTypeId() == TYPEID_PLAYER) + crit_chance -= ((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_SPELL); + } // scripted (increase crit chance ... against ... target by x% - if(pVictim->isFrozen()) // Shatter + AuraEffectList const& mOverrideClassScript = GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); + for(AuraEffectList::const_iterator i = mOverrideClassScript.begin(); i != mOverrideClassScript.end(); ++i) { - AuraList const& mOverrideClassScript = GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); - for(AuraList::const_iterator i = mOverrideClassScript.begin(); i != mOverrideClassScript.end(); ++i) + if (!((*i)->isAffectedOnSpell(spellProto))) + continue; + int32 modChance=0; + switch((*i)->GetMiscValue()) { - switch((*i)->GetModifier()->m_miscvalue) + // Shatter + case 911: modChance+= 16.0f; + case 910: modChance+= 17.0f; + case 849: modChance+= 17.0f; + if (!pVictim->HasAuraState(AURA_STATE_FROZEN, spellProto, this)) + break; + crit_chance+=modChance; + // Fingers of Frost + // TODO: Change this code to less hacky + if (Aura * aur = GetAura(44544)) + if (aur->DropAuraCharge()) + RemoveAura(aur); + break; + case 7917: // Glyph of Shadowburn + if (pVictim->HasAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, spellProto, this)) + crit_chance+=(*i)->GetAmount(); + break; + case 7997: // Renewed Hope + case 7998: + if (pVictim->HasAura(6788)) + crit_chance+=(*i)->GetAmount(); + break; + case 21: // Test of Faith + case 6935: + case 6918: + if (pVictim->GetHealth() < pVictim->GetMaxHealth()/2) + crit_chance+=(*i)->GetAmount(); + break; + default: + break; + } + } + // Custom crit by class + switch(spellProto->SpellFamilyName) + { + case SPELLFAMILY_PALADIN: + // Sacred Shield + if (spellProto->SpellFamilyFlags[0] & 0x40000000) { - case 849: crit_chance+= 10.0f; break; //Shatter Rank 1 - case 910: crit_chance+= 20.0f; break; //Shatter Rank 2 - case 911: crit_chance+= 30.0f; break; //Shatter Rank 3 - case 912: crit_chance+= 40.0f; break; //Shatter Rank 4 - case 913: crit_chance+= 50.0f; break; //Shatter Rank 5 + AuraEffect *aura = pVictim->GetDummyAura(58597); + if (aura && aura->GetCasterGUID() == GetGUID()) + crit_chance+=aura->GetAmount(); + break; } - } + break; + case SPELLFAMILY_SHAMAN: + // Lava Burst + if (spellProto->SpellFamilyFlags[1] & 0x00001000) + { + if (AuraEffect *flameShock = pVictim->GetAura(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_SHAMAN, 10000000, 0,0, GetGUID())) + { + // Consume shock aura if not have Glyph of Flame Shock + if (!GetAuraEffect(55447, 0)) + pVictim->RemoveAurasDueToSpell(flameShock->GetId(), GetGUID()); + return true; + } + break; + } + break; + } } break; @@ -8799,7 +8768,6 @@ bool Unit::isSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolM if (pVictim) { crit_chance = GetUnitCriticalChance(attackType, pVictim); - crit_chance+= (int32(GetMaxSkillValueForLevel(pVictim)) - int32(pVictim->GetDefenseSkillValue(this))) * 0.04f; crit_chance+= GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, schoolMask); } break; @@ -8818,7 +8786,7 @@ bool Unit::isSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolM return false; } -uint32 Unit::SpellCriticalBonus(SpellEntry const *spellProto, uint32 damage, Unit *pVictim) +uint32 Unit::SpellCriticalDamageBonus(SpellEntry const *spellProto, uint32 damage, Unit *pVictim) { // Calculate critical bonus int32 crit_bonus; @@ -8850,233 +8818,305 @@ uint32 Unit::SpellCriticalBonus(SpellEntry const *spellProto, uint32 damage, Uni return damage; } -uint32 Unit::SpellHealingBonus(SpellEntry const *spellProto, uint32 healamount, DamageEffectType damagetype, Unit *pVictim) +uint32 Unit::SpellCriticalHealingBonus(SpellEntry const *spellProto, uint32 damage, Unit *pVictim) +{ + // Calculate critical bonus + int32 crit_bonus; + switch(spellProto->DmgClass) + { + case SPELL_DAMAGE_CLASS_MELEE: // for melee based spells is 100% + case SPELL_DAMAGE_CLASS_RANGED: + // TODO: write here full calculation for melee/ranged spells + crit_bonus = damage; + break; + default: + crit_bonus = damage / 2; // for spells is 50% + break; + } + + + if(pVictim) + { + uint32 creatureTypeMask = pVictim->GetCreatureTypeMask(); + crit_bonus = int32(crit_bonus * GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS, creatureTypeMask)); + } + + if(crit_bonus > 0) + damage += crit_bonus; + + damage = int32(float(damage) * GetTotalAuraMultiplier(SPELL_AURA_MOD_CRITICAL_HEALING_AMOUNT)); + + return damage; +} + +uint32 Unit::SpellHealingBonus(Unit *pVictim, SpellEntry const *spellProto, uint32 healamount, DamageEffectType damagetype, uint32 stack) { + // No heal amount for this class spells + if (spellProto->DmgClass == SPELL_DAMAGE_CLASS_NONE) + return healamount; + // For totems get healing bonus from owner (statue isn't totem in fact) if( GetTypeId()==TYPEID_UNIT && ((Creature*)this)->isTotem() && ((Totem*)this)->GetTotemType()!=TOTEM_STATUE) if(Unit* owner = GetOwner()) - return owner->SpellHealingBonus(spellProto, healamount, damagetype, pVictim); + return owner->SpellHealingBonus(pVictim, spellProto, healamount, damagetype, stack); // Healing Done + // Taken/Done total percent damage auras + float DoneTotalMod = 1.0f; + float TakenTotalMod = 1.0f; + int32 DoneTotal = 0; + int32 TakenTotal = 0; - // These Spells are doing fixed amount of healing (TODO found less hack-like check) - if (spellProto->Id == 15290 || spellProto->Id == 39373 || - spellProto->Id == 33778 || spellProto->Id == 379 || - spellProto->Id == 38395 || spellProto->Id == 40972 || - spellProto->Id == 22845 || spellProto->Id == 33504 || - spellProto->Id == 34299 || spellProto->Id == 27813 || - spellProto->Id == 27817 || spellProto->Id == 27818) - return healamount; - - int32 AdvertisedBenefit = SpellBaseHealingBonus(GetSpellSchoolMask(spellProto)); - uint32 CastingTime = GetSpellCastTime(spellProto); - - // Healing Taken - AdvertisedBenefit += SpellBaseHealingBonusForVictim(GetSpellSchoolMask(spellProto), pVictim); + // Healing done percent + AuraEffectList const& mHealingDonePct = GetAurasByType(SPELL_AURA_MOD_HEALING_DONE_PERCENT); + for(AuraEffectList::const_iterator i = mHealingDonePct.begin();i != mHealingDonePct.end(); ++i) + DoneTotalMod *= (100.0f + (*i)->GetAmount()) / 100.0f; - // Blessing of Light dummy effects healing taken from Holy Light and Flash of Light - if (spellProto->SpellFamilyName == SPELLFAMILY_PALADIN && (spellProto->SpellFamilyFlags & 0x00000000C0000000LL)) + // done scripted mod (take it from owner) + Unit *owner = GetOwner(); + if (!owner) owner = this; + AuraEffectList const& mOverrideClassScript= owner->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); + for(AuraEffectList::const_iterator i = mOverrideClassScript.begin(); i != mOverrideClassScript.end(); ++i) { - AuraList const& mDummyAuras = pVictim->GetAurasByType(SPELL_AURA_DUMMY); - for(AuraList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i) + if (!(*i)->isAffectedOnSpell(spellProto)) + continue; + switch((*i)->GetMiscValue()) { - if((*i)->GetSpellProto()->SpellVisual == 9180) + case 4415: // Increased Rejuvenation Healing + case 4953: + case 3736: // Hateful Totem of the Third Wind / Increased Lesser Healing Wave / LK Arena (4/5/6) Totem of the Third Wind / Savage Totem of the Third Wind + DoneTotal+=(*i)->GetAmount(); + break; + case 7997: // Renewed Hope + case 7998: + if (pVictim->HasAura(6788)) + DoneTotalMod *=((*i)->GetAmount() + 100.0f)/100.0f; + break; + case 21: // Test of Faith + case 6935: + case 6918: + if (pVictim->GetHealth() < pVictim->GetMaxHealth()/2) + DoneTotalMod *=((*i)->GetAmount() + 100.0f)/100.0f; + break; + case 7798: // Glyph of Regrowth { - // Flash of Light - if ((spellProto->SpellFamilyFlags & 0x0000000040000000LL) && (*i)->GetEffIndex() == 1) - AdvertisedBenefit += (*i)->GetModifier()->m_amount; - // Holy Light - else if ((spellProto->SpellFamilyFlags & 0x0000000080000000LL) && (*i)->GetEffIndex() == 0) - AdvertisedBenefit += (*i)->GetModifier()->m_amount; + if (pVictim->GetAura(SPELL_AURA_PERIODIC_HEAL, SPELLFAMILY_DRUID, 0x40, 0)) + DoneTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f; + break; } - } - } - - float ActualBenefit = 0.0f; - - if (AdvertisedBenefit != 0) - { - // Healing over Time spells - float DotFactor = 1.0f; - if(damagetype == DOT) - { - int32 DotDuration = GetSpellDuration(spellProto); - if(DotDuration > 0) + case 8477: // Nourish Heal Boost { - // 200% limit - if(DotDuration > 30000) DotDuration = 30000; - if(!IsChanneledSpell(spellProto)) DotFactor = DotDuration / 15000.0f; - int x = 0; - for(int j = 0; j < 3; j++) + int32 stepPercent = (*i)->GetAmount(); + int32 modPercent = 0; + AuraMap const& victimAuras = pVictim->GetAuras(); + for (AuraMap::const_iterator itr = victimAuras.begin(); itr != victimAuras.end(); ++itr) { - if( spellProto->Effect[j] == SPELL_EFFECT_APPLY_AURA && ( - spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_HEAL || - spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_LEECH) ) - { - x = j; - break; - } + if (itr->second->GetCasterGUID()!=GetGUID()) + continue; + SpellEntry const* m_spell = itr->second->GetSpellProto(); + if ( m_spell->SpellFamilyName != SPELLFAMILY_DRUID || + !(m_spell->SpellFamilyFlags[1] & 0x00000010 || m_spell->SpellFamilyFlags[0] & 0x50)) + continue; + modPercent += stepPercent * itr->second->GetStackAmount(); } - int DotTicks = 6; - if(spellProto->EffectAmplitude[x] != 0) - DotTicks = DotDuration / spellProto->EffectAmplitude[x]; - if(DotTicks) - AdvertisedBenefit /= DotTicks; + DoneTotalMod *= (modPercent+100.0f)/100.0f; + break; + } + case 7871: // Glyph of Lesser Healing Wave + { + if (pVictim->GetAura(SPELL_AURA_DUMMY, SPELLFAMILY_SHAMAN, 0 , 0x00000400, 0, GetGUID())) + DoneTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f; + break; } + default: + break; } + } - // distribute healing to all effects, reduce AoE damage - CastingTime = GetCastingTimeForBonus( spellProto, damagetype, CastingTime ); + // Taken/Done fixed damage bonus auras + int32 DoneAdvertisedBenefit = SpellBaseHealingBonus(GetSpellSchoolMask(spellProto)); + int32 TakenAdvertisedBenefit = SpellBaseHealingBonusForVictim(GetSpellSchoolMask(spellProto), pVictim); - // 0% bonus for damage and healing spells for leech spells from healing bonus - for(int j = 0; j < 3; ++j) + // Check for table values + SpellBonusEntry const* bonus = spellmgr.GetSpellBonusData(spellProto->Id); + float coeff; + bool scripted = false; + if (bonus) + { + if (damagetype == DOT) + coeff = bonus->dot_damage; + else + coeff = bonus->direct_damage; + if (bonus->ap_bonus) + DoneTotal+=bonus->ap_bonus * GetTotalAttackPowerValue(BASE_ATTACK) * stack; + } + else // scripted bonus + { + // Gift of the Naaru + if (spellProto->Id==59547) { - if( spellProto->Effect[j] == SPELL_EFFECT_HEALTH_LEECH || - spellProto->Effect[j] == SPELL_EFFECT_APPLY_AURA && spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_LEECH ) + scripted = true; + uint32 apBonus = GetTotalAttackPowerValue(BASE_ATTACK); + if (apBonus > DoneAdvertisedBenefit) { - CastingTime = 0; - break; + DoneTotal+=apBonus * stack; + coeff = 0.0f; } + else + coeff = 1.0f; } + } - // Exception - switch (spellProto->SpellFamilyName) + // Default calculation + if (DoneAdvertisedBenefit || TakenAdvertisedBenefit) + { + if(!bonus && !scripted) { - case SPELLFAMILY_SHAMAN: - // Healing stream from totem (add 6% per tick from hill bonus owner) - if (spellProto->SpellFamilyFlags & 0x000000002000LL) - CastingTime = 210; - // Earth Shield 30% per charge - else if (spellProto->SpellFamilyFlags & 0x40000000000LL) - CastingTime = 1050; - break; - case SPELLFAMILY_DRUID: - // Lifebloom - if (spellProto->SpellFamilyFlags & 0x1000000000LL) - { - CastingTime = damagetype == DOT ? 3500 : 1200; - DotFactor = damagetype == DOT ? 0.519f : 1.0f; - } - // Tranquility triggered spell - else if (spellProto->SpellFamilyFlags & 0x80LL) - CastingTime = 667; - // Rejuvenation - else if (spellProto->SpellFamilyFlags & 0x10LL) - DotFactor = 0.845f; - // Regrowth - else if (spellProto->SpellFamilyFlags & 0x40LL) + // Damage Done from spell damage bonus + int32 CastingTime = !IsChanneledSpell(spellProto) ? GetSpellCastTime(spellProto) : GetSpellDuration(spellProto); + // Damage over Time spells bonus calculation + float DotFactor = 1.0f; + if(damagetype == DOT) + { + int32 DotDuration = GetSpellDuration(spellProto); + // 200% limit + if(DotDuration > 0) { - DotFactor = damagetype == DOT ? 0.705f : 1.0f; - CastingTime = damagetype == DOT ? 3500 : 1010; + if(DotDuration > 30000) DotDuration = 30000; + if(!IsChanneledSpell(spellProto)) DotFactor = DotDuration / 15000.0f; + int x = 0; + for(int j = 0; j < 3; j++) + { + if( spellProto->Effect[j] == SPELL_EFFECT_APPLY_AURA && ( + spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_DAMAGE || + spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_LEECH) ) + { + x = j; + break; + } + } + int32 DotTicks = 6; + if(spellProto->EffectAmplitude[x] != 0) + DotTicks = DotDuration / spellProto->EffectAmplitude[x]; + if(DotTicks) + { + DoneAdvertisedBenefit = DoneAdvertisedBenefit * int32(stack) / DotTicks; + TakenAdvertisedBenefit = TakenAdvertisedBenefit * int32(stack) / DotTicks; + } } - // Improved Leader of the Pack - else if (spellProto->AttributesEx2 == 536870912 && spellProto->SpellIconID == 312 - && spellProto->AttributesEx3 == 33554432) + } + // Distribute Damage over multiple effects, reduce by AoE + CastingTime = GetCastingTimeForBonus( spellProto, damagetype, CastingTime ); + // 50% for damage and healing spells for leech spells from damage bonus and 0% from healing + for(int j = 0; j < 3; ++j) + { + if( spellProto->Effect[j] == SPELL_EFFECT_HEALTH_LEECH || + spellProto->Effect[j] == SPELL_EFFECT_APPLY_AURA && spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_LEECH ) { - CastingTime = 0; + CastingTime /= 2; + break; } - break; - case SPELLFAMILY_PRIEST: - // Holy Nova - 14% - if ((spellProto->SpellFamilyFlags & 0x8000000LL) && spellProto->SpellIconID == 1874) - CastingTime = 500; - break; - case SPELLFAMILY_PALADIN: - // Seal and Judgement of Light - if ( spellProto->SpellFamilyFlags & 0x100040000LL ) - CastingTime = 0; - break; - case SPELLFAMILY_WARRIOR: - case SPELLFAMILY_ROGUE: - case SPELLFAMILY_HUNTER: - CastingTime = 0; - break; + } + coeff = (CastingTime / 3500.0f) * DotFactor; } - float LvlPenalty = CalculateLevelPenalty(spellProto); - - // Spellmod SpellDamage - //float SpellModSpellDamage = 100.0f; - float CoefficientPtc = ((float)CastingTime/3500.0f)*DotFactor*100.0f; - + float coeff2 = CalculateLevelPenalty(spellProto) * 1.88f * stack; + TakenTotal += TakenAdvertisedBenefit * coeff * coeff2; if(Player* modOwner = GetSpellModOwner()) - //modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_SPELL_BONUS_DAMAGE,SpellModSpellDamage); - modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_SPELL_BONUS_DAMAGE,CoefficientPtc); - - //SpellModSpellDamage /= 100.0f; - CoefficientPtc /= 100.0f; - - //ActualBenefit = (float)AdvertisedBenefit * ((float)CastingTime / 3500.0f) * DotFactor * SpellModSpellDamage * LvlPenalty; - ActualBenefit = (float)AdvertisedBenefit * CoefficientPtc * LvlPenalty; + { + coeff *= 100.0f; + modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_SPELL_BONUS_DAMAGE, coeff); + coeff /= 100.0f; + } + DoneTotal += DoneAdvertisedBenefit * coeff * coeff2; } // use float as more appropriate for negative values and percent applying - float heal = healamount + ActualBenefit; - - // TODO: check for ALL/SPELLS type - // Healing done percent - AuraList const& mHealingDonePct = GetAurasByType(SPELL_AURA_MOD_HEALING_DONE_PERCENT); - for(AuraList::const_iterator i = mHealingDonePct.begin();i != mHealingDonePct.end(); ++i) - heal *= (100.0f + (*i)->GetModifierValue()) / 100.0f; - + float heal = (healamount + DoneTotal)*DoneTotalMod; // apply spellmod to Done amount if(Player* modOwner = GetSpellModOwner()) modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, heal); + // Nourish cast + if (spellProto->SpellFamilyName == SPELLFAMILY_DRUID && spellProto->SpellFamilyFlags[1] & 0x2000000) + { + // Rejuvenation, Regrowth, Lifebloom, or Wild Growth + if (pVictim->GetAura(SPELL_AURA_PERIODIC_HEAL, SPELLFAMILY_DRUID, 0x50, 0x4000010, 0)) + //increase healing by 20% + DoneTotalMod *= 1.2f; + } + + // Taken mods // Healing Wave cast - if (spellProto->SpellFamilyName == SPELLFAMILY_SHAMAN && spellProto->SpellFamilyFlags & 0x0000000000000040LL) + if (spellProto->SpellFamilyName == SPELLFAMILY_SHAMAN && spellProto->SpellFamilyFlags[0] & 0x40) { - // Search for Healing Way on Victim (stack up to 3 time) - int32 pctMod = 0; - Unit::AuraList const& auraDummy = pVictim->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraList::const_iterator itr = auraDummy.begin(); itr!=auraDummy.end(); ++itr) + // Search for Healing Way on Victim + Unit::AuraEffectList const& auraDummy = pVictim->GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraEffectList::const_iterator itr = auraDummy.begin(); itr!=auraDummy.end(); ++itr) if((*itr)->GetId() == 29203) - pctMod += (*itr)->GetModifier()->m_amount; - // Apply bonus - if (pctMod) - heal = heal * (100 + pctMod) / 100; + TakenTotalMod *= ((*itr)->GetAmount()+100.0f) / 100.0f; } // Healing taken percent float minval = pVictim->GetMaxNegativeAuraModifier(SPELL_AURA_MOD_HEALING_PCT); if(minval) - heal *= (100.0f + minval) / 100.0f; + TakenTotalMod *= (100.0f + minval) / 100.0f; float maxval = pVictim->GetMaxPositiveAuraModifier(SPELL_AURA_MOD_HEALING_PCT); if(maxval) - heal *= (100.0f + maxval) / 100.0f; + TakenTotalMod *= (100.0f + maxval) / 100.0f; + + if(damagetype==DOT) + { + // Healing over time taken percent + float minval_hot = GetMaxNegativeAuraModifier(SPELL_AURA_MOD_HOT_PCT); + if(minval_hot) + TakenTotalMod *= (100.0f + minval_hot) / 100.0f; + + float maxval_hot = GetMaxPositiveAuraModifier(SPELL_AURA_MOD_HOT_PCT); + if(maxval_hot) + TakenTotalMod *= (100.0f + maxval_hot) / 100.0f; + } - if (heal < 0) heal = 0; + AuraEffectList const& mHealingGet= pVictim->GetAurasByType(SPELL_AURA_MOD_HEALING_RECEIVED); + for(AuraEffectList::const_iterator i = mHealingGet.begin(); i != mHealingGet.end(); ++i) + if (GetGUID()==(*i)->GetCasterGUID() && (*i)->isAffectedOnSpell(spellProto) ) + TakenTotalMod *= ((*i)->GetAmount() + 100.0f) / 100.0f; - return uint32(heal); + heal = (heal + TakenTotal) * TakenTotalMod; + + return heal < 0 ? 0 : uint32(heal); } int32 Unit::SpellBaseHealingBonus(SpellSchoolMask schoolMask) { int32 AdvertisedBenefit = 0; - AuraList const& mHealingDone = GetAurasByType(SPELL_AURA_MOD_HEALING_DONE); - for(AuraList::const_iterator i = mHealingDone.begin();i != mHealingDone.end(); ++i) - if(((*i)->GetModifier()->m_miscvalue & schoolMask) != 0) - AdvertisedBenefit += (*i)->GetModifierValue(); + AuraEffectList const& mHealingDone = GetAurasByType(SPELL_AURA_MOD_HEALING_DONE); + for(AuraEffectList::const_iterator i = mHealingDone.begin();i != mHealingDone.end(); ++i) + if(((*i)->GetMiscValue() & schoolMask) != 0) + AdvertisedBenefit += (*i)->GetAmount(); // Healing bonus of spirit, intellect and strength if (GetTypeId() == TYPEID_PLAYER) { + // Base value + AdvertisedBenefit +=((Player*)this)->GetBaseSpellHealingBonus(); + // Healing bonus from stats - AuraList const& mHealingDoneOfStatPercent = GetAurasByType(SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT); - for(AuraList::const_iterator i = mHealingDoneOfStatPercent.begin();i != mHealingDoneOfStatPercent.end(); ++i) + AuraEffectList const& mHealingDoneOfStatPercent = GetAurasByType(SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT); + for(AuraEffectList::const_iterator i = mHealingDoneOfStatPercent.begin();i != mHealingDoneOfStatPercent.end(); ++i) { // stat used dependent from misc value (stat index) Stats usedStat = Stats((*i)->GetSpellProto()->EffectMiscValue[(*i)->GetEffIndex()]); - AdvertisedBenefit += int32(GetStat(usedStat) * (*i)->GetModifierValue() / 100.0f); + AdvertisedBenefit += int32(GetStat(usedStat) * (*i)->GetAmount() / 100.0f); } // ... and attack power - AuraList const& mHealingDonebyAP = GetAurasByType(SPELL_AURA_MOD_SPELL_HEALING_OF_ATTACK_POWER); - for(AuraList::const_iterator i = mHealingDonebyAP.begin();i != mHealingDonebyAP.end(); ++i) - if ((*i)->GetModifier()->m_miscvalue & schoolMask) - AdvertisedBenefit += int32(GetTotalAttackPowerValue(BASE_ATTACK) * (*i)->GetModifierValue() / 100.0f); + AuraEffectList const& mHealingDonebyAP = GetAurasByType(SPELL_AURA_MOD_SPELL_HEALING_OF_ATTACK_POWER); + for(AuraEffectList::const_iterator i = mHealingDonebyAP.begin();i != mHealingDonebyAP.end(); ++i) + if ((*i)->GetMiscValue() & schoolMask) + AdvertisedBenefit += int32(GetTotalAttackPowerValue(BASE_ATTACK) * (*i)->GetAmount() / 100.0f); } return AdvertisedBenefit; } @@ -9084,14 +9124,14 @@ int32 Unit::SpellBaseHealingBonus(SpellSchoolMask schoolMask) int32 Unit::SpellBaseHealingBonusForVictim(SpellSchoolMask schoolMask, Unit *pVictim) { int32 AdvertisedBenefit = 0; - AuraList const& mDamageTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_HEALING); - for(AuraList::const_iterator i = mDamageTaken.begin();i != mDamageTaken.end(); ++i) - if(((*i)->GetModifier()->m_miscvalue & schoolMask) != 0) - AdvertisedBenefit += (*i)->GetModifierValue(); + AuraEffectList const& mDamageTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_HEALING); + for(AuraEffectList::const_iterator i = mDamageTaken.begin();i != mDamageTaken.end(); ++i) + if(((*i)->GetMiscValue() & schoolMask) != 0) + AdvertisedBenefit += (*i)->GetAmount(); return AdvertisedBenefit; } -bool Unit::IsImmunedToDamage(SpellSchoolMask shoolMask, bool useCharges) +bool Unit::IsImmunedToDamage(SpellSchoolMask shoolMask) { //If m_immuneToSchool type contain this school type, IMMUNE damage. SpellImmuneList const& schoolList = m_spellImmune[IMMUNITY_SCHOOL]; @@ -9108,7 +9148,7 @@ bool Unit::IsImmunedToDamage(SpellSchoolMask shoolMask, bool useCharges) return false; } -bool Unit::IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges) +bool Unit::IsImmunedToSpell(SpellEntry const* spellInfo) { if (!spellInfo) return false; @@ -9129,12 +9169,15 @@ bool Unit::IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges) return true; } - SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC]; - for(SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr) + if (spellInfo->Mechanic) { - if(itr->type == spellInfo->Mechanic) + SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC]; + for(SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr) { - return true; + if(itr->type == spellInfo->Mechanic) + { + return true; + } } } @@ -9150,18 +9193,39 @@ bool Unit::IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges) return false; } -bool Unit::IsImmunedToSpellEffect(uint32 effect, uint32 mechanic) const +bool Unit::IsImmunedToSpellEffect(SpellEntry const* spellInfo, uint32 index) const { + if (!spellInfo) + return false; //If m_immuneToEffect type contain this effect type, IMMUNE effect. + uint32 effect = spellInfo->Effect[index]; SpellImmuneList const& effectList = m_spellImmune[IMMUNITY_EFFECT]; for (SpellImmuneList::const_iterator itr = effectList.begin(); itr != effectList.end(); ++itr) if(itr->type == effect) return true; - SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC]; - for (SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr) - if(itr->type == mechanic) - return true; + if(uint32 mechanic = spellInfo->EffectMechanic[index]) + { + SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC]; + for (SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr) + if(itr->type == spellInfo->EffectMechanic[index]) + return true; + } + + if(uint32 aura = spellInfo->EffectApplyAuraName[index]) + { + SpellImmuneList const& list = m_spellImmune[IMMUNITY_STATE]; + for(SpellImmuneList::const_iterator itr = list.begin(); itr != list.end(); ++itr) + if(itr->type == aura) + return true; + // Check for immune to application of harmful magical effects + AuraEffectList const& immuneAuraApply = GetAurasByType(SPELL_AURA_MOD_IMMUNE_AURA_APPLY_SCHOOL); + for(AuraEffectList::const_iterator iter = immuneAuraApply.begin(); iter != immuneAuraApply.end(); ++iter) + if (spellInfo->Dispel == DISPEL_MAGIC && // Magic debuff + ((*iter)->GetMiscValue() & GetSpellSchoolMask(spellInfo)) && // Check school + !IsPositiveEffect(spellInfo->Id, index)) // Harmful + return true; + } return false; } @@ -9196,10 +9260,10 @@ void Unit::MeleeDamageBonus(Unit *pVictim, uint32 *pdamage,WeaponAttackType attT int32 TakenFlatBenefit = 0; // ..done (for creature type by mask) in taken - AuraList const& mDamageDoneCreature = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE_CREATURE); - for(AuraList::const_iterator i = mDamageDoneCreature.begin();i != mDamageDoneCreature.end(); ++i) - if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue)) - DoneFlatBenefit += (*i)->GetModifierValue(); + AuraEffectList const& mDamageDoneCreature = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE_CREATURE); + for(AuraEffectList::const_iterator i = mDamageDoneCreature.begin();i != mDamageDoneCreature.end(); ++i) + if(creatureTypeMask & uint32((*i)->GetMiscValue())) + DoneFlatBenefit += (*i)->GetAmount(); // ..done // SPELL_AURA_MOD_DAMAGE_DONE included in weapon damage @@ -9211,20 +9275,20 @@ void Unit::MeleeDamageBonus(Unit *pVictim, uint32 *pdamage,WeaponAttackType attT APbonus += pVictim->GetTotalAuraModifier(SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS); // ..done (base at attack power and creature type) - AuraList const& mCreatureAttackPower = GetAurasByType(SPELL_AURA_MOD_RANGED_ATTACK_POWER_VERSUS); - for(AuraList::const_iterator i = mCreatureAttackPower.begin();i != mCreatureAttackPower.end(); ++i) - if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue)) - APbonus += (*i)->GetModifierValue(); + AuraEffectList const& mCreatureAttackPower = GetAurasByType(SPELL_AURA_MOD_RANGED_ATTACK_POWER_VERSUS); + for(AuraEffectList::const_iterator i = mCreatureAttackPower.begin();i != mCreatureAttackPower.end(); ++i) + if(creatureTypeMask & uint32((*i)->GetMiscValue())) + APbonus += (*i)->GetAmount(); } else { APbonus += pVictim->GetTotalAuraModifier(SPELL_AURA_MELEE_ATTACK_POWER_ATTACKER_BONUS); // ..done (base at attack power and creature type) - AuraList const& mCreatureAttackPower = GetAurasByType(SPELL_AURA_MOD_MELEE_ATTACK_POWER_VERSUS); - for(AuraList::const_iterator i = mCreatureAttackPower.begin();i != mCreatureAttackPower.end(); ++i) - if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue)) - APbonus += (*i)->GetModifierValue(); + AuraEffectList const& mCreatureAttackPower = GetAurasByType(SPELL_AURA_MOD_MELEE_ATTACK_POWER_VERSUS); + for(AuraEffectList::const_iterator i = mCreatureAttackPower.begin();i != mCreatureAttackPower.end(); ++i) + if(creatureTypeMask & uint32((*i)->GetMiscValue())) + APbonus += (*i)->GetAmount(); } if (APbonus!=0) // Can be negative @@ -9246,10 +9310,10 @@ void Unit::MeleeDamageBonus(Unit *pVictim, uint32 *pdamage,WeaponAttackType attT } // ..taken - AuraList const& mDamageTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_TAKEN); - for(AuraList::const_iterator i = mDamageTaken.begin();i != mDamageTaken.end(); ++i) - if((*i)->GetModifier()->m_miscvalue & GetMeleeDamageSchoolMask()) - TakenFlatBenefit += (*i)->GetModifierValue(); + AuraEffectList const& mDamageTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_TAKEN); + for(AuraEffectList::const_iterator i = mDamageTaken.begin();i != mDamageTaken.end(); ++i) + if((*i)->GetMiscValue() & GetMeleeDamageSchoolMask()) + TakenFlatBenefit += (*i)->GetAmount(); if(attType!=RANGED_ATTACK) TakenFlatBenefit += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN); @@ -9257,39 +9321,39 @@ void Unit::MeleeDamageBonus(Unit *pVictim, uint32 *pdamage,WeaponAttackType attT TakenFlatBenefit += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN); // Done/Taken total percent damage auras - float DoneTotalMod = 1; - float TakenTotalMod = 1; + float DoneTotalMod = 1.0f; + float TakenTotalMod = 1.0f; // ..done // SPELL_AURA_MOD_DAMAGE_PERCENT_DONE included in weapon damage // SPELL_AURA_MOD_OFFHAND_DAMAGE_PCT included in weapon damage - AuraList const& mDamageDoneVersus = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS); - for(AuraList::const_iterator i = mDamageDoneVersus.begin();i != mDamageDoneVersus.end(); ++i) - if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue)) - DoneTotalMod *= ((*i)->GetModifierValue()+100.0f)/100.0f; + AuraEffectList const& mDamageDoneVersus = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS); + for(AuraEffectList::const_iterator i = mDamageDoneVersus.begin();i != mDamageDoneVersus.end(); ++i) + if(creatureTypeMask & uint32((*i)->GetMiscValue())) + DoneTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f; // ..taken - AuraList const& mModDamagePercentTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN); - for(AuraList::const_iterator i = mModDamagePercentTaken.begin(); i != mModDamagePercentTaken.end(); ++i) - if((*i)->GetModifier()->m_miscvalue & GetMeleeDamageSchoolMask()) - TakenTotalMod *= ((*i)->GetModifierValue()+100.0f)/100.0f; + AuraEffectList const& mModDamagePercentTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN); + for(AuraEffectList::const_iterator i = mModDamagePercentTaken.begin(); i != mModDamagePercentTaken.end(); ++i) + if((*i)->GetMiscValue() & GetMeleeDamageSchoolMask()) + TakenTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f; // .. taken pct: dummy auras - AuraList const& mDummyAuras = pVictim->GetAurasByType(SPELL_AURA_DUMMY); - for(AuraList::const_iterator i = mDummyAuras.begin(); i != mDummyAuras.end(); ++i) + AuraEffectList const& mDummyAuras = pVictim->GetAurasByType(SPELL_AURA_DUMMY); + for(AuraEffectList::const_iterator i = mDummyAuras.begin(); i != mDummyAuras.end(); ++i) { switch((*i)->GetSpellProto()->SpellIconID) { //Cheat Death case 2109: - if((*i)->GetModifier()->m_miscvalue & SPELL_SCHOOL_MASK_NORMAL) + if((*i)->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) { if(pVictim->GetTypeId() != TYPEID_PLAYER) continue; float mod = ((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_MELEE)*(-8.0f); - if (mod < (*i)->GetModifier()->m_amount) - mod = (*i)->GetModifier()->m_amount; + if (mod < (*i)->GetAmount()) + mod = (*i)->GetAmount(); TakenTotalMod *= (mod+100.0f)/100.0f; } break; @@ -9298,22 +9362,22 @@ void Unit::MeleeDamageBonus(Unit *pVictim, uint32 *pdamage,WeaponAttackType attT if(spellProto==NULL) break; // Should increase Shred (initial Damage of Lacerate and Rake handled in Spell::EffectSchoolDMG) - if(spellProto->SpellFamilyName==SPELLFAMILY_DRUID && (spellProto->SpellFamilyFlags==0x00008000LL)) - TakenTotalMod *= (100.0f+(*i)->GetModifier()->m_amount)/100.0f; + if(spellProto->SpellFamilyName==SPELLFAMILY_DRUID && spellProto->SpellFamilyFlags.IsEqual (0x00008000,0,0)) + TakenTotalMod *= (100.0f+(*i)->GetAmount())/100.0f; break; } } // .. taken pct: class scripts - AuraList const& mclassScritAuras = GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); - for(AuraList::const_iterator i = mclassScritAuras.begin(); i != mclassScritAuras.end(); ++i) + AuraEffectList const& mclassScritAuras = GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); + for(AuraEffectList::const_iterator i = mclassScritAuras.begin(); i != mclassScritAuras.end(); ++i) { switch((*i)->GetMiscValue()) { case 6427: case 6428: // Dirty Deeds - if(pVictim->HasAuraState(AURA_STATE_HEALTHLESS_35_PERCENT)) + if(pVictim->HasAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, spellProto, this)) { - Aura* eff0 = GetAura((*i)->GetId(),0); + AuraEffect* eff0 = GetAuraEffect((*i)->GetId(),0,GetGUID()); if(!eff0 || (*i)->GetEffIndex()!=1) { sLog.outError("Spell structure of DD (%u) changed.",(*i)->GetId()); @@ -9321,7 +9385,7 @@ void Unit::MeleeDamageBonus(Unit *pVictim, uint32 *pdamage,WeaponAttackType attT } // effect 0 have expected value but in negative state - TakenTotalMod *= (-eff0->GetModifier()->m_amount+100.0f)/100.0f; + TakenTotalMod *= (-eff0->GetAmount()+100.0f)/100.0f; } break; } @@ -9329,15 +9393,15 @@ void Unit::MeleeDamageBonus(Unit *pVictim, uint32 *pdamage,WeaponAttackType attT if(attType != RANGED_ATTACK) { - AuraList const& mModMeleeDamageTakenPercent = pVictim->GetAurasByType(SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN_PCT); - for(AuraList::const_iterator i = mModMeleeDamageTakenPercent.begin(); i != mModMeleeDamageTakenPercent.end(); ++i) - TakenTotalMod *= ((*i)->GetModifierValue()+100.0f)/100.0f; + AuraEffectList const& mModMeleeDamageTakenPercent = pVictim->GetAurasByType(SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN_PCT); + for(AuraEffectList::const_iterator i = mModMeleeDamageTakenPercent.begin(); i != mModMeleeDamageTakenPercent.end(); ++i) + TakenTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f; } else { - AuraList const& mModRangedDamageTakenPercent = pVictim->GetAurasByType(SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN_PCT); - for(AuraList::const_iterator i = mModRangedDamageTakenPercent.begin(); i != mModRangedDamageTakenPercent.end(); ++i) - TakenTotalMod *= ((*i)->GetModifierValue()+100.0f)/100.0f; + AuraEffectList const& mModRangedDamageTakenPercent = pVictim->GetAurasByType(SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN_PCT); + for(AuraEffectList::const_iterator i = mModRangedDamageTakenPercent.begin(); i != mModRangedDamageTakenPercent.end(); ++i) + TakenTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f; } float tmpDamage = float(int32(*pdamage) + DoneFlatBenefit) * DoneTotalMod; @@ -9392,7 +9456,23 @@ void Unit::ApplySpellDispelImmunity(const SpellEntry * spellProto, DispelType ty ApplySpellImmune(spellProto->Id,IMMUNITY_DISPEL, type, apply); if (apply && spellProto->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY) - RemoveAurasWithDispelType(type); + { + // Create dispel mask by dispel type + uint32 dispelMask = GetDispellMask(type); + // Dispel all existing auras vs current dispel type + AuraMap& auras = GetAuras(); + for(AuraMap::iterator itr = auras.begin(); itr != auras.end(); ) + { + SpellEntry const* spell = itr->second->GetSpellProto(); + if( (1<<spell->Dispel) & dispelMask ) + { + // Dispel aura + RemoveAura(itr); + } + else + ++itr; + } + } } float Unit::GetWeaponProcChance() const @@ -9406,10 +9486,15 @@ float Unit::GetWeaponProcChance() const return 0; } -float Unit::GetPPMProcChance(uint32 WeaponSpeed, float PPM) const +float Unit::GetPPMProcChance(uint32 WeaponSpeed, float PPM, const SpellEntry * spellProto) const { // proc per minute chance calculation if (PPM <= 0) return 0.0f; + // Apply chance modifer aura + if (spellProto) + if(Player* modOwner = GetSpellModOwner()) + modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_PROC_PER_MINUTE,PPM); + uint32 result = uint32((WeaponSpeed * PPM) / 600.0f); // result is chance in percents (probability = Speed_in_sec * (PPM / 60)) return result; } @@ -9427,21 +9512,7 @@ void Unit::Mount(uint32 mount) // unsummon pet if(GetTypeId() == TYPEID_PLAYER) - { - Pet* pet = GetPet(); - if(pet) - { - if(pet->isControlled()) - { - ((Player*)this)->SetTemporaryUnsummonedPetNumber(pet->GetCharmInfo()->GetPetNumber()); - ((Player*)this)->SetOldPetSpell(pet->GetUInt32Value(UNIT_CREATED_BY_SPELL)); - } - - ((Player*)this)->RemovePet(NULL,PET_SAVE_NOT_IN_SLOT); - } - else - ((Player*)this)->SetTemporaryUnsummonedPetNumber(0); - } + ((Player*)this)->UnsummonPetTemporaryIfAny(); } void Unit::Unmount() @@ -9457,14 +9528,8 @@ void Unit::Unmount() // only resummon old pet if the player is already added to a map // this prevents adding a pet to a not created map which would otherwise cause a crash // (it could probably happen when logging in after a previous crash) - if(GetTypeId() == TYPEID_PLAYER && IsInWorld() && ((Player*)this)->GetTemporaryUnsummonedPetNumber() && isAlive()) - { - Pet* NewPet = new Pet; - if(!NewPet->LoadPetFromDB(this, 0, ((Player*)this)->GetTemporaryUnsummonedPetNumber(), true)) - delete NewPet; - - ((Player*)this)->SetTemporaryUnsummonedPetNumber(0); - } + if(GetTypeId() == TYPEID_PLAYER) + ((Player*)this)->ResummonPetTemporaryUnSummonedIfAny(); } void Unit::SetInCombatWith(Unit* enemy) @@ -9492,11 +9557,18 @@ void Unit::SetInCombatWith(Unit* enemy) void Unit::CombatStart(Unit* target) { if(!target->IsStandState()/* && !target->hasUnitState(UNIT_STAT_STUNNED)*/) - target->SetStandState(PLAYER_STATE_NONE); + target->SetStandState(UNIT_STAND_STATE_STAND); if(!target->isInCombat() && target->GetTypeId() != TYPEID_PLAYER && !((Creature*)target)->HasReactState(REACT_PASSIVE) && ((Creature*)target)->IsAIEnabled) + { ((Creature*)target)->AI()->AttackStart(this); + if(((Creature*)target)->GetFormation()) + { + ((Creature*)target)->GetFormation()->MemberAttackStart((Creature*)target, this); + sLog.outDebug("Unit::CombatStart() calls CreatureGroups::MemberHasAttacked(this);"); + } + } SetInCombatWith(target); target->SetInCombatWith(this); @@ -9513,14 +9585,6 @@ void Unit::CombatStart(Unit* target) me->UpdatePvP(true); me->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); } - - //Call creature group update - if(GetTypeId()==TYPEID_UNIT && ((Creature*)this)->GetFormationID()) - { - CreatureGroupHolderType::iterator itr = CreatureGroupHolder.find(((Creature*)this)->GetFormationID()); - if(itr != CreatureGroupHolder.end()) - itr->second->MemberHasAttacked(((Creature*)this)); - } } void Unit::SetInCombatState(bool PvP) @@ -9560,6 +9624,8 @@ void Unit::ClearInCombat() // Player's state will be cleared in Player::UpdateContestedPvP if(GetTypeId()!=TYPEID_PLAYER) clearUnitState(UNIT_STAT_ATTACK_PLAYER); + else + ((Player*)this)->UpdatePotionCooldown(); if(GetTypeId() != TYPEID_PLAYER && ((Creature*)this)->isPet()) { @@ -9617,7 +9683,7 @@ bool Unit::isAttackableByAOE() const if(GetTypeId()==TYPEID_PLAYER && ((Player *)this)->isGameMaster()) return false; - return !isInFlight(); + return !hasUnitState(UNIT_STAT_UNATTACKABLE); } int32 Unit::ModifyHealth(int32 dVal) @@ -9701,8 +9767,8 @@ bool Unit::canDetectInvisibilityOf(Unit const* u) const { if(m_invisibilityMask & u->m_invisibilityMask) // same group return true; - AuraList const& auras = u->GetAurasByType(SPELL_AURA_MOD_STALKED); // Hunter mark - for(AuraList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter) + AuraEffectList const& auras = u->GetAurasByType(SPELL_AURA_MOD_STALKED); // Hunter mark + for(AuraEffectList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter) if((*iter)->GetCasterGUID()==GetGUID()) return true; @@ -9715,10 +9781,10 @@ bool Unit::canDetectInvisibilityOf(Unit const* u) const // find invisibility level uint32 invLevel = 0; - Unit::AuraList const& iAuras = u->GetAurasByType(SPELL_AURA_MOD_INVISIBILITY); - for(Unit::AuraList::const_iterator itr = iAuras.begin(); itr != iAuras.end(); ++itr) - if(((*itr)->GetModifier()->m_miscvalue)==i && invLevel < (*itr)->GetModifier()->m_amount) - invLevel = (*itr)->GetModifier()->m_amount; + Unit::AuraEffectList const& iAuras = u->GetAurasByType(SPELL_AURA_MOD_INVISIBILITY); + for(Unit::AuraEffectList::const_iterator itr = iAuras.begin(); itr != iAuras.end(); ++itr) + if(((*itr)->GetMiscValue())==i && invLevel < (*itr)->GetAmount()) + invLevel = (*itr)->GetAmount(); // find invisibility detect level uint32 detectLevel = 0; @@ -9728,10 +9794,10 @@ bool Unit::canDetectInvisibilityOf(Unit const* u) const } else { - Unit::AuraList const& dAuras = GetAurasByType(SPELL_AURA_MOD_INVISIBILITY_DETECTION); - for(Unit::AuraList::const_iterator itr = dAuras.begin(); itr != dAuras.end(); ++itr) - if(((*itr)->GetModifier()->m_miscvalue)==i && detectLevel < (*itr)->GetModifier()->m_amount) - detectLevel = (*itr)->GetModifier()->m_amount; + Unit::AuraEffectList const& dAuras = GetAurasByType(SPELL_AURA_MOD_INVISIBILITY_DETECTION); + for(Unit::AuraEffectList::const_iterator itr = dAuras.begin(); itr != dAuras.end(); ++itr) + if(((*itr)->GetMiscValue())==i && detectLevel < (*itr)->GetAmount()) + detectLevel = (*itr)->GetAmount(); } if(invLevel <= detectLevel) @@ -9753,8 +9819,8 @@ bool Unit::canDetectStealthOf(Unit const* target, float distance) const if(HasAuraType(SPELL_AURA_DETECT_STEALTH)) return true; - AuraList const& auras = target->GetAurasByType(SPELL_AURA_MOD_STALKED); // Hunter mark - for(AuraList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter) + AuraEffectList const& auras = target->GetAurasByType(SPELL_AURA_MOD_STALKED); // Hunter mark + for(AuraEffectList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter) if((*iter)->GetCasterGUID()==GetGUID()) return true; @@ -9776,7 +9842,7 @@ void Unit::DestroyForNearbyPlayers() std::list<Unit*> targets; Trinity::AnyUnitInObjectRangeCheck check(this, World::GetMaxVisibleDistance()); - Trinity::UnitListSearcher<Trinity::AnyUnitInObjectRangeCheck> searcher(targets, check); + Trinity::UnitListSearcher<Trinity::AnyUnitInObjectRangeCheck> searcher(this, targets, check); VisitNearbyWorldObject(World::GetMaxVisibleDistance(), searcher); for(std::list<Unit*>::iterator iter = targets.begin(); iter != targets.end(); ++iter) if(*iter != this && (*iter)->GetTypeId() == TYPEID_PLAYER @@ -9900,38 +9966,37 @@ void Unit::SetSpeed(UnitMoveType mtype, float rate, bool forced) propagateSpeedChange(); - // Send speed change packet only for player - if (GetTypeId()!=TYPEID_PLAYER) - return; - WorldPacket data; if(!forced) { switch(mtype) { case MOVE_WALK: - data.Initialize(MSG_MOVE_SET_WALK_SPEED, 8+4+1+4+4+4+4+4+4+4); + data.Initialize(MSG_MOVE_SET_WALK_SPEED, 8+4+2+4+4+4+4+4+4+4); break; case MOVE_RUN: - data.Initialize(MSG_MOVE_SET_RUN_SPEED, 8+4+1+4+4+4+4+4+4+4); + data.Initialize(MSG_MOVE_SET_RUN_SPEED, 8+4+2+4+4+4+4+4+4+4); break; case MOVE_RUN_BACK: - data.Initialize(MSG_MOVE_SET_RUN_BACK_SPEED, 8+4+1+4+4+4+4+4+4+4); + data.Initialize(MSG_MOVE_SET_RUN_BACK_SPEED, 8+4+2+4+4+4+4+4+4+4); break; case MOVE_SWIM: - data.Initialize(MSG_MOVE_SET_SWIM_SPEED, 8+4+1+4+4+4+4+4+4+4); + data.Initialize(MSG_MOVE_SET_SWIM_SPEED, 8+4+2+4+4+4+4+4+4+4); break; case MOVE_SWIM_BACK: - data.Initialize(MSG_MOVE_SET_SWIM_BACK_SPEED, 8+4+1+4+4+4+4+4+4+4); + data.Initialize(MSG_MOVE_SET_SWIM_BACK_SPEED, 8+4+2+4+4+4+4+4+4+4); break; case MOVE_TURN_RATE: - data.Initialize(MSG_MOVE_SET_TURN_RATE, 8+4+1+4+4+4+4+4+4+4); + data.Initialize(MSG_MOVE_SET_TURN_RATE, 8+4+2+4+4+4+4+4+4+4); break; case MOVE_FLIGHT: - data.Initialize(MSG_MOVE_SET_FLIGHT_SPEED, 8+4+1+4+4+4+4+4+4+4); + data.Initialize(MSG_MOVE_SET_FLIGHT_SPEED, 8+4+2+4+4+4+4+4+4+4); break; case MOVE_FLIGHT_BACK: - data.Initialize(MSG_MOVE_SET_FLIGHT_BACK_SPEED, 8+4+1+4+4+4+4+4+4+4); + data.Initialize(MSG_MOVE_SET_FLIGHT_BACK_SPEED, 8+4+2+4+4+4+4+4+4+4); + break; + case MOVE_PITCH_RATE: + data.Initialize(MSG_MOVE_SET_PITCH_RATE, 8+4+2+4+4+4+4+4+4+4); break; default: sLog.outError("Unit::SetSpeed: Unsupported move type (%d), data not sent to client.",mtype); @@ -9939,22 +10004,30 @@ void Unit::SetSpeed(UnitMoveType mtype, float rate, bool forced) } data.append(GetPackGUID()); - data << uint32(0); //movement flags - data << uint8(0); //unk + data << uint32(0); // movement flags + data << uint16(0); // unk flags data << uint32(getMSTime()); data << float(GetPositionX()); data << float(GetPositionY()); data << float(GetPositionZ()); data << float(GetOrientation()); - data << uint32(0); //flag unk + data << uint32(0); // fall time data << float(GetSpeed(mtype)); SendMessageToSet( &data, true ); } else { - // register forced speed changes for WorldSession::HandleForceSpeedChangeAck - // and do it only for real sent packets and use run for run/mounted as client expected - ++((Player*)this)->m_forced_speed_changes[mtype]; + if(GetTypeId() == TYPEID_PLAYER) + { + // register forced speed changes for WorldSession::HandleForceSpeedChangeAck + // and do it only for real sent packets and use run for run/mounted as client expected + ++((Player*)this)->m_forced_speed_changes[mtype]; + + if(!isInCombat()) + if(Pet* pet = ((Player*)this)->GetPet()) + pet->SetSpeed(mtype, m_speed_rate[mtype], forced); + } + switch(mtype) { case MOVE_WALK: @@ -9981,6 +10054,9 @@ void Unit::SetSpeed(UnitMoveType mtype, float rate, bool forced) case MOVE_FLIGHT_BACK: data.Initialize(SMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE, 16); break; + case MOVE_PITCH_RATE: + data.Initialize(SMSG_FORCE_PITCH_RATE_CHANGE, 16); + break; default: sLog.outError("Unit::SetSpeed: Unsupported move type (%d), data not sent to client.",mtype); return; @@ -9992,9 +10068,6 @@ void Unit::SetSpeed(UnitMoveType mtype, float rate, bool forced) data << float(GetSpeed(mtype)); SendMessageToSet( &data, true ); } - if(GetPetGUID() && !isInCombat()) - if(Pet* pet = GetPet()) - pet->SetSpeed(mtype, m_speed_rate[mtype], forced); } void Unit::SetHover(bool on) @@ -10020,8 +10093,10 @@ void Unit::setDeathState(DeathState s) if (s == JUST_DIED) { - RemoveAllAurasOnDeath(); UnsummonAllTotems(); + RemoveAllControlled(); + RemoveAllAurasOnDeath(); + //This is needed to clear visible auras after unit dies ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false); ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false); @@ -10065,6 +10140,10 @@ bool Unit::CanHaveThreatList() const if( ((Creature*)this)->isTotem() ) return false; + // vehicles can not have threat list + if( ((Creature*)this)->isVehicle() ) + return false; + // pets can not have a threat list, unless they are controlled by a creature if( ((Creature*)this)->isPet() && IS_PLAYER_GUID(((Pet*)this)->GetOwnerGUID()) ) return false; @@ -10169,18 +10248,44 @@ Unit* Creature::SelectVictim() //or who does not have threat (totem/pet/critter) //otherwise enterevademode every update - Unit* target = NULL; - if(!m_ThreatManager.isThreatListEmpty()) + + Unit* target = NULL; + // First checking if we have some taunt on us + const AuraEffectList& tauntAuras = GetAurasByType(SPELL_AURA_MOD_TAUNT); + if ( !tauntAuras.empty() ) { - if(!HasAuraType(SPELL_AURA_MOD_TAUNT)) + Unit* caster; + + // The last taunt aura caster is alive an we are happy to attack him + if ( (caster = tauntAuras.back()->GetCaster()) && caster->isAlive() ) + return getVictim(); + else if (tauntAuras.size() > 1) { - target = m_ThreatManager.getHostilTarget(); + // We do not have last taunt aura caster but we have more taunt auras, + // so find first available target + + // Auras are pushed_back, last caster will be on the end + AuraEffectList::const_iterator aura = --tauntAuras.end(); + do + { + --aura; + if ( (caster = (*aura)->GetCaster()) && + caster->IsInMap(this) && canAttack(caster) && caster->isInAccessiblePlaceFor((Creature*)this) ) + { + target = caster; + break; + } + }while (aura != tauntAuras.begin()); } else target = getVictim(); } + if ( !target && !m_ThreatManager.isThreatListEmpty() ) + // No taunt aura or taunt aura caster is dead standart target selection + target = m_ThreatManager.getHostilTarget(); + if(target) { if(!hasUnitState(UNIT_STAT_STUNNED)) @@ -10213,9 +10318,9 @@ Unit* Creature::SelectVictim() if(m_invisibilityMask) { - Unit::AuraList const& iAuras = GetAurasByType(SPELL_AURA_MOD_INVISIBILITY); - for(Unit::AuraList::const_iterator itr = iAuras.begin(); itr != iAuras.end(); ++itr) - if((*itr)->IsPermanent()) + Unit::AuraEffectList const& iAuras = GetAurasByType(SPELL_AURA_MOD_INVISIBILITY); + for(Unit::AuraEffectList::const_iterator itr = iAuras.begin(); itr != iAuras.end(); ++itr) + if((*itr)->GetParentAura()->IsPermanent()) { AI()->EnterEvadeMode(); break; @@ -10252,8 +10357,11 @@ int32 Unit::CalculateSpellDamage(SpellEntry const* spellProto, uint8 effect_inde int32 randomPoints = int32(spellProto->EffectDieSides[effect_index] + level * randomPointsPerLevel); float comboDamage = spellProto->EffectPointsPerComboPoint[effect_index]; - // prevent random generator from getting confused by spells casted with Unit::CastCustomSpell - int32 randvalue = spellProto->EffectBaseDice[effect_index] >= randomPoints ? spellProto->EffectBaseDice[effect_index]:irand(spellProto->EffectBaseDice[effect_index], randomPoints); + // range can have possitive and negative values, so order its for irand + int32 randvalue = int32(spellProto->EffectBaseDice[effect_index]) >= randomPoints + ? irand(randomPoints, int32(spellProto->EffectBaseDice[effect_index])) + : irand(int32(spellProto->EffectBaseDice[effect_index]), randomPoints); + int32 value = basePoints + randvalue; //random damage if(comboDamage != 0 && unitPlayer /*&& target && (target->GetGUID() == unitPlayer->GetComboTarget())*/) @@ -10286,7 +10394,7 @@ int32 Unit::CalculateSpellDamage(SpellEntry const* spellProto, uint8 effect_inde return value; } -int32 Unit::CalculateSpellDuration(SpellEntry const* spellProto, uint8 effect_index, Unit const* target) +int32 Unit::CalcSpellDuration(SpellEntry const* spellProto) { Player* unitPlayer = (GetTypeId() == TYPEID_PLAYER) ? (Player*)this : NULL; @@ -10302,14 +10410,24 @@ int32 Unit::CalculateSpellDuration(SpellEntry const* spellProto, uint8 effect_in else duration = minduration; - if (duration > 0) + return duration; +} + +int32 Unit::ModSpellDuration(SpellEntry const* spellProto, Unit const* target, int32 duration, bool positive) +{ + //don't mod permament auras duration + if (duration<0) + return duration; + + //cut duration only of negative effects + if (!positive) { - int32 mechanic = GetEffectMechanic(spellProto, effect_index); + int32 mechanic = spellProto->Mechanic; + // Find total mod value (negative bonus) int32 durationMod_always = target->GetTotalAuraModifierByMiscValue(SPELL_AURA_MECHANIC_DURATION_MOD, mechanic); // Find max mod (negative bonus) int32 durationMod_not_stack = target->GetMaxNegativeAuraModifierByMiscValue(SPELL_AURA_MECHANIC_DURATION_MOD_NOT_STACK, mechanic); - int32 durationMod = 0; // Select strongest negative mod if (durationMod_always > durationMod_not_stack) @@ -10318,12 +10436,41 @@ int32 Unit::CalculateSpellDuration(SpellEntry const* spellProto, uint8 effect_in durationMod = durationMod_always; if (durationMod != 0) - duration = int32(int64(duration) * (100+durationMod) /100); + duration = int32( float (duration) * float(100.0f+durationMod) /100.0f); - if (duration < 0) duration = 0; + // there are only negative mods currently + durationMod_always =target->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL, spellProto->Dispel); + durationMod_not_stack=target->GetMaxNegativeAuraModifierByMiscValue(SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL_NOT_STACK, spellProto->Dispel); + + durationMod=0; + if (durationMod_always > durationMod_not_stack) + durationMod += durationMod_not_stack; + else + durationMod += durationMod_always; + + if (durationMod != 0) + duration = int32( float (duration) * float(100.0f+durationMod) /100.0f); } + //else positive mods here, there are no currently + //when there will be, change GetTotalAuraModifierByMiscValue to GetTotalPositiveAuraModifierByMiscValue + return duration>0 ? duration : 0; +} - return duration; +void Unit::ModSpellCastTime(SpellEntry const* spellProto, int32 & castTime, Spell const * spell) +{ + if (!spellProto || castTime<0) + return; + //called from caster + if(Player* modOwner = GetSpellModOwner()) + modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CASTING_TIME, castTime, spell); + + if( !(spellProto->Attributes & (SPELL_ATTR_UNK4|SPELL_ATTR_UNK5)) && spellProto->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && spellProto->SpellFamilyName) + castTime = int32( float(castTime) * GetFloatValue(UNIT_MOD_CAST_SPEED)); + else + { + if (spellProto->Attributes & SPELL_ATTR_RANGED && !(spellProto->AttributesEx2 & SPELL_ATTR_EX2_AUTOREPEAT_FLAG)) + castTime = int32 (float(castTime) * m_modAttackSpeedPct[RANGED_ATTACK]); + } } DiminishingLevels Unit::GetDiminishing(DiminishingGroup group) @@ -10357,21 +10504,15 @@ DiminishingLevels Unit::GetDiminishing(DiminishingGroup group) void Unit::IncrDiminishing(DiminishingGroup group) { // Checking for existing in the table - bool IsExist = false; for(Diminishing::iterator i = m_Diminishing.begin(); i != m_Diminishing.end(); ++i) { if(i->DRGroup != group) continue; - - IsExist = true; if(i->hitCount < DIMINISHING_LEVEL_IMMUNE) i->hitCount += 1; - - break; + return; } - - if(!IsExist) - m_Diminishing.push_back(DiminishingReturn(group,getMSTime(),DIMINISHING_LEVEL_2)); + m_Diminishing.push_back(DiminishingReturn(group,getMSTime(),DIMINISHING_LEVEL_2)); } void Unit::ApplyDiminishingToDuration(DiminishingGroup group, int32 &duration,Unit* caster,DiminishingLevels Level) @@ -10420,17 +10561,50 @@ void Unit::ApplyDiminishingAura( DiminishingGroup group, bool apply ) if(i->DRGroup != group) continue; - i->hitTime = getMSTime(); - if(apply) i->stack += 1; else if(i->stack) + { i->stack -= 1; - + // Remember time after last aura from group removed + if (i->stack == 0) + i->hitTime = getMSTime(); + } break; } } +uint32 Unit::GetSpellMaxRangeForTarget(Unit* target,const SpellRangeEntry * rangeEntry) +{ + if (!rangeEntry) + return 0; + if (rangeEntry->maxRangeHostile == rangeEntry->maxRangeFriend) + return rangeEntry->maxRangeFriend; + if (IsHostileTo(target)) + return rangeEntry->maxRangeHostile; + return rangeEntry->maxRangeFriend; +}; +uint32 Unit::GetSpellMinRangeForTarget(Unit* target,const SpellRangeEntry * rangeEntry) +{ + if (!rangeEntry) + return 0; + if (rangeEntry->minRangeHostile == rangeEntry->minRangeFriend) + return rangeEntry->minRangeFriend; + if (IsHostileTo(target)) + return rangeEntry->minRangeHostile; + return rangeEntry->minRangeFriend; +}; +uint32 Unit::GetSpellRadiusForTarget(Unit* target,const SpellRadiusEntry * radiusEntry) +{ + if (!radiusEntry) + return 0; + if (radiusEntry->radiusHostile == radiusEntry->radiusFriend) + return radiusEntry->radiusFriend; + if (IsHostileTo(target)) + return radiusEntry->radiusHostile; + return radiusEntry->radiusFriend; +}; + Unit* Unit::GetUnit(WorldObject& object, uint64 guid) { return ObjectAccessor::GetUnit(object,guid); @@ -10448,14 +10622,14 @@ Creature* Unit::GetCreature(WorldObject& object, uint64 guid) bool Unit::isVisibleForInState( Player const* u, bool inVisibleList ) const { - return isVisibleForOrDetect(u, false, inVisibleList, false); + return u->canSeeOrDetect(this, false, inVisibleList, false); } uint32 Unit::GetCreatureType() const { if(GetTypeId() == TYPEID_PLAYER) { - SpellShapeshiftEntry const* ssEntry = sSpellShapeshiftStore.LookupEntry(((Player*)this)->m_form); + SpellShapeshiftEntry const* ssEntry = sSpellShapeshiftStore.LookupEntry(m_form); if(ssEntry && ssEntry->creatureType > 0) return ssEntry->creatureType; else @@ -10518,7 +10692,9 @@ bool Unit::HandleStatModifier(UnitMods unitMod, UnitModifierType modifierType, f case UNIT_MOD_RAGE: case UNIT_MOD_FOCUS: case UNIT_MOD_ENERGY: - case UNIT_MOD_HAPPINESS: UpdateMaxPower(GetPowerTypeByAuraGroup(unitMod)); break; + case UNIT_MOD_HAPPINESS: + case UNIT_MOD_RUNE: + case UNIT_MOD_RUNIC_POWER: UpdateMaxPower(GetPowerTypeByAuraGroup(unitMod)); break; case UNIT_MOD_RESISTANCE_HOLY: case UNIT_MOD_RESISTANCE_FIRE: @@ -10545,7 +10721,7 @@ float Unit::GetModifierValue(UnitMods unitMod, UnitModifierType modifierType) co { if( unitMod >= UNIT_MOD_END || modifierType >= MODIFIER_TYPE_END) { - sLog.outError("ERROR: trial to access non existed modifier value from UnitMods!"); + sLog.outError("trial to access non existed modifier value from UnitMods!"); return 0.0f; } @@ -10575,7 +10751,7 @@ float Unit::GetTotalAuraModValue(UnitMods unitMod) const { if(unitMod >= UNIT_MOD_END) { - sLog.outError("ERROR: trial to access non existed UnitMods in GetTotalAuraModValue()!"); + sLog.outError("trial to access non existed UnitMods in GetTotalAuraModValue()!"); return 0.0f; } @@ -10631,32 +10807,36 @@ Stats Unit::GetStatByAuraGroup(UnitMods unitMod) const Powers Unit::GetPowerTypeByAuraGroup(UnitMods unitMod) const { - Powers power = POWER_MANA; - switch(unitMod) { - case UNIT_MOD_MANA: power = POWER_MANA; break; - case UNIT_MOD_RAGE: power = POWER_RAGE; break; - case UNIT_MOD_FOCUS: power = POWER_FOCUS; break; - case UNIT_MOD_ENERGY: power = POWER_ENERGY; break; - case UNIT_MOD_HAPPINESS: power = POWER_HAPPINESS; break; - - default: - break; + case UNIT_MOD_MANA: return POWER_MANA; + case UNIT_MOD_RAGE: return POWER_RAGE; + case UNIT_MOD_FOCUS: return POWER_FOCUS; + case UNIT_MOD_ENERGY: return POWER_ENERGY; + case UNIT_MOD_HAPPINESS: return POWER_HAPPINESS; + case UNIT_MOD_RUNE: return POWER_RUNE; + case UNIT_MOD_RUNIC_POWER:return POWER_RUNIC_POWER; } - return power; + return POWER_MANA; } float Unit::GetTotalAttackPowerValue(WeaponAttackType attType) const { - UnitMods unitMod = (attType == RANGED_ATTACK) ? UNIT_MOD_ATTACK_POWER_RANGED : UNIT_MOD_ATTACK_POWER; - - float val = GetTotalAuraModValue(unitMod); - if(val < 0.0f) - val = 0.0f; - - return val; + if (attType == RANGED_ATTACK) + { + int32 ap = GetInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER) + GetInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER_MODS); + if (ap < 0) + return 0.0f; + return ap * (1.0f + GetFloatValue(UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER)); + } + else + { + int32 ap = GetInt32Value(UNIT_FIELD_ATTACK_POWER) + GetInt32Value(UNIT_FIELD_ATTACK_POWER_MODS); + if (ap < 0) + return 0.0f; + return ap * (1.0f + GetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER)); + } } float Unit::GetWeaponDamageRange(WeaponAttackType attType ,WeaponDamageRange type) const @@ -10744,6 +10924,12 @@ void Unit::SetPower(Powers power, uint32 val) SetStatInt32Value(UNIT_FIELD_POWER1 + power, val); + WorldPacket data(SMSG_POWER_UPDATE); + data.append(GetPackGUID()); + data << uint8(power); + data << uint32(val); + SendMessageToSet(&data, GetTypeId() == TYPEID_PLAYER ? true : false); + // group update if(GetTypeId() == TYPEID_PLAYER) { @@ -10838,15 +11024,6 @@ void Unit::ApplyMaxPowerMod(Powers power, uint32 val, bool apply) } } -void Unit::ApplyAuraProcTriggerDamage( Aura* aura, bool apply ) -{ - AuraList& tAuraProcTriggerDamage = m_modAuras[SPELL_AURA_PROC_TRIGGER_DAMAGE]; - if(apply) - tAuraProcTriggerDamage.push_back(aura); - else - tAuraProcTriggerDamage.remove(aura); -} - uint32 Unit::GetCreatePowers( Powers power ) const { // POWER_FOCUS and POWER_HAPPINESS only have hunter pet @@ -10857,6 +11034,7 @@ uint32 Unit::GetCreatePowers( Powers power ) const case POWER_FOCUS: return (GetTypeId()==TYPEID_PLAYER || !((Creature const*)this)->isPet() || ((Pet const*)this)->getPetType()!=HUNTER_PET ? 0 : 100); case POWER_ENERGY: return 100; case POWER_HAPPINESS: return (GetTypeId()==TYPEID_PLAYER || !((Creature const*)this)->isPet() || ((Pet const*)this)->getPetType()!=HUNTER_PET ? 0 : 1050000); + case POWER_RUNIC_POWER: return 1000; } return 0; @@ -10876,22 +11054,31 @@ void Unit::AddToWorld() void Unit::RemoveFromWorld() { // cleanup + assert(GetGUID()); + if(IsInWorld()) { + UnsummonAllTotems(); + RemoveAllControlled(); RemoveCharmAuras(); RemoveBindSightAuras(); RemoveNotOwnSingleTargetAuras(); + + if(GetCharmerGUID()) + sLog.outCrash("Unit %u has charmer guid when removed from world", GetEntry()); + WorldObject::RemoveFromWorld(); } } void Unit::CleanupsBeforeDelete() { - assert(m_uint32Values); + assert(GetGUID()); //A unit may be in removelist and not in world, but it is still in grid //and may have some references during delete RemoveAllAuras(); + _DeleteAuras(); InterruptNonMeleeSpells(true); m_Events.KillAllEvents(false); // non-delatable (currently casted spells) will not deleted now but it will deleted at call in Map::RemoveAllObjectsInRemoveList CombatStop(); @@ -11002,7 +11189,7 @@ void CharmInfo::InitEmptyActionBar(bool withAttack) for(uint32 x = 0; x < 10; ++x) { - PetActionBar[x].Type = ACT_CAST; + PetActionBar[x].Type = ACT_PASSIVE; PetActionBar[x].SpellOrAction = 0; } if (withAttack) @@ -11024,7 +11211,7 @@ void CharmInfo::InitPossessCreateSpells() if(IsPassiveSpell(spellid)) m_unit->CastSpell(m_unit, spellid, true); else - AddSpellToAB(0, spellid, ACT_CAST); + AddSpellToAB(0, spellid, ACT_DISABLED); } } } @@ -11039,7 +11226,7 @@ void CharmInfo::InitCharmCreateSpells() InitPetActionBar(); - for(uint32 x = 0; x < CREATURE_MAX_SPELLS; ++x) + for(uint32 x = 0; x < MAX_SPELL_CHARM; ++x) { uint32 spellId = ((Creature*)m_unit)->m_spells[x]; m_charmspells[x].spellId = spellId; @@ -11068,7 +11255,7 @@ void CharmInfo::InitCharmCreateSpells() if(onlyselfcast || !IsPositiveSpell(spellId)) //only self cast and spells versus enemies are autocastable newstate = ACT_DISABLED; else - newstate = ACT_CAST; + newstate = ACT_PASSIVE; AddSpellToAB(0, spellId, newstate); } @@ -11079,7 +11266,7 @@ bool CharmInfo::AddSpellToAB(uint32 oldid, uint32 newid, ActiveStates newstate) { for(uint8 i = 0; i < 10; i++) { - if((PetActionBar[i].Type == ACT_DISABLED || PetActionBar[i].Type == ACT_ENABLED || PetActionBar[i].Type == ACT_CAST) && PetActionBar[i].SpellOrAction == oldid) + if((PetActionBar[i].Type == ACT_DISABLED || PetActionBar[i].Type == ACT_ENABLED || PetActionBar[i].Type == ACT_PASSIVE) && PetActionBar[i].SpellOrAction == oldid) { PetActionBar[i].SpellOrAction = newid; if(!oldid) @@ -11101,7 +11288,7 @@ void CharmInfo::ToggleCreatureAutocast(uint32 spellid, bool apply) if(IsPassiveSpell(spellid)) return; - for(uint32 x = 0; x < CREATURE_MAX_SPELLS; ++x) + for(uint32 x = 0; x < MAX_SPELL_CHARM; ++x) { if(spellid == m_charmspells[x].spellId) { @@ -11121,209 +11308,29 @@ void CharmInfo::SetPetNumber(uint32 petnumber, bool statwindow) bool Unit::isFrozen() const { - AuraList const& mRoot = GetAurasByType(SPELL_AURA_MOD_ROOT); - for(AuraList::const_iterator i = mRoot.begin(); i != mRoot.end(); ++i) - if( GetSpellSchoolMask((*i)->GetSpellProto()) & SPELL_SCHOOL_MASK_FROST) - return true; - return false; + return HasAuraState(AURA_STATE_FROZEN); } -/* struct ProcTriggeredData { - ProcTriggeredData(Aura* _triggeredByAura, uint32 _cooldown) - : triggeredByAura(_triggeredByAura), - triggeredByAura_SpellPair(Unit::spellEffectPair(triggeredByAura->GetId(),triggeredByAura->GetEffIndex())), - cooldown(_cooldown) - {} - - Aura* triggeredByAura; // triggred aura, can be invalidate at triggered aura proccessing - Unit::spellEffectPair triggeredByAura_SpellPair; // spell pair, used for re-find aura (by pointer comparison in range) - uint32 cooldown; // possible hidden cooldown -}; - -typedef std::list< ProcTriggeredData > ProcTriggeredList; - -void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag, AuraTypeSet const& procAuraTypes, WeaponAttackType attType, SpellEntry const * procSpell, uint32 damage, SpellSchoolMask damageSchoolMask ) -{ - for(AuraTypeSet::const_iterator aur = procAuraTypes.begin(); aur != procAuraTypes.end(); ++aur) + ProcTriggeredData(Aura* _aura) + : aura(_aura) { - // List of spells (effects) that proceed. Spell prototype and aura-specific value (damage for TRIGGER_DAMAGE) - ProcTriggeredList procTriggered; - - AuraList const& auras = GetAurasByType(*aur); - for(AuraList::const_iterator i = auras.begin(), next; i != auras.end(); i = next) - { - next = i; ++next; - - Aura* i_aura = *i; - - uint32 cooldown; // returned at next line - if(!IsTriggeredAtSpellProcEvent(i_aura->GetSpellProto(), procSpell, procFlag,attType,isVictim,cooldown)) - continue; - - procTriggered.push_back( ProcTriggeredData(i_aura, cooldown) ); - } - - // Handle effects proceed this time - for(ProcTriggeredList::iterator i = procTriggered.begin(); i != procTriggered.end(); ++i) - { - // Some auras can be deleted in function called in this loop (except first, ofc) - // Until storing auras in std::multimap to hard check deleting by another way - if(i != procTriggered.begin()) - { - bool found = false; - AuraMap::const_iterator lower = GetAuras().lower_bound(i->triggeredByAura_SpellPair); - AuraMap::const_iterator upper = GetAuras().upper_bound(i->triggeredByAura_SpellPair); - for(AuraMap::const_iterator itr = lower; itr!= upper; ++itr) - { - if(itr->second==i->triggeredByAura) - { - found = true; - break; - } - } - - if(!found) - { - sLog.outError("Spell aura %u (id:%u effect:%u) has been deleted before call spell proc event handler",*aur,i->triggeredByAura_SpellPair.first,i->triggeredByAura_SpellPair.second); - sLog.outError("It can be deleted one from early processed auras:"); - for(ProcTriggeredList::iterator i2 = procTriggered.begin(); i != i2; ++i2) - sLog.outError(" Spell aura %u (id:%u effect:%u)",*aur,i2->triggeredByAura_SpellPair.first,i2->triggeredByAura_SpellPair.second); - sLog.outError(" <end of list>"); - continue; - } - } - - /// this is aura triggering code call - Aura* triggeredByAura = i->triggeredByAura; - - /// save charges existence before processing to prevent crash at access to deleted triggered aura after - /// used in speedup code check before check aura existance. - bool triggeredByAuraWithCharges = triggeredByAura->m_procCharges > 0; - - /// success in event proccesing - /// used in speedup code check before check aura existance. - bool casted = false; - - /// process triggered code - switch(*aur) - { - case SPELL_AURA_PROC_TRIGGER_SPELL: - { - sLog.outDebug("ProcDamageAndSpell: casting spell (triggered by %s proc aura of spell %u)", - (isVictim?"a victim's":"an attacker's"),triggeredByAura->GetId()); - casted = HandleProcTriggerSpell(pTarget, damage, triggeredByAura, procSpell, procFlag, attType, i->cooldown); - break; - } - case SPELL_AURA_PROC_TRIGGER_DAMAGE: - { - uint32 triggered_damage = triggeredByAura->GetModifier()->m_amount; - sLog.outDebug("ProcDamageAndSpell: doing %u damage (triggered by %s aura of spell %u)", - triggered_damage, (isVictim?"a victim's":"an attacker's"),triggeredByAura->GetId()); - SpellNonMeleeDamageLog(pTarget, triggeredByAura->GetId(), triggered_damage, true, true); - casted = true; - break; - } - case SPELL_AURA_DUMMY: - { - uint32 effect = triggeredByAura->GetEffIndex(); - sLog.outDebug("ProcDamageAndSpell: casting spell (triggered by %s dummy aura of spell %u)", - (isVictim?"a victim's":"an attacker's"),triggeredByAura->GetId()); - casted = HandleDummyAuraProc(pTarget, damage, triggeredByAura, procSpell, procFlag,i->cooldown); - break; - } - case SPELL_AURA_PRAYER_OF_MENDING: - { - sLog.outDebug("ProcDamageAndSpell: casting mending (triggered by %s dummy aura of spell %u)", - (isVictim?"a victim's":"an attacker's"),triggeredByAura->GetId()); - - casted = HandleMeandingAuraProc(triggeredByAura); - break; - } - case SPELL_AURA_MOD_HASTE: - { - sLog.outDebug("ProcDamageAndSpell: casting spell (triggered by %s haste aura of spell %u)", - (isVictim?"a victim's":"an attacker's"),triggeredByAura->GetId()); - casted = HandleHasteAuraProc(pTarget, damage, triggeredByAura, procSpell, procFlag,i->cooldown); - break; - } - case SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN: - { - // nothing do, just charges counter - // but count only in case appropriate school damage - casted = triggeredByAura->GetModifier()->m_miscvalue & damageSchoolMask; - break; - } - case SPELL_AURA_OVERRIDE_CLASS_SCRIPTS: - { - sLog.outDebug("ProcDamageAndSpell: casting spell (triggered by %s class script aura of spell %u)", - (isVictim?"a victim's":"an attacker's"),triggeredByAura->GetId()); - casted = HandleOverrideClassScriptAuraProc(pTarget, triggeredByAura, procSpell,i->cooldown); - break; - } - default: - { - // nothing do, just charges counter - casted = true; - break; - } - } - - /// Update charge (aura can be removed by triggers) - if(casted && triggeredByAuraWithCharges) - { - /// need re-found aura (can be dropped by triggers) - AuraMap::const_iterator lower = GetAuras().lower_bound(i->triggeredByAura_SpellPair); - AuraMap::const_iterator upper = GetAuras().upper_bound(i->triggeredByAura_SpellPair); - for(AuraMap::const_iterator itr = lower; itr!= upper; ++itr) - { - if(itr->second == triggeredByAura) // pointer still valid - { - if(triggeredByAura->m_procCharges > 0) - triggeredByAura->m_procCharges -= 1; - - triggeredByAura->UpdateAuraCharges(); - break; - } - } - } - } - - /// Safely remove auras with zero charges - for(AuraList::const_iterator i = auras.begin(), next; i != auras.end(); i = next) - { - next = i; ++next; - if((*i)->m_procCharges == 0) - { - RemoveAurasDueToSpell((*i)->GetId()); - next = auras.begin(); - } - } + effMask = 0; + spellProcEvent = NULL; } -} -*/ -struct ProcTriggeredData -{ - ProcTriggeredData(SpellProcEventEntry const * _spellProcEvent, Aura* _triggeredByAura) - : spellProcEvent(_spellProcEvent), triggeredByAura(_triggeredByAura), - triggeredByAura_SpellPair(Unit::spellEffectPair(triggeredByAura->GetId(),triggeredByAura->GetEffIndex())) - {} SpellProcEventEntry const *spellProcEvent; - Aura* triggeredByAura; - Unit::spellEffectPair triggeredByAura_SpellPair; + Aura * aura; + uint32 effMask; }; typedef std::list< ProcTriggeredData > ProcTriggeredList; -typedef std::list< uint32> RemoveSpellList; // List of auras that CAN be trigger but may not exist in spell_proc_event // in most case need for drop charges // in some types of aura need do additional check // for example SPELL_AURA_MECHANIC_IMMUNITY - need check for mechanic -static bool isTriggerAura[TOTAL_AURAS]; -static bool isNonTriggerAura[TOTAL_AURAS]; -void InitTriggerAuraData() +bool InitTriggerAuraData() { for (int i=0;i<TOTAL_AURAS;i++) { @@ -11342,7 +11349,7 @@ void InitTriggerAuraData() isTriggerAura[SPELL_AURA_DAMAGE_IMMUNITY] = true; isTriggerAura[SPELL_AURA_PROC_TRIGGER_SPELL] = true; isTriggerAura[SPELL_AURA_PROC_TRIGGER_DAMAGE] = true; - isTriggerAura[SPELL_AURA_MOD_CASTING_SPEED] = true; + isTriggerAura[SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK] = true; isTriggerAura[SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT] = true; isTriggerAura[SPELL_AURA_MOD_POWER_COST_SCHOOL] = true; isTriggerAura[SPELL_AURA_REFLECT_SPELLS_SCHOOL] = true; @@ -11356,10 +11363,16 @@ void InitTriggerAuraData() isTriggerAura[SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS] = true; isTriggerAura[SPELL_AURA_MOD_HASTE] = true; isTriggerAura[SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE]=true; - isTriggerAura[SPELL_AURA_PRAYER_OF_MENDING] = true; + isTriggerAura[SPELL_AURA_RAID_PROC_FROM_CHARGE] = true; + isTriggerAura[SPELL_AURA_RAID_PROC_FROM_CHARGE_WITH_VALUE] = true; + isTriggerAura[SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE] = true; + isTriggerAura[SPELL_AURA_MOD_DAMAGE_FROM_CASTER] = true; + isTriggerAura[SPELL_AURA_ABILITY_IGNORE_AURASTATE] = true; isNonTriggerAura[SPELL_AURA_MOD_POWER_REGEN]=true; - isNonTriggerAura[SPELL_AURA_RESIST_PUSHBACK]=true; + isNonTriggerAura[SPELL_AURA_REDUCE_PUSHBACK]=true; + + return true; } uint32 createProcExtendMask(SpellNonMeleeDamage *damageInfo, SpellMissInfo missCondition) @@ -11400,18 +11413,8 @@ uint32 createProcExtendMask(SpellNonMeleeDamage *damageInfo, SpellMissInfo missC return procEx; } -static int deep = 0; void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, SpellEntry const * procSpell, uint32 damage ) { - deep ++; - if (deep > 5) - { - sLog.outError("Prevent possible stack owerflow in Unit::ProcDamageAndSpellFor"); - if (procSpell) - sLog.outError(" Spell %u", procSpell->Id); - deep--; - return; - } // For melee/ranged based attack need update skills and set some Aura states if (procFlag & MELEE_BASED_TRIGGER_MASK) { @@ -11422,7 +11425,7 @@ void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag if (procExtra&(PROC_EX_NORMAL_HIT|PROC_EX_MISS|PROC_EX_RESIST)) { if (pTarget->GetTypeId() != TYPEID_PLAYER && pTarget->GetCreatureType() != CREATURE_TYPE_CRITTER) - ((Player*)this)->UpdateCombatSkills(pTarget, attType, MELEE_HIT_MISS, isVictim); + ((Player*)this)->UpdateCombatSkills(pTarget, attType, isVictim); } // Update defence if player is victim and parry/dodge/block if (isVictim && procExtra&(PROC_EX_DODGE|PROC_EX_PARRY|PROC_EX_BLOCK)) @@ -11474,199 +11477,196 @@ void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag ((Player*)this)->AddComboPoints(pTarget, 1); StartReactiveTimer( REACTIVE_OVERPOWER ); } - // Enable AURA_STATE_CRIT on crit - if (procExtra & PROC_EX_CRITICAL_HIT) - { - ModifyAuraState(AURA_STATE_CRIT, true); - StartReactiveTimer( REACTIVE_CRIT ); - if(getClass()==CLASS_HUNTER) - { - ModifyAuraState(AURA_STATE_HUNTER_CRIT_STRIKE, true); - StartReactiveTimer( REACTIVE_HUNTER_CRIT ); - } - } } } } - RemoveSpellList removedSpells; ProcTriggeredList procTriggered; // Fill procTriggered list for(AuraMap::const_iterator itr = GetAuras().begin(); itr!= GetAuras().end(); ++itr) { - SpellProcEventEntry const* spellProcEvent = NULL; - if(!IsTriggeredAtSpellProcEvent(itr->second, procSpell, procFlag, procExtra, attType, isVictim, (damage > 0), spellProcEvent)) - continue; + ProcTriggeredData triggerData(itr->second); - procTriggered.push_back( ProcTriggeredData(spellProcEvent, itr->second) ); - } - // Handle effects proceed this time - for(ProcTriggeredList::iterator i = procTriggered.begin(); i != procTriggered.end(); ++i) - { - // Some auras can be deleted in function called in this loop (except first, ofc) - // Until storing auars in std::multimap to hard check deleting by another way - if(i != procTriggered.begin()) + if(!IsTriggeredAtSpellProcEvent(pTarget, triggerData.aura, procSpell, procFlag, procExtra, attType, isVictim, (damage > 0), triggerData.spellProcEvent)) + continue; + + for (uint8 i=0; i<MAX_SPELL_EFFECTS;++i) { - bool found = false; - AuraMap::const_iterator lower = GetAuras().lower_bound(i->triggeredByAura_SpellPair); - AuraMap::const_iterator upper = GetAuras().upper_bound(i->triggeredByAura_SpellPair); - for(AuraMap::const_iterator itr = lower; itr!= upper; ++itr) - { - if(itr->second==i->triggeredByAura) - { - found = true; - break; - } - } - if(!found) + if (AuraEffect * aurEff = itr->second->GetPartAura(i)) { -// sLog.outDebug("Spell aura %u (id:%u effect:%u) has been deleted before call spell proc event handler", i->triggeredByAura->GetModifier()->m_auraname, i->triggeredByAura_SpellPair.first, i->triggeredByAura_SpellPair.second); -// sLog.outDebug("It can be deleted one from early proccesed auras:"); -// for(ProcTriggeredList::iterator i2 = procTriggered.begin(); i != i2; ++i2) -// sLog.outDebug(" Spell aura %u (id:%u effect:%u)", i->triggeredByAura->GetModifier()->m_auraname,i2->triggeredByAura_SpellPair.first,i2->triggeredByAura_SpellPair.second); -// sLog.outDebug(" <end of list>"); - continue; + // Skip this auras + if (isNonTriggerAura[aurEff->GetAuraName()]) + continue; + // If not trigger by default and spellProcEvent==NULL - skip + if (!isTriggerAura[aurEff->GetAuraName()] && triggerData.spellProcEvent==NULL) + continue; + + triggerData.effMask |= 1<<i; } } + if (triggerData.effMask) + procTriggered.push_front(triggerData); + } + + // Nothing found + if (procTriggered.empty()) + return; + // Handle effects proceed this time + for(ProcTriggeredList::iterator i = procTriggered.begin(); i != procTriggered.end(); ++i) + { + // look for aura in auras list, it may be removed while proc event processing + if (!HasAura(i->aura)) + continue; + + bool useCharges= i->aura->GetAuraCharges()>0; + bool takeCharges = false; + SpellEntry const *spellInfo = i->aura->GetSpellProto(); + uint32 Id=i->aura->GetId(); - SpellProcEventEntry const *spellProcEvent = i->spellProcEvent; - Aura *triggeredByAura = i->triggeredByAura; - Modifier *auraModifier = triggeredByAura->GetModifier(); - SpellEntry const *spellInfo = triggeredByAura->GetSpellProto(); - uint32 effIndex = triggeredByAura->GetEffIndex(); - bool useCharges = triggeredByAura->m_procCharges > 0; // For players set spell cooldown if need uint32 cooldown = 0; - if (GetTypeId() == TYPEID_PLAYER && spellProcEvent && spellProcEvent->cooldown) - cooldown = spellProcEvent->cooldown; + if (GetTypeId() == TYPEID_PLAYER && i->spellProcEvent && i->spellProcEvent->cooldown) + cooldown = i->spellProcEvent->cooldown; + + uint32 procDebug = 0; - switch(auraModifier->m_auraname) + for (uint8 effIndex = 0; effIndex<MAX_SPELL_EFFECTS;++effIndex) { - case SPELL_AURA_PROC_TRIGGER_SPELL: - { - sLog.outDebug("ProcDamageAndSpell: casting spell %u (triggered by %s aura of spell %u)", spellInfo->Id,(isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); - // Don`t drop charge or add cooldown for not started trigger - if (!HandleProcTriggerSpell(pTarget, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) - continue; - break; - } - case SPELL_AURA_PROC_TRIGGER_DAMAGE: - { - sLog.outDebug("ProcDamageAndSpell: doing %u damage from spell id %u (triggered by %s aura of spell %u)", auraModifier->m_amount, spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); - SpellNonMeleeDamage damageInfo(this, pTarget, spellInfo->Id, spellInfo->SchoolMask); - uint32 damage = SpellDamageBonus(pTarget, spellInfo, auraModifier->m_amount, SPELL_DIRECT_DAMAGE); - CalculateSpellDamageTaken(&damageInfo, damage, spellInfo); - SendSpellNonMeleeDamageLog(&damageInfo); - DealSpellDamage(&damageInfo, true); - break; - } - case SPELL_AURA_MANA_SHIELD: - case SPELL_AURA_DUMMY: - { - sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s dummy aura of spell %u)", spellInfo->Id,(isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); - if (!HandleDummyAuraProc(pTarget, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) - continue; - break; - } - case SPELL_AURA_MOD_HASTE: - { - sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s haste aura of spell %u)", spellInfo->Id,(isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); - if (!HandleHasteAuraProc(pTarget, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) - continue; - break; - } - case SPELL_AURA_OVERRIDE_CLASS_SCRIPTS: - { - sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", spellInfo->Id,(isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); - if (!HandleOverrideClassScriptAuraProc(pTarget, triggeredByAura, procSpell, cooldown)) - continue; - break; - } - case SPELL_AURA_PRAYER_OF_MENDING: + if (!(i->effMask & (1<<effIndex))) + continue; + + AuraEffect *triggeredByAura = i->aura->GetPartAura(effIndex); + assert(triggeredByAura); + + switch(triggeredByAura->GetAuraName()) { - sLog.outDebug("ProcDamageAndSpell: casting mending (triggered by %s dummy aura of spell %u)", - (isVictim?"a victim's":"an attacker's"),triggeredByAura->GetId()); + case SPELL_AURA_PROC_TRIGGER_SPELL: + { + sLog.outDebug("ProcDamageAndSpell: casting spell %u (triggered by %s aura of spell %u)", spellInfo->Id,(isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); + // Don`t drop charge or add cooldown for not started trigger + if (!HandleProcTriggerSpell(pTarget, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) + continue; + break; + } + case SPELL_AURA_PROC_TRIGGER_DAMAGE: + { + sLog.outDebug("ProcDamageAndSpell: doing %u damage from spell id %u (triggered by %s aura of spell %u)", triggeredByAura->GetAmount() , spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); + SpellNonMeleeDamage damageInfo(this, pTarget, spellInfo->Id, spellInfo->SchoolMask); + uint32 damage = SpellDamageBonus(pTarget, spellInfo, triggeredByAura->GetAmount(), SPELL_DIRECT_DAMAGE); + CalculateSpellDamageTaken(&damageInfo, damage, spellInfo); + SendSpellNonMeleeDamageLog(&damageInfo); + DealSpellDamage(&damageInfo, true); + break; + } + case SPELL_AURA_MANA_SHIELD: + case SPELL_AURA_DUMMY: + { + sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s dummy aura of spell %u)", spellInfo->Id,(isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); + if (!HandleDummyAuraProc(pTarget, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) + continue; + if (procDebug & 1) + sLog.outError("Dummy aura of spell %d procs twice from one effect!",spellInfo->Id); + procDebug |= 1; + break; + } + case SPELL_AURA_OBS_MOD_ENERGY: + sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", spellInfo->Id,(isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); + if (!HandleObsModEnergyAuraProc(pTarget, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) + continue; + if (procDebug & 2) + sLog.outError("ObsModEnergy aura of spell %d procs twice from one effect!",spellInfo->Id); + procDebug |= 2; + break; + case SPELL_AURA_MOD_HASTE: + { + sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s haste aura of spell %u)", spellInfo->Id,(isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); + if (!HandleHasteAuraProc(pTarget, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) + continue; + if (procDebug & 4) + sLog.outError("Haste aura of spell %d procs twice from one effect!",spellInfo->Id); + procDebug |= 4; + break; + } + case SPELL_AURA_OVERRIDE_CLASS_SCRIPTS: + { + sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", spellInfo->Id,(isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); + if (!HandleOverrideClassScriptAuraProc(pTarget, damage, triggeredByAura, procSpell, cooldown)) + continue; + if (procDebug & 8) + sLog.outError("OverrideClassScripts aura of spell %d procs twice from one effect!",spellInfo->Id); + procDebug |= 8; + break; + } + case SPELL_AURA_RAID_PROC_FROM_CHARGE_WITH_VALUE: + { + sLog.outDebug("ProcDamageAndSpell: casting mending (triggered by %s dummy aura of spell %u)", + (isVictim?"a victim's":"an attacker's"),triggeredByAura->GetId()); - HandleMeandingAuraProc(triggeredByAura); - break; - } - case SPELL_AURA_MOD_STUN: - // Remove by default, but if charge exist drop it - if (triggeredByAura->m_procCharges == 0) - removedSpells.push_back(triggeredByAura->GetId()); - break; - case SPELL_AURA_MELEE_ATTACK_POWER_ATTACKER_BONUS: - case SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS: - // Hunter's Mark (1-4 Rangs) - if (spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && (spellInfo->SpellFamilyFlags&0x0000000000000400LL)) + HandleAuraRaidProcFromChargeWithValue(triggeredByAura); + break; + } + case SPELL_AURA_RAID_PROC_FROM_CHARGE: { - uint32 basevalue = triggeredByAura->GetBasePoints(); - auraModifier->m_amount += basevalue/10; - if (auraModifier->m_amount > basevalue*4) - auraModifier->m_amount = basevalue*4; + sLog.outDebug("ProcDamageAndSpell: casting mending (triggered by %s dummy aura of spell %u)", + (isVictim?"a victim's":"an attacker's"),triggeredByAura->GetId()); + + HandleAuraRaidProcFromCharge(triggeredByAura); + break; } - break; - case SPELL_AURA_MOD_CASTING_SPEED: - // Skip melee hits or instant cast spells - if (procSpell == NULL || GetSpellCastTime(procSpell) == 0) - continue; - break; - case SPELL_AURA_REFLECT_SPELLS_SCHOOL: - // Skip Melee hits and spells ws wrong school - if (procSpell == NULL || (auraModifier->m_miscvalue & procSpell->SchoolMask) == 0) - continue; - break; - case SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT: - case SPELL_AURA_MOD_POWER_COST_SCHOOL: - // Skip melee hits and spells ws wrong school or zero cost - if (procSpell == NULL || - (procSpell->manaCost == 0 && procSpell->ManaCostPercentage == 0) || // Cost check - (auraModifier->m_miscvalue & procSpell->SchoolMask) == 0) // School check - continue; - break; - case SPELL_AURA_MECHANIC_IMMUNITY: - // Compare mechanic - if (procSpell==NULL || procSpell->Mechanic != auraModifier->m_miscvalue) - continue; - break; - case SPELL_AURA_MOD_MECHANIC_RESISTANCE: - // Compare mechanic - if (procSpell==NULL || procSpell->Mechanic != auraModifier->m_miscvalue) - continue; - break; - default: - // nothing do, just charges counter - break; - } - // Remove charge (aura can be removed by triggers) - if(useCharges) - { - // need found aura on drop (can be dropped by triggers) - AuraMap::const_iterator lower = GetAuras().lower_bound(i->triggeredByAura_SpellPair); - AuraMap::const_iterator upper = GetAuras().upper_bound(i->triggeredByAura_SpellPair); - for(AuraMap::const_iterator itr = lower; itr!= upper; ++itr) - { - if(itr->second == i->triggeredByAura) + case SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE: { - triggeredByAura->m_procCharges -=1; - triggeredByAura->UpdateAuraCharges(); - if (triggeredByAura->m_procCharges <= 0) - removedSpells.push_back(triggeredByAura->GetId()); + sLog.outDebug("ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)", spellInfo->Id,(isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); + + if (!HandleProcTriggerSpell(pTarget, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) + continue; break; } + case SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK: + // Skip melee hits or instant cast spells + if (procSpell == NULL || GetSpellCastTime(procSpell) == 0) + continue; + break; + case SPELL_AURA_REFLECT_SPELLS_SCHOOL: + // Skip Melee hits and spells ws wrong school + if (procSpell == NULL || (triggeredByAura->GetMiscValue() & procSpell->SchoolMask) == 0) + continue; + break; + case SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT: + case SPELL_AURA_MOD_POWER_COST_SCHOOL: + // Skip melee hits and spells ws wrong school or zero cost + if (procSpell == NULL || + (procSpell->manaCost == 0 && procSpell->ManaCostPercentage == 0) || // Cost check + (triggeredByAura->GetMiscValue() & procSpell->SchoolMask) == 0) // School check + continue; + break; + case SPELL_AURA_MECHANIC_IMMUNITY: + // Compare mechanic + if (procSpell==NULL || procSpell->Mechanic != triggeredByAura->GetMiscValue()) + continue; + break; + case SPELL_AURA_MOD_MECHANIC_RESISTANCE: + // Compare mechanic + if (procSpell==NULL || procSpell->Mechanic != triggeredByAura->GetMiscValue()) + continue; + break; + case SPELL_AURA_MOD_DAMAGE_FROM_CASTER: + // Compare casters + if (triggeredByAura->GetCasterGUID() != pTarget->GetGUID()) + continue; + break; + default: + // nothing do, just charges counter + break; } + takeCharges=true; + } + // Remove charge (aura can be removed by triggers) + if(useCharges && takeCharges) + { + if (i->aura->DropAuraCharge()) + RemoveAura(i->aura); } } - if (removedSpells.size()) - { - // Sort spells and remove dublicates - removedSpells.sort(); - removedSpells.unique(); - // Remove auras from removedAuras - for(RemoveSpellList::const_iterator i = removedSpells.begin(); i != removedSpells.end();i++) - RemoveAurasDueToSpell(*i); - } - deep--; } SpellSchoolMask Unit::GetMeleeDamageSchoolMask() const @@ -11688,15 +11688,21 @@ Player* Unit::GetSpellModOwner() const } ///----------Pet responses methods----------------- -void Unit::SendPetCastFail(uint32 spellid, uint8 msg) +void Unit::SendPetCastFail(uint32 spellid, SpellCastResult msg) { + if(msg == SPELL_CAST_OK) + return; + Unit *owner = GetCharmerOrOwner(); if(!owner || owner->GetTypeId() != TYPEID_PLAYER) return; WorldPacket data(SMSG_PET_CAST_FAILED, (4+1)); + data << uint8(0); // cast count? data << uint32(spellid); data << uint8(msg); + // uint32 for some reason + // uint32 for some reason ((Player*)owner)->GetSession()->SendPacket(&data); } @@ -11815,8 +11821,8 @@ void Unit::SetFeared(bool apply, uint64 casterGUID, uint32 spellID) // attack caster if can Unit* caster = ObjectAccessor::GetObjectInWorld(casterGUID, (Unit*)NULL); - if(caster && caster != getVictim() && ((Creature*)this)->IsAIEnabled) - ((Creature*)this)->AI()->AttackStart(caster); + if(caster && ((Creature*)this)->AI()) + ((Creature*)this)->AI()->AttackedBy(caster); } } @@ -11856,15 +11862,16 @@ void Unit::SetConfused(bool apply, uint64 casterGUID, uint32 spellID) bool Unit::IsSitState() const { uint8 s = getStandState(); - return s == PLAYER_STATE_SIT_CHAIR || s == PLAYER_STATE_SIT_LOW_CHAIR || - s == PLAYER_STATE_SIT_MEDIUM_CHAIR || s == PLAYER_STATE_SIT_HIGH_CHAIR || - s == PLAYER_STATE_SIT; + return + s == UNIT_STAND_STATE_SIT_CHAIR || s == UNIT_STAND_STATE_SIT_LOW_CHAIR || + s == UNIT_STAND_STATE_SIT_MEDIUM_CHAIR || s == UNIT_STAND_STATE_SIT_HIGH_CHAIR || + s == UNIT_STAND_STATE_SIT; } bool Unit::IsStandState() const { uint8 s = getStandState(); - return !IsSitState() && s != PLAYER_STATE_SLEEP && s != PLAYER_STATE_KNEEL; + return !IsSitState() && s != UNIT_STAND_STATE_SLEEP && s != UNIT_STAND_STATE_KNEEL; } void Unit::SetStandState(uint8 state) @@ -11918,7 +11925,6 @@ void Unit::ClearComboPointHolders() void Unit::ClearAllReactives() { - for(int i=0; i < MAX_REACTIVE; ++i) m_reactiveTimer[i] = 0; @@ -11926,11 +11932,6 @@ void Unit::ClearAllReactives() ModifyAuraState(AURA_STATE_DEFENSE, false); if (getClass() == CLASS_HUNTER && HasAuraState( AURA_STATE_HUNTER_PARRY)) ModifyAuraState(AURA_STATE_HUNTER_PARRY, false); - if (HasAuraState( AURA_STATE_CRIT)) - ModifyAuraState(AURA_STATE_CRIT, false); - if (getClass() == CLASS_HUNTER && HasAuraState( AURA_STATE_HUNTER_CRIT_STRIKE) ) - ModifyAuraState(AURA_STATE_HUNTER_CRIT_STRIKE, false); - if(getClass() == CLASS_WARRIOR && GetTypeId() == TYPEID_PLAYER) ((Player*)this)->ClearComboPoints(); } @@ -11958,14 +11959,6 @@ void Unit::UpdateReactives( uint32 p_time ) if ( getClass() == CLASS_HUNTER && HasAuraState(AURA_STATE_HUNTER_PARRY)) ModifyAuraState(AURA_STATE_HUNTER_PARRY, false); break; - case REACTIVE_CRIT: - if (HasAuraState(AURA_STATE_CRIT)) - ModifyAuraState(AURA_STATE_CRIT, false); - break; - case REACTIVE_HUNTER_CRIT: - if ( getClass() == CLASS_HUNTER && HasAuraState(AURA_STATE_HUNTER_CRIT_STRIKE) ) - ModifyAuraState(AURA_STATE_HUNTER_CRIT_STRIKE, false); - break; case REACTIVE_OVERPOWER: if(getClass() == CLASS_WARRIOR && GetTypeId() == TYPEID_PLAYER) ((Player*)this)->ClearComboPoints(); @@ -11985,7 +11978,7 @@ Unit* Unit::SelectNearbyTarget(float dist) const { std::list<Unit *> targets; Trinity::AnyUnfriendlyUnitInObjectRangeCheck u_check(this, this, dist); - Trinity::UnitListSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(targets, u_check); + Trinity::UnitListSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(this, targets, u_check); VisitNearbyObject(dist, searcher); // remove current target @@ -12018,6 +12011,16 @@ Unit* Unit::SelectNearbyTarget(float dist) const return *tcIter; } +bool Unit::hasNegativeAuraWithInterruptFlag(uint32 flag) +{ + for (AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end(); ++iter) + { + if (!iter->second->IsPositive() && iter->second->GetSpellProto()->AuraInterruptFlags & flag) + return true; + } + return false; +} + void Unit::ApplyAttackTimePercentMod( WeaponAttackType att,float val, bool apply ) { float remainingTimePct = (float)m_attackTimer[att] / (GetAttackTime(att) * m_modAttackSpeedPct[att]); @@ -12134,13 +12137,15 @@ uint32 Unit::GetCastingTimeForBonus( SpellEntry const *spellProto, DamageEffectT void Unit::UpdateAuraForGroup(uint8 slot) { + if(slot >= MAX_AURAS) // slot not found, return + return; if(GetTypeId() == TYPEID_PLAYER) { Player* player = (Player*)this; if(player->GetGroup()) { player->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_AURAS); - player->SetAuraUpdateMask(slot); + player->SetAuraUpdateMaskForRaid(slot); } } else if(GetTypeId() == TYPEID_UNIT && ((Creature*)this)->isPet()) @@ -12152,7 +12157,7 @@ void Unit::UpdateAuraForGroup(uint8 slot) if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup()) { ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_AURAS); - pet->SetAuraUpdateMask(slot); + pet->SetAuraUpdateMaskForRaid(slot); } } } @@ -12183,10 +12188,10 @@ float Unit::GetAPMultiplier(WeaponAttackType attType, bool normalized) } } -Aura* Unit::GetDummyAura( uint32 spell_id ) const +AuraEffect* Unit::GetDummyAura( uint32 spell_id ) const { - Unit::AuraList const& mDummy = GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraList::const_iterator itr = mDummy.begin(); itr != mDummy.end(); ++itr) + Unit::AuraEffectList const& mDummy = GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraEffectList::const_iterator itr = mDummy.begin(); itr != mDummy.end(); ++itr) if ((*itr)->GetId() == spell_id) return *itr; @@ -12223,21 +12228,30 @@ void Unit::SetContestedPvP(Player *attackedPlayer) void Unit::AddPetAura(PetAura const* petSpell) { + if(GetTypeId() != TYPEID_PLAYER) + return; + m_petAuras.insert(petSpell); - if(Pet* pet = GetPet()) + if(Pet* pet = ((Player*)this)->GetPet()) pet->CastPetAura(petSpell); } void Unit::RemovePetAura(PetAura const* petSpell) { + if(GetTypeId() != TYPEID_PLAYER) + return; + m_petAuras.erase(petSpell); - if(Pet* pet = GetPet()) + if(Pet* pet = ((Player*)this)->GetPet()) pet->RemoveAurasDueToSpell(petSpell->GetAura(pet->GetEntry())); } Pet* Unit::CreateTamedPetFrom(Creature* creatureTarget,uint32 spell_id) { - Pet* pet = new Pet(HUNTER_PET); + if(GetTypeId()!=TYPEID_PLAYER) + return NULL; + + Pet* pet = new Pet((Player*)this, HUNTER_PET); if(!pet->CreateBaseAtCreature(creatureTarget)) { @@ -12245,43 +12259,38 @@ Pet* Unit::CreateTamedPetFrom(Creature* creatureTarget,uint32 spell_id) return NULL; } - pet->SetOwnerGUID(GetGUID()); pet->SetCreatorGUID(GetGUID()); pet->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, getFaction()); pet->SetUInt32Value(UNIT_CREATED_BY_SPELL, spell_id); - if(!pet->InitStatsForLevel(creatureTarget->getLevel())) + if(GetTypeId()==TYPEID_PLAYER) + pet->SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); + + uint32 level = (creatureTarget->getLevel() < (getLevel() - 5)) ? (getLevel() - 5) : creatureTarget->getLevel(); + + if(!pet->InitStatsForLevel(level)) { - sLog.outError("ERROR: Pet::InitStatsForLevel() failed for creature (Entry: %u)!",creatureTarget->GetEntry()); + sLog.outError("Pet::InitStatsForLevel() failed for creature (Entry: %u)!",creatureTarget->GetEntry()); delete pet; return NULL; } pet->GetCharmInfo()->SetPetNumber(objmgr.GeneratePetNumber(), true); // this enables pet details window (Shift+P) - pet->AIM_Initialize(); pet->InitPetCreateSpells(); + pet->InitTalentForLevel(); pet->SetHealth(pet->GetMaxHealth()); return pet; } -bool Unit::IsTriggeredAtSpellProcEvent(Aura* aura, SpellEntry const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const*& spellProcEvent ) +bool Unit::IsTriggeredAtSpellProcEvent(Unit *pVictim, Aura * aura, SpellEntry const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const *& spellProcEvent ) { SpellEntry const* spellProto = aura->GetSpellProto (); // Get proc Event Entry spellProcEvent = spellmgr.GetSpellProcEvent(spellProto->Id); - // Aura info stored here - Modifier *mod = aura->GetModifier(); - // Skip this auras - if (isNonTriggerAura[mod->m_auraname]) - return false; - // If not trigger by default and spellProcEvent==NULL - skip - if (!isTriggerAura[mod->m_auraname] && spellProcEvent==NULL) - return false; - // Get EventProcFlag uint32 EventProcFlag; if (spellProcEvent && spellProcEvent->procFlags) // if exist get custom spellProcEvent->procFlags @@ -12293,10 +12302,20 @@ bool Unit::IsTriggeredAtSpellProcEvent(Aura* aura, SpellEntry const* procSpell, return false; // Check spellProcEvent data requirements - if(!SpellMgr::IsSpellProcEventCanTriggeredBy(spellProcEvent, EventProcFlag, procSpell, procFlag, procExtra, active)) + if(!spellmgr.IsSpellProcEventCanTriggeredBy(spellProcEvent, EventProcFlag, procSpell, procFlag, procExtra, active)) return false; - // Aura added by spell can`t trogger from self (prevent drop cahres/do triggers) + // In most cases req get honor or XP from kill + if (EventProcFlag & PROC_FLAG_KILL && GetTypeId() == TYPEID_PLAYER) + { + bool allow = ((Player*)this)->isHonorOrXPTarget(pVictim); + // Shadow Word: Death - can trigger from every kill + if (aura->GetId() == 32409) + allow = true; + if (!allow) + return false; + } + // Aura added by spell can`t trogger from self (prevent drop charges/do triggers) // But except periodic triggers (can triggered from self) if(procSpell && procSpell->Id == spellProto->Id && !(spellProto->procFlags&PROC_FLAG_ON_TAKE_PERIODIC)) return false; @@ -12308,13 +12327,13 @@ bool Unit::IsTriggeredAtSpellProcEvent(Aura* aura, SpellEntry const* procSpell, { Item *item = NULL; if(attType == BASE_ATTACK) - item = ((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); + item = ((Player*)this)->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); else if (attType == OFF_ATTACK) - item = ((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); + item = ((Player*)this)->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); else - item = ((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED); + item = ((Player*)this)->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED); - if (!((Player*)this)->IsUseEquipedWeapon(attType==BASE_ATTACK)) + if (((Player*)this)->IsInFeralForm()) return false; if(!item || item->IsBroken() || item->GetProto()->Class != ITEM_CLASS_WEAPON || !((1<<item->GetProto()->SubClass) & spellProto->EquippedItemSubClassMask)) @@ -12323,7 +12342,7 @@ bool Unit::IsTriggeredAtSpellProcEvent(Aura* aura, SpellEntry const* procSpell, else if(spellProto->EquippedItemClass == ITEM_CLASS_ARMOR) { // Check if player is wearing shield - Item *item = ((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); + Item *item = ((Player*)this)->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); if(!item || item->IsBroken() || item->GetProto()->Class != ITEM_CLASS_ARMOR || !((1<<item->GetProto()->SubClass) & spellProto->EquippedItemSubClassMask)) return false; } @@ -12337,60 +12356,58 @@ bool Unit::IsTriggeredAtSpellProcEvent(Aura* aura, SpellEntry const* procSpell, if(!isVictim && spellProcEvent && spellProcEvent->ppmRate != 0) { uint32 WeaponSpeed = GetAttackTime(attType); - chance = GetPPMProcChance(WeaponSpeed, spellProcEvent->ppmRate); + chance = GetPPMProcChance(WeaponSpeed, spellProcEvent->ppmRate, spellProto); } // Apply chance modifer aura if(Player* modOwner = GetSpellModOwner()) + { modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_CHANCE_OF_SUCCESS,chance); + } return roll_chance_f(chance); } -bool Unit::HandleMeandingAuraProc( Aura* triggeredByAura ) +bool Unit::HandleAuraRaidProcFromChargeWithValue( AuraEffect* triggeredByAura ) { // aura can be deleted at casts SpellEntry const* spellProto = triggeredByAura->GetSpellProto(); uint32 effIdx = triggeredByAura->GetEffIndex(); - int32 heal = triggeredByAura->GetModifier()->m_amount; + int32 heal = triggeredByAura->GetAmount(); uint64 caster_guid = triggeredByAura->GetCasterGUID(); + //Currently only Prayer Of Mending + if (!(spellProto->SpellFamilyName==SPELLFAMILY_PRIEST && spellProto->SpellFamilyFlags[1] & 0x20)) + { + sLog.outDebug("Unit::HandleAuraRaidProcFromChargeWithValue, received not handled spell: %u", spellProto->Id); + return false; + } // jumps - int32 jumps = triggeredByAura->m_procCharges-1; + int32 jumps = triggeredByAura->GetParentAura()->GetAuraCharges()-1; // current aura expire - triggeredByAura->m_procCharges = 1; // will removed at next charges decrease + triggeredByAura->GetParentAura()->SetAuraCharges(1); // will removed at next charges decrease // next target selection - if(jumps > 0 && GetTypeId()==TYPEID_PLAYER && IS_PLAYER_GUID(caster_guid)) + if(jumps > 0 && IS_PLAYER_GUID(caster_guid)) { float radius; if (spellProto->EffectRadiusIndex[effIdx]) - radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(spellProto->EffectRadiusIndex[effIdx])); + radius = GetSpellRadiusForTarget(triggeredByAura->GetCaster(), sSpellRadiusStore.LookupEntry(spellProto->EffectRadiusIndex[effIdx])); else - radius = GetSpellMaxRange(sSpellRangeStore.LookupEntry(spellProto->rangeIndex)); + radius = GetSpellMaxRangeForTarget(triggeredByAura->GetCaster() ,sSpellRangeStore.LookupEntry(spellProto->rangeIndex)); if(Player* caster = ((Player*)triggeredByAura->GetCaster())) { caster->ApplySpellMod(spellProto->Id, SPELLMOD_RADIUS, radius,NULL); - if(Player* target = ((Player*)this)->GetNextRandomRaidMember(radius)) + if (Unit* target= GetNextRandomRaidMemberOrPet(radius)) { - // aura will applied from caster, but spell casted from current aura holder - SpellModifier *mod = new SpellModifier; - mod->op = SPELLMOD_CHARGES; - mod->value = jumps-5; // negative - mod->type = SPELLMOD_FLAT; - mod->spellId = spellProto->Id; - mod->effectId = effIdx; - mod->lastAffected = NULL; - mod->mask = spellProto->SpellFamilyFlags; - mod->charges = 0; - - caster->AddSpellMod(mod, true); CastCustomSpell(target,spellProto->Id,&heal,NULL,NULL,true,NULL,triggeredByAura,caster->GetGUID()); - caster->AddSpellMod(mod, false); + if (Aura * aur = target->GetAura(spellProto->Id, caster->GetGUID())) + aur->SetAuraCharges(jumps); - heal = caster->SpellHealingBonus(spellProto, heal, HEAL, this); + //bonus must be applied after aura cast on target + heal = caster->SpellHealingBonus(this, spellProto, heal, HEAL); } } } @@ -12399,29 +12416,64 @@ bool Unit::HandleMeandingAuraProc( Aura* triggeredByAura ) CastCustomSpell(this,33110,&heal,NULL,NULL,true,NULL,NULL,caster_guid); return true; } - -void Unit::RemoveAurasAtChanneledTarget(SpellEntry const* spellInfo, Unit * caster) +bool Unit::HandleAuraRaidProcFromCharge( AuraEffect* triggeredByAura ) { -/* uint64 target_guid = GetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT); - if(target_guid == GetGUID()) - return; + // aura can be deleted at casts + SpellEntry const* spellProto = triggeredByAura->GetSpellProto(); - if(!IS_UNIT_GUID(target_guid)) - return; + uint32 damageSpellId; + switch(spellProto->Id) + { + case 57949: //shiver + damageSpellId=57952; +// animationSpellId=57951; dummy effects for jump spell have unknown use (see also 41637) + break; + case 59978: //shiver + damageSpellId=59979; + break; + case 43593: //Cold Stare + damageSpellId=43594; + break; + default: + sLog.outDebug("Unit::HandleAuraRaidProcFromCharge, received not handled spell: %u", spellProto->Id); + return false; + } - Unit* target = ObjectAccessor::GetUnit(*this, target_guid);*/ - if(!caster) - return; + uint64 caster_guid = triggeredByAura->GetCasterGUID(); + uint32 effIdx = triggeredByAura->GetEffIndex(); + + // jumps + int32 jumps = triggeredByAura->GetParentAura()->GetAuraCharges()-1; - for (AuraMap::iterator iter = GetAuras().begin(); iter != GetAuras().end(); ) + // current aura expire + triggeredByAura->GetParentAura()->SetAuraCharges(1); // will removed at next charges decrease + + // next target selection + if(jumps > 0 && IS_PLAYER_GUID(caster_guid)) { - if (iter->second->GetId() == spellInfo->Id && iter->second->GetCasterGUID() == caster->GetGUID()) - RemoveAura(iter); + float radius; + if (spellProto->EffectRadiusIndex[effIdx]) + radius = GetSpellRadiusForTarget(triggeredByAura->GetCaster(), sSpellRadiusStore.LookupEntry(spellProto->EffectRadiusIndex[effIdx])); else - ++iter; + radius = GetSpellMaxRangeForTarget(triggeredByAura->GetCaster() ,sSpellRangeStore.LookupEntry(spellProto->rangeIndex)); + + if(Player* caster = ((Player*)triggeredByAura->GetCaster())) + { + caster->ApplySpellMod(spellProto->Id, SPELLMOD_RADIUS, radius,NULL); + + if (Unit* target= GetNextRandomRaidMemberOrPet(radius)) + { + CastSpell(target, spellProto, true,NULL,triggeredByAura,caster_guid); + if (Aura * aur = target->GetAura(spellProto->Id, caster->GetGUID())) + aur->SetAuraCharges(jumps); + } + } } -} + CastSpell(this, damageSpellId, true,NULL,triggeredByAura,caster_guid); + + return true; +} /*-----------------------TRINITY-----------------------------*/ void Unit::SetToNotify() @@ -12435,6 +12487,9 @@ void Unit::SetToNotify() void Unit::Kill(Unit *pVictim, bool durabilityLoss) { + // Prevent killing unit twice (and giving reward from kill twice) + if (!pVictim->isAlive()) + return; pVictim->SetHealth(0); // find player: owner of controlled `this` or `this` itself maybe @@ -12447,7 +12502,7 @@ void Unit::Kill(Unit *pVictim, bool durabilityLoss) if(player && player!=pVictim) { if(player->RewardPlayerAndGroupAtKill(pVictim)) - player->ProcDamageAndSpell(pVictim, PROC_FLAG_KILL_AND_GET_XP, PROC_FLAG_KILLED, PROC_EX_NONE, 0); + player->ProcDamageAndSpell(pVictim, PROC_FLAG_KILL, PROC_FLAG_KILLED, PROC_EX_NONE, 0); else player->ProcDamageAndSpell(pVictim, PROC_FLAG_NONE, PROC_FLAG_KILLED,PROC_EX_NONE, 0); } @@ -12456,8 +12511,8 @@ void Unit::Kill(Unit *pVictim, bool durabilityLoss) bool SpiritOfRedemption = false; if(pVictim->GetTypeId()==TYPEID_PLAYER && pVictim->getClass()==CLASS_PRIEST ) { - AuraList const& vDummyAuras = pVictim->GetAurasByType(SPELL_AURA_DUMMY); - for(AuraList::const_iterator itr = vDummyAuras.begin(); itr != vDummyAuras.end(); ++itr) + AuraEffectList const& vDummyAuras = pVictim->GetAurasByType(SPELL_AURA_DUMMY); + for(AuraEffectList::const_iterator itr = vDummyAuras.begin(); itr != vDummyAuras.end(); ++itr) { if((*itr)->GetSpellProto()->SpellIconID==1654) { @@ -12579,6 +12634,15 @@ void Unit::Kill(Unit *pVictim, bool durabilityLoss) bg->HandleKillUnit((Creature*)pVictim, player); } } + + // achievement stuff + if (pVictim->GetTypeId() == TYPEID_PLAYER) + { + if (GetTypeId() == TYPEID_UNIT) + ((Player*)pVictim)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE, GetEntry()); + else if(GetTypeId() == TYPEID_PLAYER && pVictim != this) + ((Player*)pVictim)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER, 1, ((Player*)this)->GetTeam()); + } } void Unit::SetControlled(bool apply, UnitState state) @@ -12648,7 +12712,7 @@ void Unit::SetStunned(bool apply) if(apply) { SetUInt64Value(UNIT_FIELD_TARGET, 0); - SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE); + SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); CastStop(); // Creature specific @@ -12666,7 +12730,7 @@ void Unit::SetStunned(bool apply) { if(isAlive() && getVictim()) SetUInt64Value(UNIT_FIELD_TARGET, getVictim()->GetGUID()); - RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE); + RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); if(!hasUnitState(UNIT_STAT_ROOT)) // prevent allow move if have also root effect { @@ -12719,7 +12783,7 @@ void Unit::SetFeared(bool apply) if(apply) { Unit *caster = NULL; - Unit::AuraList const& fearAuras = GetAurasByType(SPELL_AURA_MOD_FEAR); + Unit::AuraEffectList const& fearAuras = GetAurasByType(SPELL_AURA_MOD_FEAR); if(!fearAuras.empty()) caster = ObjectAccessor::GetUnit(*this, fearAuras.front()->GetCasterGUID()); if(!caster) @@ -12762,7 +12826,7 @@ void Unit::SetCharmedOrPossessedBy(Unit* charmer, bool possess) if(this == charmer) return; - if(isInFlight()) + if(hasUnitState(UNIT_STAT_UNATTACKABLE)) return; if(GetTypeId() == TYPEID_PLAYER && ((Player*)this)->GetTransport()) @@ -12775,21 +12839,26 @@ void Unit::SetCharmedOrPossessedBy(Unit* charmer, bool possess) // Charmer stop charming if(charmer->GetTypeId() == TYPEID_PLAYER) + { ((Player*)charmer)->StopCastingCharm(); + ((Player*)charmer)->StopCastingBindSight(); + } // Charmed stop charming if(GetTypeId() == TYPEID_PLAYER) + { ((Player*)this)->StopCastingCharm(); + ((Player*)this)->StopCastingBindSight(); + } // StopCastingCharm may remove a possessed pet? if(!IsInWorld()) return; // Set charmed - charmer->SetCharm(this); - SetCharmerGUID(charmer->GetGUID()); setFaction(charmer->getFaction()); SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); + charmer->SetCharm(this, true); if(GetTypeId() == TYPEID_UNIT) { @@ -12801,11 +12870,12 @@ void Unit::SetCharmedOrPossessedBy(Unit* charmer, bool possess) { if(((Player*)this)->isAFK()) ((Player*)this)->ToggleAFK(); - ((Player*)this)->SetViewport(GetGUID(), false); + ((Player*)this)->SetClientControl(this, 0); } // Pets already have a properly initialized CharmInfo, don't overwrite it. - if(GetTypeId() == TYPEID_PLAYER || GetTypeId() == TYPEID_UNIT && !((Creature*)this)->isPet()) + if(GetTypeId() == TYPEID_PLAYER || GetTypeId() == TYPEID_UNIT + && !((Creature*)this)->HasSummonMask(SUMMON_MASK_GUARDIAN)) { CharmInfo *charmInfo = InitCharmInfo(); if(possess) @@ -12818,9 +12888,10 @@ void Unit::SetCharmedOrPossessedBy(Unit* charmer, bool possess) if(possess) { addUnitState(UNIT_STAT_POSSESSED); - SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNKNOWN5); - AddPlayerToVision((Player*)charmer); - ((Player*)charmer)->SetViewport(GetGUID(), true); + SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_24); + ((Player*)charmer)->SetClientControl(this, 1); + ((Player*)charmer)->SetMover(this); + ((Player*)charmer)->SetViewpoint(this, true); charmer->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); } // Charm demon @@ -12863,13 +12934,13 @@ void Unit::RemoveCharmedOrPossessedBy(Unit *charmer) CombatStop(); //TODO: CombatStop(true) may cause crash (interrupt spells) getHostilRefManager().deleteReferences(); DeleteThreatList(); - SetCharmerGUID(0); RestoreFaction(); + GetMotionMaster()->InitDefault(); if(possess) { clearUnitState(UNIT_STAT_POSSESSED); - RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNKNOWN5); + RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_24); } if(GetTypeId() == TYPEID_UNIT) @@ -12890,7 +12961,7 @@ void Unit::RemoveCharmedOrPossessedBy(Unit *charmer) } } else - ((Player*)this)->SetViewport(GetGUID(), true); + ((Player*)this)->SetClientControl(this, 1); // If charmer still exists if(!charmer) @@ -12898,11 +12969,12 @@ void Unit::RemoveCharmedOrPossessedBy(Unit *charmer) assert(!possess || charmer->GetTypeId() == TYPEID_PLAYER); - charmer->SetCharm(0); + charmer->SetCharm(this, false); if(possess) { - RemovePlayerFromVision((Player*)charmer); - ((Player*)charmer)->SetViewport(charmer->GetGUID(), true); + ((Player*)charmer)->SetClientControl(charmer, 1); + ((Player*)charmer)->SetMover(charmer); + ((Player*)charmer)->SetViewpoint(this, false); charmer->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); } // restore UNIT_FIELD_BYTES_0 @@ -12924,7 +12996,9 @@ void Unit::RemoveCharmedOrPossessedBy(Unit *charmer) } } - if(GetTypeId() == TYPEID_PLAYER || GetTypeId() == TYPEID_UNIT && !((Creature*)this)->isPet()) + //a guardian should always have charminfo + if(GetTypeId() == TYPEID_PLAYER || GetTypeId() == TYPEID_UNIT + && !((Creature*)this)->HasSummonMask(SUMMON_MASK_GUARDIAN)) { DeleteCharmInfo(); } @@ -12997,17 +13071,30 @@ void Unit::GetRaidMember(std::list<Unit*> &nearMembers, float radius) return; Group *pGroup = owner->GetGroup(); - if(!pGroup) - return; - - for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) + if(pGroup) { - Player* Target = itr->getSource(); + for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player* Target = itr->getSource(); - // IsHostileTo check duel and controlled by enemy - if( Target && Target != this && Target->isAlive() - && IsWithinDistInMap(Target, radius) && !IsHostileTo(Target) ) - nearMembers.push_back(Target); + if( Target && !IsHostileTo(Target) ) + { + if(Target->isAlive() && IsWithinDistInMap(Target, radius) ) + nearMembers.push_back(Target); + + if(Guardian* pet = Target->GetGuardianPet()) + if(pet->isAlive() && IsWithinDistInMap(pet, radius) ) + nearMembers.push_back(pet); + } + } + } + else + { + if(owner->isAlive() && (owner == this || IsWithinDistInMap(owner, radius))) + nearMembers.push_back(owner); + if(Guardian* pet = owner->GetGuardianPet()) + if(pet->isAlive() && (pet == this && IsWithinDistInMap(pet, radius))) + nearMembers.push_back(pet); } } @@ -13032,7 +13119,7 @@ void Unit::GetPartyMember(std::list<Unit*> &TagUnitMap, float radius) if(Target->isAlive() && IsWithinDistInMap(Target, radius) ) TagUnitMap.push_back(Target); - if(Pet* pet = Target->GetPet()) + if(Guardian* pet = Target->GetGuardianPet()) if(pet->isAlive() && IsWithinDistInMap(pet, radius) ) TagUnitMap.push_back(pet); } @@ -13042,12 +13129,38 @@ void Unit::GetPartyMember(std::list<Unit*> &TagUnitMap, float radius) { if(owner->isAlive() && (owner == this || IsWithinDistInMap(owner, radius))) TagUnitMap.push_back(owner); - if(Pet* pet = owner->GetPet()) + if(Guardian* pet = owner->GetGuardianPet()) if(pet->isAlive() && (pet == this && IsWithinDistInMap(pet, radius))) TagUnitMap.push_back(pet); } } +void Unit::HandleAuraEffect(AuraEffect * aureff, bool apply) +{ + if (aureff->GetParentAura()->IsRemoved()) + return; + if (apply) + { + m_modAuras[aureff->GetAuraName()].push_back(aureff); + aureff->ApplyModifier(true, true); + } + else + { + // remove from list before mods removing (prevent cyclic calls, mods added before including to aura list - use reverse order) + m_modAuras[aureff->GetAuraName()].remove(aureff); + aureff->ApplyModifier(false, true); + Unit * caster = aureff->GetParentAura()->GetCaster(); + if(caster && aureff->IsPersistent()) + { + DynamicObject *dynObj = caster->GetDynObject(aureff->GetId(), aureff->GetEffIndex()); + if (dynObj) + dynObj->RemoveAffected(this); + } + // Remove all triggered by aura spells vs unlimited duration + aureff->CleanupTriggeredSpells(); + } +} + void Unit::AddAura(uint32 spellId, Unit* target) { if(!target || !target->isAlive()) @@ -13060,24 +13173,193 @@ void Unit::AddAura(uint32 spellId, Unit* target) if (target->IsImmunedToSpell(spellInfo)) return; + uint8 eff_mask=0; + for(uint32 i = 0; i < 3; ++i) { - if(spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA) + if(spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA || IsAreaAuraEffect(spellInfo->Effect[i])) { - if(target->IsImmunedToSpellEffect(spellInfo->Effect[i], spellInfo->EffectMechanic[i])) + if(target->IsImmunedToSpellEffect(spellInfo, i)) continue; - - /*if(spellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_CASTER) - { - Aura *Aur = CreateAura(spellInfo, i, NULL, this, this); - AddAura(Aur); - } - else*/ - { - Aura *Aur = CreateAura(spellInfo, i, NULL, target, this); - target->AddAura(Aur); - } + eff_mask|=1<<i; } } + + if (!eff_mask) + return; + + Aura *Aur = new Aura(spellInfo, eff_mask, NULL, target, this); + target->AddAura(Aur); +} + +Aura * Unit::AddAuraEffect(uint32 spellId, uint8 effIndex, Unit* caster) +{ + SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId); + if(!spellInfo || !caster) + return NULL; + + // can't do that for passive auras - they stack from same caster so there is no way to get exact aura which should get effect + //assert (!IsPassiveSpell(spellInfo)); + + Aura * aur = GetAura(spellId, caster->GetGUID()); + + if (aur) + { + AuraEffect *aurEffect = CreateAuraEffect(aur, effIndex, NULL, caster); + if (!aurEffect) + return aur; + if (!aur->SetPartAura(aurEffect, effIndex)) + delete aurEffect; + } + else + { + aur = new Aura(spellInfo, 1<<effIndex, NULL, this ,caster); + AddAura(aur); + } + return aur; } +// Melee based spells can be miss, parry or dodge on this step +// Crit or block - determined on damage calculation phase! (and can be both in some time) +float Unit::MeleeSpellMissChance(const Unit *pVictim, WeaponAttackType attType, int32 skillDiff, uint32 spellId) const +{ + // Calculate hit chance (more correct for chance mod) + int32 HitChance; + + // PvP - PvE melee chances + /*int32 lchance = pVictim->GetTypeId() == TYPEID_PLAYER ? 5 : 7; + int32 leveldif = pVictim->getLevelForTarget(this) - getLevelForTarget(pVictim); + if(leveldif < 3) + HitChance = 95 - leveldif; + else + HitChance = 93 - (leveldif - 2) * lchance;*/ + if (spellId || attType == RANGED_ATTACK || !haveOffhandWeapon()) + HitChance = 95.0f; + else + HitChance = 76.0f; + + // Hit chance depends from victim auras + if(attType == RANGED_ATTACK) + HitChance += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE); + else + HitChance += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE); + + // Spellmod from SPELLMOD_RESIST_MISS_CHANCE + if(spellId) + { + if(Player *modOwner = GetSpellModOwner()) + modOwner->ApplySpellMod(spellId, SPELLMOD_RESIST_MISS_CHANCE, HitChance); + } + + // Miss = 100 - hit + float miss_chance= 100.0f - HitChance; + + // Bonuses from attacker aura and ratings + if (attType == RANGED_ATTACK) + miss_chance -= m_modRangedHitChance; + else + miss_chance -= m_modMeleeHitChance; + + // bonus from skills is 0.04% + //miss_chance -= skillDiff * 0.04f; + int32 diff = -skillDiff; + if(pVictim->GetTypeId()==TYPEID_PLAYER) + miss_chance += diff > 0 ? diff * 0.04 : diff * 0.02; + else + miss_chance += diff > 10 ? 2 + (diff - 10) * 0.4 : diff * 0.1; + + // Limit miss chance from 0 to 60% + if (miss_chance < 0.0f) + return 0.0f; + if (miss_chance > 60.0f) + return 60.0f; + return miss_chance; +} + +void Unit::SetPhaseMask(uint32 newPhaseMask, bool update) +{ + WorldObject::SetPhaseMask(newPhaseMask,update); + + if(!IsInWorld()) + return; + + for(ControlList::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr) + if((*itr)->GetOwnerGUID() == GetGUID()) + (*itr)->SetPhaseMask(newPhaseMask,true); + + for(int8 i = 0; i < MAX_SUMMON_SLOT; ++i) + if(m_SummonSlot[i]) + if(Creature *summon = ObjectAccessor::GetCreature(*this, m_SummonSlot[i])) + summon->SetPhaseMask(newPhaseMask,true); +} + +void Unit::KnockbackFrom(float x, float y, float speedXY, float speedZ) +{ + if(GetTypeId() == TYPEID_UNIT) + { + GetMotionMaster()->MoveKnockbackFrom(x, y, speedXY, speedZ); + } + else + { + float vcos, vsin; + GetSinCos(x, y, vsin, vcos); + + WorldPacket data(SMSG_MOVE_KNOCK_BACK, (8+4+4+4+4+4)); + data.append(GetPackGUID()); + data << uint32(0); // Sequence + data << float(vcos); // x direction + data << float(vsin); // y direction + data << float(speedXY); // Horizontal speed + data << float(-speedZ); // Z Movement speed (vertical) + + ((Player*)this)->GetSession()->SendPacket(&data); + } +} + +void Unit::JumpTo(float speedXY, float speedZ, bool forward) +{ + float angle = forward ? 0 : M_PI; + if(GetTypeId() == TYPEID_UNIT) + { + GetMotionMaster()->MoveJumpTo(angle, speedXY, speedZ); + } + else + { + float vcos = cos(angle+GetOrientation()); + float vsin = sin(angle+GetOrientation()); + + WorldPacket data(SMSG_MOVE_KNOCK_BACK, (8+4+4+4+4+4)); + data.append(GetPackGUID()); + data << uint32(0); // Sequence + data << float(vcos); // x direction + data << float(vsin); // y direction + data << float(speedXY); // Horizontal speed + data << float(-speedZ); // Z Movement speed (vertical) + + ((Player*)this)->GetSession()->SendPacket(&data); + } +} + +void Unit::JumpTo(WorldObject *obj, float speedZ) +{ + float x, y, z; + obj->GetContactPoint(this, x, y, z); + float speedXY = GetExactDistance2d(x, y) * 10.0f / speedZ; + GetMotionMaster()->MoveJump(x, y, z, speedXY, speedZ); +} + +void Unit::NearTeleportTo( float x, float y, float z, float orientation, bool casting /*= false*/ ) +{ + if(GetTypeId() == TYPEID_PLAYER) + ((Player*)this)->TeleportTo(GetMapId(), x, y, z, orientation, TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (casting ? TELE_TO_SPELL : 0)); + else + { + GetMap()->CreatureRelocation((Creature*)this, x, y, z, orientation); + + WorldPacket data; + // Work strange for many spells: triggered active mover set for targeted player to creature + //BuildTeleportAckMsg(&data, x, y, z, orientation); + BuildHeartBeatMsg(&data); + SendMessageToSet(&data, false); + } +} diff --git a/src/game/Unit.h b/src/game/Unit.h index 795d2982c07..4559bd97bba 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,7 +24,6 @@ #include "Common.h" #include "Object.h" #include "Opcodes.h" -#include "Mthread.h" #include "SpellAuraDefines.h" #include "UpdateFields.h" #include "SharedDefines.h" @@ -34,7 +33,7 @@ #include "FollowerRefManager.h" #include "Utilities/EventProcessor.h" #include "MotionMaster.h" -#include "Database/DBCStructure.h" +#include "DBCStructure.h" #include <list> #define WORLD_TRIGGER 12999 @@ -45,7 +44,8 @@ enum SpellInterruptFlags SPELL_INTERRUPT_FLAG_PUSH_BACK = 0x02, // push back SPELL_INTERRUPT_FLAG_INTERRUPT = 0x04, // interrupt SPELL_INTERRUPT_FLAG_AUTOATTACK = 0x08, // no - SPELL_INTERRUPT_FLAG_DAMAGE = 0x10 // _complete_ interrupt on direct damage? + SPELL_INTERRUPT_FLAG_ABORT_ON_DMG = 0x10, // _complete_ interrupt on direct damage + //SPELL_INTERRUPT_UNK = 0x20 // unk, 564 of 727 spells having this spell start with "Glyph" }; enum SpellChannelInterruptFlags @@ -59,9 +59,9 @@ enum SpellChannelInterruptFlags enum SpellAuraInterruptFlags { - AURA_INTERRUPT_FLAG_HITBYSPELL = 0x00000001, // 0 removed when getting hit by a negative spell? + AURA_INTERRUPT_FLAG_ANY_CAST = 0x00000001, // 0 removed by any cast AURA_INTERRUPT_FLAG_DAMAGE = 0x00000002, // 1 removed by any damage - AURA_INTERRUPT_FLAG_CC = 0x00000004, // 2 crowd control + AURA_INTERRUPT_FLAG_HITBYSPELL = 0x00000004, // 2 removed when getting hit by a negative spell? aoe damage? AURA_INTERRUPT_FLAG_MOVE = 0x00000008, // 3 removed by any movement AURA_INTERRUPT_FLAG_TURNING = 0x00000010, // 4 removed by any turning AURA_INTERRUPT_FLAG_JUMP = 0x00000020, // 5 removed by entering combat @@ -71,15 +71,15 @@ enum SpellAuraInterruptFlags AURA_INTERRUPT_FLAG_NOT_SHEATHED = 0x00000200, // 9 removed by unsheathing AURA_INTERRUPT_FLAG_TALK = 0x00000400, // 10 talk to npc / loot? action on creature AURA_INTERRUPT_FLAG_USE = 0x00000800, // 11 mine/use/open action on gameobject - AURA_INTERRUPT_FLAG_ATTACK = 0x00001000, // 12 removed by attacking - AURA_INTERRUPT_FLAG_CAST = 0x00002000, // 13 ??? + AURA_INTERRUPT_FLAG_ATTACK = 0x00001000, // 12 removed by attacking (negative spell cast?) + AURA_INTERRUPT_FLAG_CAST = 0x00002000, // 13 cast stealth breaking spell AURA_INTERRUPT_FLAG_UNK14 = 0x00004000, // 14 AURA_INTERRUPT_FLAG_TRANSFORM = 0x00008000, // 15 removed by transform? AURA_INTERRUPT_FLAG_UNK16 = 0x00010000, // 16 AURA_INTERRUPT_FLAG_MOUNT = 0x00020000, // 17 misdirect, aspect, swim speed AURA_INTERRUPT_FLAG_NOT_SEATED = 0x00040000, // 18 removed by standing up AURA_INTERRUPT_FLAG_CHANGE_MAP = 0x00080000, // 19 leaving map/getting teleported - AURA_INTERRUPT_FLAG_UNATTACKABLE = 0x00100000, // 20 invulnerable or stealth + AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION = 0x00100000, // 20 removed by auras that make you invulnerable, or make other to loose selection on you AURA_INTERRUPT_FLAG_UNK21 = 0x00200000, // 21 AURA_INTERRUPT_FLAG_TELEPORTED = 0x00400000, // 22 AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT = 0x00800000, // 23 removed by entering pvp combat @@ -101,7 +101,7 @@ enum SpellModOp SPELLMOD_CASTING_TIME = 10, SPELLMOD_COOLDOWN = 11, SPELLMOD_EFFECT2 = 12, - // spellmod 13 unused + SPELLMOD_IGNORE_ARMOR = 13, SPELLMOD_COST = 14, SPELLMOD_CRIT_DAMAGE_BONUS = 15, SPELLMOD_RESIST_MISS_CHANCE = 16, @@ -109,13 +109,16 @@ enum SpellModOp SPELLMOD_CHANCE_OF_SUCCESS = 18, SPELLMOD_ACTIVATION_TIME = 19, SPELLMOD_EFFECT_PAST_FIRST = 20, - SPELLMOD_CASTING_TIME_OLD = 21, + SPELLMOD_GLOBAL_COOLDOWN = 21, //TODO: GCD is not checked by server currently SPELLMOD_DOT = 22, SPELLMOD_EFFECT3 = 23, SPELLMOD_SPELL_BONUS_DAMAGE = 24, - // spellmod 25, 26 unused + // spellmod 25 + SPELLMOD_PROC_PER_MINUTE = 26, SPELLMOD_MULTIPLE_VALUE = 27, - SPELLMOD_RESIST_DISPEL_CHANCE = 28 + SPELLMOD_RESIST_DISPEL_CHANCE = 28, + SPELLMOD_CRIT_DAMAGE_BONUS_2 = 29, //one not used spell + SPELLMOD_SPELL_COST_REFUND_ON_FAIL = 30 }; #define MAX_SPELLMOD 32 @@ -129,6 +132,35 @@ enum SpellFacingFlags #define BASE_MAXDAMAGE 2.0f #define BASE_ATTACK_TIME 2000 +// byte value (UNIT_FIELD_BYTES_1,0) +enum UnitStandStateType +{ + UNIT_STAND_STATE_STAND = 0, + UNIT_STAND_STATE_SIT = 1, + UNIT_STAND_STATE_SIT_CHAIR = 2, + UNIT_STAND_STATE_SLEEP = 3, + UNIT_STAND_STATE_SIT_LOW_CHAIR = 4, + UNIT_STAND_STATE_SIT_MEDIUM_CHAIR = 5, + UNIT_STAND_STATE_SIT_HIGH_CHAIR = 6, + UNIT_STAND_STATE_DEAD = 7, + UNIT_STAND_STATE_KNEEL = 8 +}; + +// byte flag value (UNIT_FIELD_BYTES_1,2) +enum UnitStandFlags +{ + UNIT_STAND_FLAGS_CREEP = 0x02, + UNIT_STAND_FLAGS_ALL = 0xFF +}; + +// byte flags value (UNIT_FIELD_BYTES_1,3) +enum UnitBytes1_Flags +{ + UNIT_BYTE1_FLAG_ALWAYS_STAND = 0x01, + UNIT_BYTE1_FLAG_UNTRACKABLE = 0x04, + UNIT_BYTE1_FLAG_ALL = 0xFF +}; + // high byte (3 from 0..3) of UNIT_FIELD_BYTES_2 enum ShapeshiftForm { @@ -149,6 +181,7 @@ enum ShapeshiftForm FORM_BERSERKERSTANCE = 0x13, FORM_TEST = 0x14, FORM_ZOMBIE = 0x15, + FORM_METAMORPHOSIS = 0x16, FORM_FLIGHT_EPIC = 0x1B, FORM_SHADOW = 0x1C, FORM_FLIGHT = 0x1D, @@ -168,14 +201,14 @@ enum SheathState // byte (1 from 0..3) of UNIT_FIELD_BYTES_2 enum UnitBytes2_Flags { - UNIT_BYTE2_FLAG_UNK0 = 0x01, - UNIT_BYTE2_FLAG_UNK1 = 0x02, - UNIT_BYTE2_FLAG_UNK2 = 0x04, + UNIT_BYTE2_FLAG_PVP = 0x01, + UNIT_BYTE2_FLAG_UNK1 = 0x02, + UNIT_BYTE2_FLAG_FFA_PVP = 0x04, UNIT_BYTE2_FLAG_SANCTUARY = 0x08, - UNIT_BYTE2_FLAG_AURAS = 0x10, // show possitive auras as positive, and allow its dispel - UNIT_BYTE2_FLAG_UNK5 = 0x20, - UNIT_BYTE2_FLAG_UNK6 = 0x40, - UNIT_BYTE2_FLAG_UNK7 = 0x80 + UNIT_BYTE2_FLAG_UNK4 = 0x10, + UNIT_BYTE2_FLAG_UNK5 = 0x20, + UNIT_BYTE2_FLAG_UNK6 = 0x40, + UNIT_BYTE2_FLAG_UNK7 = 0x80 }; // byte (2 from 0..3) of UNIT_FIELD_BYTES_2 @@ -185,7 +218,11 @@ enum UnitRename UNIT_RENAME_ALLOWED = 0x03 }; -#define CREATURE_MAX_SPELLS 4 +#define CREATURE_MAX_SPELLS 8 +#define MAX_SPELL_CHARM 4 +#define MAX_SPELL_VEHICLE 6 +#define MAX_SPELL_POSSESS 8 +#define MAX_SPELL_CONTROL_BAR 10 enum Swing { @@ -213,16 +250,27 @@ enum HitInfo HITINFO_UNK1 = 0x00000001, // req correct packet structure HITINFO_NORMALSWING2 = 0x00000002, HITINFO_LEFTSWING = 0x00000004, + HITINFO_UNK2 = 0x00000008, HITINFO_MISS = 0x00000010, - HITINFO_ABSORB = 0x00000020, // plays absorb sound - HITINFO_RESIST = 0x00000040, // resisted at least some damage - HITINFO_CRITICALHIT = 0x00000080, - HITINFO_UNK2 = 0x00000100, // wotlk? - HITINFO_UNK3 = 0x00002000, // wotlk? - HITINFO_GLANCING = 0x00004000, - HITINFO_CRUSHING = 0x00008000, - HITINFO_NOACTION = 0x00010000, - HITINFO_SWINGNOHITSOUND = 0x00080000 + HITINFO_ABSORB = 0x00000020, // absorbed damage + HITINFO_ABSORB2 = 0x00000040, // absorbed damage + HITINFO_RESIST = 0x00000080, // resisted atleast some damage + HITINFO_RESIST2 = 0x00000100, // resisted atleast some damage + HITINFO_CRITICALHIT = 0x00000200, // critical hit + // 0x00000400 + // 0x00000800 + // 0x00001000 + HITINFO_BLOCK = 0x00002000, // blocked damage + // 0x00004000 + // 0x00008000 + HITINFO_GLANCING = 0x00010000, + HITINFO_CRUSHING = 0x00020000, + HITINFO_NOACTION = 0x00040000, // guessed + // 0x00080000 + // 0x00100000 + HITINFO_SWINGNOHITSOUND = 0x00200000, // guessed + // 0x00400000 + HITINFO_UNK3 = 0x00800000 }; //i would like to remove this: (it is defined in item.h @@ -233,11 +281,11 @@ enum InventorySlot }; struct FactionTemplateEntry; -struct Modifier; struct SpellEntry; struct SpellEntryExt; class Aura; +class AuraEffect; class Creature; class Spell; class DynamicObject; @@ -246,6 +294,8 @@ class Item; class Pet; class Path; class PetAura; +class Guardian; +class UnitAI; struct SpellImmune { @@ -280,9 +330,10 @@ enum DamageTypeToSchool enum AuraRemoveMode { AURA_REMOVE_BY_DEFAULT, - AURA_REMOVE_BY_STACK, // at replace by semillar aura + AURA_REMOVE_BY_STACK, // change stack, single aura remove, AURA_REMOVE_BY_CANCEL, - AURA_REMOVE_BY_DISPEL, + AURA_REMOVE_BY_ENEMY_SPELL, // dispel and absorb aura destroy + AURA_REMOVE_BY_EXPIRE, // dispel and absorb aura destroy AURA_REMOVE_BY_DEATH }; @@ -294,11 +345,13 @@ enum UnitMods UNIT_MOD_STAT_INTELLECT, UNIT_MOD_STAT_SPIRIT, UNIT_MOD_HEALTH, - UNIT_MOD_MANA, // UNIT_MOD_MANA..UNIT_MOD_HAPPINESS must be in existed order, it's accessed by index values of Powers enum. + UNIT_MOD_MANA, // UNIT_MOD_MANA..UNIT_MOD_RUNIC_POWER must be in existed order, it's accessed by index values of Powers enum. UNIT_MOD_RAGE, UNIT_MOD_FOCUS, UNIT_MOD_ENERGY, UNIT_MOD_HAPPINESS, + UNIT_MOD_RUNE, + UNIT_MOD_RUNIC_POWER, UNIT_MOD_ARMOR, // UNIT_MOD_ARMOR..UNIT_MOD_RESISTANCE_ARCANE must be in existed order, it's accessed by index values of SpellSchools enum. UNIT_MOD_RESISTANCE_HOLY, UNIT_MOD_RESISTANCE_FIRE, @@ -318,7 +371,7 @@ enum UnitMods UNIT_MOD_RESISTANCE_START = UNIT_MOD_ARMOR, UNIT_MOD_RESISTANCE_END = UNIT_MOD_RESISTANCE_ARCANE + 1, UNIT_MOD_POWER_START = UNIT_MOD_MANA, - UNIT_MOD_POWER_END = UNIT_MOD_HAPPINESS + 1 + UNIT_MOD_POWER_END = UNIT_MOD_RUNIC_POWER + 1 }; enum BaseModGroup @@ -368,8 +421,12 @@ enum UnitState UNIT_STAT_CASTING = 0x00008000, UNIT_STAT_POSSESSED = 0x00010000, UNIT_STAT_CHARGING = 0x00020000, + UNIT_STAT_JUMPING = 0x00040000, + UNIT_STAT_ONVEHICLE = 0x00080000, + UNIT_STAT_UNATTACKABLE = (UNIT_STAT_IN_FLIGHT | UNIT_STAT_ONVEHICLE), UNIT_STAT_MOVING = (UNIT_STAT_ROAMING | UNIT_STAT_CHASE), - UNIT_STAT_LOST_CONTROL = (UNIT_STAT_CONFUSED | UNIT_STAT_STUNNED | UNIT_STAT_FLEEING | UNIT_STAT_CHARGING), + UNIT_STAT_CONTROLLED = (UNIT_STAT_CONFUSED | UNIT_STAT_STUNNED | UNIT_STAT_FLEEING), + UNIT_STAT_LOST_CONTROL = (UNIT_STAT_CONTROLLED | UNIT_STAT_JUMPING | UNIT_STAT_CHARGING), UNIT_STAT_SIGHTLESS = (UNIT_STAT_LOST_CONTROL | UNIT_STAT_CHASE), UNIT_STAT_CANNOT_AUTOATTACK = (UNIT_STAT_LOST_CONTROL | UNIT_STAT_CASTING), UNIT_STAT_ALL_STATE = 0xffffffff //(UNIT_STAT_STOPPED | UNIT_STAT_MOVING | UNIT_STAT_IN_COMBAT | UNIT_STAT_IN_FLIGHT) @@ -385,23 +442,21 @@ enum UnitMoveType MOVE_TURN_RATE = 5, MOVE_FLIGHT = 6, MOVE_FLIGHT_BACK = 7, + MOVE_PITCH_RATE = 8 }; -#define MAX_MOVE_TYPE 8 +#define MAX_MOVE_TYPE 9 extern float baseMoveSpeed[MAX_MOVE_TYPE]; -// assume it is 25 yard per 0.6 second -#define SPEED_CHARGE 42.0f enum WeaponAttackType { BASE_ATTACK = 0, OFF_ATTACK = 1, - RANGED_ATTACK = 2 + RANGED_ATTACK = 2, + MAX_ATTACK }; -#define MAX_ATTACK 3 - enum CombatRating { CR_WEAPON_SKILL = 0, @@ -427,10 +482,11 @@ enum CombatRating CR_WEAPON_SKILL_MAINHAND = 20, CR_WEAPON_SKILL_OFFHAND = 21, CR_WEAPON_SKILL_RANGED = 22, - CR_EXPERTISE = 23 + CR_EXPERTISE = 23, + CR_ARMOR_PENETRATION = 24 }; -#define MAX_COMBAT_RATING 24 +#define MAX_COMBAT_RATING 25 enum DamageEffectType { @@ -455,45 +511,50 @@ enum UnitVisibility // Value masks for UNIT_FIELD_FLAGS enum UnitFlags { - UNIT_FLAG_UNKNOWN7 = 0x00000001, + UNIT_FLAG_UNK_0 = 0x00000001, UNIT_FLAG_NON_ATTACKABLE = 0x00000002, // not attackable UNIT_FLAG_DISABLE_MOVE = 0x00000004, UNIT_FLAG_PVP_ATTACKABLE = 0x00000008, // allow apply pvp rules to attackable state in addition to faction dependent state UNIT_FLAG_RENAME = 0x00000010, UNIT_FLAG_PREPARATION = 0x00000020, // don't take reagents for spells with SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP - UNIT_FLAG_UNKNOWN9 = 0x00000040, + UNIT_FLAG_UNK_6 = 0x00000040, UNIT_FLAG_NOT_ATTACKABLE_1 = 0x00000080, // ?? (UNIT_FLAG_PVP_ATTACKABLE | UNIT_FLAG_NOT_ATTACKABLE_1) is NON_PVP_ATTACKABLE UNIT_FLAG_NOT_ATTACKABLE_2 = 0x00000100, // 2.0.8 - UNIT_FLAG_UNKNOWN11 = 0x00000200, + UNIT_FLAG_UNK_9 = 0x00000200, // 3.0.3 - makes you unable to attack everything UNIT_FLAG_LOOTING = 0x00000400, // loot animation UNIT_FLAG_PET_IN_COMBAT = 0x00000800, // in combat?, 2.0.8 - UNIT_FLAG_PVP = 0x00001000, + UNIT_FLAG_PVP = 0x00001000, // changed in 3.0.3 UNIT_FLAG_SILENCED = 0x00002000, // silenced, 2.1.1 - UNIT_FLAG_UNKNOWN4 = 0x00004000, // 2.0.8 - UNIT_FLAG_UNKNOWN13 = 0x00008000, - UNIT_FLAG_UNKNOWN14 = 0x00010000, - UNIT_FLAG_PACIFIED = 0x00020000, - UNIT_FLAG_DISABLE_ROTATE = 0x00040000, // stunned, 2.1.1 + UNIT_FLAG_UNK_14 = 0x00004000, // 2.0.8 + UNIT_FLAG_UNK_15 = 0x00008000, + UNIT_FLAG_UNK_16 = 0x00010000, + UNIT_FLAG_PACIFIED = 0x00020000, // 3.0.3 ok + UNIT_FLAG_STUNNED = 0x00040000, // 3.0.3 ok UNIT_FLAG_IN_COMBAT = 0x00080000, UNIT_FLAG_TAXI_FLIGHT = 0x00100000, // disable casting at client side spell not allowed by taxi flight (mounted?), probably used with 0x4 flag - UNIT_FLAG_DISARMED = 0x00200000, // disable melee spells casting..., "Required melee weapon" added to melee spells tooltip. + 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_UNKNOWN5 = 0x01000000, // used in spell Eyes of the Beast for pet... + UNIT_FLAG_UNK_24 = 0x01000000, // used in spell Eyes of the Beast for pet... UNIT_FLAG_NOT_SELECTABLE = 0x02000000, UNIT_FLAG_SKINNABLE = 0x04000000, UNIT_FLAG_MOUNT = 0x08000000, - UNIT_FLAG_UNKNOWN17 = 0x10000000, - UNIT_FLAG_UNKNOWN6 = 0x20000000, // used in Feing Death spell - UNIT_FLAG_SHEATHE = 0x40000000 + UNIT_FLAG_UNK_28 = 0x10000000, + UNIT_FLAG_UNK_29 = 0x20000000, // used in Feing Death spell + UNIT_FLAG_SHEATHE = 0x40000000, + UNIT_FLAG_UNK_31 = 0x80000000 }; // Value masks for UNIT_FIELD_FLAGS_2 enum UnitFlags2 { - UNIT_FLAG2_FEIGN_DEATH = 0x00000001, - UNIT_FLAG2_COMPREHEND_LANG= 0x00000008, - UNIT_FLAG2_FORCE_MOVE = 0x00000040 + UNIT_FLAG2_FEIGN_DEATH = 0x00000001, + UNIT_FLAG2_UNK1 = 0x00000002, // Hide unit model (show only player equip) + UNIT_FLAG2_COMPREHEND_LANG = 0x00000008, + UNIT_FLAG2_FORCE_MOVE = 0x00000040, + UNIT_FLAG2_DISARM_OFFHAND = 0x00000080, + UNIT_FLAG2_DISARM_RANGED = 0x00000400, //this does not disable ranged weapon display (maybe additional flag needed?) + UNIT_FLAG2_REGENERATE_POWER = 0x00000800 }; /// Non Player Character flags @@ -581,7 +642,9 @@ enum DiminishingLevels struct DiminishingReturn { - DiminishingReturn(DiminishingGroup group, uint32 t, uint32 count) : DRGroup(group), hitTime(t), hitCount(count), stack(0) {} + DiminishingReturn(DiminishingGroup group, uint32 t, uint32 count) + : DRGroup(group), stack(0), hitTime(t), hitCount(count) + {} DiminishingGroup DRGroup:16; uint16 stack:16; @@ -592,8 +655,9 @@ struct DiminishingReturn enum MeleeHitOutcome { MELEE_HIT_EVADE, MELEE_HIT_MISS, MELEE_HIT_DODGE, MELEE_HIT_BLOCK, MELEE_HIT_PARRY, - MELEE_HIT_GLANCING, MELEE_HIT_CRIT, MELEE_HIT_CRUSHING, MELEE_HIT_NORMAL, MELEE_HIT_BLOCK_CRIT + MELEE_HIT_GLANCING, MELEE_HIT_CRIT, MELEE_HIT_CRUSHING, MELEE_HIT_NORMAL }; + struct CleanDamage { CleanDamage(uint32 _damage, WeaponAttackType _attackType, MeleeHitOutcome _hitOutCome) : @@ -628,9 +692,11 @@ struct CalcDamageInfo // Spell damage info structure based on structure sending in SMSG_SPELLNONMELEEDAMAGELOG opcode struct SpellNonMeleeDamage{ - SpellNonMeleeDamage(Unit *_attacker, Unit *_target, uint32 _SpellID, uint32 _schoolMask) : - attacker(_attacker), target(_target), SpellID(_SpellID), damage(0), schoolMask(_schoolMask), - absorb(0), resist(0), phusicalLog(false), unused(false), blocked(0), HitInfo(0), cleanDamage(0) {} + SpellNonMeleeDamage(Unit *_attacker, Unit *_target, uint32 _SpellID, uint32 _schoolMask) + : target(_target), attacker(_attacker), SpellID(_SpellID), damage(0), schoolMask(_schoolMask), + absorb(0), resist(0), phusicalLog(false), unused(false), blocked(0), HitInfo(0), cleanDamage(0) + {} + Unit *target; Unit *attacker; uint32 SpellID; @@ -650,8 +716,20 @@ uint32 createProcExtendMask(SpellNonMeleeDamage *damageInfo, SpellMissInfo missC struct UnitActionBarEntry { - uint32 Type; - uint32 SpellOrAction; + UnitActionBarEntry() : Raw(0) {} + + union + { + struct + { + uint16 SpellOrAction; + uint16 Type; + }; + struct + { + uint32 Raw; + }; + }; }; #define MAX_DECLINED_NAME_CASES 5 @@ -673,13 +751,12 @@ enum CurrentSpellTypes enum ActiveStates { - ACT_ENABLED = 0xC100, - ACT_DISABLED = 0x8100, - ACT_COMMAND = 0x0700, - ACT_REACTION = 0x0600, - ACT_CAST = 0x0100, - ACT_PASSIVE = 0x0000, - ACT_DECIDE = 0x0001 + ACT_PASSIVE = 0x0100, // 0x0100 - passive + ACT_DISABLED = 0x8100, // 0x8000 - castable + ACT_ENABLED = 0xC100, // 0x4000 | 0x8000 - auto cast + castable + ACT_COMMAND = 0x0700, // 0x0100 | 0x0200 | 0x0400 + ACT_REACTION = 0x0600, // 0x0200 | 0x0400 + ACT_DECIDE = 0x0001 // what is it? }; enum ReactStates @@ -748,15 +825,18 @@ struct TRINITY_DLL_SPEC CharmInfo enum ReactiveType { - REACTIVE_DEFENSE = 1, - REACTIVE_HUNTER_PARRY = 2, - REACTIVE_CRIT = 3, - REACTIVE_HUNTER_CRIT = 4, - REACTIVE_OVERPOWER = 5 + REACTIVE_DEFENSE = 0, + REACTIVE_HUNTER_PARRY = 1, + REACTIVE_OVERPOWER = 2 }; -#define MAX_REACTIVE 6 -#define MAX_TOTEM 4 +#define MAX_REACTIVE 3 +#define SUMMON_SLOT_PET 0 +#define SUMMON_SLOT_TOTEM 1 +#define MAX_TOTEM_SLOT 5 +#define SUMMON_SLOT_MINIPET 5 +#define SUMMON_SLOT_QUEST 6 +#define MAX_SUMMON_SLOT 7 // delay time next attack to prevent client attack animation problems #define ATTACK_DISPLAY_DELAY 200 @@ -767,13 +847,16 @@ class TRINITY_DLL_SPEC Unit : public WorldObject { public: typedef std::set<Unit*> AttackerSet; + typedef std::set<Unit*> ControlList; typedef std::pair<uint32, uint8> spellEffectPair; - typedef std::multimap< spellEffectPair, Aura*> AuraMap; + typedef std::multimap<uint32, Aura*> AuraMap; + typedef std::list<AuraEffect *> AuraEffectList; typedef std::list<Aura *> AuraList; typedef std::list<DiminishingReturn> Diminishing; - typedef std::set<AuraType> AuraTypeSet; typedef std::set<uint32> ComboPointHolderSet; + typedef std::map<uint8, Aura*> VisibleAuraMap; + virtual ~Unit ( ); void AddToWorld(); @@ -787,6 +870,11 @@ class TRINITY_DLL_SPEC Unit : public WorldObject void ApplyDiminishingAura(DiminishingGroup group, bool apply); void ClearDiminishings() { m_Diminishing.clear(); } + //target dependent range checks + uint32 GetSpellMaxRangeForTarget(Unit* target,const SpellRangeEntry * rangeEntry); + uint32 GetSpellMinRangeForTarget(Unit* target,const SpellRangeEntry * rangeEntry); + uint32 GetSpellRadiusForTarget(Unit* target,const SpellRadiusEntry * radiusEntry); + virtual void Update( uint32 time ); void setAttackTimer(WeaponAttackType type, uint32 time) { m_attackTimer[type] = time; } @@ -806,15 +894,11 @@ class TRINITY_DLL_SPEC Unit : public WorldObject void _addAttacker(Unit *pAttacker) // must be called only from Unit::Attack(Unit*) { - AttackerSet::iterator itr = m_attackers.find(pAttacker); - if(itr == m_attackers.end()) - m_attackers.insert(pAttacker); + m_attackers.insert(pAttacker); } void _removeAttacker(Unit *pAttacker) // must be called only from Unit::AttackStop() { - AttackerSet::iterator itr = m_attackers.find(pAttacker); - if(itr != m_attackers.end()) - m_attackers.erase(itr); + m_attackers.erase(pAttacker); } Unit * getAttackerForHelper() // If someone wants to help, who to give them { @@ -836,6 +920,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject void CombatStop(bool cast = false); void CombatStopWithPets(bool cast = false); Unit* SelectNearbyTarget(float dist = NOMINAL_MELEE_RANGE) const; + bool hasNegativeAuraWithInterruptFlag(uint32 flag); void addUnitState(uint32 f) { m_state |= f; } bool hasUnitState(const uint32 f) const { return (m_state & f); } @@ -903,8 +988,14 @@ class TRINITY_DLL_SPEC Unit : public WorldObject return false; } - bool IsPvP() const { return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP); } - void SetPvP(bool state) { if(state) SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP); else RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP); } + bool IsPvP() const { return HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_PVP); } + void SetPvP(bool state) + { + if(state) + SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_PVP); + else + RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_PVP); + } uint32 GetCreatureType() const; uint32 GetCreatureTypeMask() const { @@ -917,6 +1008,9 @@ class TRINITY_DLL_SPEC Unit : public WorldObject bool IsStandState() const; void SetStandState(uint8 state); + void SetStandFlags(uint8 flags) { SetByteFlag(UNIT_FIELD_BYTES_1, 2,flags); } + void RemoveStandFlags(uint8 flags) { RemoveByteFlag(UNIT_FIELD_BYTES_1, 2,flags); } + bool IsMounted() const { return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_MOUNT ); } uint32 GetMountID() const { return GetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID); } void Mount(uint32 mount); @@ -939,6 +1033,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject void DealMeleeDamage(CalcDamageInfo *damageInfo, bool durabilityLoss); void CalculateSpellDamageTaken(SpellNonMeleeDamage *damageInfo, int32 damage, SpellEntry const *spellInfo, WeaponAttackType attackType = BASE_ATTACK); + int32 GetIgnoredArmorMultiplier(SpellEntry const *spellInfo, WeaponAttackType attackType); void DealSpellDamage(SpellNonMeleeDamage *damageInfo, bool durabilityLoss); float MeleeSpellMissChance(const Unit *pVictim, WeaponAttackType attType, int32 skillDiff, uint32 spellId) const; @@ -950,16 +1045,26 @@ class TRINITY_DLL_SPEC Unit : public WorldObject float GetUnitParryChance() const; float GetUnitBlockChance() const; float GetUnitCriticalChance(WeaponAttackType attackType, const Unit *pVictim) const; + bool CanUseAttackType( uint8 attacktype ) const + { + switch(attacktype) + { + case BASE_ATTACK: return !HasFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_DISARMED); + case OFF_ATTACK: return !HasFlag(UNIT_FIELD_FLAGS_2,UNIT_FLAG2_DISARM_OFFHAND); + case RANGED_ATTACK: return !HasFlag(UNIT_FIELD_FLAGS_2,UNIT_FLAG2_DISARM_RANGED); + } + return true; + } virtual uint32 GetShieldBlockValue() const =0; uint32 GetUnitMeleeSkill(Unit const* target = NULL) const { return (target ? getLevelForTarget(target) : getLevel()) * 5; } uint32 GetDefenseSkillValue(Unit const* target = NULL) const; uint32 GetWeaponSkillValue(WeaponAttackType attType, Unit const* target = NULL) const; float GetWeaponProcChance() const; - float GetPPMProcChance(uint32 WeaponSpeed, float PPM) const; + float GetPPMProcChance(uint32 WeaponSpeed, float PPM, const SpellEntry * spellProto) const; MeleeHitOutcome RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttackType attType) const; - MeleeHitOutcome RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttackType attType, int32 crit_chance, int32 miss_chance, int32 dodge_chance, int32 parry_chance, int32 block_chance, bool SpellCasted ) const; + MeleeHitOutcome RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttackType attType, int32 crit_chance, int32 miss_chance, int32 dodge_chance, int32 parry_chance, int32 block_chance) const; bool isVendor() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_VENDOR ); } bool isTrainer() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_TRAINER ); } @@ -997,10 +1102,6 @@ class TRINITY_DLL_SPEC Unit : public WorldObject void ClearInCombat(); uint32 GetCombatTimer() const { return m_CombatTimer; } - bool HasAuraType(AuraType auraType) const; - bool HasAura(uint32 spellId, uint32 effIndex) const - { return m_Auras.find(spellEffectPair(spellId, effIndex)) != m_Auras.end(); } - bool virtual HasSpell(uint32 /*spellID*/) const { return false; } bool HasStealthAura() const { return HasAuraType(SPELL_AURA_MOD_STEALTH); } @@ -1021,13 +1122,15 @@ class TRINITY_DLL_SPEC Unit : public WorldObject void SendHealSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage, bool critical = false); void SendEnergizeSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage,Powers powertype); uint32 SpellNonMeleeDamageLog(Unit *pVictim, uint32 spellID, uint32 damage, bool isTriggeredSpell = false, bool useSpellDamage = true); - void CastSpell(Unit* Victim, uint32 spellId, bool triggered, Item *castItem = NULL, Aura* triggeredByAura = NULL, uint64 originalCaster = 0); - void CastSpell(Unit* Victim,SpellEntry const *spellInfo, bool triggered, Item *castItem= NULL, Aura* triggeredByAura = NULL, uint64 originalCaster = 0); - void CastCustomSpell(Unit* Victim, uint32 spellId, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem= NULL, Aura* triggeredByAura = NULL, uint64 originalCaster = 0); - void CastCustomSpell(Unit* Victim,SpellEntry const *spellInfo, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem= NULL, Aura* triggeredByAura = NULL, uint64 originalCaster = 0); - void CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item *castItem = NULL, Aura* triggeredByAura = NULL, uint64 originalCaster = 0); - void CastSpell(GameObject *go, uint32 spellId, bool triggered, Item *castItem = NULL, Aura* triggeredByAura = NULL, uint64 originalCaster = 0); + void CastSpell(Unit* Victim, uint32 spellId, bool triggered, Item *castItem = NULL, AuraEffect* triggeredByAura = NULL, uint64 originalCaster = 0); + void CastSpell(Unit* Victim,SpellEntry const *spellInfo, bool triggered, Item *castItem= NULL, AuraEffect* triggeredByAura = NULL, uint64 originalCaster = 0); + void CastCustomSpell(Unit* Victim, uint32 spellId, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem= NULL, AuraEffect* triggeredByAura = NULL, uint64 originalCaster = 0); + void CastCustomSpell(Unit* Victim,SpellEntry const *spellInfo, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem= NULL, AuraEffect* triggeredByAura = NULL, uint64 originalCaster = 0); + void CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item *castItem = NULL, AuraEffect* triggeredByAura = NULL, uint64 originalCaster = 0); + void CastSpell(GameObject *go, uint32 spellId, bool triggered, Item *castItem = NULL, AuraEffect* triggeredByAura = NULL, uint64 originalCaster = 0); void AddAura(uint32 spellId, Unit *target); + void HandleAuraEffect(AuraEffect * aureff, bool apply); + Aura *AddAuraEffect(uint32 spellId, uint8 effIndex, Unit* caster); bool IsDamageToThreatSpell(SpellEntry const * spellInfo) const; @@ -1040,15 +1143,20 @@ class TRINITY_DLL_SPEC Unit : public WorldObject void SendSpellNonMeleeDamageLog(Unit *target,uint32 SpellID,uint32 Damage, SpellSchoolMask damageSchoolMask,uint32 AbsorbedDamage, uint32 Resist,bool PhysicalDamage, uint32 Blocked, bool CriticalHit = false); void SendSpellMiss(Unit *target, uint32 spellID, SpellMissInfo missInfo); + void NearTeleportTo(float x, float y, float z, float orientation, bool casting = false); + + void KnockbackFrom(float x, float y, float speedXY, float speedZ); + void JumpTo(float speedXY, float speedZ, bool forward = true); + void JumpTo(WorldObject *obj, float speedZ); + void SendMonsterStop(); void SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint32 Time, Player* player = NULL); + void SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint32 MoveFlags, uint32 time, float speedZ, Player *player = NULL); //void SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint8 type, uint32 MovementFlags, uint32 Time, Player* player = NULL); void SendMonsterMoveByPath(Path const& path, uint32 start, uint32 end); - void SendMonsterMoveWithSpeed(float x, float y, float z, uint32 MovementFlags, uint32 transitTime = 0, Player* player = NULL); + void SendMonsterMoveWithSpeed(float x, float y, float z, uint32 transitTime = 0, Player* player = NULL); void SendMonsterMoveWithSpeedToCurrentDestination(Player* player = NULL); - virtual void MoveOutOfRange(Player &) { }; - bool isAlive() const { return (m_deathState == ALIVE); }; bool isDead() const { return ( m_deathState == DEAD || m_deathState == CORPSE ); }; DeathState getDeathState() { return m_deathState; }; @@ -1058,10 +1166,13 @@ class TRINITY_DLL_SPEC Unit : public WorldObject void SetOwnerGUID(uint64 owner) { SetUInt64Value(UNIT_FIELD_SUMMONEDBY, owner); } uint64 GetCreatorGUID() const { return GetUInt64Value(UNIT_FIELD_CREATEDBY); } void SetCreatorGUID(uint64 creator) { SetUInt64Value(UNIT_FIELD_CREATEDBY, creator); } - uint64 GetPetGUID() const { return GetUInt64Value(UNIT_FIELD_SUMMON); } + uint64 GetGuardianGUID() const { return GetUInt64Value(UNIT_FIELD_SUMMON); } + void SetGuardianGUID(uint64 guid) { SetUInt64Value(UNIT_FIELD_SUMMON, guid); } uint64 GetCharmerGUID() const { return GetUInt64Value(UNIT_FIELD_CHARMEDBY); } void SetCharmerGUID(uint64 owner) { SetUInt64Value(UNIT_FIELD_CHARMEDBY, owner); } uint64 GetCharmGUID() const { return GetUInt64Value(UNIT_FIELD_CHARM); } + void SetPetGUID(uint64 guid) { m_SummonSlot[SUMMON_SLOT_PET] = guid; } + uint64 GetPetGUID() const { return m_SummonSlot[SUMMON_SLOT_PET]; } uint64 GetCharmerOrOwnerGUID() const { return GetCharmerGUID() ? GetCharmerGUID() : GetOwnerGUID(); } uint64 GetCharmerOrOwnerOrOwnGUID() const @@ -1075,7 +1186,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject Player* GetSpellModOwner() const; Unit* GetOwner() const; - Pet* GetPet() const; + Guardian *GetGuardianPet() const; Unit* GetCharmer() const; Unit* GetCharm() const; Unit* GetCharmerOrOwner() const { return GetCharmerGUID() ? GetCharmer() : GetOwner(); } @@ -1088,12 +1199,17 @@ class TRINITY_DLL_SPEC Unit : public WorldObject } Player* GetCharmerOrOwnerPlayerOrPlayerItself() const; - void SetPet(Pet* pet); - void SetCharm(Unit* pet); + void SetGuardian(Guardian* target, bool apply); + void SetCharm(Unit* target, bool apply); + Unit* GetNextRandomRaidMemberOrPet(float radius); void SetCharmedOrPossessedBy(Unit* charmer, bool possess); void RemoveCharmedOrPossessedBy(Unit* charmer); void RestoreFaction(); + ControlList m_Controlled; + Unit* GetFirstControlled() const; + void RemoveAllControlled(); + bool isCharmed() const { return GetCharmerGUID() != 0; } bool isPossessed() const { return hasUnitState(UNIT_STAT_POSSESSED); } bool isPossessedByPlayer() const { return hasUnitState(UNIT_STAT_POSSESSED) && IS_PLAYER_GUID(GetCharmerGUID()); } @@ -1122,31 +1238,23 @@ class TRINITY_DLL_SPEC Unit : public WorldObject bool AddAura(Aura *aur); void RemoveAura(AuraMap::iterator &i, AuraRemoveMode mode = AURA_REMOVE_BY_DEFAULT); - void RemoveAura(uint32 spellId, uint32 effindex, Aura* except = NULL); - void RemoveSingleAuraFromStackByDispel(uint32 spellId); - void RemoveSingleAuraFromStack(uint32 spellId, uint32 effindex); - void RemoveAurasDueToSpell(uint32 spellId, Aura* except = NULL); - void RemoveAurasDueToItemSpell(Item* castItem,uint32 spellId); - void RemoveAurasByCasterSpell(uint32 spellId, uint64 casterGUID); - void SetAurasDurationByCasterSpell(uint32 spellId, uint64 casterGUID, int32 duration); - Aura* GetAuraByCasterSpell(uint32 spellId, uint64 casterGUID); + void RemoveAura(uint32 spellId, uint64 caster = 0 ,AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT); + void RemoveAura(Aura * aur, AuraRemoveMode mode = AURA_REMOVE_BY_DEFAULT); + void RemoveAurasDueToSpell(uint32 spellId, uint64 caster = NULL ,AuraRemoveMode removeMode= AURA_REMOVE_BY_DEFAULT); + void RemoveAuraFromStack(uint32 spellId, uint64 caster = NULL ,AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT); + inline void RemoveAuraFromStack(AuraMap::iterator &iter,AuraRemoveMode removeMode); void RemoveAurasDueToSpellByDispel(uint32 spellId, uint64 casterGUID, Unit *dispeler); void RemoveAurasDueToSpellBySteal(uint32 spellId, uint64 casterGUID, Unit *stealer); - void RemoveAurasDueToSpellByCancel(uint32 spellId); - void RemoveAurasAtChanneledTarget(SpellEntry const* spellInfo, Unit * caster); + void RemoveAurasDueToItemSpell(Item* castItem,uint32 spellId); + void RemoveAurasByType(AuraType auraType, uint64 casterGUID = 0, Aura * except=NULL); + void RemoveAurasByTypeWithDispel(AuraType auraType, Spell * spell = NULL); void RemoveNotOwnSingleTargetAuras(); - - void RemoveSpellsCausingAura(AuraType auraType); - void RemoveAuraTypeByCaster(AuraType auraType, uint64 casterGUID); - void RemoveRankAurasDueToSpell(uint32 spellId); bool RemoveNoStackAurasDueToAura(Aura *Aur); - void RemoveAurasWithInterruptFlags(uint32 flags, uint32 except = 0); - void RemoveAurasWithDispelType( DispelType type ); - + void RemoveAurasWithInterruptFlags(uint32 flag, uint32 except = NULL); void RemoveAllAuras(); void RemoveArenaAuras(bool onleave = false); void RemoveAllAurasOnDeath(); - void DelayAura(uint32 spellId, uint32 effindex, int32 delaytime); + void DelayAura(uint32 spellId, uint64 caster, int32 delaytime); float GetResistanceBuffMods(SpellSchools school, bool positive) const { return GetFloatValue(positive ? UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE+school : UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE+school ); } void SetResistanceBuffMods(SpellSchools school, bool positive, float val) { SetFloatValue(positive ? UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE+school : UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE+school,val); } @@ -1191,12 +1299,15 @@ class TRINITY_DLL_SPEC Unit : public WorldObject Spell* m_currentSpells[CURRENT_MAX_SPELL]; uint32 m_addDmgOnce; - uint64 m_TotemSlot[MAX_TOTEM]; + uint64 m_SummonSlot[MAX_SUMMON_SLOT]; uint64 m_ObjectSlot[4]; uint32 m_detectInvisibilityMask; uint32 m_invisibilityMask; + uint32 m_ShapeShiftFormSpellId; ShapeshiftForm m_form; + bool IsInFeralForm() const { return m_form == FORM_CAT || m_form == FORM_BEAR || m_form == FORM_DIREBEAR; } + float m_modMeleeHitChance; float m_modRangedHitChance; float m_modSpellHitChance; @@ -1242,17 +1353,17 @@ class TRINITY_DLL_SPEC Unit : public WorldObject void DestroyForNearbyPlayers(); // common function for visibility checks for player/creatures with detection code - virtual bool canSeeOrDetect(Unit const* u, bool detect, bool inVisibleList = false, bool is3dDistance = true) const; + virtual bool canSeeOrDetect(Unit const* u, bool detect, bool inVisibleList = false, bool is3dDistance = true) const = 0; bool isVisibleForOrDetect(Unit const* u, bool detect, bool inVisibleList = false, bool is3dDistance = true) const; bool canDetectInvisibilityOf(Unit const* u) const; bool canDetectStealthOf(Unit const* u, float distance) const; + void SetPhaseMask(uint32 newPhaseMask, bool update);// overwrite WorldObject::SetPhaseMask // virtual functions for all world objects types bool isVisibleForInState(Player const* u, bool inVisibleList) const; // function for low level grid visibility checks in player/creature cases virtual bool IsVisibleInGridForPlayer(Player const* pl) const = 0; - bool waterbreath; AuraList & GetSingleCastAuras() { return m_scAuras; } AuraList const& GetSingleCastAuras() const { return m_scAuras; } SpellImmuneList m_spellImmune[MAX_SPELL_IMMUNITY]; @@ -1269,15 +1380,33 @@ class TRINITY_DLL_SPEC Unit : public WorldObject void removeHatedBy(HostilReference* /*pHostilReference*/ ) { /* nothing to do yet */ } HostilRefManager& getHostilRefManager() { return m_HostilRefManager; } - Aura* GetAura(uint32 spellId, uint32 effindex); + VisibleAuraMap const *GetVisibleAuras() { return &m_visibleAuras; } + Aura * GetVisibleAura(uint8 slot) + { + VisibleAuraMap::iterator itr = m_visibleAuras.find(slot); + if(itr != m_visibleAuras.end()) + return itr->second; + return 0; + } + void SetVisibleAura(uint8 slot, Aura * aur){ m_visibleAuras[slot]=aur; } + void RemoveVisibleAura(uint8 slot){ m_visibleAuras.erase(slot); } + AuraMap & GetAuras() { return m_Auras; } AuraMap const& GetAuras() const { return m_Auras; } - AuraList const& GetAurasByType(AuraType type) const { return m_modAuras[type]; } - void ApplyAuraProcTriggerDamage(Aura* aura, bool apply); + AuraEffectList const& GetAurasByType(AuraType type) const { return m_modAuras[type]; } + + AuraEffect * GetAuraEffect(uint32 spellId, uint8 effIndex, uint64 casterGUID = 0) const; + Aura * GetAura(uint32 spellId, uint64 casterGUID = 0) const; + AuraEffect* GetAura(AuraType type, uint32 family, uint32 familyFlag1 , uint32 familyFlag2=0, uint32 familyFlag3=0, uint64 casterGUID=0); + bool HasAuraEffect(uint32 spellId, uint8 effIndex, uint64 caster = 0) const; + bool HasAura(uint32 spellId, uint64 caster = 0) const; + bool HasAura(Aura * aur) const; + bool HasAuraType(AuraType auraType) const; + bool HasAuraTypeWithMiscvalue(AuraType auratype, uint32 miscvalue) const; int32 GetTotalAuraModifier(AuraType auratype) const; float GetTotalAuraMultiplier(AuraType auratype) const; - int32 GetMaxPositiveAuraModifier(AuraType auratype) const; + int32 GetMaxPositiveAuraModifier(AuraType auratype); int32 GetMaxNegativeAuraModifier(AuraType auratype) const; int32 GetTotalAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const; @@ -1290,7 +1419,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject int32 GetMaxPositiveAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const; int32 GetMaxNegativeAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const; - Aura* GetDummyAura(uint32 spell_id) const; + AuraEffect* GetDummyAura(uint32 spell_id) const; uint32 GetInterruptMask() const { return m_interruptMask; } void AddInterruptMask(uint32 mask) { m_interruptMask |= mask; } void UpdateInterruptMask(); @@ -1301,30 +1430,36 @@ class TRINITY_DLL_SPEC Unit : public WorldObject void SetNativeDisplayId(uint32 modelId) { SetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID, modelId); } void setTransForm(uint32 spellid) { m_transform = spellid;} uint32 getTransForm() const { return m_transform;} + + DynamicObject* GetDynObject(uint32 spellId, uint32 effIndex); + DynamicObject* GetDynObject(uint32 spellId); void AddDynObject(DynamicObject* dynObj); void RemoveDynObject(uint32 spellid); void RemoveDynObjectWithGUID(uint64 guid) { m_dynObjGUIDs.remove(guid); } void RemoveAllDynObjects(); + + GameObject* GetGameObject(uint32 spellId) const; void AddGameObject(GameObject* gameObj); void RemoveGameObject(GameObject* gameObj, bool del); void RemoveGameObject(uint32 spellid, bool del); void RemoveAllGameObjects(); - DynamicObject *GetDynObject(uint32 spellId, uint32 effIndex); - DynamicObject *GetDynObject(uint32 spellId); + uint32 CalculateDamage(WeaponAttackType attType, bool normalized); float GetAPMultiplier(WeaponAttackType attType, bool normalized); void ModifyAuraState(AuraState flag, bool apply); - bool HasAuraState(AuraState flag) const { return HasFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1)); } + bool HasAuraState(AuraState flag, SpellEntry const *spellProto = NULL, Unit * Caster = NULL) const ; void UnsummonAllTotems(); int32 SpellBaseDamageBonus(SpellSchoolMask schoolMask); int32 SpellBaseHealingBonus(SpellSchoolMask schoolMask); int32 SpellBaseDamageBonusForVictim(SpellSchoolMask schoolMask, Unit *pVictim); int32 SpellBaseHealingBonusForVictim(SpellSchoolMask schoolMask, Unit *pVictim); - uint32 SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint32 damage, DamageEffectType damagetype); - uint32 SpellHealingBonus(SpellEntry const *spellProto, uint32 healamount, DamageEffectType damagetype, Unit *pVictim); + uint32 SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint32 damage, DamageEffectType damagetype, uint32 stack = 1); + uint32 SpellHealingBonus(Unit *pVictim, SpellEntry const *spellProto, uint32 healamount, DamageEffectType damagetype, uint32 stack = 1); bool isSpellBlocked(Unit *pVictim, SpellEntry const *spellProto, WeaponAttackType attackType = BASE_ATTACK); + bool isBlockCritical(); bool isSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolMask schoolMask, WeaponAttackType attackType = BASE_ATTACK); - uint32 SpellCriticalBonus(SpellEntry const *spellProto, uint32 damage, Unit *pVictim); + uint32 SpellCriticalDamageBonus(SpellEntry const *spellProto, uint32 damage, Unit *pVictim); + uint32 SpellCriticalHealingBonus(SpellEntry const *spellProto, uint32 damage, Unit *pVictim); void SetLastManaUse(uint32 spellCastTime) { m_lastManaUse = spellCastTime; } bool IsUnderLastManaUseEffect() const; @@ -1336,19 +1471,20 @@ class TRINITY_DLL_SPEC Unit : public WorldObject void ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply); void ApplySpellDispelImmunity(const SpellEntry * spellProto, DispelType type, bool apply); - virtual bool IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges = false); + virtual bool IsImmunedToSpell(SpellEntry const* spellInfo); // redefined in Creature - bool IsImmunedToDamage(SpellSchoolMask meleeSchoolMask, bool useCharges = false); - virtual bool IsImmunedToSpellEffect(uint32 effect, uint32 mechanic) const; + bool IsImmunedToDamage(SpellSchoolMask meleeSchoolMask); + virtual bool IsImmunedToSpellEffect(SpellEntry const* spellInfo, uint32 index) const; // redefined in Creature - uint32 CalcArmorReducedDamage(Unit* pVictim, const uint32 damage); - void CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEffectType damagetype, const uint32 damage, uint32 *absorb, uint32 *resist); + uint32 CalcArmorReducedDamage(Unit* pVictim, const uint32 damage, SpellEntry const *spellInfo, WeaponAttackType attackType=MAX_ATTACK); + void CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEffectType damagetype, const uint32 damage, uint32 *absorb, uint32 *resist, SpellEntry const *spellInfo = NULL); void UpdateSpeed(UnitMoveType mtype, bool forced); float GetSpeed( UnitMoveType mtype ) const; float GetSpeedRate( UnitMoveType mtype ) const { return m_speed_rate[mtype]; } void SetSpeed(UnitMoveType mtype, float rate, bool forced = false); + float m_TempSpeed; void SetHover(bool on); bool isHover() const { return HasAuraType(SPELL_AURA_HOVER); } @@ -1357,7 +1493,9 @@ class TRINITY_DLL_SPEC Unit : public WorldObject void _ApplyAllAuraMods(); int32 CalculateSpellDamage(SpellEntry const* spellProto, uint8 effect_index, int32 basePoints, Unit const* target); - int32 CalculateSpellDuration(SpellEntry const* spellProto, uint8 effect_index, Unit const* target); + int32 CalcSpellDuration(SpellEntry const* spellProto); + int32 ModSpellDuration(SpellEntry const* spellProto, Unit const* target, int32 duration, bool positive); + void ModSpellCastTime(SpellEntry const* spellProto, int32 & castTime, Spell const * spell=NULL); float CalculateLevelPenalty(SpellEntry const* spellProto) const; void addFollower(FollowerReference* pRef) { m_FollowingRefManager.insertFirst(pRef); } @@ -1382,17 +1520,13 @@ class TRINITY_DLL_SPEC Unit : public WorldObject void SetUnitMovementFlags(uint32 f) { m_unit_movement_flags = f; } void SetControlled(bool apply, UnitState state); - void SetFeared(bool apply/*, uint64 casterGUID = 0, uint32 spellID = 0*/); - void SetConfused(bool apply/*, uint64 casterGUID = 0, uint32 spellID = 0*/); - void SetStunned(bool apply); - void SetRooted(bool apply); void AddComboPointHolder(uint32 lowguid) { m_ComboPointHolders.insert(lowguid); } void RemoveComboPointHolder(uint32 lowguid) { m_ComboPointHolders.erase(lowguid); } void ClearComboPointHolders(); ///----------Pet responses methods----------------- - void SendPetCastFail(uint32 spellid, uint8 msg); + void SendPetCastFail(uint32 spellid, SpellCastResult msg); void SendPetActionFeedback (uint8 msg); void SendPetTalk (uint32 pettalk); void SendPetSpellCooldown (uint32 spellid, time_t cooltime); @@ -1433,6 +1567,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject UnitAI *i_AI, *i_disabledAI; void _UpdateSpells(uint32 time); + void _DeleteAuras(); void _UpdateAutoRepeatSpell(); bool m_AutoRepeatFirstCast; @@ -1451,21 +1586,23 @@ class TRINITY_DLL_SPEC Unit : public WorldObject typedef std::list<uint64> DynObjectGUIDs; DynObjectGUIDs m_dynObjGUIDs; - std::list<GameObject*> m_gameObj; + typedef std::list<GameObject*> GameObjectList; + GameObjectList m_gameObj; bool m_isSorted; uint32 m_transform; - uint32 m_removedAuras; - AuraList m_modAuras[TOTAL_AURAS]; + AuraEffectList m_modAuras[TOTAL_AURAS]; AuraList m_scAuras; // casted singlecast auras AuraList m_interruptableAuras; AuraList m_ccAuras; + AuraList m_removedAuras; uint32 m_interruptMask; float m_auraModifiersGroup[UNIT_MOD_END][MODIFIER_TYPE_END]; float m_weaponDamage[MAX_ATTACK][2]; bool m_canModifyStats; //std::list< spellEffectPair > AuraSpells[TOTAL_AURAS]; // TODO: use this if ok for mem + VisibleAuraMap m_visibleAuras; float m_speed_rate[MAX_MOVE_TYPE]; @@ -1478,6 +1615,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject uint32 m_unit_movement_flags; uint32 m_reactiveTimer[MAX_REACTIVE]; + uint32 m_regenTimer; ThreatManager m_ThreatManager; @@ -1485,12 +1623,19 @@ class TRINITY_DLL_SPEC Unit : public WorldObject void SendAttackStop(Unit* victim); // only from AttackStop(Unit*) //void SendAttackStart(Unit* pVictim); // only from Unit::AttackStart(Unit*) - bool IsTriggeredAtSpellProcEvent( Aura* aura, SpellEntry const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const*& spellProcEvent ); - bool HandleDummyAuraProc( Unit *pVictim, uint32 damage, Aura* triggredByAura, SpellEntry const *procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); - bool HandleHasteAuraProc( Unit *pVictim, uint32 damage, Aura* triggredByAura, SpellEntry const *procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); - bool HandleProcTriggerSpell(Unit *pVictim, uint32 damage, Aura* triggredByAura, SpellEntry const *procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); - bool HandleOverrideClassScriptAuraProc(Unit *pVictim, Aura* triggredByAura, SpellEntry const *procSpell, uint32 cooldown); - bool HandleMeandingAuraProc(Aura* triggeredByAura); + bool IsTriggeredAtSpellProcEvent(Unit *pVictim, Aura* aura, SpellEntry const * procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const *& spellProcEvent ); + bool HandleDummyAuraProc( Unit *pVictim, uint32 damage, AuraEffect* triggeredByAura, SpellEntry const *procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); + bool HandleObsModEnergyAuraProc( Unit *pVictim, uint32 damage, AuraEffect* triggeredByAura, SpellEntry const *procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); + bool HandleHasteAuraProc( Unit *pVictim, uint32 damage, AuraEffect* triggeredByAura, SpellEntry const *procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); + bool HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* triggeredByAura, SpellEntry const *procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); + bool HandleOverrideClassScriptAuraProc(Unit *pVictim, uint32 damage, AuraEffect* triggeredByAura, SpellEntry const *procSpell, uint32 cooldown); + bool HandleAuraRaidProcFromChargeWithValue(AuraEffect* triggeredByAura); + bool HandleAuraRaidProcFromCharge(AuraEffect* triggeredByAura); + + void SetFeared(bool apply); + void SetConfused(bool apply); + void SetStunned(bool apply); + void SetRooted(bool apply); uint32 m_state; // Even derived shouldn't modify uint32 m_CombatTimer; diff --git a/src/game/UnitEvents.h b/src/game/UnitEvents.h index 4bc362893a3..67f8d01f0d6 100644 --- a/src/game/UnitEvents.h +++ b/src/game/UnitEvents.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/UpdateData.cpp b/src/game/UpdateData.cpp index 91ef84bb372..de615c9cc8e 100644 --- a/src/game/UpdateData.cpp +++ b/src/game/UpdateData.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -108,7 +108,7 @@ bool UpdateData::BuildPacket(WorldPacket *packet, bool hasTransport) ByteBuffer buf(m_data.size() + 10 + m_outOfRangeGUIDs.size()*8); buf << (uint32) (!m_outOfRangeGUIDs.empty() ? m_blockCount + 1 : m_blockCount); - buf << (uint8) (hasTransport ? 1 : 0); + //buf << (uint8) (hasTransport ? 1 : 0); if(!m_outOfRangeGUIDs.empty()) { diff --git a/src/game/UpdateData.h b/src/game/UpdateData.h index 256c572bb1d..71a9258b90b 100644 --- a/src/game/UpdateData.h +++ b/src/game/UpdateData.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -38,11 +38,12 @@ enum OBJECT_UPDATE_FLAGS UPDATEFLAG_NONE = 0x00, UPDATEFLAG_SELF = 0x01, UPDATEFLAG_TRANSPORT = 0x02, - UPDATEFLAG_FULLGUID = 0x04, + UPDATEFLAG_HAS_TARGET = 0x04, UPDATEFLAG_LOWGUID = 0x08, UPDATEFLAG_HIGHGUID = 0x10, UPDATEFLAG_LIVING = 0x20, - UPDATEFLAG_HASPOSITION = 0x40 + UPDATEFLAG_HAS_POSITION = 0x40, + UPDATEFLAG_VEHICLE = 0x80 }; class UpdateData diff --git a/src/game/UpdateFields.h b/src/game/UpdateFields.h index c8e70fc2198..97fcbeb05d6 100644 --- a/src/game/UpdateFields.h +++ b/src/game/UpdateFields.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,7 +21,7 @@ #ifndef _UPDATEFIELDS_AUTO_H #define _UPDATEFIELDS_AUTO_H -// Auto generated for version 2, 4, 3, 8606 +// Auto generated for version 3, 0, 3, 9183 enum EObjectFields { @@ -43,13 +43,37 @@ enum EItemFields ITEM_FIELD_DURATION = OBJECT_END + 0x0009, // Size: 1, Type: INT, Flags: OWNER_ONLY, UNK2 ITEM_FIELD_SPELL_CHARGES = OBJECT_END + 0x000A, // Size: 5, Type: INT, Flags: OWNER_ONLY, UNK2 ITEM_FIELD_FLAGS = OBJECT_END + 0x000F, // Size: 1, Type: INT, Flags: PUBLIC - ITEM_FIELD_ENCHANTMENT = OBJECT_END + 0x0010, // Size: 33, Type: INT, Flags: PUBLIC - ITEM_FIELD_PROPERTY_SEED = OBJECT_END + 0x0031, // Size: 1, Type: INT, Flags: PUBLIC - ITEM_FIELD_RANDOM_PROPERTIES_ID = OBJECT_END + 0x0032, // Size: 1, Type: INT, Flags: PUBLIC - ITEM_FIELD_ITEM_TEXT_ID = OBJECT_END + 0x0033, // Size: 1, Type: INT, Flags: OWNER_ONLY - ITEM_FIELD_DURABILITY = OBJECT_END + 0x0034, // Size: 1, Type: INT, Flags: OWNER_ONLY, UNK2 - ITEM_FIELD_MAXDURABILITY = OBJECT_END + 0x0035, // Size: 1, Type: INT, Flags: OWNER_ONLY, UNK2 - ITEM_END = OBJECT_END + 0x0036, + ITEM_FIELD_ENCHANTMENT_1_1 = OBJECT_END + 0x0010, // Size: 2, Type: INT, Flags: PUBLIC + ITEM_FIELD_ENCHANTMENT_1_3 = OBJECT_END + 0x0012, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + ITEM_FIELD_ENCHANTMENT_2_1 = OBJECT_END + 0x0013, // Size: 2, Type: INT, Flags: PUBLIC + ITEM_FIELD_ENCHANTMENT_2_3 = OBJECT_END + 0x0015, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + ITEM_FIELD_ENCHANTMENT_3_1 = OBJECT_END + 0x0016, // Size: 2, Type: INT, Flags: PUBLIC + ITEM_FIELD_ENCHANTMENT_3_3 = OBJECT_END + 0x0018, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + ITEM_FIELD_ENCHANTMENT_4_1 = OBJECT_END + 0x0019, // Size: 2, Type: INT, Flags: PUBLIC + ITEM_FIELD_ENCHANTMENT_4_3 = OBJECT_END + 0x001B, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + ITEM_FIELD_ENCHANTMENT_5_1 = OBJECT_END + 0x001C, // Size: 2, Type: INT, Flags: PUBLIC + ITEM_FIELD_ENCHANTMENT_5_3 = OBJECT_END + 0x001E, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + ITEM_FIELD_ENCHANTMENT_6_1 = OBJECT_END + 0x001F, // Size: 2, Type: INT, Flags: PUBLIC + ITEM_FIELD_ENCHANTMENT_6_3 = OBJECT_END + 0x0021, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + ITEM_FIELD_ENCHANTMENT_7_1 = OBJECT_END + 0x0022, // Size: 2, Type: INT, Flags: PUBLIC + ITEM_FIELD_ENCHANTMENT_7_3 = OBJECT_END + 0x0024, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + ITEM_FIELD_ENCHANTMENT_8_1 = OBJECT_END + 0x0025, // Size: 2, Type: INT, Flags: PUBLIC + ITEM_FIELD_ENCHANTMENT_8_3 = OBJECT_END + 0x0027, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + ITEM_FIELD_ENCHANTMENT_9_1 = OBJECT_END + 0x0028, // Size: 2, Type: INT, Flags: PUBLIC + ITEM_FIELD_ENCHANTMENT_9_3 = OBJECT_END + 0x002A, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + ITEM_FIELD_ENCHANTMENT_10_1 = OBJECT_END + 0x002B, // Size: 2, Type: INT, Flags: PUBLIC + ITEM_FIELD_ENCHANTMENT_10_3 = OBJECT_END + 0x002D, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + ITEM_FIELD_ENCHANTMENT_11_1 = OBJECT_END + 0x002E, // Size: 2, Type: INT, Flags: PUBLIC + ITEM_FIELD_ENCHANTMENT_11_3 = OBJECT_END + 0x0030, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + ITEM_FIELD_ENCHANTMENT_12_1 = OBJECT_END + 0x0031, // Size: 2, Type: INT, Flags: PUBLIC + ITEM_FIELD_ENCHANTMENT_12_3 = OBJECT_END + 0x0033, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + ITEM_FIELD_PROPERTY_SEED = OBJECT_END + 0x0034, // Size: 1, Type: INT, Flags: PUBLIC + ITEM_FIELD_RANDOM_PROPERTIES_ID = OBJECT_END + 0x0035, // Size: 1, Type: INT, Flags: PUBLIC + ITEM_FIELD_ITEM_TEXT_ID = OBJECT_END + 0x0036, // Size: 1, Type: INT, Flags: OWNER_ONLY + ITEM_FIELD_DURABILITY = OBJECT_END + 0x0037, // Size: 1, Type: INT, Flags: OWNER_ONLY, UNK2 + ITEM_FIELD_MAXDURABILITY = OBJECT_END + 0x0038, // Size: 1, Type: INT, Flags: OWNER_ONLY, UNK2 + ITEM_FIELD_PAD = OBJECT_END + 0x0039, // Size: 1, Type: INT, Flags: NONE + ITEM_END = OBJECT_END + 0x003A, }; enum EContainerFields @@ -64,93 +88,94 @@ enum EUnitFields { UNIT_FIELD_CHARM = OBJECT_END + 0x0000, // Size: 2, Type: LONG, Flags: PUBLIC UNIT_FIELD_SUMMON = OBJECT_END + 0x0002, // Size: 2, Type: LONG, Flags: PUBLIC - UNIT_FIELD_CHARMEDBY = OBJECT_END + 0x0004, // Size: 2, Type: LONG, Flags: PUBLIC - UNIT_FIELD_SUMMONEDBY = OBJECT_END + 0x0006, // Size: 2, Type: LONG, Flags: PUBLIC - UNIT_FIELD_CREATEDBY = OBJECT_END + 0x0008, // Size: 2, Type: LONG, Flags: PUBLIC - UNIT_FIELD_TARGET = OBJECT_END + 0x000A, // Size: 2, Type: LONG, Flags: PUBLIC - UNIT_FIELD_PERSUADED = OBJECT_END + 0x000C, // Size: 2, Type: LONG, Flags: PUBLIC + UNIT_FIELD_CRITTER = OBJECT_END + 0x0004, // Size: 2, Type: LONG, Flags: PRIVATE + UNIT_FIELD_CHARMEDBY = OBJECT_END + 0x0006, // Size: 2, Type: LONG, Flags: PUBLIC + UNIT_FIELD_SUMMONEDBY = OBJECT_END + 0x0008, // Size: 2, Type: LONG, Flags: PUBLIC + UNIT_FIELD_CREATEDBY = OBJECT_END + 0x000A, // Size: 2, Type: LONG, Flags: PUBLIC + UNIT_FIELD_TARGET = OBJECT_END + 0x000C, // Size: 2, Type: LONG, Flags: PUBLIC UNIT_FIELD_CHANNEL_OBJECT = OBJECT_END + 0x000E, // Size: 2, Type: LONG, Flags: PUBLIC - UNIT_FIELD_HEALTH = OBJECT_END + 0x0010, // Size: 1, Type: INT, Flags: DYNAMIC - UNIT_FIELD_POWER1 = OBJECT_END + 0x0011, // Size: 1, Type: INT, Flags: PUBLIC - UNIT_FIELD_POWER2 = OBJECT_END + 0x0012, // Size: 1, Type: INT, Flags: PUBLIC - UNIT_FIELD_POWER3 = OBJECT_END + 0x0013, // Size: 1, Type: INT, Flags: PUBLIC - UNIT_FIELD_POWER4 = OBJECT_END + 0x0014, // Size: 1, Type: INT, Flags: PUBLIC - UNIT_FIELD_POWER5 = OBJECT_END + 0x0015, // Size: 1, Type: INT, Flags: PUBLIC - UNIT_FIELD_MAXHEALTH = OBJECT_END + 0x0016, // Size: 1, Type: INT, Flags: DYNAMIC - UNIT_FIELD_MAXPOWER1 = OBJECT_END + 0x0017, // Size: 1, Type: INT, Flags: PUBLIC - UNIT_FIELD_MAXPOWER2 = OBJECT_END + 0x0018, // Size: 1, Type: INT, Flags: PUBLIC - UNIT_FIELD_MAXPOWER3 = OBJECT_END + 0x0019, // Size: 1, Type: INT, Flags: PUBLIC - UNIT_FIELD_MAXPOWER4 = OBJECT_END + 0x001A, // Size: 1, Type: INT, Flags: PUBLIC - UNIT_FIELD_MAXPOWER5 = OBJECT_END + 0x001B, // Size: 1, Type: INT, Flags: PUBLIC - UNIT_FIELD_LEVEL = OBJECT_END + 0x001C, // Size: 1, Type: INT, Flags: PUBLIC - UNIT_FIELD_FACTIONTEMPLATE = OBJECT_END + 0x001D, // Size: 1, Type: INT, Flags: PUBLIC - UNIT_FIELD_BYTES_0 = OBJECT_END + 0x001E, // Size: 1, Type: BYTES, Flags: PUBLIC - UNIT_VIRTUAL_ITEM_SLOT_DISPLAY = OBJECT_END + 0x001F, // Size: 3, Type: INT, Flags: PUBLIC - UNIT_VIRTUAL_ITEM_INFO = OBJECT_END + 0x0022, // Size: 6, Type: BYTES, Flags: PUBLIC - UNIT_FIELD_FLAGS = OBJECT_END + 0x0028, // Size: 1, Type: INT, Flags: PUBLIC - UNIT_FIELD_FLAGS_2 = OBJECT_END + 0x0029, // Size: 1, Type: INT, Flags: PUBLIC - UNIT_FIELD_AURA = OBJECT_END + 0x002A, // Size: 56, Type: INT, Flags: PUBLIC - UNIT_FIELD_AURAFLAGS = OBJECT_END + 0x0062, // Size: 14, Type: BYTES, Flags: PUBLIC - UNIT_FIELD_AURALEVELS = OBJECT_END + 0x0070, // Size: 14, Type: BYTES, Flags: PUBLIC - UNIT_FIELD_AURAAPPLICATIONS = OBJECT_END + 0x007E, // Size: 14, Type: BYTES, Flags: PUBLIC - UNIT_FIELD_AURASTATE = OBJECT_END + 0x008C, // Size: 1, Type: INT, Flags: PUBLIC - UNIT_FIELD_BASEATTACKTIME = OBJECT_END + 0x008D, // Size: 2, Type: INT, Flags: PUBLIC - UNIT_FIELD_RANGEDATTACKTIME = OBJECT_END + 0x008F, // Size: 1, Type: INT, Flags: PRIVATE - UNIT_FIELD_BOUNDINGRADIUS = OBJECT_END + 0x0090, // Size: 1, Type: FLOAT, Flags: PUBLIC - UNIT_FIELD_COMBATREACH = OBJECT_END + 0x0091, // Size: 1, Type: FLOAT, Flags: PUBLIC - UNIT_FIELD_DISPLAYID = OBJECT_END + 0x0092, // Size: 1, Type: INT, Flags: PUBLIC - UNIT_FIELD_NATIVEDISPLAYID = OBJECT_END + 0x0093, // Size: 1, Type: INT, Flags: PUBLIC - UNIT_FIELD_MOUNTDISPLAYID = OBJECT_END + 0x0094, // Size: 1, Type: INT, Flags: PUBLIC - UNIT_FIELD_MINDAMAGE = OBJECT_END + 0x0095, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY, UNK3 - UNIT_FIELD_MAXDAMAGE = OBJECT_END + 0x0096, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY, UNK3 - UNIT_FIELD_MINOFFHANDDAMAGE = OBJECT_END + 0x0097, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY, UNK3 - UNIT_FIELD_MAXOFFHANDDAMAGE = OBJECT_END + 0x0098, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY, UNK3 - UNIT_FIELD_BYTES_1 = OBJECT_END + 0x0099, // Size: 1, Type: BYTES, Flags: PUBLIC - UNIT_FIELD_PETNUMBER = OBJECT_END + 0x009A, // Size: 1, Type: INT, Flags: PUBLIC - UNIT_FIELD_PET_NAME_TIMESTAMP = OBJECT_END + 0x009B, // Size: 1, Type: INT, Flags: PUBLIC - UNIT_FIELD_PETEXPERIENCE = OBJECT_END + 0x009C, // Size: 1, Type: INT, Flags: OWNER_ONLY - UNIT_FIELD_PETNEXTLEVELEXP = OBJECT_END + 0x009D, // Size: 1, Type: INT, Flags: OWNER_ONLY - UNIT_DYNAMIC_FLAGS = OBJECT_END + 0x009E, // Size: 1, Type: INT, Flags: DYNAMIC - UNIT_CHANNEL_SPELL = OBJECT_END + 0x009F, // Size: 1, Type: INT, Flags: PUBLIC - UNIT_MOD_CAST_SPEED = OBJECT_END + 0x00A0, // Size: 1, Type: FLOAT, Flags: PUBLIC - UNIT_CREATED_BY_SPELL = OBJECT_END + 0x00A1, // Size: 1, Type: INT, Flags: PUBLIC - UNIT_NPC_FLAGS = OBJECT_END + 0x00A2, // Size: 1, Type: INT, Flags: DYNAMIC - UNIT_NPC_EMOTESTATE = OBJECT_END + 0x00A3, // Size: 1, Type: INT, Flags: PUBLIC - UNIT_TRAINING_POINTS = OBJECT_END + 0x00A4, // Size: 1, Type: TWO_SHORT, Flags: OWNER_ONLY - UNIT_FIELD_STAT0 = OBJECT_END + 0x00A5, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_STAT1 = OBJECT_END + 0x00A6, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_STAT2 = OBJECT_END + 0x00A7, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_STAT3 = OBJECT_END + 0x00A8, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_STAT4 = OBJECT_END + 0x00A9, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_POSSTAT0 = OBJECT_END + 0x00AA, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_POSSTAT1 = OBJECT_END + 0x00AB, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_POSSTAT2 = OBJECT_END + 0x00AC, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_POSSTAT3 = OBJECT_END + 0x00AD, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_POSSTAT4 = OBJECT_END + 0x00AE, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_NEGSTAT0 = OBJECT_END + 0x00AF, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_NEGSTAT1 = OBJECT_END + 0x00B0, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_NEGSTAT2 = OBJECT_END + 0x00B1, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_NEGSTAT3 = OBJECT_END + 0x00B2, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_NEGSTAT4 = OBJECT_END + 0x00B3, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_RESISTANCES = OBJECT_END + 0x00B4, // Size: 7, Type: INT, Flags: PRIVATE, OWNER_ONLY, UNK3 - UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE = OBJECT_END + 0x00BB, // Size: 7, Type: INT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE = OBJECT_END + 0x00C2, // Size: 7, Type: INT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_BASE_MANA = OBJECT_END + 0x00C9, // Size: 1, Type: INT, Flags: PUBLIC - UNIT_FIELD_BASE_HEALTH = OBJECT_END + 0x00CA, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_BYTES_2 = OBJECT_END + 0x00CB, // Size: 1, Type: BYTES, Flags: PUBLIC - UNIT_FIELD_ATTACK_POWER = OBJECT_END + 0x00CC, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_ATTACK_POWER_MODS = OBJECT_END + 0x00CD, // Size: 1, Type: TWO_SHORT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_ATTACK_POWER_MULTIPLIER = OBJECT_END + 0x00CE, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_RANGED_ATTACK_POWER = OBJECT_END + 0x00CF, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_RANGED_ATTACK_POWER_MODS = OBJECT_END + 0x00D0, // Size: 1, Type: TWO_SHORT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER = OBJECT_END + 0x00D1, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_MINRANGEDDAMAGE = OBJECT_END + 0x00D2, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_MAXRANGEDDAMAGE = OBJECT_END + 0x00D3, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_POWER_COST_MODIFIER = OBJECT_END + 0x00D4, // Size: 7, Type: INT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_POWER_COST_MULTIPLIER = OBJECT_END + 0x00DB, // Size: 7, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_MAXHEALTHMODIFIER = OBJECT_END + 0x00E2, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY - UNIT_FIELD_PADDING = OBJECT_END + 0x00E3, // Size: 1, Type: INT, Flags: NONE - UNIT_END = OBJECT_END + 0x00E4, + UNIT_FIELD_BYTES_0 = OBJECT_END + 0x0010, // Size: 1, Type: BYTES, Flags: PUBLIC + UNIT_FIELD_HEALTH = OBJECT_END + 0x0011, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_POWER1 = OBJECT_END + 0x0012, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_POWER2 = OBJECT_END + 0x0013, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_POWER3 = OBJECT_END + 0x0014, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_POWER4 = OBJECT_END + 0x0015, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_POWER5 = OBJECT_END + 0x0016, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_POWER6 = OBJECT_END + 0x0017, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_POWER7 = OBJECT_END + 0x0018, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_MAXHEALTH = OBJECT_END + 0x0019, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_MAXPOWER1 = OBJECT_END + 0x001A, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_MAXPOWER2 = OBJECT_END + 0x001B, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_MAXPOWER3 = OBJECT_END + 0x001C, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_MAXPOWER4 = OBJECT_END + 0x001D, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_MAXPOWER5 = OBJECT_END + 0x001E, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_MAXPOWER6 = OBJECT_END + 0x001F, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_MAXPOWER7 = OBJECT_END + 0x0020, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER = OBJECT_END + 0x0021, // Size: 7, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER = OBJECT_END + 0x0028, // Size: 7, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_LEVEL = OBJECT_END + 0x002F, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_FACTIONTEMPLATE = OBJECT_END + 0x0030, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_VIRTUAL_ITEM_SLOT_ID = OBJECT_END + 0x0031, // Size: 3, Type: INT, Flags: PUBLIC + UNIT_FIELD_FLAGS = OBJECT_END + 0x0034, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_FLAGS_2 = OBJECT_END + 0x0035, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_AURASTATE = OBJECT_END + 0x0036, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_BASEATTACKTIME = OBJECT_END + 0x0037, // Size: 2, Type: INT, Flags: PUBLIC + UNIT_FIELD_RANGEDATTACKTIME = OBJECT_END + 0x0039, // Size: 1, Type: INT, Flags: PRIVATE + UNIT_FIELD_BOUNDINGRADIUS = OBJECT_END + 0x003A, // Size: 1, Type: FLOAT, Flags: PUBLIC + UNIT_FIELD_COMBATREACH = OBJECT_END + 0x003B, // Size: 1, Type: FLOAT, Flags: PUBLIC + UNIT_FIELD_DISPLAYID = OBJECT_END + 0x003C, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_NATIVEDISPLAYID = OBJECT_END + 0x003D, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_MOUNTDISPLAYID = OBJECT_END + 0x003E, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_MINDAMAGE = OBJECT_END + 0x003F, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY, UNK3 + UNIT_FIELD_MAXDAMAGE = OBJECT_END + 0x0040, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY, UNK3 + UNIT_FIELD_MINOFFHANDDAMAGE = OBJECT_END + 0x0041, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY, UNK3 + UNIT_FIELD_MAXOFFHANDDAMAGE = OBJECT_END + 0x0042, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY, UNK3 + UNIT_FIELD_BYTES_1 = OBJECT_END + 0x0043, // Size: 1, Type: BYTES, Flags: PUBLIC + UNIT_FIELD_PETNUMBER = OBJECT_END + 0x0044, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_PET_NAME_TIMESTAMP = OBJECT_END + 0x0045, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_PETEXPERIENCE = OBJECT_END + 0x0046, // Size: 1, Type: INT, Flags: OWNER_ONLY + UNIT_FIELD_PETNEXTLEVELEXP = OBJECT_END + 0x0047, // Size: 1, Type: INT, Flags: OWNER_ONLY + UNIT_DYNAMIC_FLAGS = OBJECT_END + 0x0048, // Size: 1, Type: INT, Flags: DYNAMIC + UNIT_CHANNEL_SPELL = OBJECT_END + 0x0049, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_MOD_CAST_SPEED = OBJECT_END + 0x004A, // Size: 1, Type: FLOAT, Flags: PUBLIC + UNIT_CREATED_BY_SPELL = OBJECT_END + 0x004B, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_NPC_FLAGS = OBJECT_END + 0x004C, // Size: 1, Type: INT, Flags: DYNAMIC + UNIT_NPC_EMOTESTATE = OBJECT_END + 0x004D, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_STAT0 = OBJECT_END + 0x004E, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_STAT1 = OBJECT_END + 0x004F, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_STAT2 = OBJECT_END + 0x0050, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_STAT3 = OBJECT_END + 0x0051, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_STAT4 = OBJECT_END + 0x0052, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_POSSTAT0 = OBJECT_END + 0x0053, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_POSSTAT1 = OBJECT_END + 0x0054, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_POSSTAT2 = OBJECT_END + 0x0055, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_POSSTAT3 = OBJECT_END + 0x0056, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_POSSTAT4 = OBJECT_END + 0x0057, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_NEGSTAT0 = OBJECT_END + 0x0058, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_NEGSTAT1 = OBJECT_END + 0x0059, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_NEGSTAT2 = OBJECT_END + 0x005A, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_NEGSTAT3 = OBJECT_END + 0x005B, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_NEGSTAT4 = OBJECT_END + 0x005C, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_RESISTANCES = OBJECT_END + 0x005D, // Size: 7, Type: INT, Flags: PRIVATE, OWNER_ONLY, UNK3 + UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE = OBJECT_END + 0x0064, // Size: 7, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE = OBJECT_END + 0x006B, // Size: 7, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_BASE_MANA = OBJECT_END + 0x0072, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_BASE_HEALTH = OBJECT_END + 0x0073, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_BYTES_2 = OBJECT_END + 0x0074, // Size: 1, Type: BYTES, Flags: PUBLIC + UNIT_FIELD_ATTACK_POWER = OBJECT_END + 0x0075, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_ATTACK_POWER_MODS = OBJECT_END + 0x0076, // Size: 1, Type: TWO_SHORT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_ATTACK_POWER_MULTIPLIER = OBJECT_END + 0x0077, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_RANGED_ATTACK_POWER = OBJECT_END + 0x0078, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_RANGED_ATTACK_POWER_MODS = OBJECT_END + 0x0079, // Size: 1, Type: TWO_SHORT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER = OBJECT_END + 0x007A, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_MINRANGEDDAMAGE = OBJECT_END + 0x007B, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_MAXRANGEDDAMAGE = OBJECT_END + 0x007C, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_POWER_COST_MODIFIER = OBJECT_END + 0x007D, // Size: 7, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_POWER_COST_MULTIPLIER = OBJECT_END + 0x0084, // Size: 7, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_MAXHEALTHMODIFIER = OBJECT_END + 0x008B, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_HOVERHEIGHT = OBJECT_END + 0x008C, // Size: 1, Type: FLOAT, Flags: PUBLIC + UNIT_FIELD_PADDING = OBJECT_END + 0x008D, // Size: 1, Type: INT, Flags: NONE + UNIT_END = OBJECT_END + 0x008E, PLAYER_DUEL_ARBITER = UNIT_END + 0x0000, // Size: 2, Type: LONG, Flags: PUBLIC PLAYER_FLAGS = UNIT_END + 0x0002, // Size: 1, Type: INT, Flags: PUBLIC @@ -262,160 +287,184 @@ enum EUnitFields PLAYER_QUEST_LOG_25_3 = UNIT_END + 0x006C, // Size: 1, Type: BYTES, Flags: PRIVATE PLAYER_QUEST_LOG_25_4 = UNIT_END + 0x006D, // Size: 1, Type: INT, Flags: PRIVATE PLAYER_VISIBLE_ITEM_1_CREATOR = UNIT_END + 0x006E, // Size: 2, Type: LONG, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_1_0 = UNIT_END + 0x0070, // Size: 12, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_1_PROPERTIES = UNIT_END + 0x007C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_1_PAD = UNIT_END + 0x007D, // Size: 1, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_2_CREATOR = UNIT_END + 0x007E, // Size: 2, Type: LONG, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_2_0 = UNIT_END + 0x0080, // Size: 12, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_2_PROPERTIES = UNIT_END + 0x008C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_2_PAD = UNIT_END + 0x008D, // Size: 1, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_3_CREATOR = UNIT_END + 0x008E, // Size: 2, Type: LONG, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_3_0 = UNIT_END + 0x0090, // Size: 12, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_3_PROPERTIES = UNIT_END + 0x009C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_3_PAD = UNIT_END + 0x009D, // Size: 1, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_4_CREATOR = UNIT_END + 0x009E, // Size: 2, Type: LONG, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_4_0 = UNIT_END + 0x00A0, // Size: 12, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_4_PROPERTIES = UNIT_END + 0x00AC, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_4_PAD = UNIT_END + 0x00AD, // Size: 1, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_5_CREATOR = UNIT_END + 0x00AE, // Size: 2, Type: LONG, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_5_0 = UNIT_END + 0x00B0, // Size: 12, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_5_PROPERTIES = UNIT_END + 0x00BC, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_5_PAD = UNIT_END + 0x00BD, // Size: 1, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_6_CREATOR = UNIT_END + 0x00BE, // Size: 2, Type: LONG, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_6_0 = UNIT_END + 0x00C0, // Size: 12, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_6_PROPERTIES = UNIT_END + 0x00CC, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_6_PAD = UNIT_END + 0x00CD, // Size: 1, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_7_CREATOR = UNIT_END + 0x00CE, // Size: 2, Type: LONG, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_7_0 = UNIT_END + 0x00D0, // Size: 12, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_7_PROPERTIES = UNIT_END + 0x00DC, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_7_PAD = UNIT_END + 0x00DD, // Size: 1, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_8_CREATOR = UNIT_END + 0x00DE, // Size: 2, Type: LONG, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_8_0 = UNIT_END + 0x00E0, // Size: 12, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_8_PROPERTIES = UNIT_END + 0x00EC, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_8_PAD = UNIT_END + 0x00ED, // Size: 1, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_9_CREATOR = UNIT_END + 0x00EE, // Size: 2, Type: LONG, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_9_0 = UNIT_END + 0x00F0, // Size: 12, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_9_PROPERTIES = UNIT_END + 0x00FC, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_9_PAD = UNIT_END + 0x00FD, // Size: 1, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_10_CREATOR = UNIT_END + 0x00FE, // Size: 2, Type: LONG, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_10_0 = UNIT_END + 0x0100, // Size: 12, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_10_PROPERTIES = UNIT_END + 0x010C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_10_PAD = UNIT_END + 0x010D, // Size: 1, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_11_CREATOR = UNIT_END + 0x010E, // Size: 2, Type: LONG, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_11_0 = UNIT_END + 0x0110, // Size: 12, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_11_PROPERTIES = UNIT_END + 0x011C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_11_PAD = UNIT_END + 0x011D, // Size: 1, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_12_CREATOR = UNIT_END + 0x011E, // Size: 2, Type: LONG, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_12_0 = UNIT_END + 0x0120, // Size: 12, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_12_PROPERTIES = UNIT_END + 0x012C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_12_PAD = UNIT_END + 0x012D, // Size: 1, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_13_CREATOR = UNIT_END + 0x012E, // Size: 2, Type: LONG, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_13_0 = UNIT_END + 0x0130, // Size: 12, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_13_PROPERTIES = UNIT_END + 0x013C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_13_PAD = UNIT_END + 0x013D, // Size: 1, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_14_CREATOR = UNIT_END + 0x013E, // Size: 2, Type: LONG, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_14_0 = UNIT_END + 0x0140, // Size: 12, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_14_PROPERTIES = UNIT_END + 0x014C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_14_PAD = UNIT_END + 0x014D, // Size: 1, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_15_CREATOR = UNIT_END + 0x014E, // Size: 2, Type: LONG, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_15_0 = UNIT_END + 0x0150, // Size: 12, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_15_PROPERTIES = UNIT_END + 0x015C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_15_PAD = UNIT_END + 0x015D, // Size: 1, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_16_CREATOR = UNIT_END + 0x015E, // Size: 2, Type: LONG, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_16_0 = UNIT_END + 0x0160, // Size: 12, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_16_PROPERTIES = UNIT_END + 0x016C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_16_PAD = UNIT_END + 0x016D, // Size: 1, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_17_CREATOR = UNIT_END + 0x016E, // Size: 2, Type: LONG, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_17_0 = UNIT_END + 0x0170, // Size: 12, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_17_PROPERTIES = UNIT_END + 0x017C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_17_PAD = UNIT_END + 0x017D, // Size: 1, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_18_CREATOR = UNIT_END + 0x017E, // Size: 2, Type: LONG, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_18_0 = UNIT_END + 0x0180, // Size: 12, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_18_PROPERTIES = UNIT_END + 0x018C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_18_PAD = UNIT_END + 0x018D, // Size: 1, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_19_CREATOR = UNIT_END + 0x018E, // Size: 2, Type: LONG, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_19_0 = UNIT_END + 0x0190, // Size: 12, Type: INT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_19_PROPERTIES = UNIT_END + 0x019C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC - PLAYER_VISIBLE_ITEM_19_PAD = UNIT_END + 0x019D, // Size: 1, Type: INT, Flags: PUBLIC - PLAYER_CHOSEN_TITLE = UNIT_END + 0x019E, // Size: 1, Type: INT, Flags: PUBLIC - PLAYER_FIELD_PAD_0 = UNIT_END + 0x019F, // Size: 1, Type: INT, Flags: NONE - PLAYER_FIELD_INV_SLOT_HEAD = UNIT_END + 0x01A0, // Size: 46, Type: LONG, Flags: PRIVATE - PLAYER_FIELD_PACK_SLOT_1 = UNIT_END + 0x01CE, // Size: 32, Type: LONG, Flags: PRIVATE - PLAYER_FIELD_BANK_SLOT_1 = UNIT_END + 0x01EE, // Size: 56, Type: LONG, Flags: PRIVATE - PLAYER_FIELD_BANKBAG_SLOT_1 = UNIT_END + 0x0226, // Size: 14, Type: LONG, Flags: PRIVATE - PLAYER_FIELD_VENDORBUYBACK_SLOT_1 = UNIT_END + 0x0234, // Size: 24, Type: LONG, Flags: PRIVATE - PLAYER_FIELD_KEYRING_SLOT_1 = UNIT_END + 0x024C, // Size: 64, Type: LONG, Flags: PRIVATE - PLAYER_FIELD_VANITYPET_SLOT_1 = UNIT_END + 0x028C, // Size: 36, Type: LONG, Flags: PRIVATE - PLAYER_FARSIGHT = UNIT_END + 0x02B0, // Size: 2, Type: LONG, Flags: PRIVATE - PLAYER__FIELD_KNOWN_TITLES = UNIT_END + 0x02B2, // Size: 2, Type: LONG, Flags: PRIVATE - PLAYER_XP = UNIT_END + 0x02B4, // Size: 1, Type: INT, Flags: PRIVATE - PLAYER_NEXT_LEVEL_XP = UNIT_END + 0x02B5, // Size: 1, Type: INT, Flags: PRIVATE - PLAYER_SKILL_INFO_1_1 = UNIT_END + 0x02B6, // Size: 384, Type: TWO_SHORT, Flags: PRIVATE - PLAYER_CHARACTER_POINTS1 = UNIT_END + 0x0436, // Size: 1, Type: INT, Flags: PRIVATE - PLAYER_CHARACTER_POINTS2 = UNIT_END + 0x0437, // Size: 1, Type: INT, Flags: PRIVATE - PLAYER_TRACK_CREATURES = UNIT_END + 0x0438, // Size: 1, Type: INT, Flags: PRIVATE - PLAYER_TRACK_RESOURCES = UNIT_END + 0x0439, // Size: 1, Type: INT, Flags: PRIVATE - PLAYER_BLOCK_PERCENTAGE = UNIT_END + 0x043A, // Size: 1, Type: FLOAT, Flags: PRIVATE - PLAYER_DODGE_PERCENTAGE = UNIT_END + 0x043B, // Size: 1, Type: FLOAT, Flags: PRIVATE - PLAYER_PARRY_PERCENTAGE = UNIT_END + 0x043C, // Size: 1, Type: FLOAT, Flags: PRIVATE - PLAYER_EXPERTISE = UNIT_END + 0x043D, // Size: 1, Type: INT, Flags: PRIVATE - PLAYER_OFFHAND_EXPERTISE = UNIT_END + 0x043E, // Size: 1, Type: INT, Flags: PRIVATE - PLAYER_CRIT_PERCENTAGE = UNIT_END + 0x043F, // Size: 1, Type: FLOAT, Flags: PRIVATE - PLAYER_RANGED_CRIT_PERCENTAGE = UNIT_END + 0x0440, // Size: 1, Type: FLOAT, Flags: PRIVATE - PLAYER_OFFHAND_CRIT_PERCENTAGE = UNIT_END + 0x0441, // Size: 1, Type: FLOAT, Flags: PRIVATE - PLAYER_SPELL_CRIT_PERCENTAGE1 = UNIT_END + 0x0442, // Size: 7, Type: FLOAT, Flags: PRIVATE - PLAYER_SHIELD_BLOCK = UNIT_END + 0x0449, // Size: 1, Type: INT, Flags: PRIVATE - PLAYER_EXPLORED_ZONES_1 = UNIT_END + 0x044A, // Size: 128, Type: BYTES, Flags: PRIVATE - PLAYER_REST_STATE_EXPERIENCE = UNIT_END + 0x04CA, // Size: 1, Type: INT, Flags: PRIVATE - PLAYER_FIELD_COINAGE = UNIT_END + 0x04CB, // Size: 1, Type: INT, Flags: PRIVATE - PLAYER_FIELD_MOD_DAMAGE_DONE_POS = UNIT_END + 0x04CC, // Size: 7, Type: INT, Flags: PRIVATE - PLAYER_FIELD_MOD_DAMAGE_DONE_NEG = UNIT_END + 0x04D3, // Size: 7, Type: INT, Flags: PRIVATE - PLAYER_FIELD_MOD_DAMAGE_DONE_PCT = UNIT_END + 0x04DA, // Size: 7, Type: INT, Flags: PRIVATE - PLAYER_FIELD_MOD_HEALING_DONE_POS = UNIT_END + 0x04E1, // Size: 1, Type: INT, Flags: PRIVATE - PLAYER_FIELD_MOD_TARGET_RESISTANCE = UNIT_END + 0x04E2, // Size: 1, Type: INT, Flags: PRIVATE - PLAYER_FIELD_MOD_TARGET_PHYSICAL_RESISTANCE = UNIT_END + 0x04E3, // Size: 1, Type: INT, Flags: PRIVATE - PLAYER_FIELD_BYTES = UNIT_END + 0x04E4, // Size: 1, Type: BYTES, Flags: PRIVATE - PLAYER_AMMO_ID = UNIT_END + 0x04E5, // Size: 1, Type: INT, Flags: PRIVATE - PLAYER_SELF_RES_SPELL = UNIT_END + 0x04E6, // Size: 1, Type: INT, Flags: PRIVATE - PLAYER_FIELD_PVP_MEDALS = UNIT_END + 0x04E7, // Size: 1, Type: INT, Flags: PRIVATE - PLAYER_FIELD_BUYBACK_PRICE_1 = UNIT_END + 0x04E8, // Size: 12, Type: INT, Flags: PRIVATE - PLAYER_FIELD_BUYBACK_TIMESTAMP_1 = UNIT_END + 0x04F4, // Size: 12, Type: INT, Flags: PRIVATE - PLAYER_FIELD_KILLS = UNIT_END + 0x0500, // Size: 1, Type: TWO_SHORT, Flags: PRIVATE - PLAYER_FIELD_TODAY_CONTRIBUTION = UNIT_END + 0x0501, // Size: 1, Type: INT, Flags: PRIVATE - PLAYER_FIELD_YESTERDAY_CONTRIBUTION = UNIT_END + 0x0502, // Size: 1, Type: INT, Flags: PRIVATE - PLAYER_FIELD_LIFETIME_HONORBALE_KILLS = UNIT_END + 0x0503, // Size: 1, Type: INT, Flags: PRIVATE - PLAYER_FIELD_BYTES2 = UNIT_END + 0x0504, // Size: 1, Type: BYTES, Flags: PRIVATE - PLAYER_FIELD_WATCHED_FACTION_INDEX = UNIT_END + 0x0505, // Size: 1, Type: INT, Flags: PRIVATE - PLAYER_FIELD_COMBAT_RATING_1 = UNIT_END + 0x0506, // Size: 24, Type: INT, Flags: PRIVATE - PLAYER_FIELD_ARENA_TEAM_INFO_1_1 = UNIT_END + 0x051E, // Size: 18, Type: INT, Flags: PRIVATE - PLAYER_FIELD_HONOR_CURRENCY = UNIT_END + 0x0530, // Size: 1, Type: INT, Flags: PRIVATE - PLAYER_FIELD_ARENA_CURRENCY = UNIT_END + 0x0531, // Size: 1, Type: INT, Flags: PRIVATE - PLAYER_FIELD_MOD_MANA_REGEN = UNIT_END + 0x0532, // Size: 1, Type: FLOAT, Flags: PRIVATE - PLAYER_FIELD_MOD_MANA_REGEN_INTERRUPT = UNIT_END + 0x0533, // Size: 1, Type: FLOAT, Flags: PRIVATE - PLAYER_FIELD_MAX_LEVEL = UNIT_END + 0x0534, // Size: 1, Type: INT, Flags: PRIVATE - PLAYER_FIELD_DAILY_QUESTS_1 = UNIT_END + 0x0535, // Size: 25, Type: INT, Flags: PRIVATE - PLAYER_END = UNIT_END + 0x054E, + PLAYER_VISIBLE_ITEM_1_0 = UNIT_END + 0x0070, // Size: 13, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_1_PROPERTIES = UNIT_END + 0x007D, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_1_SEED = UNIT_END + 0x007E, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_1_PAD = UNIT_END + 0x007F, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_2_CREATOR = UNIT_END + 0x0080, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_2_0 = UNIT_END + 0x0082, // Size: 13, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_2_PROPERTIES = UNIT_END + 0x008F, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_2_SEED = UNIT_END + 0x0090, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_2_PAD = UNIT_END + 0x0091, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_3_CREATOR = UNIT_END + 0x0092, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_3_0 = UNIT_END + 0x0094, // Size: 13, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_3_PROPERTIES = UNIT_END + 0x00A1, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_3_SEED = UNIT_END + 0x00A2, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_3_PAD = UNIT_END + 0x00A3, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_4_CREATOR = UNIT_END + 0x00A4, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_4_0 = UNIT_END + 0x00A6, // Size: 13, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_4_PROPERTIES = UNIT_END + 0x00B3, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_4_SEED = UNIT_END + 0x00B4, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_4_PAD = UNIT_END + 0x00B5, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_5_CREATOR = UNIT_END + 0x00B6, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_5_0 = UNIT_END + 0x00B8, // Size: 13, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_5_PROPERTIES = UNIT_END + 0x00C5, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_5_SEED = UNIT_END + 0x00C6, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_5_PAD = UNIT_END + 0x00C7, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_6_CREATOR = UNIT_END + 0x00C8, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_6_0 = UNIT_END + 0x00CA, // Size: 13, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_6_PROPERTIES = UNIT_END + 0x00D7, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_6_SEED = UNIT_END + 0x00D8, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_6_PAD = UNIT_END + 0x00D9, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_7_CREATOR = UNIT_END + 0x00DA, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_7_0 = UNIT_END + 0x00DC, // Size: 13, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_7_PROPERTIES = UNIT_END + 0x00E9, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_7_SEED = UNIT_END + 0x00EA, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_7_PAD = UNIT_END + 0x00EB, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_8_CREATOR = UNIT_END + 0x00EC, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_8_0 = UNIT_END + 0x00EE, // Size: 13, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_8_PROPERTIES = UNIT_END + 0x00FB, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_8_SEED = UNIT_END + 0x00FC, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_8_PAD = UNIT_END + 0x00FD, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_9_CREATOR = UNIT_END + 0x00FE, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_9_0 = UNIT_END + 0x0100, // Size: 13, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_9_PROPERTIES = UNIT_END + 0x010D, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_9_SEED = UNIT_END + 0x010E, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_9_PAD = UNIT_END + 0x010F, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_10_CREATOR = UNIT_END + 0x0110, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_10_0 = UNIT_END + 0x0112, // Size: 13, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_10_PROPERTIES = UNIT_END + 0x011F, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_10_SEED = UNIT_END + 0x0120, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_10_PAD = UNIT_END + 0x0121, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_11_CREATOR = UNIT_END + 0x0122, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_11_0 = UNIT_END + 0x0124, // Size: 13, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_11_PROPERTIES = UNIT_END + 0x0131, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_11_SEED = UNIT_END + 0x0132, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_11_PAD = UNIT_END + 0x0133, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_12_CREATOR = UNIT_END + 0x0134, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_12_0 = UNIT_END + 0x0136, // Size: 13, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_12_PROPERTIES = UNIT_END + 0x0143, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_12_SEED = UNIT_END + 0x0144, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_12_PAD = UNIT_END + 0x0145, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_13_CREATOR = UNIT_END + 0x0146, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_13_0 = UNIT_END + 0x0148, // Size: 13, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_13_PROPERTIES = UNIT_END + 0x0155, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_13_SEED = UNIT_END + 0x0156, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_13_PAD = UNIT_END + 0x0157, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_14_CREATOR = UNIT_END + 0x0158, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_14_0 = UNIT_END + 0x015A, // Size: 13, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_14_PROPERTIES = UNIT_END + 0x0167, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_14_SEED = UNIT_END + 0x0168, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_14_PAD = UNIT_END + 0x0169, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_15_CREATOR = UNIT_END + 0x016A, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_15_0 = UNIT_END + 0x016C, // Size: 13, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_15_PROPERTIES = UNIT_END + 0x0179, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_15_SEED = UNIT_END + 0x017A, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_15_PAD = UNIT_END + 0x017B, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_16_CREATOR = UNIT_END + 0x017C, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_16_0 = UNIT_END + 0x017E, // Size: 13, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_16_PROPERTIES = UNIT_END + 0x018B, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_16_SEED = UNIT_END + 0x018C, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_16_PAD = UNIT_END + 0x018D, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_17_CREATOR = UNIT_END + 0x018E, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_17_0 = UNIT_END + 0x0190, // Size: 13, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_17_PROPERTIES = UNIT_END + 0x019D, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_17_SEED = UNIT_END + 0x019E, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_17_PAD = UNIT_END + 0x019F, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_18_CREATOR = UNIT_END + 0x01A0, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_18_0 = UNIT_END + 0x01A2, // Size: 13, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_18_PROPERTIES = UNIT_END + 0x01AF, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_18_SEED = UNIT_END + 0x01B0, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_18_PAD = UNIT_END + 0x01B1, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_19_CREATOR = UNIT_END + 0x01B2, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_19_0 = UNIT_END + 0x01B4, // Size: 13, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_19_PROPERTIES = UNIT_END + 0x01C1, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_19_SEED = UNIT_END + 0x01C2, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_19_PAD = UNIT_END + 0x01C3, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_CHOSEN_TITLE = UNIT_END + 0x01C4, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_FIELD_PAD_0 = UNIT_END + 0x01C5, // Size: 1, Type: INT, Flags: NONE + PLAYER_FIELD_INV_SLOT_HEAD = UNIT_END + 0x01C6, // Size: 46, Type: LONG, Flags: PRIVATE + PLAYER_FIELD_PACK_SLOT_1 = UNIT_END + 0x01F4, // Size: 32, Type: LONG, Flags: PRIVATE + PLAYER_FIELD_BANK_SLOT_1 = UNIT_END + 0x0214, // Size: 56, Type: LONG, Flags: PRIVATE + PLAYER_FIELD_BANKBAG_SLOT_1 = UNIT_END + 0x024C, // Size: 14, Type: LONG, Flags: PRIVATE + PLAYER_FIELD_VENDORBUYBACK_SLOT_1 = UNIT_END + 0x025A, // Size: 24, Type: LONG, Flags: PRIVATE + PLAYER_FIELD_KEYRING_SLOT_1 = UNIT_END + 0x0272, // Size: 64, Type: LONG, Flags: PRIVATE + PLAYER_FIELD_VANITYPET_SLOT_1 = UNIT_END + 0x02B2, // Size: 36, Type: LONG, Flags: PRIVATE + PLAYER_FIELD_CURRENCYTOKEN_SLOT_1 = UNIT_END + 0x02D6, // Size: 64, Type: LONG, Flags: PRIVATE + PLAYER_FIELD_QUESTBAG_SLOT_1 = UNIT_END + 0x0316, // Size: 64, Type: LONG, Flags: PRIVATE + PLAYER_FARSIGHT = UNIT_END + 0x0356, // Size: 2, Type: LONG, Flags: PRIVATE + PLAYER__FIELD_KNOWN_TITLES = UNIT_END + 0x0358, // Size: 2, Type: LONG, Flags: PRIVATE + PLAYER__FIELD_KNOWN_TITLES1 = UNIT_END + 0x035A, // Size: 2, Type: LONG, Flags: PRIVATE + PLAYER_FIELD_KNOWN_CURRENCIES = UNIT_END + 0x035C, // Size: 2, Type: LONG, Flags: PRIVATE + PLAYER_XP = UNIT_END + 0x035E, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_NEXT_LEVEL_XP = UNIT_END + 0x035F, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_SKILL_INFO_1_1 = UNIT_END + 0x0360, // Size: 384, Type: TWO_SHORT, Flags: PRIVATE + PLAYER_CHARACTER_POINTS1 = UNIT_END + 0x04E0, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_CHARACTER_POINTS2 = UNIT_END + 0x04E1, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_TRACK_CREATURES = UNIT_END + 0x04E2, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_TRACK_RESOURCES = UNIT_END + 0x04E3, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_BLOCK_PERCENTAGE = UNIT_END + 0x04E4, // Size: 1, Type: FLOAT, Flags: PRIVATE + PLAYER_DODGE_PERCENTAGE = UNIT_END + 0x04E5, // Size: 1, Type: FLOAT, Flags: PRIVATE + PLAYER_PARRY_PERCENTAGE = UNIT_END + 0x04E6, // Size: 1, Type: FLOAT, Flags: PRIVATE + PLAYER_EXPERTISE = UNIT_END + 0x04E7, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_OFFHAND_EXPERTISE = UNIT_END + 0x04E8, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_CRIT_PERCENTAGE = UNIT_END + 0x04E9, // Size: 1, Type: FLOAT, Flags: PRIVATE + PLAYER_RANGED_CRIT_PERCENTAGE = UNIT_END + 0x04EA, // Size: 1, Type: FLOAT, Flags: PRIVATE + PLAYER_OFFHAND_CRIT_PERCENTAGE = UNIT_END + 0x04EB, // Size: 1, Type: FLOAT, Flags: PRIVATE + PLAYER_SPELL_CRIT_PERCENTAGE1 = UNIT_END + 0x04EC, // Size: 7, Type: FLOAT, Flags: PRIVATE + PLAYER_SHIELD_BLOCK = UNIT_END + 0x04F3, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_SHIELD_BLOCK_CRIT_PERCENTAGE = UNIT_END + 0x04F4, // Size: 1, Type: FLOAT, Flags: PRIVATE + PLAYER_EXPLORED_ZONES_1 = UNIT_END + 0x04F5, // Size: 128, Type: BYTES, Flags: PRIVATE + PLAYER_REST_STATE_EXPERIENCE = UNIT_END + 0x0575, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_COINAGE = UNIT_END + 0x0576, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_MOD_DAMAGE_DONE_POS = UNIT_END + 0x0577, // Size: 7, Type: INT, Flags: PRIVATE + PLAYER_FIELD_MOD_DAMAGE_DONE_NEG = UNIT_END + 0x057E, // Size: 7, Type: INT, Flags: PRIVATE + PLAYER_FIELD_MOD_DAMAGE_DONE_PCT = UNIT_END + 0x0585, // Size: 7, Type: INT, Flags: PRIVATE + PLAYER_FIELD_MOD_HEALING_DONE_POS = UNIT_END + 0x058C, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_MOD_TARGET_RESISTANCE = UNIT_END + 0x058D, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_MOD_TARGET_PHYSICAL_RESISTANCE = UNIT_END + 0x058E, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_BYTES = UNIT_END + 0x058F, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_AMMO_ID = UNIT_END + 0x0590, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_SELF_RES_SPELL = UNIT_END + 0x0591, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_PVP_MEDALS = UNIT_END + 0x0592, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_BUYBACK_PRICE_1 = UNIT_END + 0x0593, // Size: 12, Type: INT, Flags: PRIVATE + PLAYER_FIELD_BUYBACK_TIMESTAMP_1 = UNIT_END + 0x059F, // Size: 12, Type: INT, Flags: PRIVATE + PLAYER_FIELD_KILLS = UNIT_END + 0x05AB, // Size: 1, Type: TWO_SHORT, Flags: PRIVATE + PLAYER_FIELD_TODAY_CONTRIBUTION = UNIT_END + 0x05AC, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_YESTERDAY_CONTRIBUTION = UNIT_END + 0x05AD, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_LIFETIME_HONORBALE_KILLS = UNIT_END + 0x05AE, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_BYTES2 = UNIT_END + 0x05AF, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_FIELD_WATCHED_FACTION_INDEX = UNIT_END + 0x05B0, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_COMBAT_RATING_1 = UNIT_END + 0x05B1, // Size: 25, Type: INT, Flags: PRIVATE + PLAYER_FIELD_ARENA_TEAM_INFO_1_1 = UNIT_END + 0x05CA, // Size: 18, Type: INT, Flags: PRIVATE + PLAYER_FIELD_HONOR_CURRENCY = UNIT_END + 0x05DC, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_ARENA_CURRENCY = UNIT_END + 0x05DD, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_MAX_LEVEL = UNIT_END + 0x05DE, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_DAILY_QUESTS_1 = UNIT_END + 0x05DF, // Size: 25, Type: INT, Flags: PRIVATE + PLAYER_RUNE_REGEN_1 = UNIT_END + 0x05F8, // Size: 4, Type: FLOAT, Flags: PRIVATE + PLAYER_NO_REAGENT_COST_1 = UNIT_END + 0x05FC, // Size: 3, Type: INT, Flags: PRIVATE + PLAYER_FIELD_GLYPH_SLOTS_1 = UNIT_END + 0x05FF, // Size: 8, Type: INT, Flags: PRIVATE + PLAYER_FIELD_GLYPHS_1 = UNIT_END + 0x0607, // Size: 8, Type: INT, Flags: PRIVATE + PLAYER_GLYPHS_ENABLED = UNIT_END + 0x060F, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_END = UNIT_END + 0x0610, }; enum EGameObjectFields { OBJECT_FIELD_CREATED_BY = OBJECT_END + 0x0000, // Size: 2, Type: LONG, Flags: PUBLIC GAMEOBJECT_DISPLAYID = OBJECT_END + 0x0002, // Size: 1, Type: INT, Flags: PUBLIC - GAMEOBJECT_FLAGS = OBJECT_END + 0x0003, // Size: 1, Type: INT, Flags: PUBLIC - GAMEOBJECT_ROTATION = OBJECT_END + 0x0004, // Size: 4, Type: FLOAT, Flags: PUBLIC - GAMEOBJECT_STATE = OBJECT_END + 0x0008, // Size: 1, Type: INT, Flags: PUBLIC - GAMEOBJECT_POS_X = OBJECT_END + 0x0009, // Size: 1, Type: FLOAT, Flags: PUBLIC - GAMEOBJECT_POS_Y = OBJECT_END + 0x000A, // Size: 1, Type: FLOAT, Flags: PUBLIC - GAMEOBJECT_POS_Z = OBJECT_END + 0x000B, // Size: 1, Type: FLOAT, Flags: PUBLIC - GAMEOBJECT_FACING = OBJECT_END + 0x000C, // Size: 1, Type: FLOAT, Flags: PUBLIC - GAMEOBJECT_DYN_FLAGS = OBJECT_END + 0x000D, // Size: 1, Type: INT, Flags: DYNAMIC - GAMEOBJECT_FACTION = OBJECT_END + 0x000E, // Size: 1, Type: INT, Flags: PUBLIC - GAMEOBJECT_TYPE_ID = OBJECT_END + 0x000F, // Size: 1, Type: INT, Flags: PUBLIC + GAMEOBJECT_FLAGS = OBJECT_END + 0x0003, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + GAMEOBJECT_ROTATION = OBJECT_END + 0x0004, // Size: 2, Type: LONG, Flags: PUBLIC + GAMEOBJECT_PARENTROTATION = OBJECT_END + 0x0006, // Size: 4, Type: FLOAT, Flags: PUBLIC + GAMEOBJECT_POS_X = OBJECT_END + 0x000A, // Size: 1, Type: FLOAT, Flags: PUBLIC + GAMEOBJECT_POS_Y = OBJECT_END + 0x000B, // Size: 1, Type: FLOAT, Flags: PUBLIC + GAMEOBJECT_POS_Z = OBJECT_END + 0x000C, // Size: 1, Type: FLOAT, Flags: PUBLIC + GAMEOBJECT_FACING = OBJECT_END + 0x000D, // Size: 1, Type: FLOAT, Flags: PUBLIC + GAMEOBJECT_DYNAMIC = OBJECT_END + 0x000E, // Size: 1, Type: TWO_SHORT, Flags: DYNAMIC + GAMEOBJECT_FACTION = OBJECT_END + 0x000F, // Size: 1, Type: INT, Flags: PUBLIC GAMEOBJECT_LEVEL = OBJECT_END + 0x0010, // Size: 1, Type: INT, Flags: PUBLIC - GAMEOBJECT_ARTKIT = OBJECT_END + 0x0011, // Size: 1, Type: INT, Flags: PUBLIC - GAMEOBJECT_ANIMPROGRESS = OBJECT_END + 0x0012, // Size: 1, Type: INT, Flags: DYNAMIC - GAMEOBJECT_PADDING = OBJECT_END + 0x0013, // Size: 1, Type: INT, Flags: NONE - GAMEOBJECT_END = OBJECT_END + 0x0014, + GAMEOBJECT_BYTES_1 = OBJECT_END + 0x0011, // Size: 1, Type: BYTES, Flags: PUBLIC + GAMEOBJECT_END = OBJECT_END + 0x0012, }; enum EDynamicObjectFields diff --git a/src/game/UpdateMask.h b/src/game/UpdateMask.h index 436aa523440..3d510c00d60 100644 --- a/src/game/UpdateMask.h +++ b/src/game/UpdateMask.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,27 +36,27 @@ class UpdateMask delete [] mUpdateMask; } - inline void SetBit (uint32 index) + void SetBit (uint32 index) { ( (uint8 *)mUpdateMask )[ index >> 3 ] |= 1 << ( index & 0x7 ); } - inline void UnsetBit (uint32 index) + void UnsetBit (uint32 index) { ( (uint8 *)mUpdateMask )[ index >> 3 ] &= (0xff ^ (1 << ( index & 0x7 ) ) ); } - inline bool GetBit (uint32 index) + bool GetBit (uint32 index) const { return ( ( (uint8 *)mUpdateMask)[ index >> 3 ] & ( 1 << ( index & 0x7 ) )) != 0; } - inline uint32 GetBlockCount() { return mBlocks; } - inline uint32 GetLength() { return mBlocks << 2; } - inline uint32 GetCount() { return mCount; } - inline uint8* GetMask() { return (uint8*)mUpdateMask; } + uint32 GetBlockCount() const { return mBlocks; } + uint32 GetLength() const { return mBlocks << 2; } + uint32 GetCount() const { return mCount; } + uint8* GetMask() { return (uint8*)mUpdateMask; } - inline void SetCount (uint32 valuesCount) + void SetCount (uint32 valuesCount) { if(mUpdateMask) delete [] mUpdateMask; @@ -68,13 +68,13 @@ class UpdateMask memset(mUpdateMask, 0, mBlocks << 2); } - inline void Clear() + void Clear() { if (mUpdateMask) memset(mUpdateMask, 0, mBlocks << 2); } - inline UpdateMask& operator = ( const UpdateMask& mask ) + UpdateMask& operator = ( const UpdateMask& mask ) { SetCount(mask.mCount); memcpy(mUpdateMask, mask.mUpdateMask, mBlocks << 2); @@ -82,21 +82,21 @@ class UpdateMask return *this; } - inline void operator &= ( const UpdateMask& mask ) + void operator &= ( const UpdateMask& mask ) { ASSERT(mask.mCount <= mCount); for (uint32 i = 0; i < mBlocks; i++) mUpdateMask[i] &= mask.mUpdateMask[i]; } - inline void operator |= ( const UpdateMask& mask ) + void operator |= ( const UpdateMask& mask ) { ASSERT(mask.mCount <= mCount); for (uint32 i = 0; i < mBlocks; i++) mUpdateMask[i] |= mask.mUpdateMask[i]; } - inline UpdateMask operator & ( const UpdateMask& mask ) const + UpdateMask operator & ( const UpdateMask& mask ) const { ASSERT(mask.mCount <= mCount); @@ -107,7 +107,7 @@ class UpdateMask return newmask; } - inline UpdateMask operator | ( const UpdateMask& mask ) const + UpdateMask operator | ( const UpdateMask& mask ) const { ASSERT(mask.mCount <= mCount); diff --git a/src/game/Vehicle.cpp b/src/game/Vehicle.cpp new file mode 100644 index 00000000000..377b1d263b2 --- /dev/null +++ b/src/game/Vehicle.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Log.h" +#include "ObjectMgr.h" +#include "Vehicle.h" +#include "Unit.h" +#include "Util.h" + +Vehicle::Vehicle() : Creature(), m_vehicleId(0) +{ + m_summonMask |= SUMMON_MASK_VEHICLE; + m_updateFlag = (UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID | UPDATEFLAG_LIVING | UPDATEFLAG_HAS_POSITION | UPDATEFLAG_VEHICLE); +} + +Vehicle::~Vehicle() +{ +} + +void Vehicle::AddToWorld() +{ + if(!IsInWorld()) + { + ObjectAccessor::Instance().AddObject(this); + Unit::AddToWorld(); + AIM_Initialize(); + } +} + +void Vehicle::RemoveFromWorld() +{ + if(IsInWorld()) + { + ///- Don't call the function for Creature, normal mobs + totems go in a different storage + Unit::RemoveFromWorld(); + ObjectAccessor::Instance().RemoveObject(this); + } +} + +void Vehicle::setDeathState(DeathState s) // overwrite virtual Creature::setDeathState and Unit::setDeathState +{ + if(Unit *charmer = GetCharmer()) + if(charmer->GetTypeId() == TYPEID_PLAYER) + ((Player*)charmer)->ExitVehicle(this); + Creature::setDeathState(s); +} + +void Vehicle::Update(uint32 diff) +{ + Creature::Update(diff); +} + +bool Vehicle::Create(uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, uint32 vehicleId, uint32 team) +{ + SetMapId(map->GetId()); + SetInstanceId(map->GetInstanceId()); + + Object::_Create(guidlow, Entry, HIGHGUID_VEHICLE); + + if(!InitEntry(Entry, team)) + return false; + + m_defaultMovementType = IDLE_MOTION_TYPE; + + SetVehicleId(vehicleId); + + SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); + SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f); + + CreatureInfo const *ci = GetCreatureInfo(); + setFaction(team == ALLIANCE ? ci->faction_A : ci->faction_H); + SetMaxHealth(ci->maxhealth); + SelectLevel(ci); + SetHealth(GetMaxHealth()); + + return true; +} + +void Vehicle::Dismiss() +{ + SendObjectDeSpawnAnim(GetGUID()); + CombatStop(); + CleanupsBeforeDelete(); + AddObjectToRemoveList(); +} + +bool Vehicle::LoadFromDB(uint32 guid, Map *map) +{ + CreatureData const* data = objmgr.GetCreatureData(guid); + + if(!data) + { + sLog.outErrorDb("Creature (GUID: %u) not found in table `creature`, can't load. ",guid); + return false; + } + + uint32 id = 0; + if(const CreatureInfo *cInfo = objmgr.GetCreatureTemplate(data->id)) + id = cInfo->VehicleId; + if(!id || !sVehicleStore.LookupEntry(id)) + return false; + + m_DBTableGuid = guid; + if (map->GetInstanceId() != 0) guid = objmgr.GenerateLowGuid(HIGHGUID_VEHICLE); + + uint16 team = 0; + if(!Create(guid,map,data->phaseMask,data->id,id,team)) + return false; + + Relocate(data->posX,data->posY,data->posZ,data->orientation); + + if(!IsPositionValid()) + { + sLog.outError("Creature (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %f Y: %f)",GetGUIDLow(),GetEntry(),GetPositionX(),GetPositionY()); + return false; + } + //We should set first home position, because then AI calls home movement + SetHomePosition(data->posX,data->posY,data->posZ,data->orientation); + + m_respawnradius = data->spawndist; + + m_respawnDelay = data->spawntimesecs; + m_isDeadByDefault = data->is_dead; + m_deathState = m_isDeadByDefault ? DEAD : ALIVE; + + m_respawnTime = objmgr.GetCreatureRespawnTime(m_DBTableGuid,GetInstanceId()); + if(m_respawnTime > time(NULL)) // not ready to respawn + { + m_deathState = DEAD; + if(canFly()) + { + float tz = GetMap()->GetHeight(data->posX,data->posY,data->posZ,false); + if(data->posZ - tz > 0.1) + Relocate(data->posX,data->posY,tz); + } + } + else if(m_respawnTime) // respawn time set but expired + { + m_respawnTime = 0; + objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),0); + } + + uint32 curhealth = data->curhealth; + if(curhealth) + { + curhealth = uint32(curhealth*_GetHealthMod(GetCreatureInfo()->rank)); + if(curhealth < 1) + curhealth = 1; + } + + SetHealth(m_deathState == ALIVE ? curhealth : 0); + SetPower(POWER_MANA,data->curmana); + + // checked at creature_template loading + m_defaultMovementType = MovementGeneratorType(data->movementType); + + return true; +} diff --git a/src/game/Vehicle.h b/src/game/Vehicle.h new file mode 100644 index 00000000000..06630368798 --- /dev/null +++ b/src/game/Vehicle.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOSSERVER_VEHICLE_H +#define MANGOSSERVER_VEHICLE_H + +#include "ObjectDefines.h" +#include "Creature.h" +#include "Unit.h" + +class Vehicle : public Creature +{ + public: + explicit Vehicle(); + virtual ~Vehicle(); + + void AddToWorld(); + void RemoveFromWorld(); + + bool Create (uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, uint32 vehicleId, uint32 team); + + void setDeathState(DeathState s); // overwrite virtual Creature::setDeathState and Unit::setDeathState + void Update(uint32 diff); // overwrite virtual Creature::Update and Unit::Update + + uint32 GetVehicleId() { return m_vehicleId; } + void SetVehicleId(uint32 vehicleid) { m_vehicleId = vehicleid; } + + void Dismiss(); + + bool LoadFromDB(uint32 guid, Map *map); + protected: + uint32 m_vehicleId; + + private: + void SaveToDB(uint32, uint8) // overwrited of Creature::SaveToDB - don't must be called + { + assert(false); + } + void DeleteFromDB() // overwrited of Creature::DeleteFromDB - don't must be called + { + assert(false); + } +}; +#endif diff --git a/src/game/VoiceChatHandler.cpp b/src/game/VoiceChatHandler.cpp index ba03ee1c543..8e787549e33 100644 --- a/src/game/VoiceChatHandler.cpp +++ b/src/game/VoiceChatHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,7 +21,6 @@ #include "Common.h" #include "WorldPacket.h" #include "WorldSession.h" -#include "World.h" #include "Opcodes.h" #include "Log.h" diff --git a/src/game/WaypointManager.cpp b/src/game/WaypointManager.cpp index 30947abe4c8..a677fd5b6d2 100644 --- a/src/game/WaypointManager.cpp +++ b/src/game/WaypointManager.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/WaypointManager.h b/src/game/WaypointManager.h index 1c41617fff7..85f8b765d45 100644 --- a/src/game/WaypointManager.h +++ b/src/game/WaypointManager.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/WaypointMovementGenerator.cpp b/src/game/WaypointMovementGenerator.cpp index 12b72829248..7c606697ac3 100644 --- a/src/game/WaypointMovementGenerator.cpp +++ b/src/game/WaypointMovementGenerator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +24,7 @@ //Creature-specific headers #include "Creature.h" #include "CreatureAI.h" +#include "CreatureGroups.h" //Player-specific #include "Player.h" @@ -54,7 +55,7 @@ bool WaypointMovementGenerator<Creature>::GetDestination(float &x, float &y, flo { if(i_destinationHolder.HasArrived()) return false; - + i_destinationHolder.GetDestination(x, y, z); return true; } @@ -109,6 +110,10 @@ WaypointMovementGenerator<Creature>::Initialize(Creature &u) InitTraveller(u, *node); i_destinationHolder.SetDestination(traveller, node->x, node->y, node->z); i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); + + //Call for creature group update + if(u.GetFormation() && u.GetFormation()->getLeader() == &u) + u.GetFormation()->LeaderMoveTo(node->x, node->y, node->z); } else node = NULL; @@ -180,6 +185,10 @@ WaypointMovementGenerator<Creature>::Update(Creature &unit, const uint32 &diff) InitTraveller(unit, *node); i_destinationHolder.SetDestination(traveller, node->x, node->y, node->z); i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); + + //Call for creature group update + if(unit.GetFormation() && unit.GetFormation()->getLeader() == &unit) + unit.GetFormation()->LeaderMoveTo(node->x, node->y, node->z); } else { @@ -254,12 +263,13 @@ FlightPathMovementGenerator::Initialize(Player &player) void FlightPathMovementGenerator::Finalize(Player & player) { + // remove flag to prevent send object build movement packets for flight state and crash (movement generator already not at top of stack) + player.clearUnitState(UNIT_STAT_IN_FLIGHT); float x, y, z; i_destinationHolder.GetLocationNow(player.GetMapId(), x, y, z); player.SetPosition(x, y, z, player.GetOrientation()); - player.clearUnitState(UNIT_STAT_IN_FLIGHT); player.Unmount(); player.RemoveFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT); diff --git a/src/game/WaypointMovementGenerator.h b/src/game/WaypointMovementGenerator.h index eec6ad70e5c..9804c150d63 100644 --- a/src/game/WaypointMovementGenerator.h +++ b/src/game/WaypointMovementGenerator.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -48,7 +48,7 @@ class TRINITY_DLL_SPEC PathMovementBase PathMovementBase() : i_currentNode(0) {} virtual ~PathMovementBase() {}; - inline bool MovementInProgress(void) const { return i_currentNode < i_path.Size(); } + bool MovementInProgress(void) const { return i_currentNode < i_path.Size(); } void LoadPath(T &); void ReloadPath(T &); @@ -109,7 +109,7 @@ public PathMovementBase<Player> Path& GetPath() { return i_path; } uint32 GetPathAtMapEnd() const; - inline bool HasArrived() const { return (i_currentNode >= i_path.Size()); } + bool HasArrived() const { return (i_currentNode >= i_path.Size()); } void SetCurrentNodeAfterTeleport(); void SkipCurrentNode() { ++i_currentNode; } bool GetDestination(float& x, float& y, float& z) const { i_destinationHolder.GetDestination(x,y,z); return true; } diff --git a/src/game/Weather.cpp b/src/game/Weather.cpp index 498beab0412..5d8d1fbd9ba 100644 --- a/src/game/Weather.cpp +++ b/src/game/Weather.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,7 +24,6 @@ #include "Weather.h" #include "WorldPacket.h" -#include "WorldSession.h" #include "Player.h" #include "World.h" #include "Log.h" @@ -38,11 +37,11 @@ Weather::Weather(uint32 zone, WeatherZoneChances const* weatherChances) : m_zone m_type = WEATHER_TYPE_FINE; m_grade = 0; - sLog.outDetail("WORLD: Starting weather system for zone %u (change every %u minutes).", m_zone, (uint32)(m_timer.GetInterval() / (1000*MINUTE)) ); + sLog.outDetail("WORLD: Starting weather system for zone %u (change every %u minutes).", m_zone, (uint32)(m_timer.GetInterval() / (MINUTE*IN_MILISECONDS)) ); } /// Launch a weather update -bool Weather::Update(time_t diff) +bool Weather::Update(uint32 diff) { if (m_timer.GetCurrent()>=0) m_timer.Update(diff); diff --git a/src/game/Weather.h b/src/game/Weather.h index cbd4daad1ad..e312f004060 100644 --- a/src/game/Weather.h +++ b/src/game/Weather.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -62,7 +62,7 @@ class Weather void SetWeather(WeatherType type, float grade); /// For which zone is this weather? uint32 GetZone() { return m_zone; }; - bool Update(time_t diff); + bool Update(uint32 diff); private: WeatherState GetWeatherState() const; uint32 m_zone; diff --git a/src/game/World.cpp b/src/game/World.cpp index d138b15ac75..d9a22a4a4a2 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,7 +23,6 @@ */ #include "Common.h" -//#include "WorldSocket.h" #include "Database/DatabaseEnv.h" #include "Config/ConfigEnv.h" #include "SystemConfig.h" @@ -33,15 +32,18 @@ #include "WorldPacket.h" #include "Weather.h" #include "Player.h" +#include "Vehicle.h" #include "SkillExtraItems.h" #include "SkillDiscovery.h" #include "World.h" #include "AccountMgr.h" +#include "AchievementMgr.h" #include "AuctionHouseMgr.h" #include "ObjectMgr.h" +#include "CreatureEventAIMgr.h" #include "SpellMgr.h" #include "Chat.h" -#include "Database/DBCStores.h" +#include "DBCStores.h" #include "LootMgr.h" #include "ItemEnchantmentMgr.h" #include "MapManager.h" @@ -55,7 +57,8 @@ #include "WaypointMovementGenerator.h" #include "VMapFactory.h" #include "GlobalEvents.h" -#include "GameEvent.h" +#include "GameEventMgr.h" +#include "PoolHandler.h" #include "Database/DatabaseImpl.h" #include "GridNotifiersImpl.h" #include "CellImpl.h" @@ -81,16 +84,6 @@ float World::m_MaxVisibleDistanceInFlight = DEFAULT_VISIBILITY_DISTANCE; float World::m_VisibleUnitGreyDistance = 0; float World::m_VisibleObjectGreyDistance = 0; -// ServerMessages.dbc -enum ServerMessageType -{ - SERVER_MSG_SHUTDOWN_TIME = 1, - SERVER_MSG_RESTART_TIME = 2, - SERVER_MSG_STRING = 3, - SERVER_MSG_SHUTDOWN_CANCELLED = 4, - SERVER_MSG_RESTART_CANCELLED = 5 -}; - struct ScriptAction { uint64 sourceGUID; @@ -118,6 +111,8 @@ World::World() m_updateTimeSum = 0; m_updateTimeCount = 0; + + m_isClosed = false; } /// World destructor @@ -238,7 +233,7 @@ World::AddSession_ (WorldSession* s) uint32 Sessions = GetActiveAndQueuedSessionCount (); uint32 pLimit = GetPlayerAmountLimit (); - uint32 QueueSize = GetQueueSize (); //number of players in the queue + uint32 QueueSize = GetQueueSize (); //number of players in the queue //so we don't count the user trying to //login as a session and queue the socket that we are using @@ -255,18 +250,19 @@ World::AddSession_ (WorldSession* s) WorldPacket packet(SMSG_AUTH_RESPONSE, 1 + 4 + 1 + 4 + 1); packet << uint8 (AUTH_OK); - packet << uint32 (0); // unknown random value... - packet << uint8 (0); - packet << uint32 (0); - packet << uint8 (s->Expansion()); // 0 - normal, 1 - TBC, must be set in database manually for each account + packet << uint32 (0); // BillingTimeRemaining + packet << uint8 (0); // BillingPlanFlags + packet << uint32 (0); // BillingTimeRested + packet << uint8 (s->Expansion()); // 0 - normal, 1 - TBC, must be set in database manually for each account s->SendPacket (&packet); + s->SendAddonsInfo(); UpdateMaxSessionCounters (); // Updates the population if (pLimit > 0) { - float popu = GetActiveSessionCount (); //updated number of users on the server + float popu = GetActiveSessionCount (); //updated number of users on the server popu /= pLimit; popu *= 2; LoginDatabase.PExecute ("UPDATE realmlist SET population = '%f' WHERE id = '%d'", popu, realmID); @@ -280,12 +276,13 @@ bool World::HasRecentlyDisconnected(WorldSession* session) if(uint32 tolerance = getConfig(CONFIG_INTERVAL_DISCONNECT_TOLERANCE)) { - for(DisconnectMap::iterator i = m_disconnects.begin(); i != m_disconnects.end(); ++i) + for(DisconnectMap::iterator i = m_disconnects.begin(); i != m_disconnects.end(); ) { if(difftime(i->second, time(NULL)) < tolerance) { if(i->first == session->GetAccountId()) return true; + ++i; } else m_disconnects.erase(i); @@ -313,10 +310,10 @@ void World::AddQueuedPlayer(WorldSession* sess) // The 1st SMSG_AUTH_RESPONSE needs to contain other info too. WorldPacket packet (SMSG_AUTH_RESPONSE, 1 + 4 + 1 + 4 + 1); packet << uint8 (AUTH_WAIT_QUEUE); - packet << uint32 (0); // unknown random value... - packet << uint8 (0); - packet << uint32 (0); - packet << uint8 (sess->Expansion () ? 1 : 0); // 0 - normal, 1 - TBC, must be set in database manually for each account + packet << uint32 (0); // BillingTimeRemaining + packet << uint8 (0); // BillingPlanFlags + packet << uint32 (0); // BillingTimeRested + packet << uint8 (sess->Expansion () ? 1 : 0); // 0 - normal, 1 - TBC, must be set in database manually for each account packet << uint32(GetQueuePos (sess)); sess->SendPacket (&packet); @@ -440,24 +437,30 @@ void World::LoadConfigSettings(bool reload) rate_values[RATE_HEALTH] = sConfig.GetFloatDefault("Rate.Health", 1); if(rate_values[RATE_HEALTH] < 0) { - sLog.outError("Rate.Health (%f) mustbe > 0. Using 1 instead.",rate_values[RATE_HEALTH]); + sLog.outError("Rate.Health (%f) must be > 0. Using 1 instead.",rate_values[RATE_HEALTH]); rate_values[RATE_HEALTH] = 1; } rate_values[RATE_POWER_MANA] = sConfig.GetFloatDefault("Rate.Mana", 1); if(rate_values[RATE_POWER_MANA] < 0) { - sLog.outError("Rate.Mana (%f) mustbe > 0. Using 1 instead.",rate_values[RATE_POWER_MANA]); + sLog.outError("Rate.Mana (%f) must be > 0. Using 1 instead.",rate_values[RATE_POWER_MANA]); rate_values[RATE_POWER_MANA] = 1; } rate_values[RATE_POWER_RAGE_INCOME] = sConfig.GetFloatDefault("Rate.Rage.Income", 1); rate_values[RATE_POWER_RAGE_LOSS] = sConfig.GetFloatDefault("Rate.Rage.Loss", 1); if(rate_values[RATE_POWER_RAGE_LOSS] < 0) { - sLog.outError("Rate.Rage.Loss (%f) mustbe > 0. Using 1 instead.",rate_values[RATE_POWER_RAGE_LOSS]); + sLog.outError("Rate.Rage.Loss (%f) must be > 0. Using 1 instead.",rate_values[RATE_POWER_RAGE_LOSS]); rate_values[RATE_POWER_RAGE_LOSS] = 1; } + rate_values[RATE_POWER_RUNICPOWER_INCOME] = sConfig.GetFloatDefault("Rate.RunicPower.Income", 1); + rate_values[RATE_POWER_RUNICPOWER_LOSS] = sConfig.GetFloatDefault("Rate.RunicPower.Loss", 1); + if(rate_values[RATE_POWER_RUNICPOWER_LOSS] < 0) + { + sLog.outError("Rate.RunicPower.Loss (%f) must be > 0. Using 1 instead.",rate_values[RATE_POWER_RUNICPOWER_LOSS]); + rate_values[RATE_POWER_RUNICPOWER_LOSS] = 1; + } rate_values[RATE_POWER_FOCUS] = sConfig.GetFloatDefault("Rate.Focus", 1.0f); - rate_values[RATE_LOYALTY] = sConfig.GetFloatDefault("Rate.Loyalty", 1.0f); rate_values[RATE_SKILL_DISCOVERY] = sConfig.GetFloatDefault("Rate.Skill.Discovery", 1.0f); rate_values[RATE_DROP_ITEM_POOR] = sConfig.GetFloatDefault("Rate.Drop.Item.Poor", 1.0f); rate_values[RATE_DROP_ITEM_NORMAL] = sConfig.GetFloatDefault("Rate.Drop.Item.Normal", 1.0f); @@ -471,8 +474,9 @@ void World::LoadConfigSettings(bool reload) rate_values[RATE_XP_KILL] = sConfig.GetFloatDefault("Rate.XP.Kill", 1.0f); rate_values[RATE_XP_QUEST] = sConfig.GetFloatDefault("Rate.XP.Quest", 1.0f); rate_values[RATE_XP_EXPLORE] = sConfig.GetFloatDefault("Rate.XP.Explore", 1.0f); - rate_values[RATE_XP_PAST_70] = sConfig.GetFloatDefault("Rate.XP.PastLevel70", 1.0f); rate_values[RATE_REPUTATION_GAIN] = sConfig.GetFloatDefault("Rate.Reputation.Gain", 1.0f); + rate_values[RATE_REPUTATION_LOWLEVEL_KILL] = sConfig.GetFloatDefault("Rate.Reputation.LowLevel.Kill", 1.0f); + rate_values[RATE_REPUTATION_LOWLEVEL_QUEST] = sConfig.GetFloatDefault("Rate.Reputation.LowLevel.Quest", 1.0f); rate_values[RATE_CREATURE_NORMAL_DAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Normal.Damage", 1.0f); rate_values[RATE_CREATURE_ELITE_ELITE_DAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.Elite.Damage", 1.0f); rate_values[RATE_CREATURE_ELITE_RAREELITE_DAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.RAREELITE.Damage", 1.0f); @@ -556,10 +560,10 @@ void World::LoadConfigSettings(bool reload) } m_configs[CONFIG_ADDON_CHANNEL] = sConfig.GetBoolDefault("AddonChannel", true); m_configs[CONFIG_GRID_UNLOAD] = sConfig.GetBoolDefault("GridUnload", true); - m_configs[CONFIG_INTERVAL_SAVE] = sConfig.GetIntDefault("PlayerSaveInterval", 900000); + m_configs[CONFIG_INTERVAL_SAVE] = sConfig.GetIntDefault("PlayerSaveInterval", 15 * MINUTE * IN_MILISECONDS); m_configs[CONFIG_INTERVAL_DISCONNECT_TOLERANCE] = sConfig.GetIntDefault("DisconnectToleranceInterval", 0); - m_configs[CONFIG_INTERVAL_GRIDCLEAN] = sConfig.GetIntDefault("GridCleanUpDelay", 300000); + m_configs[CONFIG_INTERVAL_GRIDCLEAN] = sConfig.GetIntDefault("GridCleanUpDelay", 5 * MINUTE * IN_MILISECONDS); if(m_configs[CONFIG_INTERVAL_GRIDCLEAN] < MIN_GRID_DELAY) { sLog.outError("GridCleanUpDelay (%i) must be greater %u. Use this minimal value.",m_configs[CONFIG_INTERVAL_GRIDCLEAN],MIN_GRID_DELAY); @@ -577,7 +581,7 @@ void World::LoadConfigSettings(bool reload) if(reload) MapManager::Instance().SetMapUpdateInterval(m_configs[CONFIG_INTERVAL_MAPUPDATE]); - m_configs[CONFIG_INTERVAL_CHANGEWEATHER] = sConfig.GetIntDefault("ChangeWeatherInterval", 600000); + m_configs[CONFIG_INTERVAL_CHANGEWEATHER] = sConfig.GetIntDefault("ChangeWeatherInterval", 10 * MINUTE * IN_MILISECONDS); if(reload) { @@ -592,7 +596,7 @@ void World::LoadConfigSettings(bool reload) { uint32 val = sConfig.GetIntDefault("SocketSelectTime", DEFAULT_SOCKET_SELECT_TIME); if(val!=m_configs[CONFIG_SOCKET_SELECTTIME]) - sLog.outError("SocketSelectTime option can't be changed at Trinityd.conf reload, using current value (%u).",m_configs[DEFAULT_SOCKET_SELECT_TIME]); + sLog.outError("SocketSelectTime option can't be changed at Trinityd.conf reload, using current value (%u).",m_configs[CONFIG_SOCKET_SELECTTIME]); } else m_configs[CONFIG_SOCKET_SELECTTIME] = sConfig.GetIntDefault("SocketSelectTime", DEFAULT_SOCKET_SELECT_TIME); @@ -620,21 +624,21 @@ void World::LoadConfigSettings(bool reload) else m_configs[CONFIG_REALM_ZONE] = sConfig.GetIntDefault("RealmZone", REALM_ZONE_DEVELOPMENT); - m_configs[CONFIG_ALLOW_TWO_SIDE_ACCOUNTS] = sConfig.GetBoolDefault("AllowTwoSide.Accounts", false); + m_configs[CONFIG_ALLOW_TWO_SIDE_ACCOUNTS] = sConfig.GetBoolDefault("AllowTwoSide.Accounts", false); m_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT] = sConfig.GetBoolDefault("AllowTwoSide.Interaction.Chat",false); m_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL] = sConfig.GetBoolDefault("AllowTwoSide.Interaction.Channel",false); m_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP] = sConfig.GetBoolDefault("AllowTwoSide.Interaction.Group",false); m_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD] = sConfig.GetBoolDefault("AllowTwoSide.Interaction.Guild",false); m_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION] = sConfig.GetBoolDefault("AllowTwoSide.Interaction.Auction",false); m_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL] = sConfig.GetBoolDefault("AllowTwoSide.Interaction.Mail",false); - m_configs[CONFIG_ALLOW_TWO_SIDE_WHO_LIST] = sConfig.GetBoolDefault("AllowTwoSide.WhoList", false); - m_configs[CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND] = sConfig.GetBoolDefault("AllowTwoSide.AddFriend", false); - m_configs[CONFIG_ALLOW_TWO_SIDE_TRADE] = sConfig.GetBoolDefault("AllowTwoSide.trade", false); - m_configs[CONFIG_STRICT_PLAYER_NAMES] = sConfig.GetIntDefault("StrictPlayerNames", 0); - m_configs[CONFIG_STRICT_CHARTER_NAMES] = sConfig.GetIntDefault("StrictCharterNames", 0); - m_configs[CONFIG_STRICT_PET_NAMES] = sConfig.GetIntDefault("StrictPetNames", 0); + m_configs[CONFIG_ALLOW_TWO_SIDE_WHO_LIST] = sConfig.GetBoolDefault("AllowTwoSide.WhoList", false); + m_configs[CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND] = sConfig.GetBoolDefault("AllowTwoSide.AddFriend", false); + m_configs[CONFIG_ALLOW_TWO_SIDE_TRADE] = sConfig.GetBoolDefault("AllowTwoSide.trade", false); + m_configs[CONFIG_STRICT_PLAYER_NAMES] = sConfig.GetIntDefault ("StrictPlayerNames", 0); + m_configs[CONFIG_STRICT_CHARTER_NAMES] = sConfig.GetIntDefault ("StrictCharterNames", 0); + m_configs[CONFIG_STRICT_PET_NAMES] = sConfig.GetIntDefault ("StrictPetNames", 0); - m_configs[CONFIG_CHARACTERS_CREATING_DISABLED] = sConfig.GetIntDefault("CharactersCreatingDisabled", 0); + m_configs[CONFIG_CHARACTERS_CREATING_DISABLED] = sConfig.GetIntDefault ("CharactersCreatingDisabled", 0); m_configs[CONFIG_CHARACTERS_PER_REALM] = sConfig.GetIntDefault("CharactersPerRealm", 10); if(m_configs[CONFIG_CHARACTERS_PER_REALM] < 1 || m_configs[CONFIG_CHARACTERS_PER_REALM] > 10) @@ -651,8 +655,17 @@ void World::LoadConfigSettings(bool reload) m_configs[CONFIG_CHARACTERS_PER_ACCOUNT] = m_configs[CONFIG_CHARACTERS_PER_REALM]; } + m_configs[CONFIG_HEROIC_CHARACTERS_PER_REALM] = sConfig.GetIntDefault("HeroicCharactersPerRealm", 1); + if(int32(m_configs[CONFIG_HEROIC_CHARACTERS_PER_REALM]) < 0 || m_configs[CONFIG_HEROIC_CHARACTERS_PER_REALM] > 10) + { + sLog.outError("HeroicCharactersPerRealm (%i) must be in range 0..10. Set to 1.",m_configs[CONFIG_HEROIC_CHARACTERS_PER_REALM]); + m_configs[CONFIG_HEROIC_CHARACTERS_PER_REALM] = 1; + } + + m_configs[CONFIG_MIN_LEVEL_FOR_HEROIC_CHARACTER_CREATING] = sConfig.GetIntDefault("MinLevelForHeroicCharacterCreating", 55); + m_configs[CONFIG_SKIP_CINEMATICS] = sConfig.GetIntDefault("SkipCinematics", 0); - if(m_configs[CONFIG_SKIP_CINEMATICS] < 0 || m_configs[CONFIG_SKIP_CINEMATICS] > 2) + if(int32(m_configs[CONFIG_SKIP_CINEMATICS]) < 0 || m_configs[CONFIG_SKIP_CINEMATICS] > 2) { sLog.outError("SkipCinematics (%i) must be in range 0..2. Set to 0.",m_configs[CONFIG_SKIP_CINEMATICS]); m_configs[CONFIG_SKIP_CINEMATICS] = 0; @@ -660,12 +673,12 @@ void World::LoadConfigSettings(bool reload) if(reload) { - uint32 val = sConfig.GetIntDefault("MaxPlayerLevel", 60); + uint32 val = sConfig.GetIntDefault("MaxPlayerLevel", 80); if(val!=m_configs[CONFIG_MAX_PLAYER_LEVEL]) sLog.outError("MaxPlayerLevel option can't be changed at config reload, using current value (%u).",m_configs[CONFIG_MAX_PLAYER_LEVEL]); } else - m_configs[CONFIG_MAX_PLAYER_LEVEL] = sConfig.GetIntDefault("MaxPlayerLevel", 60); + m_configs[CONFIG_MAX_PLAYER_LEVEL] = sConfig.GetIntDefault("MaxPlayerLevel", 80); if(m_configs[CONFIG_MAX_PLAYER_LEVEL] > MAX_LEVEL) { @@ -685,8 +698,22 @@ void World::LoadConfigSettings(bool reload) m_configs[CONFIG_START_PLAYER_LEVEL] = m_configs[CONFIG_MAX_PLAYER_LEVEL]; } + m_configs[CONFIG_START_HEROIC_PLAYER_LEVEL] = sConfig.GetIntDefault("StartHeroicPlayerLevel", 55); + if(m_configs[CONFIG_START_HEROIC_PLAYER_LEVEL] < 1) + { + sLog.outError("StartHeroicPlayerLevel (%i) must be in range 1..MaxPlayerLevel(%u). Set to 55.", + m_configs[CONFIG_START_HEROIC_PLAYER_LEVEL],m_configs[CONFIG_MAX_PLAYER_LEVEL]); + m_configs[CONFIG_START_HEROIC_PLAYER_LEVEL] = 55; + } + else if(m_configs[CONFIG_START_HEROIC_PLAYER_LEVEL] > m_configs[CONFIG_MAX_PLAYER_LEVEL]) + { + sLog.outError("StartHeroicPlayerLevel (%i) must be in range 1..MaxPlayerLevel(%u). Set to %u.", + m_configs[CONFIG_START_HEROIC_PLAYER_LEVEL],m_configs[CONFIG_MAX_PLAYER_LEVEL],m_configs[CONFIG_MAX_PLAYER_LEVEL]); + m_configs[CONFIG_START_HEROIC_PLAYER_LEVEL] = m_configs[CONFIG_MAX_PLAYER_LEVEL]; + } + m_configs[CONFIG_START_PLAYER_MONEY] = sConfig.GetIntDefault("StartPlayerMoney", 0); - if(m_configs[CONFIG_START_PLAYER_MONEY] < 0) + if(int32(m_configs[CONFIG_START_PLAYER_MONEY]) < 0) { sLog.outError("StartPlayerMoney (%i) must be in range 0..%u. Set to %u.",m_configs[CONFIG_START_PLAYER_MONEY],MAX_MONEY_AMOUNT,0); m_configs[CONFIG_START_PLAYER_MONEY] = 0; @@ -699,18 +726,18 @@ void World::LoadConfigSettings(bool reload) } m_configs[CONFIG_MAX_HONOR_POINTS] = sConfig.GetIntDefault("MaxHonorPoints", 75000); - if(m_configs[CONFIG_MAX_HONOR_POINTS] < 0) + if(int32(m_configs[CONFIG_MAX_HONOR_POINTS]) < 0) { sLog.outError("MaxHonorPoints (%i) can't be negative. Set to 0.",m_configs[CONFIG_MAX_HONOR_POINTS]); m_configs[CONFIG_MAX_HONOR_POINTS] = 0; } m_configs[CONFIG_START_HONOR_POINTS] = sConfig.GetIntDefault("StartHonorPoints", 0); - if(m_configs[CONFIG_START_HONOR_POINTS] < 0) + if(int32(m_configs[CONFIG_START_HONOR_POINTS]) < 0) { sLog.outError("StartHonorPoints (%i) must be in range 0..MaxHonorPoints(%u). Set to %u.", m_configs[CONFIG_START_HONOR_POINTS],m_configs[CONFIG_MAX_HONOR_POINTS],0); - m_configs[CONFIG_MAX_HONOR_POINTS] = 0; + m_configs[CONFIG_START_HONOR_POINTS] = 0; } else if(m_configs[CONFIG_START_HONOR_POINTS] > m_configs[CONFIG_MAX_HONOR_POINTS]) { @@ -720,14 +747,14 @@ void World::LoadConfigSettings(bool reload) } m_configs[CONFIG_MAX_ARENA_POINTS] = sConfig.GetIntDefault("MaxArenaPoints", 5000); - if(m_configs[CONFIG_MAX_ARENA_POINTS] < 0) + if(int32(m_configs[CONFIG_MAX_ARENA_POINTS]) < 0) { sLog.outError("MaxArenaPoints (%i) can't be negative. Set to 0.",m_configs[CONFIG_MAX_ARENA_POINTS]); m_configs[CONFIG_MAX_ARENA_POINTS] = 0; } m_configs[CONFIG_START_ARENA_POINTS] = sConfig.GetIntDefault("StartArenaPoints", 0); - if(m_configs[CONFIG_START_ARENA_POINTS] < 0) + if(int32(m_configs[CONFIG_START_ARENA_POINTS]) < 0) { sLog.outError("StartArenaPoints (%i) must be in range 0..MaxArenaPoints(%u). Set to %u.", m_configs[CONFIG_START_ARENA_POINTS],m_configs[CONFIG_MAX_ARENA_POINTS],0); @@ -745,27 +772,25 @@ void World::LoadConfigSettings(bool reload) m_configs[CONFIG_INSTANCE_IGNORE_LEVEL] = sConfig.GetBoolDefault("Instance.IgnoreLevel", false); m_configs[CONFIG_INSTANCE_IGNORE_RAID] = sConfig.GetBoolDefault("Instance.IgnoreRaid", false); - m_configs[CONFIG_BATTLEGROUND_CAST_DESERTER] = sConfig.GetBoolDefault("Battleground.CastDeserter", true); - m_configs[CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE] = sConfig.GetBoolDefault("Battleground.QueueAnnouncer.Enable", true); - m_configs[CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY] = sConfig.GetBoolDefault("Battleground.QueueAnnouncer.PlayerOnly", false); - m_configs[CONFIG_CAST_UNSTUCK] = sConfig.GetBoolDefault("CastUnstuck", true); m_configs[CONFIG_INSTANCE_RESET_TIME_HOUR] = sConfig.GetIntDefault("Instance.ResetTimeHour", 4); - m_configs[CONFIG_INSTANCE_UNLOAD_DELAY] = sConfig.GetIntDefault("Instance.UnloadDelay", 1800000); + m_configs[CONFIG_INSTANCE_UNLOAD_DELAY] = sConfig.GetIntDefault("Instance.UnloadDelay", 30 * MINUTE * IN_MILISECONDS); m_configs[CONFIG_MAX_PRIMARY_TRADE_SKILL] = sConfig.GetIntDefault("MaxPrimaryTradeSkill", 2); m_configs[CONFIG_MIN_PETITION_SIGNS] = sConfig.GetIntDefault("MinPetitionSigns", 9); if(m_configs[CONFIG_MIN_PETITION_SIGNS] > 9) { - sLog.outError("MinPetitionSigns (%i) must be in range 0..9. Set to 9.",m_configs[CONFIG_MIN_PETITION_SIGNS]); + sLog.outError("MinPetitionSigns (%i) must be in range 0..9. Set to 9.", m_configs[CONFIG_MIN_PETITION_SIGNS]); m_configs[CONFIG_MIN_PETITION_SIGNS] = 9; } - m_configs[CONFIG_GM_LOGIN_STATE] = sConfig.GetIntDefault("GM.LoginState",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); - m_configs[CONFIG_GM_IN_WHO_LIST] = sConfig.GetBoolDefault("GM.InWhoList",false); + m_configs[CONFIG_GM_LOGIN_STATE] = sConfig.GetIntDefault("GM.LoginState", 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); + m_configs[CONFIG_GM_IN_WHO_LIST] = sConfig.GetBoolDefault("GM.InWhoList", false); m_configs[CONFIG_GM_LOG_TRADE] = sConfig.GetBoolDefault("GM.LogTrade", false); m_configs[CONFIG_START_GM_LEVEL] = sConfig.GetIntDefault("GM.StartLevel", 1); if(m_configs[CONFIG_START_GM_LEVEL] < m_configs[CONFIG_START_PLAYER_LEVEL]) @@ -779,23 +804,41 @@ void World::LoadConfigSettings(bool reload) sLog.outError("GM.StartLevel (%i) must be in range 1..%u. Set to %u.", m_configs[CONFIG_START_GM_LEVEL], MAX_LEVEL, MAX_LEVEL); 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); m_configs[CONFIG_MAIL_DELIVERY_DELAY] = sConfig.GetIntDefault("MailDeliveryDelay",HOUR); m_configs[CONFIG_UPTIME_UPDATE] = sConfig.GetIntDefault("UpdateUptimeInterval", 10); - if(m_configs[CONFIG_UPTIME_UPDATE]<=0) + if(int32(m_configs[CONFIG_UPTIME_UPDATE])<=0) { sLog.outError("UpdateUptimeInterval (%i) must be > 0, set to default 10.",m_configs[CONFIG_UPTIME_UPDATE]); m_configs[CONFIG_UPTIME_UPDATE] = 10; } if(reload) { - m_timers[WUPDATE_UPTIME].SetInterval(m_configs[CONFIG_UPTIME_UPDATE]*MINUTE*1000); + m_timers[WUPDATE_UPTIME].SetInterval(m_configs[CONFIG_UPTIME_UPDATE]*MINUTE*IN_MILISECONDS); m_timers[WUPDATE_UPTIME].Reset(); } + // log db cleanup interval + m_configs[CONFIG_LOGDB_CLEARINTERVAL] = sConfig.GetIntDefault("LogDB.Opt.ClearInterval", 10); + if(int32(m_configs[CONFIG_LOGDB_CLEARINTERVAL]) <= 0) + { + sLog.outError("LogDB.Opt.ClearInterval (%i) must be > 0, set to default 10.", m_configs[CONFIG_LOGDB_CLEARINTERVAL]); + m_configs[CONFIG_LOGDB_CLEARINTERVAL] = 10; + } + if(reload) + { + m_timers[WUPDATE_CLEANDB].SetInterval(m_configs[CONFIG_LOGDB_CLEARINTERVAL] * MINUTE * IN_MILISECONDS); + m_timers[WUPDATE_CLEANDB].Reset(); + } + m_configs[CONFIG_LOGDB_CLEARTIME] = sConfig.GetIntDefault("LogDB.Opt.ClearTime", 1209600); // 14 days default + sLog.outString("Will clear `logs` table of entries older than %i seconds every %u minutes.", + m_configs[CONFIG_LOGDB_CLEARTIME], m_configs[CONFIG_LOGDB_CLEARINTERVAL]); + m_configs[CONFIG_SKILL_CHANCE_ORANGE] = sConfig.GetIntDefault("SkillChance.Orange",100); m_configs[CONFIG_SKILL_CHANCE_YELLOW] = sConfig.GetIntDefault("SkillChance.Yellow",75); m_configs[CONFIG_SKILL_CHANCE_GREEN] = sConfig.GetIntDefault("SkillChance.Green",25); @@ -805,6 +848,7 @@ void World::LoadConfigSettings(bool reload) m_configs[CONFIG_SKILL_CHANCE_SKINNING_STEPS] = sConfig.GetIntDefault("SkillChance.SkinningSteps",75); m_configs[CONFIG_SKILL_PROSPECTING] = sConfig.GetBoolDefault("SkillChance.Prospecting",false); + m_configs[CONFIG_SKILL_MILLING] = sConfig.GetBoolDefault("SkillChance.Milling",false); m_configs[CONFIG_SKILL_GAIN_CRAFTING] = sConfig.GetIntDefault("SkillGain.Crafting", 1); if(m_configs[CONFIG_SKILL_GAIN_CRAFTING] < 0) @@ -878,21 +922,23 @@ void World::LoadConfigSettings(bool reload) m_configs[CONFIG_DETECT_POS_COLLISION] = sConfig.GetBoolDefault("DetectPosCollision", true); - m_configs[CONFIG_RESTRICTED_LFG_CHANNEL] = sConfig.GetBoolDefault("Channel.RestrictedLfg", true); + m_configs[CONFIG_RESTRICTED_LFG_CHANNEL] = sConfig.GetBoolDefault("Channel.RestrictedLfg", true); m_configs[CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL] = sConfig.GetBoolDefault("Channel.SilentlyGMJoin", false); - m_configs[CONFIG_TALENTS_INSPECTING] = sConfig.GetBoolDefault("TalentsInspecting", true); + m_configs[CONFIG_TALENTS_INSPECTING] = sConfig.GetBoolDefault("TalentsInspecting", true); m_configs[CONFIG_CHAT_FAKE_MESSAGE_PREVENTING] = sConfig.GetBoolDefault("ChatFakeMessagePreventing", false); - m_configs[CONFIG_CORPSE_DECAY_NORMAL] = sConfig.GetIntDefault("Corpse.Decay.NORMAL", 60); - m_configs[CONFIG_CORPSE_DECAY_RARE] = sConfig.GetIntDefault("Corpse.Decay.RARE", 300); - m_configs[CONFIG_CORPSE_DECAY_ELITE] = sConfig.GetIntDefault("Corpse.Decay.ELITE", 300); + m_configs[CONFIG_CORPSE_DECAY_NORMAL] = sConfig.GetIntDefault("Corpse.Decay.NORMAL", 60); + m_configs[CONFIG_CORPSE_DECAY_RARE] = sConfig.GetIntDefault("Corpse.Decay.RARE", 300); + m_configs[CONFIG_CORPSE_DECAY_ELITE] = sConfig.GetIntDefault("Corpse.Decay.ELITE", 300); m_configs[CONFIG_CORPSE_DECAY_RAREELITE] = sConfig.GetIntDefault("Corpse.Decay.RAREELITE", 300); m_configs[CONFIG_CORPSE_DECAY_WORLDBOSS] = sConfig.GetIntDefault("Corpse.Decay.WORLDBOSS", 3600); - m_configs[CONFIG_DEATH_SICKNESS_LEVEL] = sConfig.GetIntDefault("Death.SicknessLevel", 11); + m_configs[CONFIG_DEATH_SICKNESS_LEVEL] = sConfig.GetIntDefault ("Death.SicknessLevel", 11); m_configs[CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP] = sConfig.GetBoolDefault("Death.CorpseReclaimDelay.PvP", true); m_configs[CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE] = sConfig.GetBoolDefault("Death.CorpseReclaimDelay.PvE", true); + m_configs[CONFIG_DEATH_BONES_WORLD] = sConfig.GetBoolDefault("Death.Bones.World", true); + m_configs[CONFIG_DEATH_BONES_BG_OR_ARENA] = sConfig.GetBoolDefault("Death.Bones.BattlegroundOrArena", true); m_configs[CONFIG_THREAT_RADIUS] = sConfig.GetIntDefault("ThreatRadius", 60); @@ -904,12 +950,22 @@ void World::LoadConfigSettings(bool reload) m_configs[CONFIG_LISTEN_RANGE_TEXTEMOTE] = sConfig.GetIntDefault("ListenRange.TextEmote", 25); m_configs[CONFIG_LISTEN_RANGE_YELL] = sConfig.GetIntDefault("ListenRange.Yell", 300); - m_configs[CONFIG_ARENA_MAX_RATING_DIFFERENCE] = sConfig.GetIntDefault("Arena.MaxRatingDifference", 0); - m_configs[CONFIG_ARENA_RATING_DISCARD_TIMER] = sConfig.GetIntDefault("Arena.RatingDiscardTimer",300000); - m_configs[CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS] = sConfig.GetBoolDefault("Arena.AutoDistributePoints", false); - m_configs[CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS] = sConfig.GetIntDefault("Arena.AutoDistributeInterval", 7); + m_configs[CONFIG_BATTLEGROUND_CAST_DESERTER] = sConfig.GetBoolDefault("Battleground.CastDeserter", true); + m_configs[CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE] = sConfig.GetBoolDefault("Battleground.QueueAnnouncer.Enable", false); + m_configs[CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY] = sConfig.GetBoolDefault("Battleground.QueueAnnouncer.PlayerOnly", false); + m_configs[CONFIG_BATTLEGROUND_INVITATION_TYPE] = sConfig.GetIntDefault ("Battleground.InvitationType", 0); + m_configs[CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER] = sConfig.GetIntDefault ("BattleGround.PrematureFinishTimer", 5 * MINUTE * IN_MILISECONDS); + m_configs[CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH] = sConfig.GetIntDefault ("BattleGround.PremadeGroupWaitForMatch", 30 * MINUTE * IN_MILISECONDS); + m_configs[CONFIG_ARENA_MAX_RATING_DIFFERENCE] = sConfig.GetIntDefault ("Arena.MaxRatingDifference", 150); + m_configs[CONFIG_ARENA_RATING_DISCARD_TIMER] = sConfig.GetIntDefault ("Arena.RatingDiscardTimer", 10 * MINUTE * IN_MILISECONDS); + m_configs[CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS] = sConfig.GetBoolDefault("Arena.AutoDistributePoints", false); + m_configs[CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS] = sConfig.GetIntDefault ("Arena.AutoDistributeInterval", 7); + m_configs[CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE] = sConfig.GetBoolDefault("Arena.QueueAnnouncer.Enable", false); + m_configs[CONFIG_ARENA_SEASON_ID] = sConfig.GetIntDefault ("Arena.ArenaSeason.ID", 1); + m_configs[CONFIG_ARENA_SEASON_IN_PROGRESS] = sConfig.GetBoolDefault("Arena.ArenaSeason.InProgress", true); + + m_configs[CONFIG_OFFHAND_CHECK_AT_TALENTS_RESET] = sConfig.GetBoolDefault("OffhandCheckAtTalentsReset", false); - m_configs[CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER] = sConfig.GetIntDefault("BattleGround.PrematureFinishTimer", 0); m_configs[CONFIG_INSTANT_LOGOUT] = sConfig.GetIntDefault("InstantLogout", SEC_MODERATOR); m_VisibleUnitGreyDistance = sConfig.GetFloatDefault("Visibility.Distance.Grey.Unit", 1); @@ -1005,7 +1061,6 @@ void World::LoadConfigSettings(bool reload) m_configs[CONFIG_MAX_WHO] = sConfig.GetIntDefault("MaxWhoListReturns", 49); - m_configs[CONFIG_PREMATURE_BG_REWARD] = sConfig.GetBoolDefault("Battleground.PrematureReward", true); m_configs[CONFIG_BG_START_MUSIC] = sConfig.GetBoolDefault("MusicInBattleground", false); m_configs[CONFIG_START_ALL_SPELLS] = sConfig.GetBoolDefault("PlayerStart.AllSpells", false); m_configs[CONFIG_HONOR_AFTER_DUEL] = sConfig.GetIntDefault("HonorPointsAfterDuel", 0); @@ -1038,6 +1093,17 @@ void World::LoadConfigSettings(bool reload) token = strtok(NULL,delim); } delete[] forbiddenMaps; + + // chat logging + m_configs[CONFIG_CHATLOG_CHANNEL] = sConfig.GetBoolDefault("ChatLogs.Channel", false); + m_configs[CONFIG_CHATLOG_WHISPER] = sConfig.GetBoolDefault("ChatLogs.Whisper", false); + m_configs[CONFIG_CHATLOG_SYSCHAN] = sConfig.GetBoolDefault("ChatLogs.SysChan", false); + m_configs[CONFIG_CHATLOG_PARTY] = sConfig.GetBoolDefault("ChatLogs.Party", false); + m_configs[CONFIG_CHATLOG_RAID] = sConfig.GetBoolDefault("ChatLogs.Raid", false); + m_configs[CONFIG_CHATLOG_GUILD] = sConfig.GetBoolDefault("ChatLogs.Guild", false); + m_configs[CONFIG_CHATLOG_PUBLIC] = sConfig.GetBoolDefault("ChatLogs.Public", false); + m_configs[CONFIG_CHATLOG_ADDON] = sConfig.GetBoolDefault("ChatLogs.Addon", false); + m_configs[CONFIG_CHATLOG_BGROUND] = sConfig.GetBoolDefault("ChatLogs.BattleGround", false); } /// Initialize the World @@ -1068,7 +1134,7 @@ void World::SetInitialWorldSettings() } ///- Loading strings. Getting no records means core load has to be canceled because no error message can be output. - sLog.outString( "" ); + sLog.outString(); sLog.outString( "Loading Trinity strings..." ); if (!objmgr.LoadTrinityStrings()) exit(1); // Error message displayed in function already @@ -1092,7 +1158,7 @@ void World::SetInitialWorldSettings() sLog.outString( "Loading Script Names..."); objmgr.LoadScriptNames(); - sLog.outString( "Loading InstanceTemplate" ); + sLog.outString( "Loading InstanceTemplate..." ); objmgr.LoadInstanceTemplate(); sLog.outString( "Loading SkillLineAbilityMultiMap Data..." ); @@ -1100,11 +1166,12 @@ void World::SetInitialWorldSettings() ///- Clean up and pack instances sLog.outString( "Cleaning up instances..." ); - sInstanceSaveManager.CleanupInstances(); // must be called before `creature_respawn`/`gameobject_respawn` tables + sInstanceSaveManager.CleanupInstances(); // must be called before `creature_respawn`/`gameobject_respawn` tables sLog.outString( "Packing instances..." ); sInstanceSaveManager.PackInstances(); + sLog.outString(); sLog.outString( "Loading Localization strings..." ); objmgr.LoadCreatureLocales(); objmgr.LoadGameObjectLocales(); @@ -1113,7 +1180,10 @@ void World::SetInitialWorldSettings() objmgr.LoadNpcTextLocales(); objmgr.LoadPageTextLocales(); objmgr.LoadNpcOptionLocales(); + objmgr.LoadPointOfInterestLocales(); objmgr.SetDBCLocaleIndex(GetDefaultDbcLocale()); // Get once for all the locale index of DBC language (console/broadcasts) + sLog.outString( ">>> Localization strings loaded" ); + sLog.outString(); sLog.outString( "Loading Page Texts..." ); objmgr.LoadPageTexts(); @@ -1142,6 +1212,9 @@ void World::SetInitialWorldSettings() sLog.outString( "Loading Spell Proc Event conditions..." ); spellmgr.LoadSpellProcEvents(); + sLog.outString( "Loading Spell Bonus Data..." ); + spellmgr.LoadSpellBonusess(); + sLog.outString( "Loading Aggro Spells Definitions..."); spellmgr.LoadSpellThreats(); @@ -1172,6 +1245,9 @@ void World::SetInitialWorldSettings() sLog.outString( "Loading Creature Reputation OnKill Data..." ); objmgr.LoadReputationOnKill(); + sLog.outString( "Loading Points Of Interest Data..." ); + objmgr.LoadPointsOfInterest(); + sLog.outString( "Loading Pet Create Spells..." ); objmgr.LoadPetCreateSpells(); @@ -1179,7 +1255,10 @@ void World::SetInitialWorldSettings() objmgr.LoadCreatures(); sLog.outString( "Loading Creature Addon Data..." ); + sLog.outString(); objmgr.LoadCreatureAddons(); // must be after LoadCreatureTemplates() and LoadCreatures() + sLog.outString( ">>> Creature Addon Data loaded" ); + sLog.outString(); sLog.outString( "Loading Creature Respawn Data..." ); // must be after PackInstances() objmgr.LoadCreatureRespawnTimes(); @@ -1190,8 +1269,14 @@ void World::SetInitialWorldSettings() sLog.outString( "Loading Gameobject Respawn Data..." ); // must be after PackInstances() objmgr.LoadGameobjectRespawnTimes(); + sLog.outString( "Loading Objects Pooling Data..."); + poolhandler.LoadFromDB(); + sLog.outString( "Loading Game Event Data..."); + sLog.outString(); gameeventmgr.LoadFromDB(); + sLog.outString( ">>> Game Event Data loaded" ); + sLog.outString(); sLog.outString( "Loading Weather Data..." ); objmgr.LoadWeatherZoneChances(); @@ -1200,7 +1285,13 @@ void World::SetInitialWorldSettings() objmgr.LoadQuests(); // must be loaded after DBCs, creature_template, item_template, gameobject tables sLog.outString( "Loading Quests Relations..." ); + sLog.outString(); objmgr.LoadQuestRelations(); // must be after quest load + sLog.outString( ">>> Quests Relations loaded" ); + sLog.outString(); + + sLog.outString( "Loading SpellArea Data..." ); // must be after quest load + spellmgr.LoadSpellAreas(); sLog.outString( "Loading AreaTrigger definitions..." ); objmgr.LoadAreaTriggerTeleports(); @@ -1229,14 +1320,20 @@ void World::SetInitialWorldSettings() sLog.outString( "Loading spell pet auras..." ); spellmgr.LoadSpellPetAuras(); + sLog.outString( "Loading pet levelup spells..." ); + spellmgr.LoadPetLevelupSpellMap(); + sLog.outString( "Loading spell extra attributes...(TODO)" ); spellmgr.LoadSpellCustomAttr(); sLog.outString( "Loading linked spells..." ); spellmgr.LoadSpellLinked(); - sLog.outString( "Loading player Create Info & Level Stats..." ); + sLog.outString( "Loading Player Create Info & Level Stats..." ); + sLog.outString(); objmgr.LoadPlayerInfo(); + sLog.outString( ">>> Player Create Info & Level Stats loaded" ); + sLog.outString(); sLog.outString( "Loading Exploration BaseXP Data..." ); objmgr.LoadExplorationBaseXP(); @@ -1257,7 +1354,10 @@ void World::SetInitialWorldSettings() objmgr.LoadSpellDisabledEntrys(); sLog.outString( "Loading Loot Tables..." ); + sLog.outString(); LoadLootTables(); + sLog.outString( ">>> Loot Tables loaded" ); + sLog.outString(); sLog.outString( "Loading Skill Discovery Table..." ); LoadSkillDiscoveryTable(); @@ -1268,10 +1368,24 @@ void World::SetInitialWorldSettings() sLog.outString( "Loading Skill Fishing base level requirements..." ); objmgr.LoadFishingBaseSkillLevel(); + sLog.outString( "Loading Achievements..." ); + sLog.outString(); + achievementmgr.LoadAchievementReferenceList(); + achievementmgr.LoadAchievementCriteriaList(); + achievementmgr.LoadAchievementCriteriaData(); + achievementmgr.LoadRewards(); + achievementmgr.LoadRewardLocales(); + achievementmgr.LoadCompletedAchievements(); + sLog.outString( ">>> Achievements loaded" ); + sLog.outString(); + ///- Load dynamic data tables from the database sLog.outString( "Loading Auctions..." ); + sLog.outString(); auctionmgr.LoadAuctionItems(); auctionmgr.LoadAuctions(); + sLog.outString( ">>> Auctions loaded" ); + sLog.outString(); sLog.outString( "Loading Guilds..." ); objmgr.LoadGuilds(); @@ -1285,11 +1399,11 @@ void World::SetInitialWorldSettings() sLog.outString( "Loading ReservedNames..." ); objmgr.LoadReservedPlayersNames(); - sLog.outString( "Loading GameObject for quests..." ); + sLog.outString( "Loading GameObjects for quests..." ); objmgr.LoadGameObjectForQuests(); sLog.outString( "Loading BattleMasters..." ); - objmgr.LoadBattleMastersEntry(); + sBattleGroundMgr.LoadBattleMastersEntry(); sLog.outString( "Loading GameTeleports..." ); objmgr.LoadGameTele(); @@ -1300,13 +1414,14 @@ void World::SetInitialWorldSettings() sLog.outString( "Loading Npc Options..." ); objmgr.LoadNpcOptions(); - sLog.outString( "Loading vendors..." ); + sLog.outString( "Loading Vendors..." ); objmgr.LoadVendors(); // must be after load CreatureTemplate and ItemTemplate - sLog.outString( "Loading trainers..." ); + sLog.outString( "Loading Trainers..." ); objmgr.LoadTrainerSpell(); // must be after load CreatureTemplate sLog.outString( "Loading Waypoints..." ); + sLog.outString(); WaypointMgr.Load(); sLog.outString( "Loading Creature Formations..." ); @@ -1321,16 +1436,28 @@ void World::SetInitialWorldSettings() ///- Load and initialize scripts sLog.outString( "Loading Scripts..." ); + sLog.outString(); objmgr.LoadQuestStartScripts(); // must be after load Creature/Gameobject(Template/Data) and QuestTemplate objmgr.LoadQuestEndScripts(); // must be after load Creature/Gameobject(Template/Data) and QuestTemplate objmgr.LoadSpellScripts(); // must be after load Creature/Gameobject(Template/Data) objmgr.LoadGameObjectScripts(); // must be after load Creature/Gameobject(Template/Data) objmgr.LoadEventScripts(); // must be after load Creature/Gameobject(Template/Data) objmgr.LoadWaypointScripts(); + sLog.outString( ">>> Scripts loaded" ); + sLog.outString(); sLog.outString( "Loading Scripts text locales..." ); // must be after Load*Scripts calls objmgr.LoadDbScriptStrings(); + sLog.outString( "Loading CreatureEventAI Texts..."); + CreatureEAI_Mgr.LoadCreatureEventAI_Texts(); + + sLog.outString( "Loading CreatureEventAI Summons..."); + CreatureEAI_Mgr.LoadCreatureEventAI_Summons(); + + sLog.outString( "Loading CreatureEventAI Scripts..."); + CreatureEAI_Mgr.LoadCreatureEventAI_Scripts(); + sLog.outString( "Initializing Scripts..." ); if(!LoadScriptingModule()) exit(1); @@ -1348,23 +1475,26 @@ void World::SetInitialWorldSettings() sprintf( isoDate, "%04d-%02d-%02d %02d:%02d:%02d", local.tm_year+1900, local.tm_mon+1, local.tm_mday, local.tm_hour, local.tm_min, local.tm_sec); - WorldDatabase.PExecute("INSERT INTO uptime (startstring, starttime, uptime) VALUES('%s', " I64FMTD ", 0)", - isoDate, uint64(m_startTime)); + LoginDatabase.PExecute("INSERT INTO uptime (realmid, starttime, startstring, uptime) VALUES('%u', " I64FMTD ", '%s', 0)", + realmID, uint64(m_startTime), isoDate); m_timers[WUPDATE_OBJECTS].SetInterval(0); m_timers[WUPDATE_SESSIONS].SetInterval(0); - m_timers[WUPDATE_WEATHERS].SetInterval(1000); - m_timers[WUPDATE_AUCTIONS].SetInterval(MINUTE*1000); //set auction update interval to 1 minute - m_timers[WUPDATE_UPTIME].SetInterval(m_configs[CONFIG_UPTIME_UPDATE]*MINUTE*1000); + m_timers[WUPDATE_WEATHERS].SetInterval(1*IN_MILISECONDS); + m_timers[WUPDATE_AUCTIONS].SetInterval(MINUTE*IN_MILISECONDS); + m_timers[WUPDATE_UPTIME].SetInterval(m_configs[CONFIG_UPTIME_UPDATE]*MINUTE*IN_MILISECONDS); //Update "uptime" table based on configuration entry in minutes. - m_timers[WUPDATE_CORPSES].SetInterval(20*MINUTE*1000); //erase corpses every 20 minutes + m_timers[WUPDATE_CORPSES].SetInterval(20*MINUTE*IN_MILISECONDS); + //erase corpses every 20 minutes + m_timers[WUPDATE_CLEANDB].SetInterval(m_configs[CONFIG_LOGDB_CLEARINTERVAL]*MINUTE*IN_MILISECONDS); + // clean logs table every 14 days by default //to set mailtimer to return mails every day between 4 and 5 am //mailtimer is increased when updating auctions //one second is 1000 -(tested on win system) - mail_timer = ((((localtime( &m_gameTime )->tm_hour + 20) % 24)* HOUR * 1000) / m_timers[WUPDATE_AUCTIONS].GetInterval() ); + mail_timer = ((((localtime( &m_gameTime )->tm_hour + 20) % 24)* HOUR * IN_MILISECONDS) / m_timers[WUPDATE_AUCTIONS].GetInterval() ); //1440 - mail_timer_expires = ( (DAY * 1000) / (m_timers[WUPDATE_AUCTIONS].GetInterval())); + mail_timer_expires = ( (DAY * IN_MILISECONDS) / (m_timers[WUPDATE_AUCTIONS].GetInterval())); sLog.outDebug("Mail timer set to: %u, mail return is called every %u minutes", mail_timer, mail_timer_expires); ///- Initilize static helper structures @@ -1397,6 +1527,9 @@ void World::SetInitialWorldSettings() sLog.outString("Calculate next daily quest reset time..." ); InitDailyQuestResetTime(); + sLog.outString("Starting objects Pooling system..." ); + poolhandler.Initialize(); + sLog.outString("Starting Game Event system..." ); uint32 nextGameEvent = gameeventmgr.Initialize(); m_timers[WUPDATE_EVENTS].SetInterval(nextGameEvent); //depend on next event @@ -1404,6 +1537,19 @@ void World::SetInitialWorldSettings() sLog.outString("Initialize AuctionHouseBot..."); AuctionHouseBotInit(); + // possibly enable db logging; avoid massive startup spam by doing it here. + if (sLog.GetLogDBLater()) + { + sLog.outString("Enabling database logging..."); + sLog.SetLogDBLater(false); + sLog.SetLogDB(true); + } + else + { + sLog.SetLogDB(false); + sLog.SetLogDBLater(false); + } + sLog.outString( "WORLD: World initialized" ); } @@ -1448,6 +1594,7 @@ void World::DetectDBCLang() m_defaultDbcLocale = LocaleConstant(default_locale); sLog.outString("Using %s DBC Locale as default. All available DBC locales: %s",localeNames[m_defaultDbcLocale],availableLocalsStr.empty() ? "<none>" : availableLocalsStr.c_str()); + sLog.outString(); } void World::RecordTimeDiff(const char *text, ...) @@ -1477,7 +1624,7 @@ void World::RecordTimeDiff(const char *text, ...) } /// Update the World ! -void World::Update(time_t diff) +void World::Update(uint32 diff) { m_updateTime = uint32(diff); if(m_configs[CONFIG_INTERVAL_LOG_UPDATE]) @@ -1524,7 +1671,8 @@ void World::Update(time_t diff) mail_timer = 0; objmgr.ReturnOrDeleteOldMails(true); } - ///-Handle expired auctions + + ///- Handle expired auctions auctionmgr.Update(); } @@ -1563,10 +1711,24 @@ void World::Update(time_t diff) if (m_timers[WUPDATE_UPTIME].Passed()) { uint32 tmpDiff = (m_gameTime - m_startTime); - uint32 maxClientsNum = sWorld.GetMaxActiveSessionCount(); + uint32 maxClientsNum = GetMaxActiveSessionCount(); m_timers[WUPDATE_UPTIME].Reset(); - WorldDatabase.PExecute("UPDATE uptime SET uptime = %d, maxplayers = %d WHERE starttime = " I64FMTD, tmpDiff, maxClientsNum, uint64(m_startTime)); + LoginDatabase.PExecute("UPDATE uptime SET uptime = %u, maxplayers = %u WHERE realmid = %u AND starttime = " I64FMTD, tmpDiff, maxClientsNum, realmID, uint64(m_startTime)); + } + + /// <li> Clean logs table + if(sWorld.getConfig(CONFIG_LOGDB_CLEARTIME) > 0) // if not enabled, ignore the timer + { + if (m_timers[WUPDATE_CLEANDB].Passed()) + { + uint32 tmpDiff = (m_gameTime - m_startTime); + uint32 maxClientsNum = sWorld.GetMaxActiveSessionCount(); + + m_timers[WUPDATE_CLEANDB].Reset(); + LoginDatabase.PExecute("DELETE FROM logs WHERE (time + %u) < "I64FMTD";", + sWorld.getConfig(CONFIG_LOGDB_CLEARTIME), uint64(time(0))); + } } /// <li> Handle all other objects @@ -1718,6 +1880,9 @@ void World::ScriptsProcess() case HIGHGUID_PET: source = HashMapHolder<Pet>::Find(step.sourceGUID); break; + case HIGHGUID_VEHICLE: + source = HashMapHolder<Vehicle>::Find(step.sourceGUID); + break; case HIGHGUID_PLAYER: source = HashMapHolder<Player>::Find(step.sourceGUID); break; @@ -1757,6 +1922,9 @@ void World::ScriptsProcess() case HIGHGUID_PET: target = HashMapHolder<Pet>::Find(step.targetGUID); break; + case HIGHGUID_VEHICLE: + target = HashMapHolder<Vehicle>::Find(step.targetGUID); + break; case HIGHGUID_PLAYER: // empty GUID case also target = HashMapHolder<Player>::Find(step.targetGUID); break; @@ -1865,7 +2033,7 @@ void World::ScriptsProcess() sLog.outError("SCRIPT_COMMAND_MOVE_TO call for non-creature (TypeId: %u), skipping.",source->GetTypeId()); break; } - ((Unit *)source)->SendMonsterMoveWithSpeed(step.script->x, step.script->y, step.script->z, ((Unit *)source)->GetUnitMovementFlags(), step.script->datalong2 ); + ((Unit *)source)->SendMonsterMoveWithSpeed(step.script->x, step.script->y, step.script->z, step.script->datalong2 ); ((Unit *)source)->GetMap()->CreatureRelocation(((Creature *)source), step.script->x, step.script->y, step.script->z, 0); break; case SCRIPT_COMMAND_FLAG_SET: @@ -1987,8 +2155,8 @@ void World::ScriptsProcess() Cell cell(p); cell.data.Part.reserved = ALL_DISTRICT; - Trinity::GameObjectWithDbGUIDCheck go_check(*summoner,step.script->datalong); - Trinity::GameObjectSearcher<Trinity::GameObjectWithDbGUIDCheck> checker(go,go_check); + MaNGOS::GameObjectWithDbGUIDCheck go_check(*summoner,step.script->datalong); + MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(summoner, go,go_check); TypeContainerVisitor<Trinity::GameObjectSearcher<Trinity::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker); CellLock<GridReadGuard> cell_lock(cell, p); @@ -2048,8 +2216,8 @@ void World::ScriptsProcess() Cell cell(p); cell.data.Part.reserved = ALL_DISTRICT; - Trinity::GameObjectWithDbGUIDCheck go_check(*caster,step.script->datalong); - Trinity::GameObjectSearcher<Trinity::GameObjectWithDbGUIDCheck> checker(door,go_check); + MaNGOS::GameObjectWithDbGUIDCheck go_check(*caster,step.script->datalong); + MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(caster,door,go_check); TypeContainerVisitor<Trinity::GameObjectSearcher<Trinity::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker); CellLock<GridReadGuard> cell_lock(cell, p); @@ -2104,8 +2272,8 @@ void World::ScriptsProcess() Cell cell(p); cell.data.Part.reserved = ALL_DISTRICT; - Trinity::GameObjectWithDbGUIDCheck go_check(*caster,step.script->datalong); - Trinity::GameObjectSearcher<Trinity::GameObjectWithDbGUIDCheck> checker(door,go_check); + MaNGOS::GameObjectWithDbGUIDCheck go_check(*caster,step.script->datalong); + MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(caster,door,go_check); TypeContainerVisitor<Trinity::GameObjectSearcher<Trinity::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker); CellLock<GridReadGuard> cell_lock(cell, p); @@ -2257,24 +2425,40 @@ void World::ScriptsProcess() break; } - Object* cmdTarget = step.script->datalong2 ? source : target; + Object* cmdTarget = step.script->datalong2 & 0x01 ? source : target; if(!cmdTarget) { - sLog.outError("SCRIPT_COMMAND_CAST_SPELL call for NULL %s.",step.script->datalong2 ? "source" : "target"); + sLog.outError("SCRIPT_COMMAND_CAST_SPELL call for NULL %s.",step.script->datalong2 & 0x01 ? "source" : "target"); break; } if(!cmdTarget->isType(TYPEMASK_UNIT)) { - sLog.outError("SCRIPT_COMMAND_CAST_SPELL %s isn't unit (TypeId: %u), skipping.",step.script->datalong2 ? "source" : "target",cmdTarget->GetTypeId()); + sLog.outError("SCRIPT_COMMAND_CAST_SPELL %s isn't unit (TypeId: %u), skipping.",step.script->datalong2 & 0x01 ? "source" : "target",cmdTarget->GetTypeId()); break; } Unit* spellTarget = (Unit*)cmdTarget; + Object* cmdSource = step.script->datalong2 & 0x02 ? target : source; + + if(!cmdSource) + { + sLog.outError("SCRIPT_COMMAND_CAST_SPELL call for NULL %s.",step.script->datalong2 & 0x02 ? "target" : "source"); + break; + } + + if(!cmdSource->isType(TYPEMASK_UNIT)) + { + sLog.outError("SCRIPT_COMMAND_CAST_SPELL %s isn't unit (TypeId: %u), skipping.",step.script->datalong2 & 0x02 ? "target" : "source", cmdSource->GetTypeId()); + break; + } + + Unit* spellSource = (Unit*)cmdSource; + //TODO: when GO cast implemented, code below must be updated accordingly to also allow GO spell cast - ((Unit*)source)->CastSpell(spellTarget,step.script->datalong,false); + spellSource->CastSpell(spellTarget,step.script->datalong,false); break; } @@ -2321,7 +2505,7 @@ void World::ScriptsProcess() //sLog.outDebug("Attempting to find Creature: Db GUID: %i", step.script->datalong); Trinity::CreatureWithDbGUIDCheck target_check(((Unit*)source), step.script->datalong); - Trinity::CreatureSearcher<Trinity::CreatureWithDbGUIDCheck> checker(target,target_check); + Trinity::CreatureSearcher<Trinity::CreatureWithDbGUIDCheck> checker(((Unit*)source), target, target_check); TypeContainerVisitor<Trinity::CreatureSearcher <Trinity::CreatureWithDbGUIDCheck>, GridTypeMapContainer > unit_checker(checker); CellLock<GridReadGuard> cell_lock(cell, p); @@ -2372,15 +2556,6 @@ void World::ScriptsProcess() break; } - case SCRIPT_COMMAND_PLAYSOUND: - { - if(!source) - break; - //datalong sound_id, datalong2 onlyself - ((WorldObject*)source)->SendPlaySound(step.script->datalong, step.script->datalong2); - break; - } - case SCRIPT_COMMAND_KILL: { if(!source || ((Creature*)source)->isDead()) @@ -2396,6 +2571,47 @@ void World::ScriptsProcess() break; } + case SCRIPT_COMMAND_PLAY_SOUND: + { + if(!source) + { + sLog.outError("SCRIPT_COMMAND_PLAY_SOUND call for NULL creature."); + break; + } + + WorldObject* pSource = dynamic_cast<WorldObject*>(source); + if(!pSource) + { + sLog.outError("SCRIPT_COMMAND_PLAY_SOUND call for non-world object (TypeId: %u), skipping.",source->GetTypeId()); + break; + } + + // bitmask: 0/1=anyone/target, 0/2=with distance dependent + Player* pTarget = NULL; + if(step.script->datalong2 & 1) + { + if(!target) + { + sLog.outError("SCRIPT_COMMAND_PLAY_SOUND in targeted mode call for NULL target."); + break; + } + + if(target->GetTypeId()!=TYPEID_PLAYER) + { + sLog.outError("SCRIPT_COMMAND_PLAY_SOUND in targeted mode call for non-player (TypeId: %u), skipping.",target->GetTypeId()); + break; + } + + pTarget = (Player*)target; + } + + // bitmask: 0/1=anyone/target, 0/2=with distance dependent + if(step.script->datalong2 & 2) + pSource->PlayDistanceSound(step.script->datalong,pTarget); + else + pSource->PlayDirectSound(step.script->datalong,pTarget); + break; + } default: sLog.outError("Unknown script command %u called.",step.script->command); break; @@ -2425,16 +2641,17 @@ void World::SendGlobalMessage(WorldPacket *packet, WorldSession *self, uint32 te } } +/// Send a packet to all GMs (except self if mentioned) void World::SendGlobalGMMessage(WorldPacket *packet, WorldSession *self, uint32 team) { SessionMap::iterator itr; - for (itr = m_sessions.begin(); itr != m_sessions.end(); itr++) + for (itr = m_sessions.begin(); itr != m_sessions.end(); ++itr) { if (itr->second && itr->second->GetPlayer() && itr->second->GetPlayer()->IsInWorld() && itr->second != self && - itr->second->GetSecurity() >SEC_PLAYER && + itr->second->GetSecurity() && (team == 0 || itr->second->GetPlayer()->GetTeam() == team) ) { itr->second->SendPacket(packet); @@ -2442,115 +2659,105 @@ void World::SendGlobalGMMessage(WorldPacket *packet, WorldSession *self, uint32 } } -/// Send a System Message to all players (except self if mentioned) -void World::SendWorldText(int32 string_id, ...) +namespace MaNGOS { - std::vector<std::vector<WorldPacket*> > data_cache; // 0 = default, i => i-1 locale index - - for(SessionMap::iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr) + class WorldWorldTextBuilder { - if(!itr->second || !itr->second->GetPlayer() || !itr->second->GetPlayer()->IsInWorld() ) - continue; + public: + typedef std::vector<WorldPacket*> WorldPacketList; + explicit WorldWorldTextBuilder(int32 textId, va_list* args = NULL) : i_textId(textId), i_args(args) {} + void operator()(WorldPacketList& data_list, int32 loc_idx) + { + char const* text = objmgr.GetMangosString(i_textId,loc_idx); - uint32 loc_idx = itr->second->GetSessionDbLocaleIndex(); - uint32 cache_idx = loc_idx+1; + if(i_args) + { + // we need copy va_list before use or original va_list will corrupted + va_list ap; + va_copy(ap,*i_args); - std::vector<WorldPacket*>* data_list; + char str [2048]; + vsnprintf(str,2048,text, ap ); + va_end(ap); - // create if not cached yet - if(data_cache.size() < cache_idx+1 || data_cache[cache_idx].empty()) - { - if(data_cache.size() < cache_idx+1) - data_cache.resize(cache_idx+1); + do_helper(data_list,&str[0]); + } + else + do_helper(data_list,(char*)text); + } + private: + char* lineFromMessage(char*& pos) { char* start = strtok(pos,"\n"); pos = NULL; return start; } + void do_helper(WorldPacketList& data_list, char* text) + { + char* pos = text; - data_list = &data_cache[cache_idx]; + while(char* line = lineFromMessage(pos)) + { + WorldPacket* data = new WorldPacket(); - char const* text = objmgr.GetTrinityString(string_id,loc_idx); + uint32 lineLength = (line ? strlen(line) : 0) + 1; - char buf[1000]; + data->Initialize(SMSG_MESSAGECHAT, 100); // guess size + *data << uint8(CHAT_MSG_SYSTEM); + *data << uint32(LANG_UNIVERSAL); + *data << uint64(0); + *data << uint32(0); // can be chat msg group or something + *data << uint64(0); + *data << uint32(lineLength); + *data << line; + *data << uint8(0); - va_list argptr; - va_start( argptr, string_id ); - vsnprintf( buf,1000, text, argptr ); - va_end( argptr ); + data_list.push_back(data); + } + } - char* pos = &buf[0]; + int32 i_textId; + va_list* i_args; + }; +} // namespace MaNGOS - while(char* line = ChatHandler::LineFromMessage(pos)) - { - WorldPacket* data = new WorldPacket(); - ChatHandler::FillMessageData(data, NULL, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, NULL, 0, line, NULL); - data_list->push_back(data); - } - } - else - data_list = &data_cache[cache_idx]; +/// Send a System Message to all players (except self if mentioned) +void World::SendWorldText(int32 string_id, ...) +{ + va_list ap; + va_start(ap, string_id); + + MaNGOS::WorldWorldTextBuilder wt_builder(string_id, &ap); + MaNGOS::LocalizedPacketListDo<MaNGOS::WorldWorldTextBuilder> wt_do(wt_builder); + for(SessionMap::iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr) + { + if(!itr->second || !itr->second->GetPlayer() || !itr->second->GetPlayer()->IsInWorld() ) + continue; - for(int i = 0; i < data_list->size(); ++i) - itr->second->SendPacket((*data_list)[i]); + wt_do(itr->second->GetPlayer()); } - // free memory - for(int i = 0; i < data_cache.size(); ++i) - for(int j = 0; j < data_cache[i].size(); ++j) - delete data_cache[i][j]; + va_end(ap); } +/// Send a System Message to all GMs (except self if mentioned) void World::SendGMText(int32 string_id, ...) { - std::vector<std::vector<WorldPacket*> > data_cache; // 0 = default, i => i-1 locale index + va_list ap; + va_start(ap, string_id); + MaNGOS::WorldWorldTextBuilder wt_builder(string_id, &ap); + MaNGOS::LocalizedPacketListDo<MaNGOS::WorldWorldTextBuilder> wt_do(wt_builder); for(SessionMap::iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr) { if(!itr->second || !itr->second->GetPlayer() || !itr->second->GetPlayer()->IsInWorld() ) continue; - uint32 loc_idx = itr->second->GetSessionDbLocaleIndex(); - uint32 cache_idx = loc_idx+1; - - std::vector<WorldPacket*>* data_list; - - // create if not cached yet - if(data_cache.size() < cache_idx+1 || data_cache[cache_idx].empty()) - { - if(data_cache.size() < cache_idx+1) - data_cache.resize(cache_idx+1); - - data_list = &data_cache[cache_idx]; - - char const* text = objmgr.GetTrinityString(string_id,loc_idx); - - char buf[1000]; - - va_list argptr; - va_start( argptr, string_id ); - vsnprintf( buf,1000, text, argptr ); - va_end( argptr ); - - char* pos = &buf[0]; - - while(char* line = ChatHandler::LineFromMessage(pos)) - { - WorldPacket* data = new WorldPacket(); - ChatHandler::FillMessageData(data, NULL, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, NULL, 0, line, NULL); - data_list->push_back(data); - } - } - else - data_list = &data_cache[cache_idx]; + if(!itr->second->GetSecurity()) + continue; - for(int i = 0; i < data_list->size(); ++i) - if(itr->second->GetSecurity() > SEC_PLAYER) - itr->second->SendPacket((*data_list)[i]); + wt_do(itr->second->GetPlayer()); } - // free memory - for(int i = 0; i < data_cache.size(); ++i) - for(int j = 0; j < data_cache[i].size(); ++j) - delete data_cache[i][j]; + va_end(ap); } -/// Send a System Message to all players (except self if mentioned) +/// DEPRICATED, only for debug purpose. Send a System Message to all players (except self if mentioned) void World::SendGlobalText(const char* text, WorldSession *self) { WorldPacket data; @@ -2803,7 +3010,7 @@ void World::ShutdownMsg(bool show, Player* player) { std::string str = secsToTimeString(m_ShutdownTimer); - uint32 msgid = (m_ShutdownMask & SHUTDOWN_MASK_RESTART) ? SERVER_MSG_RESTART_TIME : SERVER_MSG_SHUTDOWN_TIME; + ServerMessageType msgid = (m_ShutdownMask & SHUTDOWN_MASK_RESTART) ? SERVER_MSG_RESTART_TIME : SERVER_MSG_SHUTDOWN_TIME; SendServerMessage(msgid,str.c_str(),player); DEBUG_LOG("Server is %s in %s",(m_ShutdownMask & SHUTDOWN_MASK_RESTART ? "restart" : "shuttingdown"),str.c_str()); @@ -2817,7 +3024,7 @@ void World::ShutdownCancel() if(!m_ShutdownTimer || m_stopEvent) return; - uint32 msgid = (m_ShutdownMask & SHUTDOWN_MASK_RESTART) ? SERVER_MSG_RESTART_CANCELLED : SERVER_MSG_SHUTDOWN_CANCELLED; + ServerMessageType msgid = (m_ShutdownMask & SHUTDOWN_MASK_RESTART) ? SERVER_MSG_RESTART_CANCELLED : SERVER_MSG_SHUTDOWN_CANCELLED; m_ShutdownMask = 0; m_ShutdownTimer = 0; @@ -2828,7 +3035,7 @@ void World::ShutdownCancel() } /// Send a server message to the user(s) -void World::SendServerMessage(uint32 type, const char *text, Player* player) +void World::SendServerMessage(ServerMessageType type, const char *text, Player* player) { WorldPacket data(SMSG_SERVER_MESSAGE, 50); // guess size data << uint32(type); @@ -2841,7 +3048,7 @@ void World::SendServerMessage(uint32 type, const char *text, Player* player) SendGlobalMessage( &data ); } -void World::UpdateSessions( time_t diff ) +void World::UpdateSessions( uint32 diff ) { ///- Add new sessions while(!addSessQueue.empty()) @@ -2994,14 +3201,20 @@ void World::UpdateMaxSessionCounters() void World::LoadDBVersion() { QueryResult* result = WorldDatabase.Query("SELECT db_version FROM version LIMIT 1"); + //QueryResult* result = WorldDatabase.Query("SELECT version, creature_ai_version FROM db_version LIMIT 1"); if(result) { Field* fields = result->Fetch(); - m_DBVersion = fields[0].GetString(); + m_DBVersion = fields[0].GetCppString(); + //m_CreatureEventAIVersion = fields[1].GetCppString(); delete result; } - else - m_DBVersion = "unknown world database"; + + if(m_DBVersion.empty()) + m_DBVersion = "Unknown world database."; + + if(m_CreatureEventAIVersion.empty()) + m_CreatureEventAIVersion = "Unknown creature EventAI."; } diff --git a/src/game/World.h b/src/game/World.h index ff03f4124b0..27075885368 100644 --- a/src/game/World.h +++ b/src/game/World.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -45,6 +45,16 @@ class SqlResultQueue; class QueryResult; class WorldSocket; +// ServerMessages.dbc +enum ServerMessageType +{ + SERVER_MSG_SHUTDOWN_TIME = 1, + SERVER_MSG_RESTART_TIME = 2, + SERVER_MSG_STRING = 3, + SERVER_MSG_SHUTDOWN_CANCELLED = 4, + SERVER_MSG_RESTART_CANCELLED = 5 +}; + enum ShutdownMask { SHUTDOWN_MASK_RESTART = 1, @@ -68,7 +78,8 @@ enum WorldTimers WUPDATE_UPTIME = 4, WUPDATE_CORPSES = 5, WUPDATE_EVENTS = 6, - WUPDATE_COUNT = 7 + WUPDATE_CLEANDB = 7, + WUPDATE_COUNT = 8 }; /// Configuration elements @@ -104,9 +115,12 @@ enum WorldConfigs CONFIG_CHARACTERS_CREATING_DISABLED, CONFIG_CHARACTERS_PER_ACCOUNT, CONFIG_CHARACTERS_PER_REALM, + CONFIG_HEROIC_CHARACTERS_PER_REALM, + CONFIG_MIN_LEVEL_FOR_HEROIC_CHARACTER_CREATING, CONFIG_SKIP_CINEMATICS, CONFIG_MAX_PLAYER_LEVEL, CONFIG_START_PLAYER_LEVEL, + CONFIG_START_HEROIC_PLAYER_LEVEL, CONFIG_START_PLAYER_MONEY, CONFIG_MAX_HONOR_POINTS, CONFIG_START_HONOR_POINTS, @@ -114,21 +128,22 @@ enum WorldConfigs CONFIG_START_ARENA_POINTS, CONFIG_INSTANCE_IGNORE_LEVEL, CONFIG_INSTANCE_IGNORE_RAID, - CONFIG_BATTLEGROUND_CAST_DESERTER, - CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE, - CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY, CONFIG_INSTANCE_RESET_TIME_HOUR, CONFIG_INSTANCE_UNLOAD_DELAY, CONFIG_CAST_UNSTUCK, 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, CONFIG_GM_IN_WHO_LIST, 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, @@ -171,6 +186,8 @@ enum WorldConfigs CONFIG_DEATH_SICKNESS_LEVEL, CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP, CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE, + CONFIG_DEATH_BONES_WORLD, + CONFIG_DEATH_BONES_BG_OR_ARENA, CONFIG_THREAT_RADIUS, CONFIG_INSTANT_LOGOUT, CONFIG_DISABLE_BREATHING, @@ -179,12 +196,20 @@ enum WorldConfigs CONFIG_LISTEN_RANGE_SAY, CONFIG_LISTEN_RANGE_TEXTEMOTE, CONFIG_LISTEN_RANGE_YELL, + CONFIG_SKILL_MILLING, + CONFIG_BATTLEGROUND_CAST_DESERTER, + CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE, + CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY, + CONFIG_BATTLEGROUND_INVITATION_TYPE, + CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER, + CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH, CONFIG_ARENA_MAX_RATING_DIFFERENCE, CONFIG_ARENA_RATING_DISCARD_TIMER, CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS, CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS, - CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER, - + CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE, + CONFIG_ARENA_SEASON_ID, + CONFIG_ARENA_SEASON_IN_PROGRESS, CONFIG_MAX_WHO, CONFIG_BG_START_MUSIC, CONFIG_START_ALL_SPELLS, @@ -201,8 +226,18 @@ enum WorldConfigs CONFIG_INTERVAL_LOG_UPDATE, CONFIG_MIN_LOG_UPDATE, CONFIG_ENABLE_SINFO_LOGIN, - CONFIG_PREMATURE_BG_REWARD, - + CONFIG_OFFHAND_CHECK_AT_TALENTS_RESET, + CONFIG_CHATLOG_CHANNEL, + CONFIG_CHATLOG_WHISPER, + CONFIG_CHATLOG_SYSCHAN, + CONFIG_CHATLOG_PARTY, + CONFIG_CHATLOG_RAID, + CONFIG_CHATLOG_GUILD, + CONFIG_CHATLOG_PUBLIC, + CONFIG_CHATLOG_ADDON, + CONFIG_CHATLOG_BGROUND, + CONFIG_LOGDB_CLEARINTERVAL, + CONFIG_LOGDB_CLEARTIME, CONFIG_VALUE_COUNT }; @@ -213,6 +248,8 @@ enum Rates RATE_POWER_MANA, RATE_POWER_RAGE_INCOME, RATE_POWER_RAGE_LOSS, + RATE_POWER_RUNICPOWER_INCOME, + RATE_POWER_RUNICPOWER_LOSS, RATE_POWER_FOCUS, RATE_SKILL_DISCOVERY, RATE_DROP_ITEM_POOR, @@ -227,8 +264,9 @@ enum Rates RATE_XP_KILL, RATE_XP_QUEST, RATE_XP_EXPLORE, - RATE_XP_PAST_70, RATE_REPUTATION_GAIN, + RATE_REPUTATION_LOWLEVEL_KILL, + RATE_REPUTATION_LOWLEVEL_QUEST, RATE_CREATURE_NORMAL_HP, RATE_CREATURE_ELITE_ELITE_HP, RATE_CREATURE_ELITE_RAREELITE_HP, @@ -256,7 +294,6 @@ enum Rates RATE_MINING_AMOUNT, RATE_MINING_NEXT, RATE_TALENT, - RATE_LOYALTY, RATE_CORPSE_DECAY_LOOTED, RATE_INSTANCE_RESET_TIME, RATE_TARGET_POS_RECALCULATION_RANGE, @@ -328,12 +365,11 @@ enum RealmZone #define SCRIPT_COMMAND_CLOSE_DOOR 12 // source = unit, datalong=db_guid, datalong2=reset_delay #define SCRIPT_COMMAND_ACTIVATE_OBJECT 13 // source = unit, target=GO #define SCRIPT_COMMAND_REMOVE_AURA 14 // source (datalong2!=0) or target (datalong==0) unit, datalong = spell_id -#define SCRIPT_COMMAND_CAST_SPELL 15 // source (datalong2!=0) or target (datalong==0) unit, datalong = spell_id -#define SCRIPT_COMMAND_LOAD_PATH 16 // source = unit, path = datalong, repeatable datalong2 -#define SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT 17 // datalong scriptid, lowguid datalong2, dataint table -#define SCRIPT_COMMAND_PLAYSOUND 18 // datalong soundid, datalong2 play only self -#define SCRIPT_COMMAND_KILL 19 // datalong removecorpse - +#define SCRIPT_COMMAND_CAST_SPELL 15 // source/target cast spell at target/source (script->datalong2: 0: s->t 1: s->s 2: t->t 3: t->s +#define SCRIPT_COMMAND_PLAY_SOUND 16 // source = any object, target=any/player, datalong (sound_id), datalong2 (bitmask: 0/1=anyone/target, 0/2=with distance dependent, so 1|2 = 3 is target with distance dependent) +#define SCRIPT_COMMAND_LOAD_PATH 20 // source = unit, path = datalong, repeatable datalong2 +#define SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT 21 // datalong scriptid, lowguid datalong2, dataint table +#define SCRIPT_COMMAND_KILL 22 // datalong removecorpse /// Storage class for commands issued for delayed execution struct CliCommandHolder @@ -380,6 +416,12 @@ class World Weather* AddWeather(uint32 zone_id); void RemoveWeather(uint32 zone_id); + /// Deny clients? + bool IsClosed() { return m_isClosed; } + + /// Close world + void SetClosed(bool val) { m_isClosed = val; } + /// Get the active session server limit (or security level limitations) uint32 GetPlayerAmountLimit() const { return m_playerLimit >= 0 ? m_playerLimit : 0; } AccountTypes GetPlayerSecurityLimit() const { return m_playerLimit <= 0 ? AccountTypes(-m_playerLimit) : SEC_PLAYER; } @@ -443,7 +485,7 @@ class World void SendGlobalGMMessage(WorldPacket *packet, WorldSession *self = 0, uint32 team = 0); void SendZoneMessage(uint32 zone, WorldPacket *packet, WorldSession *self = 0, uint32 team = 0); void SendZoneText(uint32 zone, const char *text, WorldSession *self = 0, uint32 team = 0); - void SendServerMessage(uint32 type, const char *text = "", Player* player = NULL); + void SendServerMessage(ServerMessageType type, const char *text = "", Player* player = NULL); /// Are we in the middle of a shutdown? bool IsShutdowning() const { return m_ShutdownTimer > 0; } @@ -454,9 +496,9 @@ class World static void StopNow(uint8 exitcode) { m_stopEvent = true; m_ExitCode = exitcode; } static bool IsStopped() { return m_stopEvent; } - void Update(time_t diff); + void Update(uint32 diff); - void UpdateSessions( time_t diff ); + void UpdateSessions( uint32 diff ); /// Set a server rate (see #Rates) void setRate(Rates rate,float value) { rate_values[rate]=value; } /// Get a server rate (see #Rates) @@ -518,6 +560,7 @@ class World //used World DB version void LoadDBVersion(); char const* GetDBVersion() { return m_DBVersion.c_str(); } + char const* GetCreatureEventAIVersion() { return m_CreatureEventAIVersion.c_str(); } //used Script version void SetScriptsVersion(char const* version) { m_ScriptsVersion = version ? version : "unknown scripting library"; } @@ -538,6 +581,8 @@ class World uint32 m_ShutdownTimer; uint32 m_ShutdownMask; + bool m_isClosed; + time_t m_startTime; time_t m_gameTime; IntervalTimer m_timers[WUPDATE_COUNT]; @@ -596,6 +641,7 @@ class World //used versions std::string m_DBVersion; + std::string m_CreatureEventAIVersion; std::string m_ScriptsVersion; }; diff --git a/src/game/WorldLog.cpp b/src/game/WorldLog.cpp index a1065dced5c..d5bd3d0d181 100644 --- a/src/game/WorldLog.cpp +++ b/src/game/WorldLog.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,6 +25,7 @@ #include "WorldLog.h" #include "Policies/SingletonImp.h" #include "Config/ConfigEnv.h" +#include "Log.h" #define CLASS_LOCK Trinity::ClassLevelLockable<WorldLog, ZThread::FastMutex> INSTANTIATE_SINGLETON_2(WorldLog, CLASS_LOCK); @@ -32,6 +33,18 @@ INSTANTIATE_CLASS_MUTEX(WorldLog, ZThread::FastMutex); #define WORLD_LOG_FILE_STRING "world.log" +WorldLog::WorldLog() : i_file(NULL) +{ + Initialize(); +} + +WorldLog::~WorldLog() +{ + if( i_file != NULL ) + fclose(i_file); + i_file = NULL; +} + /// Open the log file (if specified so in the configuration file) void WorldLog::Initialize() { @@ -48,6 +61,35 @@ void WorldLog::Initialize() { i_file = fopen((logsDir+logname).c_str(), "w"); } + + m_dbWorld = sConfig.GetBoolDefault("LogDB.World", false); // can be VERY heavy if enabled +} + +void WorldLog::outLog(char const *fmt, ...) +{ + if( LogWorld() ) + { + Guard guard(*this); + ASSERT(i_file); + + va_list args; + va_start(args, fmt); + vfprintf(i_file, fmt, args); + fprintf(i_file, "\n" ); + va_end(args); + + fflush(i_file); + } + + if (sLog.GetLogDB() && m_dbWorld) + { + va_list ap2; + va_start(ap2, fmt); + char nnew_str[MAX_QUERY_LEN]; + vsnprintf(nnew_str, MAX_QUERY_LEN, fmt, ap2); + sLog.outDB(LOG_TYPE_WORLD, nnew_str); + va_end(ap2); + } } #define sWorldLog WorldLog::Instance() diff --git a/src/game/WorldLog.h b/src/game/WorldLog.h index 0a7c2a08067..4ba651809ee 100644 --- a/src/game/WorldLog.h +++ b/src/game/WorldLog.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,42 +35,25 @@ class TRINITY_DLL_DECL WorldLog : public Trinity::Singleton<WorldLog, Trinity::ClassLevelLockable<WorldLog, ZThread::FastMutex> > { friend class Trinity::OperatorNew<WorldLog>; - WorldLog() : i_file(NULL) { Initialize(); } + WorldLog(); WorldLog(const WorldLog &); WorldLog& operator=(const WorldLog &); typedef Trinity::ClassLevelLockable<WorldLog, ZThread::FastMutex>::Lock Guard; /// Close the file in destructor - ~WorldLog() - { - if( i_file != NULL ) - fclose(i_file); - i_file = NULL; - } + ~WorldLog(); public: void Initialize(); /// Is the world logger active? - inline bool LogWorld(void) const { return (i_file != NULL); } + bool LogWorld(void) const { return (i_file != NULL); } /// %Log to the file - inline void Log(char const *fmt, ...) - { - if( LogWorld() ) - { - Guard guard(*this); - ASSERT(i_file); - - va_list args; - va_start(args, fmt); - vfprintf(i_file, fmt, args); - va_end(args); - - fflush(i_file); - } - } + void outLog(char const *fmt, ...); private: FILE *i_file; + + bool m_dbWorld; }; #define sWorldLog WorldLog::Instance() diff --git a/src/game/WorldSession.cpp b/src/game/WorldSession.cpp index 5b1261e664a..da6837b1662 100644 --- a/src/game/WorldSession.cpp +++ b/src/game/WorldSession.cpp @@ -34,13 +34,12 @@ #include "Group.h" #include "Guild.h" #include "World.h" -#include "MapManager.h" #include "ObjectAccessor.h" #include "BattleGroundMgr.h" #include "OutdoorPvPMgr.h" -#include "Language.h" // for CMSG_CANCEL_MOUNT_AURA handler -#include "Chat.h" +//#include "Language.h" // for CMSG_CANCEL_MOUNT_AURA handler #include "SocialMgr.h" +#include "zlib/zlib.h" /// WorldSession constructor WorldSession::WorldSession(uint32 id, WorldSocket *sock, uint32 sec, uint8 expansion, time_t mute_time, LocaleConstant locale) : @@ -241,7 +240,7 @@ bool WorldSession::Update(uint32 /*diff*/) void WorldSession::LogoutPlayer(bool Save) { // finish pending transfers before starting the logout - while(_player && _player->IsBeingTeleported()) + while(_player && _player->IsBeingTeleportedFar()) HandleMoveWorldportAckOpcode(); m_playerLogout = true; @@ -298,25 +297,28 @@ void WorldSession::LogoutPlayer(bool Save) else if(_player->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION)) { // this will kill character by SPELL_AURA_SPIRIT_OF_REDEMPTION - _player->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT); + _player->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); //_player->SetDeathPvP(*); set at SPELL_AURA_SPIRIT_OF_REDEMPTION apply time _player->KillPlayer(); _player->BuildPlayerRepop(); _player->RepopAtGraveyard(); } + //drop a flag if player is carrying it + if(BattleGround *bg = _player->GetBattleGround()) + bg->EventPlayerLoggedOut(_player); - ///- Remove player from battleground (teleport to entrance) - if(_player->InBattleGround()) - _player->LeaveBattleground(); + ///- Teleport to home if the player is in an invalid instance + if(!_player->m_InstanceValid && !_player->isGameMaster()) + _player->TeleportTo(_player->m_homebindMapId, _player->m_homebindX, _player->m_homebindY, _player->m_homebindZ, _player->GetOrientation()); sOutdoorPvPMgr.HandlePlayerLeaveZone(_player,_player->GetZoneId()); for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) { - if(int32 bgTypeId = _player->GetBattleGroundQueueId(i)) + if(BattleGroundQueueTypeId bgQueueTypeId = _player->GetBattleGroundQueueTypeId(i)) { - _player->RemoveBattleGroundQueueId(bgTypeId); - sBattleGroundMgr.m_BattleGroundQueues[ bgTypeId ].RemovePlayer(_player->GetGUID(), true); + _player->RemoveBattleGroundQueueId(bgQueueTypeId); + sBattleGroundMgr.m_BattleGroundQueues[ bgQueueTypeId ].RemovePlayer(_player->GetGUID(), true); } } @@ -468,6 +470,13 @@ void WorldSession::SendNotification(int32 string_id,...) } } +void WorldSession::SendSetPhaseShift(uint32 PhaseShift) +{ + WorldPacket data(SMSG_SET_PHASE_SHIFT, 4); + data << uint32(PhaseShift); + SendPacket(&data); +} + const char * WorldSession::GetTrinityString( int32 entry ) const { return objmgr.GetTrinityString(entry,GetSessionDbLocaleIndex()); @@ -518,3 +527,212 @@ void WorldSession::SendAuthWaitQue(uint32 position) } } +void WorldSession::LoadAccountData() +{ + for (uint32 i = 0; i < NUM_ACCOUNT_DATA_TYPES; ++i) + { + AccountData data; + m_accountData[i] = data; + } + + QueryResult *result = CharacterDatabase.PQuery("SELECT type, time, data FROM account_data WHERE account='%u'", GetAccountId()); + + if(!result) + return; + + do + { + Field *fields = result->Fetch(); + + uint32 type = fields[0].GetUInt32(); + if(type < NUM_ACCOUNT_DATA_TYPES) + { + m_accountData[type].Time = fields[1].GetUInt32(); + m_accountData[type].Data = fields[2].GetCppString(); + } + } while (result->NextRow()); + + delete result; +} + +void WorldSession::SetAccountData(uint32 type, time_t time_, std::string data) +{ + m_accountData[type].Time = time_; + m_accountData[type].Data = data; + + uint32 acc = GetAccountId(); + + CharacterDatabase.BeginTransaction (); + CharacterDatabase.PExecute("DELETE FROM account_data WHERE account='%u' AND type='%u'", acc, type); + CharacterDatabase.escape_string(data); + CharacterDatabase.PExecute("INSERT INTO account_data VALUES ('%u','%u','%u','%s')", acc, type, (uint32)time_, data.c_str()); + CharacterDatabase.CommitTransaction (); +} + +void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo *mi) +{ + CHECK_PACKET_SIZE(data, data.rpos()+4+2+4+4+4+4+4); + data >> mi->flags; + data >> mi->unk1; + data >> mi->time; + data >> mi->x; + data >> mi->y; + data >> mi->z; + data >> mi->o; + + if(mi->flags & MOVEMENTFLAG_ONTRANSPORT) + { + CHECK_PACKET_SIZE(data, data.rpos()+8+4+4+4+4+4+1); + data >> mi->t_guid; + data >> mi->t_x; + data >> mi->t_y; + data >> mi->t_z; + data >> mi->t_o; + data >> mi->t_time; + data >> mi->t_seat; + } + + if((mi->flags & (MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING2)) || (mi->unk1 & 0x20)) + { + CHECK_PACKET_SIZE(data, data.rpos()+4); + data >> mi->s_pitch; + } + + CHECK_PACKET_SIZE(data, data.rpos()+4); + data >> mi->fallTime; + + if(mi->flags & MOVEMENTFLAG_JUMPING) + { + CHECK_PACKET_SIZE(data, data.rpos()+4+4+4+4); + data >> mi->j_unk; + data >> mi->j_sinAngle; + data >> mi->j_cosAngle; + data >> mi->j_xyspeed; + } + + if(mi->flags & MOVEMENTFLAG_SPLINE) + { + CHECK_PACKET_SIZE(data, data.rpos()+4); + data >> mi->u_unk1; + } +} + +void WorldSession::ReadAddonsInfo(WorldPacket &data) +{ + if (data.rpos() + 4 > data.size()) + return; + uint32 size; + data >> size; + + if(!size) + return; + + uLongf uSize = size; + + uint32 pos = data.rpos(); + + ByteBuffer addonInfo; + addonInfo.resize(size); + + if (uncompress(const_cast<uint8*>(addonInfo.contents()), &uSize, const_cast<uint8*>(data.contents() + pos), data.size() - pos) == Z_OK) + { + uint32 addonsCount; + addonInfo >> addonsCount; // addons count + + for(uint32 i = 0; i < addonsCount; ++i) + { + std::string addonName; + uint8 enabled; + uint32 crc, unk1; + + // check next addon data format correctness + if(addonInfo.rpos()+1 > addonInfo.size()) + return; + + addonInfo >> addonName; + + // recheck next addon data format correctness + if(addonInfo.rpos()+1+4+4 > addonInfo.size()) + return; + + addonInfo >> enabled >> crc >> unk1; + + sLog.outDebug("ADDON: Name: %s, Enabled: 0x%x, CRC: 0x%x, Unknown2: 0x%x", addonName.c_str(), enabled, crc, unk1); + + m_addonsList.push_back(AddonInfo(addonName, enabled, crc)); + } + + uint32 unk2; + addonInfo >> unk2; + + if(addonInfo.rpos() != addonInfo.size()) + sLog.outDebug("packet under read!"); + } + else + sLog.outError("Addon packet uncompress error!"); +} + +void WorldSession::SendAddonsInfo() +{ + unsigned char tdata[256] = + { + 0xC3, 0x5B, 0x50, 0x84, 0xB9, 0x3E, 0x32, 0x42, 0x8C, 0xD0, 0xC7, 0x48, 0xFA, 0x0E, 0x5D, 0x54, + 0x5A, 0xA3, 0x0E, 0x14, 0xBA, 0x9E, 0x0D, 0xB9, 0x5D, 0x8B, 0xEE, 0xB6, 0x84, 0x93, 0x45, 0x75, + 0xFF, 0x31, 0xFE, 0x2F, 0x64, 0x3F, 0x3D, 0x6D, 0x07, 0xD9, 0x44, 0x9B, 0x40, 0x85, 0x59, 0x34, + 0x4E, 0x10, 0xE1, 0xE7, 0x43, 0x69, 0xEF, 0x7C, 0x16, 0xFC, 0xB4, 0xED, 0x1B, 0x95, 0x28, 0xA8, + 0x23, 0x76, 0x51, 0x31, 0x57, 0x30, 0x2B, 0x79, 0x08, 0x50, 0x10, 0x1C, 0x4A, 0x1A, 0x2C, 0xC8, + 0x8B, 0x8F, 0x05, 0x2D, 0x22, 0x3D, 0xDB, 0x5A, 0x24, 0x7A, 0x0F, 0x13, 0x50, 0x37, 0x8F, 0x5A, + 0xCC, 0x9E, 0x04, 0x44, 0x0E, 0x87, 0x01, 0xD4, 0xA3, 0x15, 0x94, 0x16, 0x34, 0xC6, 0xC2, 0xC3, + 0xFB, 0x49, 0xFE, 0xE1, 0xF9, 0xDA, 0x8C, 0x50, 0x3C, 0xBE, 0x2C, 0xBB, 0x57, 0xED, 0x46, 0xB9, + 0xAD, 0x8B, 0xC6, 0xDF, 0x0E, 0xD6, 0x0F, 0xBE, 0x80, 0xB3, 0x8B, 0x1E, 0x77, 0xCF, 0xAD, 0x22, + 0xCF, 0xB7, 0x4B, 0xCF, 0xFB, 0xF0, 0x6B, 0x11, 0x45, 0x2D, 0x7A, 0x81, 0x18, 0xF2, 0x92, 0x7E, + 0x98, 0x56, 0x5D, 0x5E, 0x69, 0x72, 0x0A, 0x0D, 0x03, 0x0A, 0x85, 0xA2, 0x85, 0x9C, 0xCB, 0xFB, + 0x56, 0x6E, 0x8F, 0x44, 0xBB, 0x8F, 0x02, 0x22, 0x68, 0x63, 0x97, 0xBC, 0x85, 0xBA, 0xA8, 0xF7, + 0xB5, 0x40, 0x68, 0x3C, 0x77, 0x86, 0x6F, 0x4B, 0xD7, 0x88, 0xCA, 0x8A, 0xD7, 0xCE, 0x36, 0xF0, + 0x45, 0x6E, 0xD5, 0x64, 0x79, 0x0F, 0x17, 0xFC, 0x64, 0xDD, 0x10, 0x6F, 0xF3, 0xF5, 0xE0, 0xA6, + 0xC3, 0xFB, 0x1B, 0x8C, 0x29, 0xEF, 0x8E, 0xE5, 0x34, 0xCB, 0xD1, 0x2A, 0xCE, 0x79, 0xC3, 0x9A, + 0x0D, 0x36, 0xEA, 0x01, 0xE0, 0xAA, 0x91, 0x20, 0x54, 0xF0, 0x72, 0xD8, 0x1E, 0xC7, 0x89, 0xD2 + }; + + WorldPacket data(SMSG_ADDON_INFO, 4); + + for(AddonsList::iterator itr = m_addonsList.begin(); itr != m_addonsList.end(); ++itr) + { + uint8 state = (itr->Enabled ? 2 : 1); + data << uint8(state); + + uint8 unk1 = (itr->Enabled ? 1 : 0); + data << uint8(unk1); + if (unk1) + { + uint8 unk2 = (itr->CRC != 0x4c1c776d); // If addon is Standard addon CRC + data << uint8(unk2); + if (unk2) + data.append(tdata, sizeof(tdata)); + + data << uint32(0); + } + + uint8 unk3 = (itr->Enabled ? 0 : 1); + data << uint8(unk3); + if (unk3) + { + // String, 256 (null terminated?) + data << uint8(0); + } + } + + m_addonsList.clear(); + + uint32 count = 0; + data << uint32(count); + /*for(uint32 i = 0; i < count; ++i) + { + uint32 + string (16 bytes) + string (16 bytes) + uint32 + }*/ + + SendPacket(&data); +} diff --git a/src/game/WorldSession.h b/src/game/WorldSession.h index 1461d47fd2b..0848ef63ecb 100644 --- a/src/game/WorldSession.h +++ b/src/game/WorldSession.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,6 +26,7 @@ #define __WORLDSESSION_H #include "Common.h" +#include "SharedDefines.h" class MailItemsInfo; struct ItemPrototype; @@ -47,6 +48,32 @@ class CharacterHandler; #define CHECK_PACKET_SIZE(P,S) if((P).size() < (S)) return SizeError((P),(S)); +#define NUM_ACCOUNT_DATA_TYPES 8 + +struct AccountData +{ + AccountData() : Time(0), Data("") {} + + time_t Time; + std::string Data; +}; + +struct AddonInfo +{ + AddonInfo(std::string name, uint8 enabled, uint32 crc) + { + Name = name; + Enabled = enabled; + CRC = crc; + } + + std::string Name; + uint8 Enabled; + uint32 CRC; +}; + +typedef std::list<AddonInfo> AddonsList; + enum PartyOperation { PARTY_OP_INVITE = 0, @@ -81,6 +108,11 @@ class TRINITY_DLL_SPEC WorldSession void SizeError(WorldPacket const& packet, uint32 size) const; + void ReadAddonsInfo(WorldPacket &data); + void SendAddonsInfo(); + + void ReadMovementInfo(WorldPacket &data, MovementInfo *mi); + void SendPacket(WorldPacket const* packet); void SendNotification(const char *format,...) ATTR_PRINTF(2,3); void SendNotification(int32 string_id,...); @@ -88,6 +120,7 @@ class TRINITY_DLL_SPEC WorldSession void SendLfgResult(uint32 type, uint32 entry, uint8 lfg_type); void SendPartyResult(PartyOperation operation, const std::string& member, PartyResult res); void SendAreaTriggerMessage(const char* Text, ...) ATTR_PRINTF(2,3); + void SendSetPhaseShift(uint32 phaseShift); uint32 GetSecurity() const { return _security; } uint32 GetAccountId() const { return _accountId; } @@ -141,7 +174,7 @@ class TRINITY_DLL_SPEC WorldSession void SendAttackStop(Unit const* enemy); - void SendBattlegGroundList( uint64 guid, uint32 bgTypeId ); + void SendBattlegGroundList( uint64 guid, BattleGroundTypeId bgTypeId ); void SendTradeStatus(uint32 status); void SendCancelTrade(); @@ -153,6 +186,11 @@ class TRINITY_DLL_SPEC WorldSession //pet void SendPetNameQuery(uint64 guid, uint32 petnumber); + // Account Data + AccountData *GetAccountData(uint32 type) { return &m_accountData[type]; } + void SetAccountData(uint32 type, time_t time_, std::string data); + void LoadAccountData(); + //mail //used with item_page table bool SendItemInfo( uint32 itemid, WorldPacket data ); @@ -309,6 +347,7 @@ class TRINITY_DLL_SPEC WorldSession void HandleGameObjectUseOpcode(WorldPacket& recPacket); void HandleMeetingStoneInfo(WorldPacket& recPacket); + void HandleGameobjectReportUse(WorldPacket& recvPacket); void HandleNameQueryOpcode(WorldPacket& recvPacket); @@ -322,9 +361,10 @@ class TRINITY_DLL_SPEC WorldSession void HandleMoveWorldportAckOpcode(); // for server-side calls void HandleMovementOpcodes(WorldPacket& recvPacket); - void HandlePossessedMovement(WorldPacket& recv_data, MovementInfo& movementInfo, uint32& MovementFlags); + //void HandlePossessedMovement(WorldPacket& recv_data, MovementInfo& movementInfo, uint32& MovementFlags); void HandleSetActiveMoverOpcode(WorldPacket &recv_data); - void HandleNotActiveMoverOpcode(WorldPacket &recv_data); + void HandleMoveNotActiveMover(WorldPacket &recv_data); + void HandleDismissControlledVehicle(WorldPacket &recv_data); void HandleMoveTimeSkippedOpcode(WorldPacket &recv_data); void HandleRequestRaidInfoOpcode( WorldPacket & recv_data ); @@ -385,7 +425,7 @@ class TRINITY_DLL_SPEC WorldSession void HandleGuildSaveEmblemOpcode(WorldPacket& recvPacket); void HandleTaxiNodeStatusQueryOpcode(WorldPacket& recvPacket); - void HandleTaxiQueryAvailableNodesOpcode(WorldPacket& recvPacket); + void HandleTaxiQueryAvailableNodes(WorldPacket& recvPacket); void HandleActivateTaxiOpcode(WorldPacket& recvPacket); void HandleActivateTaxiFarOpcode(WorldPacket& recvPacket); void HandleTaxiNextDestinationOpcode(WorldPacket& recvPacket); @@ -429,6 +469,7 @@ class TRINITY_DLL_SPEC WorldSession void HandleAuctionRemoveItem( WorldPacket & recv_data ); void HandleAuctionListOwnerItems( WorldPacket & recv_data ); void HandleAuctionPlaceBid( WorldPacket & recv_data ); + void HandleAuctionListPendingSales( WorldPacket & recv_data ); void HandleGetMail( WorldPacket & recv_data ); void HandleSendMail( WorldPacket & recv_data ); @@ -536,6 +577,7 @@ class TRINITY_DLL_SPEC WorldSession //Pet void HandlePetAction( WorldPacket & recv_data ); + void HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid, uint16 flag, uint64 guid2); void HandlePetNameQuery( WorldPacket & recv_data ); void HandlePetSetAction( WorldPacket & recv_data ); void HandlePetAbandon( WorldPacket & recv_data ); @@ -544,6 +586,7 @@ class TRINITY_DLL_SPEC WorldSession void HandlePetUnlearnOpcode( WorldPacket& recvPacket ); void HandlePetSpellAutocastOpcode( WorldPacket& recvPacket ); void HandlePetCastSpellOpcode( WorldPacket& recvPacket ); + void HandlePetLearnTalent( WorldPacket& recvPacket ); void HandleSetActionBar(WorldPacket& recv_data); @@ -580,10 +623,9 @@ class TRINITY_DLL_SPEC WorldSession void HandleLfmSetNoneOpcode(WorldPacket& recv_data); void HandleLfmSetOpcode(WorldPacket& recv_data); void HandleLfgSetCommentOpcode(WorldPacket& recv_data); - void HandleNewUnknownOpcode(WorldPacket& recv_data); void HandleChooseTitleOpcode(WorldPacket& recv_data); void HandleRealmStateRequestOpcode(WorldPacket& recv_data); - void HandleAllowMoveAckOpcode(WorldPacket& recv_data); + void HandleTimeSyncResp(WorldPacket& recv_data); void HandleWhoisOpcode(WorldPacket& recv_data); void HandleResetInstancesOpcode(WorldPacket& recv_data); @@ -629,6 +671,29 @@ class TRINITY_DLL_SPEC WorldSession void HandleGuildBankBuyTab(WorldPacket& recv_data); void HandleGuildBankTabText(WorldPacket& recv_data); void HandleGuildBankSetTabText(WorldPacket& recv_data); + + // Calendar + void HandleCalendarGetCalendar(WorldPacket& recv_data); + void HandleCalendarGetEvent(WorldPacket& recv_data); + void HandleCalendarGuildFilter(WorldPacket& recv_data); + void HandleCalendarArenaTeam(WorldPacket& recv_data); + void HandleCalendarAddEvent(WorldPacket& recv_data); + void HandleCalendarUpdateEvent(WorldPacket& recv_data); + void HandleCalendarRemoveEvent(WorldPacket& recv_data); + void HandleCalendarCopyEvent(WorldPacket& recv_data); + void HandleCalendarEventInvite(WorldPacket& recv_data); + void HandleCalendarEventRsvp(WorldPacket& recv_data); + void HandleCalendarEventRemoveInvite(WorldPacket& recv_data); + void HandleCalendarEventStatus(WorldPacket& recv_data); + void HandleCalendarEventModeratorStatus(WorldPacket& recv_data); + void HandleCalendarComplain(WorldPacket& recv_data); + void HandleCalendarGetNumPending(WorldPacket& recv_data); + + void HandleSpellClick(WorldPacket& recv_data); + void HandleAlterAppearance(WorldPacket& recv_data); + void HandleRemoveGlyph(WorldPacket& recv_data); + void HandleCharCustomize(WorldPacket& recv_data); + void HandleInspectAchievements(WorldPacket& recv_data); private: // private trade methods void moveItems(Item* myItems[], Item* hisItems[]); @@ -651,6 +716,8 @@ class TRINITY_DLL_SPEC WorldSession LocaleConstant m_sessionDbcLocale; int m_sessionDbLocaleIndex; uint32 m_latency; + AccountData m_accountData[NUM_ACCOUNT_DATA_TYPES]; + AddonsList m_addonsList; ZThread::LockedQueue<WorldPacket*,ZThread::FastMutex> _recvQueue; }; diff --git a/src/game/WorldSocket.cpp b/src/game/WorldSocket.cpp index 9a5ec6c48c3..cefa4683502 100644 --- a/src/game/WorldSocket.cpp +++ b/src/game/WorldSocket.cpp @@ -1,7 +1,7 @@ /* -* Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> +* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * -* Copyright (C) 2008 Trinity <http://www.trinitycore.org/> +* Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,7 +37,6 @@ #include "WorldPacket.h" #include "SharedDefines.h" #include "ByteBuffer.h" -#include "AddonHandler.h" #include "Opcodes.h" #include "Database/DatabaseEnv.h" #include "Auth/Sha1.h" @@ -54,8 +53,37 @@ struct ServerPktHeader { - uint16 size; - uint16 cmd; + /** + * size is the length of the payload _plus_ the length of the opcode + */ + ServerPktHeader(uint32 size, uint16 cmd) : size(size) + { + uint8 headerIndex=0; + if(isLargePacket()) + { + sLog.outDebug("initializing large server to client packet. Size: %u, cmd: %u", size, cmd); + header[headerIndex++] = 0x80|(0xFF &(size>>16)); + } + header[headerIndex++] = 0xFF &(size>>8); + header[headerIndex++] = 0xFF &size; + + header[headerIndex++] = 0xFF & cmd; + header[headerIndex++] = 0xFF & (cmd>>8); + } + + uint8 getHeaderLength() + { + // cmd = 2 bytes, size= 2||3bytes + return 2+(isLargePacket()?3:2); + } + + bool isLargePacket() + { + return size > 0x7FFF; + } + + const uint32 size; + uint8 header[5]; }; struct ClientPktHeader @@ -84,6 +112,9 @@ m_OverSpeedPings (0), m_LastPingTime (ACE_Time_Value::zero) { reference_counting_policy ().value (ACE_Event_Handler::Reference_Counting_Policy::ENABLED); + + msg_queue()->high_water_mark(8*1024*1024); + msg_queue()->low_water_mark(8*1024*1024); } WorldSocket::~WorldSocket (void) @@ -97,10 +128,6 @@ WorldSocket::~WorldSocket (void) closing_ = true; peer ().close (); - - WorldPacket* pct; - while (m_PacketQueue.dequeue_head (pct) == 0) - delete pct; } bool WorldSocket::IsClosed (void) const @@ -142,7 +169,7 @@ int WorldSocket::SendPacket (const WorldPacket& pct) // Dump outgoing packet. if (sWorldLog.LogWorld ()) { - sWorldLog.Log ("SERVER:\nSOCKET: %u\nLENGTH: %u\nOPCODE: %s (0x%.4X)\nDATA:\n", + sWorldLog.outLog ("SERVER:\nSOCKET: %u\nLENGTH: %u\nOPCODE: %s (0x%.4X)\nDATA:\n", (uint32) get_handle (), pct.size (), LookupOpcodeName (pct.GetOpcode ()), @@ -152,26 +179,41 @@ int WorldSocket::SendPacket (const WorldPacket& pct) while (p < pct.size ()) { for (uint32 j = 0; j < 16 && p < pct.size (); j++) - sWorldLog.Log ("%.2X ", const_cast<WorldPacket&>(pct)[p++]); + sWorldLog.outLog ("%.2X ", const_cast<WorldPacket&>(pct)[p++]); - sWorldLog.Log ("\n"); + sWorldLog.outLog (""); } - - sWorldLog.Log ("\n\n"); } - if (iSendPacket (pct) == -1) + ServerPktHeader header(pct.size()+2, pct.GetOpcode()); + m_Crypt.EncryptSend ((uint8*)header.header, header.getHeaderLength()); + + if (m_OutBuffer->space () >= pct.size () + header.getHeaderLength() && msg_queue()->is_empty()) { - WorldPacket* npct; + // Put the packet on the buffer. + if (m_OutBuffer->copy ((char*) header.header, header.getHeaderLength()) == -1) + ACE_ASSERT (false); - ACE_NEW_RETURN (npct, WorldPacket (pct), -1); + if (!pct.empty ()) + if (m_OutBuffer->copy ((char*) pct.contents (), pct.size ()) == -1) + ACE_ASSERT (false); + } + else + { + // Enqueue the packet. + ACE_Message_Block* mb; - // NOTE maybe check of the size of the queue can be good ? - // to make it bounded instead of unbounded - if (m_PacketQueue.enqueue_tail (npct) == -1) + ACE_NEW_RETURN(mb, ACE_Message_Block(pct.size () + header.getHeaderLength()), -1); + + mb->copy((char*) header.header, header.getHeaderLength()); + + if (!pct.empty ()) + mb->copy((const char*)pct.contents(), pct.size ()); + + if(msg_queue()->enqueue_tail(mb,(ACE_Time_Value*)&ACE_Time_Value::zero) == -1) { - delete npct; - sLog.outError ("WorldSocket::SendPacket: m_PacketQueue.enqueue_tail failed"); + sLog.outError("WorldSocket::SendPacket enqueue_tail"); + mb->release(); return -1; } } @@ -272,7 +314,7 @@ int WorldSocket::handle_input (ACE_HANDLE) } case 0: { - DEBUG_LOG ("WorldSocket::handle_input: Peer has closed connection\n"); + DEBUG_LOG ("WorldSocket::handle_input: Peer has closed connection"); errno = ECONNRESET; return -1; @@ -296,7 +338,7 @@ int WorldSocket::handle_output (ACE_HANDLE) const size_t send_len = m_OutBuffer->length (); if (send_len == 0) - return cancel_wakeup_output (Guard); + return handle_output_queue (Guard); #ifdef MSG_NOSIGNAL ssize_t n = peer ().send (m_OutBuffer->rd_ptr (), send_len, MSG_NOSIGNAL); @@ -326,15 +368,73 @@ int WorldSocket::handle_output (ACE_HANDLE) { m_OutBuffer->reset (); - if (!iFlushPacketQueue ()) - return cancel_wakeup_output (Guard); - else - return schedule_wakeup_output (Guard); + return handle_output_queue (Guard); } ACE_NOTREACHED (return 0); } +int WorldSocket::handle_output_queue (GuardType& g) +{ + if(msg_queue()->is_empty()) + return cancel_wakeup_output(g); + + ACE_Message_Block *mblk; + + if(msg_queue()->dequeue_head(mblk, (ACE_Time_Value*)&ACE_Time_Value::zero) == -1) + { + sLog.outError("WorldSocket::handle_output_queue dequeue_head"); + return -1; + } + + const size_t send_len = mblk->length (); + +#ifdef MSG_NOSIGNAL + ssize_t n = peer ().send (mblk->rd_ptr (), send_len, MSG_NOSIGNAL); +#else + ssize_t n = peer ().send (mblk->rd_ptr (), send_len); +#endif // MSG_NOSIGNAL + + if (n == 0) + { + mblk->release(); + + return -1; + } + else if (n == -1) + { + if (errno == EWOULDBLOCK || errno == EAGAIN) + { + msg_queue()->enqueue_head(mblk, (ACE_Time_Value*) &ACE_Time_Value::zero); + return schedule_wakeup_output (g); + } + + mblk->release(); + return -1; + } + else if (n < send_len) //now n > 0 + { + mblk->rd_ptr (static_cast<size_t> (n)); + + if (msg_queue()->enqueue_head(mblk, (ACE_Time_Value*) &ACE_Time_Value::zero) == -1) + { + sLog.outError("WorldSocket::handle_output_queue enqueue_head"); + mblk->release(); + return -1; + } + + return schedule_wakeup_output (g); + } + else //now n == send_len + { + mblk->release(); + + return msg_queue()->is_empty() ? cancel_wakeup_output(g) : ACE_Event_Handler::WRITE_MASK; + } + + ACE_NOTREACHED(return -1); +} + int WorldSocket::handle_close (ACE_HANDLE h, ACE_Reactor_Mask) { // Critical section @@ -362,10 +462,15 @@ int WorldSocket::Update (void) if (closing_) return -1; - if (m_OutActive || m_OutBuffer->length () == 0) + if (m_OutActive || (m_OutBuffer->length () == 0 && msg_queue()->is_empty())) return 0; - return handle_output (get_handle ()); + int ret; + do + ret = handle_output (get_handle ()); + while( ret > 0 ); + + return ret; } int WorldSocket::handle_input_header (void) @@ -374,15 +479,14 @@ int WorldSocket::handle_input_header (void) ACE_ASSERT (m_Header.length () == sizeof (ClientPktHeader)); - m_Crypt.DecryptRecv ((ACE_UINT8*) m_Header.rd_ptr (), sizeof (ClientPktHeader)); + m_Crypt.DecryptRecv ((uint8*) m_Header.rd_ptr (), sizeof (ClientPktHeader)); ClientPktHeader& header = *((ClientPktHeader*) m_Header.rd_ptr ()); EndianConvertReverse(header.size); EndianConvert(header.cmd); - if ((header.size < 4) || (header.size > 10240) || - (header.cmd < 0) || (header.cmd > 10240) ) + if ((header.size < 4) || (header.size > 10240) || (header.cmd > 10240)) { sLog.outError ("WorldSocket::handle_input_header: client sent malformed packet size = %d , cmd = %d", header.size, header.cmd); @@ -574,7 +678,7 @@ int WorldSocket::ProcessIncoming (WorldPacket* new_pct) // Dump received packet. if (sWorldLog.LogWorld ()) { - sWorldLog.Log ("CLIENT:\nSOCKET: %u\nLENGTH: %u\nOPCODE: %s (0x%.4X)\nDATA:\n", + sWorldLog.outLog ("CLIENT:\nSOCKET: %u\nLENGTH: %u\nOPCODE: %s (0x%.4X)\nDATA:\n", (uint32) get_handle (), new_pct->size (), LookupOpcodeName (new_pct->GetOpcode ()), @@ -584,10 +688,10 @@ int WorldSocket::ProcessIncoming (WorldPacket* new_pct) while (p < new_pct->size ()) { for (uint32 j = 0; j < 16 && p < new_pct->size (); j++) - sWorldLog.Log ("%.2X ", (*new_pct)[p++]); - sWorldLog.Log ("\n"); + sWorldLog.outLog ("%.2X ", (*new_pct)[p++]); + + sWorldLog.outLog (""); } - sWorldLog.Log ("\n\n"); } // like one switch ;) @@ -626,7 +730,7 @@ int WorldSocket::ProcessIncoming (WorldPacket* new_pct) } else { - sLog.outError ("WorldSocket::ProcessIncoming: Client not authed opcode = ", opcode); + sLog.outError ("WorldSocket::ProcessIncoming: Client not authed opcode = %u", uint32(opcode)); return -1; } } @@ -639,7 +743,7 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket) // NOTE: ATM the socket is singlethread, have this in mind ... uint8 digest[20]; uint32 clientSeed; - uint32 unk2; + uint32 unk2, unk3; uint32 BuiltNumberClient; uint32 id, security; //uint8 expansion = 0; @@ -657,10 +761,21 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket) return -1; } + if(sWorld.IsClosed()) + { + packet.Initialize(SMSG_AUTH_RESPONSE, 1); + packet << uint8(AUTH_REJECT); + SendPacket (packet); + + sLog.outError ("WorldSocket::HandleAuthSession: World closed, denying client (%s).", m_Session->GetRemoteAddress()); + return -1; + } + // Read the content of the packet recvPacket >> BuiltNumberClient; // for now no use recvPacket >> unk2; recvPacket >> account; + recvPacket >> unk3; if (recvPacket.size () < (4 + 4 + (account.size () + 1) + 4 + 20)) { @@ -684,17 +799,17 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket) QueryResult *result = LoginDatabase.PQuery ("SELECT " - "id, " //0 - "gmlevel, " //1 - "sessionkey, " //2 - "last_ip, " //3 - "locked, " //4 - "sha_pass_hash, " //5 - "v, " //6 - "s, " //7 - "expansion, " //8 - "mutetime, " //9 - "locale " //10 + "id, " //0 + "gmlevel, " //1 + "sessionkey, " //2 + "last_ip, " //3 + "locked, " //4 + "sha_pass_hash, " //5 + "v, " //6 + "s, " //7 + "expansion, " //8 + "mutetime, " //9 + "locale " //10 "FROM account " "WHERE username = '%s'", safe_account.c_str ()); @@ -788,6 +903,11 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket) id = fields[0].GetUInt32 (); security = fields[1].GetUInt16 (); + /* + if(security > SEC_ADMINISTRATOR) // prevent invalid security settings in DB + security = SEC_ADMINISTRATOR; + */ + K.SetHexStr (fields[2].GetString ()); time_t mutetime = time_t (fields[9].GetUInt64 ()); @@ -880,15 +1000,14 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket) m_Crypt.SetKey (&K); m_Crypt.Init (); + m_Session->LoadAccountData(); + m_Session->ReadAddonsInfo(recvPacket); + // In case needed sometime the second arg is in microseconds 1 000 000 = 1 sec ACE_OS::sleep (ACE_Time_Value (0, 10000)); sWorld.AddSession (m_Session); - // Create and send the Addon packet - if (sAddOnHandler.BuildAddonPacket (&recvPacket, &SendAddonPacked)) - SendPacket (SendAddonPacked); - return 0; } @@ -961,25 +1080,19 @@ int WorldSocket::HandlePing (WorldPacket& recvPacket) return SendPacket (packet); } -int WorldSocket::iSendPacket (const WorldPacket& pct) +/*int WorldSocket::iSendPacket (const WorldPacket& pct) { - if (m_OutBuffer->space () < pct.size () + sizeof (ServerPktHeader)) + ServerPktHeader header(pct.size()+2, pct.GetOpcode()); + if (m_OutBuffer->space () < pct.size () + header.getHeaderLength()) { errno = ENOBUFS; return -1; } - ServerPktHeader header; - header.cmd = pct.GetOpcode (); - EndianConvert(header.cmd); - - header.size = (uint16) pct.size () + 2; - EndianConvertReverse(header.size); + m_Crypt.EncryptSend ( header.header, header.getHeaderLength()); - m_Crypt.EncryptSend ((uint8*) & header, sizeof (header)); - - if (m_OutBuffer->copy ((char*) & header, sizeof (header)) == -1) + if (m_OutBuffer->copy ((char*) header.header, header.getHeaderLength()) == -1) ACE_ASSERT (false); if (!pct.empty ()) @@ -1015,5 +1128,5 @@ bool WorldSocket::iFlushPacketQueue () } return haveone; -} +}*/ diff --git a/src/game/WorldSocket.h b/src/game/WorldSocket.h index 7adf16373fe..67e15adf148 100644 --- a/src/game/WorldSocket.h +++ b/src/game/WorldSocket.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -103,9 +103,6 @@ class WorldSocket : protected WorldHandler typedef ACE_Thread_Mutex LockType; typedef ACE_Guard<LockType> GuardType; - /// Queue for storing packets for which there is no space. - typedef ACE_Unbounded_Queue< WorldPacket* > PacketQueueT; - /// Check if socket is closed. bool IsClosed (void) const; @@ -161,6 +158,9 @@ class WorldSocket : protected WorldHandler int cancel_wakeup_output (GuardType& g); int schedule_wakeup_output (GuardType& g); + /// Drain the queue if its not empty. + int handle_output_queue (GuardType& g); + /// process one incoming packet. /// @param new_pct received packet ,note that you need to delete it. int ProcessIncoming (WorldPacket* new_pct); @@ -171,16 +171,6 @@ class WorldSocket : protected WorldHandler /// Called by ProcessIncoming() on CMSG_PING. int HandlePing (WorldPacket& recvPacket); - /// Try to write WorldPacket to m_OutBuffer ,return -1 if no space - /// Need to be called with m_OutBufferLock lock held - int iSendPacket (const WorldPacket& pct); - - /// Flush m_PacketQueue if there are packets in it - /// Need to be called with m_OutBufferLock lock held - /// @return true if it wrote to the buffer ( AKA you need - /// to mark the socket for output ). - bool iFlushPacketQueue (); - private: /// Time in which the last ping was received ACE_Time_Value m_LastPingTime; @@ -220,10 +210,6 @@ class WorldSocket : protected WorldHandler /// Size of the m_OutBuffer. size_t m_OutBufferSize; - /// Here are stored packets for which there was no space on m_OutBuffer, - /// this allows not-to kick player if its buffer is overflowed. - PacketQueueT m_PacketQueue; - /// True if the socket is registered with the reactor for output bool m_OutActive; diff --git a/src/game/WorldSocketMgr.cpp b/src/game/WorldSocketMgr.cpp index e73b8993d0b..e8f8bad5894 100644 --- a/src/game/WorldSocketMgr.cpp +++ b/src/game/WorldSocketMgr.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2005-2008,2007 MaNGOS <http://getmangos.com/> * -* Copyright (C) 2008 Trinity <http://www.trinitycore.org/> +* Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/game/WorldSocketMgr.h b/src/game/WorldSocketMgr.h index f6e55614d46..94cfa78324c 100644 --- a/src/game/WorldSocketMgr.h +++ b/src/game/WorldSocketMgr.h @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by |
