diff --git a/addition/3_mangos_autobroadcast.sql b/addition/3_mangos_autobroadcast.sql new file mode 100644 index 0000000..6fae3ba --- /dev/null +++ b/addition/3_mangos_autobroadcast.sql @@ -0,0 +1,2 @@ +DELETE FROM `mangos_string` WHERE `entry` = 1300; +INSERT INTO `mangos_string` VALUES (1300, '|cffffcc00[Server]: |cff00ff00%s|r', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); diff --git a/addition/3_realmd_autobroadcast.sql b/addition/3_realmd_autobroadcast.sql new file mode 100644 index 0000000..391bbcc --- /dev/null +++ b/addition/3_realmd_autobroadcast.sql @@ -0,0 +1,6 @@ +CREATE TABLE IF NOT EXISTS `autobroadcast` ( + `id` int(11) NOT NULL auto_increment, + `text` longtext NOT NULL, + `next` int(11) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; diff --git a/src/game/AchievementMgr.cpp b/src/game/AchievementMgr.cpp index c594447..c43f545 100644 --- a/src/game/AchievementMgr.cpp +++ b/src/game/AchievementMgr.cpp @@ -795,7 +795,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui { case 161: // AB, Overcome a 500 resource disadvantage { - if (bg->GetTypeID() != BATTLEGROUND_AB) + if (bg->GetTypeID(true) != BATTLEGROUND_AB) continue; if(!((BattleGroundAB*)bg)->IsTeamScores500Disadvantage(GetPlayer()->GetTeam())) continue; diff --git a/src/game/AggressorAI.cpp b/src/game/AggressorAI.cpp index 8978436..b5648a0 100644 --- a/src/game/AggressorAI.cpp +++ b/src/game/AggressorAI.cpp @@ -56,7 +56,6 @@ AggressorAI::MoveInLineOfSight(Unit *u) if(!m_creature->getVictim()) { AttackStart(u); - u->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); } else if(sMapStore.LookupEntry(m_creature->GetMapId())->IsDungeon()) { diff --git a/src/game/BattleGround.cpp b/src/game/BattleGround.cpp index eb6ba1b..5c58b77 100644 --- a/src/game/BattleGround.cpp +++ b/src/game/BattleGround.cpp @@ -209,6 +209,7 @@ void BattleGround::BroadcastWorker(Do& _do) BattleGround::BattleGround() { m_TypeID = BattleGroundTypeId(0); + m_RandomTypeID = BattleGroundTypeId(0); m_Status = STATUS_NONE; m_ClientInstanceID = 0; m_EndTime = 0; @@ -222,6 +223,7 @@ BattleGround::BattleGround() m_Events = 0; m_IsRated = false; m_BuffChange = false; + m_IsRandom = false; m_Name = ""; m_LevelMin = 0; m_LevelMax = 0; @@ -794,14 +796,31 @@ void BattleGround::EndBattleGround(uint32 winner) } } + uint32 win_kills = plr->GetRandomWinner() ? BG_REWARD_WINNER_HONOR_LAST : BG_REWARD_WINNER_HONOR_FIRST; + uint32 loos_kills = plr->GetRandomWinner() ? BG_REWARD_LOOSER_HONOR_LAST : BG_REWARD_LOOSER_HONOR_FIRST; + uint32 win_arena = plr->GetRandomWinner() ? BG_REWARD_WINNER_ARENA_LAST : BG_REWARD_WINNER_ARENA_FIRST; + if (team == winner) { RewardMark(plr,ITEM_WINNER_COUNT); RewardQuestComplete(plr); - plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_BG, 1); + + if (IsRandom() || BattleGroundMgr::IsBGWeekend(GetTypeID())) + { + UpdatePlayerScore(plr, SCORE_BONUS_HONOR, GetBonusHonorFromKill(win_kills*4)); + plr->ModifyArenaPoints(win_arena); + if(!plr->GetRandomWinner()) + plr->SetRandomWinner(true); + } + + plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_BG, 1); } else + { RewardMark(plr,ITEM_LOSER_COUNT); + if (IsRandom() || BattleGroundMgr::IsBGWeekend(GetTypeID())) + UpdatePlayerScore(plr, SCORE_BONUS_HONOR, GetBonusHonorFromKill(loos_kills*4)); + } plr->CombatStopWithPets(true); @@ -842,7 +861,7 @@ uint32 BattleGround::GetBonusHonorFromKill(uint32 kills) const uint32 BattleGround::GetBattlemasterEntry() const { - switch(GetTypeID()) + switch(GetTypeID(true)) { case BATTLEGROUND_AV: return 15972; case BATTLEGROUND_WS: return 14623; @@ -961,7 +980,7 @@ void BattleGround::SendRewardMarkByMail(Player *plr,uint32 mark, uint32 count) void BattleGround::RewardQuestComplete(Player *plr) { uint32 quest; - switch(GetTypeID()) + switch(GetTypeID(true)) { case BATTLEGROUND_AV: quest = SPELL_AV_QUEST_REWARD; @@ -1038,10 +1057,6 @@ void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPac 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) - // 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(); - if (isRated() && GetStatus() == STATUS_IN_PROGRESS) { //left a rated match while the encounter was in progress, consider as loser @@ -1207,7 +1222,7 @@ void BattleGround::AddPlayer(Player *plr) } plr->DestroyConjuredItems(true); - plr->UnsummonPetTemporaryIfAny(); + plr->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT); if(GetStatus() == STATUS_WAIT_JOIN) // not started yet { @@ -1691,7 +1706,7 @@ void BattleGround::HandleTriggerBuff(uint64 const& go_guid) index--; if (index < 0) { - sLog.outError("BattleGround (Type: %u) has buff gameobject (Guid: %u Entry: %u Type:%u) but it hasn't that object in its internal data",GetTypeID(),GUID_LOPART(go_guid),obj->GetEntry(),obj->GetGoType()); + sLog.outError("BattleGround (Type: %u) has buff gameobject (Guid: %u Entry: %u Type:%u) but it hasn't that object in its internal data",GetTypeID(true),GUID_LOPART(go_guid),obj->GetEntry(),obj->GetGoType()); return; } diff --git a/src/game/BattleGround.h b/src/game/BattleGround.h index 32554e4..ce5557a 100644 --- a/src/game/BattleGround.h +++ b/src/game/BattleGround.h @@ -128,6 +128,16 @@ enum BattleGroundBuffObjects BG_OBJECTID_BERSERKERBUFF_ENTRY = 179905 }; +enum BattleGroundRandomRewards +{ + BG_REWARD_WINNER_HONOR_FIRST = 30, + BG_REWARD_WINNER_ARENA_FIRST = 25, + BG_REWARD_WINNER_HONOR_LAST = 15, + BG_REWARD_WINNER_ARENA_LAST = 0, + BG_REWARD_LOOSER_HONOR_FIRST = 5, + BG_REWARD_LOOSER_HONOR_LAST = 5 +}; + const uint32 Buff_Entries[3] = { BG_OBJECTID_SPEEDBUFF_ENTRY, BG_OBJECTID_REGENBUFF_ENTRY, BG_OBJECTID_BERSERKERBUFF_ENTRY }; enum BattleGroundStatus @@ -164,11 +174,12 @@ enum BattleGroundQueueTypeId BATTLEGROUND_QUEUE_EY = 4, BATTLEGROUND_QUEUE_SA = 5, BATTLEGROUND_QUEUE_IC = 6, - BATTLEGROUND_QUEUE_2v2 = 7, - BATTLEGROUND_QUEUE_3v3 = 8, - BATTLEGROUND_QUEUE_5v5 = 9 + BATTLEGROUND_QUEUE_RB = 7, + BATTLEGROUND_QUEUE_2v2 = 8, + BATTLEGROUND_QUEUE_3v3 = 9, + BATTLEGROUND_QUEUE_5v5 = 10 }; -#define MAX_BATTLEGROUND_QUEUE_TYPES 10 +#define MAX_BATTLEGROUND_QUEUE_TYPES 11 enum ScoreType { @@ -307,7 +318,7 @@ class BattleGround /* Battleground */ // Get methods: char const* GetName() const { return m_Name; } - BattleGroundTypeId GetTypeID() const { return m_TypeID; } + BattleGroundTypeId GetTypeID(bool GetRandom = false) const { return GetRandom ? m_RandomTypeID : m_TypeID; } BattleGroundBracketId GetBracketId() const { return m_BracketId; } // the instanceId check is also used to determine a bg-template // that's why the m_map hack is here.. @@ -330,10 +341,12 @@ class BattleGround uint8 GetWinner() const { return m_Winner; } uint32 GetBattlemasterEntry() const; uint32 GetBonusHonorFromKill(uint32 kills) const; + bool IsRandom() { return m_IsRandom; } // Set methods: void SetName(char const* Name) { m_Name = Name; } void SetTypeID(BattleGroundTypeId TypeID) { m_TypeID = TypeID; } + void SetRandomTypeID(BattleGroundTypeId TypeID) { m_RandomTypeID = TypeID; } //here we can count minlevel and maxlevel for players void SetBracket(PvPDifficultyEntry const* bracketEntry); void SetStatus(BattleGroundStatus Status) { m_Status = Status; } @@ -359,6 +372,8 @@ class BattleGround void DecreaseInvitedCount(uint32 team) { (team == ALLIANCE) ? --m_InvitedAlliance : --m_InvitedHorde; } void IncreaseInvitedCount(uint32 team) { (team == ALLIANCE) ? ++m_InvitedAlliance : ++m_InvitedHorde; } + + void SetRandom(bool isRandom) { m_IsRandom = isRandom; } uint32 GetInvitedCount(uint32 team) const { if (team == ALLIANCE) @@ -568,10 +583,12 @@ class BattleGround uint32 m_StartMessageIds[BG_STARTING_EVENT_COUNT]; bool m_BuffChange; + bool m_IsRandom; private: /* Battleground */ BattleGroundTypeId m_TypeID; + BattleGroundTypeId m_RandomTypeID; BattleGroundStatus m_Status; uint32 m_ClientInstanceID; //the instance-id which is sent to the client and without any other internal use uint32 m_StartTime; diff --git a/src/game/BattleGroundDS.cpp b/src/game/BattleGroundDS.cpp index e6a298d..62d347e 100644 --- a/src/game/BattleGroundDS.cpp +++ b/src/game/BattleGroundDS.cpp @@ -16,14 +16,16 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "Object.h" #include "Player.h" #include "BattleGround.h" #include "BattleGroundDS.h" +#include "ObjectMgr.h" +#include "WorldPacket.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; @@ -43,6 +45,24 @@ BattleGroundDS::~BattleGroundDS() void BattleGroundDS::Update(uint32 diff) { BattleGround::Update(diff); + if (GetStatus() == STATUS_IN_PROGRESS) + { + // knockback + if(m_uiKnockback < diff) + { + for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) + { + Player * plr = sObjectMgr.GetPlayer(itr->first); + if (plr && plr->IsWithinLOS(1214,765,14) && plr->GetDistance2d(1214,765) <= 50) + plr->KnockBackPlayerWithAngle(6.40f,55,7); + if (plr && plr->IsWithinLOS(1369,817,14) && plr->GetDistance2d(1369,817) <= 50) + plr->KnockBackPlayerWithAngle(3.03f,55,7); + } + m_uiKnockback = 1000; + } + else + m_uiKnockback -= diff; + } } void BattleGroundDS::StartingEventCloseDoors() @@ -51,6 +71,7 @@ void BattleGroundDS::StartingEventCloseDoors() void BattleGroundDS::StartingEventOpenDoors() { + OpenDoorEvent(BG_EVENT_DOOR); } void BattleGroundDS::AddPlayer(Player *plr) @@ -60,19 +81,76 @@ void BattleGroundDS::AddPlayer(Player *plr) BattleGroundDSScore* sc = new BattleGroundDSScore; m_PlayerScores[plr->GetGUID()] = sc; + + UpdateWorldState(0xe11, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(0xe10, GetAlivePlayersCountByTeam(HORDE)); +} + +void BattleGroundDS::RemovePlayer(Player* /*plr*/, uint64 /*guid*/) +{ + if (GetStatus() == STATUS_WAIT_LEAVE) + return; + + UpdateWorldState(0xe11, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(0xe10, GetAlivePlayersCountByTeam(HORDE)); + + CheckArenaWinConditions(); } -void BattleGroundDS::RemovePlayer(Player * /*plr*/, uint64 /*guid*/) +void BattleGroundDS::HandleKillPlayer(Player *player, Player *killer) { + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + if (!killer) + { + sLog.outError("BattleGroundDS: Killer player not found"); + return; + } + + BattleGround::HandleKillPlayer(player,killer); + + UpdateWorldState(0xe11, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(0xe10, GetAlivePlayersCountByTeam(HORDE)); + + CheckArenaWinConditions(); +} + +bool BattleGroundDS::HandlePlayerUnderMap(Player *player) +{ + player->TeleportTo(GetMapId(),1299.046f,784.825f,9.338f,player->GetOrientation(),false); + return true; +} + +void BattleGroundDS::HandleAreaTrigger(Player *Source, uint32 Trigger) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + switch(Trigger) + { + case 5347: + case 5348: + break; + default: + sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); + Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); + break; + } } -void BattleGroundDS::HandleKillPlayer(Player* player, Player* killer) +void BattleGroundDS::FillInitialWorldStates(WorldPacket &data, uint32& count) { - BattleGround::HandleKillPlayer(player, killer); + FillInitialWorldState(data, count, 0xe11, GetAlivePlayersCountByTeam(ALLIANCE)); + FillInitialWorldState(data, count, 0xe10, GetAlivePlayersCountByTeam(HORDE)); + FillInitialWorldState(data, count, 0xe1a, 1); } -void BattleGroundDS::HandleAreaTrigger(Player * /*Source*/, uint32 /*Trigger*/) +void BattleGroundDS::Reset() { + //call parent's class reset + BattleGround::Reset(); + m_uiKnockback = 5000; } bool BattleGroundDS::SetupBattleGround() diff --git a/src/game/BattleGroundDS.h b/src/game/BattleGroundDS.h index 0034226..2d21b58 100644 --- a/src/game/BattleGroundDS.h +++ b/src/game/BattleGroundDS.h @@ -45,6 +45,11 @@ class BattleGroundDS : public BattleGround void RemovePlayer(Player *plr, uint64 guid); void HandleAreaTrigger(Player *Source, uint32 Trigger); bool SetupBattleGround(); + virtual void Reset(); + virtual void FillInitialWorldStates(WorldPacket &d, uint32& count); void HandleKillPlayer(Player* player, Player *killer); + bool HandlePlayerUnderMap(Player * plr); + private: + uint32 m_uiKnockback; }; #endif diff --git a/src/game/BattleGroundHandler.cpp b/src/game/BattleGroundHandler.cpp index 3d43158..ac6d571 100644 --- a/src/game/BattleGroundHandler.cpp +++ b/src/game/BattleGroundHandler.cpp @@ -99,6 +99,7 @@ void WorldSession::HandleBattlemasterJoinOpcode( WorldPacket & recv_data ) // can do this, since it's battleground, not arena BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, 0); + BattleGroundQueueTypeId bgQueueTypeIdRandom = BattleGroundMgr::BGQueueTypeId(BATTLEGROUND_RB, 0); // ignore if player is already in BG if (_player->InBattleGround()) @@ -133,13 +134,38 @@ void WorldSession::HandleBattlemasterJoinOpcode( WorldPacket & recv_data ) _player->GetSession()->SendPacket(&data); return; } + + if (_player->GetBattleGroundQueueIndex(bgQueueTypeIdRandom) < PLAYER_MAX_BATTLEGROUND_QUEUES) + { + //player is already in random queue + WorldPacket data; + sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, ERR_IN_RANDOM_BG); + _player->GetSession()->SendPacket(&data); + return; + } + + if(_player->InBattleGroundQueue() && bgTypeId == BATTLEGROUND_RB) + { + //player is already in queue, can't start random queue + WorldPacket data; + sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, ERR_IN_NON_RANDOM_BG); + _player->GetSession()->SendPacket(&data); + return; + } + // 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()) + { + WorldPacket data; + sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, ERR_BATTLEGROUND_TOO_MANY_QUEUES); + _player->GetSession()->SendPacket(&data); return; + } } else { @@ -220,7 +246,7 @@ void WorldSession::HandleBattleGroundPlayerPositionsOpcode( WorldPacket & /*recv if(!bg) // can't be received if player not in battleground return; - switch( bg->GetTypeID() ) + switch( bg->GetTypeID(true) ) { case BATTLEGROUND_WS: { diff --git a/src/game/BattleGroundMgr.cpp b/src/game/BattleGroundMgr.cpp index f423a1d..09c7a28 100644 --- a/src/game/BattleGroundMgr.cpp +++ b/src/game/BattleGroundMgr.cpp @@ -43,6 +43,7 @@ #include "World.h" #include "WorldPacket.h" #include "GameEventMgr.h" +#include "Formulas.h" #include "Policies/SingletonImp.h" @@ -938,6 +939,8 @@ void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BattleGroundBracketI //optimalization : --- we dont need to use selection_pools - each update we select max 2 groups + uint32 teamId = 0; + for(uint32 i = BG_QUEUE_PREMADE_ALLIANCE; i < BG_QUEUE_NORMAL_ALLIANCE; i++) { // take the group that joined first @@ -969,8 +972,11 @@ void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BattleGroundBracketI && (((*itr_team[BG_TEAM_ALLIANCE])->ArenaTeamRating >= arenaMinRating && (*itr_team[BG_TEAM_ALLIANCE])->ArenaTeamRating <= arenaMaxRating) || (*itr_team[BG_TEAM_ALLIANCE])->JoinTime < discardTime) ) { - m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*itr_team[BG_TEAM_ALLIANCE]), MaxPlayersPerTeam); - break; + if((*itr_team[BG_TEAM_ALLIANCE])->ArenaTeamId != teamId) + { + m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*itr_team[BG_TEAM_ALLIANCE]), MaxPlayersPerTeam); + break; + } } } } @@ -985,8 +991,11 @@ void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BattleGroundBracketI && (((*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; + if((*itr_team[BG_TEAM_HORDE])->ArenaTeamId != teamId) + { + m_SelectionPools[BG_TEAM_HORDE].AddGroup((*itr_team[BG_TEAM_HORDE]), MaxPlayersPerTeam); + break; + } } } } @@ -1323,7 +1332,7 @@ void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg) } *data << (int32)itr->second->DamageDone; // damage done *data << (int32)itr->second->HealingDone; // healing done - switch(bg->GetTypeID()) // battleground specific things + switch(bg->GetTypeID(true)) // battleground specific things { case BATTLEGROUND_AV: *data << (uint32)0x00000005; // count of next fields @@ -1359,7 +1368,7 @@ void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg) *data << (int32)0; // 0 break; default: - DEBUG_LOG("Unhandled MSG_PVP_LOG_DATA for BG id %u", bg->GetTypeID()); + DEBUG_LOG("Unhandled MSG_PVP_LOG_DATA for BG id %u", bg->GetTypeID(true)); *data << (int32)0; break; } @@ -1478,8 +1487,8 @@ BattleGround * BattleGroundMgr::CreateNewBattleGround(BattleGroundTypeId bgTypeI //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); + BattleGroundTypeId arenas[] = {BATTLEGROUND_NA, BATTLEGROUND_BE, BATTLEGROUND_RL, BATTLEGROUND_DS, BATTLEGROUND_RV}; + uint32 arena_num = urand(0,4); bgTypeId = arenas[arena_num]; bg_template = GetBattleGroundTemplate(bgTypeId); if (!bg_template) @@ -1489,6 +1498,22 @@ BattleGround * BattleGroundMgr::CreateNewBattleGround(BattleGroundTypeId bgTypeI } } + bool isRandom = false; + + if(bgTypeId==BATTLEGROUND_RB) + { + BattleGroundTypeId random_bgs[] = {BATTLEGROUND_AV, BATTLEGROUND_WS, BATTLEGROUND_AB, BATTLEGROUND_EY/*, BATTLEGROUND_SA, BATTLEGROUND_IC*/}; + uint32 bg_num = urand(0,3/*5*/); + bgTypeId = random_bgs[bg_num]; + bg_template = GetBattleGroundTemplate(bgTypeId); + if (!bg_template) + { + sLog.outError("BattleGround: CreateNewBattleGround - bg template not found for %u", bgTypeId); + return NULL; + } + isRandom = true; + } + BattleGround *bg = NULL; // create a copy of the BG template switch(bgTypeId) @@ -1543,7 +1568,7 @@ BattleGround * BattleGroundMgr::CreateNewBattleGround(BattleGroundTypeId bgTypeI // will also set m_bgMap, instanceid sMapMgr.CreateBgMap(bg->GetMapId(), bg); - bg->SetClientInstanceID(CreateClientVisibleInstanceId(bgTypeId, bracketEntry->GetBracketId())); + bg->SetClientInstanceID(CreateClientVisibleInstanceId(isRandom ? BATTLEGROUND_RB : bgTypeId, bracketEntry->GetBracketId())); // reset the new bg (set status to status_wait_queue from status_none) bg->Reset(); @@ -1552,6 +1577,9 @@ BattleGround * BattleGroundMgr::CreateNewBattleGround(BattleGroundTypeId bgTypeI bg->SetStatus(STATUS_WAIT_JOIN); bg->SetArenaType(arenaType); bg->SetRated(isRated); + bg->SetRandom(isRandom); + bg->SetTypeID(isRandom ? BATTLEGROUND_RB : bgTypeId); + bg->SetRandomTypeID(bgTypeId); return bg; } @@ -1787,6 +1815,13 @@ void BattleGroundMgr::BuildBattleGroundListPacket(WorldPacket *data, const uint6 if (!plr) return; + uint32 win_kills = plr->GetRandomWinner() ? BG_REWARD_WINNER_HONOR_LAST : BG_REWARD_WINNER_HONOR_FIRST; + uint32 win_arena = plr->GetRandomWinner() ? BG_REWARD_WINNER_ARENA_LAST : BG_REWARD_WINNER_ARENA_FIRST; + uint32 loos_kills = plr->GetRandomWinner() ? BG_REWARD_LOOSER_HONOR_LAST : BG_REWARD_LOOSER_HONOR_FIRST; + + win_kills = (uint32)MaNGOS::Honor::hk_honor_at_level(plr->getLevel(), win_kills*4); + loos_kills = (uint32)MaNGOS::Honor::hk_honor_at_level(plr->getLevel(), loos_kills*4); + data->Initialize(SMSG_BATTLEFIELD_LIST); *data << uint64(guid); // battlemaster guid *data << uint8(fromWhere); // from where you joined @@ -1795,20 +1830,22 @@ void BattleGroundMgr::BuildBattleGroundListPacket(WorldPacket *data, const uint6 *data << uint8(0); // unk // Rewards - *data << uint8(0); // 3.3.3 hasWin - *data << uint32(0); // 3.3.3 winHonor - *data << uint32(0); // 3.3.3 winArena - *data << uint32(0); // 3.3.3 lossHonor + *data << uint8( plr->GetRandomWinner() ); // 3.3.3 hasWin + *data << uint32( win_kills ); // 3.3.3 winHonor + *data << uint32( win_arena ); // 3.3.3 winArena + *data << uint32( loos_kills ); // 3.3.3 lossHonor + + uint8 isRandom = bgTypeId == BATTLEGROUND_RB; - uint8 isRandom = 0; *data << uint8(isRandom); // 3.3.3 isRandom + if(isRandom) { // Rewards (random) - *data << uint8(0); // 3.3.3 hasWin_Random - *data << uint32(0); // 3.3.3 winHonor_Random - *data << uint32(0); // 3.3.3 winArena_Random - *data << uint32(0); // 3.3.3 lossHonor_Random + *data << uint8( plr->GetRandomWinner() ); // 3.3.3 hasWin_Random + *data << uint32( win_kills ); // 3.3.3 winHonor_Random + *data << uint32( win_arena ); // 3.3.3 winArena_Random + *data << uint32( loos_kills ); // 3.3.3 lossHonor_Random } if(bgTypeId == BATTLEGROUND_AA) // arena @@ -1864,7 +1901,9 @@ bool BattleGroundMgr::IsArenaType(BattleGroundTypeId bgTypeId) return ( bgTypeId == BATTLEGROUND_AA || bgTypeId == BATTLEGROUND_BE || bgTypeId == BATTLEGROUND_NA || - bgTypeId == BATTLEGROUND_RL ); + bgTypeId == BATTLEGROUND_RL || + bgTypeId == BATTLEGROUND_DS || + bgTypeId == BATTLEGROUND_RV ); } BattleGroundQueueTypeId BattleGroundMgr::BGQueueTypeId(BattleGroundTypeId bgTypeId, uint8 arenaType) @@ -1884,7 +1923,7 @@ BattleGroundQueueTypeId BattleGroundMgr::BGQueueTypeId(BattleGroundTypeId bgType case BATTLEGROUND_IC: return BATTLEGROUND_QUEUE_IC; case BATTLEGROUND_RB: - return BATTLEGROUND_QUEUE_NONE; + return BATTLEGROUND_QUEUE_RB; case BATTLEGROUND_AA: case BATTLEGROUND_NA: case BATTLEGROUND_RL: @@ -1923,6 +1962,8 @@ BattleGroundTypeId BattleGroundMgr::BGTemplateId(BattleGroundQueueTypeId bgQueue return BATTLEGROUND_SA; case BATTLEGROUND_QUEUE_IC: return BATTLEGROUND_IC; + case BATTLEGROUND_QUEUE_RB: + return BATTLEGROUND_RB; case BATTLEGROUND_QUEUE_2v2: case BATTLEGROUND_QUEUE_3v3: case BATTLEGROUND_QUEUE_5v5: diff --git a/src/game/BattleGroundRV.cpp b/src/game/BattleGroundRV.cpp index e54e4db..9bdf290 100644 --- a/src/game/BattleGroundRV.cpp +++ b/src/game/BattleGroundRV.cpp @@ -19,11 +19,13 @@ #include "Player.h" #include "BattleGround.h" #include "BattleGroundRV.h" +#include "ObjectMgr.h" +#include "WorldPacket.h" +#include "GameObject.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; @@ -43,6 +45,24 @@ BattleGroundRV::~BattleGroundRV() void BattleGroundRV::Update(uint32 diff) { BattleGround::Update(diff); + if (GetStatus() == STATUS_IN_PROGRESS) + { + // teleport buggers + if(m_uiTeleport < diff) + { + for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) + { + Player * plr = sObjectMgr.GetPlayer(itr->first); + if (plr && plr->GetPositionZ() < 27) + plr->TeleportTo(618, plr->GetPositionX(), plr->GetPositionY(), 29, plr->GetOrientation(), false); + if (plr && plr->GetPositionZ() < 27) + plr->TeleportTo(618, plr->GetPositionX(), plr->GetPositionY(), 29, plr->GetOrientation(), false); + } + m_uiTeleport = 1000; + } + else + m_uiTeleport -= diff; + } } void BattleGroundRV::StartingEventCloseDoors() @@ -51,6 +71,7 @@ void BattleGroundRV::StartingEventCloseDoors() void BattleGroundRV::StartingEventOpenDoors() { + OpenDoorEvent(BG_EVENT_DOOR); } void BattleGroundRV::AddPlayer(Player *plr) @@ -60,19 +81,78 @@ void BattleGroundRV::AddPlayer(Player *plr) BattleGroundRVScore* sc = new BattleGroundRVScore; m_PlayerScores[plr->GetGUID()] = sc; + + UpdateWorldState(0xe11, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(0xe10, GetAlivePlayersCountByTeam(HORDE)); } void BattleGroundRV::RemovePlayer(Player * /*plr*/, uint64 /*guid*/) { + if (GetStatus() == STATUS_WAIT_LEAVE) + return; + + UpdateWorldState(0xe11, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(0xe10, GetAlivePlayersCountByTeam(HORDE)); + + CheckArenaWinConditions(); } void BattleGroundRV::HandleKillPlayer(Player* player, Player* killer) { + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + if (!killer) + { + sLog.outError("BattleGroundRV: Killer player not found"); + return; + } + BattleGround::HandleKillPlayer(player, killer); + + UpdateWorldState(0xe11, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(0xe10, GetAlivePlayersCountByTeam(HORDE)); + + CheckArenaWinConditions(); +} + +bool BattleGroundRV::HandlePlayerUnderMap(Player *player) +{ + player->TeleportTo(GetMapId(), 763.5f, -284, 28.276f, player->GetOrientation(), false); + return true; +} + +void BattleGroundRV::HandleAreaTrigger(Player * Source, uint32 Trigger) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + switch(Trigger) + { + case 5224: + case 5226: + case 5473: + case 5474: + break; + default: + sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); + Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); + break; + } +} + +void BattleGroundRV::FillInitialWorldStates(WorldPacket &data, uint32& count) +{ + FillInitialWorldState(data, count, 0xe11, GetAlivePlayersCountByTeam(ALLIANCE)); + FillInitialWorldState(data, count, 0xe10, GetAlivePlayersCountByTeam(HORDE)); + FillInitialWorldState(data, count, 0xe1a, 1); } -void BattleGroundRV::HandleAreaTrigger(Player * /*Source*/, uint32 /*Trigger*/) +void BattleGroundRV::Reset() { + //call parent's class reset + BattleGround::Reset(); + m_uiTeleport = 22000; } bool BattleGroundRV::SetupBattleGround() diff --git a/src/game/BattleGroundRV.h b/src/game/BattleGroundRV.h index d53dd23..d8bc665 100644 --- a/src/game/BattleGroundRV.h +++ b/src/game/BattleGroundRV.h @@ -45,6 +45,11 @@ class BattleGroundRV : public BattleGround void RemovePlayer(Player *plr, uint64 guid); void HandleAreaTrigger(Player *Source, uint32 Trigger); bool SetupBattleGround(); + virtual void Reset(); + virtual void FillInitialWorldStates(WorldPacket &d, uint32& count); void HandleKillPlayer(Player* player, Player *killer); + bool HandlePlayerUnderMap(Player * plr); + private: + uint32 m_uiTeleport; }; #endif diff --git a/src/game/CharacterHandler.cpp b/src/game/CharacterHandler.cpp index cc7b062..36581d4 100644 --- a/src/game/CharacterHandler.cpp +++ b/src/game/CharacterHandler.cpp @@ -101,6 +101,7 @@ bool LoginQueryHolder::Initialize() res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGLYPHS, "SELECT spec, slot, glyph FROM character_glyphs WHERE guid='%u'", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADMAILS, "SELECT id,messageType,sender,receiver,subject,body,has_items,expire_time,deliver_time,money,cod,checked,stationery,mailTemplateId FROM mail WHERE receiver = '%u' ORDER BY id DESC", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADMAILEDITEMS, "SELECT data, text, mail_id, item_guid, item_template FROM mail_items JOIN item_instance ON item_guid = guid WHERE receiver = '%u'", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADRANDOMBG, "SELECT guid FROM character_battleground_random WHERE guid = '%u'", GUID_LOPART(m_guid)); return res; } @@ -1022,7 +1023,7 @@ void WorldSession::HandleSetPlayerDeclinedNames(WorldPacket& recv_data) } } - if(!ObjectMgr::CheckDeclinedNames(GetMainPartOfName(wname, 0), declinedname)) + if(!ObjectMgr::CheckDeclinedNames(wname, declinedname)) { WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8); data << uint32(1); @@ -1050,8 +1051,18 @@ void WorldSession::HandleAlterAppearance( WorldPacket & recv_data ) { DEBUG_LOG("CMSG_ALTER_APPEARANCE"); - uint32 Hair, Color, FacialHair; - recv_data >> Hair >> Color >> FacialHair; + uint32 skinTone_id = -1; + + uint32 Hair, Color, FacialHair, SkinTone; + if(_player->getRace() != RACE_TAUREN) recv_data >> Hair >> Color >> FacialHair; + else + { + recv_data >> Hair >> Color >> FacialHair >> SkinTone; + BarberShopStyleEntry const* bs_skinTone = sBarberShopStyleStore.LookupEntry(SkinTone); + if(!bs_skinTone || bs_skinTone->type != 3 || bs_skinTone->race != _player->getRace() || bs_skinTone->gender != _player->getGender()) + return; + skinTone_id = bs_skinTone->hair_id; + } BarberShopStyleEntry const* bs_hair = sBarberShopStyleStore.LookupEntry(Hair); @@ -1063,7 +1074,7 @@ void WorldSession::HandleAlterAppearance( WorldPacket & recv_data ) 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); + uint32 Cost = _player->GetBarberShopCost(bs_hair->hair_id, Color, bs_facialHair->hair_id, skinTone_id); // 0 - ok // 1,3 - not enough money @@ -1088,6 +1099,7 @@ void WorldSession::HandleAlterAppearance( WorldPacket & recv_data ) _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)); + if(_player->getRace() == RACE_TAUREN) _player->SetByteValue(PLAYER_BYTES, 0, uint8(skinTone_id)); _player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP, 1); diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp index d40f762..63a580b 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -508,6 +508,7 @@ ChatCommand * ChatHandler::getCommandTable() { "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 }, { NULL, 0, false, NULL, "", NULL } }; diff --git a/src/game/Chat.h b/src/game/Chat.h index 5cf2b99..07fac9c 100644 --- a/src/game/Chat.h +++ b/src/game/Chat.h @@ -422,6 +422,7 @@ class ChatHandler bool HandleReloadSpellTargetPositionCommand(const char* args); bool HandleReloadSpellThreatsCommand(const char* args); bool HandleReloadSpellPetAurasCommand(const char* args); + bool HandleReloadSpellDisabledCommand(const char* args); bool HandleResetAchievementsCommand(const char * args); bool HandleResetAllCommand(const char * args); diff --git a/src/game/ChatHandler.cpp b/src/game/ChatHandler.cpp index 6d778db..4708379 100644 --- a/src/game/ChatHandler.cpp +++ b/src/game/ChatHandler.cpp @@ -18,6 +18,7 @@ #include "Common.h" #include "Log.h" +#include "ChatLog.h" #include "WorldPacket.h" #include "WorldSession.h" #include "World.h" @@ -179,11 +180,20 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) break; if(type == CHAT_MSG_SAY) + { + sChatLog.ChatMsg(GetPlayer(), msg, type); GetPlayer()->Say(msg, lang); + } else if(type == CHAT_MSG_EMOTE) + { + sChatLog.ChatMsg(GetPlayer(), msg, type); GetPlayer()->TextEmote(msg); + } else if(type == CHAT_MSG_YELL) + { + sChatLog.ChatMsg(GetPlayer(), msg, type); GetPlayer()->Yell(msg, lang); + } } break; case CHAT_MSG_WHISPER: @@ -198,6 +208,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if(msg.empty()) break; + sChatLog.WhisperMsg(GetPlayer(), to, msg); + if(!normalizePlayerName(to)) { SendPlayerNotFoundNotice(to); @@ -245,6 +257,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if(msg.empty()) break; + sChatLog.PartyMsg(GetPlayer(), msg); + // if player is in battleground, he cannot say to battleground members by /p Group *group = GetPlayer()->GetOriginalGroup(); if(!group) @@ -279,6 +293,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if(msg.empty()) break; + sChatLog.GuildMsg(GetPlayer(), msg, false); + if (GetPlayer()->GetGuildId()) if (Guild *guild = sObjectMgr.GetGuildById(GetPlayer()->GetGuildId())) guild->BroadcastToGuild(this, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL); @@ -301,6 +317,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if(msg.empty()) break; + sChatLog.GuildMsg(GetPlayer(), msg, true); + if (GetPlayer()->GetGuildId()) if (Guild *guild = sObjectMgr.GetGuildById(GetPlayer()->GetGuildId())) guild->BroadcastToOfficers(this, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL); @@ -323,6 +341,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if(msg.empty()) break; + sChatLog.RaidMsg(GetPlayer(), msg, type); + // if player is in battleground, he cannot say to battleground members by /ra Group *group = GetPlayer()->GetOriginalGroup(); if(!group) @@ -353,6 +373,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if(msg.empty()) break; + sChatLog.RaidMsg(GetPlayer(), msg, type); + // if player is in battleground, he cannot say to battleground members by /ra Group *group = GetPlayer()->GetOriginalGroup(); if(!group) @@ -378,6 +400,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if(msg.empty()) break; + sChatLog.RaidMsg(GetPlayer(), msg, type); + Group *group = GetPlayer()->GetGroup(); if(!group || !group->isRaidGroup() || !(group->IsLeader(GetPlayer()->GetGUID()) || group->IsAssistant(GetPlayer()->GetGUID()))) return; @@ -399,6 +423,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if(msg.empty()) break; + sChatLog.BattleGroundMsg(GetPlayer(), msg, type); + // battleground raid is always in Player->GetGroup(), never in GetOriginalGroup() Group *group = GetPlayer()->GetGroup(); if(!group || !group->isBGGroup()) @@ -420,6 +446,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if(msg.empty()) break; + sChatLog.BattleGroundMsg(GetPlayer(), msg, type); + // battleground raid is always in Player->GetGroup(), never in GetOriginalGroup() Group *group = GetPlayer()->GetGroup(); if(!group || !group->isBGGroup() || !group->IsLeader(GetPlayer()->GetGUID())) @@ -442,6 +470,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if(msg.empty()) break; + sChatLog.ChannelMsg(GetPlayer(), channel, msg); + if(ChannelMgr* cMgr = channelMgr(_player->GetTeam())) if(Channel *chn = cMgr->GetChannel(channel, _player)) chn->Say(_player->GetGUID(), msg.c_str(), lang); diff --git a/src/game/ChatLexicsCutter.cpp b/src/game/ChatLexicsCutter.cpp new file mode 100644 index 0000000..9980148 --- /dev/null +++ b/src/game/ChatLexicsCutter.cpp @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2005,2006,2007 MaNGOS + * + * This 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 "ChatLexicsCutter.h" +#include "Log.h" + +LexicsCutter::LexicsCutter() +{ + InvalidChars = "~`!@#$%^&*()-_+=[{]}|\\;:'\",<.>/?"; +} + +bool LexicsCutter::ReadUTF8(std::string& in, std::string& out, unsigned int& pos) +{ + if (pos >= in.length()) return false; + + out = ""; + unsigned char c = in[pos++]; + out += c; + int toread = trailingBytesForUTF8[(int) c]; + while ((pos < in.length()) && (toread > 0)) + { + out += in[pos++]; + toread--; + } + + return true; +} + +std::string LexicsCutter::trim(std::string& s, const std::string& drop) +{ + std::string r = s.erase(s.find_last_not_of(drop) + 1); + return r.erase(0, r.find_first_not_of(drop)); +} + +bool LexicsCutter::Read_Letter_Analogs(std::string& FileName) +{ + FILE *ma_file; + char line[1024]; + unsigned int pos; + std::string line_s; + std::string lchar; + std::string lanalog; + + ma_file = fopen(FileName.c_str(), "rb"); + + if (!ma_file) + { + sLog.outError("Chat lexics cutter disabled. Reason: LexicsCutterAnalogsFile file does not exist in the server directory."); + return false; + } + + while (!feof(ma_file)) + { + line[0] = 0x0; + fgets(line, 1020, ma_file); + + // check for UTF8 prefix and comments + if (strlen(line) >= 3) + { + if (line[0] == '\xEF' && line[1] == '\xBB' && line[2] == '\xBF') + { + strncpy(&line[0], &line[3], strlen(line) - 3); + } + } + + if (strlen(line) >= 2) + { + if (line[0] == '/' && line[1] == '/') continue; + } + + // check for empty string + line_s = line; + line_s = trim(line_s, "\x0A\x0D\x20"); + if (line_s == "") continue; + + // process line without CR/LF + line_s = line; + line_s = trim(line_s, "\x0A\x0D"); + + pos = 0; + if (ReadUTF8(line_s, lchar, pos)) + { + // create analogs vector + LC_AnalogVector av; + while (ReadUTF8(line_s, lanalog, pos)) + { + av.push_back(lanalog); + } + + // store vector in hash map + AnalogMap[lchar] = av; + } + } + + fclose(ma_file); + + return true; +} + +bool LexicsCutter::Read_Innormative_Words(std::string& FileName) +{ + FILE *ma_file; + char line[1024]; + unsigned int pos; + std::string line_s; + std::string lchar; + + ma_file = fopen(FileName.c_str(), "rb"); + + if (!ma_file) + { + sLog.outError("Chat lexics cutter disabled. Reason: LexicsCutterWordsFile file does not exist in the server directory."); + return false; + } + + while (!feof(ma_file)) + { + line[0] = 0x0; + fgets(line, 1020, ma_file); + + // check for UTF8 prefix and comment + if (strlen(line) >= 3) + { + if (line[0] == '\xEF' && line[1] == '\xBB' && line[2] == '\xBF') + { + strncpy(&line[0], &line[3], strlen(line) - 3); + } + } + + if (strlen(line) >= 2) + { + if (line[0] == '/' && line[1] == '/') continue; + } + + // check for empty string + line_s = line; + line_s = trim(line_s, "\x0A\x0D\x20"); + if (line_s == "") continue; + + // process line without CR/LF + line_s = line; + line_s = trim(line_s, "\x0A\x0D"); + + // create word vector of vectors + LC_WordVector vw; + pos = 0; + while (ReadUTF8(line_s, lchar, pos)) + { + // create letter set + LC_LetterSet vl; + + // initialize letter set with letter read + vl.insert(lchar); + + // find letter analogs and push them onto the vector + LC_AnalogMap::iterator itr = AnalogMap.find(lchar); + if (itr != AnalogMap.end()) + { + // analogs present, iterate + for (LC_AnalogVector::iterator itr2 = itr->second.begin(); itr2 != itr->second.end(); itr2++) + { + vl.insert(*itr2); + } + } + + // add letter vector to word vector + vw.push_back(vl); + } + + // push new word to words list + WordList.push_back(vw); + } + + fclose(ma_file); + + return true; +} + +void LexicsCutter::Map_Innormative_Words() +{ + // process all the words in the vector + for (unsigned int i = 0; i < WordList.size(); i++) + { + // parse all analogs in the first word letter + for (LC_LetterSet::iterator itr = (*WordList[i].begin()).begin(); itr != (*WordList[i].begin()).end(); itr++) + { + // map the word to its first letter variants + WordMap.insert(std::pair< std::string, unsigned int >(*itr, i)); + } + } +} + +bool LexicsCutter::Compare_Word(std::string& str, unsigned int pos, LC_WordVector word) +{ + std::string lchar_prev; + std::string lchar; + + // read first letter of the word into lchar_prev + ReadUTF8(str, lchar, pos); + + // okay, here we go, comparing word + // first letter is already okay, we do begin from second and go on + LC_WordVector::iterator i = word.begin(); + i++; + while (i != word.end()) + { + // get letter from word, return false if the string is shorter + if (!ReadUTF8(str, lchar, pos)) return(false); + // check, if the letter is in the set + LC_LetterSet ls = *i; + if (ls.count(lchar) == 0) + { + // letter is not in set, but we must check, if it is not space or repeat + if ( (!(IgnoreMiddleSpaces && (lchar == " "))) && + (!(IgnoreLetterRepeat && (lchar == lchar_prev))) ) + { + // no checks viable + return(false); + } + } + else + { + // next word letter + i++; + } + // set previous string letter to compare if needed (this check can really conserve time) + if (IgnoreLetterRepeat) lchar_prev = lchar; + } + + return(true); +} + +bool LexicsCutter::Check_Lexics(std::string& Phrase) +{ + std::string lchar; + LC_WordMap::iterator i; + std::pair< LC_WordMap::iterator, LC_WordMap::iterator > ii; + + if (Phrase.size() == 0) return(false); + + // first, convert the string, adding spaces and removing invalid characters + // also create fast position vector for the new positions + std::string str = " "; + unsigned int pos = 0; + while (ReadUTF8(Phrase, lchar, pos)) + { + if (InvalidChars.find(lchar) == std::string::npos) + { + str.append(lchar); + } + } + + // string prepared, now parse it and scan for all the words + unsigned int pos_prev = 0; + pos = 0; + while (ReadUTF8(str, lchar, pos)) + { + // got character, now try to find wordmap for it + ii = WordMap.equal_range(lchar); + // iterate over all found words + for (i = ii.first; i != ii.second; i++) + { + // compare word at initial position + if (Compare_Word(str, pos_prev, WordList[i->second])) return(true); + } + // set initial position to the current position + pos_prev = pos; + } + + return(false); +} diff --git a/src/game/ChatLexicsCutter.h b/src/game/ChatLexicsCutter.h new file mode 100644 index 0000000..dce3e4f --- /dev/null +++ b/src/game/ChatLexicsCutter.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2005,2006,2007 MaNGOS + * + * This 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_CHATLEXICSCUTTER_H +#define MANGOSSERVER_CHATLEXICSCUTTER_H + +typedef std::vector< std::string > LC_AnalogVector; +typedef std::map< std::string, LC_AnalogVector > LC_AnalogMap; +typedef std::set< std::string > LC_LetterSet; +typedef std::vector< LC_LetterSet > LC_WordVector; +typedef std::vector< LC_WordVector > LC_WordList; +typedef std::multimap< std::string, unsigned int > LC_WordMap; + +static int trailingBytesForUTF8[256] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 +}; + +class LexicsCutter +{ + protected: + LC_AnalogMap AnalogMap; + LC_WordList WordList; + LC_WordMap WordMap; + + std::string InvalidChars; + + public: + LexicsCutter(); + + static bool ReadUTF8(std::string& in, std::string& out, unsigned int& pos); + + std::string trim(std::string& s, const std::string& drop = " "); + bool Read_Letter_Analogs(std::string& FileName); + bool Read_Innormative_Words(std::string& FileName); + void Map_Innormative_Words(); + bool Compare_Word(std::string& str, unsigned int pos, LC_WordVector word); + bool Check_Lexics(std::string& Phrase); + + std::vector< std::pair< unsigned int, unsigned int > > Found; + bool IgnoreMiddleSpaces; + bool IgnoreLetterRepeat; +}; + +#endif diff --git a/src/game/ChatLog.cpp b/src/game/ChatLog.cpp new file mode 100644 index 0000000..6ba6181 --- /dev/null +++ b/src/game/ChatLog.cpp @@ -0,0 +1,859 @@ +/* + * Copyright (C) 2005,2006,2007 MaNGOS + * + * This 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 "ChatLexicsCutter.h" +#include "ChatLog.h" +#include "Chat.h" +#include "Group.h" +#include "Guild.h" +#include "ObjectMgr.h" +#include "SpellAuras.h" +#include "Policies/SingletonImp.h" +#include "Config/Config.h" + +INSTANTIATE_SINGLETON_1( ChatLog ); + +ChatLog::ChatLog() +{ + for (int i = 0; i <= CHATLOG_CHAT_TYPES_COUNT - 1; i++) + { + names[i] = ""; + files[i] = NULL; + } + + Lexics = NULL; + fn_innormative = ""; + f_innormative = NULL; + + Initialize(); +} + +ChatLog::~ChatLog() +{ + // close all files (avoiding double-close) + CloseAllFiles(); + + if (Lexics) + { + delete Lexics; + Lexics = NULL; + } +} + +void ChatLog::Initialize() +{ + // determine, if the chat logs are enabled + ChatLogEnable = sConfig.GetBoolDefault("ChatLogEnable", false); + ChatLogDateSplit = sConfig.GetBoolDefault("ChatLogDateSplit", false); + ChatLogUTFHeader = sConfig.GetBoolDefault("ChatLogUTFHeader", false); + ChatLogIgnoreUnprintable = sConfig.GetBoolDefault("ChatLogIgnoreUnprintable", false); + + if (ChatLogEnable) + { + // read chat log file names + names[CHAT_LOG_CHAT] = sConfig.GetStringDefault("ChatLogChatFile", ""); + names[CHAT_LOG_PARTY] = sConfig.GetStringDefault("ChatLogPartyFile", ""); + names[CHAT_LOG_GUILD] = sConfig.GetStringDefault("ChatLogGuildFile", ""); + names[CHAT_LOG_WHISPER] = sConfig.GetStringDefault("ChatLogWhisperFile", ""); + names[CHAT_LOG_CHANNEL] = sConfig.GetStringDefault("ChatLogChannelFile", ""); + names[CHAT_LOG_RAID] = sConfig.GetStringDefault("ChatLogRaidFile", ""); + names[CHAT_LOG_BATTLEGROUND] = sConfig.GetStringDefault("ChatLogBattleGroundFile", ""); + + // read screen log flags + screenflag[CHAT_LOG_CHAT] = sConfig.GetBoolDefault("ChatLogChatScreen", false); + screenflag[CHAT_LOG_PARTY] = sConfig.GetBoolDefault("ChatLogPartyScreen", false); + screenflag[CHAT_LOG_GUILD] = sConfig.GetBoolDefault("ChatLogGuildScreen", false); + screenflag[CHAT_LOG_WHISPER] = sConfig.GetBoolDefault("ChatLogWhisperScreen", false); + screenflag[CHAT_LOG_CHANNEL] = sConfig.GetBoolDefault("ChatLogChannelScreen", false); + screenflag[CHAT_LOG_RAID] = sConfig.GetBoolDefault("ChatLogRaidScreen", false); + screenflag[CHAT_LOG_BATTLEGROUND] = sConfig.GetBoolDefault("ChatLogBattleGroundScreen", false); + } + + // lexics cutter + LexicsCutterEnable = sConfig.GetBoolDefault("LexicsCutterEnable", false); + + if (LexicsCutterEnable) + { + // initialize lexics cutter parameters + LexicsCutterInnormativeCut = sConfig.GetBoolDefault("LexicsCutterInnormativeCut", true); + LexicsCutterNoActionOnGM = sConfig.GetBoolDefault("LexicsCutterNoActionOnGM", true); + LexicsCutterScreenLog = sConfig.GetBoolDefault("LexicsCutterScreenLog", false); + LexicsCutterCutReplacement = sConfig.GetStringDefault("LexicsCutterCutReplacement", "&!@^%!^&*!!! [gibberish]"); + LexicsCutterAction = sConfig.GetIntDefault("LexicsCutterAction", 0); + LexicsCutterActionDuration = sConfig.GetIntDefault("LexicsCutterActionDuration", 60000); + std::string fn_analogsfile = sConfig.GetStringDefault("LexicsCutterAnalogsFile", ""); + std::string fn_wordsfile = sConfig.GetStringDefault("LexicsCutterWordsFile", ""); + + // read lexics cutter flags + cutflag[CHAT_LOG_CHAT] = sConfig.GetBoolDefault("LexicsCutInChat", true); + cutflag[CHAT_LOG_PARTY] = sConfig.GetBoolDefault("LexicsCutInParty", true); + cutflag[CHAT_LOG_GUILD] = sConfig.GetBoolDefault("LexicsCutInGuild", true); + cutflag[CHAT_LOG_WHISPER] = sConfig.GetBoolDefault("LexicsCutInWhisper", true); + cutflag[CHAT_LOG_CHANNEL] = sConfig.GetBoolDefault("LexicsCutInChannel", true); + cutflag[CHAT_LOG_RAID] = sConfig.GetBoolDefault("LexicsCutInRaid", true); + cutflag[CHAT_LOG_BATTLEGROUND] = sConfig.GetBoolDefault("LexicsCutInBattleGround", true); + + if (fn_analogsfile == "" || fn_wordsfile == "") + { + LexicsCutterEnable = false; + } + else + { + // initialize lexics cutter + Lexics = new LexicsCutter; + if (Lexics) Lexics->Read_Letter_Analogs(fn_analogsfile); + if (Lexics) Lexics->Read_Innormative_Words(fn_wordsfile); + if (Lexics) Lexics->Map_Innormative_Words(); + + // read additional parameters + Lexics->IgnoreLetterRepeat = sConfig.GetBoolDefault("LexicsCutterIgnoreRepeats", true); + Lexics->IgnoreMiddleSpaces = sConfig.GetBoolDefault("LexicsCutterIgnoreSpaces", true); + fn_innormative = sConfig.GetStringDefault("LexicsCutterLogFile", ""); + } + } + + // open all files (with aliasing) + OpenAllFiles(); + + // write timestamps (init) + WriteInitStamps(); +} + +bool ChatLog::_ChatCommon(int ChatType, Player *player, std::string &msg) +{ + if (LexicsCutterEnable && Lexics && cutflag[ChatType] && Lexics->Check_Lexics(msg)) ChatBadLexicsAction(player, msg); + + if (!ChatLogEnable) return(false); + + if (ChatLogIgnoreUnprintable) + { + // have to ignore unprintables, verify string by UTF8 here + unsigned int pos = 0; + std::string lchar; + while (LexicsCutter::ReadUTF8(msg, lchar, pos)) + { + if (lchar.size() == 1) + { + if (lchar[0] < ' ') return(false); // unprintable detected + } + } + } + + return(true); +} + +void ChatLog::ChatMsg(Player *player, std::string &msg, uint32 type) +{ + if (!_ChatCommon(CHAT_LOG_CHAT, player, msg)) return; + + CheckDateSwitch(); + + std::string log_str = ""; + + switch (type) + { + case CHAT_MSG_EMOTE: + log_str.append("{EMOTE} "); + break; + + case CHAT_MSG_YELL: + log_str.append("{YELL} "); + break; + } + + log_str.append("["); + log_str.append(player->GetName()); + log_str.append("] "); + + log_str.append(msg); + + log_str.append("\n"); + + if (screenflag[CHAT_LOG_CHAT]) printf("%s", log_str.c_str()); + if (files[CHAT_LOG_CHAT]) + { + OutTimestamp(files[CHAT_LOG_CHAT]); + fprintf(files[CHAT_LOG_CHAT], "%s", log_str.c_str()); + fflush(files[CHAT_LOG_CHAT]); + } +} + +void ChatLog::PartyMsg(Player *player, std::string &msg) +{ + if (!_ChatCommon(CHAT_LOG_PARTY, player, msg)) return; + + CheckDateSwitch(); + + std::string log_str = ""; + + log_str.append("["); + log_str.append(player->GetName()); + log_str.append("]->GROUP:"); + + Group *group = player->GetGroup(); + if (!group) + { + log_str.append("[unknown group] "); + } + else + { + // obtain group information + log_str.append("["); + + uint8 gm_count = group->GetMembersCount(); + uint8 gm_count_m1 = gm_count - 1; + uint64 gm_leader_GUID = group->GetLeaderGUID(); + Player *gm_member; + + gm_member = sObjectMgr.GetPlayer(gm_leader_GUID); + if (gm_member) + { + log_str.append(gm_member->GetName()); + log_str.append(","); + } + + Group::MemberSlotList g_members = group->GetMemberSlots(); + + for (Group::member_citerator itr = g_members.begin(); itr != g_members.end(); itr++) + { + if (itr->guid == gm_leader_GUID) continue; + + gm_member = sObjectMgr.GetPlayer(itr->guid); + if (gm_member) + { + log_str.append(itr->name); + log_str.append(","); + } + } + + log_str.erase(log_str.length() - 1); + log_str.append("] "); + } + + log_str.append(msg); + + log_str.append("\n"); + + if (screenflag[CHAT_LOG_PARTY]) printf("%s", log_str.c_str()); + if (files[CHAT_LOG_PARTY]) + { + OutTimestamp(files[CHAT_LOG_PARTY]); + fprintf(files[CHAT_LOG_PARTY], "%s", log_str.c_str()); + fflush(files[CHAT_LOG_PARTY]); + } +} + +void ChatLog::GuildMsg(Player *player, std::string &msg, bool officer) +{ + if (!_ChatCommon(CHAT_LOG_GUILD, player, msg)) return; + + CheckDateSwitch(); + + std::string log_str = ""; + + log_str.append("["); + log_str.append(player->GetName()); + log_str.append((officer ? "]->GUILD_OFF:" : "]->GUILD:")); + + if (!player->GetGuildId()) + { + log_str.append("[unknown guild] "); + } + else + { + Guild *guild = sObjectMgr.GetGuildById(player->GetGuildId()); + if (!guild) + { + log_str.append("[unknown guild] "); + } + else + { + // obtain guild information + log_str.append("("); + log_str.append(guild->GetName()); + log_str.append(") "); + } + } + + log_str.append(msg); + + log_str.append("\n"); + + if (screenflag[CHAT_LOG_GUILD]) printf("%s", log_str.c_str()); + if (files[CHAT_LOG_GUILD]) + { + OutTimestamp(files[CHAT_LOG_GUILD]); + fprintf(files[CHAT_LOG_GUILD], "%s", log_str.c_str()); + fflush(files[CHAT_LOG_GUILD]); + } +} + +void ChatLog::WhisperMsg(Player *player, std::string &to, std::string &msg) +{ + if (!_ChatCommon(CHAT_LOG_WHISPER, player, msg)) return; + + CheckDateSwitch(); + + std::string log_str = ""; + + log_str.append("["); + log_str.append(player->GetName()); + log_str.append("]->"); + + if (to.size() == 0) + { + log_str.append("[???] "); + } + else + { + normalizePlayerName(to); + log_str.append("["); + log_str.append(to); + log_str.append("] "); + } + + log_str.append(msg); + + log_str.append("\n"); + + if (screenflag[CHAT_LOG_WHISPER]) printf("%s", log_str.c_str()); + if (files[CHAT_LOG_WHISPER]) + { + OutTimestamp(files[CHAT_LOG_WHISPER]); + fprintf(files[CHAT_LOG_WHISPER], "%s", log_str.c_str()); + fflush(files[CHAT_LOG_WHISPER]); + } +} + +void ChatLog::ChannelMsg(Player *player, std::string &channel, std::string &msg) +{ + if (!_ChatCommon(CHAT_LOG_CHANNEL, player, msg)) return; + + CheckDateSwitch(); + + std::string log_str = ""; + + log_str.append("["); + log_str.append(player->GetName()); + log_str.append("]->CHANNEL:"); + + if (channel.size() == 0) + { + log_str.append("[unknown channel] "); + } + else + { + log_str.append("["); + log_str.append(channel); + log_str.append("] "); + } + + log_str.append(msg); + + log_str.append("\n"); + + if (screenflag[CHAT_LOG_CHANNEL]) printf("%s", log_str.c_str()); + if (files[CHAT_LOG_CHANNEL]) + { + OutTimestamp(files[CHAT_LOG_CHANNEL]); + fprintf(files[CHAT_LOG_CHANNEL], "%s", log_str.c_str()); + fflush(files[CHAT_LOG_CHANNEL]); + } +} + +void ChatLog::RaidMsg(Player *player, std::string &msg, uint32 type) +{ + if (!_ChatCommon(CHAT_LOG_RAID, player, msg)) return; + + CheckDateSwitch(); + + std::string log_str = ""; + + log_str.append("["); + log_str.append(player->GetName()); + + switch (type) + { + case CHAT_MSG_RAID: + log_str.append("]->RAID:"); + break; + + case CHAT_MSG_RAID_LEADER: + log_str.append("]->RAID_LEADER:"); + break; + + case CHAT_MSG_RAID_WARNING: + log_str.append("]->RAID_WARN:"); + break; + + default: + log_str.append("]->RAID_UNKNOWN:"); + } + + Group *group = player->GetGroup(); + if (!group) + { + log_str.append("[unknown raid] "); + } + else + { + // obtain group information + log_str.append("["); + + uint8 gm_count = group->GetMembersCount(); + uint8 gm_count_m1 = gm_count - 1; + uint64 gm_leader_GUID = group->GetLeaderGUID(); + Player *gm_member; + + gm_member = sObjectMgr.GetPlayer(gm_leader_GUID); + if (gm_member) + { + log_str.append(gm_member->GetName()); + log_str.append(","); + } + + Group::MemberSlotList g_members = group->GetMemberSlots(); + + for (Group::member_citerator itr = g_members.begin(); itr != g_members.end(); itr++) + { + if (itr->guid == gm_leader_GUID) continue; + + gm_member = sObjectMgr.GetPlayer(itr->guid); + if (gm_member) + { + log_str.append(itr->name); + log_str.append(","); + } + } + + log_str.erase(log_str.length() - 1); + log_str.append("] "); + } + + log_str.append(msg); + + log_str.append("\n"); + + if (screenflag[CHAT_LOG_RAID]) printf("%s", log_str.c_str()); + if (files[CHAT_LOG_RAID]) + { + OutTimestamp(files[CHAT_LOG_RAID]); + fprintf(files[CHAT_LOG_RAID], "%s", log_str.c_str()); + fflush(files[CHAT_LOG_RAID]); + } +} + +void ChatLog::BattleGroundMsg(Player *player, std::string &msg, uint32 type) +{ + if (!_ChatCommon(CHAT_LOG_BATTLEGROUND, player, msg)) return; + + CheckDateSwitch(); + + std::string log_str = ""; + + log_str.append("["); + log_str.append(player->GetName()); + + switch (type) + { + case CHAT_MSG_BATTLEGROUND: + log_str.append("]->BG:"); + break; + + case CHAT_MSG_BATTLEGROUND_LEADER: + log_str.append("]->BG_LEADER:"); + break; + + default: + log_str.append("]->BG_UNKNOWN:"); + } + + Group *group = player->GetGroup(); + if (!group) + { + log_str.append("[unknown group] "); + } + else + { + // obtain group information + log_str.append("["); + + uint8 gm_count = group->GetMembersCount(); + uint8 gm_count_m1 = gm_count - 1; + uint64 gm_leader_GUID = group->GetLeaderGUID(); + Player *gm_member; + + gm_member = sObjectMgr.GetPlayer(gm_leader_GUID); + if (gm_member) + { + log_str.append(gm_member->GetName()); + log_str.append(","); + } + + Group::MemberSlotList g_members = group->GetMemberSlots(); + + for (Group::member_citerator itr = g_members.begin(); itr != g_members.end(); itr++) + { + if (itr->guid == gm_leader_GUID) continue; + + gm_member = sObjectMgr.GetPlayer(itr->guid); + if (gm_member) + { + log_str.append(itr->name); + log_str.append(","); + } + } + + log_str.erase(log_str.length() - 1); + log_str.append("] "); + } + + log_str.append(msg); + + log_str.append("\n"); + + if (screenflag[CHAT_LOG_BATTLEGROUND]) printf("%s", log_str.c_str()); + if (files[CHAT_LOG_BATTLEGROUND]) + { + OutTimestamp(files[CHAT_LOG_BATTLEGROUND]); + fprintf(files[CHAT_LOG_BATTLEGROUND], "%s", log_str.c_str()); + fflush(files[CHAT_LOG_BATTLEGROUND]); + } +} + +void ChatLog::OpenAllFiles() +{ + std::string tempname; + char dstr[12]; + + if (ChatLogDateSplit) + { + time_t t = time(NULL); + tm* aTm = localtime(&t); + sprintf(dstr, "%-4d-%02d-%02d", aTm->tm_year + 1900, aTm->tm_mon + 1, aTm->tm_mday); + } + + if (ChatLogEnable) + { + for (int i = 0; i <= CHATLOG_CHAT_TYPES_COUNT - 1; i++) + { + if (names[i] != "") + { + for (int j = i - 1; j >= 0; j--) + { + if (names[i] == names[j]) + { + files[i] = files[j]; + break; + } + } + if (!files[i]) + { + tempname = names[i]; + if (ChatLogDateSplit) + { + // append date instead of $d if applicable + int dpos = tempname.find("$d"); + if (dpos != tempname.npos) + { + tempname.replace(dpos, 2, &dstr[0], 10); + } + } + files[i] = fopen(tempname.c_str(), "a+b"); + if (ChatLogUTFHeader && (ftell(files[i]) == 0)) fputs("\xEF\xBB\xBF", files[i]); + } + } + } + } + + // initialize innormative log + if (LexicsCutterEnable) + { + if (fn_innormative != "") + { + tempname = fn_innormative; + if (ChatLogDateSplit) + { + // append date instead of $d if applicable + int dpos = tempname.find("$d"); + if (dpos != tempname.npos) + { + tempname.replace(dpos, 2, &dstr[0], 10); + } + } + f_innormative = fopen(tempname.c_str(), "a+b"); + if (ChatLogUTFHeader && (ftell(f_innormative) == 0)) fputs("\xEF\xBB\xBF", f_innormative); + } + } +} + +void ChatLog::CloseAllFiles() +{ + for (int i = 0; i <= CHATLOG_CHAT_TYPES_COUNT - 1; i++) + { + if (files[i]) + { + for (int j = i + 1; j <= CHATLOG_CHAT_TYPES_COUNT - 1; j++) + { + if (files[j] == files[i]) files[j] = NULL; + } + + fclose(files[i]); + files[i] = NULL; + } + } + + if (f_innormative) + { + fclose(f_innormative); + f_innormative = NULL; + } +} + +void ChatLog::CheckDateSwitch() +{ + if (ChatLogDateSplit) + { + time_t t = time(NULL); + tm* aTm = localtime(&t); + if (lastday != aTm->tm_mday) + { + // date switched + CloseAllFiles(); + OpenAllFiles(); + WriteInitStamps(); + } + } +} + +void ChatLog::WriteInitStamps() +{ + // remember date + time_t t = time(NULL); + tm* aTm = localtime(&t); + lastday = aTm->tm_mday; + + if (files[CHAT_LOG_CHAT]) + { + OutTimestamp(files[CHAT_LOG_CHAT]); + fprintf(files[CHAT_LOG_CHAT], "%s", "[SYSTEM] Chat Log Initialized\n"); + } + if (files[CHAT_LOG_PARTY]) + { + OutTimestamp(files[CHAT_LOG_PARTY]); + fprintf(files[CHAT_LOG_PARTY], "%s", "[SYSTEM] Party Chat Log Initialized\n"); + } + if (files[CHAT_LOG_GUILD]) + { + OutTimestamp(files[CHAT_LOG_GUILD]); + fprintf(files[CHAT_LOG_GUILD], "%s", "[SYSTEM] Guild Chat Log Initialized\n"); + } + if (files[CHAT_LOG_WHISPER]) + { + OutTimestamp(files[CHAT_LOG_WHISPER]); + fprintf(files[CHAT_LOG_WHISPER], "%s", "[SYSTEM] Whisper Log Initialized\n"); + } + if (files[CHAT_LOG_CHANNEL]) + { + OutTimestamp(files[CHAT_LOG_CHANNEL]); + fprintf(files[CHAT_LOG_CHANNEL], "%s", "[SYSTEM] Chat Channels Log Initialized\n"); + } + if (files[CHAT_LOG_RAID]) + { + OutTimestamp(files[CHAT_LOG_RAID]); + fprintf(files[CHAT_LOG_RAID], "%s", "[SYSTEM] Raid Party Chat Log Initialized\n"); + } + + if (f_innormative) + { + OutTimestamp(f_innormative); + fprintf(f_innormative, "%s", "[SYSTEM] Innormative Lexics Log Initialized\n"); + } +} + +void ChatLog::OutTimestamp(FILE* file) +{ + time_t t = time(NULL); + tm* aTm = localtime(&t); + fprintf(file, "%-4d-%02d-%02d %02d:%02d:%02d ", aTm->tm_year + 1900, aTm->tm_mon + 1, aTm->tm_mday, aTm->tm_hour, aTm->tm_min, aTm->tm_sec); +} + +void ChatLog::ChatBadLexicsAction(Player* player, std::string& msg) +{ + // logging + std::string log_str = ""; + + log_str.append("["); + log_str.append(player->GetName()); + log_str.append("] "); + + log_str.append(msg); + + log_str.append("\n"); + + if (LexicsCutterScreenLog) printf(" %s", log_str.c_str()); + if (f_innormative) + { + OutTimestamp(f_innormative); + fprintf(f_innormative, "%s", log_str.c_str()); + fflush(f_innormative); + } + + // cutting innormative lexics + if (LexicsCutterInnormativeCut) + { + msg = LexicsCutterCutReplacement; + } + + if (!player || !player->GetSession()) return; + + if (LexicsCutterNoActionOnGM && player->GetSession()->GetSecurity()) return; + + // special action + const SpellEntry* sl; + + switch (LexicsCutterAction) + { + case LEXICS_ACTION_SHEEP: + { + // sheep me, yeah, yeah, sheep me + sl = sSpellStore.LookupEntry(118); + if (sl) + { + for (int i = 0; i < MAX_EFFECT_INDEX; i++) + { + SpellAuraHolder *holder = CreateSpellAuraHolder(sl, player, NULL); + Aura* Aur = CreateAura(sl, SpellEffectIndex(i), NULL, holder, player); + if (Aur) + { + Aur->SetAuraDuration(LexicsCutterActionDuration); + holder->AddAura(Aur, SpellEffectIndex(i)); + } + } + } + } + break; + + case LEXICS_ACTION_STUN: + { + // stunned surprised + sl = sSpellStore.LookupEntry(13005); + if (sl) + { + for (int i = 0; i < MAX_EFFECT_INDEX; i++) + { + SpellAuraHolder *holder = CreateSpellAuraHolder(sl, player, NULL); + Aura* Aur = CreateAura(sl, SpellEffectIndex(i), NULL, holder, player); + if (Aur) + { + Aur->SetAuraDuration(LexicsCutterActionDuration); + holder->AddAura(Aur, SpellEffectIndex(i)); + } + } + } + } + break; + + case LEXICS_ACTION_DIE: + { + // oops, kicked the bucket + player->DealDamage(player, player->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + break; + + case LEXICS_ACTION_DRAIN: + { + // living corpse :) + player->DealDamage(player, player->GetHealth() - 5, NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + break; + + case LEXICS_ACTION_SILENCE: + { + // glue the mouth + time_t mutetime = time(NULL) + (int) (LexicsCutterActionDuration / 1000); + player->GetSession()->m_muteTime = mutetime; + } + break; + + case LEXICS_ACTION_STUCK: + { + // yo, the Matrix has had you :) [by KAPATEJIb] + sl = sSpellStore.LookupEntry(23312); + if (sl) + { + for (int i = 0; i < MAX_EFFECT_INDEX; i++) + { + SpellAuraHolder *holder = CreateSpellAuraHolder(sl, player, NULL); + Aura* Aur = CreateAura(sl, SpellEffectIndex(i), NULL, holder, player); + if (Aur) + { + Aur->SetAuraDuration(LexicsCutterActionDuration); + holder->AddAura(Aur, SpellEffectIndex(i)); + } + } + } + } + break; + + case LEXICS_ACTION_SICKNESS: + { + // for absence of censorship, there is punishment [by Koshei] + sl = sSpellStore.LookupEntry(15007); + if (sl) + { + for (int i = 0; i < MAX_EFFECT_INDEX; i++) + { + SpellAuraHolder *holder = CreateSpellAuraHolder(sl, player, NULL); + Aura* Aur = CreateAura(sl, SpellEffectIndex(i), NULL, holder, player); + if (Aur) + { + Aur->SetAuraDuration(LexicsCutterActionDuration); + holder->AddAura(Aur, SpellEffectIndex(i)); + } + } + } + } + break; + + case LEXICS_ACTION_SHEAR: + { + // Lord Illidan to watch you [by Koshei] + sl = sSpellStore.LookupEntry(41032); + if (sl) + { + for (int i = 0; i < MAX_EFFECT_INDEX; i++) + { + SpellAuraHolder *holder = CreateSpellAuraHolder(sl, player, NULL); + Aura* Aur = CreateAura(sl, SpellEffectIndex(i), NULL, holder, player); + if (Aur) + { + Aur->SetAuraDuration(LexicsCutterActionDuration); + holder->AddAura(Aur, SpellEffectIndex(i)); + } + } + } + } + break; + + default: + // no action except logging + break; + } +} \ No newline at end of file diff --git a/src/game/ChatLog.h b/src/game/ChatLog.h new file mode 100644 index 0000000..e2962c8 --- /dev/null +++ b/src/game/ChatLog.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2005,2006,2007 MaNGOS + * + * This 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_CHATLOG_H +#define MANGOSSERVER_CHATLOG_H + +#include "SharedDefines.h" +#include "ChatLexicsCutter.h" +#include "ObjectMgr.h" +#include "Policies/Singleton.h" + +#define CHATLOG_CHAT_TYPES_COUNT 7 + +enum ChatLogFiles +{ + CHAT_LOG_CHAT = 0, + CHAT_LOG_PARTY = 1, + CHAT_LOG_GUILD = 2, + CHAT_LOG_WHISPER = 3, + CHAT_LOG_CHANNEL = 4, + CHAT_LOG_RAID = 5, + CHAT_LOG_BATTLEGROUND = 6, +}; + +enum LexicsActions +{ + LEXICS_ACTION_LOG = 0, + LEXICS_ACTION_SHEEP = 1, + LEXICS_ACTION_STUN = 2, + LEXICS_ACTION_DIE = 3, + LEXICS_ACTION_DRAIN = 4, + LEXICS_ACTION_SILENCE = 5, + LEXICS_ACTION_STUCK = 6, + LEXICS_ACTION_SICKNESS = 7, + LEXICS_ACTION_SHEAR = 8, +}; + +class ChatLog : public MaNGOS::Singleton > +{ + public: + ChatLog(); + ~ChatLog(); + + void Initialize(); + + void ChatMsg(Player *player, std::string &msg, uint32 type); + void PartyMsg(Player *player, std::string &msg); + void GuildMsg(Player *player, std::string &msg, bool officer); + void WhisperMsg(Player *player, std::string &to, std::string &msg); + void ChannelMsg(Player *player, std::string &channel, std::string &msg); + void RaidMsg(Player *player, std::string &msg, uint32 type); + void BattleGroundMsg(Player *player, std::string &msg, uint32 type); + + void ChatBadLexicsAction(Player *player, std::string &msg); + + private: + bool _ChatCommon(int ChatType, Player *player, std::string &msg); + + bool ChatLogEnable; + bool ChatLogDateSplit; + bool ChatLogUTFHeader; + bool ChatLogIgnoreUnprintable; + + int lastday; + + FILE* files[CHATLOG_CHAT_TYPES_COUNT]; + std::string names[CHATLOG_CHAT_TYPES_COUNT]; + bool screenflag[CHATLOG_CHAT_TYPES_COUNT]; + + LexicsCutter* Lexics; + bool cutflag[CHATLOG_CHAT_TYPES_COUNT]; + + bool LexicsCutterEnable; + bool LexicsCutterInnormativeCut; + bool LexicsCutterNoActionOnGM; + bool LexicsCutterScreenLog; + std::string LexicsCutterCutReplacement; + int LexicsCutterAction; + int LexicsCutterActionDuration; + std::string fn_innormative; + FILE* f_innormative; + + void OpenAllFiles(); + void CloseAllFiles(); + void CheckDateSwitch(); + + void WriteInitStamps(); + void OutTimestamp(FILE *file); +}; + +#define sChatLog MaNGOS::Singleton::Instance() +#endif diff --git a/src/game/ConfusedMovementGenerator.cpp b/src/game/ConfusedMovementGenerator.cpp index 4cf7bf2..8f132b5 100644 --- a/src/game/ConfusedMovementGenerator.cpp +++ b/src/game/ConfusedMovementGenerator.cpp @@ -53,7 +53,8 @@ ConfusedMovementGenerator::Initialize(T &unit) bool is_water = map->IsInWater(i_waypoints[idx][0],i_waypoints[idx][1],z); // if generated wrong path just ignore - if ((is_water && !is_water_ok) || (!is_water && !is_land_ok)) + if ((is_water && !is_water_ok) || (!is_water && !is_land_ok) || + !map->IsNextZcoordOK(i_waypoints[idx][0], i_waypoints[idx][1], z)) // check if not under map { i_waypoints[idx][0] = idx > 0 ? i_waypoints[idx-1][0] : x; i_waypoints[idx][1] = idx > 0 ? i_waypoints[idx-1][1] : y; diff --git a/src/game/CreatureAI.cpp b/src/game/CreatureAI.cpp index 33cb568..8f9e0df 100644 --- a/src/game/CreatureAI.cpp +++ b/src/game/CreatureAI.cpp @@ -74,6 +74,9 @@ CanCastResult CreatureAI::CanCastSpell(Unit* pTarget, const SpellEntry *pSpell, CanCastResult CreatureAI::DoCastSpellIfCan(Unit* pTarget, uint32 uiSpell, uint32 uiCastFlags, uint64 uiOriginalCasterGUID) { + if (!pTarget) + return CAST_FAIL_OTHER; + Unit* pCaster = m_creature; if (uiCastFlags & CAST_FORCE_TARGET_SELF) diff --git a/src/game/DBCStores.cpp b/src/game/DBCStores.cpp index aaf0d37..2393a36 100644 --- a/src/game/DBCStores.cpp +++ b/src/game/DBCStores.cpp @@ -278,13 +278,19 @@ struct LocalData }; template -inline void LoadDBC(LocalData& localeData,barGoLink& bar, StoreProblemList& errlist, DBCStorage& storage, const std::string& dbc_path, const std::string& filename) +inline void LoadDBC(LocalData& localeData,barGoLink& bar, StoreProblemList& errlist, DBCStorage& storage, const std::string& dbc_path, const std::string& filename, const std::string * custom_entries = NULL, const std::string * idname = NULL) { // 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())) + + SqlDbc * sql = NULL; + + if (custom_entries) + sql = new SqlDbc(&filename,custom_entries,idname,storage.GetFormat()); + + if(storage.Load(dbc_filename.c_str(),sql)) { bar.step(); for(uint8 i = 0; fullLocaleNameList[i].name; ++i) @@ -338,6 +344,9 @@ inline void LoadDBC(LocalData& localeData,barGoLink& bar, StoreProblemList& errl else errlist.push_back(dbc_filename); } + + if (sql) + delete sql; } void LoadDBCStores(const std::string& dataPath) @@ -471,7 +480,7 @@ void LoadDBCStores(const std::string& dataPath) 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"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellStore, dbcPath,"Spell.dbc", &CustomSpellEntryfmt, &CustomSpellEntryIndex); for(uint32 i = 1; i < sSpellStore.GetNumRows(); ++i) { SpellEntry const * spell = sSpellStore.LookupEntry(i); diff --git a/src/game/DBCStructure.h b/src/game/DBCStructure.h index 55c21f7..5d1350d 100644 --- a/src/game/DBCStructure.h +++ b/src/game/DBCStructure.h @@ -1584,9 +1584,9 @@ struct SpellItemEnchantmentEntry 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 - // 37 new in 3.1 + uint32 requiredSkill; // 35 m_requiredSkillID + uint32 requiredSkillValue; // 36 m_requiredSkillRank + uint32 requiredLevel; // 37 m_requiredLevel }; struct SpellItemEnchantmentConditionEntry diff --git a/src/game/DBCfmt.h b/src/game/DBCfmt.h index 27d82d9..6be8402 100644 --- a/src/game/DBCfmt.h +++ b/src/game/DBCfmt.h @@ -89,8 +89,10 @@ const char SpellCastTimefmt[]="nixx"; const char SpellDurationfmt[]="niii"; const char SpellDifficultyfmt[]="niiii"; const char SpellEntryfmt[]="niiiiiiiiiixixixiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiifxiiiiiiiiiiiiiiiiiiiiiiiiiiiifffiiiiiiiiiiiiiiiiiiiiifffiiiiiiiiiiiiiiifffiiiiiiiiiiiiixssssssssssssssssxssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiiiiiiiiiiixfffxxxiiiiixxxxxxi"; +const std::string CustomSpellEntryfmt="pappppppppaapaaapaaaaaaaaaaapaaapapppppppaaaaapaapaaaaaaaaaaaaaaaaaappppppppppppppppppppppppppppppppppppaaaaaapppppppppaaapppppppppaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaappppppppapppaaaaappaaaaaaa"; +const std::string CustomSpellEntryIndex = "Id"; const char SpellFocusObjectfmt[]="nxxxxxxxxxxxxxxxxx"; -const char SpellItemEnchantmentfmt[]="nxiiiiiixxxiiissssssssssssssssxiiiixxx"; +const char SpellItemEnchantmentfmt[]="nxiiiiiixxxiiissssssssssssssssxiiiiiii"; const char SpellItemEnchantmentConditionfmt[]="nbbbbbxxxxxbbbbbbbbbbiiiiiXXXXX"; const char SpellRadiusfmt[]="nfxf"; const char SpellRangefmt[]="nffffxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; diff --git a/src/game/GameObject.cpp b/src/game/GameObject.cpp index c05e963..9857bb5 100644 --- a/src/game/GameObject.cpp +++ b/src/game/GameObject.cpp @@ -154,6 +154,13 @@ bool GameObject::Create(uint32 guidlow, uint32 name_id, Map *map, uint32 phaseMa ((InstanceMap*)map)->GetInstanceData()->OnObjectCreate(this); } + if (goinfo->type == GAMEOBJECT_TYPE_TRANSPORT) + { + SetUInt32Value(GAMEOBJECT_LEVEL, goinfo->transport.pause); + if (goinfo->transport.startOpen) + SetGoState(GO_STATE_ACTIVE); + } + return true; } @@ -661,6 +668,15 @@ bool GameObject::IsTransport() const return gInfo->type == GAMEOBJECT_TYPE_TRANSPORT || gInfo->type == GAMEOBJECT_TYPE_MO_TRANSPORT; } +// is Dynamic transport = non-stop Transport +bool GameObject::IsDynTransport() const +{ + // If something is marked as a transport, don't transmit an out of range packet for it. + GameObjectInfo const * gInfo = GetGOInfo(); + if(!gInfo) return false; + return gInfo->type == GAMEOBJECT_TYPE_MO_TRANSPORT || (gInfo->type == GAMEOBJECT_TYPE_TRANSPORT && !gInfo->transport.pause); +} + Unit* GameObject::GetOwner() const { return ObjectAccessor::GetUnit(*this, GetOwnerGUID()); @@ -694,12 +710,18 @@ bool GameObject::isVisibleForInState(Player const* u, WorldObject const* viewPoi return false; // special invisibility cases - /* TODO: implement trap stealth, take look at spell 2836 - if(GetGOInfo()->type == GAMEOBJECT_TYPE_TRAP && GetGOInfo()->trap.stealthed && u->IsHostileTo(GetOwner())) + // TODO: implement trap stealth, take look at spell 2836 + if(GetGOInfo()->type == GAMEOBJECT_TYPE_TRAP && GetGOInfo()->trap.stealthed) { - if(check stuff here) + if(u->HasAura(2836) && isInFront(u, 15.0f)) // hack, maybe values are wrong + return true; + + if (GetOwner() && u->IsFriendlyTo(GetOwner())) + return true; + + if(m_lootState == GO_READY) return false; - }*/ + } } // check distance @@ -731,7 +753,7 @@ bool GameObject::ActivateToQuest( Player *pTarget)const //look for battlegroundAV for some objects which are only activated after mine gots captured by own team if (GetEntry() == BG_AV_OBJECTID_MINE_N || GetEntry() == BG_AV_OBJECTID_MINE_S) if (BattleGround *bg = pTarget->GetBattleGround()) - if (bg->GetTypeID() == BATTLEGROUND_AV && !(((BattleGroundAV*)bg)->PlayerCanDoMineQuest(GetEntry(),pTarget->GetTeam()))) + if (bg->GetTypeID(true) == BATTLEGROUND_AV && !(((BattleGroundAV*)bg)->PlayerCanDoMineQuest(GetEntry(),pTarget->GetTeam()))) return false; return true; } @@ -1321,15 +1343,15 @@ void GameObject::Use(Unit* user) { case 179785: // Silverwing Flag // check if it's correct bg - if(bg->GetTypeID() == BATTLEGROUND_WS) + if(bg->GetTypeID(true) == BATTLEGROUND_WS) bg->EventPlayerClickedOnFlag(player, this); break; case 179786: // Warsong Flag - if(bg->GetTypeID() == BATTLEGROUND_WS) + if(bg->GetTypeID(true) == BATTLEGROUND_WS) bg->EventPlayerClickedOnFlag(player, this); break; case 184142: // Netherstorm Flag - if(bg->GetTypeID() == BATTLEGROUND_EY) + if(bg->GetTypeID(true) == BATTLEGROUND_EY) bg->EventPlayerClickedOnFlag(player, this); break; } diff --git a/src/game/GameObject.h b/src/game/GameObject.h index d7a9ee6..dac0fe4 100644 --- a/src/game/GameObject.h +++ b/src/game/GameObject.h @@ -590,6 +590,7 @@ class MANGOS_DLL_SPEC GameObject : public WorldObject GameObjectInfo const* GetGOInfo() const; bool IsTransport() const; + bool IsDynTransport() const; uint32 GetDBTableGUIDLow() const { return m_DBTableGuid; } diff --git a/src/game/Group.cpp b/src/game/Group.cpp index 0126867..96f758c 100644 --- a/src/game/Group.cpp +++ b/src/game/Group.cpp @@ -28,6 +28,7 @@ #include "Formulas.h" #include "ObjectAccessor.h" #include "BattleGround.h" +#include "BattleGroundMgr.h" #include "MapManager.h" #include "InstanceSaveMgr.h" #include "MapInstanced.h" @@ -1581,6 +1582,10 @@ GroupJoinBattlegroundResult Group::CanJoinBattleGroundQueue(BattleGround const* uint32 arenaTeamId = reference->GetArenaTeamId(arenaSlot); uint32 team = reference->GetTeam(); + uint32 allowedPlayerCount = 0; + + BattleGroundQueueTypeId bgQueueTypeIdRandom = BattleGroundMgr::BGQueueTypeId(BATTLEGROUND_RB, 0); + // check every member of the group to be able to join for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next()) { @@ -1601,13 +1606,26 @@ GroupJoinBattlegroundResult Group::CanJoinBattleGroundQueue(BattleGround const* // don't let join if someone from the group is already in that bg queue if(member->InBattleGroundQueueForBattleGroundQueueType(bgQueueTypeId)) return ERR_BATTLEGROUND_JOIN_FAILED; // not blizz-like + // don't let join if someone from the group is in bg queue random + if(member->InBattleGroundQueueForBattleGroundQueueType(bgQueueTypeIdRandom)) + return ERR_IN_RANDOM_BG; + // don't let join to bg queue random if someone from the group is already in bg queue + if(bgOrTemplate->GetTypeID() == BATTLEGROUND_RB && member->InBattleGroundQueue()) + return ERR_IN_NON_RANDOM_BG; // check for deserter debuff in case not arena queue if(bgOrTemplate->GetTypeID() != BATTLEGROUND_AA && !member->CanJoinToBattleground()) return ERR_GROUP_JOIN_BATTLEGROUND_DESERTERS; // check if member can join any more battleground queues if(!member->HasFreeBattleGroundQueueId()) return ERR_BATTLEGROUND_TOO_MANY_QUEUES; // not blizz-like + + ++allowedPlayerCount; } + + if(bgOrTemplate->GetTypeID() == BATTLEGROUND_AA) + if(allowedPlayerCount < MinPlayerCount || allowedPlayerCount > MaxPlayerCount) + return ERR_ARENA_TEAM_PARTY_SIZE; + return GroupJoinBattlegroundResult(bgOrTemplate->GetTypeID()); } diff --git a/src/game/GuardAI.cpp b/src/game/GuardAI.cpp index 7315097..7c3d9d4 100644 --- a/src/game/GuardAI.cpp +++ b/src/game/GuardAI.cpp @@ -50,7 +50,6 @@ void GuardAI::MoveInLineOfSight(Unit *u) { //Need add code to let guard support player AttackStart(u); - u->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); } } } diff --git a/src/game/Item.cpp b/src/game/Item.cpp index 3548a30..5ce9860 100644 --- a/src/game/Item.cpp +++ b/src/game/Item.cpp @@ -715,6 +715,9 @@ bool Item::CanBeTraded(bool mail) const if (m_lootGenerated) return false; + if(!mail && IsBoundAccountWide()) // Dirty hack, because trade window is closing + return false; + if ((!mail || !IsBoundAccountWide()) && IsSoulBound()) return false; diff --git a/src/game/Language.h b/src/game/Language.h index 9ddab47..af25b94 100644 --- a/src/game/Language.h +++ b/src/game/Language.h @@ -861,10 +861,14 @@ enum MangosStrings // FREE IDS 1300-9999 + // Broadcaster + LANG_AUTO_BROADCAST = 1300, + // Use for not-in-offcial-sources patches // 10000-10999 // Use for custom patches 11000-11999 + LANG_ANNOUNCE_CHEAT = 11000, // NOT RESERVED IDS 12000-1999999999 // `db_script_string` table index 2000000000-2000009999 (MIN_DB_SCRIPT_STRING_ID-MAX_DB_SCRIPT_STRING_ID) diff --git a/src/game/Level1.cpp b/src/game/Level1.cpp index 3a05e7b..6aa6c45 100644 --- a/src/game/Level1.cpp +++ b/src/game/Level1.cpp @@ -1318,7 +1318,7 @@ bool ChatHandler::HandleModifyScaleCommand(const char* args) return false; float Scale = (float)atof((char*)args); - if (Scale > 10.0f || Scale <= 0.0f) + if (Scale > 15.0f || Scale <= 0.0f) { SendSysMessage(LANG_BAD_VALUE); SetSentErrorMessage(true); diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp index aca69b2..4a63b23 100644 --- a/src/game/Level3.cpp +++ b/src/game/Level3.cpp @@ -168,6 +168,7 @@ bool ChatHandler::HandleReloadAllSpellCommand(const char*) HandleReloadSpellTargetPositionCommand("a"); HandleReloadSpellThreatsCommand("a"); HandleReloadSpellPetAurasCommand("a"); + HandleReloadSpellDisabledCommand("a"); return true; } @@ -907,6 +908,17 @@ bool ChatHandler::HandleReloadMailLevelRewardCommand(const char* /*arg*/) return true; } +bool ChatHandler::HandleReloadSpellDisabledCommand(const char* /*arg*/) +{ + sLog.outString( "Re-Loading spell disabled table..."); + + sObjectMgr.LoadSpellDisabledEntrys(); + + SendGlobalSysMessage("DB table `spell_disabled` reloaded."); + + return true; +} + bool ChatHandler::HandleLoadScriptsCommand(const char* args) { if(!LoadScriptingModule(args)) return true; @@ -5498,9 +5510,15 @@ bool ChatHandler::HandleGMFlyCommand(const char* args) WorldPacket data(12); if (strncmp(args, "on", 3) == 0) + { data.SetOpcode(SMSG_MOVE_SET_CAN_FLY); + ((Player*)(target))->SetCanFly(true); + } else if (strncmp(args, "off", 4) == 0) + { data.SetOpcode(SMSG_MOVE_UNSET_CAN_FLY); + ((Player*)(target))->SetCanFly(false); + } else { SendSysMessage(LANG_USE_BOL); diff --git a/src/game/Mail.cpp b/src/game/Mail.cpp index 7ace9d0..7b035a2 100644 --- a/src/game/Mail.cpp +++ b/src/game/Mail.cpp @@ -201,7 +201,7 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data ) return; } - if (item->IsBoundAccountWide() && item->IsSoulBound() && pl->GetSession()->GetAccountId() != rc_account) + if (item->IsBoundAccountWide() /*&& item->IsSoulBound() */&& pl->GetSession()->GetAccountId() != rc_account) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_ARTEFACTS_ONLY_FOR_OWN_CHARACTERS); return; diff --git a/src/game/Makefile.am b/src/game/Makefile.am index 1008833..b3f332f 100644 --- a/src/game/Makefile.am +++ b/src/game/Makefile.am @@ -91,6 +91,10 @@ libmangosgame_a_SOURCES = \ Chat.cpp \ Chat.h \ ChatHandler.cpp \ + ChatLexicsCutter.cpp \ + ChatLexicsCutter.h \ + ChatLog.cpp \ + ChatLog.h \ CombatHandler.cpp \ ConfusedMovementGenerator.cpp \ ConfusedMovementGenerator.h \ diff --git a/src/game/Map.cpp b/src/game/Map.cpp index ea76558..eb65e18 100644 --- a/src/game/Map.cpp +++ b/src/game/Map.cpp @@ -1439,7 +1439,8 @@ void Map::AddObjectToRemoveList(WorldObject *obj) { ASSERT(obj->GetMapId()==GetId() && obj->GetInstanceId()==GetInstanceId()); - obj->CleanupsBeforeDelete(); // remove or simplify at least cross referenced links + // need clean references at end of update cycle, NOT during it! called at Map::Remove + //obj->CleanupsBeforeDelete(); // remove or simplify at least cross referenced links i_objectsToRemove.insert(obj); //DEBUG_LOG("Object (GUID: %u TypeId: %u ) added to removing list.",obj->GetGUIDLow(),obj->GetTypeId()); @@ -2969,6 +2970,28 @@ void Map::SendObjectUpdates() } } +bool Map::IsNextZcoordOK(float x, float y, float oldZ, float maxDiff) const +{ + // The fastest way to get an accurate result 90% of the time. + // Better result can be obtained like 99% accuracy with a ray light, but the cost is too high and the code is too long. + maxDiff = maxDiff >= 100.0f ? 10.0f : sqrtf(maxDiff); + bool useVmaps = false; + if( GetHeight(x, y, oldZ, false) < GetHeight(x, y, oldZ, true) ) // check use of vmaps + useVmaps = true; + + float newZ = GetHeight(x, y, oldZ+maxDiff-2.0f, useVmaps); + + if (fabs(newZ-oldZ) > maxDiff) // bad... + { + useVmaps = !useVmaps; // try change vmap use + newZ = GetHeight(x, y, oldZ+maxDiff-2.0f, useVmaps); + + if (fabs(newZ-oldZ) > maxDiff) + return false; + } + return true; +} + uint32 Map::GenerateLocalLowGuid(HighGuid guidhigh) { // TODO: for map local guid counters possible force reload map instead shutdown server at guid counter overflow diff --git a/src/game/Map.h b/src/game/Map.h index 6d55351..a2f693f 100644 --- a/src/game/Map.h +++ b/src/game/Map.h @@ -211,6 +211,7 @@ class MANGOS_DLL_SPEC Map : public GridRefManager, public MaNGOS::Obj 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 IsNextZcoordOK(float x, float y, float oldZ, float maxDiff = 5.0f) const; InstanceSave* GetInstanceSave() const { return m_instanceSave; } diff --git a/src/game/MiscHandler.cpp b/src/game/MiscHandler.cpp index 6e4f43c..2499d91 100644 --- a/src/game/MiscHandler.cpp +++ b/src/game/MiscHandler.cpp @@ -1390,6 +1390,14 @@ void WorldSession::HandleSetDungeonDifficultyOpcode( WorldPacket & recv_data ) { if(pGroup->IsLeader(_player->GetGUID())) { + //do not let set dungeon difficulty if any one in this group in dungeon + Group::MemberSlotList g_members = pGroup->GetMemberSlots(); + for (Group::member_citerator itr = g_members.begin(); itr != g_members.end(); itr++) + { + Player *gm_member = sObjectMgr.GetPlayer(itr->guid); + if (gm_member && gm_member->GetMap() && gm_member->GetMap()->IsDungeon()) + return; + } // the difficulty is set even if the instances can't be reset //_player->SendDungeonDifficulty(true); pGroup->ResetInstances(INSTANCE_RESET_CHANGE_DIFFICULTY, false, _player); @@ -1434,6 +1442,14 @@ void WorldSession::HandleSetRaidDifficultyOpcode( WorldPacket & recv_data ) { if(pGroup->IsLeader(_player->GetGUID())) { + //do not let set dungeon difficulty if any one in this group in dungeon + Group::MemberSlotList g_members = pGroup->GetMemberSlots(); + for (Group::member_citerator itr = g_members.begin(); itr != g_members.end(); itr++) + { + Player *gm_member = sObjectMgr.GetPlayer(itr->guid); + if (gm_member && gm_member->GetMap() && gm_member->GetMap()->IsDungeon()) + return; + } // the difficulty is set even if the instances can't be reset //_player->SendDungeonDifficulty(true); pGroup->ResetInstances(INSTANCE_RESET_CHANGE_DIFFICULTY, true, _player); diff --git a/src/game/MovementHandler.cpp b/src/game/MovementHandler.cpp index b02645f..26dfea4 100644 --- a/src/game/MovementHandler.cpp +++ b/src/game/MovementHandler.cpp @@ -31,6 +31,183 @@ #include "WaypointMovementGenerator.h" #include "InstanceSaveMgr.h" #include "ObjectMgr.h" +#include "World.h" +#include "Language.h" + +//#define __ANTI_DEBUG__ + +#ifdef __ANTI_DEBUG__ +#include "Chat.h" +std::string FlagsToStr(const uint32 Flags) +{ + std::string Ret=""; + if(Flags==0) + { + Ret="None"; + return Ret; + } + + if(Flags & MOVEMENTFLAG_FORWARD) + { Ret+="FW "; } + if(Flags & MOVEMENTFLAG_BACKWARD) + { Ret+="BW "; } + if(Flags & MOVEMENTFLAG_STRAFE_LEFT) + { Ret+="STL "; } + if(Flags & MOVEMENTFLAG_STRAFE_RIGHT) + { Ret+="STR "; } + if(Flags & MOVEMENTFLAG_LEFT) + { Ret+="LF "; } + if(Flags & MOVEMENTFLAG_RIGHT) + { Ret+="RI "; } + if(Flags & MOVEMENTFLAG_PITCH_UP) + { Ret+="PTUP "; } + if(Flags & MOVEMENTFLAG_PITCH_DOWN) + { Ret+="PTDW "; } + if(Flags & MOVEMENTFLAG_WALK_MODE) + { Ret+="WALK "; } + if(Flags & MOVEMENTFLAG_ONTRANSPORT) + { Ret+="TRANS "; } + if(Flags & MOVEMENTFLAG_LEVITATING) + { Ret+="LEVI "; } + if(Flags & MOVEMENTFLAG_FLY_UNK1) + { Ret+="FLYUNK1 "; } + if(Flags & MOVEMENTFLAG_JUMPING) + { Ret+="JUMP "; } + if(Flags & MOVEMENTFLAG_UNK4) + { Ret+="UNK4 "; } + if(Flags & MOVEMENTFLAG_FALLING) + { Ret+="FALL "; } + if(Flags & MOVEMENTFLAG_SWIMMING) + { Ret+="SWIM "; } + if(Flags & MOVEMENTFLAG_FLY_UP) + { Ret+="FLYUP "; } + if(Flags & MOVEMENTFLAG_CAN_FLY) + { Ret+="CFLY "; } + if(Flags & MOVEMENTFLAG_FLYING) + { Ret+="FLY "; } + if(Flags & MOVEMENTFLAG_FLYING2) + { Ret+="FLY2 "; } + if(Flags & MOVEMENTFLAG_WATERWALKING) + { Ret+="WTWALK "; } + if(Flags & MOVEMENTFLAG_SAFE_FALL) + { Ret+="SAFE "; } + if(Flags & MOVEMENTFLAG_UNK3) + { Ret+="UNK3 "; } + if(Flags & MOVEMENTFLAG_SPLINE) + { Ret+="SPLINE "; } + if(Flags & MOVEMENTFLAG_SPLINE2) + { Ret+="SPLINE2 "; } + + return Ret; +} +#endif // __ANTI_DEBUG__ + +bool WorldSession::Anti__ReportCheat(const char* Reason,float Speed,const char* Op,float Val1,uint32 Val2) +{ + if(!Reason) + { + sLog.outError("Anti__ReportCheat: Missing Reason parameter!"); + return false; + } + const char* Player=GetPlayer()->GetName(); + uint32 Acc=GetPlayer()->GetSession()->GetAccountId(); + uint32 Map=GetPlayer()->GetMapId(); + if(!Player) + { + sLog.outError("Anti__ReportCheat: Player with no name?!?"); + return false; + } + + QueryResult *Res=CharacterDatabase.PQuery("SELECT speed,Val1 FROM cheaters WHERE player='%s' AND reason LIKE '%s' AND Map='%u' AND last_date >= NOW()-300",Player,Reason,Map); + if(Res) + { + Field* Fields = Res->Fetch(); + + std::stringstream Query; + Query << "UPDATE cheaters SET count=count+1,last_date=NOW()"; + Query.precision(5); + if(Speed>0.0f && Speed > Fields[0].GetFloat()) + { + Query << ",speed='"; + Query << std::fixed << Speed; + Query << "'"; + } + + if(Val1>0.0f && Val1 > Fields[1].GetFloat()) + { + Query << ",Val1='"; + Query << std::fixed << Val1; + Query << "'"; + } + + Query << " WHERE player='" << Player << "' AND reason='" << Reason << "' AND Map='" << Map << "' AND last_date >= NOW()-300 ORDER BY entry DESC LIMIT 1"; + + CharacterDatabase.Execute(Query.str().c_str()); + delete Res; + } + else + { + if(!Op) + { Op=""; } + std::stringstream Pos; + Pos << "OldPos: " << GetPlayer()->GetPositionX() << " " << GetPlayer()->GetPositionY() << " " + << GetPlayer()->GetPositionZ(); + CharacterDatabase.PExecute("INSERT INTO cheaters (player,acctid,reason,speed,count,first_date,last_date,`Op`,Val1,Val2,Map,Pos,Level) " + "VALUES ('%s','%u','%s','%f','1',NOW(),NOW(),'%s','%f','%u','%u','%s','%u')", + Player,Acc,Reason,Speed,Op,Val1,Val2,Map, + Pos.str().c_str(),GetPlayer()->getLevel()); + } + + if(sWorld.GetMvAnticheatKill() && GetPlayer()->isAlive()) + { + GetPlayer()->DealDamage(GetPlayer(), GetPlayer()->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + if(sWorld.GetMvAnticheatKick()) + { + GetPlayer()->GetSession()->KickPlayer(); + } + if(sWorld.GetMvAnticheatBan() & 1) + { + sWorld.BanAccount(BAN_CHARACTER,Player,sWorld.GetMvAnticheatBanTime(),"Cheat","Anticheat"); + } + if(sWorld.GetMvAnticheatBan() & 2) + { + QueryResult *result = LoginDatabase.PQuery("SELECT last_ip FROM account WHERE id=%u", Acc); + if(result) + { + + Field *fields = result->Fetch(); + std::string LastIP = fields[0].GetCppString(); + if(!LastIP.empty()) + { + sWorld.BanAccount(BAN_IP,LastIP,sWorld.GetMvAnticheatBanTime(),"Cheat","Anticheat"); + } + delete result; + } + } + return true; +} + +bool WorldSession::Anti__CheatOccurred(uint32 CurTime,const char* Reason,float Speed,const char* Op, float Val1,uint32 Val2) +{ + if(!Reason) + { + sLog.outError("Anti__CheatOccurred: Missing Reason parameter!"); + return false; + } + + GetPlayer()->m_anti_lastalarmtime = CurTime; + GetPlayer()->m_anti_alarmcount = GetPlayer()->m_anti_alarmcount + 1; + + if (GetPlayer()->m_anti_alarmcount > sWorld.GetMvAnticheatAlarmCount()) + { + Anti__ReportCheat(Reason,Speed,Op,Val1,Val2); + if (sWorld.GetMvAnticheatAnnounce()) + sWorld.SendWorldText(LANG_ANNOUNCE_CHEAT, GetPlayer()->GetName(), Reason); + return true; + } + return false; +} void WorldSession::HandleMoveWorldportAckOpcode( WorldPacket & /*recv_data*/ ) { @@ -72,6 +249,7 @@ void WorldSession::HandleMoveWorldportAckOpcode() // relocate the player to the teleport destination GetPlayer()->SetMap(sMapMgr.CreateMap(loc.mapid, GetPlayer())); GetPlayer()->Relocate(loc.coord_x, loc.coord_y, loc.coord_z, loc.orientation); + GetPlayer()->m_anti_TeleTime=time(NULL); GetPlayer()->SendInitialPacketsBeforeAddToMap(); // the CanEnter checks are done in TeleporTo but conditions may change @@ -163,6 +341,8 @@ void WorldSession::HandleMoveWorldportAckOpcode() // resummon pet GetPlayer()->ResummonPetTemporaryUnSummonedIfAny(); + GetPlayer()->Anti__SetLastTeleTime(::time(NULL)); + GetPlayer()->m_anti_BeginFallZ=INVALID_HEIGHT; //lets process all delayed operations on successful teleport GetPlayer()->ProcessDelayedOperations(); @@ -212,6 +392,11 @@ void WorldSession::HandleMoveTeleportAck(WorldPacket& recv_data) // resummon pet GetPlayer()->ResummonPetTemporaryUnSummonedIfAny(); + if(plMover) + { + plMover->Anti__SetLastTeleTime(::time(NULL)); + plMover->m_anti_BeginFallZ=INVALID_HEIGHT; + } //lets process all delayed operations on successful teleport GetPlayer()->ProcessDelayedOperations(); @@ -268,6 +453,11 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) // if we boarded a transport, add us to it if (plMover && !plMover->m_transport) { + float trans_rad = movementInfo.GetTransportPos()->x*movementInfo.GetTransportPos()->x + movementInfo.GetTransportPos()->y*movementInfo.GetTransportPos()->y + movementInfo.GetTransportPos()->z*movementInfo.GetTransportPos()->z; + if (trans_rad > 3600.0f) // transport radius = 60 yards //cheater with on_transport_flag + { + return; + } // elevators also cause the client to send MOVEFLAG_ONTRANSPORT - just unmount if the guid can be found in the transport list for (MapManager::TransportSet::const_iterator iter = sMapMgr.m_Transports.begin(); iter != sMapMgr.m_Transports.end(); ++iter) { @@ -295,9 +485,160 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) { // now client not include swimming flag in case jumping under water plMover->SetInWater( !plMover->IsInWater() || plMover->GetBaseMap()->IsUnderWater(movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z) ); + if(plMover->GetBaseMap()->IsUnderWater(movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z-7.0f)) + { + plMover->m_anti_BeginFallZ=INVALID_HEIGHT; + } } - /*----------------------*/ + // ---- anti-cheat features -->>> + uint32 Anti_TeleTimeDiff=plMover ? time(NULL) - plMover->Anti__GetLastTeleTime() : time(NULL); + static const uint32 Anti_TeleTimeIgnoreDiff=sWorld.GetMvAnticheatIgnoreAfterTeleport(); + if (plMover && (plMover->m_transport == 0) && sWorld.GetMvAnticheatEnable() && + GetPlayer()->GetSession()->GetSecurity() <= sWorld.GetMvAnticheatGmLevel() && + GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType()!=FLIGHT_MOTION_TYPE && + Anti_TeleTimeDiff>Anti_TeleTimeIgnoreDiff) + { + const uint32 CurTime=getMSTime(); + if (getMSTimeDiff(GetPlayer()->m_anti_lastalarmtime,CurTime) > sWorld.GetMvAnticheatAlarmPeriod()) + { + GetPlayer()->m_anti_alarmcount = 0; + } + /* I really don't care about movement-type yet (todo) + UnitMoveType move_type; + + if (movementInfo.flags & MOVEMENTFLAG_FLYING) move_type = MOVE_FLY; + else if (movementInfo.flags & MOVEMENTFLAG_SWIMMING) move_type = MOVE_SWIM; + else if (movementInfo.flags & MOVEMENTFLAG_WALK_MODE) move_type = MOVE_WALK; + else move_type = MOVE_RUN;*/ + + float delta_x = GetPlayer()->GetPositionX() - movementInfo.GetPos()->x; + float delta_y = GetPlayer()->GetPositionY() - movementInfo.GetPos()->y; + float delta_z = GetPlayer()->GetPositionZ() - movementInfo.GetPos()->z; + float delta = sqrt(delta_x * delta_x + delta_y * delta_y); // Len of movement-vector via Pythagoras (a^2+b^2=Len^2) + float tg_z = 0.0f; //tangens + float delta_t = getMSTimeDiff(GetPlayer()->m_anti_lastmovetime,CurTime); + + GetPlayer()->m_anti_lastmovetime = CurTime; + GetPlayer()->m_anti_MovedLen += delta; + + if (delta_t > 15000.0f) + { delta_t = 15000.0f; } + + // Tangens of walking angel + if (!(movementInfo.GetMovementFlags() & (MOVEFLAG_FLYING | MOVEFLAG_SWIMMING))) + { + tg_z = ((delta !=0.0f) && (delta_z > 0.0f)) ? (atan((delta_z*delta_z) / delta) * 180.0f / M_PI) : 0.0f; + } + + //antiOFF fall-damage, MOVEMENTFLAG_UNK4 seted by client if player try movement when falling and unset in this case the MOVEMENTFLAG_FALLING flag. + if ((!GetPlayer()->CanFly() && GetPlayer()->m_anti_BeginFallZ == INVALID_HEIGHT) && + (movementInfo.GetMovementFlags() & (MOVEFLAG_FALLING | MOVEFLAG_FALLINGFAR)) != 0) + { + GetPlayer()->m_anti_BeginFallZ=(float)(movementInfo.GetPos()->z); + } + + if (GetPlayer()->m_anti_NextLenCheck <= CurTime) + { + // Check every 500ms is a lot more advisable then 1000ms, because normal movment packet arrives every 500ms + uint32 OldNextLenCheck=GetPlayer()->m_anti_NextLenCheck; + float delta_xyt=GetPlayer()->m_anti_MovedLen/(float)(getMSTimeDiff(OldNextLenCheck-500,CurTime)); + GetPlayer()->m_anti_NextLenCheck = CurTime+500; + GetPlayer()->m_anti_MovedLen = 0.0f; + static const float MaxDeltaXYT = sWorld.GetMvAnticheatMaxXYT(); + + if (delta_xyt > MaxDeltaXYT && delta<=100.0f && GetPlayer()->GetZoneId() != 2257) + { + if (sWorld.GetMvAnticheatSpeedCheck()) + Anti__CheatOccurred(CurTime,"Speed hack",delta_xyt,LookupOpcodeName(opcode), + (float)(GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType()), + (float)(getMSTimeDiff(OldNextLenCheck-500,CurTime))); + } + } + + if (delta > 100.0f && GetPlayer()->GetZoneId() != 2257) + { + if (sWorld.GetMvAnticheatTeleportCheck()) + Anti__ReportCheat("Tele hack",delta,LookupOpcodeName(opcode)); + } + + // Check for waterwalking . Fix new way of checking for waterwalking by Darky88 + if (movementInfo.HasMovementFlag(MOVEFLAG_WATERWALKING) && + !(GetPlayer()->HasAuraType(SPELL_AURA_WATER_WALK) || GetPlayer()->HasAuraType(SPELL_AURA_GHOST))) + { + if(sWorld.GetMvAnticheatWaterCheck()) + Anti__CheatOccurred(CurTime,"Water walking",0.0f,NULL,0.0f,(uint32)(movementInfo.GetMovementFlags())); + } + + // Check for walking upwards a mountain while not beeing able to do that, New check by Darky88 + if ((delta_z < -2.3f) && (tg_z > 2.37f)) + { + if (sWorld.GetMvAnticheatMountainCheck()) + Anti__CheatOccurred(CurTime,"Mountain hack",tg_z,NULL,delta,delta_z); + } + + static const float DIFF_OVERGROUND = 10.0f; + float Anti__GroundZ = GetPlayer()->GetMap()->GetHeight(GetPlayer()->GetPositionX(),GetPlayer()->GetPositionY(),MAX_HEIGHT); + float Anti__FloorZ = GetPlayer()->GetMap()->GetHeight(GetPlayer()->GetPositionX(),GetPlayer()->GetPositionY(),GetPlayer()->GetPositionZ()); + float Anti__MapZ = ((Anti__FloorZ <= (INVALID_HEIGHT+5.0f)) ? Anti__GroundZ : Anti__FloorZ) + DIFF_OVERGROUND; + + if (!GetPlayer()->CanFly() && + !GetPlayer()->GetBaseMap()->IsUnderWater(movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z-7.0f) && + Anti__MapZ < GetPlayer()->GetPositionZ() && Anti__MapZ > (INVALID_HEIGHT+DIFF_OVERGROUND + 5.0f)) + { + static const float DIFF_AIRJUMP=25.0f; // 25 is realy high, but to many false positives... + + // Air-Jump-Detection definitively needs a better way to be detected... + if ((movementInfo.GetMovementFlags() & (MOVEFLAG_CAN_FLY | MOVEFLAG_FLYING | MOVEFLAG_ROOT)) != 0) // Fly Hack + { + // Fix Aura 55164 + if (!GetPlayer()->HasAura(55164) || !GetPlayer()->HasAuraType(SPELL_AURA_FEATHER_FALL)) + if (sWorld.GetMvAnticheatFlyCheck()) + Anti__CheatOccurred(CurTime,"Fly hack", + ((uint8)(GetPlayer()->HasAuraType(SPELL_AURA_FLY))) + + ((uint8)(GetPlayer()->HasAuraType(SPELL_AURA_MOD_FLIGHT_SPEED_MOUNTED))*2), + NULL,GetPlayer()->GetPositionZ()-Anti__MapZ); + } + + // Need a better way to do that - currently a lot of fake alarms + else if ((Anti__MapZ+DIFF_AIRJUMP < GetPlayer()->GetPositionZ() && + (movementInfo.GetMovementFlags() & (MOVEFLAG_FALLINGFAR | MOVEFLAG_PENDINGSTOP))==0) || + (Anti__MapZ < GetPlayer()->GetPositionZ() && opcode==MSG_MOVE_JUMP) && + !GetPlayer()->HasAuraType(SPELL_AURA_FEATHER_FALL)) + { + if (sWorld.GetMvAnticheatJumpCheck()) + Anti__CheatOccurred(CurTime,"Possible Air Jump Hack",0.0f,LookupOpcodeName(opcode),0.0f,movementInfo.GetMovementFlags()); + } + } + + /*if(Anti__FloorZ < -199900.0f && Anti__GroundZ >= -199900.0f && + GetPlayer()->GetPositionZ()+5.0f < Anti__GroundZ) + { + Anti__CheatOccurred(CurTime,"Teleport2Plane hack", + GetPlayer()->GetPositionZ(),NULL,Anti__GroundZ); + }*/ + + //Teleport To Plane checks + if (movementInfo.GetPos()->z < 0.0001f && movementInfo.GetPos()->z > -0.0001f && (!movementInfo.HasMovementFlag(MovementFlags(MOVEFLAG_SWIMMING | MOVEFLAG_CAN_FLY | MOVEFLAG_FLYING)))) + { + if(sWorld.GetMvAnticheatTeleport2PlaneCheck()) + { + // Prevent using TeleportToPlan. + Map *map = GetPlayer()->GetMap(); + if (map) + { + float plane_z = map->GetHeight(movementInfo.GetPos()->x, movementInfo.GetPos()->y, MAX_HEIGHT) - movementInfo.GetPos()->z; + plane_z = (plane_z < -500.0f) ? 0 : plane_z; //check holes in heigth map + if(plane_z > 0.1f || plane_z < -0.1f) + { + if(sWorld.GetMvAnticheatTeleport2PlaneCheck()) + Anti__CheatOccurred(CurTime,"Teleport2Plane hack",GetPlayer()->GetPositionZ(),NULL,plane_z); + } + } + } + } + } + // <<---- anti-cheat features /* process position-change */ movementInfo.UpdateTime(getMSTime()); @@ -437,12 +778,10 @@ void WorldSession::HandleSetActiveMoverOpcode(WorldPacket &recv_data) ObjectGuid guid; recv_data >> guid; - if(_player->GetMover()->GetObjectGuid() != guid) - { - sLog.outError("HandleSetActiveMoverOpcode: incorrect mover guid: mover is %s and should be %s", - _player->GetMover()->GetObjectGuid().GetString().c_str(), guid.GetString().c_str()); - return; - } + if (Unit *pMover = ObjectAccessor::GetUnit(*GetPlayer(), guid)) + GetPlayer()->SetMover(pMover); + else + GetPlayer()->SetMover(NULL); } void WorldSession::HandleMoveNotActiveMover(WorldPacket &recv_data) diff --git a/src/game/Object.cpp b/src/game/Object.cpp index 056db1a..e59e3df 100644 --- a/src/game/Object.cpp +++ b/src/game/Object.cpp @@ -546,7 +546,7 @@ void Object::BuildValuesUpdate(uint8 updatetype, ByteBuffer * data, UpdateMask * bool IsPerCasterAuraState = false; if (updatetype == UPDATETYPE_CREATE_OBJECT || updatetype == UPDATETYPE_CREATE_OBJECT2) { - if (isType(TYPEMASK_GAMEOBJECT) && !((GameObject*)this)->IsTransport()) + if (isType(TYPEMASK_GAMEOBJECT) && !((GameObject*)this)->IsDynTransport()) { if ( ((GameObject*)this)->ActivateToQuest(target) || target->isGameMaster()) IsActivateToQuest = true; @@ -564,7 +564,7 @@ void Object::BuildValuesUpdate(uint8 updatetype, ByteBuffer * data, UpdateMask * } else // case UPDATETYPE_VALUES { - if (isType(TYPEMASK_GAMEOBJECT) && !((GameObject*)this)->IsTransport()) + if (isType(TYPEMASK_GAMEOBJECT) && !((GameObject*)this)->IsDynTransport()) { if ( ((GameObject*)this)->ActivateToQuest(target) || target->isGameMaster()) { @@ -1247,8 +1247,32 @@ bool WorldObject::IsWithinLOS(float ox, float oy, float oz) const { float x,y,z; GetPosition(x,y,z); + z += 2.0f; + oz += 2.0f; + + // check for line of sight because of terrain height differences + Map const *map = GetBaseMap(); + float dx = ox - x, dy = oy - y, dz = oz - z; + float dist = sqrt(dx*dx + dy*dy + dz*dz); + if (dist > ATTACK_DISTANCE && dist < MAX_VISIBILITY_DISTANCE) + { + uint32 steps = uint32(dist / TERRAIN_LOS_STEP_DISTANCE); + float step_dist = dist / (float)steps; // to make sampling intervals symmetric in both directions + float inc_factor = step_dist / dist; + float incx = dx*inc_factor, incy = dy*inc_factor, incz = dz*inc_factor; + float px = x, py = y, pz = z; + for (; steps; --steps) + { + if (map->GetHeight(px, py, pz, false) > pz) + return false; // found intersection with ground + px += incx; + py += incy; + pz += incz; + } + } + VMAP::IVMapManager *vMapManager = VMAP::VMapFactory::createOrGetVMapManager(); - return vMapManager->isInLineOfSight(GetMapId(), x, y, z+2.0f, ox, oy, oz+2.0f); + return vMapManager->isInLineOfSight(GetMapId(), x, y, z, ox, oy, oz); } bool WorldObject::GetDistanceOrder(WorldObject const* obj1, WorldObject const* obj2, bool is3D /* = true */) const @@ -1424,11 +1448,23 @@ void WorldObject::GetRandomPoint( float x, float y, float z, float distance, flo UpdateGroundPositionZ(rand_x,rand_y,rand_z); // update to LOS height if available } -void WorldObject::UpdateGroundPositionZ(float x, float y, float &z) const +void WorldObject::UpdateGroundPositionZ(float x, float y, float &z, float maxDiff) const { - float new_z = GetBaseMap()->GetHeight(x,y,z,true); - if(new_z > INVALID_HEIGHT) - z = new_z+ 0.05f; // just to be sure that we are not a few pixel under the surface + maxDiff = maxDiff >= 100.0f ? 10.0f : sqrtf(maxDiff); + bool useVmaps = false; + if( GetBaseMap()->GetHeight(x, y, z, false) < GetBaseMap()->GetHeight(x, y, z, true) ) // check use of vmaps + useVmaps = true; + + float normalizedZ = GetBaseMap()->GetHeight(x, y, z, useVmaps); + // check if its reacheable + if(normalizedZ <= INVALID_HEIGHT || fabs(normalizedZ-z) > maxDiff) + { + useVmaps = !useVmaps; // try change vmap use + normalizedZ = GetBaseMap()->GetHeight(x, y, z, useVmaps); + if(normalizedZ <= INVALID_HEIGHT || fabs(normalizedZ-z) > maxDiff) + return; // Do nothing in case of another bad result + } + z = normalizedZ + 0.1f; // just to be sure that we are not a few pixel under the surface } bool WorldObject::IsPositionValid() const @@ -1664,6 +1700,26 @@ Creature* WorldObject::SummonCreature(uint32 id, float x, float y, float z, floa return pCreature; } +GameObject* WorldObject::SummonGameobject(uint32 id, float x, float y, float z, float angle, uint32 despwtime) +{ + GameObject* pGameObj = new GameObject; + + Map *map = GetMap(); + + if(!pGameObj->Create(sObjectMgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), id, map, + GetPhaseMask(), x, y, z, angle, 0.0f, 0.0f, 0.0f, 0.0f, 100, GO_STATE_READY)) + { + delete pGameObj; + return NULL; + } + + pGameObj->SetRespawnTime(despwtime/IN_MILLISECONDS); + + map->Add(pGameObj); + + return pGameObj; +} + namespace MaNGOS { class NearUsedPosDo diff --git a/src/game/Object.h b/src/game/Object.h index c900e4d..bcc8618 100644 --- a/src/game/Object.h +++ b/src/game/Object.h @@ -41,6 +41,7 @@ #define DEFAULT_OBJECT_SCALE 1.0f // player/item scale as default, npc/go from database, pets from dbc #define MAX_STEALTH_DETECT_RANGE 45.0f +#define TERRAIN_LOS_STEP_DISTANCE 3.0f uint32 GuidHigh2TypeId(uint32 guid_hi); @@ -66,6 +67,7 @@ class WorldPacket; class UpdateData; class WorldSession; class Creature; +class GameObject; class Player; class Unit; class Map; @@ -374,7 +376,7 @@ class MANGOS_DLL_SPEC WorldObject : public Object virtual float GetObjectBoundingRadius() const { return DEFAULT_WORLD_OBJECT_SIZE; } bool IsPositionValid() const; - void UpdateGroundPositionZ(float x, float y, float &z) const; + void UpdateGroundPositionZ(float x, float y, float &z, float maxDiff = 30.0f) const; void GetRandomPoint( float x, float y, float z, float distance, float &rand_x, float &rand_y, float &rand_z ) const; @@ -486,6 +488,7 @@ class MANGOS_DLL_SPEC WorldObject : public Object void BuildUpdateData(UpdateDataMapType &); Creature* SummonCreature(uint32 id, float x, float y, float z, float ang,TempSummonType spwtype,uint32 despwtime); + GameObject* SummonGameobject(uint32 id, float x, float y, float z, float angle, uint32 despwtime); bool isActiveObject() const { return m_isActiveObject || m_viewPoint.hasViewers(); } diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index 2e5f2af..e7b6636 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -6545,8 +6545,8 @@ void ObjectMgr::LoadReputationOnKill() // 0 1 2 QueryResult *result = WorldDatabase.Query("SELECT creature_id, RewOnKillRepFaction1, RewOnKillRepFaction2," - // 3 4 5 6 7 8 9 - "IsTeamAward1, MaxStanding1, RewOnKillRepValue1, IsTeamAward2, MaxStanding2, RewOnKillRepValue2, TeamDependent " + // 3 4 5 6 7 8 9 10 + "IsTeamAward1, MaxStanding1, RewOnKillRepValue1, IsTeamAward2, MaxStanding2, RewOnKillRepValue2, TeamDependent, ChampioningAura " "FROM creature_onkill_reputation"); if(!result) @@ -6579,6 +6579,7 @@ void ObjectMgr::LoadReputationOnKill() repOnKill.reputation_max_cap2 = fields[7].GetUInt32(); repOnKill.repvalue2 = fields[8].GetInt32(); repOnKill.team_dependent = fields[9].GetUInt8(); + repOnKill.championingAura = fields[10].GetUInt32(); if(!GetCreatureTemplate(creature_id)) { @@ -7618,6 +7619,46 @@ const char *ObjectMgr::GetMangosString(int32 entry, int locale_idx) const return ""; } +void ObjectMgr::LoadSpellDisabledEntrys() +{ + m_spell_disabled.clear(); // need for reload case + QueryResult *result = WorldDatabase.Query("SELECT entry, ischeat_spell FROM spell_disabled where active=1"); + + uint32 total_count = 0; + uint32 cheat_spell_count=0; + + if( !result ) + { + barGoLink bar( 1 ); + bar.step(); + + sLog.outString(); + sLog.outString( ">> Loaded %u disabled spells", total_count ); + return; + } + + barGoLink bar( result->GetRowCount() ); + + Field* fields; + do + { + bar.step(); + fields = result->Fetch(); + uint32 spellid = fields[0].GetUInt32(); + bool ischeater = fields[1].GetBool(); + m_spell_disabled[spellid] = ischeater; + ++total_count; + if(ischeater) + ++cheat_spell_count; + + } while ( result->NextRow() ); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u disabled spells ( %u - is cheaters spells)", total_count, cheat_spell_count); +} + void ObjectMgr::LoadFishingBaseSkillLevel() { mFishingBaseForArea.clear(); // for reload case @@ -7686,8 +7727,14 @@ uint16 ObjectMgr::GetConditionId( ConditionType condition, uint32 value1, uint32 return mConditions.size() - 1; } -bool ObjectMgr::CheckDeclinedNames( std::wstring mainpart, DeclinedName const& names ) +bool ObjectMgr::CheckDeclinedNames( std::wstring w_ownname, DeclinedName const& names ) { + // get main part of the name + std::wstring mainpart = GetMainPartOfName(w_ownname, 0); + // prepare flags + bool x = true; + bool y = true; + // check declined names for(int i =0; i < MAX_DECLINED_NAME_CASES; ++i) { std::wstring wname; @@ -7695,9 +7742,12 @@ bool ObjectMgr::CheckDeclinedNames( std::wstring mainpart, DeclinedName const& n return false; if(mainpart!=GetMainPartOfName(wname,i+1)) - return false; + x = false; + + if(w_ownname!=wname) + y = false; } - return true; + return (x||y); } uint32 ObjectMgr::GetAreaTriggerScriptId(uint32 trigger_id) diff --git a/src/game/ObjectMgr.h b/src/game/ObjectMgr.h index 027fa33..37870cd 100644 --- a/src/game/ObjectMgr.h +++ b/src/game/ObjectMgr.h @@ -256,6 +256,7 @@ struct ReputationOnKillEntry uint32 reputation_max_cap2; int32 repvalue2; bool team_dependent; + uint32 championingAura; }; struct RepSpilloverTemplate @@ -934,7 +935,21 @@ class ObjectMgr static PetNameInvalidReason CheckPetName( const std::string& name ); static bool IsValidCharterName( const std::string& name ); - static bool CheckDeclinedNames(std::wstring mainpart, DeclinedName const& names); + static bool CheckDeclinedNames(std::wstring w_ownname, DeclinedName const& names); + + void LoadSpellDisabledEntrys(); + uint8 IsSpellDisabled(uint32 spellid) + { + uint8 result=0; + SpellDisabledMap::const_iterator itr = m_spell_disabled.find(spellid); + if(itr != m_spell_disabled.end()) + { + result=1; + if(itr->second != 0) + result=2; + } + return result; + } int GetIndexForLocale(LocaleConstant loc); LocaleConstant GetLocaleForIndex(int i); @@ -1070,6 +1085,9 @@ class ObjectMgr typedef std::set ReservedNamesMap; ReservedNamesMap m_ReservedNames; + typedef UNORDERED_MAP SpellDisabledMap; + SpellDisabledMap m_spell_disabled; + GraveYardMap mGraveYardMap; GameTeleMap m_GameTeleMap; diff --git a/src/game/Pet.cpp b/src/game/Pet.cpp index fdf93cd..0d6088b 100644 --- a/src/game/Pet.cpp +++ b/src/game/Pet.cpp @@ -335,6 +335,10 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool } } + //set last used pet number (for use in BG's) + if(owner->GetTypeId() == TYPEID_PLAYER && isControlled() && !isTemporarySummoned() && (getPetType() == SUMMON_PET || getPetType() == HUNTER_PET)) + ((Player*)owner)->SetLastPetNumber(pet_number); + m_loading = false; SynchronizeLevelWithOwner(); diff --git a/src/game/PetAI.cpp b/src/game/PetAI.cpp index 3498083..cfdfd21 100644 --- a/src/game/PetAI.cpp +++ b/src/game/PetAI.cpp @@ -62,7 +62,6 @@ void PetAI::MoveInLineOfSight(Unit *u) if(m_creature->IsWithinLOSInMap(u)) { AttackStart(u); - u->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); } } } @@ -167,7 +166,7 @@ void PetAI::UpdateAI(const uint32 diff) return; } // not required to be stopped case - else if (m_creature->isAttackReady() && m_creature->canReachWithAttack(m_creature->getVictim())) + else if (m_creature->isAttackReady() && m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE)) { m_creature->AttackerStateUpdate(m_creature->getVictim()); @@ -358,6 +357,6 @@ void PetAI::AttackedBy(Unit *attacker) { //when attacked, fight back in case 1)no victim already AND 2)not set to passive AND 3)not set to stay, unless can it can reach attacker with melee attack anyway if(!m_creature->getVictim() && m_creature->GetCharmInfo() && !m_creature->GetCharmInfo()->HasReactState(REACT_PASSIVE) && - (!m_creature->GetCharmInfo()->HasCommandState(COMMAND_STAY) || m_creature->canReachWithAttack(attacker))) + (!m_creature->GetCharmInfo()->HasCommandState(COMMAND_STAY) || m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE))) AttackStart(attacker); } diff --git a/src/game/PetHandler.cpp b/src/game/PetHandler.cpp index 57356b3..a92592d 100644 --- a/src/game/PetHandler.cpp +++ b/src/game/PetHandler.cpp @@ -472,7 +472,7 @@ void WorldSession::HandlePetRename( WorldPacket & recv_data ) std::wstring wname; Utf8toWStr(name, wname); - if(!ObjectMgr::CheckDeclinedNames(GetMainPartOfName(wname,0),declinedname)) + if(!ObjectMgr::CheckDeclinedNames(wname, declinedname)) { SendPetNameInvalid(PET_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME, name, &declinedname); return; diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 9faf816..73358c6 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -530,6 +530,7 @@ Player::Player (WorldSession *session): Unit(), m_achievementMgr(this), m_reputa //returning reagents for temporarily removed pets //when dying/logging out m_oldpetspell = 0; + m_lastpetnumber = 0; ////////////////////Rest System///////////////////// time_inn_enter=0; @@ -538,6 +539,17 @@ Player::Player (WorldSession *session): Unit(), m_achievementMgr(this), m_reputa rest_type=REST_TYPE_NO; ////////////////////Rest System///////////////////// + //movement anticheat + m_anti_lastmovetime = 0; //last movement time + m_anti_NextLenCheck = 0; + m_anti_MovedLen = 0.0f; + m_anti_BeginFallZ = INVALID_HEIGHT; + m_anti_lastalarmtime = 0; //last time when alarm generated + m_anti_alarmcount = 0; //alarm counter + m_anti_TeleTime = 0; + m_CanFly=false; + ///////////////////////////////// + m_mailsUpdated = false; unReadMails = 0; m_nextMailDelivereTime = 0; @@ -580,6 +592,8 @@ Player::Player (WorldSession *session): Unit(), m_achievementMgr(this), m_reputa // Honor System m_lastHonorUpdateTime = time(NULL); + m_IsBGRandomWinner = false; + // Player summoning m_summon_expire = 0; m_summon_mapid = 0; @@ -1403,6 +1417,9 @@ void Player::Update( uint32 p_time ) RegenerateAll(); } + if (!isAlive() && !HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) + SetHealth(0); + if (m_deathState == JUST_DIED) KillPlayer(); @@ -2146,6 +2163,22 @@ void Player::Regenerate(Powers power, uint32 diff) cd_diff = cd_diff * ((*i)->GetModifier()->m_amount + 100) / 100; SetRuneCooldown(rune, (cd < cd_diff) ? 0 : cd - cd_diff); + + // check if we don't have cooldown, need convert and that our rune wasn't already converted + if (cd < cd_diff && m_runes->IsRuneNeedsConvert(rune) && GetBaseRune(rune) == GetCurrentRune(rune)) + { + // currently all delayed rune converts happen with rune death + // ConvertedBy was initialized at proc + ConvertRune(rune, RUNE_DEATH); + SetNeedConvertRune(rune, false); + } + } + else if (m_runes->IsRuneNeedsConvert(rune) && GetBaseRune(rune) == GetCurrentRune(rune)) + { + // currently all delayed rune converts happen with rune death + // ConvertedBy was initialized at proc + ConvertRune(rune, RUNE_DEATH); + SetNeedConvertRune(rune, false); } } } break; @@ -4414,7 +4447,8 @@ void Player::BuildPlayerRepop() if(GetCorpse()) { sLog.outError("BuildPlayerRepop: player %s(%d) already has a corpse", GetName(), GetGUIDLow()); - ASSERT(false); + sLog.outError("Removing player %s(%d) corpse from DB", GetName(), GetGUIDLow()); + CharacterDatabase.PExecute("DELETE FROM corpse WHERE player = '%d'",GetGUIDLow()); } // create a corpse and place it at the player's location @@ -4849,7 +4883,7 @@ void Player::RepopAtGraveyard() AreaTableEntry const *zone = GetAreaEntryByAreaID(GetAreaId()); // Such zones are considered unreachable as a ghost and the player must be automatically revived - if ((!isAlive() && zone && zone->flags & AREA_FLAG_NEED_FLY) || GetTransport()) + if ((!isAlive() && zone && zone->flags & AREA_FLAG_NEED_FLY) || GetTransport() || GetPositionZ() < -500.0f) { ResurrectPlayer(0.5f); SpawnCorpseBones(); @@ -6293,11 +6327,28 @@ void Player::RewardReputation(Unit *pVictim, float rate) if(!Rep) return; + uint32 Repfaction1 = Rep->repfaction1; + uint32 Repfaction2 = Rep->repfaction2; + uint32 tabardFactionID = 0; + + // Championning tabard reputation system + if(HasAura(Rep->championingAura)) + { + if( Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_TABARD ) ) + { + if ( tabardFactionID = pItem->GetProto()->RequiredReputationFaction ) + { + Repfaction1 = tabardFactionID; + Repfaction2 = tabardFactionID; + } + } + } + if(Rep->repfaction1 && (!Rep->team_dependent || GetTeam()==ALLIANCE)) { - int32 donerep1 = CalculateReputationGain(pVictim->getLevel(), Rep->repvalue1, Rep->repfaction1, false); + int32 donerep1 = CalculateReputationGain(pVictim->getLevel(), Rep->repvalue1, Repfaction1, false); donerep1 = int32(donerep1*rate); - FactionEntry const *factionEntry1 = sFactionStore.LookupEntry(Rep->repfaction1); + FactionEntry const *factionEntry1 = sFactionStore.LookupEntry(Repfaction1); uint32 current_reputation_rank1 = GetReputationMgr().GetRank(factionEntry1); if (factionEntry1 && current_reputation_rank1 <= Rep->reputation_max_cap1) GetReputationMgr().ModifyReputation(factionEntry1, donerep1); @@ -6313,9 +6364,9 @@ void Player::RewardReputation(Unit *pVictim, float rate) if(Rep->repfaction2 && (!Rep->team_dependent || GetTeam()==HORDE)) { - int32 donerep2 = CalculateReputationGain(pVictim->getLevel(), Rep->repvalue2, Rep->repfaction2, false); + int32 donerep2 = CalculateReputationGain(pVictim->getLevel(), Rep->repvalue2, Repfaction2, false); donerep2 = int32(donerep2*rate); - FactionEntry const *factionEntry2 = sFactionStore.LookupEntry(Rep->repfaction2); + FactionEntry const *factionEntry2 = sFactionStore.LookupEntry(Repfaction2); uint32 current_reputation_rank2 = GetReputationMgr().GetRank(factionEntry2); if (factionEntry2 && current_reputation_rank2 <= Rep->reputation_max_cap2) GetReputationMgr().ModifyReputation(factionEntry2, donerep2); @@ -6503,7 +6554,7 @@ bool Player::RewardHonor(Unit *uVictim, uint32 groupsize, float honor) if (!cVictim->isRacialLeader()) return false; - honor = 100; // ??? need more info + honor = 2000; // ??? need more info victim_rank = 19; // HK: Leader } } @@ -7826,7 +7877,7 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type) uint32 lootid = go->GetGOInfo()->GetLootId(); if ((go->GetEntry() == BG_AV_OBJECTID_MINE_N || go->GetEntry() == BG_AV_OBJECTID_MINE_S)) if (BattleGround *bg = GetBattleGround()) - if (bg->GetTypeID() == BATTLEGROUND_AV) + if (bg->GetTypeID(true) == BATTLEGROUND_AV) if (!(((BattleGroundAV*)bg)->PlayerCanDoMineQuest(go->GetEntry(), GetTeam()))) { SendLootRelease(guid); @@ -7901,7 +7952,7 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type) bones->lootForBody = true; uint32 pLevel = bones->loot.gold; bones->loot.clear(); - if (GetBattleGround()->GetTypeID() == BATTLEGROUND_AV) + if (GetBattleGround() && GetBattleGround()->GetTypeID(true) == BATTLEGROUND_AV) loot->FillLoot(0, LootTemplates_Creature, this, false); // It may need a better formula // Now it works like this: lvl10: ~6copper, lvl70: ~9silver @@ -8395,25 +8446,25 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid) case 2257: break; case 2597: // AV - if (bg && bg->GetTypeID() == BATTLEGROUND_AV) + if (bg && bg->GetTypeID(true) == BATTLEGROUND_AV) bg->FillInitialWorldStates(data, count); else FillInitialWorldState(data,count, AV_world_states); break; case 3277: // WS - if (bg && bg->GetTypeID() == BATTLEGROUND_WS) + if (bg && bg->GetTypeID(true) == BATTLEGROUND_WS) bg->FillInitialWorldStates(data, count); else FillInitialWorldState(data,count, WS_world_states); break; case 3358: // AB - if (bg && bg->GetTypeID() == BATTLEGROUND_AB) + if (bg && bg->GetTypeID(true) == BATTLEGROUND_AB) bg->FillInitialWorldStates(data, count); else FillInitialWorldState(data,count, AB_world_states); break; case 3820: // EY - if (bg && bg->GetTypeID() == BATTLEGROUND_EY) + if (bg && bg->GetTypeID(true) == BATTLEGROUND_EY) bg->FillInitialWorldStates(data, count); else FillInitialWorldState(data,count, EY_world_states); @@ -8428,7 +8479,7 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid) FillInitialWorldState(data,count, ZM_world_states); break; case 3698: // Nagrand Arena - if (bg && bg->GetTypeID() == BATTLEGROUND_NA) + if (bg && bg->GetTypeID(true) == BATTLEGROUND_NA) bg->FillInitialWorldStates(data, count); else { @@ -8438,7 +8489,7 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid) } break; case 3702: // Blade's Edge Arena - if (bg && bg->GetTypeID() == BATTLEGROUND_BE) + if (bg && bg->GetTypeID(true) == BATTLEGROUND_BE) bg->FillInitialWorldStates(data, count); else { @@ -8448,7 +8499,7 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid) } break; case 3968: // Ruins of Lordaeron - if (bg && bg->GetTypeID() == BATTLEGROUND_RL) + if (bg && bg->GetTypeID(true) == BATTLEGROUND_RL) bg->FillInitialWorldStates(data, count); else { @@ -8457,6 +8508,26 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid) FillInitialWorldState(data,count,0xbba,0x0);// 9 show } break; + case 4378: // Dalaran Severs + if (bg && bg->GetTypeID(true) == BATTLEGROUND_DS) + bg->FillInitialWorldStates(data, count); + else + { + FillInitialWorldState(data,count,0xe11,0x0);// 7 gold + FillInitialWorldState(data,count,0xe10,0x0);// 8 green + FillInitialWorldState(data,count,0xe1a,0x0);// 9 show + } + break; + case 4406: // Ring of Valor + if (bg && bg->GetTypeID(true) == BATTLEGROUND_RV) + bg->FillInitialWorldStates(data, count); + else + { + FillInitialWorldState(data,count,0xe11,0x0);// 7 gold + FillInitialWorldState(data,count,0xe10,0x0);// 8 green + FillInitialWorldState(data,count,0xe1a,0x0);// 9 show + } + break; case 3703: // Shattrath City break; default: @@ -12286,6 +12357,15 @@ void Player::ApplyEnchantment(Item *item, EnchantmentSlot slot, bool apply, bool if (!ignore_condition && pEnchant->EnchantmentCondition && !((Player*)this)->EnchantmentFitsRequirements(pEnchant->EnchantmentCondition, -1)) return; + if ((pEnchant->requiredLevel) > ((Player*)this)->getLevel()) + return; + + if ((pEnchant->requiredSkill) > 0) + { + if ((pEnchant->requiredSkillValue) > (((Player*)this)->GetSkillValue(pEnchant->requiredSkill))) + return; + } + if (!item->IsBroken()) { for (int s = 0; s < 3; ++s) @@ -13564,7 +13644,7 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver RemoveTimedQuest(quest_id); if (BattleGround* bg = GetBattleGround()) - if (bg->GetTypeID() == BATTLEGROUND_AV) + if (bg->GetTypeID(true) == BATTLEGROUND_AV) ((BattleGroundAV*)bg)->HandleQuestComplete(pQuest->GetQuestId(), this); if (pQuest->GetRewChoiceItemsCount() > 0) @@ -15405,6 +15485,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) _LoadQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS)); _LoadDailyQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS)); _LoadWeeklyQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADWEEKLYQUESTSTATUS)); + _LoadRandomBGStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADRANDOMBG)); _LoadTalents(holder->GetResult(PLAYER_LOGIN_QUERY_LOADTALENTS)); @@ -16924,7 +17005,7 @@ void Player::_SaveAuras() SpellAuraHolder *holder = itr->second; //skip all holders from spells that are passive //do not save single target holders (unless they were cast by the player) - if (!holder->IsPassive() && (holder->GetCasterGUID() == GetGUID() || !holder->IsSingleTarget())) + if (!holder->IsPassive() && (holder->GetCasterGUID() == GetGUID() || !holder->IsSingleTarget()) && !IsChanneledSpell(holder->GetSpellProto())) { int32 damage[MAX_EFFECT_INDEX]; int32 remaintime[MAX_EFFECT_INDEX]; @@ -17584,7 +17665,7 @@ void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent) if (pet && m_temporaryUnsummonedPetNumber && m_temporaryUnsummonedPetNumber != pet->GetCharmInfo()->GetPetNumber() && mode == PET_SAVE_AS_CURRENT) mode = PET_SAVE_NOT_IN_SLOT; - if (returnreagent && pet && mode != PET_SAVE_AS_CURRENT) + if (returnreagent && pet && mode != PET_SAVE_AS_CURRENT && !InBattleGround()) { //returning of reagents only for players, so best done here uint32 spellId = pet ? pet->GetUInt32Value(UNIT_CREATED_BY_SPELL) : m_oldpetspell; @@ -20692,7 +20773,7 @@ bool Player::CanCaptureTowerPoint() ); } -uint32 Player::GetBarberShopCost(uint8 newhairstyle, uint8 newhaircolor, uint8 newfacialhair) +uint32 Player::GetBarberShopCost(uint8 newhairstyle, uint8 newhaircolor, uint8 newfacialhair, uint8 newskintone) { uint32 level = getLevel(); @@ -20702,8 +20783,10 @@ uint32 Player::GetBarberShopCost(uint8 newhairstyle, uint8 newhaircolor, uint8 n uint8 hairstyle = GetByteValue(PLAYER_BYTES, 2); uint8 haircolor = GetByteValue(PLAYER_BYTES, 3); uint8 facialhair = GetByteValue(PLAYER_BYTES_2, 0); + uint8 skintone = GetByteValue(PLAYER_BYTES, 0); - if((hairstyle == newhairstyle) && (haircolor == newhaircolor) && (facialhair == newfacialhair)) + if((hairstyle == newhairstyle) && (haircolor == newhaircolor) && (facialhair == newfacialhair) && + ((skintone == newskintone) || (newskintone == -1))) return 0; GtBarberShopCostBaseEntry const *bsc = sGtBarberShopCostBaseStore.LookupEntry(level - 1); @@ -20722,6 +20805,9 @@ uint32 Player::GetBarberShopCost(uint8 newhairstyle, uint8 newhaircolor, uint8 n if(facialhair != newfacialhair) cost += bsc->cost * 0.75f; // +3/4 of price + if(skintone != newskintone && newskintone != -1) // +1/2 of price + cost += bsc->cost * 0.5f; + return uint32(cost); } @@ -20918,10 +21004,13 @@ void Player::SetTitle(CharTitlesEntry const* title, bool lost) GetSession()->SendPacket(&data); } -void Player::ConvertRune(uint8 index, RuneType newType) +void Player::ConvertRune(uint8 index, RuneType newType, uint32 spellid) { SetCurrentRune(index, newType); + if (spellid != 0) + SetConvertedBy(index, spellid); + WorldPacket data(SMSG_CONVERT_RUNE, 2); data << uint8(index); data << uint8(newType); @@ -20963,12 +21052,14 @@ void Player::InitRunes() m_runes = new Runes; m_runes->runeState = 0; + m_runes->needConvert = 0; for(uint32 i = 0; i < MAX_RUNES; ++i) { SetBaseRune(i, runeSlotTypes[i]); // init base types SetCurrentRune(i, runeSlotTypes[i]); // init current types SetRuneCooldown(i, 0); // reset cooldowns + SetConvertedBy(i, 0); // init spellid m_runes->SetRuneState(i); } @@ -21247,7 +21338,9 @@ uint8 Player::CanEquipUniqueItem( ItemPrototype const* itemProto, uint8 except_s void Player::HandleFall(MovementInfo const& movementInfo) { // calculate total z distance of the fall - float z_diff = m_lastFallZ - movementInfo.GetPos()->z; + float z_diff = (m_lastFallZ >= m_anti_BeginFallZ ? m_lastFallZ : m_anti_BeginFallZ) - movementInfo.GetPos()->z; + + m_anti_BeginFallZ=INVALID_HEIGHT; DEBUG_LOG("zDiff = %f", z_diff); //Players with low fall distance, Feather Fall or physical immunity (charges used) are ignored @@ -21557,6 +21650,12 @@ void Player::UnsummonPetTemporaryIfAny() if(!pet) return; + if (((Player*)this)->InArena()) + { + RemovePet(pet, PET_SAVE_NOT_IN_SLOT); // remove pet while is player teleported to arena + return; + } + if(!m_temporaryUnsummonedPetNumber && pet->isControlled() && !pet->isTemporarySummoned() ) { m_temporaryUnsummonedPetNumber = pet->GetCharmInfo()->GetPetNumber(); @@ -22237,3 +22336,21 @@ void Player::SetRestType( RestType n_r_type, uint32 areaTriggerId /*= 0*/) SetFFAPvP(false); } } + +void Player::SetRandomWinner(bool isWinner) +{ + m_IsBGRandomWinner = isWinner; + if(m_IsBGRandomWinner) + CharacterDatabase.PExecute("INSERT INTO character_battleground_random (guid) VALUES ('%u')", GetGUIDLow()); +} + +void Player::_LoadRandomBGStatus(QueryResult *result) +{ + //QueryResult *result = CharacterDatabase.PQuery("SELECT guid FROM character_battleground_random WHERE guid = '%u'", GetGUIDLow()); + + if (result) + { + m_IsBGRandomWinner = true; + delete result; + } +} diff --git a/src/game/Player.h b/src/game/Player.h index d4ba36e..dec7068 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -366,12 +366,14 @@ struct RuneInfo uint8 BaseRune; uint8 CurrentRune; uint16 Cooldown; // msec + uint32 ConvertedBy; }; struct Runes { RuneInfo runes[MAX_RUNES]; uint8 runeState; // mask of available runes + uint8 needConvert; // mask of runes that need to be converted void SetRuneState(uint8 index, bool set = true) { @@ -380,6 +382,17 @@ struct Runes else runeState &= ~(1 << index); // on cooldown } + + bool IsRuneNeedsConvert(uint8 index) + { + if (!needConvert) + return false; + + if (needConvert & (1 << index)) + return true; + else + return false; + } }; struct EnchantDuration @@ -914,6 +927,7 @@ enum PlayerLoginQueryIndex PLAYER_LOGIN_QUERY_LOADMAILEDITEMS, PLAYER_LOGIN_QUERY_LOADTALENTS, PLAYER_LOGIN_QUERY_LOADWEEKLYQUESTSTATUS, + PLAYER_LOGIN_QUERY_LOADRANDOMBG, MAX_PLAYER_LOGIN_QUERY }; @@ -1139,7 +1153,7 @@ class MANGOS_DLL_SPEC Player : public Unit std::string afkMsg; std::string dndMsg; - uint32 GetBarberShopCost(uint8 newhairstyle, uint8 newhaircolor, uint8 newfacialhair); + uint32 GetBarberShopCost(uint8 newhairstyle, uint8 newhaircolor, uint8 newfacialhair, uint8 newskintone); PlayerSocial *GetSocial() { return m_social; } @@ -2187,6 +2201,13 @@ class MANGOS_DLL_SPEC Player : public Unit bool isTotalImmune(); bool CanCaptureTowerPoint(); + // last used pet number (for BG's) + uint32 GetLastPetNumber() const { return m_lastpetnumber; } + void SetLastPetNumber(uint32 petnumber) { m_lastpetnumber = petnumber; } + + bool GetRandomWinner() { return m_IsBGRandomWinner; } + void SetRandomWinner(bool isWinner); + /*********************************************************/ /*** REST SYSTEM ***/ /*********************************************************/ @@ -2227,7 +2248,11 @@ class MANGOS_DLL_SPEC Player : public Unit bool isMoving() const { return m_movementInfo.HasMovementFlag(movementFlagsMask); } bool isMovingOrTurning() const { return m_movementInfo.HasMovementFlag(movementOrTurningFlagsMask); } - bool CanFly() const { return m_movementInfo.HasMovementFlag(MOVEFLAG_CAN_FLY); } + uint32 Anti__GetLastTeleTime() const { return m_anti_TeleTime; } + void Anti__SetLastTeleTime(uint32 TeleTime) { m_anti_TeleTime=TeleTime; } + //bool CanFly() const { return m_movementInfo.HasMovementFlag(MOVEFLAG_CAN_FLY); } + bool CanFly() const { return m_CanFly; } + void SetCanFly(bool CanFly) { m_CanFly=CanFly; } bool IsFlying() const { return m_movementInfo.HasMovementFlag(MOVEFLAG_FLYING); } bool IsFreeFlying() const { return HasAuraType(SPELL_AURA_MOD_FLIGHT_SPEED_MOUNTED) || HasAuraType(SPELL_AURA_FLY); } bool CanStartFlyInArea(uint32 mapid, uint32 zone, uint32 area) const; @@ -2369,7 +2394,20 @@ class MANGOS_DLL_SPEC Player : public Unit void SetBaseRune(uint8 index, RuneType baseRune) { m_runes->runes[index].BaseRune = baseRune; } void SetCurrentRune(uint8 index, RuneType currentRune) { m_runes->runes[index].CurrentRune = currentRune; } void SetRuneCooldown(uint8 index, uint16 cooldown) { m_runes->runes[index].Cooldown = cooldown; m_runes->SetRuneState(index, (cooldown == 0) ? true : false); } - void ConvertRune(uint8 index, RuneType newType); + void ConvertRune(uint8 index, RuneType newType, uint32 spellid = 0); + void SetConvertedBy(uint8 index, uint32 spellid) { m_runes->runes[index].ConvertedBy = spellid; } + void ClearConvertedBy(uint8 index) { m_runes->runes[index].ConvertedBy = 0; } + bool IsRuneConvertedBy(uint8 index, uint32 spellid) { return m_runes->runes[index].ConvertedBy == spellid; } + void SetNeedConvertRune(uint8 index, bool convert, uint32 spellid = 0) + { + if (convert) + m_runes->needConvert |= (1 << index); // need convert + else + m_runes->needConvert &= ~(1 << index); // removed from convert + + if (spellid != 0) + SetConvertedBy(index, spellid); + } void ResyncRunes(uint8 count); void AddRunePower(uint8 index); void InitRunes(); @@ -2403,6 +2441,8 @@ class MANGOS_DLL_SPEC Player : public Unit BgBattleGroundQueueID_Rec m_bgBattleGroundQueueID[PLAYER_MAX_BATTLEGROUND_QUEUES]; BGData m_bgData; + bool m_IsBGRandomWinner; + /*********************************************************/ /*** QUEST SYSTEM ***/ /*********************************************************/ @@ -2428,6 +2468,7 @@ class MANGOS_DLL_SPEC Player : public Unit void _LoadQuestStatus(QueryResult *result); void _LoadDailyQuestStatus(QueryResult *result); void _LoadWeeklyQuestStatus(QueryResult *result); + void _LoadRandomBGStatus(QueryResult *result); void _LoadGroup(QueryResult *result); void _LoadSkills(QueryResult *result); void _LoadSpells(QueryResult *result); @@ -2579,6 +2620,16 @@ class MANGOS_DLL_SPEC Player : public Unit RestType rest_type; ////////////////////Rest System///////////////////// + //movement anticheat + uint32 m_anti_lastmovetime; //last movement time + float m_anti_MovedLen; //Length of traveled way + uint32 m_anti_NextLenCheck; + float m_anti_BeginFallZ; //alternative falling begin + uint32 m_anti_lastalarmtime; //last time when alarm generated + uint32 m_anti_alarmcount; //alarm counter + uint32 m_anti_TeleTime; + bool m_CanFly; + // Transports Transport * m_transport; @@ -2599,6 +2650,9 @@ class MANGOS_DLL_SPEC Player : public Unit uint64 m_miniPet; + // last used pet number (for BG's) + uint32 m_lastpetnumber; + // Player summoning time_t m_summon_expire; uint32 m_summon_mapid; diff --git a/src/game/RandomMovementGenerator.cpp b/src/game/RandomMovementGenerator.cpp index 4ada553..9bf6a6d 100644 --- a/src/game/RandomMovementGenerator.cpp +++ b/src/game/RandomMovementGenerator.cpp @@ -68,26 +68,10 @@ RandomMovementGenerator::_setRandomLocation(Creature &creature) //else if (is_water_ok) // 3D system under water and above ground (swimming mode) else // 2D only { - dist = dist >= 100.0f ? 10.0f : sqrtf(dist); // 10.0 is the max that vmap high can check (MAX_CAN_FALL_DISTANCE) - - // The fastest way to get an accurate result 90% of the time. - // Better result can be obtained like 99% accuracy with a ray light, but the cost is too high and the code is too long. - nz = map->GetHeight(nx, ny, Z+dist-2.0f, false); - - if (fabs(nz-Z) > dist) // Map check - { - nz = map->GetHeight(nx, ny, Z-2.0f, true); // Vmap Horizontal or above - - if (fabs(nz-Z) > dist) - { - // Vmap Higher - nz = map->GetHeight(nx, ny, Z+dist-2.0f, true); - - // let's forget this bad coords where a z cannot be find and retry at next tick - if (fabs(nz-Z) > dist) - return; - } - } + nz = Z; + if(!map->IsNextZcoordOK(nx, ny, nz, dist)) + return; // let's forget this bad coords where a z cannot be find and retry at next tick + creature.UpdateGroundPositionZ(nx, ny, nz, dist); } Traveller traveller(creature); diff --git a/src/game/ReputationMgr.cpp b/src/game/ReputationMgr.cpp index 136a95f..64fe554 100644 --- a/src/game/ReputationMgr.cpp +++ b/src/game/ReputationMgr.cpp @@ -255,6 +255,8 @@ bool ReputationMgr::SetReputation(FactionEntry const* factionEntry, int32 standi } } } + if (factionEntry->ID == 1037 || factionEntry->ID == 1052) + res = SetOneFactionReputation(factionEntry, standing, incremental); return res; } else @@ -355,7 +357,8 @@ void ReputationMgr::SetVisible(FactionEntry const *factionEntry) 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)) + // except if faction has FACTION_FLAG_SPECIAL + if(faction->Flags & (FACTION_FLAG_INVISIBLE_FORCED|FACTION_FLAG_HIDDEN) && !(faction->Flags & FACTION_FLAG_SPECIAL) ) return; // already set diff --git a/src/game/ReputationMgr.h b/src/game/ReputationMgr.h index 1c21727..dc2b014 100644 --- a/src/game/ReputationMgr.h +++ b/src/game/ReputationMgr.h @@ -32,7 +32,8 @@ enum FactionFlags 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 + FACTION_FLAG_RIVAL = 0x40, // flag for the two competing outland factions + FACTION_FLAG_SPECIAL = 0x80 }; typedef uint32 RepListID; diff --git a/src/game/SharedDefines.h b/src/game/SharedDefines.h index 609981a..dcefa4f 100644 --- a/src/game/SharedDefines.h +++ b/src/game/SharedDefines.h @@ -917,7 +917,7 @@ enum AuraState AURA_STATE_SWIFTMEND = 15, // T | AURA_STATE_DEADLY_POISON = 16, // T | AURA_STATE_ENRAGE = 17, // C | - //AURA_STATE_UNKNOWN18 = 18, // C t| + AURA_STATE_MECHANIC_BLEED = 18, // C t| //AURA_STATE_UNKNOWN19 = 19, // | not used //AURA_STATE_UNKNOWN20 = 20, // c | only (45317 Suicide) //AURA_STATE_UNKNOWN21 = 21, // | not used @@ -936,7 +936,7 @@ enum Mechanics MECHANIC_FEAR = 5, MECHANIC_GRIP = 6, MECHANIC_ROOT = 7, - MECHANIC_PACIFY = 8, //0 spells use this mechanic + MECHANIC_SLOWATTACK = 8, //0 spells use this mechanic, but some SPELL_AURA_MOD_HASTE and SPELL_AURA_MOD_RANGED_HASTE use as effect mechanic MECHANIC_SILENCE = 9, MECHANIC_SLEEP = 10, MECHANIC_SNARE = 11, @@ -965,7 +965,7 @@ enum Mechanics // 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))|(1<<(MECHANIC_DISORIENTED-1))|(1<<(MECHANIC_FEAR -1))| \ - (1<<(MECHANIC_ROOT -1))|(1<<(MECHANIC_PACIFY -1))|(1<<(MECHANIC_SLEEP -1))| \ + (1<<(MECHANIC_ROOT -1))|(1<<(MECHANIC_SLOWATTACK -1))|(1<<(MECHANIC_SLEEP -1))| \ (1<<(MECHANIC_SNARE -1))|(1<<(MECHANIC_STUN -1))|(1<<(MECHANIC_FREEZE-1))| \ (1<<(MECHANIC_KNOCKOUT-1))|(1<<(MECHANIC_POLYMORPH -1))|(1<<(MECHANIC_BANISH-1))| \ (1<<(MECHANIC_SHACKLE -1))|(1<<(MECHANIC_TURN -1))|(1<<(MECHANIC_HORROR-1))| \ @@ -985,10 +985,10 @@ enum Mechanics // Daze and all croud control spells except polymorph are not removed #define MECHANIC_NOT_REMOVED_BY_SHAPESHIFT ( \ - (1<<(MECHANIC_CHARM -1))|(1<<(MECHANIC_DISORIENTED-1))|(1<<(MECHANIC_FEAR -1))| \ - (1<<(MECHANIC_PACIFY-1))|(1<<(MECHANIC_STUN -1))|(1<<(MECHANIC_FREEZE-1))| \ - (1<<(MECHANIC_BANISH-1))|(1<<(MECHANIC_SHACKLE -1))|(1<<(MECHANIC_HORROR-1))| \ - (1<<(MECHANIC_TURN -1))|(1<<(MECHANIC_DAZE -1))|(1<<(MECHANIC_SAPPED-1))) + (1<<(MECHANIC_CHARM -1))|(1<<(MECHANIC_DISORIENTED-1))|(1<<(MECHANIC_FEAR -1))| \ + (1<<(MECHANIC_STUN -1))|(1<<(MECHANIC_FREEZE -1))|(1<<(MECHANIC_BANISH-1))| \ + (1<<(MECHANIC_SHACKLE-1))|(1<<(MECHANIC_HORROR -1))|(1<<(MECHANIC_TURN -1))| \ + (1<<(MECHANIC_DAZE -1))|(1<<(MECHANIC_SAPPED -1))) // Spell dispell type enum DispelType diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index 3da421f..d12be8e 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -716,8 +716,7 @@ void Spell::prepareDataForTriggerSystem() break; case SPELLFAMILY_HUNTER: // Hunter Rapid Killing/Explosive Trap Effect/Immolation Trap Effect/Frost Trap Aura/Snake Trap Effect/Explosive Shot - if ((m_spellInfo->SpellFamilyFlags & UI64LIT(0x0100200000000214)) || - m_spellInfo->SpellFamilyFlags2 & 0x200) + if ((m_spellInfo->SpellFamilyFlags & UI64LIT(0x0100000000000000)) || m_spellInfo->SpellFamilyFlags2 & 0x200) m_canTrigger = true; break; case SPELLFAMILY_PALADIN: @@ -778,9 +777,9 @@ void Spell::prepareDataForTriggerSystem() if (!IsPositiveEffect(m_spellInfo->Id, SpellEffectIndex(i))) m_negativeEffectMask |= (1<SpellFamilyName == SPELLFAMILY_HUNTER && (m_spellInfo->SpellFamilyFlags & UI64LIT(0x000020000000001C))) + // Hunter traps spells: Immolation Trap Effect, Frost Trap (triggering spell!!), + // Freezing Trap Effect(+ Freezing Arrow Effect), Explosive Trap Effect, Snake Trap Effect + if (m_spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000200000002008) || m_spellInfo->SpellFamilyFlags2 & 0x00064000)) m_procAttacker |= PROC_FLAG_ON_TRAP_ACTIVATION; } @@ -822,7 +821,10 @@ void Spell::AddUnitTarget(Unit* pVictim, SpellEffectIndex effIndex) target.processed = false; // Effects not apply on target // Calculate hit result - target.missCondition = m_caster->SpellHitResult(pVictim, m_spellInfo, m_canReflect); + + // Procs can miss, weapon enchants can miss, triggered spells and effects cannot miss (miss already calculated in triggering spell) + bool canMiss = (m_triggeredByAuraSpell || !m_IsTriggeredSpell); + target.missCondition = m_caster->SpellHitResult(pVictim, m_spellInfo, m_canReflect, canMiss); // Spell have speed - need calculate incoming time if (m_spellInfo->speed > 0.0f) @@ -1119,7 +1121,8 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) // Recheck immune (only for delayed spells) if (m_spellInfo->speed && ( unit->IsImmunedToDamage(GetSpellSchoolMask(m_spellInfo)) || - unit->IsImmunedToSpell(m_spellInfo))) + unit->IsImmunedToSpell(m_spellInfo)) && + !(m_spellInfo->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY)) { if (realCaster) realCaster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_IMMUNE); @@ -1128,6 +1131,16 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) return; } + // Recheck deflection (only for delayed spells) + if (m_spellInfo->speed && unit->HasAura(19263)) + { + if (realCaster) + realCaster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_DEFLECT); + + ResetEffectDamageAndHeal(); + return; + } + if (unit->GetTypeId() == TYPEID_PLAYER) { ((Player*)unit)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, m_spellInfo->Id); @@ -1927,11 +1940,11 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& break; case 64844: // Divine Hymn // target amount stored in parent spell dummy effect but hard to access - FillRaidOrPartyHealthPriorityTargets(targetUnitMap, m_caster, m_caster, radius, 3, true, false, false); + FillRaidOrPartyHealthPriorityTargets(targetUnitMap, m_caster, m_caster, radius, 3, true, false, true); break; case 64904: // Hymn of Hope // target amount stored in parent spell dummy effect but hard to access - FillRaidOrPartyManaPriorityTargets(targetUnitMap, m_caster, m_caster, radius, 3, true, false, false); + FillRaidOrPartyManaPriorityTargets(targetUnitMap, m_caster, m_caster, radius, 3, true, false, true); break; default: // selected friendly units (for casting objects) around casting object @@ -2048,8 +2061,13 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& FillAreaTargets(targetUnitMap, m_caster->GetPositionX(), m_caster->GetPositionY(), radius, PUSH_IN_FRONT_15, SPELL_TARGETS_AOE_DAMAGE); break; case TARGET_IN_FRONT_OF_CASTER_30: - FillAreaTargets(targetUnitMap, m_caster->GetPositionX(), m_caster->GetPositionY(), radius, PUSH_IN_FRONT_30, SPELL_TARGETS_AOE_DAMAGE); + { + if (m_spellInfo->SpellFamilyName == SPELLFAMILY_GENERIC) + FillAreaTargets(targetUnitMap, m_caster->GetPositionX(), m_caster->GetPositionY(), radius, PUSH_IN_FRONT_30, SPELL_TARGETS_AOE_DAMAGE); + else + FillAreaTargets(targetUnitMap, m_caster->GetPositionX(), m_caster->GetPositionY(), radius, PUSH_IN_FRONT_90, SPELL_TARGETS_AOE_DAMAGE); break; + } case TARGET_DUELVSPLAYER: { Unit *target = m_targets.getUnitTarget(); @@ -2413,6 +2431,11 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& { // add here custom effects that need default target. // FOR EVERY TARGET TYPE THERE IS A DIFFERENT FILL!! + if (m_spellInfo->SpellFamilyFlags2 & UI64LIT (0x00000020) && m_spellInfo->SpellIconID == 3217) + { + targetUnitMap.push_back(m_caster); + break; + } switch(m_spellInfo->Effect[effIndex]) { case SPELL_EFFECT_DUMMY: @@ -2625,6 +2648,19 @@ void Spell::prepare(SpellCastTargets const* targets, Aura* triggeredByAura) return; } + if(uint8 result = sObjectMgr.IsSpellDisabled(m_spellInfo->Id)) + { + if(m_caster->GetTypeId() == TYPEID_PLAYER) + { + sLog.outDebug("Player %s cast a spell %u which was disabled by server administrator", m_caster->GetName(), m_spellInfo->Id); + if(result == 2) + sLog.outChar("Player %s cast a spell %u which was disabled by server administrator and marked as CheatSpell", m_caster->GetName(), m_spellInfo->Id); + } + SendCastResult(SPELL_FAILED_SPELL_UNAVAILABLE); + finish(false); + return; + } + // Fill cost data m_powerCost = CalculatePowerCost(); @@ -2653,10 +2689,7 @@ void Spell::prepare(SpellCastTargets const* targets, Aura* triggeredByAura) // stealth must be removed at cast starting (at show channel bar) // skip triggered spell (item equip spell casting and other not explicit character casts/item uses) if ( !m_IsTriggeredSpell && isSpellBreakStealth(m_spellInfo) ) - { - m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); - m_caster->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - } + m_caster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CAST); // add non-triggered (with cast time and without) if (!m_IsTriggeredSpell) @@ -2780,6 +2813,14 @@ void Spell::cast(bool skipCheck) } } + if (m_spellInfo->Id == 27827) + if(const SpellEntry* spellInfo = sSpellStore.LookupEntry(m_spellInfo->Id)) + const_cast(spellInfo)->AuraInterruptFlags = 0; + + if (m_spellInfo->Id == 32592) + if(const SpellEntry* spellInfo = sSpellStore.LookupEntry(m_spellInfo->Id)) + const_cast(spellInfo)->Attributes |= SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY; + // different triggred (for caster) and precast (casted before apply effect to target) cases switch(m_spellInfo->SpellFamilyName) { @@ -2801,6 +2842,9 @@ void Spell::cast(bool skipCheck) // Ice Block if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000008000000000)) AddPrecastSpell(41425); // Hypothermia + // Fingers of Frost + else if (m_spellInfo->Id == 44544) + AddPrecastSpell(74396); break; } case SPELLFAMILY_WARRIOR: @@ -2809,6 +2853,9 @@ void Spell::cast(bool skipCheck) if ((m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000020000000000)) && m_spellInfo->Category==1209) if (m_caster->HasAura(58375)) // Glyph of Blocking AddTriggeredSpell(58374); // Glyph of Blocking + // Shattering Throw + else if (m_spellInfo->Id == 64382) + AddTriggeredSpell(64380); // Shattering Throw break; } case SPELLFAMILY_PRIEST: @@ -2856,6 +2903,9 @@ void Spell::cast(bool skipCheck) // Lock and Load if (m_spellInfo->Id == 56453) AddPrecastSpell(67544); // Lock and Load Marker + // Deterrence + else if (m_spellInfo->Id == 19263) + AddTriggeredSpell(67801); break; } case SPELLFAMILY_PALADIN: @@ -2945,7 +2995,7 @@ void Spell::cast(bool skipCheck) InitializeDamageMultipliers(); // Okay, everything is prepared. Now we need to distinguish between immediate and evented delayed spells - if (m_spellInfo->speed > 0.0f) + if (m_spellInfo->speed > 0.0f || m_spellInfo->Id == 14157) { // Remove used for cast item if need (it can be already NULL after TakeReagents call @@ -3156,6 +3206,9 @@ void Spell::update(uint32 difftime) { if(m_timer) { + if (m_targets.getUnitTarget() && m_targets.getUnitTarget()->isAlive() && !m_targets.getUnitTarget()->isVisibleForOrDetect(m_caster, m_caster, false)) + cancel(); + if(difftime >= m_timer) m_timer = 0; else @@ -3332,6 +3385,39 @@ void Spell::finish(bool ok) // Stop Attack for some spells if( m_spellInfo->Attributes & SPELL_ATTR_STOP_ATTACK_TARGET ) m_caster->AttackStop(); + + // hack for Fingers of Frost stacks remove + if(m_caster->HasAura(74396) && !m_IsTriggeredSpell && m_spellInfo->SpellFamilyName == SPELLFAMILY_MAGE) + if (Aura *aur = m_caster->GetAura(74396, EFFECT_INDEX_0)) + if(aur->GetHolder()->DropAuraCharge()) + m_caster->RemoveAura(aur); + + // hack for SPELL_AURA_IGNORE_UNIT_STATE charges + bool break_for = false; + Unit::AuraList const& stateAuras = m_caster->GetAurasByType(SPELL_AURA_IGNORE_UNIT_STATE); + for(Unit::AuraList::const_iterator j = stateAuras.begin();j != stateAuras.end(); ++j) + { + switch((*j)->GetId()) + { + case 52437: //Sudden death should disappear after execute + if (m_spellInfo->SpellIconID == 1648) + { + m_caster->RemoveAura((*j)); + break_for = true; + } + break; + case 60503: // Taste for blood + case 68051: // Glyph of overpower - Both should disappear after overpower + if(m_spellInfo->Id == 7384) + { + m_caster->RemoveAura((*j)); + break_for = true; + } + break; + } + if(break_for) + break; + } } void Spell::SendCastResult(SpellCastResult result) @@ -3417,13 +3503,15 @@ void Spell::SendSpellStart() if (m_spellInfo->runeCostID) castFlags |= CAST_FLAG_UNKNOWN10; + Unit *caster = (m_originalCaster && m_IsTriggeredSpell) ? m_originalCaster : m_caster; + WorldPacket data(SMSG_SPELL_START, (8+8+4+4+2)); if (m_CastItem) data << m_CastItem->GetPackGUID(); else - data << m_caster->GetPackGUID(); + data << caster->GetPackGUID(); - data << m_caster->GetPackGUID(); + data << caster->GetPackGUID(); data << uint8(m_cast_count); // pending spell cast? data << uint32(m_spellInfo->Id); // spellId data << uint32(castFlags); // cast flags @@ -3479,14 +3567,16 @@ void Spell::SendSpellGo() castFlags |= CAST_FLAG_UNKNOWN7; // rune cooldowns list } + Unit *caster = (m_originalCaster && m_IsTriggeredSpell) ? m_originalCaster : m_caster; + WorldPacket data(SMSG_SPELL_GO, 50); // guess size if(m_CastItem) data << m_CastItem->GetPackGUID(); else - data << m_caster->GetPackGUID(); + data << caster->GetPackGUID(); - data << m_caster->GetPackGUID(); + data << caster->GetPackGUID(); data << uint8(m_cast_count); // pending spell cast? data << uint32(m_spellInfo->Id); // spellId data << uint32(castFlags); // cast flags @@ -3497,7 +3587,7 @@ void Spell::SendSpellGo() data << m_targets; if ( castFlags & CAST_FLAG_UNKNOWN6 ) // unknown wotlk, predicted power? - data << uint32(0); + data << uint32(m_caster->GetPower(m_caster->getPowerType())); // Yes, it is really predicted power. if ( castFlags & CAST_FLAG_UNKNOWN7 ) // rune cooldowns list { @@ -4045,7 +4135,10 @@ SpellCastResult Spell::CheckOrTakeRunePower(bool take) --runeCost[rune]; if (take) + { plr->ConvertRune(i, plr->GetBaseRune(i)); + plr->ClearConvertedBy(i); + } } } @@ -4198,11 +4291,11 @@ SpellCastResult Spell::CheckCast(bool strict) { // check cooldowns to prevent cheating (ignore passive spells, that client side visual only) if (m_caster->GetTypeId()==TYPEID_PLAYER && !(m_spellInfo->Attributes & SPELL_ATTR_PASSIVE) && - ((Player*)m_caster)->HasSpellCooldown(m_spellInfo->Id)) + ((Player*)m_caster)->HasSpellCooldown(m_spellInfo->Id) && !m_caster->isIgnoreUnitState(m_spellInfo)) { if(m_triggeredByAuraSpell) return SPELL_FAILED_DONT_REPORT; - else + else if(!m_IsTriggeredSpell || m_CastItem) return SPELL_FAILED_NOT_READY; } @@ -4270,6 +4363,10 @@ SpellCastResult Spell::CheckCast(bool strict) else if(m_caster->HasAura(m_spellInfo->excludeCasterAuraSpell)) return SPELL_FAILED_CASTER_AURASTATE; } + //Check Caster for combat + if(m_caster->isInCombat() && IsNonCombatSpell(m_spellInfo) && + !m_IsTriggeredSpell && !m_caster->isIgnoreUnitState(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) @@ -4312,7 +4409,7 @@ SpellCastResult Spell::CheckCast(bool strict) if(non_caster_target) { // target state requirements (apply to non-self only), to allow cast affects to self like Dirty Deeds - if(m_spellInfo->TargetAuraState && !target->HasAuraStateForCaster(AuraState(m_spellInfo->TargetAuraState),m_caster->GetGUID())) + if(m_spellInfo->TargetAuraState && !target->HasAuraStateForCaster(AuraState(m_spellInfo->TargetAuraState),m_caster->GetGUID()) && !m_caster->isIgnoreUnitState(m_spellInfo)) return SPELL_FAILED_TARGET_AURASTATE; // Not allow casting on flying player @@ -4440,7 +4537,7 @@ SpellCastResult Spell::CheckCast(bool strict) } // TODO: this check can be applied and for player to prevent cheating when IsPositiveSpell will return always correct result. // check target for pet/charmed casts (not self targeted), self targeted cast used for area effects and etc - if (!explicit_target_mode && m_caster->GetTypeId() == TYPEID_UNIT && m_caster->GetCharmerOrOwnerGUID()) + if (!explicit_target_mode && m_caster->GetTypeId() == TYPEID_UNIT && m_caster->GetCharmerOrOwnerGUID() && !IsDispelSpell(m_spellInfo)) { // check correctness positive/negative cast target (pet cast real check and cheating check) if(IsPositiveSpell(m_spellInfo->Id)) @@ -4743,7 +4840,7 @@ SpellCastResult Spell::CheckCast(bool strict) { if(m_spellInfo->SpellIconID == 1648) // Execute { - if(!m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetHealth() > m_targets.getUnitTarget()->GetMaxHealth()*0.2) + if(!m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetHealth() > m_targets.getUnitTarget()->GetMaxHealth()*0.2 && !m_caster->isIgnoreUnitState(m_spellInfo)) return SPELL_FAILED_BAD_TARGETS; } else if (m_spellInfo->Id == 51582) // Rocket Boots Engaged @@ -4751,6 +4848,12 @@ SpellCastResult Spell::CheckCast(bool strict) if(m_caster->IsInWater()) return SPELL_FAILED_ONLY_ABOVEWATER; } + else if (m_spellInfo->SpellFamilyFlags == UI64LIT(0x2000)) // Death Coil (DeathKnight) + { + Unit* target = m_targets.getUnitTarget(); + if (!target || (target->IsFriendlyTo(m_caster) && target->GetCreatureType() != CREATURE_TYPE_UNDEAD)) + return SPELL_FAILED_BAD_TARGETS; + } else if(m_spellInfo->SpellIconID == 156) // Holy Shock { // spell different for friends and enemies @@ -5027,6 +5130,17 @@ SpellCastResult Spell::CheckCast(bool strict) break; } + case SPELL_EFFECT_SUMMON_OBJECT_SLOT1: + case SPELL_EFFECT_SUMMON_OBJECT_SLOT2: + case SPELL_EFFECT_SUMMON_OBJECT_SLOT3: + case SPELL_EFFECT_SUMMON_OBJECT_SLOT4: + { + if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (((Player*)m_caster)->HasMovementFlag(MOVEFLAG_ONTRANSPORT)) + return SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW; + + break; + } case SPELL_EFFECT_SUMMON_PET: { if(m_caster->GetPetGUID()) //let warlock do a replacement summon @@ -5075,24 +5189,16 @@ SpellCastResult Spell::CheckCast(bool strict) case SPELL_EFFECT_LEAP: case SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER: { - float dis = GetSpellRadius(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 - float fz = m_caster->GetBaseMap()->GetHeight(fx, fy, m_caster->GetPositionZ(), true); - if(fz <= INVALID_HEIGHT) // note: this also will prevent use effect in instances without vmaps height enabled - return SPELL_FAILED_TRY_AGAIN; - - float caster_pos_z = m_caster->GetPositionZ(); - // Control the caster to not climb or drop when +-fz > 8 - if(!(fz <= caster_pos_z + 8 && fz >= caster_pos_z - 8)) - return SPELL_FAILED_TRY_AGAIN; - // not allow use this effect at battleground until battleground start if(m_caster->GetTypeId() == TYPEID_PLAYER) + { if(BattleGround const *bg = ((Player*)m_caster)->GetBattleGround()) if(bg->GetStatus() != STATUS_IN_PROGRESS) return SPELL_FAILED_TRY_AGAIN; + + if(((Player*)m_caster)->HasMovementFlag(MOVEFLAG_ONTRANSPORT)) + return SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW; + } break; } case SPELL_EFFECT_STEAL_BENEFICIAL_BUFF: @@ -5101,6 +5207,13 @@ SpellCastResult Spell::CheckCast(bool strict) return SPELL_FAILED_BAD_TARGETS; break; } + case SPELL_EFFECT_LEAP_BACK: + { + if(m_spellInfo->Id == 781) + if(!m_caster->isInCombat()) + return SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW; + break; + } default:break; } } @@ -5332,7 +5445,7 @@ SpellCastResult Spell::CheckPetCast(Unit* target) if(!_target->isAlive()) return SPELL_FAILED_BAD_TARGETS; - if(IsPositiveSpell(m_spellInfo->Id)) + if(IsPositiveSpell(m_spellInfo->Id) && !IsDispelSpell(m_spellInfo)) { if(m_caster->IsHostileTo(_target)) return SPELL_FAILED_BAD_TARGETS; @@ -5345,7 +5458,7 @@ SpellCastResult Spell::CheckPetCast(Unit* target) //TARGET_DUELVSPLAYER is positive AND negative duelvsplayertar |= (m_spellInfo->EffectImplicitTargetA[j] == TARGET_DUELVSPLAYER); } - if(m_caster->IsFriendlyTo(target) && !duelvsplayertar) + if(m_caster->IsFriendlyTo(target) && !duelvsplayertar && !IsDispelSpell(m_spellInfo)) { return SPELL_FAILED_BAD_TARGETS; } @@ -5397,7 +5510,8 @@ SpellCastResult Spell::CheckCasterAuras() const 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 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)) + if (unitflag & UNIT_FLAG_STUNNED && (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_STUNNED) || + (m_spellInfo->Id == 33206 && !m_caster->HasAura(63248)))) prevented_reason = SPELL_FAILED_STUNNED; else if (unitflag & UNIT_FLAG_CONFUSED && !(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_CONFUSED)) prevented_reason = SPELL_FAILED_CONFUSED; @@ -6304,6 +6418,11 @@ bool Spell::CheckTarget( Unit* target, SpellEffectIndex eff ) return false; } + // Check Sated & Exhaustion debuffs + if (((m_spellInfo->Id == 2825) && (target->HasAura(57724))) || + ((m_spellInfo->Id == 32182) && (target->HasAura(57723)))) + return false; + // Check targets for LOS visibility (except spells without range limitations ) switch(m_spellInfo->Effect[eff]) { diff --git a/src/game/SpellAuraDefines.h b/src/game/SpellAuraDefines.h index 630d2ac..5486d79 100644 --- a/src/game/SpellAuraDefines.h +++ b/src/game/SpellAuraDefines.h @@ -297,7 +297,7 @@ enum AuraType SPELL_AURA_259 = 259, SPELL_AURA_SCREEN_EFFECT = 260, SPELL_AURA_PHASE = 261, - SPELL_AURA_262 = 262, + SPELL_AURA_IGNORE_UNIT_STATE = 262, SPELL_AURA_ALLOW_ONLY_ABILITY = 263, SPELL_AURA_264 = 264, SPELL_AURA_265 = 265, @@ -319,7 +319,7 @@ enum AuraType SPELL_AURA_MOD_HONOR_GAIN = 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_LINKED = 284, SPELL_AURA_MOD_ATTACK_POWER_OF_ARMOR = 285, SPELL_AURA_ABILITY_PERIODIC_CRIT = 286, SPELL_AURA_DEFLECT_SPELLS = 287, @@ -336,7 +336,7 @@ enum AuraType SPELL_AURA_298 = 298, SPELL_AURA_299 = 299, SPELL_AURA_300 = 300, - SPELL_AURA_301 = 301, + SPELL_AURA_SCHOOL_HEAL_ABSORB = 301, SPELL_AURA_302 = 302, SPELL_AURA_303 = 303, SPELL_AURA_304 = 304, diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index d124b78..d991696 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -312,7 +312,7 @@ pAuraHandler AuraHandler[TOTAL_AURAS]= &Aura::HandleNULL, //259 corrupt healing over time spell &Aura::HandleNoImmediateEffect, //260 SPELL_AURA_SCREEN_EFFECT (miscvalue = id in ScreenEffect.dbc) not required any code &Aura::HandlePhase, //261 SPELL_AURA_PHASE undetectable invisibility? implemented in Unit::isVisibleForOrDetect - &Aura::HandleNULL, //262 ignore combat/aura state? + &Aura::HandleIgnoreUnitState, //262 SPELL_AURA_IGNORE_UNIT_STATE Allows some abilities which are avaible only in some cases.... implemented in Unit::isIgnoreUnitState & Spell::CheckCast &Aura::HandleAllowOnlyAbility, //263 SPELL_AURA_ALLOW_ONLY_ABILITY player can use only abilities set in SpellClassMask &Aura::HandleUnused, //264 unused (3.0.8a-3.2.2a) &Aura::HandleUnused, //265 unused (3.0.8a-3.2.2a) @@ -334,7 +334,7 @@ pAuraHandler AuraHandler[TOTAL_AURAS]= &Aura::HandleNoImmediateEffect, //281 SPELL_AURA_MOD_HONOR_GAIN implemented in Player::RewardHonor &Aura::HandleAuraIncreaseBaseHealthPercent, //282 SPELL_AURA_INCREASE_BASE_HEALTH_PERCENT &Aura::HandleNoImmediateEffect, //283 SPELL_AURA_MOD_HEALING_RECEIVED implemented in Unit::SpellHealingBonusTaken - &Aura::HandleNULL, //284 51 spells + &Aura::HandleAuraLinked, //284 SPELL_AURA_LINKED &Aura::HandleAuraModAttackPowerOfArmor, //285 SPELL_AURA_MOD_ATTACK_POWER_OF_ARMOR implemented in Player::UpdateAttackPowerAndDamage &Aura::HandleNoImmediateEffect, //286 SPELL_AURA_ABILITY_PERIODIC_CRIT implemented in Aura::IsCritFromAbilityAura called from Aura::PeriodicTick &Aura::HandleNoImmediateEffect, //287 SPELL_AURA_DEFLECT_SPELLS implemented in Unit::MagicSpellHitResult and Unit::MeleeSpellHitResult @@ -351,7 +351,7 @@ pAuraHandler AuraHandler[TOTAL_AURAS]= &Aura::HandleUnused, //298 unused (3.2.2a) &Aura::HandleUnused, //299 unused (3.2.2a) &Aura::HandleNULL, //300 3 spells (share damage?) - &Aura::HandleNULL, //301 5 spells + &Aura::HandleNoImmediateEffect, //301 SPELL_AURA_SCHOOL_HEAL_ABSORB implemented in Unit::CalculateHealAbsorb &Aura::HandleUnused, //302 unused (3.2.2a) &Aura::HandleNULL, //303 17 spells &Aura::HandleNULL, //304 2 spells (alcohol effect?) @@ -1079,6 +1079,15 @@ void Aura::HandleAddModifier(bool apply, bool Real) ((Player*)GetTarget())->AddSpellMod(m_spellmod, apply); ReapplyAffectedPassiveAuras(); + + if(GetSpellProto()->SpellFamilyName == SPELLFAMILY_DRUID && (m_spellmod->mask2 & UI64LIT(0x20000))) + { + GetTarget()->RemoveAurasDueToSpell(66530); + + // Aura 66530 is immediately applied ONLY when "Improved Barkskin" is learned in Caster/Travel Form + if(apply && (GetTarget()->m_form == FORM_NONE || GetTarget()->m_form == FORM_TRAVEL)) + GetTarget()->CastSpell(GetTarget(),66530,true); + } } void Aura::TriggerSpell() @@ -1858,6 +1867,27 @@ void Aura::TriggerSpell() return; break; + } + case 6474: // Earthen Power (from Earthbind Totem Passive) + { + Unit *owner = triggerTarget->GetOwner(); + + if (!owner) + break; + + Unit::AuraList const& dummyAuras = owner->GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraList::const_iterator itr = dummyAuras.begin(); itr != dummyAuras.end(); ++itr) + { + if ((*itr)->GetSpellProto()->SpellIconID == 2289 && (*itr)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_SHAMAN) + { + if (!roll_chance_i((*itr)->GetModifier()->m_amount)) + break; + + triggerTarget->CastSpell(triggerTarget, 59566, true, NULL, this); + break; + } + } + break; } case 16191: // Mana Tide { @@ -1872,7 +1902,10 @@ void Aura::TriggerSpell() if (Unit* caster = GetCaster()) caster->CastSpell(triggerTarget, trigger_spell_id, true, NULL, this); return; - } + } + case 48094: // Intense Cold + triggerTarget->CastSpell(triggerTarget, trigger_spell_id, true, NULL, this); + return; case 53563: // Beacon of Light // original caster must be target (beacon) target->CastSpell(target, trigger_spell_id, true, NULL, this, target->GetGUID()); @@ -2006,9 +2039,42 @@ void Aura::HandleAuraDummy(bool apply, bool Real) target->CastSpell(target, 47189, true, NULL, this); // allow script to process further (text) break; + case 47977: // Magic Broom + Spell::SelectMountByAreaAndSkill(target, 42680, 42683, 42667, 42668, 0); + return; case 48025: // Headless Horseman's Mount Spell::SelectMountByAreaAndSkill(target, 51621, 48024, 51617, 48023, 0); return; + case 55328: // Stoneclaw Totem I + target->CastSpell(target, 5728, true); + return; + case 55329: // Stoneclaw Totem II + target->CastSpell(target, 6397, true); + return; + case 55330: // Stoneclaw Totem III + target->CastSpell(target, 6398, true); + return; + case 55332: // Stoneclaw Totem IV + target->CastSpell(target, 6399, true); + return; + case 55333: // Stoneclaw Totem V + target->CastSpell(target, 10425, true); + return; + case 55335: // Stoneclaw Totem VI + target->CastSpell(target, 10426, true); + return; + case 55278: // Stoneclaw Totem VII + target->CastSpell(target, 25513, true); + return; + case 58589: // Stoneclaw Totem VIII + target->CastSpell(target, 58583, true); + return; + case 58590: // Stoneclaw Totem IX + target->CastSpell(target, 58584, true); + return; + case 58591: // Stoneclaw Totem X + target->CastSpell(target, 58585, true); + return; case 62061: // Festive Holiday Mount if (target->HasAuraType(SPELL_AURA_MOUNTED)) // Reindeer Transformation @@ -2076,6 +2142,19 @@ void Aura::HandleAuraDummy(bool apply, bool Real) } break; } + case SPELLFAMILY_MAGE: + { + // hack for Fingers of Frost stacks + if (GetId() == 74396) + { + if (Aura *aur = target->GetAura(74396, EFFECT_INDEX_0)) + { + if (aur->GetStackAmount() < 3) + GetHolder()->SetAuraCharges(3); + } + } + break; + } case SPELLFAMILY_SHAMAN: { // Tidal Force @@ -2105,6 +2184,18 @@ void Aura::HandleAuraDummy(bool apply, bool Real) } break; } + case SPELLFAMILY_DEATHKNIGHT: + { + // Hungering Cold - disease apply + if(GetId() == 51209) + { + Unit *caster = GetCaster(); + if(!caster) + return; + caster->CastSpell(target, 55095, true); + } + break; + } } } // AT REMOVE @@ -2236,6 +2327,12 @@ void Aura::HandleAuraDummy(bool apply, bool Real) } return; } + case 74396: // Fingers of Frost effect remove + { + if (GetHolder()->GetAuraCharges() <= 0) + target->RemoveAurasDueToSpell(44544); + return; + } } // Living Bomb @@ -2443,13 +2540,24 @@ void Aura::HandleAuraDummy(bool apply, bool Real) int32 bp0 = m_modifier.m_amount; if (Unit* caster = GetCaster()) - target->CastCustomSpell(caster, 48210, &bp0, NULL, NULL, true, NULL, this); + target->CastCustomSpell(caster, 48210, &bp0, NULL, NULL, true, NULL, this, GetCasterGUID()); } } break; } case SPELLFAMILY_PRIEST: { + // Penance + if (GetSpellProto()->SpellIconID == 2818) + { + Unit* caster = GetCaster(); + if (!caster || caster->GetTypeId() != TYPEID_PLAYER) + return; + + if (apply && target) + ((Player*)caster)->SetSelection(target->GetGUID()); + return; + } // Pain and Suffering if (GetSpellProto()->SpellIconID == 2874 && target->GetTypeId()==TYPEID_PLAYER) { @@ -2759,25 +2867,86 @@ void Aura::HandleAuraModShapeshift(bool apply, bool Real) modelid = ssEntry->modelID_A; else { - // players are a bit difficult since the dbc has seldomly an horde modelid - // so we add hacks here to set the right model - if (Player::TeamForRace(target->getRace()) == ALLIANCE) - modelid = ssEntry->modelID_A; - else // 3.2.3 only the moonkin form has this information - modelid = ssEntry->modelID_H; - + // The following are the different shapeshifting models for cat/bear forms according + // to hair color for druids and skin tone for tauren introduced in patch 3.2 + if (form == FORM_CAT || form == FORM_BEAR || form == FORM_DIREBEAR) + { + if (Player::TeamForRace(target->getRace()) == ALLIANCE) + { + uint8 hairColour = target->GetByteValue(PLAYER_BYTES, 3); + if (form == FORM_CAT) + { + if (hairColour >= 0 && hairColour <= 2) modelid = 29407; + else if (hairColour == 3 || hairColour == 5) modelid = 29405; + else if (hairColour == 6) modelid = 892; + else if (hairColour == 7) modelid = 29406; + else if (hairColour == 4) modelid = 29408; + } + else + { + if (hairColour >= 0 && hairColour <= 2) modelid = 29413; + else if (hairColour == 3 || hairColour == 5) modelid = 29415; + else if (hairColour == 6) modelid = 29414; + else if (hairColour == 7) modelid = 29417; + else if (hairColour == 4) modelid = 29416; + } + } + else if (Player::TeamForRace(target->getRace()) == HORDE) + { + uint8 skinColour = target->GetByteValue(PLAYER_BYTES, 0); + if (target->getGender() == GENDER_MALE) + { + if (form == FORM_CAT) + { + if (skinColour >= 0 && skinColour <= 5) modelid = 29412; + else if (skinColour >= 6 && skinColour <= 8) modelid = 29411; + else if (skinColour >= 9 && skinColour <= 11) modelid = 29410; + else if (skinColour >= 12 && skinColour <= 14 || skinColour == 18) modelid = 29409; + else if (skinColour >= 15 && skinColour <= 17) modelid = 8571; + } + else + { + if (skinColour >= 0 && skinColour <= 2) modelid = 29418; + else if (skinColour >= 3 && skinColour <= 5 || skinColour >= 12 && skinColour <= 14) modelid = 29419; + else if (skinColour >= 9 && skinColour <= 11 || skinColour >= 15 && skinColour <= 17) modelid = 29420; + else if (skinColour >= 6 && skinColour <= 8) modelid = 2289; + else if (skinColour == 18) modelid = 29421; + } + } + else + { + if (form == FORM_CAT) + { + if (skinColour >= 0 && skinColour <= 3) modelid = 29412; + else if (skinColour == 4 || skinColour == 5) modelid = 29411; + else if (skinColour == 6 || skinColour == 7) modelid = 29410; + else if (skinColour == 8 || skinColour == 9) modelid = 8571; + else if (skinColour == 10) modelid = 29409; + } + else + { + if (skinColour == 0 || skinColour == 1) modelid = 29418; + else if (skinColour == 2 || skinColour == 3) modelid = 29419; + else if (skinColour == 4 || skinColour == 5) modelid = 2289; + else if (skinColour >= 6 && skinColour <= 9) modelid = 29420; + else if (skinColour == 10) modelid = 29421; + } + } + } + } + else + { + if (Player::TeamForRace(target->getRace()) == ALLIANCE) modelid = ssEntry->modelID_A; + if (Player::TeamForRace(target->getRace()) == HORDE) modelid = ssEntry->modelID_H; + } // no model found, if player is horde we look here for our hardcoded modelids if (!modelid && Player::TeamForRace(target->getRace()) == HORDE) { - switch(form) { case FORM_CAT: - modelid = 8571; - break; case FORM_BEAR: case FORM_DIREBEAR: - modelid = 2289; break; case FORM_FLIGHT: modelid = 20872; @@ -2799,6 +2968,7 @@ void Aura::HandleAuraModShapeshift(bool apply, bool Real) switch(form) { case FORM_CAT: + case FORM_SHADOW_DANCE: PowerType = POWER_ENERGY; break; case FORM_BEAR: @@ -2865,7 +3035,8 @@ void Aura::HandleAuraModShapeshift(bool apply, bool Real) if(target->m_ShapeShiftFormSpellId) target->RemoveAurasDueToSpell(target->m_ShapeShiftFormSpellId, GetHolder()); - target->SetByteValue(UNIT_FIELD_BYTES_2, 3, form); + // For Shadow Dance we must apply Stealth form (30) instead of current (13) + target->SetByteValue(UNIT_FIELD_BYTES_2, 3, (form == FORM_SHADOW_DANCE) ? uint8(FORM_STEALTH) : form); if(modelid > 0) target->SetDisplayId(modelid); @@ -2934,6 +3105,10 @@ void Aura::HandleAuraModShapeshift(bool apply, bool Real) target->SetPower(POWER_RAGE, Rage_val); break; } + // Shadow Dance - apply stealth mode stand flag + case FORM_SHADOW_DANCE: + target->SetStandFlags(UNIT_STAND_FLAGS_CREEP); + break; default: break; } @@ -2974,6 +3149,10 @@ void Aura::HandleAuraModShapeshift(bool apply, bool Real) if(Aura* dummy = target->GetDummyAura(37324) ) target->CastSpell(target, 37325, true, NULL, dummy); break; + // Shadow Dance - remove stealth mode stand flag + case FORM_SHADOW_DANCE: + target->RemoveStandFlags(UNIT_STAND_FLAGS_CREEP); + break; default: break; } @@ -2990,6 +3169,8 @@ void Aura::HandleAuraModShapeshift(bool apply, bool Real) // add/remove the shapeshift aura's boosts HandleShapeshiftBoosts(apply); + target->UpdateSpeed(MOVE_RUN, true); + if(target->GetTypeId() == TYPEID_PLAYER) ((Player*)target)->InitDataForForm(); } @@ -3703,6 +3884,9 @@ void Aura::HandleAuraModStun(bool apply, bool Real) Unit *target = GetTarget(); + if(target->IsTaxiFlying()) + return; + if (apply) { // Frost stun aura -> freeze/unfreeze target @@ -4219,9 +4403,16 @@ void Aura::HandleAuraModIncreaseFlightSpeed(bool apply, bool Real) { WorldPacket data; if(apply) + { + ((Player*)target)->SetCanFly(true); data.Initialize(SMSG_MOVE_SET_CAN_FLY, 12); + } else + { data.Initialize(SMSG_MOVE_UNSET_CAN_FLY, 12); + ((Player*)target)->SetCanFly(false); + } + //data.append(target->GetPackGUID()); data << target->GetPackGUID(); data << uint32(0); // unknown target->SendMessageToSet(&data, true); @@ -4335,7 +4526,7 @@ void Aura::HandleModMechanicImmunity(bool apply, bool /*Real*/) uint32 mechanic = 1 << (misc-1); //immune movement impairment and loss of control - if(GetId()==42292 || GetId()==59752 || GetId()==65547) + if(GetId()==42292 || GetId()==59752 || GetId()==65547 || GetId()==53490) mechanic=IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; target->RemoveAurasAtMechanicImmunity(mechanic,GetId()); @@ -4343,6 +4534,19 @@ void Aura::HandleModMechanicImmunity(bool apply, bool /*Real*/) target->ApplySpellImmune(GetId(),IMMUNITY_MECHANIC,misc,apply); + // Demonic Circle + if (GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK && GetSpellProto()->SpellIconID == 3221) + { + if (target->GetTypeId() != TYPEID_PLAYER) + return; + if (apply) + { + GameObject* obj = target->GetGameObject(48018); + if (obj) + ((Player*)target)->TeleportTo(obj->GetMapId(),obj->GetPositionX(),obj->GetPositionY(),obj->GetPositionZ(),obj->GetOrientation()); + } + } + // Bestial Wrath if (GetSpellProto()->SpellFamilyName == SPELLFAMILY_HUNTER && GetSpellProto()->SpellIconID == 1680) { @@ -4586,6 +4790,18 @@ void Aura::HandlePeriodicEnergize(bool apply, bool Real) break; } } + if (!apply && !loading) + { + switch (GetId()) + { + case 5229: // Druid Bear Enrage + if (target->HasAura(51185)) // King of the Jungle self Enrage bonus with infinity duration + target->RemoveAurasDueToSpell(51185); + break; + default: + break; + } + } m_isPeriodic = apply; } @@ -4621,6 +4837,21 @@ void Aura::HandleAuraPeriodicDummy(bool apply, bool Real) } break; } + case SPELLFAMILY_WARLOCK: + { + switch (spell->Id) + { + case 48018: + if (apply) + target->CastSpell(target, 62388, true); + else + { + target->RemoveGameObject(spell->Id,true); + target->RemoveAurasDueToSpell(62388); + } + break; + } + } case SPELLFAMILY_HUNTER: { Unit* caster = GetCaster(); @@ -4910,11 +5141,21 @@ void Aura::HandleAuraModResistanceExclusive(bool apply, bool /*Real*/) { for(int8 x = SPELL_SCHOOL_NORMAL; x < MAX_SPELL_SCHOOL;x++) { + int32 oldMaxValue = 0; if(m_modifier.m_miscvalue & int32(1<HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + x), BASE_VALUE, float(m_modifier.m_amount), apply); + // no same resistance auras stack together + Unit::AuraList const& REAuras = GetTarget()->GetAurasByType(SPELL_AURA_MOD_RESISTANCE_EXCLUSIVE); + for (Unit::AuraList::const_iterator i = REAuras.begin(); i != REAuras.end(); ++i) + if (((*i)->GetMiscValue() & int32(1<GetSpellProto()->Id != GetSpellProto()->Id) + if (oldMaxValue < (*i)->GetModifier()->m_amount) + oldMaxValue = (*i)->GetModifier()->m_amount; + + float value = (m_modifier.m_amount > oldMaxValue) ? m_modifier.m_amount - oldMaxValue : 0.0f; + + GetTarget()->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + x), BASE_VALUE, value, apply); if(GetTarget()->GetTypeId() == TYPEID_PLAYER) - GetTarget()->ApplyResistanceBuffModsMod(SpellSchools(x), m_positive, float(m_modifier.m_amount), apply); + GetTarget()->ApplyResistanceBuffModsMod(SpellSchools(x), m_positive, value, apply); } } } @@ -5235,6 +5476,7 @@ void Aura::HandleAuraModIncreaseHealth(bool apply, bool Real) case 34511: // Valor (Bulwark of Kings, Bulwark of the Ancient Kings) case 44055: case 55915: case 55917: case 67596: // Tremendous Fortitude (Battlemaster's Alacrity) case 50322: // Survival Instincts + case 53479: // Hunter pet - Last Stand case 54443: // Demonic Empowerment (Voidwalker) case 55233: // Vampiric Blood case 59465: // Brood Rage (Ahn'Kahet) @@ -5863,9 +6105,13 @@ void Aura::HandleShapeshiftBoosts(bool apply) break; case FORM_SHADOW: spellId1 = 49868; + spellId2 = 71167; - if(target->GetTypeId() == TYPEID_PLAYER) // Spell 49868 have same category as main form spell and share cooldown + if(target->GetTypeId() == TYPEID_PLAYER) // Spell 49868 and 71167 have same category as main form spell and share cooldown + { ((Player*)target)->RemoveSpellCooldown(49868); + ((Player*)target)->RemoveSpellCooldown(71167); + } break; case FORM_GHOSTWOLF: spellId1 = 67116; @@ -5903,7 +6149,7 @@ void Aura::HandleShapeshiftBoosts(bool apply) if (MasterShaperSpellId) { Unit::AuraList const& ShapeShifterAuras = target->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraList::const_iterator i = ShapeShifterAuras.begin(); i != ShapeShifterAuras.end(); i++) + for(Unit::AuraList::const_iterator i = ShapeShifterAuras.begin(); i != ShapeShifterAuras.end(); ++i) { if ((*i)->GetSpellProto()->SpellIconID == 2851) { @@ -5930,7 +6176,7 @@ void Aura::HandleShapeshiftBoosts(bool apply) if (form == FORM_MOONKIN) { Unit::AuraList const& dummyAuras = target->GetAurasByType(SPELL_AURA_DUMMY); - for(Unit::AuraList::const_iterator i = dummyAuras.begin(); i != dummyAuras.end(); i++) + for(Unit::AuraList::const_iterator i = dummyAuras.begin(); i != dummyAuras.end(); ++i) { if ((*i)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_DRUID && (*i)->GetSpellProto()->SpellIconID == 2855) @@ -5953,6 +6199,35 @@ void Aura::HandleShapeshiftBoosts(bool apply) } } + // Improved Barkskin - apply/remove armor bonus due to shapeshift remove + if (((Player*)target)->HasSpell(63410) || ((Player*)target)->HasSpell(63411)) + { + if (form == FORM_TRAVEL) + { + target->RemoveAurasDueToSpell(66530); + target->CastSpell(target,66530,true); + } + else + target->RemoveAurasDueToSpell(66530); + } + + // Survival of the Fittest (Armor part) + if (form == FORM_BEAR || form == FORM_DIREBEAR) + { + Unit::AuraList const& modAuras = target->GetAurasByType(SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE); + for (Unit::AuraList::const_iterator i = modAuras.begin(); i != modAuras.end(); ++i) + { + if ((*i)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_DRUID && + (*i)->GetSpellProto()->SpellIconID == 961) + { + int32 bp = (*i)->GetSpellProto()->CalculateSimpleValue(EFFECT_INDEX_2); + if (bp) + target->CastCustomSpell(target, 62069, &bp, NULL, NULL, true, NULL, this); + break; + } + } + } + // Heart of the Wild if (HotWSpellId) { @@ -5992,6 +6267,12 @@ void Aura::HandleShapeshiftBoosts(bool apply) else ++itr; } + // Improved Barkskin - apply/remove armor bonus due to shapeshift + if (((Player*)target)->HasSpell(63410) || ((Player*)target)->HasSpell(63411)) + { + target->RemoveAurasDueToSpell(66530); + target->CastSpell(target,66530,true); + } } } @@ -6015,9 +6296,6 @@ void Aura::HandleAuraUntrackable(bool apply, bool /*Real*/) void Aura::HandleAuraModPacify(bool apply, bool /*Real*/) { - if(GetTarget()->GetTypeId() != TYPEID_PLAYER) - return; - if(apply) GetTarget()->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED); else @@ -6054,9 +6332,15 @@ void Aura::HandleAuraAllowFlight(bool apply, bool Real) // allow fly WorldPacket data; if(apply) + { + ((Player*)GetTarget())->SetCanFly(true); data.Initialize(SMSG_MOVE_SET_CAN_FLY, 12); + } else + { data.Initialize(SMSG_MOVE_UNSET_CAN_FLY, 12); + ((Player*)GetTarget())->SetCanFly(false); + } data << GetTarget()->GetPackGUID(); data << uint32(0); // unk GetTarget()->SendMessageToSet(&data, true); @@ -6633,8 +6917,12 @@ void Aura::PeriodicTick() DETAIL_FILTER_LOG(LOG_FILTER_PERIODIC_AFFECTS, "PeriodicTick: %u (TypeId: %u) heal of %u (TypeId: %u) for %u health inflicted by %u", GUID_LOPART(GetCasterGUID()), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), target->GetGUIDLow(), target->GetTypeId(), pdamage, GetId()); + // calculate heal absorb and reduce healing + uint32 absorb = 0; + pCaster->CalculateHealAbsorb(target, spellProto, pdamage, absorb); + int32 gain = target->ModifyHealth(pdamage); - SpellPeriodicAuraLogInfo pInfo(this, pdamage, (pdamage - uint32(gain)), 0, 0, 0.0f, isCrit); + SpellPeriodicAuraLogInfo pInfo(this, pdamage, (pdamage - uint32(gain)), absorb, 0, 0.0f, isCrit); target->SendPeriodicAuraLog(&pInfo); // Set trigger flag @@ -7159,6 +7447,30 @@ void Aura::PeriodicDummyTick() case 2: target->CastSpell(target, 55739, true); break; } return; + case 66118: // Leeching Swarm 10 man + case 68646: + { + int32 damage = (m_modifier.m_amount * target->GetHealth()) / 100; + if (damage < 250) + damage = 250; + int32 heal = damage * 68 / 100; + target->CastCustomSpell(target, 66240, &damage, NULL, NULL, true, NULL, this); + if (Unit* caster = GetCaster()) + target->CastCustomSpell(caster, 66125, &heal, NULL, NULL, true, NULL, this); + return; + } + case 67630: // Leeching Swarm 25 man + case 68647: + { + int32 damage = (m_modifier.m_amount * target->GetHealth()) / 100; + if (damage < 250) + damage = 250; + int32 heal = damage * 155 / 100; + target->CastCustomSpell(target, 66240, &damage, NULL, NULL, true, NULL, this); + if (Unit* caster = GetCaster()) + target->CastCustomSpell(caster, 66125, &heal, NULL, NULL, true, NULL, this); + return; + } // Exist more after, need add later default: break; @@ -7235,6 +7547,25 @@ void Aura::PeriodicDummyTick() } break; } + case SPELLFAMILY_WARLOCK: + switch (spell->Id) + { + case 48018: + GameObject* obj = target->GetGameObject(spell->Id); + if (!obj) + { + target->RemoveAurasDueToSpell(spell->Id); + target->RemoveAurasDueToSpell(62388); + return; + } + // We must take a range of teleport spell, not summon. + const SpellEntry* goToCircleSpell = sSpellStore.LookupEntry(48020); + if (target->IsWithinDist(obj,GetSpellMaxRange(sSpellRangeStore.LookupEntry(goToCircleSpell->rangeIndex)))) + target->CastSpell(target, 62388, true); + else + target->RemoveAurasDueToSpell(62388); + } + break; case SPELLFAMILY_ROGUE: { switch (spell->Id) @@ -7337,12 +7668,17 @@ void Aura::PeriodicDummyTick() } return; } + // Hysteria + if (spell->SpellFamilyFlags & UI64LIT(0x0000000020000000)) + { + uint32 deal = m_modifier.m_amount * target->GetMaxHealth() / 100; + target->DealDamage(target, deal, NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + target->SendSpellNonMeleeDamageLog(target, spell->Id, deal, SPELL_SCHOOL_MASK_NORMAL, 0, 0, false, 0, false); + return; + } // Summon Gargoyle // if (spell->SpellFamilyFlags & UI64LIT(0x0000008000000000)) // return; - // Death Rune Mastery -// if (spell->SpellFamilyFlags & UI64LIT(0x0000000000004000)) -// return; // Bladed Armor if (spell->SpellIconID == 2653) { @@ -7352,12 +7688,46 @@ void Aura::PeriodicDummyTick() target->CastCustomSpell(target, 61217, &apBonus, &apBonus, NULL, true, NULL, this); return; } - // Reaping -// if (spell->SpellIconID == 22) -// return; - // Blood of the North -// if (spell->SpellIconID == 30412) -// return; + // Death Rune Mastery + if (spell->SpellIconID == 2622) + { + if (target->GetTypeId() != TYPEID_PLAYER || target->isInCombat()) + return; + + Player *player = (Player*)target; + for (uint32 i = 0; i < MAX_RUNES; ++i) + { + if (!player->GetRuneCooldown(i)) + { + RuneType type = player->GetBaseRune(i); + if (player->GetCurrentRune(i) == RUNE_DEATH && (type == RUNE_FROST || type == RUNE_UNHOLY) && player->IsRuneConvertedBy(i, spell->Id)) + { + player->ConvertRune(i, type); + player->ClearConvertedBy(i); + } + } + } + } + // Blood of the North and Reaping + if (spell->SpellIconID == 3041 || spell->SpellIconID == 22) + { + if (target->GetTypeId() != TYPEID_PLAYER || target->isInCombat()) + return; + + Player *player = (Player*)target; + for (uint32 i = 0; i < MAX_RUNES; ++i) + { + if (!player->GetRuneCooldown(i) && player->IsRuneConvertedBy(i, spell->Id)) + { + RuneType type = player->GetBaseRune(i); + if (player->GetCurrentRune(i) == RUNE_DEATH && type == RUNE_BLOOD) + { + player->ConvertRune(i, type); + player->ClearConvertedBy(i); + } + } + } + } break; } default: @@ -7459,6 +7829,25 @@ void Aura::HandleAuraControlVehicle(bool apply, bool Real) } } +void Aura::HandleAuraLinked(bool apply, bool Real) +{ + if (!Real) + return; + + uint32 linkedSpell = GetSpellProto()->EffectTriggerSpell[m_effIndex]; + SpellEntry const *spellInfo = sSpellStore.LookupEntry(linkedSpell); + if (!spellInfo) + { + sLog.outError("HandleAuraLinked for spell %u effect %u: triggering unknown spell %u", GetSpellProto()->Id, m_effIndex, linkedSpell); + return; + } + + if (apply) + GetTarget()->CastSpell(GetTarget(), linkedSpell, true, NULL, this); + else + GetTarget()->RemoveAurasByCasterSpell(linkedSpell, GetCasterGUID()); +} + void Aura::HandleAuraOpenStable(bool apply, bool Real) { if(!Real || GetTarget()->GetTypeId() != TYPEID_PLAYER || !GetTarget()->IsInWorld()) @@ -7494,7 +7883,7 @@ void Aura::HandleAuraConvertRune(bool apply, bool Real) { if (plr->GetCurrentRune(i) == runeFrom && !plr->GetRuneCooldown(i)) { - plr->ConvertRune(i, runeTo); + plr->ConvertRune(i, runeTo, GetId()); break; } } @@ -7506,6 +7895,7 @@ void Aura::HandleAuraConvertRune(bool apply, bool Real) if(plr->GetCurrentRune(i) == runeTo && plr->GetBaseRune(i) == runeFrom) { plr->ConvertRune(i, runeFrom); + plr->ClearConvertedBy(i); break; } } @@ -7574,6 +7964,16 @@ void Aura::HandlePhase(bool apply, bool Real) target->SetVisibility(target->GetVisibility()); } +void Aura::HandleIgnoreUnitState(bool apply, bool Real) +{ + if(GetTarget()->GetTypeId() != TYPEID_PLAYER || !Real) + return; + + // for alowing charge/intercept/intervene in different stances + if (GetId() == 57499 && apply) + GetHolder()->SetAuraFlags(19); +} + void Aura::HandleAuraSafeFall( bool Apply, bool Real ) { // implemented in WorldSession::HandleMovementOpcodes @@ -7596,6 +7996,17 @@ bool Aura::IsCritFromAbilityAura(Unit* caster, uint32& damage) damage = caster->SpellCriticalDamageBonus(GetSpellProto(), damage, GetTarget()); return true; } + + // Special exception for Rupture spell, damage can crit after patch 3.3.3 + if (GetSpellProto()->SpellFamilyName == SPELLFAMILY_ROGUE && GetSpellProto()->SpellFamilyFlags & UI64LIT(0x000000000000100000)) + { + if(caster->IsSpellCrit(GetTarget(), GetSpellProto(), GetSpellSchoolMask(GetSpellProto()))) + { + damage = caster->SpellCriticalDamageBonus(GetSpellProto(), damage, GetTarget()); + return true; + } + } + return false; } @@ -7830,6 +8241,10 @@ void SpellAuraHolder::_AddSpellAuraHolder() // Faerie Fire (druid versions) if (m_spellProto->SpellFamilyName == SPELLFAMILY_DRUID && (m_spellProto->SpellFamilyFlags & UI64LIT(0x0000000000000400))) m_target->ModifyAuraState(AURA_STATE_FAERIE_FIRE, true); + + // Sting (hunter's pet ability) + if (m_spellProto->Category == 1133) + m_target->ModifyAuraState(AURA_STATE_FAERIE_FIRE, true); // Victorious if (m_spellProto->SpellFamilyName == SPELLFAMILY_WARRIOR && (m_spellProto->SpellFamilyFlags & UI64LIT(0x0004000000000000))) @@ -7847,6 +8262,9 @@ void SpellAuraHolder::_AddSpellAuraHolder() if(m_spellProto->Dispel == DISPEL_ENRAGE) m_target->ModifyAuraState(AURA_STATE_ENRAGE, true); + // Mechanic bleed aura state + if(GetAllSpellMechanicMask(m_spellProto) & (1 << (MECHANIC_BLEED-1))) + m_target->ModifyAuraState(AURA_STATE_MECHANIC_BLEED, true); } } @@ -7901,6 +8319,10 @@ void SpellAuraHolder::_RemoveSpellAuraHolder() if(m_spellProto->Dispel == DISPEL_ENRAGE) m_target->ModifyAuraState(AURA_STATE_ENRAGE, false); + // Mechanic bleed aura state + if(GetAllSpellMechanicMask(m_spellProto) & (1 << (MECHANIC_BLEED-1))) + m_target->ModifyAuraState(AURA_STATE_MECHANIC_BLEED, false); + uint32 removeState = 0; uint64 removeFamilyFlag = m_spellProto->SpellFamilyFlags; uint32 removeFamilyFlag2 = m_spellProto->SpellFamilyFlags2; @@ -8110,7 +8532,7 @@ bool SpellAuraHolder::IsNeedVisibleSlot(Unit const* caster) const return true; else if (IsSpellHaveAura(m_spellProto, SPELL_AURA_MOD_IGNORE_SHAPESHIFT)) return true; - else if (IsSpellHaveAura(m_spellProto, SPELL_AURA_262)) + else if (IsSpellHaveAura(m_spellProto, SPELL_AURA_IGNORE_UNIT_STATE)) return true; // passive auras (except totem auras) do not get placed in the slots @@ -8471,6 +8893,28 @@ void SpellAuraHolder::HandleSpellSpecificBoosts(bool apply) } case SPELLFAMILY_PALADIN: { + if (m_spellProto->Id == 19746) // Aura Mastery (on Concentration Aura remove and apply) + { + Unit *caster = GetCaster(); + if (!caster) + return; + + if (apply && caster->HasAura(31821)) + caster->CastSpell(caster, 64364, true, NULL); + else if (!apply) + caster->RemoveAurasDueToSpell(64364); + } + if (m_spellProto->Id == 31821) // Aura Mastery (on Aura Mastery original buff remove) + { + Unit *caster = GetCaster(); + if (!caster) + return; + + if (apply && caster->HasAura(19746)) + caster->CastSpell(caster, 64364, true, NULL); + else if (!apply) + caster->RemoveAurasDueToSpell(64364); + } if (m_spellProto->Id == 31884) // Avenging Wrath { if(!apply) @@ -8848,7 +9292,7 @@ void SpellAuraHolder::UnregisterSingleCastHolder() else { sLog.outError("Couldn't find the caster of the single target aura (SpellId %u), may crash later!", GetId()); - ASSERT(false); + //ASSERTt(false); } m_isSingleTarget = false; } diff --git a/src/game/SpellAuras.h b/src/game/SpellAuras.h index 376c88a..2c9f2e2 100644 --- a/src/game/SpellAuras.h +++ b/src/game/SpellAuras.h @@ -361,8 +361,10 @@ class MANGOS_DLL_SPEC Aura void HandleAuraIncreaseBaseHealthPercent(bool Apply, bool Real); void HandleNoReagentUseAura(bool Apply, bool Real); void HandlePhase(bool Apply, bool Real); + void HandleIgnoreUnitState(bool Apply, bool Real); void HandleModTargetArmorPct(bool Apply, bool Real); void HandleAuraModAllCritChance(bool Apply, bool Real); + void HandleAuraLinked(bool Apply, bool Real); void HandleAllowOnlyAbility(bool Apply, bool Real); void HandleAuraOpenStable(bool apply, bool Real); @@ -392,6 +394,7 @@ class MANGOS_DLL_SPEC Aura uint32 GetAuraTicks() const { return m_periodicTick; } uint32 GetAuraMaxTicks() const { return m_maxduration > 0 && m_modifier.periodictime > 0 ? m_maxduration / m_modifier.periodictime : 0; } uint8 GetStackAmount() const { return GetHolder()->GetStackAmount(); } + void SetAuraPeriodicTimer (int32 timer) { m_periodicTimer = timer; } void SetLoadedState(int32 damage,int32 maxduration,int32 duration) { diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index d8377e2..34ed5dc 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -325,6 +325,15 @@ void Spell::EffectSchoolDMG(SpellEffectIndex effect_idx) damage /= count; // divide to all targets break; } + // AoE spells, which damage is reduced with distance from the initial hit point + case 62598: case 62937: // Detonate + case 65279: // Lightning Nova + case 62311: case 64596: // Cosmic Smash + { + float distance = unitTarget->GetDistance2d(m_targets.m_destX, m_targets.m_destY); + damage *= exp(-distance/15.0f); + break; + } // percent from health with min case 25599: // Thundercrash { @@ -493,7 +502,9 @@ void Spell::EffectSchoolDMG(SpellEffectIndex effect_idx) if (aura) { int32 damagetick = aura->GetModifier()->m_amount; - damage += damagetick * 4; + // Save value of further damage + m_currentBasePoints[1] = damagetick * 2 / 3; + damage += damagetick * 3; // Glyph of Conflagrate if (!m_caster->HasAura(56235)) @@ -755,7 +766,11 @@ void Spell::EffectSchoolDMG(SpellEffectIndex effect_idx) else if (m_spellInfo->Id == 54158) { // [1 + 0.25 * SPH + 0.16 * AP] - damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * 0.16f); + float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK); + int32 holy = m_caster->SpellBaseDamageBonusDone(GetSpellSchoolMask(m_spellInfo)); + if (holy < 0) + holy = 0; + damage += int32(ap * 0.16f) + int32(holy * 25 / 100); } break; } @@ -1875,13 +1890,18 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) uint32 rage = m_caster->GetPower(POWER_RAGE); - // up to max 30 rage cost - if (rage > 300) - rage = 300; + if (!rage) + m_caster->SetPower(POWER_RAGE, 1); - // Glyph of Execution bonus - uint32 rage_modified = rage; + uint32 rage_addition = rage; + + // up to max 30 total rage cost + if (rage_addition + GetPowerCost() > 300) + rage_addition = 300 - GetPowerCost(); + uint32 rage_modified = rage_addition; + + // Glyph of Execution bonus if (Aura *aura = m_caster->GetDummyAura(58367)) rage_modified += aura->GetModifier()->m_amount*10; @@ -1890,25 +1910,26 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) m_caster->CastCustomSpell(unitTarget, 20647, &basePoints0, NULL, NULL, true, 0); + uint32 new_rage = rage - rage_addition; + // Sudden Death - if (m_caster->HasAura(52437)) + Unit::AuraList const& auras = m_caster->GetAurasByType(SPELL_AURA_PROC_TRIGGER_SPELL); + for (Unit::AuraList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) { - Unit::AuraList const& auras = m_caster->GetAurasByType(SPELL_AURA_PROC_TRIGGER_SPELL); - for (Unit::AuraList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) + // Only Sudden Death have this SpellIconID with SPELL_AURA_PROC_TRIGGER_SPELL + if ((*itr)->GetSpellProto()->SpellIconID == 1989) { - // Only Sudden Death have this SpellIconID with SPELL_AURA_PROC_TRIGGER_SPELL - if ((*itr)->GetSpellProto()->SpellIconID == 1989) - { - // saved rage top stored in next affect - uint32 lastrage = (*itr)->GetSpellProto()->CalculateSimpleValue(EFFECT_INDEX_1)*10; - if(lastrage < rage) - rage -= lastrage; - break; - } + // saved rage top stored in next affect + uint32 save_rage = (*itr)->GetSpellProto()->CalculateSimpleValue(EFFECT_INDEX_1)*10; + + if (new_rage < save_rage) + new_rage = save_rage; + + break; } } - m_caster->SetPower(POWER_RAGE,m_caster->GetPower(POWER_RAGE)-rage); + m_caster->SetPower(POWER_RAGE, new_rage); return; } // Slam @@ -2112,6 +2133,9 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) if (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && (spellInfo->SpellFamilyFlags & UI64LIT(0x0000024000000860))) ((Player*)m_caster)->RemoveSpellCooldown((itr++)->first,true); + // Glyph of Preparation + else if (m_caster->HasAura(56819) && (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && (spellInfo->SpellFamilyFlags & 0x40000010 || spellInfo->Id == 51722))) + ((Player*)m_caster)->RemoveSpellCooldown((itr++)->first,true); else ++itr; } @@ -2122,6 +2146,16 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) m_caster->CastSpell(m_caster, 45182, true); return; } + case 51662: // Hunger for Blood + { + m_caster->CastSpell(m_caster, 63848, true); + return; + } + case 51690: // Killing Spree + { + m_caster->CastSpell(m_caster, 61851, true); + return; + } } break; } @@ -2354,6 +2388,8 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) { if (Unit *owner = m_caster->GetOwner()) { + damage += int32(m_caster->GetOwner()->SpellDamageBonusDone(unitTarget, m_spellInfo, 0, HEAL) * 0.45f); + // Restorative Totems Unit::AuraList const& mDummyAuras = owner->GetAurasByType(SPELL_AURA_DUMMY); for(Unit::AuraList::const_iterator i = mDummyAuras.begin(); i != mDummyAuras.end(); ++i) @@ -2521,6 +2557,24 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) m_caster->CastCustomSpell(m_caster, 45470, &bp, NULL, NULL, true); return; } + // Death Grip + else if (m_spellInfo->Id == 49576) + { + if (!unitTarget) + return; + + m_caster->CastSpell(unitTarget, 49560, true); + return; + } + else if (m_spellInfo->Id == 49560) + { + if (!unitTarget) + return; + + uint32 spellId = m_spellInfo->CalculateSimpleValue(EFFECT_INDEX_0); + unitTarget->CastSpell(m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ(), spellId, true); + return; + } break; } } @@ -3654,7 +3708,7 @@ void Spell::EffectOpenLock(SpellEffectIndex eff_idx) if (BattleGround *bg = player->GetBattleGround()) { // check if it's correct bg - if (bg->GetTypeID() == BATTLEGROUND_AB || bg->GetTypeID() == BATTLEGROUND_AV) + if (bg->GetTypeID(true) == BATTLEGROUND_AB || bg->GetTypeID(true) == BATTLEGROUND_AV) bg->EventPlayerClickedOnFlag(player, gameObjTarget); return; } @@ -3665,7 +3719,7 @@ void Spell::EffectOpenLock(SpellEffectIndex eff_idx) // in battleground check if (BattleGround *bg = player->GetBattleGround()) { - if (bg->GetTypeID() == BATTLEGROUND_EY) + if (bg->GetTypeID(true) == BATTLEGROUND_EY) bg->EventPlayerClickedOnFlag(player, gameObjTarget); return; } @@ -4182,6 +4236,11 @@ void Spell::EffectDispel(SpellEffectIndex eff_idx) { int32 heal_amount = m_spellInfo->CalculateSimpleValue(EFFECT_INDEX_1); m_caster->CastCustomSpell(m_caster, 19658, &heal_amount, NULL, NULL, true); + + // Glyph of Felhunter + if (Unit *owner = m_caster->GetOwner()) + if (owner->HasAura(56249)) + m_caster->CastCustomSpell(owner, 19658, &heal_amount, NULL, NULL, true); } } // Send fail log to client @@ -5041,6 +5100,27 @@ void Spell::EffectWeaponDmg(SpellEffectIndex eff_idx) } break; } + case SPELLFAMILY_DRUID: + { + // Rend and Tear ( on Maul / Shred ) + if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000008800)) + { + if(unitTarget && unitTarget->HasAuraState(AURA_STATE_MECHANIC_BLEED)) + { + Unit::AuraList const& aura = m_caster->GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraList::const_iterator itr = aura.begin(); itr != aura.end(); ++itr) + { + if ((*itr)->GetSpellProto()->SpellIconID == 2859 && (*itr)->GetEffIndex() == 0) + { + totalDamagePercentMod += (totalDamagePercentMod * (*itr)->GetModifier()->m_amount) / 100; + break; + } + } + } + } + + break; + } case SPELLFAMILY_WARRIOR: { // Devastate bonus and sunder armor refresh @@ -5218,6 +5298,12 @@ void Spell::EffectWeaponDmg(SpellEffectIndex eff_idx) { totalDamagePercentMod *= 1.2f; } + // Rune strike + if( m_spellInfo->SpellIconID == 3007) + { + int32 count = CalculateDamage(EFFECT_INDEX_2, unitTarget); + spell_bonus += int32(count * m_caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100.0f); + } break; } } @@ -5342,7 +5428,7 @@ void Spell::EffectHealMaxHealth(SpellEffectIndex /*eff_idx*/) m_healing += heal; } -void Spell::EffectInterruptCast(SpellEffectIndex /*eff_idx*/) +void Spell::EffectInterruptCast(SpellEffectIndex eff_idx) { if(!unitTarget) return; @@ -5359,7 +5445,7 @@ void Spell::EffectInterruptCast(SpellEffectIndex /*eff_idx*/) // check if we can interrupt spell if ((curSpellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_INTERRUPT) && curSpellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE ) { - unitTarget->ProhibitSpellSchool(GetSpellSchoolMask(curSpellInfo), GetSpellDuration(m_spellInfo)); + unitTarget->ProhibitSpellSchool(GetSpellSchoolMask(curSpellInfo), unitTarget->CalculateSpellDuration(m_spellInfo, eff_idx, unitTarget)); unitTarget->InterruptSpell(CurrentSpellTypes(i),false); } } @@ -5412,7 +5498,7 @@ void Spell::EffectSummonObjectWild(SpellEffectIndex eff_idx) { case 489: //WS { - if(bg && bg->GetTypeID()==BATTLEGROUND_WS && bg->GetStatus() == STATUS_IN_PROGRESS) + if(bg && bg->GetTypeID(true)==BATTLEGROUND_WS && bg->GetStatus() == STATUS_IN_PROGRESS) { uint32 team = ALLIANCE; @@ -5425,7 +5511,7 @@ void Spell::EffectSummonObjectWild(SpellEffectIndex eff_idx) } case 566: //EY { - if(bg && bg->GetTypeID()==BATTLEGROUND_EY && bg->GetStatus() == STATUS_IN_PROGRESS) + if(bg && bg->GetTypeID(true)==BATTLEGROUND_EY && bg->GetStatus() == STATUS_IN_PROGRESS) { ((BattleGroundEY*)bg)->SetDroppedFlagGUID(pGameObj->GetGUID()); } @@ -5447,6 +5533,38 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx) { switch(m_spellInfo->Id) { + case 6962: + { + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + Player* plr = ((Player*)m_caster); + if(plr && plr->GetLastPetNumber()) + { + PetType NewPetType = (plr->getClass()==CLASS_HUNTER) ? HUNTER_PET : SUMMON_PET; + if (Pet* NewPet = new Pet(NewPetType)) + { + if(NewPet->LoadPetFromDB(plr, 0, plr->GetLastPetNumber(), true)) + { + NewPet->SetHealth(NewPet->GetMaxHealth()); + NewPet->SetPower(NewPet->getPowerType(),NewPet->GetMaxPower(NewPet->getPowerType())); + + switch (NewPet->GetEntry()) + { + case 11859: + case 89: + NewPet->SetEntry(416); + break; + default: + break; + } + } + else + delete NewPet; + } + } + return; + } case 8856: // Bending Shinbone { if (!itemTarget && m_caster->GetTypeId()!=TYPEID_PLAYER) @@ -6042,6 +6160,31 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx) unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOD_STUN); return; } + case 55328: // Stoneclaw Totem I + case 55329: // Stoneclaw Totem II + case 55330: // Stoneclaw Totem III + case 55332: // Stoneclaw Totem IV + case 55333: // Stoneclaw Totem V + case 55335: // Stoneclaw Totem VI + case 55278: // Stoneclaw Totem VII + case 58589: // Stoneclaw Totem VIII + case 58590: // Stoneclaw Totem IX + case 58591: // Stoneclaw Totem X + { + if (!unitTarget) // Stoneclaw Totem owner + return; + // Absorb shield for totems + for(int itr = 0; itr < MAX_TOTEM_SLOT; ++itr) + if (Totem* totem = unitTarget->GetTotem(TotemSlot(itr))) + m_caster->CastCustomSpell(totem, 55277, &damage, NULL, NULL, true); + // Glyph of Stoneclaw Totem + if(Aura* auraGlyph = unitTarget->GetAura(63298, EFFECT_INDEX_0)) + { + int32 playerAbsorb = damage * auraGlyph->GetModifier()->m_amount; + m_caster->CastCustomSpell(unitTarget, 55277, &playerAbsorb, NULL, NULL, true); + } + return; + } case 55693: // Remove Collapsing Cave Aura { if (!unitTarget) @@ -6382,7 +6525,7 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx) } if (spellId) - m_caster->CastCustomSpell(target, spellId, &basePoint, 0, 0, false); + m_caster->CastCustomSpell(target, spellId, &basePoint, 0, 0, true); return; } @@ -6558,6 +6701,27 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx) } break; } + case SPELLFAMILY_WARRIOR: + { + switch(m_spellInfo->Id) + { + case 64380: // Shattering Throw + { + if (!unitTarget || !unitTarget->isAlive()) + return; + + // remove immunity effects + unitTarget->RemoveAurasDueToSpell(642); // Divine Shield + unitTarget->RemoveAurasDueToSpell(1022); // Hand of Protection rank 1 + unitTarget->RemoveAurasDueToSpell(5599); // Hand of Protection rank 2 + unitTarget->RemoveAurasDueToSpell(10278); // Hand of Protection rank 3 + unitTarget->RemoveAurasDueToSpell(19753); // Divine Intervention + unitTarget->RemoveAurasDueToSpell(45438); // Ice Block + break; + } + } + break; + } } // normal DB scripted effect @@ -6701,8 +6865,7 @@ void Spell::EffectStuck(SpellEffectIndex /*eff_idx*/) if(pTarget->IsTaxiFlying()) return; - // homebind location is loaded always - pTarget->TeleportToHomebind(unitTarget==m_caster ? TELE_TO_SPELL : 0); + pTarget->RepopAtGraveyard(); // Stuck spell trigger Hearthstone cooldown SpellEntry const *spellInfo = sSpellStore.LookupEntry(8690); @@ -6828,6 +6991,9 @@ void Spell::DoSummonTotem(SpellEffectIndex eff_idx, uint8 slot_dbc) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration); pTotem->SetDuration(duration); + if (m_spellInfo->Id == 16190) + damage = m_caster->GetMaxHealth() * m_spellInfo->CalculateSimpleValue(EFFECT_INDEX_1) / 100; + if (damage) // if not spell info, DB values used { pTotem->SetMaxHealth(damage); @@ -7007,7 +7173,16 @@ void Spell::EffectSummonObject(SpellEffectIndex eff_idx) } // Summon in random point all other units if location present else - m_caster->GetClosePoint(x, y, z, DEFAULT_WORLD_OBJECT_SIZE); + { + if(m_spellInfo->Id == 48018) + { + x = m_caster->GetPositionX(); + y = m_caster->GetPositionY(); + z = m_caster->GetPositionZ(); + } + else + m_caster->GetClosePoint(x, y, z, DEFAULT_WORLD_OBJECT_SIZE); + } Map *map = m_caster->GetMap(); if(!pGameObj->Create(sObjectMgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), go_id, map, @@ -7106,24 +7281,78 @@ void Spell::EffectLeapForward(SpellEffectIndex eff_idx) if( m_spellInfo->rangeIndex == 1) //self range { + uint32 mapid = m_caster->GetMapId(); float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[eff_idx])); - - // before caster - float fx, fy, fz; - unitTarget->GetClosePoint(fx, fy, fz, unitTarget->GetObjectBoundingRadius(), dis); - float ox, oy, oz; - unitTarget->GetPosition(ox, oy, oz); - - float fx2, fy2, fz2; // getObjectHitPos overwrite last args in any result case - if(VMAP::VMapFactory::createOrGetVMapManager()->getObjectHitPos(unitTarget->GetMapId(), ox,oy,oz+0.5f, fx,fy,oz+0.5f,fx2,fy2,fz2, -0.5f)) + //For glyph of blink + if(m_caster->GetTypeId() == TYPEID_PLAYER) + ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, dis, this); + + // Start Info // + float cx,cy,cz; + float dx,dy,dz; + float angle = unitTarget->GetOrientation(); + unitTarget->GetPosition(cx,cy,cz); + + //Check use of vmaps// + bool useVmap = false; + bool swapZone = true; + + if( unitTarget->GetMap()->GetHeight(cx, cy, cz, false) < unitTarget->GetMap()->GetHeight(cx, cy, cz, true) ) + useVmap = true; + + const int itr = int(dis/0.5f); + const float _dx = 0.5f * cos(angle); + const float _dy = 0.5f * sin(angle); + dx = cx; + dy = cy; + + //Going foward 0.5f until max distance + for(float i=0.5f; iUpdateGroundPositionZ(fx, fy, fz); + //unitTarget->GetNearPoint2D(dx,dy,i,angle); + dx += _dx; + dy += _dy; + MaNGOS::NormalizeMapCoord(dx); + MaNGOS::NormalizeMapCoord(dy); + dz = cz; + + //Prevent climbing and go around object maybe 2.0f is to small? use 3.0f? + if( unitTarget->GetMap()->IsNextZcoordOK(dx, dy, dz, 3.0f) && (unitTarget->IsWithinLOS(dx, dy, dz))) + { + //No climb, the z differenze between this and prev step is ok. Store this destination for future use or check. + cx = dx; + cy = dy; + unitTarget->UpdateGroundPositionZ(cx, cy, cz, 3.0f); + } + else + { + //Something wrong with los or z differenze... maybe we are going from outer world inside a building or viceversa + if(swapZone) + { + //so... change use of vamp and go back 1 step backward and recheck again. + swapZone = false; + useVmap = !useVmap; + //i-=0.5f; + --i; + dx -= _dx; + dy -= _dy; + } + else + { + //bad recheck result... so break this and use last good coord for teleport player... + dz += 0.5f; + break; + } + } } + + //Prevent Falling during swap building/outerspace + unitTarget->UpdateGroundPositionZ(cx, cy, cz); - unitTarget->NearTeleportTo(fx, fy, fz, unitTarget->GetOrientation(), unitTarget == m_caster); + if(unitTarget->GetTypeId() == TYPEID_PLAYER) + ((Player*)unitTarget)->TeleportTo(mapid, cx, cy, cz, unitTarget->GetOrientation(), TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget==m_caster ? TELE_TO_SPELL : 0)); + else + unitTarget->GetMap()->CreatureRelocation((Creature*)unitTarget, cx, cy, cz, unitTarget->GetOrientation()); } } @@ -7233,7 +7462,10 @@ void Spell::EffectCharge(SpellEffectIndex /*eff_idx*/) //TODO: research more ContactPoint/attack distance. //3.666666 instead of ATTACK_DISTANCE(5.0f) in below seem to give more accurate result. float x, y, z; - unitTarget->GetContactPoint(m_caster, x, y, z, 3.666666f); + unitTarget->GetContactPoint(m_caster, x, y, z, 3.6f); + + // Try to normalize Z coord cuz GetContactPoint do nothing with Z axis + unitTarget->UpdateGroundPositionZ(x, y, z, 5.0f); if (unitTarget->GetTypeId() != TYPEID_PLAYER) ((Creature *)unitTarget)->StopMoving(); @@ -7259,10 +7491,13 @@ void Spell::EffectCharge2(SpellEffectIndex /*eff_idx*/) ((Creature *)unitTarget)->StopMoving(); } else if (unitTarget && unitTarget != m_caster) - unitTarget->GetContactPoint(m_caster, x, y, z, 3.666666f); + unitTarget->GetContactPoint(m_caster, x, y, z, 3.6f); else return; + // Try to normalize Z coord cuz GetContactPoint do nothing with Z axis + unitTarget->UpdateGroundPositionZ(x, y, z, 5.0f); + // Only send MOVEMENTFLAG_WALK_MODE, client has strange issues with other move flags m_caster->MonsterMove(x, y, z, 1); @@ -7376,11 +7611,7 @@ void Spell::EffectPlayerPull(SpellEffectIndex eff_idx) if(!unitTarget) return; - float dist = unitTarget->GetDistance2d(m_caster); - if (damage && dist > damage) - dist = float(damage); - - unitTarget->KnockBackFrom(m_caster,-dist,float(m_spellInfo->EffectMiscValue[eff_idx])/10); + unitTarget->KnockBackFrom(m_caster, -unitTarget->GetDistance2d(m_caster), float(m_spellInfo->EffectMiscValue[eff_idx])/30); } void Spell::EffectDispelMechanic(SpellEffectIndex eff_idx) @@ -7443,7 +7674,8 @@ void Spell::EffectSummonAllTotems(SpellEffectIndex eff_idx) if (ActionButton const* actionButton = ((Player*)m_caster)->GetActionButton(start_button+slot)) if (actionButton->GetType()==ACTION_BUTTON_SPELL) if (uint32 spell_id = actionButton->GetAction()) - m_caster->CastSpell(unitTarget,spell_id,true); + if (!((Player*)m_caster)->HasSpellCooldown(spell_id)) + m_caster->CastSpell(unitTarget, spell_id, true); } void Spell::EffectDestroyAllTotems(SpellEffectIndex /*eff_idx*/) @@ -7717,6 +7949,8 @@ void Spell::EffectSpiritHeal(SpellEffectIndex /*eff_idx*/) ((Player*)unitTarget)->ResurrectPlayer(1.0f); ((Player*)unitTarget)->SpawnCorpseBones(); + + ((Player*)unitTarget)->CastSpell(unitTarget, 6962, true); } // remove insignia spell effect @@ -7753,29 +7987,33 @@ void Spell::EffectStealBeneficialBuff(SpellEffectIndex eff_idx) // Ok if exist some buffs for dispel try dispel it if (!steal_list.empty()) { - std::list < std::pair > success_list; - int32 list_size = steal_list.size(); + std::list < std::pair > success_list;// (spell_id,casterGuid) + std::list < uint32 > fail_list; // spell_id // Dispell 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 && !steal_list.empty(); ++count) { // Random select buff for dispel - SpellAuraHolder *holder = steal_list[urand(0, list_size-1)]; - // Not use chance for steal - // TODO possible need do it - success_list.push_back( std::pair(holder->GetId(),holder->GetCasterGUID())); + std::vector::iterator steal_itr = steal_list.begin(); + std::advance(steal_itr,urand(0, steal_list.size()-1)); - // Remove buff from list for prevent doubles - for (std::vector::iterator j = steal_list.begin(); j != steal_list.end(); ) - { - SpellAuraHolder *stealed = *j; - if (stealed->GetId() == holder->GetId() && stealed->GetCasterGUID() == holder->GetCasterGUID()) - { - j = steal_list.erase(j); - --list_size; - } - else - ++j; - } + SpellAuraHolder *aur = *steal_itr; + // remove entry from steal_list + steal_list.erase(steal_itr); + + SpellEntry const* spellInfo = aur->GetSpellProto(); + // Base dispel chance + int32 miss_chance = 0; + // Apply dispel mod from aura caster + if (Unit *caster = aur->GetCaster()) + { + if ( Player* modOwner = caster->GetSpellModOwner() ) + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_RESIST_DISPEL_CHANCE, miss_chance, this); + } + // Try dispel + if (roll_chance_i(miss_chance)) + fail_list.push_back(spellInfo->Id); + else + success_list.push_back(std::pair(aur->GetId(),aur->GetCasterGUID())); } // Really try steal and send log if (!success_list.empty()) @@ -7796,6 +8034,18 @@ void Spell::EffectStealBeneficialBuff(SpellEffectIndex eff_idx) } m_caster->SendMessageToSet(&data, true); } + // Send fail log to client + if (!fail_list.empty()) + { + // Failed to steal + 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); // Steal 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); + } } } diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp index ebb9818..05014f0 100644 --- a/src/game/SpellMgr.cpp +++ b/src/game/SpellMgr.cpp @@ -627,6 +627,19 @@ bool IsPositiveEffect(uint32 spellId, SpellEffectIndex effIndex) SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId); if (!spellproto) return false; + switch(spellId) + { + case 47540: // Penance start dummy aura - Rank 1 + case 53005: // Penance start dummy aura - Rank 2 + case 53006: // Penance start dummy aura - Rank 3 + case 53007: // Penance start dummy aura - Rank 4 + case 47757: // Penance heal effect trigger - Rank 1 + case 52986: // Penance heal effect trigger - Rank 2 + case 52987: // Penance heal effect trigger - Rank 3 + case 52988: // Penance heal effect trigger - Rank 4 + return true; + } + switch(spellproto->Effect[effIndex]) { case SPELL_EFFECT_DUMMY: @@ -1717,124 +1730,55 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons switch(spellInfo_1->SpellFamilyName) { case SPELLFAMILY_GENERIC: - switch(spellInfo_2->SpellFamilyName) + if (spellInfo_2->SpellFamilyName == SPELLFAMILY_GENERIC) { - case SPELLFAMILY_GENERIC: // same family case - { - // Thunderfury - if ((spellInfo_1->Id == 21992 && spellInfo_2->Id == 27648) || - (spellInfo_2->Id == 21992 && spellInfo_1->Id == 27648)) - return false; - - // Lightning Speed (Mongoose) and Fury of the Crashing Waves (Tsunami Talisman) - if ((spellInfo_1->Id == 28093 && spellInfo_2->Id == 42084) || - (spellInfo_2->Id == 28093 && spellInfo_1->Id == 42084)) - return false; - - // Soulstone Resurrection and Twisting Nether (resurrector) - if( spellInfo_1->SpellIconID == 92 && spellInfo_2->SpellIconID == 92 && ( - spellInfo_1->SpellVisual[0] == 99 && spellInfo_2->SpellVisual[0] == 0 || - spellInfo_2->SpellVisual[0] == 99 && spellInfo_1->SpellVisual[0] == 0 ) ) - return false; - - // Heart of the Wild, Agility and various Idol Triggers - if(spellInfo_1->SpellIconID == 240 && spellInfo_2->SpellIconID == 240) - return false; - - // Personalized Weather (thunder effect should overwrite rainy aura) - if(spellInfo_1->SpellIconID == 2606 && spellInfo_2->SpellIconID == 2606) - return false; - - // Brood Affliction: Bronze - if( (spellInfo_1->Id == 23170 && spellInfo_2->Id == 23171) || - (spellInfo_2->Id == 23170 && spellInfo_1->Id == 23171) ) - return false; - - // Cool Down (See PeriodicAuraTick()) - if ((spellInfo_1->Id == 52441 && spellInfo_2->Id == 52443) || - (spellInfo_2->Id == 52441 && spellInfo_1->Id == 52443)) - return false; - - // See Chapel Invisibility and See Noth Invisibility - if( (spellInfo_1->Id == 52950 && spellInfo_2->Id == 52707) || - (spellInfo_2->Id == 52950 && spellInfo_1->Id == 52707) ) - return false; - - // Regular and Night Elf Ghost - if( (spellInfo_1->Id == 8326 && spellInfo_2->Id == 20584) || - (spellInfo_2->Id == 8326 && spellInfo_1->Id == 20584) ) - return false; - - // Kindred Spirits - if( spellInfo_1->SpellIconID == 3559 && spellInfo_2->SpellIconID == 3559 ) - return false; - - break; - } - case SPELLFAMILY_MAGE: - // Arcane Intellect and Insight - if( spellInfo_2->SpellIconID == 125 && spellInfo_1->Id == 18820 ) - return false; - break; - case SPELLFAMILY_WARRIOR: - { - // Scroll of Protection and Defensive Stance (multi-family check) - if( spellInfo_1->SpellIconID == 276 && spellInfo_1->SpellVisual[0] == 196 && spellInfo_2->Id == 71) - return false; + // Thunderfury + if ((spellInfo_1->Id == 21992 && spellInfo_2->Id == 27648) || + (spellInfo_2->Id == 21992 && spellInfo_1->Id == 27648)) + return false; - // Improved Hamstring -> Hamstring (multi-family check) - if( (spellInfo_2->SpellFamilyFlags & UI64LIT(0x2)) && spellInfo_1->Id == 23694 ) - return false; + // Lightning Speed (Mongoose) and Fury of the Crashing Waves (Tsunami Talisman) + if ((spellInfo_1->Id == 28093 && spellInfo_2->Id == 42084) || + (spellInfo_2->Id == 28093 && spellInfo_1->Id == 42084)) + return false; - break; - } - case SPELLFAMILY_DRUID: - { - // Scroll of Stamina and Leader of the Pack (multi-family check) - if( spellInfo_1->SpellIconID == 312 && spellInfo_1->SpellVisual[0] == 216 && spellInfo_2->Id == 24932 ) - return false; + // Soulstone Resurrection and Twisting Nether (resurrector) + if( spellInfo_1->SpellIconID == 92 && spellInfo_2->SpellIconID == 92 && ( + spellInfo_1->SpellVisual[0] == 99 && spellInfo_2->SpellVisual[0] == 0 || + spellInfo_2->SpellVisual[0] == 99 && spellInfo_1->SpellVisual[0] == 0 ) ) + return false; - // Dragonmaw Illusion (multi-family check) - if (spellId_1 == 40216 && spellId_2 == 42016 ) - return false; + // Heart of the Wild, Agility and various Idol Triggers + if(spellInfo_1->SpellIconID == 240 && spellInfo_2->SpellIconID == 240) + return false; - break; - } - case SPELLFAMILY_ROGUE: - { - // Garrote-Silence -> Garrote (multi-family check) - if( spellInfo_1->SpellIconID == 498 && spellInfo_1->SpellVisual[0] == 0 && spellInfo_2->SpellIconID == 498 ) - return false; + // Personalized Weather (thunder effect should overwrite rainy aura) + if(spellInfo_1->SpellIconID == 2606 && spellInfo_2->SpellIconID == 2606) + return false; - break; - } - case SPELLFAMILY_HUNTER: - { - // Concussive Shot and Imp. Concussive Shot (multi-family check) - if( spellInfo_1->Id == 19410 && spellInfo_2->Id == 5116 ) - return false; + // Brood Affliction: Bronze + if( (spellInfo_1->Id == 23170 && spellInfo_2->Id == 23171) || + (spellInfo_2->Id == 23170 && spellInfo_1->Id == 23171) ) + return false; - // Improved Wing Clip -> Wing Clip (multi-family check) - if( (spellInfo_2->SpellFamilyFlags & UI64LIT(0x40)) && spellInfo_1->Id == 19229 ) - return false; - break; - } - case SPELLFAMILY_PALADIN: - { - // Unstable Currents and other -> *Sanctity Aura (multi-family check) - if( spellInfo_2->SpellIconID==502 && spellInfo_1->SpellIconID==502 && spellInfo_1->SpellVisual[0]==969 ) - return false; + // Cool Down (See PeriodicAuraTick()) + if ((spellInfo_1->Id == 52441 && spellInfo_2->Id == 52443) || + (spellInfo_2->Id == 52441 && spellInfo_1->Id == 52443)) + return false; - // *Band of Eternal Champion and Seal of Command(multi-family check) - if( spellId_1 == 35081 && spellInfo_2->SpellIconID==561 && spellInfo_2->SpellVisual[0]==7992) - return false; + // See Chapel Invisibility and See Noth Invisibility + if( (spellInfo_1->Id == 52950 && spellInfo_2->Id == 52707) || + (spellInfo_2->Id == 52950 && spellInfo_1->Id == 52707) ) + return false; - // Blessing of Sanctuary (multi-family check, some from 16 spell icon spells) - if (spellInfo_1->Id == 67480 && spellInfo_2->Id == 20911) - return false; + // Regular and Night Elf Ghost + if( (spellInfo_1->Id == 8326 && spellInfo_2->Id == 20584) || + (spellInfo_2->Id == 8326 && spellInfo_1->Id == 20584) ) + return false; - break; - } + // Kindred Spirits + if( spellInfo_1->SpellIconID == 3559 && spellInfo_2->SpellIconID == 3559 ) + return false; } // Dragonmaw Illusion, Blood Elf Illusion, Human Illusion, Illidari Agent Illusion, Scarlet Crusade Disguise if(spellInfo_1->SpellIconID == 1691 && spellInfo_2->SpellIconID == 1691) @@ -1853,6 +1797,10 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons (spellInfo_2->SpellFamilyFlags & UI64LIT(0x0000000000010000)) && (spellInfo_1->SpellVisual[0] == 72 && spellInfo_1->SpellIconID == 1499) ) return false; + // Fingers of Frost effects + if( spellInfo_1->SpellIconID == 2947 && spellInfo_2->SpellIconID == 2947) + return false; + // Living Bomb & Ignite (Dots) if( (spellInfo_1->SpellFamilyFlags & UI64LIT(0x2000000000000)) && (spellInfo_2->SpellFamilyFlags & UI64LIT(0x8000000)) || (spellInfo_2->SpellFamilyFlags & UI64LIT(0x2000000000000)) && (spellInfo_1->SpellFamilyFlags & UI64LIT(0x8000000)) ) @@ -1863,18 +1811,6 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons (spellInfo_2->SpellFamilyFlags & UI64LIT(0x1)) && (spellInfo_1->SpellFamilyFlags & UI64LIT(0x400000)) ) return false; } - // Detect Invisibility and Mana Shield (multi-family check) - if( spellInfo_2->Id == 132 && spellInfo_1->SpellIconID == 209 && spellInfo_1->SpellVisual[0] == 968 ) - return false; - - // Combustion and Fire Protection Aura (multi-family check) - if( spellInfo_1->Id == 11129 && spellInfo_2->SpellIconID == 33 && spellInfo_2->SpellVisual[0] == 321 ) - return false; - - // Arcane Intellect and Insight - if( spellInfo_1->SpellIconID == 125 && spellInfo_2->Id == 18820 ) - return false; - break; case SPELLFAMILY_WARLOCK: if( spellInfo_2->SpellFamilyName == SPELLFAMILY_WARLOCK ) @@ -1903,10 +1839,11 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons // Metamorphosis, diff effects if (spellInfo_1->SpellIconID == 3314 && spellInfo_2->SpellIconID == 3314) return false; + + // Nether Protection effects + if( spellInfo_2->SpellIconID == 1985 && spellInfo_1->SpellIconID == 1985 && spellInfo_1->SpellVisual[0] == 9750 ) + return false; } - // Detect Invisibility and Mana Shield (multi-family check) - if( spellInfo_1->Id == 132 && spellInfo_2->SpellIconID == 209 && spellInfo_2->SpellVisual[0] == 968 ) - return false; break; case SPELLFAMILY_WARRIOR: if( spellInfo_2->SpellFamilyName == SPELLFAMILY_WARRIOR ) @@ -1920,20 +1857,12 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons if( (spellInfo_1->SpellIconID == 456 && spellInfo_2->SpellIconID == 2006) || (spellInfo_2->SpellIconID == 456 && spellInfo_1->SpellIconID == 2006) ) return false; - } - - // Hamstring -> Improved Hamstring (multi-family check) - if( (spellInfo_1->SpellFamilyFlags & UI64LIT(0x2)) && spellInfo_2->Id == 23694 ) - return false; - - // Defensive Stance and Scroll of Protection (multi-family check) - if( spellInfo_1->Id == 71 && spellInfo_2->SpellIconID == 276 && spellInfo_2->SpellVisual[0] == 196 ) - return false; - - // Bloodlust and Bloodthirst (multi-family check) - if( spellInfo_2->Id == 2825 && spellInfo_1->SpellIconID == 38 && spellInfo_1->SpellVisual[0] == 0 ) - return false; + // Taste of Blood and Sudden Death + if( (spellInfo_1->Id == 52437 && spellInfo_2->Id == 60503) || + (spellInfo_2->Id == 52437 && spellInfo_1->Id == 60503) ) + return false; + } break; case SPELLFAMILY_PRIEST: if( spellInfo_2->SpellFamilyName == SPELLFAMILY_PRIEST ) @@ -1951,6 +1880,10 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons if ((spellInfo_1->Id == 47585 && spellInfo_2->Id == 60069) || (spellInfo_2->Id == 47585 && spellInfo_1->Id == 60069)) return false; + // Power Word: Shield and Divine Aegis + if ((spellInfo_1->SpellIconID == 566 && spellInfo_2->SpellIconID == 2820) || + (spellInfo_2->SpellIconID == 566 && spellInfo_1->SpellIconID == 2820)) + return false; } break; case SPELLFAMILY_DRUID: @@ -1975,6 +1908,11 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons if (spellInfo_1->SpellIconID == 62 && spellInfo_2->SpellIconID == 62) return false; + // Lacerate and Moonfire + if((spellInfo_1->SpellIconID == 225 && spellInfo_2->SpellIconID == 2246) || + (spellInfo_2->SpellIconID == 225 && spellInfo_1->SpellIconID == 2246)) + return false; + // Wrath of Elune and Nature's Grace if( spellInfo_1->Id == 16886 && spellInfo_2->Id == 46833 || spellInfo_2->Id == 16886 && spellInfo_1->Id == 46833 ) return false; @@ -1999,15 +1937,6 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons if( spellInfo_1->Id == 22842 && spellInfo_2->Id == 62606 || spellInfo_2->Id == 22842 && spellInfo_1->Id == 62606 ) return false; } - - // Leader of the Pack and Scroll of Stamina (multi-family check) - if( spellInfo_1->Id == 24932 && spellInfo_2->SpellIconID == 312 && spellInfo_2->SpellVisual[0] == 216 ) - return false; - - // Dragonmaw Illusion (multi-family check) - if (spellId_1 == 42016 && spellId_2 == 40216 ) - return false; - break; case SPELLFAMILY_ROGUE: if( spellInfo_2->SpellFamilyName == SPELLFAMILY_ROGUE ) @@ -2022,14 +1951,6 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons spellInfo_2->Category == 44 && spellInfo_1->Category == 0)) return false; } - - //Overkill - if( spellInfo_1->SpellIconID == 2285 && spellInfo_2->SpellIconID == 2285 ) - return false; - - // Garrote -> Garrote-Silence (multi-family check) - if( spellInfo_1->SpellIconID == 498 && spellInfo_2->SpellIconID == 498 && spellInfo_2->SpellVisual[0] == 0 ) - return false; break; case SPELLFAMILY_HUNTER: if( spellInfo_2->SpellFamilyName == SPELLFAMILY_HUNTER ) @@ -2044,18 +1965,14 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons (spellInfo_2->SpellFamilyFlags & UI64LIT(0x4)) && (spellInfo_1->SpellFamilyFlags & UI64LIT(0x00000004000)) ) return false; + // Deterrence + if( spellInfo_1->SpellIconID == 83 && spellInfo_2->SpellIconID == 83 ) + return false; + // Bestial Wrath if( spellInfo_1->SpellIconID == 1680 && spellInfo_2->SpellIconID == 1680 ) return false; } - - // Wing Clip -> Improved Wing Clip (multi-family check) - if( (spellInfo_1->SpellFamilyFlags & UI64LIT(0x40)) && spellInfo_2->Id == 19229 ) - return false; - - // Concussive Shot and Imp. Concussive Shot (multi-family check) - if( spellInfo_2->Id == 19410 && spellInfo_1->Id == 5116 ) - return false; break; case SPELLFAMILY_PALADIN: if( spellInfo_2->SpellFamilyName == SPELLFAMILY_PALADIN ) @@ -2064,6 +1981,10 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons if (IsSealSpell(spellInfo_1) && IsSealSpell(spellInfo_2)) return true; + // Repentance removes Righteous Vengeance + if (spellInfo_1->Id == 20066 && spellInfo_2->Id == 61840) + return true; + // Swift Retribution / Improved Devotion Aura (talents) and Paladin Auras if ((spellInfo_1->SpellFamilyFlags2 & 0x00000020) && (spellInfo_2->SpellIconID == 291 || spellInfo_2->SpellIconID == 3028) || (spellInfo_2->SpellFamilyFlags2 & 0x00000020) && (spellInfo_1->SpellIconID == 291 || spellInfo_1->SpellIconID == 3028)) @@ -2085,22 +2006,6 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons if (spellInfo_1->SpellIconID == 3837 && spellInfo_2->SpellIconID == 3837) return false; } - - // Blessing of Sanctuary (multi-family check, some from 16 spell icon spells) - if (spellInfo_2->Id == 67480 && spellInfo_1->Id == 20911) - return false; - - // Combustion and Fire Protection Aura (multi-family check) - if( spellInfo_2->Id == 11129 && spellInfo_1->SpellIconID == 33 && spellInfo_1->SpellVisual[0] == 321 ) - return false; - - // *Sanctity Aura -> Unstable Currents and other (multi-family check) - if( spellInfo_1->SpellIconID==502 && spellInfo_2->SpellFamilyName == SPELLFAMILY_GENERIC && spellInfo_2->SpellIconID==502 && spellInfo_2->SpellVisual[0]==969 ) - return false; - - // *Seal of Command and Band of Eternal Champion (multi-family check) - if( spellInfo_1->SpellIconID==561 && spellInfo_1->SpellVisual[0]==7992 && spellId_2 == 35081) - return false; break; case SPELLFAMILY_SHAMAN: if( spellInfo_2->SpellFamilyName == SPELLFAMILY_SHAMAN ) @@ -2118,9 +2023,6 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons if (spellInfo_1->SpellIconID == 2019 && spellInfo_2->SpellIconID == 2019) return false; } - // Bloodlust and Bloodthirst (multi-family check) - if( spellInfo_1->Id == 2825 && spellInfo_2->SpellIconID == 38 && spellInfo_2->SpellVisual[0] == 0 ) - return false; break; case SPELLFAMILY_DEATHKNIGHT: if (spellInfo_2->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT) @@ -2146,6 +2048,12 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons break; } + if (spellInfo_1->SpellFamilyName == 0 || spellInfo_2->SpellFamilyName == 0) + return false; + + if (spellInfo_1->SpellFamilyName != spellInfo_2->SpellFamilyName) + return false; + // more generic checks if (spellInfo_1->SpellIconID == spellInfo_2->SpellIconID && spellInfo_1->SpellIconID != 0 && spellInfo_2->SpellIconID != 0) @@ -2167,12 +2075,6 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons if (IsRankSpellDueToSpell(spellInfo_1, spellId_2)) return true; - if (spellInfo_1->SpellFamilyName == 0 || spellInfo_2->SpellFamilyName == 0) - return false; - - if (spellInfo_1->SpellFamilyName != spellInfo_2->SpellFamilyName) - return false; - bool dummy_only = true; for (int i = 0; i < MAX_EFFECT_INDEX; ++i) { diff --git a/src/game/SpellMgr.h b/src/game/SpellMgr.h index 6ad666f..43ed1bb 100644 --- a/src/game/SpellMgr.h +++ b/src/game/SpellMgr.h @@ -967,6 +967,16 @@ class SpellMgr return false; } + uint8 IsHighestRankOfSpell(uint32 spell) const + { + SpellChainMapNext::const_iterator itr = mSpellChainsNext.find(spell); + + if (!(itr == mSpellChainsNext.end()) && (itr->second)) // the spell is in the chain list and a higher-rank spell is available + return false; + else + return true; + } + bool IsRankSpellDueToSpell(SpellEntry const *spellInfo_1,uint32 spellId_2) const; bool IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) const; bool canStackSpellRanksInSpellBook(SpellEntry const *spellInfo) const; diff --git a/src/game/StatSystem.cpp b/src/game/StatSystem.cpp index f6e30e1..89c1b94 100644 --- a/src/game/StatSystem.cpp +++ b/src/game/StatSystem.cpp @@ -875,7 +875,9 @@ bool Pet::UpdateStats(Stats stat) Unit *owner = GetOwner(); if ( stat == STAT_STAMINA ) { - if(owner) + if(owner && owner->GetTypeId() == TYPEID_PLAYER && owner->getClass() == CLASS_WARLOCK) + value += float(owner->GetStat(stat)) * 0.75f; + else if (owner) value += float(owner->GetStat(stat)) * 0.3f; } //warlock's and mage's pets gain 30% of owner's intellect @@ -956,10 +958,22 @@ void Pet::UpdateMaxHealth() { UnitMods unitMod = UNIT_MOD_HEALTH; float stamina = GetStat(STAT_STAMINA) - GetCreateStat(STAT_STAMINA); + float multiplicator; + + // nesocips warlock pet stats calculation + switch(GetEntry()) + { + case 416: multiplicator = 8.4f; break; // imp + case 1860: // voidwalker + case 17252: multiplicator = 11.0f; break; // felguard + case 1863: multiplicator = 9.1f; break; // succubus + case 417: multiplicator = 9.5f; break; // felhunter + default: multiplicator = 10.0f; break; + } float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth(); value *= GetModifierValue(unitMod, BASE_PCT); - value += GetModifierValue(unitMod, TOTAL_VALUE) + stamina * 10.0f; + value += GetModifierValue(unitMod, TOTAL_VALUE) + stamina * multiplicator; value *= GetModifierValue(unitMod, TOTAL_PCT); SetMaxHealth((uint32)value); @@ -970,10 +984,22 @@ void Pet::UpdateMaxPower(Powers power) UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power); float addValue = (power == POWER_MANA) ? GetStat(STAT_INTELLECT) - GetCreateStat(STAT_INTELLECT) : 0.0f; + float multiplicator; + + // nesocips warlock pet stats calculation + switch(GetEntry()) + { + case 416: multiplicator = 4.95f; break; // imp + case 1860: // voidwalker + case 1863: // succubus + case 417: // felhunter + case 17252: multiplicator = 11.5f; break; // felguard + default: multiplicator = 15.0f; break; + } float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreatePowers(power); value *= GetModifierValue(unitMod, BASE_PCT); - value += GetModifierValue(unitMod, TOTAL_VALUE) + addValue * 15.0f; + value += GetModifierValue(unitMod, TOTAL_VALUE) + addValue * multiplicator; value *= GetModifierValue(unitMod, TOTAL_PCT); SetMaxPower(power, uint32(value)); diff --git a/src/game/TemporarySummon.cpp b/src/game/TemporarySummon.cpp index 184ea0e..ac4f94e 100644 --- a/src/game/TemporarySummon.cpp +++ b/src/game/TemporarySummon.cpp @@ -104,18 +104,13 @@ void TemporarySummon::Update( uint32 diff ) return; } - if (!isInCombat()) + if (m_timer <= diff) { - if (m_timer <= diff) - { - UnSummon(); - return; - } - else - m_timer -= diff; + UnSummon(); + return; } - else if (m_timer != m_lifetime) - m_timer = m_lifetime; + else + m_timer -= diff; break; } case TEMPSUMMON_TIMED_OR_DEAD_DESPAWN: @@ -127,18 +122,13 @@ void TemporarySummon::Update( uint32 diff ) return; } - if (!isInCombat() && isAlive() ) + if (m_timer <= diff) { - if (m_timer <= diff) - { - UnSummon(); - return; - } - else - m_timer -= diff; + UnSummon(); + return; } - else if (m_timer != m_lifetime) - m_timer = m_lifetime; + else + m_timer -= diff; break; } default: diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index add667b..5197ec9 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -1227,11 +1227,10 @@ void Unit::CalculateSpellDamage(SpellNonMeleeDamage *damageInfo, int32 damage, S damageInfo->HitInfo|= SPELL_HIT_TYPE_CRIT; damage = SpellCriticalDamageBonus(spellInfo, damage, pVictim); // Resilience - reduce crit damage - uint32 redunction_affected_damage = CalcNotIgnoreDamageRedunction(damage,damageSchoolMask); if (attackType != RANGED_ATTACK) - damage -= pVictim->GetMeleeCritDamageReduction(redunction_affected_damage); + damage -= pVictim->GetMeleeCritDamageReduction(damage); else - damage -= pVictim->GetRangedCritDamageReduction(redunction_affected_damage); + damage -= pVictim->GetRangedCritDamageReduction(damage); } } break; @@ -1249,8 +1248,7 @@ void Unit::CalculateSpellDamage(SpellNonMeleeDamage *damageInfo, int32 damage, S damageInfo->HitInfo|= SPELL_HIT_TYPE_CRIT; damage = SpellCriticalDamageBonus(spellInfo, damage, pVictim); // Resilience - reduce crit damage - uint32 redunction_affected_damage = CalcNotIgnoreDamageRedunction(damage,damageSchoolMask); - damage -= pVictim->GetSpellCritDamageReduction(redunction_affected_damage); + damage -= pVictim->GetSpellCritDamageReduction(damage); } } break; @@ -1260,7 +1258,7 @@ void Unit::CalculateSpellDamage(SpellNonMeleeDamage *damageInfo, int32 damage, S if (GetTypeId() == TYPEID_PLAYER) { uint32 redunction_affected_damage = CalcNotIgnoreDamageRedunction(damage,damageSchoolMask); - damage -= pVictim->GetSpellDamageReduction(redunction_affected_damage); + damage -= pVictim->GetSpellDamageReduction(damage); } // damage mitigation @@ -1440,12 +1438,11 @@ void Unit::CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *da damageInfo->damage = int32((damageInfo->damage) * float((100.0f + mod)/100.0f)); // Resilience - reduce crit damage - uint32 redunction_affected_damage = CalcNotIgnoreDamageRedunction(damageInfo->damage,damageInfo->damageSchoolMask); uint32 resilienceReduction; if (attackType != RANGED_ATTACK) - resilienceReduction = pVictim->GetMeleeCritDamageReduction(redunction_affected_damage); + resilienceReduction = pVictim->GetMeleeCritDamageReduction(damageInfo->damage); else - resilienceReduction = pVictim->GetRangedCritDamageReduction(redunction_affected_damage); + resilienceReduction = pVictim->GetRangedCritDamageReduction(damageInfo->damage); damageInfo->damage -= resilienceReduction; damageInfo->cleanDamage += resilienceReduction; @@ -1559,12 +1556,11 @@ void Unit::CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *da // only from players if (GetTypeId() == TYPEID_PLAYER) { - uint32 redunction_affected_damage = CalcNotIgnoreDamageRedunction(damageInfo->damage,damageInfo->damageSchoolMask); uint32 resilienceReduction; if (attackType != RANGED_ATTACK) - resilienceReduction = pVictim->GetMeleeDamageReduction(redunction_affected_damage); + resilienceReduction = pVictim->GetMeleeDamageReduction(damageInfo->damage); else - resilienceReduction = pVictim->GetRangedDamageReduction(redunction_affected_damage); + resilienceReduction = pVictim->GetRangedDamageReduction(damageInfo->damage); damageInfo->damage -= resilienceReduction; damageInfo->cleanDamage += resilienceReduction; } @@ -1698,6 +1694,23 @@ void Unit::DealMeleeDamage(CalcDamageInfo *damageInfo, bool durabilityLoss) alreadyDone.insert(*i); uint32 damage=(*i)->GetModifier()->m_amount; SpellEntry const *i_spellProto = (*i)->GetSpellProto(); + + // Thorns + if (i_spellProto->SpellFamilyName == SPELLFAMILY_DRUID && i_spellProto->SpellFamilyFlags & UI64LIT(0x00000100)) + { + Unit::AuraList const& dummyList = pVictim->GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraList::const_iterator iter = dummyList.begin(); iter != dummyList.end(); ++iter) + { + // Brambles + if((*iter)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_DRUID && + (*iter)->GetSpellProto()->SpellIconID == 53) + { + damage += uint32(damage * (*iter)->GetModifier()->m_amount / 100); + break; + } + } + } + //Calculate absorb resist ??? no data in opcode for this possibly unable to absorb or resist? //uint32 absorb; //uint32 resist; @@ -2009,6 +2022,43 @@ void Unit::CalculateAbsorbAndResist(Unit *pCaster, SpellSchoolMask schoolMask, D } break; } + case SPELLFAMILY_PALADIN: + { + // Ardent Defender + if (spellProto->SpellIconID == 2135 && GetTypeId() == TYPEID_PLAYER) + { + int32 remainingHealth = GetHealth() - RemainingDamage; + uint32 allowedHealth = GetMaxHealth() * 0.35f; + // If damage kills us + if (remainingHealth <= 0 && !HasAura(66233)) + { + // Cast healing spell, completely avoid damage + RemainingDamage = 0; + + uint32 defenseSkillValue = GetDefenseSkillValue(); + // Max heal when defense skill denies critical hits from raid bosses + // Formula: max defense at level + 140 (raiting from gear) + uint32 reqDefForMaxHeal = getLevel() * 5 + 140; + float pctFromDefense = (defenseSkillValue >= reqDefForMaxHeal) + ? 1.0f + : float(defenseSkillValue) / float(reqDefForMaxHeal); + + int32 healAmount = GetMaxHealth() * ((*i)->GetSpellProto()->EffectBasePoints[1] + 1) / 100.0f * pctFromDefense; + CastSpell(this, 66233, true); + CastCustomSpell(this, 66235, &healAmount, NULL, NULL, true); + } + else if (remainingHealth < int32(allowedHealth)) + { + // Reduce damage that brings us under 35% (or full damage if we are already under 35%) by x% + uint32 damageToReduce = (GetHealth() < allowedHealth) + ? RemainingDamage + : allowedHealth - remainingHealth; + RemainingDamage -= damageToReduce * currentAbsorb / 100; + } + continue; + } + break; + } case SPELLFAMILY_PRIEST: { // Guardian Spirit @@ -2350,6 +2400,67 @@ void Unit::CalculateAbsorbResistBlock(Unit *pCaster, SpellNonMeleeDamage *damage damageInfo->damage-= damageInfo->absorb + damageInfo->resist; } +void Unit::CalculateHealAbsorb(Unit *pVictim, const SpellEntry *spellProto, uint32 &HealAmount, uint32 &Absorbed) +{ + int32 finalAmount = int32(HealAmount); + bool existExpired = false; + + // handle heal absorb effects + AuraList const& healAbsorbAuras = pVictim->GetAurasByType(SPELL_AURA_SCHOOL_HEAL_ABSORB); + for (AuraList::const_iterator aura = healAbsorbAuras.begin(); aura != healAbsorbAuras.end() && finalAmount > 0; ++aura) + { + Modifier* mod = (*aura)->GetModifier(); + + // check if affects this school + if (!(mod->m_miscvalue & spellProto->SchoolMask)) + continue; + + // max amount that can be absorbed by this aura + int32 currentAbsorb = mod->m_amount; + + // found empty aura (impossible but..) + if (currentAbsorb <= 0) + { + existExpired = true; + continue; + } + + // can't absorb more than heal amount + if (finalAmount < currentAbsorb) + currentAbsorb = finalAmount; + + // reduce heal amount by absorb amount + finalAmount -= currentAbsorb; + + // reduce aura amount + mod->m_amount -= currentAbsorb; + + if ((*aura)->GetHolder()->DropAuraCharge()) + mod->m_amount = 0; + + // check if aura needs to be removed + if (mod->m_amount <= 0) + existExpired = true; + } + // Remove all consumed absorb auras + if (existExpired) + { + for (AuraList::const_iterator aura = healAbsorbAuras.begin(); aura != healAbsorbAuras.end(); ) + { + if ((*aura)->GetModifier()->m_amount <= 0) + { + pVictim->RemoveAurasDueToSpell((*aura)->GetId()); + aura = healAbsorbAuras.begin(); + } + else + ++aura; + } + } + + Absorbed = HealAmount - finalAmount; + HealAmount = finalAmount; +} + void Unit::AttackerStateUpdate (Unit *pVictim, WeaponAttackType attType, bool extra ) { if(hasUnitState(UNIT_STAT_CAN_NOT_REACT) || HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED) ) @@ -2495,7 +2606,7 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttack dodge_chance -= GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE)*25; // Modify dodge chance by attacker SPELL_AURA_MOD_COMBAT_RESULT_CHANCE - dodge_chance+= GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_COMBAT_RESULT_CHANCE, VICTIMSTATE_DODGE); + dodge_chance+= GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_COMBAT_RESULT_CHANCE, VICTIMSTATE_DODGE)*100; tmp = dodge_chance; if ( (tmp > 0) // check if unit _can_ dodge @@ -2510,7 +2621,7 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttack // parry & block chances // check if attack comes from behind, nobody can parry or block if attacker is behind - if (!pVictim->HasInArc(M_PI_F,this)) + if (!pVictim->HasInArc(M_PI_F,this) && !pVictim->HasAura(19263)) { DEBUG_FILTER_LOG(LOG_FILTER_COMBAT, "RollMeleeOutcomeAgainst: attack came from behind."); } @@ -2652,6 +2763,9 @@ float Unit::CalculateLevelPenalty(SpellEntry const* spellProto) const if(spellProto->spellLevel <= 0) return 1.0f; + if (sSpellMgr.IsHighestRankOfSpell(spellProto->Id)) + return 1.0f; + float LvlPenalty = 0.0f; if(spellProto->spellLevel < 20) @@ -2764,7 +2878,7 @@ float Unit::MeleeSpellMissChance(Unit *pVictim, WeaponAttackType attType, int32 } // Melee based spells hit result calculations -SpellMissInfo Unit::MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell) +SpellMissInfo Unit::MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell, bool canMiss) { WeaponAttackType attType = BASE_ATTACK; @@ -2772,17 +2886,23 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell) attType = RANGED_ATTACK; // bonus from skills is 0.04% per skill Diff - int32 attackerWeaponSkill = int32(GetWeaponSkillValue(attType,pVictim)); + int32 attackerWeaponSkill = !(spell->DmgClass == SPELL_DAMAGE_CLASS_RANGED && !(spell->Attributes & SPELL_ATTR_RANGED) ) + ? int32(GetWeaponSkillValue(attType,pVictim)) + : int32(GetMaxSkillValueForLevel()); int32 skillDiff = attackerWeaponSkill - int32(pVictim->GetMaxSkillValueForLevel(this)); int32 fullSkillDiff = attackerWeaponSkill - int32(pVictim->GetDefenseSkillValue(this)); uint32 roll = urand (0, 10000); + uint32 tmp = 0; - uint32 missChance = uint32(MeleeSpellMissChance(pVictim, attType, fullSkillDiff, spell)*100.0f); - // Roll miss - uint32 tmp = missChance; - if (roll < tmp) - return SPELL_MISS_MISS; + if(canMiss) + { + uint32 missChance = uint32(MeleeSpellMissChance(pVictim, attType, fullSkillDiff, spell)*100.0f); + // Roll miss + tmp += missChance; + if (roll < tmp) + return SPELL_MISS_MISS; + } // Chance resist mechanic (select max value from every mechanic spell effect) int32 resist_mech = 0; @@ -2805,23 +2925,13 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell) bool canDodge = true; bool canParry = true; - // Same spells cannot be parry/dodge + // Some spells cannot be parry/dodge if (spell->Attributes & SPELL_ATTR_IMPOSSIBLE_DODGE_PARRY_BLOCK) return SPELL_MISS_NONE; - // Ranged attack cannot be parry/dodge only deflect + // Ranged attack cannot be parry/dodge only miss if (attType == RANGED_ATTACK) - { - // only if in front - if (pVictim->HasInArc(M_PI_F,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; - } // Check for attack from behind if (!pVictim->HasInArc(M_PI_F,this)) @@ -2830,7 +2940,8 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell) if (GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER) canDodge = false; // Can`t parry - canParry = false; + if (!pVictim->HasAura(19263)) + canParry = false; } // Check creatures flags_extra for disable parry if(pVictim->GetTypeId()==TYPEID_UNIT) @@ -2964,14 +3075,10 @@ SpellMissInfo Unit::MagicSpellHitResult(Unit *pVictim, SpellEntry const *spell) if (rand < tmp) return SPELL_MISS_MISS; - // cast by caster in front of victim - if (pVictim->HasInArc(M_PI_F,this)) - { - int32 deflect_chance = pVictim->GetTotalAuraModifier(SPELL_AURA_DEFLECT_SPELLS)*100; - tmp+=deflect_chance; - if (rand < tmp) - return SPELL_MISS_DEFLECT; - } + int32 deflect_chance = pVictim->GetTotalAuraModifier(SPELL_AURA_DEFLECT_SPELLS)*100; + tmp+=deflect_chance; + if (rand < tmp) + return SPELL_MISS_DEFLECT; return SPELL_MISS_NONE; } @@ -2984,27 +3091,32 @@ SpellMissInfo Unit::MagicSpellHitResult(Unit *pVictim, SpellEntry const *spell) // Parry // For spells // Resist -SpellMissInfo Unit::SpellHitResult(Unit *pVictim, SpellEntry const *spell, bool CanReflect) +SpellMissInfo Unit::SpellHitResult(Unit *pVictim, SpellEntry const *spell, bool canReflect, bool canMiss) { // Return evade for units in evade mode if (pVictim->GetTypeId()==TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode()) return SPELL_MISS_EVADE; - // Check for immune - if (pVictim->IsImmunedToSpell(spell)) - return SPELL_MISS_IMMUNE; + if (!(spell->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY)) + { + // Check for immune + if (pVictim->IsImmunedToSpell(spell)) + return SPELL_MISS_IMMUNE; - // All positive spells can`t miss - // TODO: client not show miss log for this spells - so need find info for this in dbc and use it! - if (IsPositiveSpell(spell->Id)) - return SPELL_MISS_NONE; + // All positive spells can`t miss + // TODO: client not show miss log for this spells - so need find info for this in dbc and use it! + if (IsPositiveSpell(spell->Id) && IsFriendlyTo(pVictim)) + return SPELL_MISS_NONE; - // Check for immune - if (pVictim->IsImmunedToDamage(GetSpellSchoolMask(spell))) - return SPELL_MISS_IMMUNE; + // Check for immune + if (pVictim->IsImmunedToDamage(GetSpellSchoolMask(spell))) + return SPELL_MISS_IMMUNE; + } + else if (IsPositiveSpell(spell->Id) && IsFriendlyTo(pVictim)) + return SPELL_MISS_NONE; // Try victim reflect spell - if (CanReflect) + if (canReflect) { int32 reflectchance = pVictim->GetTotalAuraModifier(SPELL_AURA_REFLECT_SPELLS); Unit::AuraList const& mReflectSpellsSchool = pVictim->GetAurasByType(SPELL_AURA_REFLECT_SPELLS_SCHOOL); @@ -3027,7 +3139,7 @@ SpellMissInfo Unit::SpellHitResult(Unit *pVictim, SpellEntry const *spell, bool return MagicSpellHitResult(pVictim, spell); case SPELL_DAMAGE_CLASS_MELEE: case SPELL_DAMAGE_CLASS_RANGED: - return MeleeSpellHitResult(pVictim, spell); + return MeleeSpellHitResult(pVictim, spell, canMiss); } return SPELL_MISS_NONE; } @@ -3964,6 +4076,12 @@ bool Unit::AddSpellAuraHolder(SpellAuraHolder *holder) (*itr)->GetTarget()->RemoveSpellAuraHolder((*itr)); restart = true; break; + } + // Judgements are always single + else if (GetSpellSpecific(holder->GetId()) == SPELL_JUDGEMENT) + { + (*itr)->GetTarget()->RemoveSpellAuraHolder((*itr)); + break; } } @@ -4982,6 +5100,7 @@ void Unit::SendPeriodicAuraLog(SpellPeriodicAuraLogInfo *pInfo) case SPELL_AURA_OBS_MOD_HEALTH: data << uint32(pInfo->damage); // damage data << uint32(pInfo->overDamage); // overheal? + data << uint32(pInfo->absorb); // absorb data << uint8(pInfo->critical ? 1 : 0); // new 3.1.2 critical flag break; case SPELL_AURA_OBS_MOD_MANA: @@ -5693,8 +5812,9 @@ Pet* Unit::GetPet() const { if(uint64 pet_guid = GetPetGUID()) { - if(Pet* pet = GetMap()->GetPet(pet_guid)) - return pet; + if(IsInWorld()) + if(Pet* pet = GetMap()->GetPet(pet_guid)) + return pet; sLog.outError("Unit::GetPet: Pet %u not exist.",GUID_LOPART(pet_guid)); const_cast(this)->SetPet(0); @@ -5833,7 +5953,11 @@ void Unit::UnsummonAllTotems() int32 Unit::DealHeal(Unit *pVictim, uint32 addhealth, SpellEntry const *spellProto, bool critical) { - int32 gain = pVictim->ModifyHealth(int32(addhealth)); + // calculate heal absorb and reduce healing + uint32 absorb = 0; + CalculateHealAbsorb(pVictim, spellProto, addhealth, absorb); + + int32 gain = addhealth ? pVictim->ModifyHealth(int32(addhealth)) : 0; Unit* unit = this; @@ -5843,7 +5967,7 @@ int32 Unit::DealHeal(Unit *pVictim, uint32 addhealth, SpellEntry const *spellPro if (unit->GetTypeId()==TYPEID_PLAYER) { // overheal = addhealth - gain - unit->SendHealSpellLog(pVictim, spellProto->Id, addhealth, addhealth - gain, critical); + unit->SendHealSpellLog(pVictim, spellProto->Id, addhealth, addhealth - gain, absorb, critical); if (BattleGround *bg = ((Player*)unit)->GetBattleGround()) bg->UpdatePlayerScore((Player*)unit, SCORE_HEALING_DONE, gain); @@ -5870,7 +5994,8 @@ Unit* Unit::SelectMagnetTarget(Unit *victim, SpellEntry const *spellInfo) return NULL; // Magic case - if(spellInfo && (spellInfo->DmgClass == SPELL_DAMAGE_CLASS_NONE || spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC)) + if(spellInfo && (spellInfo->DmgClass == SPELL_DAMAGE_CLASS_NONE || spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC) && + (spellInfo->SchoolMask & SPELL_SCHOOL_MASK_MAGIC || spellInfo->Mechanic == MECHANIC_GRIP)) { Unit::AuraList const& magnetAuras = victim->GetAurasByType(SPELL_AURA_SPELL_MAGNET); for(Unit::AuraList::const_iterator itr = magnetAuras.begin(); itr != magnetAuras.end(); ++itr) @@ -5892,15 +6017,16 @@ Unit* Unit::SelectMagnetTarget(Unit *victim, SpellEntry const *spellInfo) return victim; } -void Unit::SendHealSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage, uint32 OverHeal, bool critical) +void Unit::SendHealSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage, uint32 OverHeal, uint32 Absorbed, bool critical) { // we guess size - WorldPacket data(SMSG_SPELLHEALLOG, (8+8+4+4+1)); + WorldPacket data(SMSG_SPELLHEALLOG, (8+8+4+4+4+4+1+1)); data << pVictim->GetPackGUID(); data << GetPackGUID(); data << uint32(SpellID); data << uint32(Damage); data << uint32(OverHeal); + data << uint32(Absorbed); data << uint8(critical ? 1 : 0); data << uint8(0); // unused in client? SendMessageToSet(&data, true); @@ -6120,7 +6246,7 @@ uint32 Unit::SpellDamageBonusDone(Unit *pVictim, SpellEntry const *spellProto, u case 7293: // Rage of Rivendare { if (pVictim->GetAura(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_DEATHKNIGHT, UI64LIT(0x0200000000000000))) - DoneTotalMod *= ((*i)->GetModifier()->m_amount+100.0f)/100.0f; + DoneTotalMod *= ((*i)->GetSpellProto()->CalculateSimpleValue(EFFECT_INDEX_1)*2+100.0f)/100.0f; break; } // Twisted Faith @@ -6144,6 +6270,22 @@ uint32 Unit::SpellDamageBonusDone(Unit *pVictim, SpellEntry const *spellProto, u } } + // custom scripted mod from dummy + AuraList const& mDummy = owner->GetAurasByType(SPELL_AURA_DUMMY); + for(AuraList::const_iterator i = mDummy.begin(); i != mDummy.end(); ++i) + { + SpellEntry const *spell = (*i)->GetSpellProto(); + //Fire and Brimstone + if (spell->SpellFamilyName == SPELLFAMILY_WARLOCK && spell->SpellIconID == 3173) + { + if (pVictim->HasAuraState(AURA_STATE_CONFLAGRATE) && (spellProto->SpellFamilyName == SPELLFAMILY_WARLOCK && spellProto->SpellFamilyFlags & UI64LIT(0x0002004000000000))) + { + DoneTotalMod *= ((*i)->GetModifier()->m_amount+100.0f) / 100.0f; + break; + } + } + } + // Custom scripted damage switch(spellProto->SpellFamilyName) { @@ -6152,7 +6294,7 @@ uint32 Unit::SpellDamageBonusDone(Unit *pVictim, SpellEntry const *spellProto, u // Ice Lance if (spellProto->SpellIconID == 186) { - if (pVictim->isFrozen()) + if (pVictim->isFrozen() || isIgnoreUnitState(spellProto)) { float multiplier = 3.0f; @@ -6383,7 +6525,7 @@ int32 Unit::SpellBaseDamageBonusDone(SpellSchoolMask schoolMask) } } - return DoneAdvertisedBenefit; + return DoneAdvertisedBenefit > 0 ? DoneAdvertisedBenefit : 0; } int32 Unit::SpellBaseDamageBonusTaken(SpellSchoolMask schoolMask) @@ -6398,11 +6540,15 @@ int32 Unit::SpellBaseDamageBonusTaken(SpellSchoolMask schoolMask) TakenAdvertisedBenefit += (*i)->GetModifier()->m_amount; } - return TakenAdvertisedBenefit; + return TakenAdvertisedBenefit > 0 ? TakenAdvertisedBenefit : 0; } bool Unit::IsSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolMask schoolMask, WeaponAttackType attackType) { + // mobs can't crit with spells at all + if (GetObjectGuid().IsCreature()) + return false; + // not critting spell if((spellProto->AttributesEx2 & SPELL_ATTR_EX2_CANT_CRIT)) return false; @@ -6446,9 +6592,9 @@ bool Unit::IsSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolM continue; switch((*i)->GetModifier()->m_miscvalue) { - case 849: if (pVictim->isFrozen()) crit_chance+= 17.0f; break; //Shatter Rank 1 - case 910: if (pVictim->isFrozen()) crit_chance+= 34.0f; break; //Shatter Rank 2 - case 911: if (pVictim->isFrozen()) crit_chance+= 50.0f; break; //Shatter Rank 3 + case 849: if (pVictim->isFrozen() || isIgnoreUnitState(spellProto)) crit_chance+= 17.0f; break; //Shatter Rank 1 + case 910: if (pVictim->isFrozen() || isIgnoreUnitState(spellProto)) crit_chance+= 34.0f; break; //Shatter Rank 2 + case 911: if (pVictim->isFrozen() || isIgnoreUnitState(spellProto)) crit_chance+= 50.0f; break; //Shatter Rank 3 case 7917: // Glyph of Shadowburn if (pVictim->HasAuraState(AURA_STATE_HEALTHLESS_35_PERCENT)) crit_chance+=(*i)->GetModifier()->m_amount; @@ -6532,6 +6678,26 @@ bool Unit::IsSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolM break; } case SPELL_DAMAGE_CLASS_MELEE: + // Rend and Tear crit chance with Ferocious Bite on bleeding target + if (spellProto->SpellFamilyName == SPELLFAMILY_DRUID) + { + if(spellProto->SpellFamilyFlags & UI64LIT(0x0000000000800000)) + { + if(pVictim->HasAuraState(AURA_STATE_MECHANIC_BLEED)) + { + Unit::AuraList const& aura = GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraList::const_iterator itr = aura.begin(); itr != aura.end(); ++itr) + { + if ((*itr)->GetSpellProto()->SpellIconID == 2859 && (*itr)->GetEffIndex() == 1) + { + crit_chance += (*itr)->GetModifier()->m_amount; + break; + } + } + } + } + } + // do not use break here case SPELL_DAMAGE_CLASS_RANGED: { if (pVictim) @@ -6930,6 +7096,15 @@ bool Unit::IsImmunedToSpellEffect(SpellEntry const* spellInfo, SpellEffectIndex ((*iter)->GetModifier()->m_miscvalue & GetSpellSchoolMask(spellInfo)) && // Check school !IsPositiveEffect(spellInfo->Id, index)) // Harmful return true; + + AuraList const& immuneMechanicAuraApply = GetAurasByType(SPELL_AURA_MECHANIC_IMMUNITY_MASK); + for(AuraList::const_iterator i = immuneMechanicAuraApply.begin(); i != immuneMechanicAuraApply.end(); ++i) + if ((spellInfo->EffectMechanic[index] & (*i)->GetMiscValue() || + spellInfo->Mechanic & (*i)->GetMiscValue()) || + ((*i)->GetId() == 46924 && // Bladestorm Immunity + spellInfo->EffectMechanic[index] & IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK || + spellInfo->Mechanic & IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK)) + return true; } return false; @@ -7080,7 +7255,7 @@ uint32 Unit::MeleeDamageBonusDone(Unit *pVictim, uint32 pdamage,WeaponAttackType case 7293: // Rage of Rivendare { if (pVictim->GetAura(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_DEATHKNIGHT, UI64LIT(0x0200000000000000))) - DonePercent *= ((*i)->GetModifier()->m_amount+100.0f)/100.0f; + DonePercent *= ((*i)->GetSpellProto()->CalculateSimpleValue(EFFECT_INDEX_1)*2+100.0f)/100.0f; break; } // Marked for Death @@ -7335,6 +7510,10 @@ void Unit::ApplySpellDispelImmunity(const SpellEntry * spellProto, DispelType ty { ApplySpellImmune(spellProto->Id,IMMUNITY_DISPEL, type, apply); + // such dispell type should not remove auras but only return visibility + if(type == DISPEL_STEALTH || type == DISPEL_INVISIBILITY) + return; + if (apply && spellProto->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY) RemoveAurasWithDispelType(type); } @@ -7718,11 +7897,17 @@ bool Unit::isVisibleForOrDetect(Unit const* u, WorldObject const* viewPoint, boo else { // Hunter mark functionality - AuraList const& auras = GetAurasByType(SPELL_AURA_MOD_STALKED); - for(AuraList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter) + AuraList const& aurasstalked = GetAurasByType(SPELL_AURA_MOD_STALKED); + for(AuraList::const_iterator iter = aurasstalked.begin(); iter != aurasstalked.end(); ++iter) if((*iter)->GetCasterGUID()==u->GetGUID()) return true; + // Flare functionality + AuraList const& aurasimunity = GetAurasByType(SPELL_AURA_DISPEL_IMMUNITY); + for(AuraList::const_iterator iter = aurasimunity.begin(); iter != aurasimunity.end(); ++iter) + if((*iter)->GetMiscValue() == uint8(invisible ? DISPEL_INVISIBILITY : DISPEL_STEALTH)) + return true; + // else apply detecting check for stealth } @@ -7991,6 +8176,21 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced, float ratio) return; } + // Remove Druid Dash bonus if not in Cat Form + if (m_form != FORM_CAT) + { + AuraList const& speed_increase_auras = GetAurasByType(SPELL_AURA_MOD_INCREASE_SPEED); + for(AuraList::const_iterator itr = speed_increase_auras.begin(); itr != speed_increase_auras.end(); ++itr) + { + const SpellEntry* aura_proto = (*itr)->GetSpellProto(); + if (aura_proto->SpellFamilyName == SPELLFAMILY_DRUID && aura_proto->SpellIconID == 959) + { + main_speed_mod -= (*itr)->GetModifier()->m_amount; + break; + } + } + } + float bonus = non_stack_bonus > stack_bonus ? non_stack_bonus : stack_bonus; // now we ready for speed calculation float speed = main_speed_mod ? bonus*(100.0f + main_speed_mod)/100.0f : bonus; @@ -8503,6 +8703,37 @@ int32 Unit::CalculateSpellDuration(SpellEntry const* spellProto, SpellEffectInde else duration = minduration; + if (unitPlayer && target == this) + { + switch(spellProto->SpellFamilyName) + { + case SPELLFAMILY_DRUID: + if (spellProto->SpellFamilyFlags & UI64LIT(0x100)) + { + // Glyph of Thorns + if (Aura *aur = GetAura(57862, EFFECT_INDEX_0)) + duration += aur->GetModifier()->m_amount * MINUTE * IN_MILLISECONDS; + } + break; + case SPELLFAMILY_PALADIN: + if (spellProto->SpellIconID == 298 && spellProto->SpellFamilyFlags & UI64LIT(0x00000002)) + { + // Glyph of Blessing of Might + if (Aura *aur = GetAura(57958, EFFECT_INDEX_0)) + duration += aur->GetModifier()->m_amount * MINUTE * IN_MILLISECONDS; + } + else if (spellProto->SpellIconID == 306 && spellProto->SpellFamilyFlags & UI64LIT(0x00010000)) + { + // Glyph of Blessing of Wisdom + if (Aura *aur = GetAura(57979, EFFECT_INDEX_0)) + duration += aur->GetModifier()->m_amount * MINUTE * IN_MILLISECONDS; + } + break; + default: + break; + } + } + if (duration > 0) { int32 mechanic = GetEffectMechanic(spellProto, effect_index); @@ -8577,7 +8808,7 @@ void Unit::IncrDiminishing(DiminishingGroup group) void Unit::ApplyDiminishingToDuration(DiminishingGroup group, int32 &duration,Unit* caster,DiminishingLevels Level, int32 limitduration) { - if(duration == -1 || group == DIMINISHING_NONE || caster->IsFriendlyTo(this) ) + if(duration == -1 || group == DIMINISHING_NONE) return; // Duration of crowd control abilities on pvp target is limited by 10 sec. (2.2.0) @@ -9671,7 +9902,7 @@ void Unit::SetFeared(bool apply, uint64 const& casterGUID, uint32 spellID, uint3 { RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING); - GetMotionMaster()->MovementExpired(false); + GetMotionMaster()->Clear(false); if( GetTypeId() != TYPEID_PLAYER && isAlive() ) { @@ -9706,7 +9937,7 @@ void Unit::SetConfused(bool apply, uint64 const& casterGUID, uint32 spellID) { RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED); - GetMotionMaster()->MovementExpired(false); + GetMotionMaster()->Clear(false); if (GetTypeId() != TYPEID_PLAYER && isAlive()) { @@ -10356,6 +10587,27 @@ void Unit::KnockBackFrom(Unit* target, float horizontalSpeed, float verticalSpee } } +void Unit::KnockBackPlayerWithAngle(float angle, float horizontalSpeed, float verticalSpeed) +{ + float vsin = sin(angle); + float vcos = cos(angle); + + // Effect propertly implemented only for players + if(GetTypeId()==TYPEID_PLAYER) + { + WorldPacket data(SMSG_MOVE_KNOCK_BACK, 8+4+4+4+4+4); + data << GetPackGUID(); + data << uint32(0); // Sequence + data << float(vcos); // x direction + data << float(vsin); // y direction + data << float(horizontalSpeed); // Horizontal speed + data << float(-verticalSpeed); // Z Movement speed (vertical) + ((Player*)this)->GetSession()->SendPacket(&data); + } + else + sLog.outError("KnockBackPlayer: Target of KnockBackPlayer must be player!"); +} + float Unit::GetCombatRatingReduction(CombatRating cr) const { if (GetTypeId() == TYPEID_PLAYER) @@ -10473,6 +10725,32 @@ void Unit::StopAttackFaction(uint32 faction_id) CallForAllControlledUnits(StopAttackFactionHelper(faction_id),false,true,true); } +bool Unit::isIgnoreUnitState(SpellEntry const *spell) +{ + if(!HasAuraType(SPELL_AURA_IGNORE_UNIT_STATE)) + return false; + + if(spell->SpellFamilyName == SPELLFAMILY_MAGE) + { + // Ice Lance + if(spell->SpellIconID == 186) + return true; + // Shatter + if(spell->Id == 11170 || spell->Id == 12982 || spell->Id == 12983) + return true; + } + Unit::AuraList const& stateAuras = GetAurasByType(SPELL_AURA_IGNORE_UNIT_STATE); + for(Unit::AuraList::const_iterator j = stateAuras.begin();j != stateAuras.end(); ++j) + { + if((*j)->isAffectedOnSpell(spell)) + { + return true; + break; + } + } + return false; +} + void Unit::CleanupDeletedAuras() { for (SpellAuraHolderList::const_iterator iter = m_deletedHolders.begin(); iter != m_deletedHolders.end(); ++iter) diff --git a/src/game/Unit.h b/src/game/Unit.h index 41fcac5..36d4a66 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -69,7 +69,7 @@ enum SpellAuraInterruptFlags AURA_INTERRUPT_FLAG_NOT_UNDERWATER = 0x00000100, // 8 removed by leaving water AURA_INTERRUPT_FLAG_NOT_SHEATHED = 0x00000200, // 9 removed by unsheathing AURA_INTERRUPT_FLAG_UNK10 = 0x00000400, // 10 - AURA_INTERRUPT_FLAG_UNK11 = 0x00000800, // 11 + AURA_INTERRUPT_FLAG_CAST = 0x00000800, // 11 removed by casting a spell AURA_INTERRUPT_FLAG_UNK12 = 0x00001000, // 12 removed by attack? AURA_INTERRUPT_FLAG_UNK13 = 0x00002000, // 13 AURA_INTERRUPT_FLAG_UNK14 = 0x00004000, // 14 @@ -1307,9 +1307,9 @@ class MANGOS_DLL_SPEC Unit : public WorldObject uint32 GetSpellDamageReduction(uint32 damage) const { return GetCombatRatingDamageReduction(CR_CRIT_TAKEN_MELEE, 2.0f, 100.0f, damage); } float MeleeSpellMissChance(Unit *pVictim, WeaponAttackType attType, int32 skillDiff, SpellEntry const *spell); - SpellMissInfo MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell); + SpellMissInfo MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell, bool canMiss = true); SpellMissInfo MagicSpellHitResult(Unit *pVictim, SpellEntry const *spell); - SpellMissInfo SpellHitResult(Unit *pVictim, SpellEntry const *spell, bool canReflect = false); + SpellMissInfo SpellHitResult(Unit *pVictim, SpellEntry const *spell, bool canReflect = false, bool canMiss = true); float GetUnitDodgeChance() const; float GetUnitParryChance() const; @@ -1386,6 +1386,7 @@ class MANGOS_DLL_SPEC Unit : public WorldObject bool IsPolymorphed() const; bool isFrozen() const; + bool isIgnoreUnitState(SpellEntry const *spell); void RemoveSpellbyDamageTaken(AuraType auraType, uint32 damage); @@ -1396,7 +1397,7 @@ class MANGOS_DLL_SPEC Unit : public WorldObject virtual bool IsUnderWater() const; bool isInAccessablePlaceFor(Creature const* c) const; - void SendHealSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage, uint32 OverHeal, bool critical = false); + void SendHealSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage, uint32 OverHeal, uint32 Absorbed, bool critical = false); void SendEnergizeSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage,Powers powertype); void EnergizeBySpell(Unit *pVictim, uint32 SpellID, uint32 Damage, Powers powertype); uint32 SpellNonMeleeDamageLog(Unit *pVictim, uint32 spellID, uint32 damage); @@ -1824,6 +1825,7 @@ class MANGOS_DLL_SPEC Unit : public WorldObject uint32 CalcArmorReducedDamage(Unit* pVictim, const uint32 damage); void CalculateAbsorbAndResist(Unit *pCaster, SpellSchoolMask schoolMask, DamageEffectType damagetype, const uint32 damage, uint32 *absorb, uint32 *resist, bool canReflect = false); void CalculateAbsorbResistBlock(Unit *pCaster, SpellNonMeleeDamage *damageInfo, SpellEntry const* spellProto, WeaponAttackType attType = BASE_ATTACK); + void CalculateHealAbsorb(Unit *pVictim, const SpellEntry *spellProto, uint32 &HealAmount, uint32 &Absorbed); void UpdateWalkMode(Unit* source, bool self = true); void UpdateSpeed(UnitMoveType mtype, bool forced, float ratio = 1.0f); @@ -1835,6 +1837,7 @@ class MANGOS_DLL_SPEC Unit : public WorldObject bool isHover() const { return HasAuraType(SPELL_AURA_HOVER); } void KnockBackFrom(Unit* target, float horizontalSpeed, float verticalSpeed); + void KnockBackPlayerWithAngle(float angle, float horizontalSpeed, float verticalSpeed); void _RemoveAllAuraMods(); void _ApplyAllAuraMods(); diff --git a/src/game/UnitAuraProcHandler.cpp b/src/game/UnitAuraProcHandler.cpp index 8efae7c..5d326c2 100644 --- a/src/game/UnitAuraProcHandler.cpp +++ b/src/game/UnitAuraProcHandler.cpp @@ -1334,7 +1334,19 @@ SpellAuraProcResult Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura // Divine Aegis case 2820: { - basepoints[0] = damage * triggerAmount/100; + if(!pVictim || !pVictim->isAlive()) + return SPELL_AURA_PROC_FAILED; + + // find Divine Aegis on the target and get absorb amount + Aura* DivineAegis = pVictim->GetAura(47753,EFFECT_INDEX_0); + if (DivineAegis) + basepoints[0] = DivineAegis->GetModifier()->m_amount; + basepoints[0] += damage * triggerAmount/100; + + // limit absorb amount + int32 levelbonus = pVictim->getLevel()*125; + if (basepoints[0] > levelbonus) + basepoints[0] = levelbonus; triggered_spell_id = 47753; break; } @@ -1439,6 +1451,13 @@ SpellAuraProcResult Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura // triggered_spell_id in spell data break; } + // Glyph of Prayer of Healing + case 55680: + { + basepoints[0] = int32(damage * 20 / 100 / 2); // divided in two ticks + triggered_spell_id = 56161; + break; + } } break; } @@ -1570,8 +1589,33 @@ SpellAuraProcResult Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura return SPELL_AURA_PROC_OK; } } + // King of the Jungle + if (dummySpell->SpellIconID == 2850) + { + if (!procSpell) + return SPELL_AURA_PROC_FAILED; + + // Enrage (bear) - single rank - the aura for the bear form from the 2 existing kotj auras has a miscValue == 126 + if (procSpell->Id == 5229 && triggeredByAura->GetMiscValue() == 126) + { + // note : the remove part is done in spellAuras/HandlePeriodicEnergize as RemoveAurasDueToSpell + basepoints[0] = triggerAmount; + triggered_spell_id = 51185; + target = this; + break; + } + // Tiger Fury (cat) - all ranks - the aura for the cat form from the 2 existing kotj auras has a miscValue != 126 + if (procSpell->SpellFamilyFlags2 & UI64LIT(0x00000800) && triggeredByAura->GetMiscValue() != 126) + { + basepoints[0] = triggerAmount; + triggered_spell_id = 51178; + target = this; + break; + } + return SPELL_AURA_PROC_FAILED; + } // Eclipse - if (dummySpell->SpellIconID == 2856) + else if (dummySpell->SpellIconID == 2856) { if (!procSpell) return SPELL_AURA_PROC_FAILED; @@ -1922,6 +1966,9 @@ SpellAuraProcResult Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura Unit* beacon = triggeredByAura->GetCaster(); if (!beacon) return SPELL_AURA_PROC_FAILED; + + if (procSpell->Id == 20267) + return SPELL_AURA_PROC_FAILED; // find caster main aura at beacon Aura* dummy = NULL; @@ -2425,18 +2472,27 @@ SpellAuraProcResult Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura if (dummySpell->Id == 49028) { // 1 dummy aura for dismiss rune blade - if (effIndex != EFFECT_INDEX_2) + if (effIndex != EFFECT_INDEX_1) return SPELL_AURA_PROC_FAILED; - // TODO: wite script for this "fights on its own, doing the same attacks" - // NOTE: Trigger here on every attack and spell cast - return SPELL_AURA_PROC_FAILED; + + Pet* runeBlade = FindGuardianWithEntry(27893); + + if (runeBlade && pVictim && damage && procSpell) + { + int32 procDmg = damage * 0.5; + runeBlade->CastCustomSpell(pVictim, procSpell->Id, &procDmg, NULL, NULL, true, NULL, NULL, runeBlade->GetGUID()); + SendSpellNonMeleeDamageLog(pVictim, procSpell->Id, procDmg, SPELL_SCHOOL_MASK_NORMAL, 0, 0, false, 0, false); + break; + } + else + return SPELL_AURA_PROC_FAILED; } // Mark of Blood if (dummySpell->Id == 49005) { // TODO: need more info (cooldowns/PPM) - triggered_spell_id = 61607; - break; + target->CastSpell(target, 61607, true, NULL, triggeredByAura); + return SPELL_AURA_PROC_OK; } // Vendetta if (dummySpell->SpellFamilyFlags & UI64LIT(0x0000000000010000)) @@ -2446,6 +2502,20 @@ SpellAuraProcResult Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura target = this; break; } + // Unholy Blight + if (dummySpell->Id == 49194) + { + basepoints[0] = damage * triggerAmount / 100; + + // Glyph of Unholy Blight + if (Aura *aura = GetDummyAura(63332)) + basepoints[0] += basepoints[0] * aura->GetModifier()->m_amount / 100; + + // Split between 10 ticks + basepoints[0] /= 10; + triggered_spell_id = 50536; + break; + } // Necrosis if (dummySpell->SpellIconID == 2709) { @@ -2536,6 +2606,62 @@ SpellAuraProcResult Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura } break; } + // Blood of the North and Reaping + if (dummySpell->SpellIconID == 3041 || dummySpell->SpellIconID == 22) + { + if(GetTypeId()!=TYPEID_PLAYER) + return SPELL_AURA_PROC_FAILED; + + Player *player = (Player*)this; + for (uint32 i = 0; i < MAX_RUNES; ++i) + { + if (player->GetCurrentRune(i) == RUNE_BLOOD) + { + if(!player->GetRuneCooldown(i)) + player->ConvertRune(i, RUNE_DEATH, dummySpell->Id); + else + { + // search for another rune that might be available + for (uint32 iter = i; iter < MAX_RUNES; ++iter) + { + if(player->GetCurrentRune(iter) == RUNE_BLOOD && !player->GetRuneCooldown(iter)) + { + player->ConvertRune(iter, RUNE_DEATH, dummySpell->Id); + triggeredByAura->SetAuraPeriodicTimer(0); + return SPELL_AURA_PROC_OK; + } + } + player->SetNeedConvertRune(i, true, dummySpell->Id); + } + triggeredByAura->SetAuraPeriodicTimer(0); + return SPELL_AURA_PROC_OK; + } + } + return SPELL_AURA_PROC_FAILED; + } + // Death Rune Mastery + if (dummySpell->SpellIconID == 2622) + { + if(GetTypeId()!=TYPEID_PLAYER) + return SPELL_AURA_PROC_FAILED; + + Player *player = (Player*)this; + for (uint32 i = 0; i < MAX_RUNES; ++i) + { + RuneType currRune = player->GetCurrentRune(i); + if (currRune == RUNE_UNHOLY || currRune == RUNE_FROST) + { + uint16 cd = player->GetRuneCooldown(i); + if(!cd) + player->ConvertRune(i, RUNE_DEATH, dummySpell->Id); + else // there is a cd + player->SetNeedConvertRune(i, true, dummySpell->Id); + // no break because it converts all + } + } + triggeredByAura->SetAuraPeriodicTimer(0); + return SPELL_AURA_PROC_OK; + } // Runic Power Back on Snare/Root if (dummySpell->Id == 61257) { @@ -2549,6 +2675,31 @@ SpellAuraProcResult Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura target = this; break; } + // Sudden Doom + if (dummySpell->SpellIconID == 1939) + { + if (!target || !target->isAlive() || this->GetTypeId() != TYPEID_PLAYER) + return SPELL_AURA_PROC_FAILED; + + // get highest rank of Death Coil spell + const PlayerSpellMap& sp_list = ((Player*)this)->GetSpellMap(); + for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr) + { + 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->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT && spellInfo->SpellFamilyFlags & UI64LIT(0x2000)) + { + triggered_spell_id = spellInfo->Id; + break; + } + } + break; + } // Wandering Plague if (dummySpell->SpellIconID == 1614) { @@ -2558,6 +2709,12 @@ SpellAuraProcResult Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura triggered_spell_id = 50526; break; } + // Hungering Cold - not break from diseases + if (dummySpell->SpellIconID == 2797) + { + if (procSpell && procSpell->Dispel == DISPEL_DISEASE) + return SPELL_AURA_PROC_FAILED; + } // Blood-Caked Blade if (dummySpell->SpellIconID == 138) { @@ -2690,6 +2847,10 @@ SpellAuraProcResult Unit::HandleProcTriggerSpellAuraProc(Unit *pVictim, uint32 d //case 36207: break: // Steal Weapon //case 36576: break: // Shaleskin (Shaleskin Flayer, Shaleskin Ripper) 30023 trigger //case 37030: break; // Chaotic Temperament + case 38164: // Unyielding Knights + if (pVictim->GetEntry() != 19457) + return SPELL_AURA_PROC_FAILED; + break; //case 38363: break; // Gushing Wound //case 39215: break; // Gushing Wound //case 40250: break; // Improved Duration @@ -3098,7 +3259,7 @@ SpellAuraProcResult Unit::HandleProcTriggerSpellAuraProc(Unit *pVictim, uint32 d case SPELLFAMILY_SHAMAN: { // Lightning Shield (overwrite non existing triggered spell call in spell.dbc - if (auraSpellInfo->SpellFamilyFlags & UI64LIT(0x0000000000000400)) + if (auraSpellInfo->SpellFamilyFlags & UI64LIT(0x0000000000000400) && auraSpellInfo->SpellVisual[0] == 37) { switch(auraSpellInfo->Id) { @@ -3184,6 +3345,12 @@ SpellAuraProcResult Unit::HandleProcTriggerSpellAuraProc(Unit *pVictim, uint32 d return SPELL_AURA_PROC_FAILED; } } + // Glyph of Death's Embrace + else if (auraSpellInfo->Id == 58677) + { + if (procSpell->Id != 47633) + return SPELL_AURA_PROC_FAILED; + } // Blade Barrier else if (auraSpellInfo->SpellIconID == 85) { diff --git a/src/game/WaypointMovementGenerator.cpp b/src/game/WaypointMovementGenerator.cpp index 502708f..9def84d 100644 --- a/src/game/WaypointMovementGenerator.cpp +++ b/src/game/WaypointMovementGenerator.cpp @@ -315,6 +315,7 @@ void FlightPathMovementGenerator::Finalize(Player & player) float x, y, z; i_destinationHolder.GetLocationNow(player.GetBaseMap(), x, y, z); + player.Anti__SetLastTeleTime(time(NULL)); player.SetPosition(x, y, z, player.GetOrientation()); player.Unmount(); diff --git a/src/game/World.cpp b/src/game/World.cpp index 28393bd..094ff2b 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -49,6 +49,7 @@ #include "CreatureAIRegistry.h" #include "Policies/SingletonImp.h" #include "BattleGroundMgr.h" +#include "Language.h" #include "TemporarySummon.h" #include "VMapFactory.h" #include "GameEventMgr.h" @@ -512,6 +513,28 @@ void World::LoadConfigSettings(bool reload) setConfigPos(CONFIG_FLOAT_CREATURE_FAMILY_FLEE_ASSISTANCE_RADIUS, "CreatureFamilyFleeAssistanceRadius", 30.0f); ///- Read other configuration items from the config file + + // movement anticheat + m_MvAnticheatEnable = sConfig.GetBoolDefault("Anticheat.Movement.Enable",false); + m_MvAnticheatKick = sConfig.GetBoolDefault("Anticheat.Movement.Kick",false); + m_MvAnticheatAnnounce = sConfig.GetBoolDefault("Anticheat.Movement.Announce",false); + m_MvAnticheatAlarmCount = (uint32)sConfig.GetIntDefault("Anticheat.Movement.AlarmCount", 5); + m_MvAnticheatAlarmPeriod = (uint32)sConfig.GetIntDefault("Anticheat.Movement.AlarmTime", 5000); + m_MvAntiCheatBan = (unsigned char)sConfig.GetIntDefault("Anticheat.Movement.BanType",0); + m_MvAnticheatBanTime = sConfig.GetStringDefault("Anticheat.Movement.BanTime","1m"); + m_MvAnticheatGmLevel = (unsigned char)sConfig.GetIntDefault("Anticheat.Movement.GmLevel",0); + m_MvAnticheatKill = sConfig.GetBoolDefault("Anticheat.Movement.Kill",false); + m_MvAnticheatMaxXYT = sConfig.GetFloatDefault("Anticheat.Movement.MaxXYT",0.04f); + m_MvAnticheatIgnoreAfterTeleport = (uint16)sConfig.GetIntDefault("Anticheat.Movement.IgnoreSecAfterTeleport",10); + + m_MvAnticheatSpeedCheck = sConfig.GetBoolDefault("Anticheat.Movement.DetectSpeedHack",1); + m_MvAnticheatWaterCheck = sConfig.GetBoolDefault("Anticheat.Movement.DetectWaterWalk",1); + m_MvAnticheatFlyCheck = sConfig.GetBoolDefault("Anticheat.Movement.DetectFlyHack",1); + m_MvAnticheatMountainCheck = sConfig.GetBoolDefault("Anticheat.Movement.DetectMountainHack",1); + m_MvAnticheatJumpCheck = sConfig.GetBoolDefault("Anticheat.Movement.DetectAirJumpHack",1); + m_MvAnticheatTeleportCheck = sConfig.GetBoolDefault("Anticheat.Movement.DetectTeleportHack",1); + m_MvAnticheatTeleport2PlaneCheck = sConfig.GetBoolDefault("Anticheat.Movement.DetectTeleport2PlaneHack",0); + setConfigMinMax(CONFIG_UINT32_COMPRESSION, "Compression", 1, 1, 9); setConfig(CONFIG_BOOL_ADDON_CHANNEL, "AddonChannel", true); setConfig(CONFIG_BOOL_CLEAN_CHARACTER_DB, "CleanCharacterDB", true); @@ -722,6 +745,7 @@ void World::LoadConfigSettings(bool reload) setConfig(CONFIG_UINT32_BATTLEGROUND_INVITATION_TYPE, "Battleground.InvitationType", 0); setConfig(CONFIG_UINT32_BATTLEGROUND_PREMATURE_FINISH_TIMER, "BattleGround.PrematureFinishTimer", 5 * MINUTE * IN_MILLISECONDS); setConfig(CONFIG_UINT32_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH, "BattleGround.PremadeGroupWaitForMatch", 30 * MINUTE * IN_MILLISECONDS); + setConfigMinMax(CONFIG_UINT32_RANDOM_BG_RESET_HOUR, "BattleGround.Random.ResetHour", 6, 0, 23); setConfig(CONFIG_UINT32_ARENA_MAX_RATING_DIFFERENCE, "Arena.MaxRatingDifference", 150); setConfig(CONFIG_UINT32_ARENA_RATING_DISCARD_TIMER, "Arena.RatingDiscardTimer", 10 * MINUTE * IN_MILLISECONDS); setConfig(CONFIG_BOOL_ARENA_AUTO_DISTRIBUTE_POINTS, "Arena.AutoDistributePoints", false); @@ -1125,6 +1149,9 @@ void World::SetInitialWorldSettings() sLog.outString( "Loading Player level dependent mail rewards..." ); sObjectMgr.LoadMailLevelRewards(); + sLog.outString( "Loading Spell disabled..." ); + sObjectMgr.LoadSpellDisabledEntrys(); + sLog.outString( "Loading Loot Tables..." ); sLog.outString(); LoadLootTables(); @@ -1258,8 +1285,10 @@ 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); - LoginDatabase.PExecute("INSERT INTO uptime (realmid, starttime, startstring, uptime) VALUES('%u', " UI64FMTD ", '%s', 0)", - realmID, uint64(m_startTime), isoDate); + LoginDatabase.PExecute("INSERT INTO uptime (realmid, starttime, startstring, uptime) VALUES('%u', " UI64FMTD ", '%s', 0)", realmID, uint64(m_startTime), isoDate); + + static uint32 abtimer = 0; + abtimer = sConfig.GetIntDefault("AutoBroadcast.Timer", 60000); m_timers[WUPDATE_OBJECTS].SetInterval(0); m_timers[WUPDATE_SESSIONS].SetInterval(0); @@ -1269,6 +1298,7 @@ void World::SetInitialWorldSettings() //Update "uptime" table based on configuration entry in minutes. m_timers[WUPDATE_CORPSES].SetInterval(3*HOUR*IN_MILLISECONDS); m_timers[WUPDATE_DELETECHARS].SetInterval(DAY*IN_MILLISECONDS); // check for chars to delete every day + m_timers[WUPDATE_AUTOBROADCAST].SetInterval(abtimer); //to set mailtimer to return mails every day between 4 and 5 am //mailtimer is increased when updating auctions @@ -1304,6 +1334,9 @@ void World::SetInitialWorldSettings() sLog.outString("Calculate next weekly quest reset time..." ); InitWeeklyQuestResetTime(); + sLog.outString("Calculate random battleground reset time..." ); + InitRandomBGResetTime(); + sLog.outString("Starting objects Pooling system..." ); sPoolMgr.Initialize(); @@ -1314,6 +1347,7 @@ void World::SetInitialWorldSettings() // Delete all characters which have been deleted X days before Player::DeleteOldCharacters(); + sLog.outString("Starting Autobroadcast system by Xeross..." ); sLog.outString( "WORLD: World initialized" ); uint32 uStartInterval = getMSTimeDiff(uStartTime, getMSTime()); @@ -1388,6 +1422,9 @@ void World::Update(uint32 diff) if (m_gameTime > m_NextWeeklyQuestReset) ResetWeeklyQuests(); + if (m_gameTime > m_NextRandomBGReset) + ResetRandomBG(); + ///
  • Handle auctions when the timer has passed if (m_timers[WUPDATE_AUCTIONS].Passed()) { @@ -1480,6 +1517,16 @@ void World::Update(uint32 diff) m_timers[WUPDATE_EVENTS].SetInterval(nextGameEvent); m_timers[WUPDATE_EVENTS].Reset(); } + static uint32 autobroadcaston = 0; + autobroadcaston = sConfig.GetIntDefault("AutoBroadcast.On", 0); + if(autobroadcaston == 1) + { + if (m_timers[WUPDATE_AUTOBROADCAST].Passed()) + { + m_timers[WUPDATE_AUTOBROADCAST].Reset(); + SendBroadcast(); + } + } ///
///- Move all creatures with "delayed move" and remove and delete all objects with "delayed remove" @@ -1894,6 +1941,57 @@ void World::ProcessCliCommands() } } +void World::SendBroadcast() +{ + std::string msg; + static int nextid; + + QueryResult *result; + if(nextid != 0) + { + result = LoginDatabase.PQuery("SELECT `text`, `next` FROM `autobroadcast` WHERE `id` = %u", nextid); + } + else + { + result = LoginDatabase.PQuery("SELECT `text`, `next` FROM `autobroadcast` ORDER BY RAND() LIMIT 1"); + } + + if(!result) + return; + + Field *fields = result->Fetch(); + nextid = fields[1].GetUInt32(); + msg = fields[0].GetString(); + delete result; + + static uint32 abcenter = 0; + abcenter = sConfig.GetIntDefault("AutoBroadcast.Center", 0); + if(abcenter == 0) + { + sWorld.SendWorldText(LANG_AUTO_BROADCAST, msg.c_str()); + + sLog.outString("AutoBroadcast: '%s'",msg.c_str()); + } + if(abcenter == 1) + { + WorldPacket data(SMSG_NOTIFICATION, (msg.size()+1)); + data << msg; + sWorld.SendGlobalMessage(&data); + + sLog.outString("AutoBroadcast: '%s'",msg.c_str()); + } + if(abcenter == 2) + { + sWorld.SendWorldText(LANG_AUTO_BROADCAST, msg.c_str()); + + WorldPacket data(SMSG_NOTIFICATION, (msg.size()+1)); + data << msg; + sWorld.SendGlobalMessage(&data); + + sLog.outString("AutoBroadcast: '%s'",msg.c_str()); + } +} + void World::InitResultQueue() { m_resultQueue = new SqlResultQueue; @@ -1988,6 +2086,37 @@ void World::InitDailyQuestResetTime() delete result; } +void World::InitRandomBGResetTime() +{ + QueryResult * result = CharacterDatabase.Query("SELECT NextRandomBGResetTime FROM saved_variables"); + if (!result) + m_NextRandomBGReset = time_t(time(NULL)); // game time not yet init + else + m_NextRandomBGReset = time_t((*result)[0].GetUInt64()); + + // generate time by config + time_t curTime = time(NULL); + tm localTm = *localtime(&curTime); + localTm.tm_hour = getConfig(CONFIG_UINT32_RANDOM_BG_RESET_HOUR); + localTm.tm_min = 0; + localTm.tm_sec = 0; + + // current day reset time + time_t nextDayResetTime = mktime(&localTm); + + // next reset time before current moment + if (curTime >= nextDayResetTime) + nextDayResetTime += DAY; + + // normalize reset time + m_NextRandomBGReset = m_NextRandomBGReset < curTime ? nextDayResetTime - DAY : nextDayResetTime; + + if (!result) + CharacterDatabase.PExecute("INSERT INTO saved_variables (NextRandomBGResetTime) VALUES ('"UI64FMTD"')", uint64(m_NextRandomBGReset)); + else + delete result; +} + void World::ResetDailyQuests() { DETAIL_LOG("Daily quests reset for all characters."); @@ -2012,6 +2141,18 @@ void World::ResetWeeklyQuests() CharacterDatabase.PExecute("UPDATE saved_variables SET NextWeeklyQuestResetTime = '"UI64FMTD"'", uint64(m_NextWeeklyQuestReset)); } +void World::ResetRandomBG() +{ + sLog.outDetail("Random BG status reset for all characters."); + CharacterDatabase.Execute("DELETE FROM character_battleground_random"); + for(SessionMap::const_iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr) + if (itr->second->GetPlayer()) + itr->second->GetPlayer()->SetRandomWinner(false); + + m_NextRandomBGReset = time_t(m_NextRandomBGReset + DAY); + CharacterDatabase.PExecute("UPDATE saved_variables SET NextRandomBGResetTime = '"UI64FMTD"'", uint64(m_NextRandomBGReset)); +} + void World::SetPlayerLimit( int32 limit, bool needUpdate ) { if (limit < -SEC_ADMINISTRATOR) diff --git a/src/game/World.h b/src/game/World.h index 6aadc0d..0663138 100644 --- a/src/game/World.h +++ b/src/game/World.h @@ -78,7 +78,8 @@ enum WorldTimers WUPDATE_CORPSES = 5, WUPDATE_EVENTS = 6, WUPDATE_DELETECHARS = 7, - WUPDATE_COUNT = 8 + WUPDATE_AUTOBROADCAST = 8, + WUPDATE_COUNT = 9 }; /// Configuration elements @@ -182,6 +183,7 @@ enum eConfigUInt32Values CONFIG_UINT32_CHARDELETE_KEEP_DAYS, CONFIG_UINT32_CHARDELETE_METHOD, CONFIG_UINT32_CHARDELETE_MIN_LEVEL, + CONFIG_UINT32_RANDOM_BG_RESET_HOUR, CONFIG_UINT32_VALUE_COUNT }; @@ -452,6 +454,7 @@ class World WorldSession* FindSession(uint32 id) const; void AddSession(WorldSession *s); + void SendBroadcast(); bool RemoveSession(uint32 id); /// Get the number of current active sessions void UpdateMaxSessionCounters(); @@ -506,6 +509,7 @@ class World /// Next daily quests reset time time_t GetNextDailyQuestsResetTime() const { return m_NextDailyQuestReset; } time_t GetNextWeeklyQuestsResetTime() const { return m_NextWeeklyQuestReset; } + time_t GetNextRandomBGResetTime() const { return m_NextRandomBGReset; } /// Get the maximum skill level a player can reach uint16 GetConfigMaxSkillValue() const @@ -581,6 +585,28 @@ class World static float GetVisibleUnitGreyDistance() { return m_VisibleUnitGreyDistance; } static float GetVisibleObjectGreyDistance() { return m_VisibleObjectGreyDistance; } + //movement anticheat enable flag + inline bool GetMvAnticheatEnable() {return m_MvAnticheatEnable;} + inline bool GetMvAnticheatKick() {return m_MvAnticheatKick;} + inline bool GetMvAnticheatAnnounce() {return m_MvAnticheatAnnounce;} + inline uint32 GetMvAnticheatAlarmCount() {return m_MvAnticheatAlarmCount;} + inline uint32 GetMvAnticheatAlarmPeriod() {return m_MvAnticheatAlarmPeriod;} + inline unsigned char GetMvAnticheatBan() {return m_MvAntiCheatBan;} + inline std::string GetMvAnticheatBanTime() {return m_MvAnticheatBanTime;} + inline unsigned char GetMvAnticheatGmLevel() {return m_MvAnticheatGmLevel;} + inline bool GetMvAnticheatKill() {return m_MvAnticheatKill;} + inline float GetMvAnticheatMaxXYT() {return m_MvAnticheatMaxXYT;} + inline uint16 GetMvAnticheatIgnoreAfterTeleport() {return m_MvAnticheatIgnoreAfterTeleport;} + + inline bool GetMvAnticheatSpeedCheck() {return m_MvAnticheatSpeedCheck;} + inline bool GetMvAnticheatWaterCheck() {return m_MvAnticheatWaterCheck;} + inline bool GetMvAnticheatFlyCheck() {return m_MvAnticheatFlyCheck;} + inline bool GetMvAnticheatMountainCheck() {return m_MvAnticheatMountainCheck;} + inline bool GetMvAnticheatJumpCheck() {return m_MvAnticheatJumpCheck;} + inline bool GetMvAnticheatTeleportCheck() {return m_MvAnticheatTeleportCheck;} + inline bool GetMvAnticheatTeleport2PlaneCheck() {return m_MvAnticheatTeleport2PlaneCheck;} + + void ProcessCliCommands(); void QueueCliCommand(CliCommandHolder* commandHolder) { cliCmdQueue.add(commandHolder); } @@ -607,8 +633,10 @@ class World void InitDailyQuestResetTime(); void InitWeeklyQuestResetTime(); + void InitRandomBGResetTime(); void ResetDailyQuests(); void ResetWeeklyQuests(); + void ResetRandomBG(); private: void setConfig(eConfigUInt32Values index, char const* fieldname, uint32 defvalue); void setConfig(eConfigInt32Values index, char const* fieldname, int32 defvalue); @@ -672,6 +700,26 @@ class World static float m_VisibleUnitGreyDistance; static float m_VisibleObjectGreyDistance; + //movement anticheat enable flag + bool m_MvAnticheatEnable; + bool m_MvAnticheatKick; + bool m_MvAnticheatAnnounce; + uint32 m_MvAnticheatAlarmCount; + uint32 m_MvAnticheatAlarmPeriod; + unsigned char m_MvAntiCheatBan; + std::string m_MvAnticheatBanTime; + unsigned char m_MvAnticheatGmLevel; + bool m_MvAnticheatKill; + float m_MvAnticheatMaxXYT; + uint16 m_MvAnticheatIgnoreAfterTeleport; + bool m_MvAnticheatSpeedCheck; + bool m_MvAnticheatWaterCheck; + bool m_MvAnticheatFlyCheck; + bool m_MvAnticheatMountainCheck; + bool m_MvAnticheatJumpCheck; + bool m_MvAnticheatTeleportCheck; + bool m_MvAnticheatTeleport2PlaneCheck; + // CLI command holder to be thread safe ACE_Based::LockedQueue cliCmdQueue; SqlResultQueue *m_resultQueue; @@ -679,6 +727,7 @@ class World // next daily quests reset time time_t m_NextDailyQuestReset; time_t m_NextWeeklyQuestReset; + time_t m_NextRandomBGReset; //Player Queue Queue m_QueuedPlayer; diff --git a/src/game/WorldSession.h b/src/game/WorldSession.h index d9b9759..7c74c0a 100644 --- a/src/game/WorldSession.h +++ b/src/game/WorldSession.h @@ -153,6 +153,9 @@ class MANGOS_DLL_SPEC WorldSession bool PlayerLogout() const { return m_playerLogout; } bool PlayerLogoutWithSave() const { return m_playerLogout && m_playerSave; } + inline bool Anti__CheatOccurred(uint32 CurTime,const char* Reason,float Speed,const char* Op=NULL,float Val1=0.0f,uint32 Val2=0); + bool Anti__ReportCheat(const char* Reason,float Speed,const char* Op=NULL,float Val1=0.0f,uint32 Val2=0); + void SizeError(WorldPacket const& packet, uint32 size) const; void ReadAddonsInfo(WorldPacket &data); diff --git a/src/mangosd/mangosd.conf.dist.in b/src/mangosd/mangosd.conf.dist.in index f464eb8..9032620 100644 --- a/src/mangosd/mangosd.conf.dist.in +++ b/src/mangosd/mangosd.conf.dist.in @@ -366,6 +366,78 @@ GmLogPerAccount = 0 RaLogFile = "" LogColors = "" +# Chat log parameters +ChatLogEnable = 0 + +# If this is enabled, all $d in file names are replaced with current date +# This does include innormative lexics log +ChatLogDateSplit = 1 + +# If this is enabled, UTF8 header is written into new files +ChatLogUTFHeader = 1 + +# If this is enabled, chat log will ignore messages with unprintable chars +ChatLogIgnoreUnprintable = 1 + +# Chat log files +ChatLogChatFile = main_chat-$d.log +ChatLogPartyFile = party_chat-$d.log +ChatLogGuildFile = guild_chat-$d.log +ChatLogWhisperFile = whisper_chat-$d.log +ChatLogChannelFile = channel_chat-$d.log +ChatLogRaidFile = raid_chat-$d.log +ChatLogBattleGroundFile = bg_chat-$d.log + +# Chat log screen logging +ChatLogChatScreen = 0 +ChatLogPartyScreen = 0 +ChatLogGuildScreen = 0 +ChatLogWhisperScreen = 0 +ChatLogChannelScreen = 0 +ChatLogRaidScreen = 0 +ChatLogBattleGroundScreen = 0 + +# Lexics cutter parameters +LexicsCutterEnable = 1 + +LexicsCutterInnormativeCut = 1 +LexicsCutterCutReplacement = &!@^%!^&*!!! +LexicsCutterLogFile = innormative-$d.log +LexicsCutterScreenLog = 0 +LexicsCutterAnalogsFile = letter_analogs.txt +LexicsCutterWordsFile = innormative_words.txt + +# Where to cut lexics +LexicsCutInChat = 1 +LexicsCutInParty = 1 +LexicsCutInGuild = 1 +LexicsCutInWhisper = 1 +LexicsCutInChannel = 1 +LexicsCutInRaid = 1 +LexicsCutInBattleGround = 1 + +# Ignore word spaces like: W O R D +LexicsCutterIgnoreSpaces = 1 + +# Ignore repeats like: WWOOOORRRRRD +LexicsCutterIgnoreRepeats = 1 + +# --- Action: +# --- 0: log only +# --- 1: sheep +# --- 2: stun +# --- 3: kill +# --- 4: leave 5 health +# --- 5: disable chat +# --- 6: stuck (works as stun + 50% health) +# --- 7: resurrection sickness +# --- 8: shear +LexicsCutterAction = 0 +LexicsCutterActionDuration = 60000 + +# Do not perform action on GM if this is enabled +LexicsCutterNoActionOnGM = 1 + ################################################################################################################### # SERVER SETTINGS # @@ -821,6 +893,29 @@ AllowTwoSide.AddFriend = 0 TalentsInspecting = 1 ################################################################################################################### +# AUTO BROADCAST +# +# AutoBroadcast.On +# Enable auto broadcast +# Default: 0 - off +# 1 - on +# +# AutoBroadcast.Center +# Display method +# Default: 0 - announce +# 1 - notify +# 2 - both +# +# AutoBroadcast.Timer +# Timer for auto broadcast +# +################################################################################################################### + +AutoBroadcast.On = 0 +AutoBroadcast.Center = 0 +AutoBroadcast.Timer = 30000 + +################################################################################################################### # CREATURE SETTINGS # # ThreatRadius @@ -1355,6 +1450,10 @@ Death.Bones.BattlegroundOrArena = 1 # Default: 1800000 (30 minutes) # 0 - disable (not recommended) # +# BattleGround.Random.ResetHour +# Hour when random bg reset (0..23) +# Default: 6 +# ################################################################################################################### Battleground.CastDeserter = 1 @@ -1363,6 +1462,7 @@ Battleground.QueueAnnouncer.Start = 0 Battleground.InvitationType = 0 BattleGround.PrematureFinishTimer = 300000 BattleGround.PremadeGroupWaitForMatch = 1800000 +BattleGround.Random.ResetHour = 6 ################################################################################################################### # ARENA CONFIG @@ -1525,6 +1625,106 @@ SOAP.IP = 127.0.0.1 SOAP.Port = 7878 ################################################################################################################### +# MOVEMENT ANTICHEAT +# +# Anticheat.Movement.Enable +# Enable Movement Anticheat +# Default: 0 - off +# 1 - on +# +# Anticheat.Movement.AlarmCount +# Count alarms. After AlarmCount is exceeded, actions are taken against the player. (default 5) +# +# Anticheat.Movement.AlarmTime +# Reset alarm-count after this milliseconds. (default 5000) +# +# Anticheat.Movement.Kill +# Enable Kill cheater +# Default: 0 - off +# 1 - on +# +# Anticheat.Movement.Kick +# Enable Kick cheater +# Default: 0 - off +# 1 - on +# +# Anticheat.Movement.BanType +# Enable Ban cheater +# Default: 0 - off +# 1 - Ban Account +# 2 - Ban IP +# 3 - Ban Account + IP +# +# Anticheat.Movement.Announce +# Enable Announce cheater +# Default: 0 - off +# 1 - on +# +# Anticheat.Movement.DetectSpeedHack +# Turn Speed Check on/off +# Default: 1 (on) +# +# Anticheat.Movement.DetectWaterWalk +# Turn WaterWalk Check on/off +# Default: 1 (on) +# +# Anticheat.Movement.DetectFlyHack +# Turn Fly Hack Check on/off +# Default: 1 (on) +# +# Anticheat.Movement.DetectMountainHack +# Turn Speed Check on/off +# Default: 1 (on) +# Anticheat.Movement.DetectAirJumpHack +# Turn Air/Graviti Jump Check on/off +# Default: 1 (on) +# +# Anticheat.Movement.DetectTeleportHack +# Turn Teleport Check on/off +# Default: 1 (on) +# +# Anticheat.Movement.DetectTeleport2PlaneHack +# Turn Teleport2Plane Check on/off +# Default: 1 (on) +# +# Anticheat.Movement.BanTime +# How long the ban should last. +# Default: 1 Minute - 1m +# +# Anticheat.Movement.MaxXYT +# Max units a player is allowed to travel per millisecond. +# Default: 0.04 (This is ~400% Speed, 0.007 is walk-speed, 310% is 0.0287) +# +# Anticheat.Movement.IgnoreSecAfterTeleport +# After being teleported this number of seconds no cheat is reported. +# Default: 10 seconds - 10 +# +# Anticheat.Movement.GmLevel +# Only accounts that are below this gm-level or exact at the same, are reported for cheating. +# Default: Only normal Players - 0 +# +################################################################################################################### + +Anticheat.Movement.Enable = 1 +Anticheat.Movement.AlarmCount = 5 +Anticheat.Movement.AlarmTime = 5000 +Anticheat.Movement.Kill = 0 +Anticheat.Movement.Kick = 0 +Anticheat.Movement.Announce = 0 +Anticheat.Movement.DetectSpeedHack = 1 +Anticheat.Movement.DetectWaterWalk = 1 +Anticheat.Movement.DetectFlyHack = 1 +Anticheat.Movement.DetectMountainHack = 1 +Anticheat.Movement.DetectAirJumpHack = 1 +Anticheat.Movement.DetectTeleportHack = 1 +Anticheat.Movement.DetectTeleport2PlaneHack = 0 +Anticheat.Movement.BanType = 0 +Anticheat.Movement.BanTime = "1m" +Anticheat.Movement.MaxXYT = 0.04 +Anticheat.Movement.IgnoreSecAfterTeleport = 10 +Anticheat.Movement.GmLevel = 0 + +################################################################################################################### # CharDelete.Method # Character deletion behavior # Default: 0 - Completely remove the character from the database diff --git a/src/shared/Database/DBCFileLoader.cpp b/src/shared/Database/DBCFileLoader.cpp index c5ac9dc..4c661ce 100644 --- a/src/shared/Database/DBCFileLoader.cpp +++ b/src/shared/Database/DBCFileLoader.cpp @@ -135,7 +135,7 @@ uint32 DBCFileLoader::GetFormatRecordSize(const char * format,int32* index_pos) return recordsize; } -char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char**& indexTable) +char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char**& indexTable, uint32 sqlRecordCount, uint32 sqlHighestIndex, char *& sqlDataTable) { /* format STRING, NA, FLOAT,NA,INT <=> @@ -166,6 +166,10 @@ char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char** if(ind>maxi)maxi=ind; } + // If higher index avalible from sql - use it instead of dbcs + if (sqlHighestIndex > maxi) + maxi = sqlHighestIndex; + ++maxi; records=maxi; indexTable=new ptr[maxi]; @@ -173,11 +177,11 @@ char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char** } else { - records = recordCount; - indexTable = new ptr[recordCount]; + records = recordCount + sqlRecordCount; + indexTable = new ptr[recordCount + sqlRecordCount]; } - char* dataTable= new char[recordCount*recordsize]; + char* dataTable= new char[(recordCount + sqlRecordCount)*recordsize]; uint32 offset=0; @@ -215,6 +219,8 @@ char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char** } } + sqlDataTable = dataTable + offset; + return dataTable; } diff --git a/src/shared/Database/DBCFileLoader.h b/src/shared/Database/DBCFileLoader.h index 3564e8c..d3c0fcb 100644 --- a/src/shared/Database/DBCFileLoader.h +++ b/src/shared/Database/DBCFileLoader.h @@ -32,7 +32,9 @@ enum FT_BYTE='b', //uint8 FT_SORT='d', //sorted by this field, field is not included FT_IND='n', //the same,but parsed to data - FT_LOGIC='l' //Logical (boolean) + FT_LOGIC='l', //Logical (boolean) + FT_SQL_PRESENT='p', //Used in sql format to mark column present in sql dbc + FT_SQL_ABSENT='a' //Used in sql format to mark column absent in sql dbc }; class DBCFileLoader @@ -87,11 +89,12 @@ class DBCFileLoader Record getRecord(size_t id); /// Get begin iterator over records - uint32 GetNumRows() const { return recordCount;} + uint32 GetNumRows() const { return recordCount; } + uint32 GetRowSize() const { return recordSize; } uint32 GetCols() const { return fieldCount; } uint32 GetOffset(size_t id) const { return (fieldsOffset != NULL && id < fieldCount) ? fieldsOffset[id] : 0; } bool IsLoaded() {return (data!=NULL);} - char* AutoProduceData(const char* fmt, uint32& count, char**& indexTable); + char* AutoProduceData(const char* fmt, uint32& count, char**& indexTable, uint32 sqlRecordCount, uint32 sqlHighestIndex, char *& sqlDataTable); char* AutoProduceStrings(const char* fmt, char* dataTable); static uint32 GetFormatRecordSize(const char * format, int32 * index_pos = NULL); private: diff --git a/src/shared/Database/DBCStore.h b/src/shared/Database/DBCStore.h index 17a64aa..01e6955 100644 --- a/src/shared/Database/DBCStore.h +++ b/src/shared/Database/DBCStore.h @@ -20,6 +20,47 @@ #define DBCSTORE_H #include "DBCFileLoader.h" +#include "Database/Field.h" +#include "Database/QueryResult.h" +#include "SQLStorage.h" + +struct SqlDbc +{ + const std::string * formatString; + const std::string * indexName; + std::string sqlTableName; + int32 indexPos; + int32 sqlIndexPos; + SqlDbc(const std::string * _filename, const std::string * _format, const std::string * _idname, const char * fmt) + :formatString(_format),sqlIndexPos(0), indexName (_idname) + { + // Convert dbc file name to sql table name + sqlTableName = *_filename; + for (uint32 i = 0; i< sqlTableName.size(); ++i) + { + if (isalpha(sqlTableName[i])) + sqlTableName[i] = tolower(sqlTableName[i]); + else if (sqlTableName[i] == '.') + sqlTableName[i] = '_'; + } + + // Get sql index position + DBCFileLoader::GetFormatRecordSize(fmt, &indexPos); + if (indexPos>=0) + { + for (uint32 x=0; x < formatString->size(); x++) + { + // Count only fields present in sql + if ((*formatString)[x] == FT_SQL_PRESENT) + { + if (x == indexPos) + break; + ++sqlIndexPos; + } + } + } + } +}; template class DBCStorage @@ -34,21 +75,151 @@ class DBCStorage char const* GetFormat() const { return fmt; } uint32 GetFieldCount() const { return fieldCount; } - bool Load(char const* fn) + bool Load(char const* fn, SqlDbc * sql) { DBCFileLoader dbc; // Check if load was sucessful, only then continue if(!dbc.Load(fn, fmt)) return false; + uint32 sqlRecordCount = 0; + uint32 sqlHighestIndex = 0; + Field *fields = NULL; + QueryResult *result = (QueryResult *)NULL; + // Load data from sql + if (sql) + { + std::string query = "SELECT * FROM " + sql->sqlTableName; + if (sql->indexPos >= 0) + query +=" ORDER BY + " + *sql->indexName + " DESC"; + query += ";"; + + result = WorldDatabase.Query(query.c_str()); + if (result) + { + sqlRecordCount = result->GetRowCount(); + if (sql->indexPos >= 0) + { + fields = result->Fetch(); + sqlHighestIndex = fields[sql->sqlIndexPos].GetUInt32(); + } + // Check if sql index pos is valid + if (int32(result->GetFieldCount()-1) < sql->sqlIndexPos) + { + sLog.outError("Invalid index pos for dbc:'%s'", sql->sqlTableName.c_str()); + return false; + } + } + } + char * sqlDataTable; fieldCount = dbc.GetCols(); // load raw non-string data - m_dataTable = (T*)dbc.AutoProduceData(fmt,nCount,(char**&)indexTable); + m_dataTable = (T*)dbc.AutoProduceData(fmt,nCount,(char**&)indexTable, sqlRecordCount, sqlHighestIndex, sqlDataTable); // load strings from dbc data m_stringPoolList.push_back(dbc.AutoProduceStrings(fmt,(char*)m_dataTable)); + // Insert sql data into arrays + if (result) + { + if (indexTable) + { + uint32 offset = 0; + uint32 rowIndex = dbc.GetNumRows(); + do + { + if (!fields) + fields = result->Fetch(); + + if(sql->indexPos >= 0) + { + uint32 id = fields[sql->sqlIndexPos].GetUInt32(); + if (indexTable[id]) + { + sLog.outError("Index %d already exists in dbc:'%s'", id, sql->sqlTableName.c_str()); + return false; + } + indexTable[id]=(T*)&sqlDataTable[offset]; + } + else + indexTable[rowIndex]=(T*)&sqlDataTable[offset]; + uint32 columnNumber = 0; + uint32 sqlColumnNumber = 0; + + for (; columnNumber < sql->formatString->size(); ++columnNumber) + { + if ((*sql->formatString)[columnNumber] == FT_SQL_ABSENT) + { + switch(fmt[columnNumber]) + { + case FT_FLOAT: + *((float*)(&sqlDataTable[offset]))= 0.0f; + offset+=4; + break; + case FT_IND: + case FT_INT: + *((uint32*)(&sqlDataTable[offset]))=uint32(0); + offset+=4; + break; + case FT_BYTE: + *((uint8*)(&sqlDataTable[offset]))=uint8(0); + offset+=1; + break; + case FT_STRING: + // Beginning of the pool - empty string + *((char**)(&sqlDataTable[offset]))=m_stringPoolList.back(); + offset+=sizeof(char*); + break; + } + } + else if ((*sql->formatString)[columnNumber] == FT_SQL_PRESENT) + { + bool validSqlColumn = true; + switch(fmt[columnNumber]) + { + case FT_FLOAT: + *((float*)(&sqlDataTable[offset]))=fields[sqlColumnNumber].GetFloat(); + offset+=4; + break; + case FT_IND: + case FT_INT: + *((uint32*)(&sqlDataTable[offset]))=fields[sqlColumnNumber].GetUInt32(); + offset+=4; + break; + case FT_BYTE: + *((uint8*)(&sqlDataTable[offset]))=fields[sqlColumnNumber].GetUInt8(); + offset+=1; + break; + case FT_STRING: + sLog.outError("Unsupported data type in table '%s' at char %d", sql->sqlTableName.c_str(), columnNumber); + return false; + case FT_SORT: + break; + default: + validSqlColumn = false; + } + if (validSqlColumn && (columnNumber != (sql->formatString->size()-1))) + sqlColumnNumber++; + } + else + { + sLog.outError("Incorrect sql format string '%s' at char %d", sql->sqlTableName.c_str(), columnNumber); + return false; + } + } + if (sqlColumnNumber != (result->GetFieldCount()-1)) + { + sLog.outError("SQL and DBC format strings are not matching for table: '%s'", sql->sqlTableName.c_str()); + return false; + } + + fields = NULL; + ++rowIndex; + }while (result->NextRow()); + } + } + // error in dbc file at loading if NULL return indexTable!=NULL; } diff --git a/src/shared/Database/DatabaseMysql.cpp b/src/shared/Database/DatabaseMysql.cpp index 02d1193..2b555d8 100644 --- a/src/shared/Database/DatabaseMysql.cpp +++ b/src/shared/Database/DatabaseMysql.cpp @@ -166,12 +166,27 @@ bool DatabaseMysql::Initialize(const char *infoString) PExecute("SET NAMES `utf8`"); PExecute("SET CHARACTER SET `utf8`"); +#if MYSQL_VERSION_ID >= 50003 + my_bool my_true = (my_bool)1; + if (mysql_options(mMysql, MYSQL_OPT_RECONNECT, &my_true)) + { + sLog.outDetail("Failed to turn on MYSQL_OPT_RECONNECT."); + } + else + { + sLog.outDetail("Successfully turned on MYSQL_OPT_RECONNECT."); + } +#else + sLog.outDetail("Your mySQL client lib version does not support reconnecting after a timeout."); + sLog.outDetail("If this causes you any trouble we advice you to upgrade"); + sLog.outDetail("your mySQL client libs to at least mySQL 5.0.13 to resolve this problem."); +#endif return true; } else { sLog.outError( "Could not connect to MySQL database at %s: %s\n", - host.c_str(),mysql_error(mysqlInit)); + host.c_str(),mysql_error(mysqlInit)); mysql_close(mysqlInit); return false; } diff --git a/win/VC100/game.vcxproj b/win/VC100/game.vcxproj index 4525ed4..ad8bb59 100644 --- a/win/VC100/game.vcxproj +++ b/win/VC100/game.vcxproj @@ -379,6 +379,8 @@ + + @@ -527,6 +529,8 @@ + + diff --git a/win/VC80/game.vcproj b/win/VC80/game.vcproj index d083dbd..362e66f 100644 --- a/win/VC80/game.vcproj +++ b/win/VC80/game.vcproj @@ -721,6 +721,22 @@ RelativePath="..\..\src\game\ChatHandler.cpp" > + + + + + + + + diff --git a/win/VC90/game.vcproj b/win/VC90/game.vcproj index 1d491a2..bc801e0 100644 --- a/win/VC90/game.vcproj +++ b/win/VC90/game.vcproj @@ -715,6 +715,22 @@ > + + + + + + + +