diff --git a/src/game/BattleGround/BattleGround.cpp b/src/game/BattleGround/BattleGround.cpp index 351d1182d..af4915092 100644 --- a/src/game/BattleGround/BattleGround.cpp +++ b/src/game/BattleGround/BattleGround.cpp @@ -213,32 +213,32 @@ BattleGround::BattleGround() m_MapId = 0; m_Map = NULL; - m_TeamStartLocX[BG_TEAM_ALLIANCE] = 0; - m_TeamStartLocX[BG_TEAM_HORDE] = 0; + m_TeamStartLocX[TEAM_INDEX_ALLIANCE] = 0; + m_TeamStartLocX[TEAM_INDEX_HORDE] = 0; - m_TeamStartLocY[BG_TEAM_ALLIANCE] = 0; - m_TeamStartLocY[BG_TEAM_HORDE] = 0; + m_TeamStartLocY[TEAM_INDEX_ALLIANCE] = 0; + m_TeamStartLocY[TEAM_INDEX_HORDE] = 0; - m_TeamStartLocZ[BG_TEAM_ALLIANCE] = 0; - m_TeamStartLocZ[BG_TEAM_HORDE] = 0; + m_TeamStartLocZ[TEAM_INDEX_ALLIANCE] = 0; + m_TeamStartLocZ[TEAM_INDEX_HORDE] = 0; - m_TeamStartLocO[BG_TEAM_ALLIANCE] = 0; - m_TeamStartLocO[BG_TEAM_HORDE] = 0; + m_TeamStartLocO[TEAM_INDEX_ALLIANCE] = 0; + m_TeamStartLocO[TEAM_INDEX_HORDE] = 0; - m_ArenaTeamIds[BG_TEAM_ALLIANCE] = 0; - m_ArenaTeamIds[BG_TEAM_HORDE] = 0; + m_ArenaTeamIds[TEAM_INDEX_ALLIANCE] = 0; + m_ArenaTeamIds[TEAM_INDEX_HORDE] = 0; - m_ArenaTeamRatingChanges[BG_TEAM_ALLIANCE] = 0; - m_ArenaTeamRatingChanges[BG_TEAM_HORDE] = 0; + m_ArenaTeamRatingChanges[TEAM_INDEX_ALLIANCE] = 0; + m_ArenaTeamRatingChanges[TEAM_INDEX_HORDE] = 0; - m_BgRaids[BG_TEAM_ALLIANCE] = NULL; - m_BgRaids[BG_TEAM_HORDE] = NULL; + m_BgRaids[TEAM_INDEX_ALLIANCE] = NULL; + m_BgRaids[TEAM_INDEX_HORDE] = NULL; - m_PlayersCount[BG_TEAM_ALLIANCE] = 0; - m_PlayersCount[BG_TEAM_HORDE] = 0; + m_PlayersCount[TEAM_INDEX_ALLIANCE] = 0; + m_PlayersCount[TEAM_INDEX_HORDE] = 0; - m_TeamScores[BG_TEAM_ALLIANCE] = 0; - m_TeamScores[BG_TEAM_HORDE] = 0; + m_TeamScores[TEAM_INDEX_ALLIANCE] = 0; + m_TeamScores[TEAM_INDEX_HORDE] = 0; m_PrematureCountDown = false; m_PrematureCountDownTimer = 0; @@ -459,6 +459,14 @@ void BattleGround::Update(uint32 diff) /*********************************************************/ /*** BATTLEGROUND ENDING SYSTEM ***/ /*********************************************************/ + if (GetStatus() == STATUS_IN_PROGRESS && isArena()) + { + // after 45 minutes without one team losing, the arena closes with no winner and -16 rating change for both + if (m_StartTime > uint32(m_StartDelayTimes[BG_STARTING_EVENT_FIRST] + ARENA_FORCED_DRAW)) + { + EndBattleGround(TEAM_NONE); + } + } if (GetStatus() == STATUS_WAIT_LEAVE) { @@ -485,7 +493,7 @@ void BattleGround::Update(uint32 diff) void BattleGround::SetTeamStartLoc(Team team, float X, float Y, float Z, float O) { - BattleGroundTeamIndex teamIdx = GetTeamIndexByTeamId(team); + PvpTeamIndex teamIdx = GetTeamIndexByTeamId(team); m_TeamStartLocX[teamIdx] = X; m_TeamStartLocY[teamIdx] = Y; m_TeamStartLocZ[teamIdx] = Z; @@ -1322,18 +1330,20 @@ void BattleGround::AddOrSetPlayerToCorrectBgGroup(Player* plr, ObjectGuid plr_gu } // This method should be called when player logs into running battleground -void BattleGround::EventPlayerLoggedIn(Player* player, ObjectGuid plr_guid) +void BattleGround::EventPlayerLoggedIn(Player* player) { + ObjectGuid playerGuid = player->GetObjectGuid(); + // player is correct pointer for (OfflineQueue::iterator itr = m_OfflineQueue.begin(); itr != m_OfflineQueue.end(); ++itr) { - if (*itr == plr_guid) + if (*itr == playerGuid) { m_OfflineQueue.erase(itr); break; } } - m_Players[plr_guid].OfflineRemoveTime = 0; + m_Players[playerGuid].OfflineRemoveTime = 0; PlayerAddedToBGCheckIfBGIsRunning(player); // if battleground is starting, then add preparation aura // we don't have to do that, because preparation aura isn't removed when player logs out @@ -1783,7 +1793,7 @@ WorldSafeLocsEntry const* BattleGround::GetClosestGraveYard(Player* player) bool BattleGround::IsTeamScoreInRange(Team team, uint32 minScore, uint32 maxScore) const { - BattleGroundTeamIndex team_idx = GetTeamIndexByTeamId(team); + PvpTeamIndex team_idx = GetTeamIndexByTeamId(team); uint32 score = (m_TeamScores[team_idx] < 0) ? 0 : uint32(m_TeamScores[team_idx]); return score >= minScore && score <= maxScore; } diff --git a/src/game/BattleGround/BattleGround.h b/src/game/BattleGround/BattleGround.h index 31948337a..0bf2ffdb4 100644 --- a/src/game/BattleGround/BattleGround.h +++ b/src/game/BattleGround/BattleGround.h @@ -112,13 +112,14 @@ enum BattleGroundTimeIntervals { RESURRECTION_INTERVAL = 30000, // ms INVITATION_REMIND_TIME = 20000, // ms - INVITE_ACCEPT_WAIT_TIME = 40000, // ms + INVITE_ACCEPT_WAIT_TIME = 60000, // ms TIME_TO_AUTOREMOVE = 120000, // ms MAX_OFFLINE_TIME = 300, // secs RESPAWN_ONE_DAY = 86400, // secs RESPAWN_IMMEDIATELY = 0, // secs BUFF_RESPAWN_TIME = 180, // secs ARENA_SPAWN_BUFF_OBJECTS = 90000, // ms - 90sec after start + ARENA_FORCED_DRAW = 2700000, // ms - 45min after start BATTLEGROUND_COUNTDOWN_MAX = 120, // secs ARENA_COUNTDOWN_MAX = 60, // secs }; @@ -212,14 +213,6 @@ enum BattleGroundType TYPE_ARENA = 4 }; -enum BattleGroundTeamIndex -{ - BG_TEAM_ALLIANCE = 0, - BG_TEAM_HORDE = 1 -}; - -#define BG_TEAMS_COUNT 2 - enum BattleGroundStartingEvents { BG_STARTING_EVENT_NONE = 0x00, @@ -432,7 +425,7 @@ class BattleGround void SetTeamStartLoc(Team team, float X, float Y, float Z, float O); void GetTeamStartLoc(Team team, float& X, float& Y, float& Z, float& O) const { - BattleGroundTeamIndex idx = GetTeamIndexByTeamId(team); + PvpTeamIndex idx = GetTeamIndexByTeamId(team); X = m_TeamStartLocX[idx]; Y = m_TeamStartLocY[idx]; Z = m_TeamStartLocZ[idx]; @@ -477,7 +470,7 @@ class BattleGround virtual void UpdatePlayerScore(Player* Source, uint32 type, uint32 value); - static BattleGroundTeamIndex GetTeamIndexByTeamId(Team team) { return team == ALLIANCE ? BG_TEAM_ALLIANCE : BG_TEAM_HORDE; } + static PvpTeamIndex GetTeamIndexByTeamId(Team team) { return team == ALLIANCE ? TEAM_INDEX_ALLIANCE : TEAM_INDEX_HORDE; } uint32 GetPlayersCountByTeam(Team team) const { return m_PlayersCount[GetTeamIndexByTeamId(team)]; } uint32 GetAlivePlayersCountByTeam(Team team) const; // used in arenas to correctly handle death in spirit of redemption / last stand etc. (killer = killed) cases void UpdatePlayersCountByTeam(Team team, bool remove) @@ -518,7 +511,7 @@ class BattleGround virtual void EventPlayerDroppedFlag(Player* /*player*/) {} virtual void EventPlayerClickedOnFlag(Player* /*player*/, GameObject* /*target_obj*/) {} virtual void EventPlayerCapturedFlag(Player* /*player*/) {} - void EventPlayerLoggedIn(Player* player, ObjectGuid plr_guid); + void EventPlayerLoggedIn(Player* player); void EventPlayerLoggedOut(Player* player); /* Death related */ @@ -563,11 +556,11 @@ class BattleGround Team GetPlayerTeam(ObjectGuid guid); uint32 GetRemainingTime() const { return m_EndTime; } static Team GetOtherTeam(Team team) { return team ? ((team == ALLIANCE) ? HORDE : ALLIANCE) : TEAM_NONE; } - static BattleGroundTeamIndex GetOtherTeamIndex(BattleGroundTeamIndex teamIdx) { return teamIdx == BG_TEAM_ALLIANCE ? BG_TEAM_HORDE : BG_TEAM_ALLIANCE; } + static PvpTeamIndex GetOtherTeamIndex(PvpTeamIndex teamIdx) { return teamIdx == TEAM_INDEX_ALLIANCE ? TEAM_INDEX_HORDE : TEAM_INDEX_ALLIANCE; } bool IsPlayerInBattleGround(ObjectGuid guid); /* virtual score-array - get's used in bg-subclasses */ - int32 m_TeamScores[BG_TEAMS_COUNT]; + int32 m_TeamScores[PVP_TEAM_COUNT]; struct EventObjects { @@ -640,15 +633,15 @@ class BattleGround uint32 m_InvitedHorde; /* Raid Group */ - Group* m_BgRaids[BG_TEAMS_COUNT]; // 0 - alliance, 1 - horde + Group* m_BgRaids[PVP_TEAM_COUNT]; // 0 - alliance, 1 - horde /* Players count by team */ - uint32 m_PlayersCount[BG_TEAMS_COUNT]; + uint32 m_PlayersCount[PVP_TEAM_COUNT]; /* Arena team ids by team */ - uint32 m_ArenaTeamIds[BG_TEAMS_COUNT]; + uint32 m_ArenaTeamIds[PVP_TEAM_COUNT]; - int32 m_ArenaTeamRatingChanges[BG_TEAMS_COUNT]; + int32 m_ArenaTeamRatingChanges[PVP_TEAM_COUNT]; /* Limits */ uint32 m_LevelMin; @@ -661,10 +654,10 @@ class BattleGround /* Start location */ uint32 m_MapId; BattleGroundMap* m_Map; - float m_TeamStartLocX[BG_TEAMS_COUNT]; - float m_TeamStartLocY[BG_TEAMS_COUNT]; - float m_TeamStartLocZ[BG_TEAMS_COUNT]; - float m_TeamStartLocO[BG_TEAMS_COUNT]; + float m_TeamStartLocX[PVP_TEAM_COUNT]; + float m_TeamStartLocY[PVP_TEAM_COUNT]; + float m_TeamStartLocZ[PVP_TEAM_COUNT]; + float m_TeamStartLocO[PVP_TEAM_COUNT]; }; // helper functions for world state list fill diff --git a/src/game/BattleGround/BattleGroundAB.cpp b/src/game/BattleGround/BattleGroundAB.cpp index d35f6a2e5..da1142477 100644 --- a/src/game/BattleGround/BattleGroundAB.cpp +++ b/src/game/BattleGround/BattleGroundAB.cpp @@ -53,7 +53,7 @@ void BattleGroundAB::Update(uint32 diff) if (GetStatus() == STATUS_IN_PROGRESS) { - int team_points[BG_TEAMS_COUNT] = { 0, 0 }; + int team_points[PVP_TEAM_COUNT] = { 0, 0 }; for (uint8 node = 0; node < BG_AB_NODES_MAX; ++node) { @@ -100,13 +100,13 @@ void BattleGroundAB::Update(uint32 diff) } } - for (uint8 team = 0; team < BG_TEAMS_COUNT; ++team) + for (uint8 team = 0; team < PVP_TEAM_COUNT; ++team) if (m_Nodes[node] == team + BG_AB_NODE_TYPE_OCCUPIED) ++team_points[team]; } // Accumulate points - for (uint8 team = 0; team < BG_TEAMS_COUNT; ++team) + for (uint8 team = 0; team < PVP_TEAM_COUNT; ++team) { int points = team_points[team]; if (!points) @@ -120,17 +120,17 @@ void BattleGroundAB::Update(uint32 diff) m_ReputationScoreTics[team] += BG_AB_TickPoints[points]; if (m_ReputationScoreTics[team] >= m_ReputationTics) { - (team == BG_TEAM_ALLIANCE) ? RewardReputationToTeam(509, 10, ALLIANCE) : RewardReputationToTeam(510, 10, HORDE); + (team == TEAM_INDEX_ALLIANCE) ? RewardReputationToTeam(509, 10, ALLIANCE) : RewardReputationToTeam(510, 10, HORDE); m_ReputationScoreTics[team] -= m_ReputationTics; } if (m_honorScoreTicks[team] >= m_honorTicks) { - RewardHonorToTeam(GetBonusHonorFromKill(1), (team == BG_TEAM_ALLIANCE) ? ALLIANCE : HORDE); + RewardHonorToTeam(GetBonusHonorFromKill(1), (team == TEAM_INDEX_ALLIANCE) ? ALLIANCE : HORDE); m_honorScoreTicks[team] -= m_honorTicks; } if (!m_IsInformedNearVictory && m_TeamScores[team] > BG_AB_WARNING_NEAR_VICTORY_SCORE) { - if (team == BG_TEAM_ALLIANCE) + if (team == TEAM_INDEX_ALLIANCE) SendMessageToAll(LANG_BG_AB_A_NEAR_VICTORY, CHAT_MSG_BG_SYSTEM_NEUTRAL); else SendMessageToAll(LANG_BG_AB_H_NEAR_VICTORY, CHAT_MSG_BG_SYSTEM_NEUTRAL); @@ -140,24 +140,24 @@ void BattleGroundAB::Update(uint32 diff) if (m_TeamScores[team] > BG_AB_MAX_TEAM_SCORE) m_TeamScores[team] = BG_AB_MAX_TEAM_SCORE; - if (team == BG_TEAM_ALLIANCE) + if (team == TEAM_INDEX_ALLIANCE) UpdateWorldState(BG_AB_OP_RESOURCES_ALLY, m_TeamScores[team]); - if (team == BG_TEAM_HORDE) + if (team == TEAM_INDEX_HORDE) UpdateWorldState(BG_AB_OP_RESOURCES_HORDE, m_TeamScores[team]); // update achievement flags // we increased m_TeamScores[team] so we just need to check if it is 500 more than other teams resources // horde will be a bit disadvantaged, but we can assume that points aren't updated for both team in same Update() call - uint8 otherTeam = (team + 1) % BG_TEAMS_COUNT; + uint8 otherTeam = (team + 1) % PVP_TEAM_COUNT; if (m_TeamScores[team] > m_TeamScores[otherTeam] + 500) m_TeamScores500Disadvantage[otherTeam] = true; } } // Test win condition - if (m_TeamScores[BG_TEAM_ALLIANCE] >= BG_AB_MAX_TEAM_SCORE) + if (m_TeamScores[TEAM_INDEX_ALLIANCE] >= BG_AB_MAX_TEAM_SCORE) EndBattleGround(ALLIANCE); - if (m_TeamScores[BG_TEAM_HORDE] >= BG_AB_MAX_TEAM_SCORE) + if (m_TeamScores[TEAM_INDEX_HORDE] >= BG_AB_MAX_TEAM_SCORE) EndBattleGround(HORDE); } } @@ -277,8 +277,8 @@ void BattleGroundAB::FillInitialWorldStates(WorldPacket& data, uint32& count) // Team scores FillInitialWorldState(data, count, BG_AB_OP_RESOURCES_MAX, BG_AB_MAX_TEAM_SCORE); FillInitialWorldState(data, count, BG_AB_OP_RESOURCES_WARNING, BG_AB_WARNING_NEAR_VICTORY_SCORE); - FillInitialWorldState(data, count, BG_AB_OP_RESOURCES_ALLY, m_TeamScores[BG_TEAM_ALLIANCE]); - FillInitialWorldState(data, count, BG_AB_OP_RESOURCES_HORDE, m_TeamScores[BG_TEAM_HORDE]); + FillInitialWorldState(data, count, BG_AB_OP_RESOURCES_ALLY, m_TeamScores[TEAM_INDEX_ALLIANCE]); + FillInitialWorldState(data, count, BG_AB_OP_RESOURCES_HORDE, m_TeamScores[TEAM_INDEX_HORDE]); // other unknown FillInitialWorldState(data, count, 0x745, 0x2); // 37 1861 unk @@ -333,7 +333,7 @@ void BattleGroundAB::EventPlayerClickedOnFlag(Player* source, GameObject* target return; BG_AB_Nodes node = BG_AB_Nodes(event); - BattleGroundTeamIndex teamIndex = GetTeamIndexByTeamId(source->GetTeam()); + PvpTeamIndex teamIndex = GetTeamIndexByTeamId(source->GetTeam()); // Check if player really could use this banner, not cheated if (!(m_Nodes[node] == 0 || teamIndex == m_Nodes[node] % 2)) @@ -376,7 +376,7 @@ void BattleGroundAB::EventPlayerClickedOnFlag(Player* source, GameObject* target _SendNodeUpdate(node); m_NodeTimers[node] = BG_AB_FLAG_CAPTURING_TIME; - if (teamIndex == BG_TEAM_ALLIANCE) + if (teamIndex == TEAM_INDEX_ALLIANCE) SendMessage2ToAll(LANG_BG_AB_NODE_ASSAULTED, CHAT_MSG_BG_SYSTEM_ALLIANCE, source, _GetNodeNameId(node)); else SendMessage2ToAll(LANG_BG_AB_NODE_ASSAULTED, CHAT_MSG_BG_SYSTEM_HORDE, source, _GetNodeNameId(node)); @@ -391,14 +391,14 @@ void BattleGroundAB::EventPlayerClickedOnFlag(Player* source, GameObject* target _CreateBanner(node, BG_AB_NODE_TYPE_OCCUPIED, teamIndex, true); _SendNodeUpdate(node); m_NodeTimers[node] = 0; - _NodeOccupied(node, (teamIndex == BG_TEAM_ALLIANCE) ? ALLIANCE : HORDE); + _NodeOccupied(node, (teamIndex == TEAM_INDEX_ALLIANCE) ? ALLIANCE : HORDE); - if (teamIndex == BG_TEAM_ALLIANCE) + if (teamIndex == TEAM_INDEX_ALLIANCE) SendMessage2ToAll(LANG_BG_AB_NODE_DEFENDED, CHAT_MSG_BG_SYSTEM_ALLIANCE, source, _GetNodeNameId(node)); else SendMessage2ToAll(LANG_BG_AB_NODE_DEFENDED, CHAT_MSG_BG_SYSTEM_HORDE, source, _GetNodeNameId(node)); } - sound = (teamIndex == BG_TEAM_ALLIANCE) ? BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE : BG_AB_SOUND_NODE_ASSAULTED_HORDE; + sound = (teamIndex == TEAM_INDEX_ALLIANCE) ? BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE : BG_AB_SOUND_NODE_ASSAULTED_HORDE; } // If node is occupied, change to enemy-contested else @@ -411,18 +411,18 @@ void BattleGroundAB::EventPlayerClickedOnFlag(Player* source, GameObject* target _SendNodeUpdate(node); m_NodeTimers[node] = BG_AB_FLAG_CAPTURING_TIME; - if (teamIndex == BG_TEAM_ALLIANCE) + if (teamIndex == TEAM_INDEX_ALLIANCE) SendMessage2ToAll(LANG_BG_AB_NODE_ASSAULTED, CHAT_MSG_BG_SYSTEM_ALLIANCE, source, _GetNodeNameId(node)); else SendMessage2ToAll(LANG_BG_AB_NODE_ASSAULTED, CHAT_MSG_BG_SYSTEM_HORDE, source, _GetNodeNameId(node)); - sound = (teamIndex == BG_TEAM_ALLIANCE) ? BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE : BG_AB_SOUND_NODE_ASSAULTED_HORDE; + sound = (teamIndex == TEAM_INDEX_ALLIANCE) ? BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE : BG_AB_SOUND_NODE_ASSAULTED_HORDE; } // If node is occupied again, send "X has taken the Y" msg. if (m_Nodes[node] >= BG_AB_NODE_TYPE_OCCUPIED) { - if (teamIndex == BG_TEAM_ALLIANCE) + if (teamIndex == TEAM_INDEX_ALLIANCE) SendMessage2ToAll(LANG_BG_AB_NODE_TAKEN, CHAT_MSG_BG_SYSTEM_ALLIANCE, NULL, LANG_BG_ALLY, _GetNodeNameId(node)); else SendMessage2ToAll(LANG_BG_AB_NODE_TAKEN, CHAT_MSG_BG_SYSTEM_HORDE, NULL, LANG_BG_HORDE, _GetNodeNameId(node)); @@ -435,7 +435,7 @@ void BattleGroundAB::Reset() // call parent's class reset BattleGround::Reset(); - for (uint8 i = 0; i < BG_TEAMS_COUNT; ++i) + for (uint8 i = 0; i < PVP_TEAM_COUNT; ++i) { m_TeamScores[i] = 0; m_lastTick[i] = 0; @@ -477,7 +477,7 @@ void BattleGroundAB::EndBattleGround(Team winner) WorldSafeLocsEntry const* BattleGroundAB::GetClosestGraveYard(Player* player) { - BattleGroundTeamIndex teamIndex = GetTeamIndexByTeamId(player->GetTeam()); + PvpTeamIndex teamIndex = GetTeamIndexByTeamId(player->GetTeam()); // Is there any occupied node for this team? std::vector nodes; diff --git a/src/game/BattleGround/BattleGroundAB.h b/src/game/BattleGround/BattleGroundAB.h index 3c5cdfed3..de82130f5 100644 --- a/src/game/BattleGround/BattleGroundAB.h +++ b/src/game/BattleGround/BattleGroundAB.h @@ -153,6 +153,10 @@ class BattleGroundABScore : public BattleGroundScore public: BattleGroundABScore(): BasesAssaulted(0), BasesDefended(0) {}; virtual ~BattleGroundABScore() {}; + + uint32 GetAttr1() const { return BasesAssaulted; } + uint32 GetAttr2() const { return BasesDefended; } + uint32 BasesAssaulted; uint32 BasesDefended; }; @@ -207,13 +211,13 @@ class BattleGroundAB : public BattleGround uint8 m_prevNodes[BG_AB_NODES_MAX]; // used for performant wordlstate-updating BG_AB_BannerTimer m_BannerTimers[BG_AB_NODES_MAX]; uint32 m_NodeTimers[BG_AB_NODES_MAX]; - uint32 m_lastTick[BG_TEAMS_COUNT]; - uint32 m_honorScoreTicks[BG_TEAMS_COUNT]; - uint32 m_ReputationScoreTics[BG_TEAMS_COUNT]; + uint32 m_lastTick[PVP_TEAM_COUNT]; + uint32 m_honorScoreTicks[PVP_TEAM_COUNT]; + uint32 m_ReputationScoreTics[PVP_TEAM_COUNT]; bool m_IsInformedNearVictory; uint32 m_honorTicks; uint32 m_ReputationTics; // need for achievements - bool m_TeamScores500Disadvantage[BG_TEAMS_COUNT]; + bool m_TeamScores500Disadvantage[PVP_TEAM_COUNT]; }; #endif diff --git a/src/game/BattleGround/BattleGroundAV.cpp b/src/game/BattleGround/BattleGroundAV.cpp index 266f386cb..cf177a193 100644 --- a/src/game/BattleGround/BattleGroundAV.cpp +++ b/src/game/BattleGround/BattleGroundAV.cpp @@ -78,7 +78,7 @@ void BattleGroundAV::HandleKillUnit(Creature* creature, Player* killer) return; RewardReputationToTeam(BG_AV_FACTION_H, m_RepCaptain, HORDE); RewardHonorToTeam(GetBonusHonorFromKill(BG_AV_KILL_CAPTAIN), HORDE); - UpdateScore(BG_TEAM_ALLIANCE, (-1) * BG_AV_RES_CAPTAIN); + UpdateScore(TEAM_INDEX_ALLIANCE, (-1) * BG_AV_RES_CAPTAIN); // spawn destroyed aura SpawnEvent(BG_AV_NodeEventCaptainDead_A, 0, true); break; @@ -87,7 +87,7 @@ void BattleGroundAV::HandleKillUnit(Creature* creature, Player* killer) return; RewardReputationToTeam(BG_AV_FACTION_A, m_RepCaptain, ALLIANCE); RewardHonorToTeam(GetBonusHonorFromKill(BG_AV_KILL_CAPTAIN), ALLIANCE); - UpdateScore(BG_TEAM_HORDE, (-1) * BG_AV_RES_CAPTAIN); + UpdateScore(TEAM_INDEX_HORDE, (-1) * BG_AV_RES_CAPTAIN); // spawn destroyed aura SpawnEvent(BG_AV_NodeEventCaptainDead_H, 0, true); break; @@ -211,10 +211,10 @@ void BattleGroundAV::HandleQuestComplete(uint32 questid, Player* player) RewardReputationToTeam((player->GetTeam() == ALLIANCE) ? BG_AV_FACTION_A : BG_AV_FACTION_H, reputation, player->GetTeam()); } -void BattleGroundAV::UpdateScore(BattleGroundTeamIndex teamIdx, int32 points) +void BattleGroundAV::UpdateScore(PvpTeamIndex teamIdx, int32 points) { // note: to remove reinforcements points must be negative, for adding reinforcements points must be positive - MANGOS_ASSERT(teamIdx == BG_TEAM_ALLIANCE || teamIdx == BG_TEAM_HORDE); + MANGOS_ASSERT(teamIdx == TEAM_INDEX_ALLIANCE || teamIdx == TEAM_INDEX_HORDE); m_TeamScores[teamIdx] += points; // m_TeamScores is int32 - so no problems here if (points < 0) @@ -223,17 +223,17 @@ void BattleGroundAV::UpdateScore(BattleGroundTeamIndex teamIdx, int32 points) { m_TeamScores[teamIdx] = 0; // other team will win: - EndBattleGround((teamIdx == BG_TEAM_ALLIANCE) ? HORDE : ALLIANCE); + EndBattleGround((teamIdx == TEAM_INDEX_ALLIANCE) ? HORDE : ALLIANCE); } else if (!m_IsInformedNearLose[teamIdx] && m_TeamScores[teamIdx] < BG_AV_SCORE_NEAR_LOSE) { - SendMessageToAll((teamIdx == BG_TEAM_HORDE) ? LANG_BG_AV_H_NEAR_LOSE : LANG_BG_AV_A_NEAR_LOSE, CHAT_MSG_BG_SYSTEM_NEUTRAL); + SendMessageToAll((teamIdx == TEAM_INDEX_HORDE) ? LANG_BG_AV_H_NEAR_LOSE : LANG_BG_AV_A_NEAR_LOSE, CHAT_MSG_BG_SYSTEM_NEUTRAL); PlaySoundToAll(BG_AV_SOUND_NEAR_LOSE); m_IsInformedNearLose[teamIdx] = true; } } // must be called here, else it could display a negative value - UpdateWorldState(((teamIdx == BG_TEAM_HORDE) ? BG_AV_Horde_Score : BG_AV_Alliance_Score), m_TeamScores[teamIdx]); + UpdateWorldState(((teamIdx == TEAM_INDEX_HORDE) ? BG_AV_Horde_Score : BG_AV_Alliance_Score), m_TeamScores[teamIdx]); } void BattleGroundAV::Update(uint32 diff) @@ -251,7 +251,7 @@ void BattleGroundAV::Update(uint32 diff) m_Mine_Timer[mine] -= diff; if (m_Mine_Timer[mine] <= 0) { - UpdateScore(BattleGroundTeamIndex(m_Mine_Owner[mine]), WORLD_STATE_ADD); + UpdateScore(PvpTeamIndex(m_Mine_Owner[mine]), WORLD_STATE_ADD); m_Mine_Timer[mine] = BG_AV_MINE_TICK_TIMER; } @@ -296,18 +296,18 @@ void BattleGroundAV::AddPlayer(Player* plr) void BattleGroundAV::EndBattleGround(Team winner) { // calculate bonuskills for both teams: - uint32 tower_survived[BG_TEAMS_COUNT] = {0, 0}; - uint32 graves_owned[BG_TEAMS_COUNT] = {0, 0}; - uint32 mines_owned[BG_TEAMS_COUNT] = {0, 0}; + uint32 tower_survived[PVP_TEAM_COUNT] = {0, 0}; + uint32 graves_owned[PVP_TEAM_COUNT] = {0, 0}; + uint32 mines_owned[PVP_TEAM_COUNT] = {0, 0}; // towers all not destroyed: for (BG_AV_Nodes i = BG_AV_NODES_DUNBALDAR_SOUTH; i <= BG_AV_NODES_STONEHEART_BUNKER; ++i) if (m_Nodes[i].State == POINT_CONTROLLED) if (m_Nodes[i].TotalOwner == BG_AV_TEAM_ALLIANCE) - ++tower_survived[BG_TEAM_ALLIANCE]; + ++tower_survived[TEAM_INDEX_ALLIANCE]; for (BG_AV_Nodes i = BG_AV_NODES_ICEBLOOD_TOWER; i <= BG_AV_NODES_FROSTWOLF_WTOWER; ++i) if (m_Nodes[i].State == POINT_CONTROLLED) if (m_Nodes[i].TotalOwner == BG_AV_TEAM_HORDE) - ++tower_survived[BG_TEAM_HORDE]; + ++tower_survived[TEAM_INDEX_HORDE]; // graves all controlled for (BG_AV_Nodes i = BG_AV_NODES_FIRSTAID_STATION; i < BG_AV_NODES_MAX; ++i) @@ -319,9 +319,9 @@ void BattleGroundAV::EndBattleGround(Team winner) ++mines_owned[m_Mine_Owner[i]]; // now we have the values give the honor/reputation to the teams: - Team team[BG_TEAMS_COUNT] = { ALLIANCE, HORDE }; - uint32 faction[BG_TEAMS_COUNT] = { BG_AV_FACTION_A, BG_AV_FACTION_H }; - for (uint8 i = 0; i < BG_TEAMS_COUNT; ++i) + Team team[PVP_TEAM_COUNT] = { ALLIANCE, HORDE }; + uint32 faction[PVP_TEAM_COUNT] = { BG_AV_FACTION_A, BG_AV_FACTION_H }; + for (uint8 i = 0; i < PVP_TEAM_COUNT; ++i) { if (tower_survived[i]) { @@ -417,8 +417,8 @@ void BattleGroundAV::EventPlayerDestroyedPoint(BG_AV_Nodes node) DEBUG_LOG("BattleGroundAV: player destroyed point node %i", node); MANGOS_ASSERT(m_Nodes[node].Owner != BG_AV_TEAM_NEUTRAL) - BattleGroundTeamIndex ownerTeamIdx = BattleGroundTeamIndex(m_Nodes[node].Owner); - Team ownerTeam = ownerTeamIdx == BG_TEAM_ALLIANCE ? ALLIANCE : HORDE; + PvpTeamIndex ownerTeamIdx = PvpTeamIndex(m_Nodes[node].Owner); + Team ownerTeam = ownerTeamIdx == TEAM_INDEX_ALLIANCE ? ALLIANCE : HORDE; // despawn banner DestroyNode(node); @@ -531,7 +531,7 @@ void BattleGroundAV::EventPlayerDefendsPoint(Player* player, BG_AV_Nodes node) { MANGOS_ASSERT(GetStatus() == STATUS_IN_PROGRESS); - BattleGroundTeamIndex teamIdx = GetTeamIndexByTeamId(player->GetTeam()); + PvpTeamIndex teamIdx = GetTeamIndexByTeamId(player->GetTeam()); if (m_Nodes[node].Owner == BattleGroundAVTeamIndex(teamIdx) || m_Nodes[node].State != POINT_ASSAULTED) return; @@ -559,7 +559,7 @@ void BattleGroundAV::EventPlayerDefendsPoint(Player* player, BG_AV_Nodes node) { SendYell2ToAll(LANG_BG_AV_TOWER_DEFENDED, LANG_UNIVERSAL, GetSingleCreatureGuid(BG_AV_HERALD, 0), GetNodeName(node), - (teamIdx == BG_TEAM_ALLIANCE) ? LANG_BG_ALLY : LANG_BG_HORDE); + (teamIdx == TEAM_INDEX_ALLIANCE) ? LANG_BG_ALLY : LANG_BG_HORDE); UpdatePlayerScore(player, SCORE_TOWERS_DEFENDED, 1); PlaySoundToAll(BG_AV_SOUND_BOTH_TOWER_DEFEND); } @@ -567,17 +567,17 @@ void BattleGroundAV::EventPlayerDefendsPoint(Player* player, BG_AV_Nodes node) { SendYell2ToAll(LANG_BG_AV_GRAVE_DEFENDED, LANG_UNIVERSAL, GetSingleCreatureGuid(BG_AV_HERALD, 0), GetNodeName(node), - (teamIdx == BG_TEAM_ALLIANCE) ? LANG_BG_ALLY : LANG_BG_HORDE); + (teamIdx == TEAM_INDEX_ALLIANCE) ? LANG_BG_ALLY : LANG_BG_HORDE); UpdatePlayerScore(player, SCORE_GRAVEYARDS_DEFENDED, 1); // update the statistic for the defending player - PlaySoundToAll((teamIdx == BG_TEAM_ALLIANCE) ? BG_AV_SOUND_ALLIANCE_GOOD : BG_AV_SOUND_HORDE_GOOD); + PlaySoundToAll((teamIdx == TEAM_INDEX_ALLIANCE) ? BG_AV_SOUND_ALLIANCE_GOOD : BG_AV_SOUND_HORDE_GOOD); } } void BattleGroundAV::EventPlayerAssaultsPoint(Player* player, BG_AV_Nodes node) { // TODO implement quest 7101, 7081 - BattleGroundTeamIndex teamIdx = GetTeamIndexByTeamId(player->GetTeam()); + PvpTeamIndex teamIdx = GetTeamIndexByTeamId(player->GetTeam()); DEBUG_LOG("BattleGroundAV: player assaults node %i", node); if (m_Nodes[node].Owner == BattleGroundAVTeamIndex(teamIdx) || BattleGroundAVTeamIndex(teamIdx) == m_Nodes[node].TotalOwner) return; @@ -590,19 +590,19 @@ void BattleGroundAV::EventPlayerAssaultsPoint(Player* player, BG_AV_Nodes node) { SendYell2ToAll(LANG_BG_AV_TOWER_ASSAULTED, LANG_UNIVERSAL, GetSingleCreatureGuid(BG_AV_HERALD, 0), GetNodeName(node), - (teamIdx == BG_TEAM_ALLIANCE) ? LANG_BG_ALLY : LANG_BG_HORDE); + (teamIdx == TEAM_INDEX_ALLIANCE) ? LANG_BG_ALLY : LANG_BG_HORDE); UpdatePlayerScore(player, SCORE_TOWERS_ASSAULTED, 1); } else { SendYell2ToAll(LANG_BG_AV_GRAVE_ASSAULTED, LANG_UNIVERSAL, GetSingleCreatureGuid(BG_AV_HERALD, 0), GetNodeName(node), - (teamIdx == BG_TEAM_ALLIANCE) ? LANG_BG_ALLY : LANG_BG_HORDE); + (teamIdx == TEAM_INDEX_ALLIANCE) ? LANG_BG_ALLY : LANG_BG_HORDE); // update the statistic for the assaulting player UpdatePlayerScore(player, SCORE_GRAVEYARDS_ASSAULTED, 1); } - PlaySoundToAll((teamIdx == BG_TEAM_ALLIANCE) ? BG_AV_SOUND_ALLIANCE_ASSAULTS : BG_AV_SOUND_HORDE_ASSAULTS); + PlaySoundToAll((teamIdx == TEAM_INDEX_ALLIANCE) ? BG_AV_SOUND_ALLIANCE_ASSAULTS : BG_AV_SOUND_HORDE_ASSAULTS); } void BattleGroundAV::FillInitialWorldStates(WorldPacket& data, uint32& count) @@ -623,8 +623,8 @@ void BattleGroundAV::FillInitialWorldStates(WorldPacket& data, uint32& count) if (m_Nodes[BG_AV_NODES_SNOWFALL_GRAVE].Owner == BG_AV_TEAM_NEUTRAL) // cause neutral teams aren't handled generic FillInitialWorldState(data, count, AV_SNOWFALL_N, WORLD_STATE_ADD); - FillInitialWorldState(data, count, BG_AV_Alliance_Score, m_TeamScores[BG_TEAM_ALLIANCE]); - FillInitialWorldState(data, count, BG_AV_Horde_Score, m_TeamScores[BG_TEAM_HORDE]); + FillInitialWorldState(data, count, BG_AV_Alliance_Score, m_TeamScores[TEAM_INDEX_ALLIANCE]); + FillInitialWorldState(data, count, BG_AV_Horde_Score, m_TeamScores[TEAM_INDEX_HORDE]); if (GetStatus() == STATUS_IN_PROGRESS) // only if game is running the teamscores are displayed { FillInitialWorldState(data, count, BG_AV_SHOW_A_SCORE, WORLD_STATE_ADD); @@ -718,7 +718,7 @@ uint32 BattleGroundAV::GetNodeName(BG_AV_Nodes node) const } } -void BattleGroundAV::AssaultNode(BG_AV_Nodes node, BattleGroundTeamIndex teamIdx) +void BattleGroundAV::AssaultNode(BG_AV_Nodes node, PvpTeamIndex teamIdx) { MANGOS_ASSERT(m_Nodes[node].TotalOwner != BattleGroundAVTeamIndex(teamIdx)); MANGOS_ASSERT(m_Nodes[node].Owner != BattleGroundAVTeamIndex(teamIdx)); @@ -758,7 +758,7 @@ void BattleGroundAV::InitNode(BG_AV_Nodes node, BattleGroundAVTeamIndex teamIdx, m_ActiveEvents[node + BG_AV_NODES_MAX] = teamIdx * BG_AV_MAX_GRAVETYPES; } -void BattleGroundAV::DefendNode(BG_AV_Nodes node, BattleGroundTeamIndex teamIdx) +void BattleGroundAV::DefendNode(BG_AV_Nodes node, PvpTeamIndex teamIdx) { MANGOS_ASSERT(m_Nodes[node].TotalOwner == BattleGroundAVTeamIndex(teamIdx)); MANGOS_ASSERT(m_Nodes[node].Owner != BattleGroundAVTeamIndex(teamIdx)); @@ -785,7 +785,7 @@ void BattleGroundAV::Reset() m_RepSurviveTower = (isBGWeekend) ? BG_AV_REP_SURVIVING_TOWER_HOLIDAY : BG_AV_REP_SURVIVING_TOWER; m_RepOwnedMine = (isBGWeekend) ? BG_AV_REP_OWNED_MINE_HOLIDAY : BG_AV_REP_OWNED_MINE; - for (uint8 i = 0; i < BG_TEAMS_COUNT; ++i) + for (uint8 i = 0; i < PVP_TEAM_COUNT; ++i) { for (uint8 j = 0; j < 9; ++j) // 9 quests getting tracked m_Team_QuestStatus[i][j] = 0; diff --git a/src/game/BattleGround/BattleGroundAV.h b/src/game/BattleGround/BattleGroundAV.h index 3e274392c..ae3a0c2b8 100644 --- a/src/game/BattleGround/BattleGroundAV.h +++ b/src/game/BattleGround/BattleGroundAV.h @@ -208,13 +208,13 @@ enum BG_AV_WorldStates AV_SNOWFALL_N = 1966, }; -// special version with more wide values range that BattleGroundTeamIndex -// BattleGroundAVTeamIndex <- BattleGroundTeamIndex cast safe -// BattleGroundAVTeamIndex -> BattleGroundTeamIndex cast safe and array with BG_TEAMS_COUNT elements must checked != BG_AV_TEAM_NEUTRAL before used +// special version with more wide values range that PvpTeamIndex +// BattleGroundAVTeamIndex <- PvpTeamIndex cast safe +// BattleGroundAVTeamIndex -> PvpTeamIndex cast safe and array with PVP_TEAM_COUNT elements must checked != BG_AV_TEAM_NEUTRAL before used enum BattleGroundAVTeamIndex { - BG_AV_TEAM_ALLIANCE = BG_TEAM_ALLIANCE, - BG_AV_TEAM_HORDE = BG_TEAM_HORDE, + BG_AV_TEAM_ALLIANCE = TEAM_INDEX_ALLIANCE, + BG_AV_TEAM_HORDE = TEAM_INDEX_HORDE, BG_AV_TEAM_NEUTRAL = 2, // this is the neutral owner of snowfall }; @@ -311,6 +311,13 @@ class BattleGroundAVScore : public BattleGroundScore public: BattleGroundAVScore() : GraveyardsAssaulted(0), GraveyardsDefended(0), TowersAssaulted(0), TowersDefended(0), SecondaryObjectives(0) {}; virtual ~BattleGroundAVScore() {}; + + uint32 GetAttr1() const { return GraveyardsAssaulted; } + uint32 GetAttr2() const { return GraveyardsDefended; } + uint32 GetAttr3() const { return TowersAssaulted; } + uint32 GetAttr4() const { return TowersDefended; } + uint32 GetAttr5() const { return SecondaryObjectives; } + uint32 GraveyardsAssaulted; uint32 GraveyardsDefended; uint32 TowersAssaulted; @@ -337,7 +344,7 @@ class BattleGroundAV : public BattleGround virtual void Reset() override; /*general stuff*/ - void UpdateScore(BattleGroundTeamIndex teamIdx, int32 points); + void UpdateScore(PvpTeamIndex teamIdx, int32 points); void UpdatePlayerScore(Player* source, uint32 type, uint32 value) override; /*handle stuff*/ // these are functions which get called from extern scripts @@ -358,10 +365,10 @@ class BattleGroundAV : public BattleGround void EventPlayerDefendsPoint(Player* player, BG_AV_Nodes node); void EventPlayerDestroyedPoint(BG_AV_Nodes node); - void AssaultNode(BG_AV_Nodes node, BattleGroundTeamIndex teamIdx); + void AssaultNode(BG_AV_Nodes node, PvpTeamIndex teamIdx); void DestroyNode(BG_AV_Nodes node); void InitNode(BG_AV_Nodes node, BattleGroundAVTeamIndex teamIdx, bool tower); - void DefendNode(BG_AV_Nodes node, BattleGroundTeamIndex teamIdx); + void DefendNode(BG_AV_Nodes node, PvpTeamIndex teamIdx); void PopulateNode(BG_AV_Nodes node); @@ -378,7 +385,7 @@ class BattleGroundAV : public BattleGround void UpdateNodeWorldState(BG_AV_Nodes node); /*variables */ - uint32 m_Team_QuestStatus[BG_TEAMS_COUNT][9]; // [x][y] x=team y=questcounter + uint32 m_Team_QuestStatus[PVP_TEAM_COUNT][9]; // [x][y] x=team y=questcounter BG_AV_NodeInfo m_Nodes[BG_AV_NODES_MAX]; @@ -388,7 +395,7 @@ class BattleGroundAV : public BattleGround int32 m_Mine_Timer[BG_AV_MAX_MINES]; uint32 m_Mine_Reclaim_Timer[BG_AV_MAX_MINES]; - bool m_IsInformedNearLose[BG_TEAMS_COUNT]; + bool m_IsInformedNearLose[PVP_TEAM_COUNT]; uint32 m_HonorMapComplete; uint32 m_RepTowerDestruction; diff --git a/src/game/BattleGround/BattleGroundEY.cpp b/src/game/BattleGround/BattleGroundEY.cpp index b65b0f3bb..6d0806a12 100644 --- a/src/game/BattleGround/BattleGroundEY.cpp +++ b/src/game/BattleGround/BattleGroundEY.cpp @@ -72,6 +72,28 @@ void BattleGroundEY::Update(uint32 diff) else m_flagRespawnTimer -= diff; } + + // workaround for Fel Reaver Ruins flag capture needed on 3.3.5 only + // the original areatrigger (4514) is covered by a bigger one (4515) and is not triggered on client side + if (IsFlagPickedUp()) + { + if (m_felReaverFlagTimer < diff) + { + Player* flagCarrier = sObjectMgr.GetPlayer(GetFlagCarrierGuid()); + if (flagCarrier) + { + if (m_towerOwner[NODE_FEL_REAVER_RUINS] == flagCarrier->GetTeam()) + { + // coords and range taken from DBC of areatrigger (4514) + if (flagCarrier->GetDistance(2044.0f, 1729.729f, 1190.03f) <= 3.0f) + EventPlayerCapturedFlag(flagCarrier, NODE_FEL_REAVER_RUINS); + } + } + m_felReaverFlagTimer = EY_FEL_REAVER_FLAG_UPDATE_TIME; + } + else + m_felReaverFlagTimer -= diff; + } } void BattleGroundEY::StartingEventOpenDoors() @@ -85,7 +107,7 @@ void BattleGroundEY::StartingEventOpenDoors() void BattleGroundEY::AddPoints(Team team, uint32 points) { - BattleGroundTeamIndex team_index = GetTeamIndexByTeamId(team); + PvpTeamIndex team_index = GetTeamIndexByTeamId(team); m_TeamScores[team_index] += points; m_honorScoreTicks[team_index] += points; if (m_honorScoreTicks[team_index] >= m_honorTicks) @@ -231,7 +253,7 @@ void BattleGroundEY::ProcessCaptureEvent(GameObject* go, uint32 towerId, Team te SendMessageToAll(message, CHAT_MSG_BG_SYSTEM_ALLIANCE); // spawn gameobjects - SpawnEvent(towerId, BG_TEAM_ALLIANCE, true); + SpawnEvent(towerId, TEAM_INDEX_ALLIANCE, true); } else if (team == HORDE) { @@ -242,7 +264,7 @@ void BattleGroundEY::ProcessCaptureEvent(GameObject* go, uint32 towerId, Team te SendMessageToAll(message, CHAT_MSG_BG_SYSTEM_HORDE); // spawn gameobjects - SpawnEvent(towerId, BG_TEAM_HORDE, true); + SpawnEvent(towerId, TEAM_INDEX_HORDE, true); } else { @@ -310,15 +332,15 @@ void BattleGroundEY::Reset() // call parent's class reset BattleGround::Reset(); - m_TeamScores[BG_TEAM_ALLIANCE] = 0; - m_TeamScores[BG_TEAM_HORDE] = 0; + m_TeamScores[TEAM_INDEX_ALLIANCE] = 0; + m_TeamScores[TEAM_INDEX_HORDE] = 0; m_towersAlliance = 0; m_towersHorde = 0; m_honorTicks = BattleGroundMgr::IsBGWeekend(GetTypeID()) ? EY_WEEKEND_HONOR_INTERVAL : EY_NORMAL_HONOR_INTERVAL; - m_honorScoreTicks[BG_TEAM_ALLIANCE] = 0; - m_honorScoreTicks[BG_TEAM_HORDE] = 0; + m_honorScoreTicks[TEAM_INDEX_ALLIANCE] = 0; + m_honorScoreTicks[TEAM_INDEX_HORDE] = 0; m_flagState = EY_FLAG_STATE_ON_BASE; m_flagCarrier.Clear(); @@ -326,6 +348,7 @@ void BattleGroundEY::Reset() m_flagRespawnTimer = 0; m_resourceUpdateTimer = 0; + m_felReaverFlagTimer = 0; m_towerWorldState[NODE_BLOOD_ELF_TOWER] = WORLD_STATE_EY_BLOOD_ELF_TOWER_NEUTRAL; m_towerWorldState[NODE_FEL_REAVER_RUINS] = WORLD_STATE_EY_FEL_REAVER_RUINS_NEUTRAL; @@ -512,8 +535,8 @@ void BattleGroundEY::FillInitialWorldStates(WorldPacket& data, uint32& count) FillInitialWorldState(data, count, WORLD_STATE_EY_TOWER_COUNT_ALLIANCE, m_towersAlliance); FillInitialWorldState(data, count, WORLD_STATE_EY_TOWER_COUNT_HORDE, m_towersHorde); - FillInitialWorldState(data, count, WORLD_STATE_EY_RESOURCES_ALLIANCE, m_TeamScores[BG_TEAM_ALLIANCE]); - FillInitialWorldState(data, count, WORLD_STATE_EY_RESOURCES_HORDE, m_TeamScores[BG_TEAM_HORDE]); + FillInitialWorldState(data, count, WORLD_STATE_EY_RESOURCES_ALLIANCE, m_TeamScores[TEAM_INDEX_ALLIANCE]); + FillInitialWorldState(data, count, WORLD_STATE_EY_RESOURCES_HORDE, m_TeamScores[TEAM_INDEX_HORDE]); // tower world states FillInitialWorldState(data, count, WORLD_STATE_EY_BLOOD_ELF_TOWER_ALLIANCE, m_towerOwner[NODE_BLOOD_ELF_TOWER] == ALLIANCE); diff --git a/src/game/BattleGround/BattleGroundEY.h b/src/game/BattleGround/BattleGroundEY.h index f32c6a5e9..9a85830c5 100644 --- a/src/game/BattleGround/BattleGroundEY.h +++ b/src/game/BattleGround/BattleGroundEY.h @@ -31,6 +31,7 @@ class BattleGround; #define EY_FLAG_RESPAWN_TIME (10 * IN_MILLISECONDS) //10 seconds #define EY_RESOURCES_UPDATE_TIME (2 * IN_MILLISECONDS) //2 seconds +#define EY_FEL_REAVER_FLAG_UPDATE_TIME (1 * IN_MILLISECONDS) //1 second enum EYWorldStates { @@ -305,9 +306,10 @@ class BattleGroundEY : public BattleGround ObjectGuid m_towers[EY_NODES_MAX]; uint32 m_honorTicks; - uint32 m_honorScoreTicks[BG_TEAMS_COUNT]; + uint32 m_honorScoreTicks[PVP_TEAM_COUNT]; uint32 m_flagRespawnTimer; uint32 m_resourceUpdateTimer; + uint32 m_felReaverFlagTimer; }; #endif diff --git a/src/game/BattleGround/BattleGroundMgr.cpp b/src/game/BattleGround/BattleGroundMgr.cpp index d415f30d3..9f958f244 100644 --- a/src/game/BattleGround/BattleGroundMgr.cpp +++ b/src/game/BattleGround/BattleGroundMgr.cpp @@ -59,7 +59,7 @@ INSTANTIATE_SINGLETON_1(BattleGroundMgr); BattleGroundQueue::BattleGroundQueue() { - for (uint8 i = 0; i < BG_TEAMS_COUNT; ++i) + for (uint8 i = 0; i < PVP_TEAM_COUNT; ++i) { for (uint8 j = 0; j < MAX_BATTLEGROUND_BRACKETS; ++j) { @@ -175,7 +175,7 @@ GroupQueueInfo* BattleGroundQueue::AddGroup(Player* leader, Group* grp, BattleGr // compute index (if group is premade or joined a rated match) to queues uint32 index = 0; if (!isRated && !isPremade) - index += BG_TEAMS_COUNT; // BG_QUEUE_PREMADE_* -> BG_QUEUE_NORMAL_* + index += PVP_TEAM_COUNT; // BG_QUEUE_PREMADE_* -> BG_QUEUE_NORMAL_* if (ginfo->GroupTeam == HORDE) ++index; // BG_QUEUE_*_ALLIANCE -> BG_QUEUE_*_HORDE @@ -260,16 +260,16 @@ GroupQueueInfo* BattleGroundQueue::AddGroup(Player* leader, Group* grp, BattleGr void BattleGroundQueue::PlayerInvitedToBGUpdateAverageWaitTime(GroupQueueInfo* ginfo, BattleGroundBracketId bracket_id) { uint32 timeInQueue = WorldTimer::getMSTimeDiff(ginfo->JoinTime, WorldTimer::getMSTime()); - uint8 team_index = BG_TEAM_ALLIANCE; // default set to BG_TEAM_ALLIANCE - or non rated arenas! + uint8 team_index = TEAM_INDEX_ALLIANCE; // default set to TEAM_INDEX_ALLIANCE - or non rated arenas! if (ginfo->arenaType == ARENA_TYPE_NONE) { if (ginfo->GroupTeam == HORDE) - team_index = BG_TEAM_HORDE; + team_index = TEAM_INDEX_HORDE; } else { if (ginfo->IsRated) - team_index = BG_TEAM_HORDE; // for rated arenas use BG_TEAM_HORDE + team_index = TEAM_INDEX_HORDE; // for rated arenas use TEAM_INDEX_HORDE } // store pointer to arrayindex of player that was added first @@ -287,16 +287,16 @@ void BattleGroundQueue::PlayerInvitedToBGUpdateAverageWaitTime(GroupQueueInfo* g uint32 BattleGroundQueue::GetAverageQueueWaitTime(GroupQueueInfo* ginfo, BattleGroundBracketId bracket_id) { - uint8 team_index = BG_TEAM_ALLIANCE; // default set to BG_TEAM_ALLIANCE - or non rated arenas! + uint8 team_index = TEAM_INDEX_ALLIANCE; // default set to TEAM_INDEX_ALLIANCE - or non rated arenas! if (ginfo->arenaType == ARENA_TYPE_NONE) { if (ginfo->GroupTeam == HORDE) - team_index = BG_TEAM_HORDE; + team_index = TEAM_INDEX_HORDE; } else { if (ginfo->IsRated) - team_index = BG_TEAM_HORDE; // for rated arenas use BG_TEAM_HORDE + team_index = TEAM_INDEX_HORDE; // for rated arenas use TEAM_INDEX_HORDE } // check if there is enought values(we always add values > 0) if (m_WaitTimes[team_index][bracket_id][COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME - 1]) @@ -526,13 +526,13 @@ void BattleGroundQueue::FillPlayersToBG(BattleGround* bg, BattleGroundBracketId uint32 aliCount = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE].size(); // index to queue which group is current uint32 aliIndex = 0; - for (; aliIndex < aliCount && m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*Ali_itr), aliFree); ++aliIndex) + for (; aliIndex < aliCount && m_SelectionPools[TEAM_INDEX_ALLIANCE].AddGroup((*Ali_itr), aliFree); ++aliIndex) ++Ali_itr; // the same thing for horde GroupsQueueType::const_iterator Horde_itr = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_HORDE].begin(); uint32 hordeCount = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_HORDE].size(); uint32 hordeIndex = 0; - for (; hordeIndex < hordeCount && m_SelectionPools[BG_TEAM_HORDE].AddGroup((*Horde_itr), hordeFree); ++hordeIndex) + for (; hordeIndex < hordeCount && m_SelectionPools[TEAM_INDEX_HORDE].AddGroup((*Horde_itr), hordeFree); ++hordeIndex) ++Horde_itr; // if ofc like BG queue invitation is set in config, then we are happy @@ -548,45 +548,45 @@ void BattleGroundQueue::FillPlayersToBG(BattleGround* bg, BattleGroundBracketId */ // At first we need to compare free space in bg and our selection pool - int32 diffAli = aliFree - int32(m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()); - int32 diffHorde = hordeFree - int32(m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()); - while (abs(diffAli - diffHorde) > 1 && (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() > 0 || m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() > 0)) + int32 diffAli = aliFree - int32(m_SelectionPools[TEAM_INDEX_ALLIANCE].GetPlayerCount()); + int32 diffHorde = hordeFree - int32(m_SelectionPools[TEAM_INDEX_HORDE].GetPlayerCount()); + while (abs(diffAli - diffHorde) > 1 && (m_SelectionPools[TEAM_INDEX_HORDE].GetPlayerCount() > 0 || m_SelectionPools[TEAM_INDEX_ALLIANCE].GetPlayerCount() > 0)) { // each cycle execution we need to kick at least 1 group if (diffAli < diffHorde) { // kick alliance group, add to pool new group if needed - if (m_SelectionPools[BG_TEAM_ALLIANCE].KickGroup(diffHorde - diffAli)) + if (m_SelectionPools[TEAM_INDEX_ALLIANCE].KickGroup(diffHorde - diffAli)) { - for (; aliIndex < aliCount && m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*Ali_itr), (aliFree >= diffHorde) ? aliFree - diffHorde : 0); ++aliIndex) + for (; aliIndex < aliCount && m_SelectionPools[TEAM_INDEX_ALLIANCE].AddGroup((*Ali_itr), (aliFree >= diffHorde) ? aliFree - diffHorde : 0); ++aliIndex) ++Ali_itr; } // if ali selection is already empty, then kick horde group, but if there are less horde than ali in bg - break; - if (!m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()) + if (!m_SelectionPools[TEAM_INDEX_ALLIANCE].GetPlayerCount()) { if (aliFree <= diffHorde + 1) break; - m_SelectionPools[BG_TEAM_HORDE].KickGroup(diffHorde - diffAli); + m_SelectionPools[TEAM_INDEX_HORDE].KickGroup(diffHorde - diffAli); } } else { // kick horde group, add to pool new group if needed - if (m_SelectionPools[BG_TEAM_HORDE].KickGroup(diffAli - diffHorde)) + if (m_SelectionPools[TEAM_INDEX_HORDE].KickGroup(diffAli - diffHorde)) { - for (; hordeIndex < hordeCount && m_SelectionPools[BG_TEAM_HORDE].AddGroup((*Horde_itr), (hordeFree >= diffAli) ? hordeFree - diffAli : 0); ++hordeIndex) + for (; hordeIndex < hordeCount && m_SelectionPools[TEAM_INDEX_HORDE].AddGroup((*Horde_itr), (hordeFree >= diffAli) ? hordeFree - diffAli : 0); ++hordeIndex) ++Horde_itr; } - if (!m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()) + if (!m_SelectionPools[TEAM_INDEX_HORDE].GetPlayerCount()) { if (hordeFree <= diffAli + 1) break; - m_SelectionPools[BG_TEAM_ALLIANCE].KickGroup(diffAli - diffHorde); + m_SelectionPools[TEAM_INDEX_ALLIANCE].KickGroup(diffAli - diffHorde); } } // count diffs after small update - diffAli = aliFree - int32(m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()); - diffHorde = hordeFree - int32(m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()); + diffAli = aliFree - int32(m_SelectionPools[TEAM_INDEX_ALLIANCE].GetPlayerCount()); + diffHorde = hordeFree - int32(m_SelectionPools[TEAM_INDEX_HORDE].GetPlayerCount()); } } @@ -610,12 +610,12 @@ bool BattleGroundQueue::CheckPremadeMatch(BattleGroundBracketId bracket_id, uint if (ali_group != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].end() && horde_group != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].end()) { - m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*ali_group), MaxPlayersPerTeam); - m_SelectionPools[BG_TEAM_HORDE].AddGroup((*horde_group), MaxPlayersPerTeam); + m_SelectionPools[TEAM_INDEX_ALLIANCE].AddGroup((*ali_group), MaxPlayersPerTeam); + m_SelectionPools[TEAM_INDEX_HORDE].AddGroup((*horde_group), MaxPlayersPerTeam); // add groups/players from normal queue to size of bigger group - uint32 maxPlayers = std::max(m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount(), m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()); + uint32 maxPlayers = std::max(m_SelectionPools[TEAM_INDEX_ALLIANCE].GetPlayerCount(), m_SelectionPools[TEAM_INDEX_HORDE].GetPlayerCount()); GroupsQueueType::const_iterator itr; - for (uint8 i = 0; i < BG_TEAMS_COUNT; ++i) + for (uint8 i = 0; i < PVP_TEAM_COUNT; ++i) { for (itr = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].begin(); itr != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].end(); ++itr) { @@ -633,7 +633,7 @@ bool BattleGroundQueue::CheckPremadeMatch(BattleGroundBracketId bracket_id, uint // if first is invited to BG and seconds timer expired, but we can ignore it, because players have only 80 seconds to click to enter bg // and when they click or after 80 seconds the queue info is removed from queue uint32 time_before = WorldTimer::getMSTime() - sWorld.getConfig(CONFIG_UINT32_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH); - for (uint8 i = 0; i < BG_TEAMS_COUNT; ++i) + for (uint8 i = 0; i < PVP_TEAM_COUNT; ++i) { if (!m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE + i].empty()) { @@ -653,8 +653,8 @@ bool BattleGroundQueue::CheckPremadeMatch(BattleGroundBracketId bracket_id, uint // this method tries to create battleground or arena with MinPlayersPerTeam against MinPlayersPerTeam bool BattleGroundQueue::CheckNormalMatch(BattleGround* bg_template, BattleGroundBracketId bracket_id, uint32 minPlayers, uint32 maxPlayers) { - GroupsQueueType::const_iterator itr_team[BG_TEAMS_COUNT]; - for (uint8 i = 0; i < BG_TEAMS_COUNT; ++i) + GroupsQueueType::const_iterator itr_team[PVP_TEAM_COUNT]; + for (uint8 i = 0; i < PVP_TEAM_COUNT; ++i) { itr_team[i] = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].begin(); for (; itr_team[i] != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].end(); ++(itr_team[i])) @@ -668,43 +668,43 @@ bool BattleGroundQueue::CheckNormalMatch(BattleGround* bg_template, BattleGround } } // try to invite same number of players - this cycle may cause longer wait time even if there are enough players in queue, but we want ballanced bg - uint32 j = BG_TEAM_ALLIANCE; - if (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() < m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()) - j = BG_TEAM_HORDE; + uint32 j = TEAM_INDEX_ALLIANCE; + if (m_SelectionPools[TEAM_INDEX_HORDE].GetPlayerCount() < m_SelectionPools[TEAM_INDEX_ALLIANCE].GetPlayerCount()) + j = TEAM_INDEX_HORDE; if (sWorld.getConfig(CONFIG_UINT32_BATTLEGROUND_INVITATION_TYPE) != 0 - && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() >= minPlayers && m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() >= minPlayers) + && m_SelectionPools[TEAM_INDEX_HORDE].GetPlayerCount() >= minPlayers && m_SelectionPools[TEAM_INDEX_ALLIANCE].GetPlayerCount() >= minPlayers) { // we will try to invite more groups to team with less players indexed by j ++(itr_team[j]); // this will not cause a crash, because for cycle above reached break; for (; itr_team[j] != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + j].end(); ++(itr_team[j])) { if (!(*(itr_team[j]))->IsInvitedToBGInstanceGUID) - if (!m_SelectionPools[j].AddGroup(*(itr_team[j]), m_SelectionPools[(j + 1) % BG_TEAMS_COUNT].GetPlayerCount())) + if (!m_SelectionPools[j].AddGroup(*(itr_team[j]), m_SelectionPools[(j + 1) % PVP_TEAM_COUNT].GetPlayerCount())) break; } // do not allow to start bg with more than 2 players more on 1 faction - if (abs((int32)(m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() - m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount())) > 2) + if (abs((int32)(m_SelectionPools[TEAM_INDEX_HORDE].GetPlayerCount() - m_SelectionPools[TEAM_INDEX_ALLIANCE].GetPlayerCount())) > 2) return false; } // allow 1v0 if debug bg - if (sBattleGroundMgr.isTesting() && bg_template->isBattleGround() && (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() || m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount())) + if (sBattleGroundMgr.isTesting() && bg_template->isBattleGround() && (m_SelectionPools[TEAM_INDEX_ALLIANCE].GetPlayerCount() || m_SelectionPools[TEAM_INDEX_HORDE].GetPlayerCount())) return true; // return true if there are enough players in selection pools - enable to work .debug bg command correctly - return m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() >= minPlayers && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() >= minPlayers; + return m_SelectionPools[TEAM_INDEX_ALLIANCE].GetPlayerCount() >= minPlayers && m_SelectionPools[TEAM_INDEX_HORDE].GetPlayerCount() >= minPlayers; } // this method will check if we can invite players to same faction skirmish match bool BattleGroundQueue::CheckSkirmishForSameFaction(BattleGroundBracketId bracket_id, uint32 minPlayersPerTeam) { - if (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() < minPlayersPerTeam && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() < minPlayersPerTeam) + if (m_SelectionPools[TEAM_INDEX_ALLIANCE].GetPlayerCount() < minPlayersPerTeam && m_SelectionPools[TEAM_INDEX_HORDE].GetPlayerCount() < minPlayersPerTeam) return false; - BattleGroundTeamIndex teamIdx = BG_TEAM_ALLIANCE; - BattleGroundTeamIndex otherTeamIdx = BG_TEAM_HORDE; + PvpTeamIndex teamIdx = TEAM_INDEX_ALLIANCE; + PvpTeamIndex otherTeamIdx = TEAM_INDEX_HORDE; Team otherTeamId = HORDE; - if (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() == minPlayersPerTeam) + if (m_SelectionPools[TEAM_INDEX_HORDE].GetPlayerCount() == minPlayersPerTeam) { - teamIdx = BG_TEAM_HORDE; - otherTeamIdx = BG_TEAM_ALLIANCE; + teamIdx = TEAM_INDEX_HORDE; + otherTeamIdx = TEAM_INDEX_ALLIANCE; otherTeamId = ALLIANCE; } // clear other team's selection @@ -782,16 +782,16 @@ void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BattleGroundBracketI // and iterator is invalid // clear selection pools - m_SelectionPools[BG_TEAM_ALLIANCE].Init(); - m_SelectionPools[BG_TEAM_HORDE].Init(); + m_SelectionPools[TEAM_INDEX_ALLIANCE].Init(); + m_SelectionPools[TEAM_INDEX_HORDE].Init(); // call a function that does the job for us FillPlayersToBG(bg, bracket_id); // now everything is set, invite players - for (GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_ALLIANCE].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_ALLIANCE].SelectedGroups.end(); ++citr) + for (GroupsQueueType::const_iterator citr = m_SelectionPools[TEAM_INDEX_ALLIANCE].SelectedGroups.begin(); citr != m_SelectionPools[TEAM_INDEX_ALLIANCE].SelectedGroups.end(); ++citr) InviteGroupToBG((*citr), bg, (*citr)->GroupTeam); - for (GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_HORDE].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_HORDE].SelectedGroups.end(); ++citr) + for (GroupsQueueType::const_iterator citr = m_SelectionPools[TEAM_INDEX_HORDE].SelectedGroups.begin(); citr != m_SelectionPools[TEAM_INDEX_HORDE].SelectedGroups.end(); ++citr) InviteGroupToBG((*citr), bg, (*citr)->GroupTeam); if (!bg->HasFreeSlots()) @@ -853,8 +853,8 @@ void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BattleGroundBracketI } } - m_SelectionPools[BG_TEAM_ALLIANCE].Init(); - m_SelectionPools[BG_TEAM_HORDE].Init(); + m_SelectionPools[TEAM_INDEX_ALLIANCE].Init(); + m_SelectionPools[TEAM_INDEX_HORDE].Init(); if (bg_template->isBattleGround()) { @@ -869,14 +869,14 @@ void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BattleGroundBracketI return; } // invite those selection pools - for (uint8 i = 0; i < BG_TEAMS_COUNT; ++i) - for (GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.end(); ++citr) + for (uint8 i = 0; i < PVP_TEAM_COUNT; ++i) + for (GroupsQueueType::const_iterator citr = m_SelectionPools[TEAM_INDEX_ALLIANCE + i].SelectedGroups.begin(); citr != m_SelectionPools[TEAM_INDEX_ALLIANCE + i].SelectedGroups.end(); ++citr) InviteGroupToBG((*citr), bg2, (*citr)->GroupTeam); // start bg bg2->StartBattleGround(); // clear structures - m_SelectionPools[BG_TEAM_ALLIANCE].Init(); - m_SelectionPools[BG_TEAM_HORDE].Init(); + m_SelectionPools[TEAM_INDEX_ALLIANCE].Init(); + m_SelectionPools[TEAM_INDEX_HORDE].Init(); } } @@ -896,8 +896,8 @@ void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BattleGroundBracketI } // invite those selection pools - for (uint8 i = 0; i < BG_TEAMS_COUNT; ++i) - for (GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.end(); ++citr) + for (uint8 i = 0; i < PVP_TEAM_COUNT; ++i) + for (GroupsQueueType::const_iterator citr = m_SelectionPools[TEAM_INDEX_ALLIANCE + i].SelectedGroups.begin(); citr != m_SelectionPools[TEAM_INDEX_ALLIANCE + i].SelectedGroups.end(); ++citr) InviteGroupToBG((*citr), bg2, (*citr)->GroupTeam); // start bg bg2->StartBattleGround(); @@ -942,7 +942,7 @@ void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BattleGroundBracketI // we need to find 2 teams which will play next game - GroupsQueueType::iterator itr_team[BG_TEAMS_COUNT]; + GroupsQueueType::iterator itr_team[PVP_TEAM_COUNT]; // optimalization : --- we dont need to use selection_pools - each update we select max 2 groups @@ -967,40 +967,40 @@ void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BattleGroundBracketI // if we don't have, we must try to continue search in same queue // tmp variables are correctly set // this code isn't much userfriendly - but it is supposed to continue search for mathing group in HORDE queue - if (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() == 0 && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()) + if (m_SelectionPools[TEAM_INDEX_ALLIANCE].GetPlayerCount() == 0 && m_SelectionPools[TEAM_INDEX_HORDE].GetPlayerCount()) { - itr_team[BG_TEAM_ALLIANCE] = itr_team[BG_TEAM_HORDE]; - ++itr_team[BG_TEAM_ALLIANCE]; - for (; itr_team[BG_TEAM_ALLIANCE] != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].end(); ++(itr_team[BG_TEAM_ALLIANCE])) + itr_team[TEAM_INDEX_ALLIANCE] = itr_team[TEAM_INDEX_HORDE]; + ++itr_team[TEAM_INDEX_ALLIANCE]; + for (; itr_team[TEAM_INDEX_ALLIANCE] != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].end(); ++(itr_team[TEAM_INDEX_ALLIANCE])) { - if (!(*itr_team[BG_TEAM_ALLIANCE])->IsInvitedToBGInstanceGUID - && (((*itr_team[BG_TEAM_ALLIANCE])->ArenaTeamRating >= arenaMinRating && (*itr_team[BG_TEAM_ALLIANCE])->ArenaTeamRating <= arenaMaxRating) - || (*itr_team[BG_TEAM_ALLIANCE])->JoinTime < discardTime)) + if (!(*itr_team[TEAM_INDEX_ALLIANCE])->IsInvitedToBGInstanceGUID + && (((*itr_team[TEAM_INDEX_ALLIANCE])->ArenaTeamRating >= arenaMinRating && (*itr_team[TEAM_INDEX_ALLIANCE])->ArenaTeamRating <= arenaMaxRating) + || (*itr_team[TEAM_INDEX_ALLIANCE])->JoinTime < discardTime)) { - m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*itr_team[BG_TEAM_ALLIANCE]), MaxPlayersPerTeam); + m_SelectionPools[TEAM_INDEX_ALLIANCE].AddGroup((*itr_team[TEAM_INDEX_ALLIANCE]), MaxPlayersPerTeam); break; } } } // this code isn't much userfriendly - but it is supposed to continue search for mathing group in ALLIANCE queue - if (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() == 0 && m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()) + if (m_SelectionPools[TEAM_INDEX_HORDE].GetPlayerCount() == 0 && m_SelectionPools[TEAM_INDEX_ALLIANCE].GetPlayerCount()) { - itr_team[BG_TEAM_HORDE] = itr_team[BG_TEAM_ALLIANCE]; - ++itr_team[BG_TEAM_HORDE]; - for (; itr_team[BG_TEAM_HORDE] != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].end(); ++(itr_team[BG_TEAM_HORDE])) + itr_team[TEAM_INDEX_HORDE] = itr_team[TEAM_INDEX_ALLIANCE]; + ++itr_team[TEAM_INDEX_HORDE]; + for (; itr_team[TEAM_INDEX_HORDE] != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].end(); ++(itr_team[TEAM_INDEX_HORDE])) { - if (!(*itr_team[BG_TEAM_HORDE])->IsInvitedToBGInstanceGUID - && (((*itr_team[BG_TEAM_HORDE])->ArenaTeamRating >= arenaMinRating && (*itr_team[BG_TEAM_HORDE])->ArenaTeamRating <= arenaMaxRating) - || (*itr_team[BG_TEAM_HORDE])->JoinTime < discardTime)) + if (!(*itr_team[TEAM_INDEX_HORDE])->IsInvitedToBGInstanceGUID + && (((*itr_team[TEAM_INDEX_HORDE])->ArenaTeamRating >= arenaMinRating && (*itr_team[TEAM_INDEX_HORDE])->ArenaTeamRating <= arenaMaxRating) + || (*itr_team[TEAM_INDEX_HORDE])->JoinTime < discardTime)) { - m_SelectionPools[BG_TEAM_HORDE].AddGroup((*itr_team[BG_TEAM_HORDE]), MaxPlayersPerTeam); + m_SelectionPools[TEAM_INDEX_HORDE].AddGroup((*itr_team[TEAM_INDEX_HORDE]), MaxPlayersPerTeam); break; } } } // if we have 2 teams, then start new arena and invite players! - if (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()) + if (m_SelectionPools[TEAM_INDEX_ALLIANCE].GetPlayerCount() && m_SelectionPools[TEAM_INDEX_HORDE].GetPlayerCount()) { BattleGround* arena = sBattleGroundMgr.CreateNewBattleGround(bgTypeId, bracketEntry, arenaType, true); if (!arena) @@ -1009,28 +1009,28 @@ void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BattleGroundBracketI return; } - (*(itr_team[BG_TEAM_ALLIANCE]))->OpponentsTeamRating = (*(itr_team[BG_TEAM_HORDE]))->ArenaTeamRating; - DEBUG_LOG("setting oposite teamrating for team %u to %u", (*(itr_team[BG_TEAM_ALLIANCE]))->ArenaTeamId, (*(itr_team[BG_TEAM_ALLIANCE]))->OpponentsTeamRating); - (*(itr_team[BG_TEAM_HORDE]))->OpponentsTeamRating = (*(itr_team[BG_TEAM_ALLIANCE]))->ArenaTeamRating; - DEBUG_LOG("setting oposite teamrating for team %u to %u", (*(itr_team[BG_TEAM_HORDE]))->ArenaTeamId, (*(itr_team[BG_TEAM_HORDE]))->OpponentsTeamRating); + (*(itr_team[TEAM_INDEX_ALLIANCE]))->OpponentsTeamRating = (*(itr_team[TEAM_INDEX_HORDE]))->ArenaTeamRating; + DEBUG_LOG("setting oposite teamrating for team %u to %u", (*(itr_team[TEAM_INDEX_ALLIANCE]))->ArenaTeamId, (*(itr_team[TEAM_INDEX_ALLIANCE]))->OpponentsTeamRating); + (*(itr_team[TEAM_INDEX_HORDE]))->OpponentsTeamRating = (*(itr_team[TEAM_INDEX_ALLIANCE]))->ArenaTeamRating; + DEBUG_LOG("setting oposite teamrating for team %u to %u", (*(itr_team[TEAM_INDEX_HORDE]))->ArenaTeamId, (*(itr_team[TEAM_INDEX_HORDE]))->OpponentsTeamRating); // now we must move team if we changed its faction to another faction queue, because then we will spam log by errors in Queue::RemovePlayer - if ((*(itr_team[BG_TEAM_ALLIANCE]))->GroupTeam != ALLIANCE) + if ((*(itr_team[TEAM_INDEX_ALLIANCE]))->GroupTeam != ALLIANCE) { // add to alliance queue - m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].push_front(*(itr_team[BG_TEAM_ALLIANCE])); + m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].push_front(*(itr_team[TEAM_INDEX_ALLIANCE])); // erase from horde queue - m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].erase(itr_team[BG_TEAM_ALLIANCE]); - itr_team[BG_TEAM_ALLIANCE] = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].begin(); + m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].erase(itr_team[TEAM_INDEX_ALLIANCE]); + itr_team[TEAM_INDEX_ALLIANCE] = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].begin(); } - if ((*(itr_team[BG_TEAM_HORDE]))->GroupTeam != HORDE) + if ((*(itr_team[TEAM_INDEX_HORDE]))->GroupTeam != HORDE) { - m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].push_front(*(itr_team[BG_TEAM_HORDE])); - m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].erase(itr_team[BG_TEAM_HORDE]); - itr_team[BG_TEAM_HORDE] = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].begin(); + m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].push_front(*(itr_team[TEAM_INDEX_HORDE])); + m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].erase(itr_team[TEAM_INDEX_HORDE]); + itr_team[TEAM_INDEX_HORDE] = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].begin(); } - InviteGroupToBG(*(itr_team[BG_TEAM_ALLIANCE]), arena, ALLIANCE); - InviteGroupToBG(*(itr_team[BG_TEAM_HORDE]), arena, HORDE); + InviteGroupToBG(*(itr_team[TEAM_INDEX_ALLIANCE]), arena, ALLIANCE); + InviteGroupToBG(*(itr_team[TEAM_INDEX_HORDE]), arena, HORDE); DEBUG_LOG("Starting rated arena match!"); diff --git a/src/game/BattleGround/BattleGroundMgr.h b/src/game/BattleGround/BattleGroundMgr.h index 19714d2f9..dd78655c8 100644 --- a/src/game/BattleGround/BattleGroundMgr.h +++ b/src/game/BattleGround/BattleGroundMgr.h @@ -134,12 +134,12 @@ class BattleGroundQueue }; // one selection pool for horde, other one for alliance - SelectionPool m_SelectionPools[BG_TEAMS_COUNT]; + SelectionPool m_SelectionPools[PVP_TEAM_COUNT]; bool InviteGroupToBG(GroupQueueInfo* ginfo, BattleGround* bg, Team side); - uint32 m_WaitTimes[BG_TEAMS_COUNT][MAX_BATTLEGROUND_BRACKETS][COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME]; - uint32 m_WaitTimeLastPlayer[BG_TEAMS_COUNT][MAX_BATTLEGROUND_BRACKETS]; - uint32 m_SumOfWaitTimes[BG_TEAMS_COUNT][MAX_BATTLEGROUND_BRACKETS]; + uint32 m_WaitTimes[PVP_TEAM_COUNT][MAX_BATTLEGROUND_BRACKETS][COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME]; + uint32 m_WaitTimeLastPlayer[PVP_TEAM_COUNT][MAX_BATTLEGROUND_BRACKETS]; + uint32 m_SumOfWaitTimes[PVP_TEAM_COUNT][MAX_BATTLEGROUND_BRACKETS]; }; /* diff --git a/src/game/BattleGround/BattleGroundWS.cpp b/src/game/BattleGround/BattleGroundWS.cpp index c2e003f23..1aad78153 100644 --- a/src/game/BattleGround/BattleGroundWS.cpp +++ b/src/game/BattleGround/BattleGroundWS.cpp @@ -48,52 +48,52 @@ void BattleGroundWS::Update(uint32 diff) if (GetStatus() == STATUS_IN_PROGRESS) { - if (m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_WAIT_RESPAWN) + if (m_FlagState[TEAM_INDEX_ALLIANCE] == BG_WS_FLAG_STATE_WAIT_RESPAWN) { - m_FlagsTimer[BG_TEAM_ALLIANCE] -= diff; + m_FlagsTimer[TEAM_INDEX_ALLIANCE] -= diff; - if (m_FlagsTimer[BG_TEAM_ALLIANCE] < 0) + if (m_FlagsTimer[TEAM_INDEX_ALLIANCE] < 0) { - m_FlagsTimer[BG_TEAM_ALLIANCE] = 0; + m_FlagsTimer[TEAM_INDEX_ALLIANCE] = 0; RespawnFlag(ALLIANCE, true); } } - if (m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_ON_GROUND) + if (m_FlagState[TEAM_INDEX_ALLIANCE] == BG_WS_FLAG_STATE_ON_GROUND) { - m_FlagsDropTimer[BG_TEAM_ALLIANCE] -= diff; + m_FlagsDropTimer[TEAM_INDEX_ALLIANCE] -= diff; - if (m_FlagsDropTimer[BG_TEAM_ALLIANCE] < 0) + if (m_FlagsDropTimer[TEAM_INDEX_ALLIANCE] < 0) { - m_FlagsDropTimer[BG_TEAM_ALLIANCE] = 0; + m_FlagsDropTimer[TEAM_INDEX_ALLIANCE] = 0; RespawnDroppedFlag(ALLIANCE); } } - if (m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_WAIT_RESPAWN) + if (m_FlagState[TEAM_INDEX_HORDE] == BG_WS_FLAG_STATE_WAIT_RESPAWN) { - m_FlagsTimer[BG_TEAM_HORDE] -= diff; + m_FlagsTimer[TEAM_INDEX_HORDE] -= diff; - if (m_FlagsTimer[BG_TEAM_HORDE] < 0) + if (m_FlagsTimer[TEAM_INDEX_HORDE] < 0) { - m_FlagsTimer[BG_TEAM_HORDE] = 0; + m_FlagsTimer[TEAM_INDEX_HORDE] = 0; RespawnFlag(HORDE, true); } } - if (m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_ON_GROUND) + if (m_FlagState[TEAM_INDEX_HORDE] == BG_WS_FLAG_STATE_ON_GROUND) { - m_FlagsDropTimer[BG_TEAM_HORDE] -= diff; + m_FlagsDropTimer[TEAM_INDEX_HORDE] -= diff; - if (m_FlagsDropTimer[BG_TEAM_HORDE] < 0) + if (m_FlagsDropTimer[TEAM_INDEX_HORDE] < 0) { - m_FlagsDropTimer[BG_TEAM_HORDE] = 0; + m_FlagsDropTimer[TEAM_INDEX_HORDE] = 0; RespawnDroppedFlag(HORDE); } } if (m_EndTimer <= diff) { - if (m_TeamScores[BG_TEAM_ALLIANCE] > m_TeamScores[BG_TEAM_HORDE]) + if (m_TeamScores[TEAM_INDEX_ALLIANCE] > m_TeamScores[TEAM_INDEX_HORDE]) EndBattleGround(ALLIANCE); - else if (m_TeamScores[BG_TEAM_ALLIANCE] < m_TeamScores[BG_TEAM_HORDE]) + else if (m_TeamScores[TEAM_INDEX_ALLIANCE] < m_TeamScores[TEAM_INDEX_HORDE]) EndBattleGround(HORDE); else { @@ -141,13 +141,13 @@ void BattleGroundWS::RespawnFlag(Team team, bool captured) if (team == ALLIANCE) { DEBUG_LOG("Respawn Alliance flag"); - m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_BASE; + m_FlagState[TEAM_INDEX_ALLIANCE] = BG_WS_FLAG_STATE_ON_BASE; SpawnEvent(WS_EVENT_FLAG_A, 0, true); } else { DEBUG_LOG("Respawn Horde flag"); - m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_BASE; + m_FlagState[TEAM_INDEX_HORDE] = BG_WS_FLAG_STATE_ON_BASE; SpawnEvent(WS_EVENT_FLAG_H, 0, true); } @@ -197,11 +197,11 @@ void BattleGroundWS::EventPlayerCapturedFlag(Player* source) return; ClearHordeFlagCarrier(); // must be before aura remove to prevent 2 events (drop+capture) at the same time // horde flag in base (but not respawned yet) - m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_WAIT_RESPAWN; + m_FlagState[TEAM_INDEX_HORDE] = BG_WS_FLAG_STATE_WAIT_RESPAWN; // Drop Horde Flag from Player source->RemoveAurasDueToSpell(BG_WS_SPELL_WARSONG_FLAG); - if (m_TeamScores[BG_TEAM_ALLIANCE] < BG_WS_MAX_TEAM_SCORE) - m_TeamScores[BG_TEAM_ALLIANCE] += 1; + if (m_TeamScores[TEAM_INDEX_ALLIANCE] < BG_WS_MAX_TEAM_SCORE) + m_TeamScores[TEAM_INDEX_ALLIANCE] += 1; PlaySoundToAll(BG_WS_SOUND_FLAG_CAPTURED_ALLIANCE); RewardReputationToTeam(890, m_ReputationCapture, ALLIANCE); } @@ -211,11 +211,11 @@ void BattleGroundWS::EventPlayerCapturedFlag(Player* source) return; ClearAllianceFlagCarrier(); // must be before aura remove to prevent 2 events (drop+capture) at the same time // alliance flag in base (but not respawned yet) - m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_WAIT_RESPAWN; + m_FlagState[TEAM_INDEX_ALLIANCE] = BG_WS_FLAG_STATE_WAIT_RESPAWN; // Drop Alliance Flag from Player source->RemoveAurasDueToSpell(BG_WS_SPELL_SILVERWING_FLAG); - if (m_TeamScores[BG_TEAM_HORDE] < BG_WS_MAX_TEAM_SCORE) - m_TeamScores[BG_TEAM_HORDE] += 1; + if (m_TeamScores[TEAM_INDEX_HORDE] < BG_WS_MAX_TEAM_SCORE) + m_TeamScores[TEAM_INDEX_HORDE] += 1; PlaySoundToAll(BG_WS_SOUND_FLAG_CAPTURED_HORDE); RewardReputationToTeam(889, m_ReputationCapture, HORDE); } @@ -237,9 +237,9 @@ void BattleGroundWS::EventPlayerCapturedFlag(Player* source) UpdatePlayerScore(source, SCORE_FLAG_CAPTURES, 1); // +1 flag captures Team winner = TEAM_NONE; - if (m_TeamScores[BG_TEAM_ALLIANCE] == BG_WS_MAX_TEAM_SCORE) + if (m_TeamScores[TEAM_INDEX_ALLIANCE] == BG_WS_MAX_TEAM_SCORE) winner = ALLIANCE; - else if (m_TeamScores[BG_TEAM_HORDE] == BG_WS_MAX_TEAM_SCORE) + else if (m_TeamScores[TEAM_INDEX_HORDE] == BG_WS_MAX_TEAM_SCORE) winner = HORDE; if (winner) @@ -296,7 +296,7 @@ void BattleGroundWS::EventPlayerDroppedFlag(Player* source) { ClearHordeFlagCarrier(); source->RemoveAurasDueToSpell(BG_WS_SPELL_WARSONG_FLAG); - m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_GROUND; + m_FlagState[TEAM_INDEX_HORDE] = BG_WS_FLAG_STATE_ON_GROUND; source->CastSpell(source, BG_WS_SPELL_WARSONG_FLAG_DROPPED, true); set = true; } @@ -309,7 +309,7 @@ void BattleGroundWS::EventPlayerDroppedFlag(Player* source) { ClearAllianceFlagCarrier(); source->RemoveAurasDueToSpell(BG_WS_SPELL_SILVERWING_FLAG); - m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_GROUND; + m_FlagState[TEAM_INDEX_ALLIANCE] = BG_WS_FLAG_STATE_ON_GROUND; source->CastSpell(source, BG_WS_SPELL_SILVERWING_FLAG_DROPPED, true); set = true; } @@ -354,7 +354,7 @@ void BattleGroundWS::EventPlayerClickedOnFlag(Player* source, GameObject* target PlaySoundToAll(BG_WS_SOUND_ALLIANCE_FLAG_PICKED_UP); SpawnEvent(WS_EVENT_FLAG_A, 0, false); SetAllianceFlagCarrier(source->GetObjectGuid()); - m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_PLAYER; + m_FlagState[TEAM_INDEX_ALLIANCE] = BG_WS_FLAG_STATE_ON_PLAYER; // update world state to show correct flag carrier UpdateFlagState(HORDE, BG_WS_FLAG_STATE_ON_PLAYER); UpdateWorldState(BG_WS_FLAG_UNK_ALLIANCE, 1); @@ -370,7 +370,7 @@ void BattleGroundWS::EventPlayerClickedOnFlag(Player* source, GameObject* target PlaySoundToAll(BG_WS_SOUND_HORDE_FLAG_PICKED_UP); SpawnEvent(WS_EVENT_FLAG_H, 0, false); SetHordeFlagCarrier(source->GetObjectGuid()); - m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_PLAYER; + m_FlagState[TEAM_INDEX_HORDE] = BG_WS_FLAG_STATE_ON_PLAYER; // update world state to show correct flag carrier UpdateFlagState(ALLIANCE, BG_WS_FLAG_STATE_ON_PLAYER); UpdateWorldState(BG_WS_FLAG_UNK_HORDE, 1); @@ -397,7 +397,7 @@ void BattleGroundWS::EventPlayerClickedOnFlag(Player* source, GameObject* target SpawnEvent(WS_EVENT_FLAG_A, 0, false); SetAllianceFlagCarrier(source->GetObjectGuid()); source->CastSpell(source, BG_WS_SPELL_SILVERWING_FLAG, true); - m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_PLAYER; + m_FlagState[TEAM_INDEX_ALLIANCE] = BG_WS_FLAG_STATE_ON_PLAYER; UpdateFlagState(HORDE, BG_WS_FLAG_STATE_ON_PLAYER); UpdateWorldState(BG_WS_FLAG_UNK_ALLIANCE, 1); } @@ -425,7 +425,7 @@ void BattleGroundWS::EventPlayerClickedOnFlag(Player* source, GameObject* target SpawnEvent(WS_EVENT_FLAG_H, 0, false); SetHordeFlagCarrier(source->GetObjectGuid()); source->CastSpell(source, BG_WS_SPELL_WARSONG_FLAG, true); - m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_PLAYER; + m_FlagState[TEAM_INDEX_HORDE] = BG_WS_FLAG_STATE_ON_PLAYER; UpdateFlagState(ALLIANCE, BG_WS_FLAG_STATE_ON_PLAYER); UpdateWorldState(BG_WS_FLAG_UNK_HORDE, 1); } @@ -478,9 +478,9 @@ void BattleGroundWS::UpdateFlagState(Team team, uint32 value) void BattleGroundWS::UpdateTeamScore(Team team) { if (team == ALLIANCE) - UpdateWorldState(BG_WS_FLAG_CAPTURES_ALLIANCE, m_TeamScores[BG_TEAM_ALLIANCE]); + UpdateWorldState(BG_WS_FLAG_CAPTURES_ALLIANCE, m_TeamScores[TEAM_INDEX_ALLIANCE]); else - UpdateWorldState(BG_WS_FLAG_CAPTURES_HORDE, m_TeamScores[BG_TEAM_HORDE]); + UpdateWorldState(BG_WS_FLAG_CAPTURES_HORDE, m_TeamScores[TEAM_INDEX_HORDE]); } void BattleGroundWS::HandleAreaTrigger(Player* source, uint32 trigger) @@ -501,12 +501,12 @@ void BattleGroundWS::HandleAreaTrigger(Player* source, uint32 trigger) case 3709: // Horde elixir of berserk spawn break; case 3646: // Alliance Flag spawn - if (m_FlagState[BG_TEAM_HORDE] && !m_FlagState[BG_TEAM_ALLIANCE]) + if (m_FlagState[TEAM_INDEX_HORDE] && !m_FlagState[TEAM_INDEX_ALLIANCE]) if (GetHordeFlagCarrierGuid() == source->GetObjectGuid()) EventPlayerCapturedFlag(source); break; case 3647: // Horde Flag spawn - if (m_FlagState[BG_TEAM_ALLIANCE] && !m_FlagState[BG_TEAM_HORDE]) + if (m_FlagState[TEAM_INDEX_ALLIANCE] && !m_FlagState[TEAM_INDEX_HORDE]) if (GetAllianceFlagCarrierGuid() == source->GetObjectGuid()) EventPlayerCapturedFlag(source); break; @@ -532,7 +532,7 @@ void BattleGroundWS::Reset() m_ActiveEvents[WS_EVENT_FLAG_A] = BG_EVENT_NONE; m_ActiveEvents[WS_EVENT_FLAG_H] = BG_EVENT_NONE; - for (uint8 i = 0; i < BG_TEAMS_COUNT; ++i) + for (uint8 i = 0; i < PVP_TEAM_COUNT; ++i) { m_DroppedFlagGuid[i].Clear(); m_FlagState[i] = BG_WS_FLAG_STATE_ON_BASE; @@ -620,31 +620,31 @@ WorldSafeLocsEntry const* BattleGroundWS::GetClosestGraveYard(Player* player) void BattleGroundWS::FillInitialWorldStates(WorldPacket& data, uint32& count) { - FillInitialWorldState(data, count, BG_WS_FLAG_CAPTURES_ALLIANCE, m_TeamScores[BG_TEAM_ALLIANCE]); - FillInitialWorldState(data, count, BG_WS_FLAG_CAPTURES_HORDE, m_TeamScores[BG_TEAM_HORDE]); + FillInitialWorldState(data, count, BG_WS_FLAG_CAPTURES_ALLIANCE, m_TeamScores[TEAM_INDEX_ALLIANCE]); + FillInitialWorldState(data, count, BG_WS_FLAG_CAPTURES_HORDE, m_TeamScores[TEAM_INDEX_HORDE]); - if (m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_ON_GROUND) + if (m_FlagState[TEAM_INDEX_ALLIANCE] == BG_WS_FLAG_STATE_ON_GROUND) FillInitialWorldState(data, count, BG_WS_FLAG_UNK_ALLIANCE, -1); - else if (m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_ON_PLAYER) + else if (m_FlagState[TEAM_INDEX_ALLIANCE] == BG_WS_FLAG_STATE_ON_PLAYER) FillInitialWorldState(data, count, BG_WS_FLAG_UNK_ALLIANCE, 1); else FillInitialWorldState(data, count, BG_WS_FLAG_UNK_ALLIANCE, 0); - if (m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_ON_GROUND) + if (m_FlagState[TEAM_INDEX_HORDE] == BG_WS_FLAG_STATE_ON_GROUND) FillInitialWorldState(data, count, BG_WS_FLAG_UNK_HORDE, -1); - else if (m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_ON_PLAYER) + else if (m_FlagState[TEAM_INDEX_HORDE] == BG_WS_FLAG_STATE_ON_PLAYER) FillInitialWorldState(data, count, BG_WS_FLAG_UNK_HORDE, 1); else FillInitialWorldState(data, count, BG_WS_FLAG_UNK_HORDE, 0); FillInitialWorldState(data, count, BG_WS_FLAG_CAPTURES_MAX, BG_WS_MAX_TEAM_SCORE); - if (m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_ON_PLAYER) + if (m_FlagState[TEAM_INDEX_HORDE] == BG_WS_FLAG_STATE_ON_PLAYER) FillInitialWorldState(data, count, BG_WS_FLAG_STATE_HORDE, 2); else FillInitialWorldState(data, count, BG_WS_FLAG_STATE_HORDE, 1); - if (m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_ON_PLAYER) + if (m_FlagState[TEAM_INDEX_ALLIANCE] == BG_WS_FLAG_STATE_ON_PLAYER) FillInitialWorldState(data, count, BG_WS_FLAG_STATE_ALLIANCE, 2); else FillInitialWorldState(data, count, BG_WS_FLAG_STATE_ALLIANCE, 1); diff --git a/src/game/BattleGround/BattleGroundWS.h b/src/game/BattleGround/BattleGroundWS.h index af268f7ca..4ab1c7775 100644 --- a/src/game/BattleGround/BattleGroundWS.h +++ b/src/game/BattleGround/BattleGroundWS.h @@ -87,6 +87,10 @@ class BattleGroundWGScore : public BattleGroundScore public: BattleGroundWGScore() : FlagCaptures(0), FlagReturns(0) {}; virtual ~BattleGroundWGScore() {}; + + uint32 GetAttr1() const { return FlagCaptures; } + uint32 GetAttr2() const { return FlagReturns; } + uint32 FlagCaptures; uint32 FlagReturns; }; @@ -154,10 +158,10 @@ class BattleGroundWS : public BattleGround ObjectGuid m_flagCarrierAlliance; ObjectGuid m_flagCarrierHorde; - ObjectGuid m_DroppedFlagGuid[BG_TEAMS_COUNT]; - uint8 m_FlagState[BG_TEAMS_COUNT]; - int32 m_FlagsTimer[BG_TEAMS_COUNT]; - int32 m_FlagsDropTimer[BG_TEAMS_COUNT]; + ObjectGuid m_DroppedFlagGuid[PVP_TEAM_COUNT]; + uint8 m_FlagState[PVP_TEAM_COUNT]; + int32 m_FlagsTimer[PVP_TEAM_COUNT]; + int32 m_FlagsDropTimer[PVP_TEAM_COUNT]; uint32 m_ReputationCapture; uint32 m_HonorWinKills; diff --git a/src/game/ChatCommands/Level2.cpp b/src/game/ChatCommands/Level2.cpp index f88be755b..c841031bf 100644 --- a/src/game/ChatCommands/Level2.cpp +++ b/src/game/ChatCommands/Level2.cpp @@ -50,6 +50,7 @@ #include "Util.h" #include "GridNotifiers.h" #include "GridNotifiersImpl.h" +#include "CellImpl.h" #include "WaypointMovementGenerator.h" #include #include diff --git a/src/game/ChatCommands/Level3.cpp b/src/game/ChatCommands/Level3.cpp index c3c5112b3..30249bf80 100644 --- a/src/game/ChatCommands/Level3.cpp +++ b/src/game/ChatCommands/Level3.cpp @@ -6101,9 +6101,7 @@ bool ChatHandler::HandleGMFlyCommand(char* args) if (!target) target = m_session->GetPlayer(); - WorldPacket data; - target->BuildMoveSetCanFlyPacket(&data, value, 0); - target->SendMessageToSet(&data, true); + target->SetCanFly(value); PSendSysMessage(LANG_COMMAND_FLYMODE_STATUS, GetNameLink(target).c_str(), args); return true; } @@ -7310,6 +7308,64 @@ bool ChatHandler::HandleMmapTestArea(char* args) return true; } +// use ".mmap testheight 10" selecting any creature/player +bool ChatHandler::HandleMmapTestHeight(char* args) +{ + float radius = 0.0f; + ExtractFloat(&args, radius); + if (radius > 40.0f) + radius = 40.0f; + + Unit* unit = getSelectedUnit(); + + Player* player = m_session->GetPlayer(); + if (!unit) + unit = player; + + if (unit->GetTypeId() == TYPEID_UNIT) + { + if (radius < 0.1f) + radius = static_cast(unit)->GetRespawnRadius(); + } + else + { + if (unit->GetTypeId() != TYPEID_PLAYER) + { + PSendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + return false; + } + } + + if (radius < 0.1f) + { + PSendSysMessage("Provided spawn radius for %s is too small. Using 5.0f instead.", unit->GetGuidStr().c_str()); + radius = 5.0f; + } + + float gx, gy, gz; + unit->GetPosition(gx, gy, gz); + + Creature* summoned = unit->SummonCreature(VISUAL_WAYPOINT, gx, gy, gz + 0.5f, 0, TEMPSUMMON_TIMED_DESPAWN, 20000); + summoned->CastSpell(summoned, 8599, false); + uint32 tries = 1; + uint32 successes = 0; + uint32 startTime = WorldTimer::getMSTime(); + for (; tries < 500; ++tries) + { + unit->GetPosition(gx, gy, gz); + if (unit->GetMap()->GetReachableRandomPosition(unit, gx, gy, gz, radius)) + { + unit->SummonCreature(VISUAL_WAYPOINT, gx, gy, gz, 0, TEMPSUMMON_TIMED_DESPAWN, 15000); + ++successes; + if (successes >= 100) + break; + } + } + uint32 genTime = WorldTimer::getMSTimeDiff(startTime, WorldTimer::getMSTime()); + PSendSysMessage("Generated %u valid points for %u try in %ums.", successes, tries, genTime); + return true; +} + bool ChatHandler::HandleServerResetAllRaidCommand(char* args) { PSendSysMessage("Global raid instances reset, all players in raid instances will be teleported to homebind!"); diff --git a/src/game/ChatCommands/debugcmds.cpp b/src/game/ChatCommands/debugcmds.cpp index 1aa615852..53fb5ad69 100644 --- a/src/game/ChatCommands/debugcmds.cpp +++ b/src/game/ChatCommands/debugcmds.cpp @@ -1117,10 +1117,10 @@ bool ChatHandler::HandleDebugSpellCoefsCommand(char* args) char const* dotDamageStr = GetMangosString(LANG_DOT_DAMAGE); PSendSysMessage(LANG_SPELLCOEFS, spellid, isDirectHeal ? directHealStr : directDamageStr, - direct_calc, direct_calc * 1.88f, bonus ? bonus->direct_damage : 0.0f, bonus ? bonus->ap_bonus : 0.0f); + direct_calc, direct_calc * SCALE_SPELLPOWER_HEALING, bonus ? bonus->direct_damage : 0.0f, bonus ? bonus->ap_bonus : 0.0f); PSendSysMessage(LANG_SPELLCOEFS, spellid, isDotHeal ? dotHealStr : dotDamageStr, - dot_calc, dot_calc * 1.88f, bonus ? bonus->dot_damage : 0.0f, bonus ? bonus->ap_dot_bonus : 0.0f); - + dot_calc, dot_calc * SCALE_SPELLPOWER_HEALING, bonus ? bonus->dot_damage : 0.0f, bonus ? bonus->ap_dot_bonus : 0.0f); + return true; } diff --git a/src/game/MotionGenerators/ConfusedMovementGenerator.cpp b/src/game/MotionGenerators/ConfusedMovementGenerator.cpp index 480b5976f..b40fdf32b 100644 --- a/src/game/MotionGenerators/ConfusedMovementGenerator.cpp +++ b/src/game/MotionGenerators/ConfusedMovementGenerator.cpp @@ -118,7 +118,7 @@ template<> void ConfusedMovementGenerator::Finalize(Player& unit) { unit.clearUnitState(UNIT_STAT_CONFUSED | UNIT_STAT_CONFUSED_MOVE); - unit.StopMoving(); + unit.StopMoving(true); } template<> diff --git a/src/game/MotionGenerators/FleeingMovementGenerator.cpp b/src/game/MotionGenerators/FleeingMovementGenerator.cpp index bc1c287dd..50b03e92f 100644 --- a/src/game/MotionGenerators/FleeingMovementGenerator.cpp +++ b/src/game/MotionGenerators/FleeingMovementGenerator.cpp @@ -37,16 +37,17 @@ template void FleeingMovementGenerator::_setTargetLocation(T& owner) { - if (!&owner) - return; - // ignore in case other no reaction state if (owner.hasUnitState((UNIT_STAT_CAN_NOT_REACT | UNIT_STAT_NOT_MOVE) & ~UNIT_STAT_FLEEING)) return; float x, y, z; if (!_getPoint(owner, x, y, z)) + { + // random point not found recheck later + i_nextCheckTime.Reset(50); return; + } owner.addUnitState(UNIT_STAT_FLEEING_MOVE); @@ -55,7 +56,8 @@ void FleeingMovementGenerator::_setTargetLocation(T& owner) path.calculate(x, y, z); if (path.getPathType() & PATHFIND_NOPATH) { - i_nextCheckTime.Reset(urand(1000, 1500)); + // path not found recheck later + i_nextCheckTime.Reset(50); return; } @@ -69,9 +71,6 @@ void FleeingMovementGenerator::_setTargetLocation(T& owner) template bool FleeingMovementGenerator::_getPoint(T& owner, float& x, float& y, float& z) { - if (!&owner) - return false; - float dist_from_caster, angle_to_caster; if (Unit* fright = ObjectAccessor::GetUnit(owner, i_frightGuid)) { @@ -109,12 +108,23 @@ bool FleeingMovementGenerator::_getPoint(T& owner, float& x, float& y, float& x = curr_x + dist * cos(angle); y = curr_y + dist * sin(angle); - z = curr_z; + z = curr_z + 0.5f; + + // try to fix z + if (!owner.GetMap()->GetHeightInRange(owner.GetPhaseMask(), x, y, z)) + return false; if (owner.GetTypeId() == TYPEID_PLAYER) - owner.GetMap()->GetHitPosition(curr_x, curr_y, curr_z, x, y, z, owner.GetPhaseMask(), -0.1f); - - owner.UpdateAllowedPositionZ(x, y, z); + { + // check any collision + float testZ = z + 0.5f; // needed to avoid some false positive hit detection of terrain or passable little object + if (owner.GetMap()->GetHitPosition(curr_x, curr_y, curr_z + 0.5f, x, y, testZ, owner.GetPhaseMask(), -0.1f)) + { + z = testZ; + if (!owner.GetMap()->GetHeightInRange(owner.GetPhaseMask(), x, y, z)) + return false; + } + } return true; } diff --git a/src/game/MotionGenerators/MotionMaster.cpp b/src/game/MotionGenerators/MotionMaster.cpp index 57cdde682..657d45c5c 100644 --- a/src/game/MotionGenerators/MotionMaster.cpp +++ b/src/game/MotionGenerators/MotionMaster.cpp @@ -509,6 +509,16 @@ void MotionMaster::MoveJump(float x, float y, float z, float horizontalSpeed, fl Mutate(new EffectMovementGenerator(id)); } +void MotionMaster::MoveDestination(float x, float y, float z, float o, float horizontalSpeed, float max_height, Unit* target) +{ + Movement::MoveSplineInit init(*m_owner); + init.MoveTo(x, y, z); + init.SetParabolic(max_height, 0); + init.SetVelocity(horizontalSpeed); + target ? init.SetFacing(target) : init.SetFacing(o); + init.Launch(); +} + void MotionMaster::MoveFall() { // use larger distance for vmap height search than in most other cases diff --git a/src/game/MotionGenerators/MotionMaster.h b/src/game/MotionGenerators/MotionMaster.h index 62727098a..6dee0c5ba 100644 --- a/src/game/MotionGenerators/MotionMaster.h +++ b/src/game/MotionGenerators/MotionMaster.h @@ -117,6 +117,7 @@ class MotionMaster : private std::stack void MoveTaxiFlight(uint32 path, uint32 pathnode); void MoveDistract(uint32 timeLimit); void MoveJump(float x, float y, float z, float horizontalSpeed, float max_height, uint32 id = 0); + void MoveDestination(float x, float y, float z, float o, float horizontalSpeed, float max_height, Unit* target = nullptr); void MoveFall(); void MoveFlyOrLand(uint32 id, float x, float y, float z, bool liftOff); diff --git a/src/game/MotionGenerators/WaypointMovementGenerator.cpp b/src/game/MotionGenerators/WaypointMovementGenerator.cpp index e6f1ac76b..a93181037 100644 --- a/src/game/MotionGenerators/WaypointMovementGenerator.cpp +++ b/src/game/MotionGenerators/WaypointMovementGenerator.cpp @@ -333,6 +333,7 @@ void FlightPathMovementGenerator::Finalize(Player& player) player.Unmount(); player.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT); + player.SetClientControl(&player, 1); if (player.m_taxi.empty()) { diff --git a/src/game/Object/AggressorAI.cpp b/src/game/Object/AggressorAI.cpp index 9cbfff38b..9b77956d1 100644 --- a/src/game/Object/AggressorAI.cpp +++ b/src/game/Object/AggressorAI.cpp @@ -55,7 +55,7 @@ AggressorAI::MoveInLineOfSight(Unit* u) return; if (m_creature->CanInitiateAttack() && u->IsTargetableForAttack() && - m_creature->IsHostileTo(u) && u->isInAccessablePlaceFor(m_creature)) + m_creature->IsHostileTo(u) && u->IsInAccessablePlaceFor(m_creature)) { float attackRadius = m_creature->GetAttackDistance(u); if (m_creature->IsWithinDistInMap(u, attackRadius) && m_creature->IsWithinLOSInMap(u)) diff --git a/src/game/Object/Creature.cpp b/src/game/Object/Creature.cpp index 21bb0fddd..15dafe4dc 100644 --- a/src/game/Object/Creature.cpp +++ b/src/game/Object/Creature.cpp @@ -208,6 +208,10 @@ void Creature::AddToWorld() { GetMap()->GetObjectsStore().insert(GetObjectGuid(), (Creature*)this); } Unit::AddToWorld(); + + // Make active if required + if (sWorld.isForceLoadMap(GetMapId()) || (GetCreatureInfo()->ExtraFlags & CREATURE_EXTRA_FLAG_ACTIVE)) + SetActiveObjectState(true); } void Creature::RemoveFromWorld() @@ -384,7 +388,14 @@ bool Creature::InitEntry(uint32 Entry, CreatureData const* data /*=NULL*/, GameE UpdateSpeed(MOVE_WALK, false); UpdateSpeed(MOVE_RUN, false); - SetLevitate(cinfo->InhabitType & INHABIT_AIR); + SetLevitate((cinfo->InhabitType & INHABIT_AIR) != 0); // TODO: may not be correct to send opcode at this point (already handled by UPDATE_OBJECT createObject) + + // check if we need to add swimming movement. TODO: i thing movement flags should be computed automatically at each movement of creature so we need a sort of UpdateMovementFlags() method + if (cinfo->InhabitType & INHABIT_WATER && // check inhabit type water + !(cinfo->ExtraFlags & CREATURE_EXTRA_FLAG_WALK_IN_WATER) && // check if creature is forced to walk (crabs, giant,...) + data && // check if there is data to get creature spawn pos + GetMap()->GetTerrain()->IsSwimmable(data->posX, data->posY, data->posZ, minfo->bounding_radius)) // check if creature is in water and have enough space to swim + m_movementInfo.AddMovementFlag(MOVEFLAG_SWIMMING); // add swimming movement // checked at loading m_defaultMovementType = MovementGeneratorType(cinfo->MovementType); @@ -485,8 +496,10 @@ uint32 Creature::ChooseDisplayId(const CreatureInfo* cinfo, const CreatureData* // if mod2 use mod2 unless mod2 has modelid_alt_model (then both by 50%-chance) // if mod1 use mod1 - // model selected here may be replaced with other_gender using own function + // The follow decision tree needs to be updated if MAX_CREATURE_MODEL is changed. + static_assert(MAX_CREATURE_MODEL == 4, "Need to update model selection code for new or removed model fields"); + // model selected here may be replaced with other_gender using own function if (cinfo->ModelId[3] && cinfo->ModelId[2] && cinfo->ModelId[1] && cinfo->ModelId[0]) { display_id = cinfo->ModelId[urand(0, 3)]; @@ -1159,6 +1172,9 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) CreatureInfo const* cinfo = GetCreatureInfo(); if (cinfo) { + // The following if-else assumes that there are 4 model fields and needs updating if this is changed. + static_assert(MAX_CREATURE_MODEL == 4, "Need to update custom model check for new/removed model fields."); + if (displayId != cinfo->ModelId[0] && displayId != cinfo->ModelId[1] && displayId != cinfo->ModelId[2] && displayId != cinfo->ModelId[3]) { @@ -1765,6 +1781,35 @@ bool Creature::IsImmuneToSpellEffect(SpellEntry const* spellInfo, SpellEffectInd return Unit::IsImmuneToSpellEffect(spellInfo, index, castOnSelf); } +// Set loot status. Also handle remove corpse timer +void Creature::SetLootStatus(CreatureLootStatus status) +{ + if (status <= m_lootStatus) + return; + + m_lootStatus = status; + switch (status) + { + case CREATURE_LOOT_STATUS_LOOTED: + if (m_creatureInfo->SkinningLootId) + SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); + else + RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + break; + case CREATURE_LOOT_STATUS_SKINNED: + m_corpseDecayTimer = 0; // remove corpse at next update + RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); + RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + break; + case CREATURE_LOOT_STATUS_SKIN_AVAILABLE: + SetFlag(UNIT_FIELD_FLAGS, UNIT_DYNFLAG_LOOTABLE); + RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); + break; + default: + break; + } +} + // return true if this creature is tapped by the player or by a member of his group. bool Creature::IsTappedBy(Player const* player) const { @@ -2038,7 +2083,7 @@ bool Creature::IsOutOfThreatArea(Unit* pVictim) const if (!pVictim->IsTargetableForAttack()) return true; - if (!pVictim->isInAccessablePlaceFor(this)) + if (!pVictim->IsInAccessablePlaceFor(this)) return true; if (!pVictim->IsVisibleForOrDetect(this, this, false)) @@ -2771,6 +2816,54 @@ void Creature::SetLevitate(bool enable) } } +void Creature::SetSwim(bool enable) +{ + if (enable) + m_movementInfo.AddMovementFlag(MOVEFLAG_SWIMMING); + else + m_movementInfo.RemoveMovementFlag(MOVEFLAG_SWIMMING); + + WorldPacket data(enable ? SMSG_SPLINE_MOVE_START_SWIM : SMSG_SPLINE_MOVE_STOP_SWIM); + data << GetPackGUID(); + SendMessageToSet(&data, true); +} + +void Creature::SetCanFly(bool enable) +{ + if (enable) + m_movementInfo.AddMovementFlag(MOVEFLAG_CAN_FLY); + else + m_movementInfo.RemoveMovementFlag(MOVEFLAG_CAN_FLY); + + WorldPacket data(enable ? SMSG_SPLINE_MOVE_SET_FLYING : SMSG_SPLINE_MOVE_UNSET_FLYING, 9); + data << GetPackGUID(); + SendMessageToSet(&data, true); +} + +void Creature::SetFeatherFall(bool enable) +{ + if (enable) + m_movementInfo.AddMovementFlag(MOVEFLAG_SAFE_FALL); + else + m_movementInfo.RemoveMovementFlag(MOVEFLAG_SAFE_FALL); + + WorldPacket data(enable ? SMSG_SPLINE_MOVE_FEATHER_FALL : SMSG_SPLINE_MOVE_NORMAL_FALL); + data << GetPackGUID(); + SendMessageToSet(&data, true); +} + +void Creature::SetHover(bool enable) +{ + if (enable) + m_movementInfo.AddMovementFlag(MOVEFLAG_HOVER); + else + m_movementInfo.RemoveMovementFlag(MOVEFLAG_HOVER); + + WorldPacket data(enable ? SMSG_SPLINE_MOVE_SET_HOVER : SMSG_SPLINE_MOVE_UNSET_HOVER, 9); + data << GetPackGUID(); + SendMessageToSet(&data, false); +} + void Creature::SetRoot(bool enable) { if (enable) diff --git a/src/game/Object/Creature.h b/src/game/Object/Creature.h index 292b8c5da..283a2b85e 100644 --- a/src/game/Object/Creature.h +++ b/src/game/Object/Creature.h @@ -542,11 +542,11 @@ class Creature : public Unit void SetCorpseDelay(uint32 delay) { m_corpseDelay = delay; } uint32 GetCorpseDelay() const { return m_corpseDelay; } bool IsRacialLeader() const { return GetCreatureInfo()->RacialLeader; } - bool IsCivilian() const { return GetCreatureInfo()->ExtraFlags & CREATURE_EXTRA_FLAG_CIVILIAN; } - bool IsGuard() const { return GetCreatureInfo()->ExtraFlags & CREATURE_EXTRA_FLAG_GUARD; } + bool IsCivilian() const { return (GetCreatureInfo()->ExtraFlags & CREATURE_EXTRA_FLAG_CIVILIAN) != 0; } + bool IsGuard() const { return (GetCreatureInfo()->ExtraFlags & CREATURE_EXTRA_FLAG_GUARD) != 0; } - bool CanWalk() const { return GetCreatureInfo()->InhabitType & INHABIT_GROUND; } - bool CanSwim() const { return GetCreatureInfo()->InhabitType & INHABIT_WATER; } + bool CanWalk() const { return (GetCreatureInfo()->InhabitType & INHABIT_GROUND) != 0; } + bool CanSwim() const { return (GetCreatureInfo()->InhabitType & INHABIT_WATER) != 0; } bool IsSwimming() const { return (m_movementInfo.HasMovementFlag((MovementFlags)(MOVEFLAG_SWIMMING))); } bool CanFly() const { return (GetCreatureInfo()->InhabitType & INHABIT_AIR) || (GetByteValue(UNIT_FIELD_BYTES_1, 3) & UNIT_BYTE1_FLAG_FLY_ANIM) || m_movementInfo.HasMovementFlag((MovementFlags)(MOVEFLAG_LEVITATING | MOVEFLAG_CAN_FLY)); } bool IsFlying() const { return (m_movementInfo.HasMovementFlag((MovementFlags)(MOVEFLAG_FLYING | MOVEFLAG_LEVITATING))); } @@ -560,6 +560,7 @@ class Creature : public Unit bool IsImmuneToSpell(SpellEntry const* spellInfo, bool castOnSelf) override; bool IsImmuneToSpellEffect(SpellEntry const* spellInfo, SpellEffectIndex index, bool castOnSelf) const override; + void SetLootStatus(CreatureLootStatus status); bool IsTappedBy(Player const* player) const; bool IsElite() const @@ -591,6 +592,10 @@ class Creature : public Unit void SetWalk(bool enable, bool asDefault = true); void SetLevitate(bool enable); + void SetSwim(bool enable) override; + void SetCanFly(bool enable) override; + void SetFeatherFall(bool enable) override; + void SetHover(bool enable) override; void SetRoot(bool enable) override; void SetWaterWalk(bool enable) override; @@ -818,6 +823,7 @@ class Creature : public Unit uint32 m_lootMoney; ObjectGuid m_lootRecipientGuid; // player who will have rights for looting if m_lootGroupRecipient==0 or group disbanded uint32 m_lootGroupRecipientId; // group who will have rights for looting if set and exist + CreatureLootStatus m_lootStatus; // loot status (used to know when we could loot, pickpocket or skin) /// Timers uint32 m_corpseDecayTimer; // (msecs)timer for death or corpse disappearance diff --git a/src/game/Object/CreatureEventAI.cpp b/src/game/Object/CreatureEventAI.cpp index 21bea214b..9089fbf70 100644 --- a/src/game/Object/CreatureEventAI.cpp +++ b/src/game/Object/CreatureEventAI.cpp @@ -182,6 +182,7 @@ inline bool IsTimerBasedEvent(EventAI_Type type) case EVENT_T_MISSING_AURA: case EVENT_T_TARGET_MISSING_AURA: case EVENT_T_RANGE: + case EVENT_T_ENERGY: return true; default: return false; @@ -276,6 +277,13 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction pHolder.UpdateRepeatTimer(m_creature, event.spell_hit.repeatMin, event.spell_hit.repeatMax); break; case EVENT_T_RANGE: + if (!m_creature->IsInCombat() || !m_creature->getVictim() || !m_creature->IsInMap(m_creature->getVictim())) + return false; + + // DISCUSS TODO - Likely replace IsInRange check with CombatReach checks (as used rather for such checks) + if (!m_creature->IsInRange(m_creature->getVictim(), (float)event.range.minDist, (float)event.range.maxDist)) + return false; + // Repeat Timers pHolder.UpdateRepeatTimer(m_creature, event.range.repeatMin, event.range.repeatMax); break; @@ -394,6 +402,9 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction break; case EVENT_T_AURA: { + if (!m_creature->IsInCombat()) + return false; + SpellAuraHolder* holder = m_creature->GetSpellAuraHolder(event.buffed.spellId); if (!holder || holder->GetStackAmount() < event.buffed.amount) return false; @@ -419,6 +430,9 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction } case EVENT_T_MISSING_AURA: { + if (!m_creature->IsInCombat()) + return false; + SpellAuraHolder* holder = m_creature->GetSpellAuraHolder(event.buffed.spellId); if (holder && holder->GetStackAmount() >= event.buffed.amount) return false; @@ -444,6 +458,21 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction } case EVENT_T_RECEIVE_AI_EVENT: break; + case EVENT_T_ENERGY: + { + if (!m_creature->IsInCombat() || !m_creature->GetMaxPower(POWER_ENERGY)) + return false; + + uint32 perc = (m_creature->GetPower(POWER_ENERGY) * 100) / m_creature->GetMaxPower(POWER_ENERGY); + + if (perc > event.percent_range.percentMax || perc < event.percent_range.percentMin) + return false; + + LOG_PROCESS_EVENT; + // Repeat Timers + pHolder.UpdateRepeatTimer(m_creature, event.percent_range.repeatMin, event.percent_range.repeatMax); + break; + } default: sLog.outErrorEventAI("Creature %u using Event %u has invalid Event Type(%u), missing from ProcessEvent() Switch.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); break; @@ -478,7 +507,7 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction if (count) { // select action number from found amount - uint32 idx = urand(0, count - 1); + uint32 idx = rnd % count; // find selected action, skipping not used uint32 j = 0; @@ -520,17 +549,17 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32 if (action.type == ACTION_T_TEXT) { if (action.text.TextId[1] && action.text.TextId[2]) - textId = action.text.TextId[urand(0, 2)]; - else if (action.text.TextId[1] && urand(0, 1)) + textId = action.text.TextId[rnd % 3]; + else if (action.text.TextId[1] && (rnd % 2)) textId = action.text.TextId[1]; else textId = action.text.TextId[0]; } // ACTION_T_CHANCED_TEXT, chance hits - else if (urand(0, 99) < action.chanced_text.chance) + else if ((rnd % 100) < action.chanced_text.chance) { if (action.chanced_text.TextId[0] && action.chanced_text.TextId[1]) - textId = action.chanced_text.TextId[urand(0, 1)]; + textId = action.chanced_text.TextId[rnd % 2]; else textId = action.chanced_text.TextId[0]; } @@ -839,9 +868,9 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32 Creature* pCreature = NULL; if ((*i).second.SpawnTimeSecs) - pCreature = m_creature->SummonCreature(action.summon_id.creatureId, (*i).second.position_x, (*i).second.position_y, (*i).second.position_z, (*i).second.orientation, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, (*i).second.SpawnTimeSecs); + pCreature = m_creature->SummonCreature(action.summon_id.creatureId, i->second.position_x, i->second.position_y, i->second.position_z, i->second.orientation, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, i->second.SpawnTimeSecs); else - pCreature = m_creature->SummonCreature(action.summon_id.creatureId, (*i).second.position_x, (*i).second.position_y, (*i).second.position_z, (*i).second.orientation, TEMPSUMMON_TIMED_OOC_DESPAWN, 0); + pCreature = m_creature->SummonCreature(action.summon_id.creatureId, i->second.position_x, i->second.position_y, i->second.position_z, i->second.orientation, TEMPSUMMON_TIMED_OOC_DESPAWN, 0); if (!pCreature) sLog.outErrorEventAI("failed to spawn creature %u. EventId %d.Creature %d", action.summon_id.creatureId, EventId, m_creature->GetEntry()); @@ -1035,29 +1064,26 @@ void CreatureEventAI::Reset() { m_EventUpdateTime = EVENT_UPDATE_TIME; m_EventDiff = 0; - m_throwAIEventStep = 0; // Reset all events to enabled for (CreatureEventAIList::iterator i = m_CreatureEventAIList.begin(); i != m_CreatureEventAIList.end(); ++i) { - CreatureEventAI_Event const& event = (*i).Event; + CreatureEventAI_Event const& event = i->Event; switch (event.event_type) { - // Reset all out of combat timers + // Reset all out of combat timers case EVENT_T_TIMER_OOC: { - if ((*i).UpdateRepeatTimer(m_creature, event.timer.initialMin, event.timer.initialMax)) - (*i).Enabled = true; + if (i->UpdateRepeatTimer(m_creature, event.timer.initialMin, event.timer.initialMax)) + i->Enabled = true; break; } default: + // TODO: enable below code line / verify this is correct to enable events previously disabled (ex. aggro yell), instead of enable this in void Aggro() + //i->Enabled = true; + //i->Time = 0; break; - // default: - // TODO: enable below code line / verify this is correct to enable events previously disabled (ex. aggro yell), instead of enable this in void Aggro() - //(*i).Enabled = true; - //(*i).Time = 0; - // break; } } } @@ -1066,7 +1092,7 @@ void CreatureEventAI::JustReachedHome() { for (CreatureEventAIList::iterator i = m_CreatureEventAIList.begin(); i != m_CreatureEventAIList.end(); ++i) { - if ((*i).Event.event_type == EVENT_T_REACHED_HOME) + if (i->Event.event_type == EVENT_T_REACHED_HOME) ProcessEvent(*i); } @@ -1079,18 +1105,16 @@ void CreatureEventAI::EnterEvadeMode() m_creature->DeleteThreatList(); m_creature->CombatStop(true); - if (m_creature->IsAlive()) + // only alive creatures that are not on transport can return to home position + if (m_creature->IsAlive() && !m_creature->IsBoarded()) m_creature->GetMotionMaster()->MoveTargetedHome(); - m_creature->SetLootRecipient(NULL); - - if (m_bEmptyList) - return; + m_creature->SetLootRecipient(nullptr); // Handle Evade events for (CreatureEventAIList::iterator i = m_CreatureEventAIList.begin(); i != m_CreatureEventAIList.end(); ++i) { - if ((*i).Event.event_type == EVENT_T_EVADE) + if (i->Event.event_type == EVENT_T_EVADE) ProcessEvent(*i); } } @@ -1112,7 +1136,7 @@ void CreatureEventAI::JustDied(Unit* killer) // Handle On Death events for (CreatureEventAIList::iterator i = m_CreatureEventAIList.begin(); i != m_CreatureEventAIList.end(); ++i) { - if ((*i).Event.event_type == EVENT_T_DEATH) + if (i->Event.event_type == EVENT_T_DEATH) ProcessEvent(*i, killer); } @@ -1127,7 +1151,7 @@ void CreatureEventAI::KilledUnit(Unit* victim) for (CreatureEventAIList::iterator i = m_CreatureEventAIList.begin(); i != m_CreatureEventAIList.end(); ++i) { - if ((*i).Event.event_type == EVENT_T_KILL) + if (i->Event.event_type == EVENT_T_KILL) ProcessEvent(*i, victim); } } @@ -1136,7 +1160,7 @@ void CreatureEventAI::JustSummoned(Creature* pUnit) { for (CreatureEventAIList::iterator i = m_CreatureEventAIList.begin(); i != m_CreatureEventAIList.end(); ++i) { - if ((*i).Event.event_type == EVENT_T_SUMMONED_UNIT) + if (i->Event.event_type == EVENT_T_SUMMONED_UNIT) ProcessEvent(*i, pUnit); } } @@ -1145,7 +1169,7 @@ void CreatureEventAI::SummonedCreatureJustDied(Creature* pUnit) { for (CreatureEventAIList::iterator i = m_CreatureEventAIList.begin(); i != m_CreatureEventAIList.end(); ++i) { - if ((*i).Event.event_type == EVENT_T_SUMMONED_JUST_DIED) + if (i->Event.event_type == EVENT_T_SUMMONED_JUST_DIED) ProcessEvent(*i, pUnit); } } @@ -1154,12 +1178,12 @@ void CreatureEventAI::SummonedCreatureDespawn(Creature* pUnit) { for (CreatureEventAIList::iterator i = m_CreatureEventAIList.begin(); i != m_CreatureEventAIList.end(); ++i) { - if ((*i).Event.event_type == EVENT_T_SUMMONED_JUST_DESPAWN) + if (i->Event.event_type == EVENT_T_SUMMONED_JUST_DESPAWN) ProcessEvent(*i, pUnit); } } -void CreatureEventAI::ReceiveAIEvent(AIEventType eventType, Creature* pSender, Unit* pInvoker, uint32 miscValue) +void CreatureEventAI::ReceiveAIEvent(AIEventType eventType, Creature* pSender, Unit* pInvoker, uint32 /*miscValue*/) { MANGOS_ASSERT(pSender); @@ -1169,29 +1193,29 @@ void CreatureEventAI::ReceiveAIEvent(AIEventType eventType, Creature* pSender, U itr->Event.receiveAIEvent.eventType == eventType && (!itr->Event.receiveAIEvent.senderEntry || itr->Event.receiveAIEvent.senderEntry == pSender->GetEntry())) ProcessEvent(*itr, pInvoker, pSender); } -} +} void CreatureEventAI::EnterCombat(Unit* enemy) { // Check for on combat start events for (CreatureEventAIList::iterator i = m_CreatureEventAIList.begin(); i != m_CreatureEventAIList.end(); ++i) { - CreatureEventAI_Event const& event = (*i).Event; + CreatureEventAI_Event const& event = i->Event; switch (event.event_type) { case EVENT_T_AGGRO: - (*i).Enabled = true; + i->Enabled = true; ProcessEvent(*i, enemy); break; - // Reset all in combat timers + // Reset all in combat timers case EVENT_T_TIMER_IN_COMBAT: - if ((*i).UpdateRepeatTimer(m_creature, event.timer.initialMin, event.timer.initialMax)) - (*i).Enabled = true; + if (i->UpdateRepeatTimer(m_creature, event.timer.initialMin, event.timer.initialMax)) + i->Enabled = true; break; - // All normal events need to be re-enabled and their time set to 0 + // All normal events need to be re-enabled and their time set to 0 default: - (*i).Enabled = true; - (*i).Time = 0; + i->Enabled = true; + i->Time = 0; break; } } @@ -1221,21 +1245,21 @@ void CreatureEventAI::MoveInLineOfSight(Unit* who) return; // Check for OOC LOS Event - if (!m_creature->getVictim()) + if (m_HasOOCLoSEvent && !m_creature->getVictim()) { for (CreatureEventAIList::iterator itr = m_CreatureEventAIList.begin(); itr != m_CreatureEventAIList.end(); ++itr) { - if ((*itr).Event.event_type == EVENT_T_OOC_LOS) + if (itr->Event.event_type == EVENT_T_OOC_LOS) { // can trigger if closer than fMaxAllowedRange - float fMaxAllowedRange = (float)(*itr).Event.ooc_los.maxRange; + float fMaxAllowedRange = (float)itr->Event.ooc_los.maxRange; - // if range is ok and we are actually in LOS - if (m_creature->IsWithinDistInMap(who, fMaxAllowedRange) && m_creature->IsWithinLOSInMap(who)) + // if friendly event && who is not hostile OR hostile event && who is hostile + if ((itr->Event.ooc_los.noHostile && !m_creature->IsHostileTo(who)) || + ((!itr->Event.ooc_los.noHostile) && m_creature->IsHostileTo(who))) { - // if friendly event&&who is not hostile OR hostile event&&who is hostile - if (((*itr).Event.ooc_los.noHostile && !m_creature->IsHostileTo(who)) || - ((!(*itr).Event.ooc_los.noHostile) && m_creature->IsHostileTo(who))) + // if range is ok and we are actually in LOS + if (m_creature->IsWithinDistInMap(who, fMaxAllowedRange) && m_creature->IsWithinLOSInMap(who)) ProcessEvent(*itr, who); } } @@ -1246,7 +1270,7 @@ void CreatureEventAI::MoveInLineOfSight(Unit* who) return; if (m_creature->CanInitiateAttack() && who->IsTargetableForAttack() && - m_creature->IsHostileTo(who) && who->isInAccessablePlaceFor(m_creature)) + m_creature->IsHostileTo(who) && who->IsInAccessablePlaceFor(m_creature)) { if (!m_creature->CanFly() && m_creature->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE) return; @@ -1271,10 +1295,10 @@ void CreatureEventAI::MoveInLineOfSight(Unit* who) void CreatureEventAI::SpellHit(Unit* pUnit, const SpellEntry* pSpell) { for (CreatureEventAIList::iterator i = m_CreatureEventAIList.begin(); i != m_CreatureEventAIList.end(); ++i) - if ((*i).Event.event_type == EVENT_T_SPELLHIT) + if (i->Event.event_type == EVENT_T_SPELLHIT) // If spell id matches (or no spell id) & if spell school matches (or no spell school) - if (!(*i).Event.spell_hit.spellId || pSpell->Id == (*i).Event.spell_hit.spellId) - if (pSpell->SchoolMask & (*i).Event.spell_hit.schoolMask) + if (!i->Event.spell_hit.spellId || pSpell->Id == i->Event.spell_hit.spellId) + if (pSpell->SchoolMask & i->Event.spell_hit.schoolMask) ProcessEvent(*i, pUnit); } @@ -1292,55 +1316,28 @@ void CreatureEventAI::UpdateAI(const uint32 diff) for (CreatureEventAIList::iterator i = m_CreatureEventAIList.begin(); i != m_CreatureEventAIList.end(); ++i) { // Decrement Timers - if ((*i).Time) + if (i->Time) { - if ((*i).Time > m_EventDiff) + if (i->Time > m_EventDiff) { // Do not decrement timers if event cannot trigger in this phase - if (!((*i).Event.event_inverse_phase_mask & (1 << m_Phase))) - (*i).Time -= m_EventDiff; - - // Skip processing of events that have time remaining - continue; + if (!(i->Event.event_inverse_phase_mask & (1 << m_Phase))) + i->Time -= m_EventDiff; } - else (*i).Time = 0; + else + i->Time = 0; } - // Events that are updated every EVENT_UPDATE_TIME - switch ((*i).Event.event_type) - { - case EVENT_T_TIMER_OOC: - case EVENT_T_TIMER_GENERIC: - ProcessEvent(*i); - break; - case EVENT_T_TIMER_IN_COMBAT: - case EVENT_T_MANA: - case EVENT_T_HP: - case EVENT_T_TARGET_HP: - case EVENT_T_TARGET_CASTING: - case EVENT_T_FRIENDLY_HP: - case EVENT_T_AURA: - case EVENT_T_TARGET_AURA: - case EVENT_T_MISSING_AURA: - case EVENT_T_TARGET_MISSING_AURA: - if (Combat) - ProcessEvent(*i); - break; - case EVENT_T_RANGE: - if (Combat) - { - if (m_creature->getVictim() && m_creature->IsInMap(m_creature->getVictim())) - if (m_creature->IsInRange(m_creature->getVictim(), (float)(*i).Event.range.minDist, (float)(*i).Event.range.maxDist)) - ProcessEvent(*i); - } - break; - default: - break; - } + // Skip processing of events that have time remaining or are disabled + if (!(i->Enabled) || i->Time) + continue; - m_EventDiff = 0; - m_EventUpdateTime = EVENT_UPDATE_TIME; + if (IsTimerBasedEvent(i->Event.event_type)) + ProcessEvent(*i); } + + m_EventDiff = 0; + m_EventUpdateTime = EVENT_UPDATE_TIME; } else { @@ -1348,9 +1345,23 @@ void CreatureEventAI::UpdateAI(const uint32 diff) m_EventUpdateTime -= diff; } - // Melee Auto-Attack (recheck m_creature->getVictim in case of combat state was changed while processing events) - if (Combat && m_MeleeEnabled && m_creature->getVictim()) - DoMeleeAttackIfReady(); + // Melee Auto-Attack (getVictim might be nullptr as result of timer based events and actions) + if (Combat && m_creature->getVictim()) + { + // Update creature dynamic movement position before doing anything else + if (m_DynamicMovement) + { + if (!m_creature->hasUnitState(UNIT_STAT_CAN_NOT_REACT) && !m_creature->IsNonMeleeSpellCasted(false)) + { + if (m_LastSpellMaxRange && m_creature->IsInRange(m_creature->getVictim(), 0, (m_LastSpellMaxRange / 1.5f))) + SetCombatMovement(false, true); + else + SetCombatMovement(true, true); + } + } + else if (m_MeleeEnabled) + DoMeleeAttackIfReady(); + } } bool CreatureEventAI::IsVisible(Unit* pl) const @@ -1478,12 +1489,12 @@ void CreatureEventAI::ReceiveEmote(Player* pPlayer, uint32 text_emote) { for (CreatureEventAIList::iterator itr = m_CreatureEventAIList.begin(); itr != m_CreatureEventAIList.end(); ++itr) { - if ((*itr).Event.event_type == EVENT_T_RECEIVE_EMOTE) + if (itr->Event.event_type == EVENT_T_RECEIVE_EMOTE) { - if ((*itr).Event.receive_emote.emoteId != text_emote) - return; + if (itr->Event.receive_emote.emoteId != text_emote) + continue; - PlayerCondition pcon(0, (*itr).Event.receive_emote.condition, (*itr).Event.receive_emote.conditionValue1, (*itr).Event.receive_emote.conditionValue2); + PlayerCondition pcon(0, itr->Event.receive_emote.condition, itr->Event.receive_emote.conditionValue1, itr->Event.receive_emote.conditionValue2); if (pcon.Meets(pPlayer, m_creature->GetMap(), m_creature, CONDITION_FROM_EVENTAI)) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "CreatureEventAI: ReceiveEmote CreatureEventAI: Condition ok, processing"); diff --git a/src/game/Object/CreatureEventAI.h b/src/game/Object/CreatureEventAI.h index d62304dea..a70550525 100644 --- a/src/game/Object/CreatureEventAI.h +++ b/src/game/Object/CreatureEventAI.h @@ -70,6 +70,7 @@ enum EventAI_Type EVENT_T_TARGET_MISSING_AURA = 28, // Param1 = SpellID, Param2 = Number of time stacked expected, Param3/4 Repeat Min/Max EVENT_T_TIMER_GENERIC = 29, // InitialMin, InitialMax, RepeatMin, RepeatMax EVENT_T_RECEIVE_AI_EVENT = 30, // AIEventType, Sender-Entry, unused, unused + EVENT_T_ENERGY = 31, // EnergyMax%, EnergyMin%, RepeatMin, RepeatMax EVENT_T_END, }; @@ -467,6 +468,7 @@ struct CreatureEventAI_Event // EVENT_T_MANA = 3 // EVENT_T_TARGET_HP = 12 // EVENT_T_TARGET_MANA = 18 + // EVENT_T_ENERGY = 31 struct { uint32 percentMax; diff --git a/src/game/Object/CreatureEventAIMgr.cpp b/src/game/Object/CreatureEventAIMgr.cpp index 6c82bde0d..a857e0398 100644 --- a/src/game/Object/CreatureEventAIMgr.cpp +++ b/src/game/Object/CreatureEventAIMgr.cpp @@ -90,7 +90,7 @@ void CreatureEventAIMgr::CheckUnusedAITexts() sLog.outErrorEventAI("Entry %i in table `creature_ai_texts` but not used in EventAI scripts.", *itr); } -/// Helper function to check if a target-suite is suitable for the event-type +/// Helper function to check if a target-type is suitable for the event-type bool IsValidTargetType(EventAI_Type eventType, EventAI_ActionType actionType, uint32 targetType, uint32 eventId, uint8 action) { switch (targetType) @@ -327,6 +327,7 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts() case EVENT_T_MANA: case EVENT_T_TARGET_HP: case EVENT_T_TARGET_MANA: + case EVENT_T_ENERGY: if (temp.percent_range.percentMax > 100) sLog.outErrorEventAI("Creature %u are using percentage event(%u) with param2 (MinPercent) > 100. Event will never trigger! ", temp.creature_id, i); diff --git a/src/game/Object/GameObject.cpp b/src/game/Object/GameObject.cpp index e7c31a891..cfaa0fbd2 100644 --- a/src/game/Object/GameObject.cpp +++ b/src/game/Object/GameObject.cpp @@ -1269,8 +1269,11 @@ void GameObject::Use(Unit* user) player->RewardPlayerAndGroupAtCast(this); } - if (scriptReturnValue) - { return; } + // activate script + if (!scriptReturnValue) + GetMap()->ScriptsStart(sGameObjectScripts, GetGUIDLow(), spellCaster, this); + else + return; // cast this spell later if provided spellId = info->goober.spellId; @@ -2382,6 +2385,23 @@ void GameObject::ForceGameObjectHealth(int32 diff, Unit* caster) SetGoAnimProgress(GetMaxHealth() ? m_useTimes * 255 / GetMaxHealth() : 255); } +float GameObject::GetInteractionDistance() +{ + switch (GetGoType()) + { + // TODO: find out how the client calculates the maximal usage distance to spellless working + // gameobjects like guildbanks and mailboxes - 10.0 is a just an abitrary chosen number + case GAMEOBJECT_TYPE_GUILD_BANK: + case GAMEOBJECT_TYPE_MAILBOX: + return 10.0f; + case GAMEOBJECT_TYPE_FISHINGHOLE: + case GAMEOBJECT_TYPE_FISHINGNODE: + return 20.0f + CONTACT_DISTANCE; // max spell range + default: + return INTERACTION_DISTANCE; + } +} + uint32 GameObject::GetScriptId() { return sScriptMgr.GetBoundScriptId(SCRIPTED_GAMEOBJECT, -int32(GetGUIDLow())) ? sScriptMgr.GetBoundScriptId(SCRIPTED_GAMEOBJECT, -int32(GetGUIDLow())) : sScriptMgr.GetBoundScriptId(SCRIPTED_GAMEOBJECT, GetEntry()); diff --git a/src/game/Object/GameObject.h b/src/game/Object/GameObject.h index c625405f2..f5b9be0a3 100644 --- a/src/game/Object/GameObject.h +++ b/src/game/Object/GameObject.h @@ -842,6 +842,11 @@ class GameObject : public WorldObject ObjectGuid m_lootRecipientGuid; // player who will have rights for looting if m_lootGroupRecipient==0 or group disbanded uint32 m_lootGroupRecipientId; // group who will have rights for looting if set and exist + // Used for chest type + bool m_isInUse; // only one player at time are allowed to open chest + time_t m_reStockTimer; // timer to refill the chest + time_t m_despawnTimer; // timer to despawn the chest if something changed in it + private: void SwitchDoorOrButton(bool activate, bool alternative = false); void TickCapturePoint(); diff --git a/src/game/Object/GuardAI.cpp b/src/game/Object/GuardAI.cpp index 2a45cd0a9..301d14a07 100644 --- a/src/game/Object/GuardAI.cpp +++ b/src/game/Object/GuardAI.cpp @@ -48,7 +48,7 @@ void GuardAI::MoveInLineOfSight(Unit* u) if (!m_creature->getVictim() && u->IsTargetableForAttack() && (u->IsHostileToPlayers() || m_creature->IsHostileTo(u) /*|| u->getVictim() && m_creature->IsFriendlyTo(u->getVictim())*/) && - u->isInAccessablePlaceFor(m_creature)) + u->IsInAccessablePlaceFor(m_creature)) { float attackRadius = m_creature->GetAttackDistance(u); if (m_creature->IsWithinDistInMap(u, attackRadius)) diff --git a/src/game/Object/LootMgr.cpp b/src/game/Object/LootMgr.cpp index a64b580eb..8c5a6d4ee 100644 --- a/src/game/Object/LootMgr.cpp +++ b/src/game/Object/LootMgr.cpp @@ -332,7 +332,7 @@ bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const return false; } - if (maxcount < mincountOrRef) // wrong max count + if (maxcount < (uint32)mincountOrRef) // wrong max count { sLog.outErrorDb("Table '%s' entry %d item %d: max count (%u) less that min count (%i) - skipped", store.GetName(), entry, itemid, uint32(maxcount), mincountOrRef); return false; diff --git a/src/game/Object/LootMgr.h b/src/game/Object/LootMgr.h index 4ea8b68dc..a11af2b4e 100644 --- a/src/game/Object/LootMgr.h +++ b/src/game/Object/LootMgr.h @@ -70,7 +70,9 @@ enum LootType LOOT_FISHINGHOLE = 20, // unsupported by client, sending LOOT_FISHING instead LOOT_FISHING_FAIL = 21, // unsupported by client, sending LOOT_FISHING instead - LOOT_INSIGNIA = 22 // unsupported by client, sending LOOT_CORPSE instead + LOOT_INSIGNIA = 22, // unsupported by client, sending LOOT_CORPSE instead + LOOT_MAIL = 23, + LOOT_SPELL = 24, }; enum LootSlotType diff --git a/src/game/Object/Object.cpp b/src/game/Object/Object.cpp index 4d7111678..6f8391729 100644 --- a/src/game/Object/Object.cpp +++ b/src/game/Object/Object.cpp @@ -664,19 +664,37 @@ void Object::BuildValuesUpdate(uint8 updatetype, ByteBuffer* data, UpdateMask* u { *data << (m_uint32Values[index] & ~UNIT_FLAG_NOT_SELECTABLE); } - // hide lootable animation for unallowed players + /* Hide loot animation for players that aren't permitted to loot the corpse */ else if (index == UNIT_DYNAMIC_FLAGS && GetTypeId() == TYPEID_UNIT) { + uint32 send_value = m_uint32Values[index]; + + /* Initiate pointer to creature so we can check loot */ + if (Creature* my_creature = (Creature*)this) + /* If the creature is NOT fully looted */ + if (!my_creature->loot.isLooted()) + /* If the lootable flag is NOT set */ + if (!(send_value & UNIT_DYNFLAG_LOOTABLE)) + { + /* Update it on the creature */ + my_creature->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + /* Update it in the packet */ + send_value = send_value | UNIT_DYNFLAG_LOOTABLE; + } + + /* If we're not allowed to loot the target, destroy the lootable flag */ if (!target->isAllowedToLoot((Creature*)this)) - *data << (m_uint32Values[index] & ~(UNIT_DYNFLAG_LOOTABLE | UNIT_DYNFLAG_TAPPED_BY_PLAYER)); - else - { - // flag only for original loot recipent - if (target->GetObjectGuid() == ((Creature*)this)->GetLootRecipientGuid()) - *data << m_uint32Values[index]; - else - *data << (m_uint32Values[index] & ~(UNIT_DYNFLAG_TAPPED | UNIT_DYNFLAG_TAPPED_BY_PLAYER)); - } + if (send_value & UNIT_DYNFLAG_LOOTABLE) + { send_value = send_value & ~UNIT_DYNFLAG_LOOTABLE; } + + /* If we are allowed to loot it and mob is tapped by us, destroy the tapped flag */ + bool is_tapped = target->IsTappedByMeOrMyGroup((Creature*)this); + + /* If the creature has tapped flag but is tapped by us, remove the flag */ + if (send_value & UNIT_DYNFLAG_TAPPED && is_tapped) + { send_value = send_value & ~UNIT_DYNFLAG_TAPPED; } + + *data << send_value; } else { @@ -1479,8 +1497,11 @@ void WorldObject::UpdateGroundPositionZ(float x, float y, float& z) const { z = new_z + 0.05f; } // just to be sure that we are not a few pixel under the surface } -void WorldObject::UpdateAllowedPositionZ(float x, float y, float& z) const +void WorldObject::UpdateAllowedPositionZ(float x, float y, float& z, Map* atMap /*=nullptr*/) const { + if (!atMap) + atMap = GetMap(); + switch (GetTypeId()) { case TYPEID_UNIT: @@ -1492,21 +1513,21 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float& z) const bool canSwim = ((Creature const*)this)->CanSwim(); float ground_z = z; float max_z = canSwim - ? GetTerrain()->GetWaterOrGroundLevel(x, y, z, &ground_z, !((Unit const*)this)->HasAuraType(SPELL_AURA_WATER_WALK)) - : ((ground_z = GetMap()->GetHeight(GetPhaseMask(), x, y, z))); + ? atMap->GetTerrain()->GetWaterOrGroundLevel(x, y, z, &ground_z, !((Unit const*)this)->HasAuraType(SPELL_AURA_WATER_WALK)) + : ((ground_z = atMap->GetHeight(GetPhaseMask(), x, y, z))); if (max_z > INVALID_HEIGHT) { if (z > max_z) - { z = max_z; } + z = max_z; else if (z < ground_z) - { z = ground_z; } + z = ground_z; } } else { - float ground_z = GetMap()->GetHeight(GetPhaseMask(), x, y, z); + float ground_z = atMap->GetHeight(GetPhaseMask(), x, y, z); if (z < ground_z) - { z = ground_z; } + z = ground_z; } break; } @@ -1516,18 +1537,18 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float& z) const if (!((Player const*)this)->CanFly()) { float ground_z = z; - float max_z = GetTerrain()->GetWaterOrGroundLevel(x, y, z, &ground_z, !((Unit const*)this)->HasAuraType(SPELL_AURA_WATER_WALK)); + float max_z = atMap->GetTerrain()->GetWaterOrGroundLevel(x, y, z, &ground_z, !((Unit const*)this)->HasAuraType(SPELL_AURA_WATER_WALK)); if (max_z > INVALID_HEIGHT) { if (z > max_z) - { z = max_z; } + z = max_z; else if (z < ground_z) - { z = ground_z; } + z = ground_z; } } else { - float ground_z = GetMap()->GetHeight(GetPhaseMask(), x, y, z); + float ground_z = atMap->GetHeight(GetPhaseMask(), x, y, z); if (z < ground_z) z = ground_z; } @@ -1535,9 +1556,9 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float& z) const } default: { - float ground_z = GetMap()->GetHeight(GetPhaseMask(), x, y, z); + float ground_z = atMap->GetHeight(GetPhaseMask(), x, y, z); if (ground_z > INVALID_HEIGHT) - { z = ground_z; } + z = ground_z; break; } } @@ -1914,9 +1935,9 @@ void WorldObject::GetNearPoint(WorldObject const* searcher, float& x, float& y, if (!sWorld.getConfig(CONFIG_BOOL_DETECT_POS_COLLISION)) { if (searcher) - searcher->UpdateAllowedPositionZ(x, y, z); // update to LOS height if available + searcher->UpdateAllowedPositionZ(x, y, z, GetMap()); // update to LOS height if available else - { UpdateGroundPositionZ(x, y, z); } + UpdateGroundPositionZ(x, y, z); return; } @@ -1942,12 +1963,12 @@ void WorldObject::GetNearPoint(WorldObject const* searcher, float& x, float& y, if (selector.CheckOriginalAngle()) { if (searcher) - searcher->UpdateAllowedPositionZ(x, y, z); // update to LOS height if available + searcher->UpdateAllowedPositionZ(x, y, z, GetMap()); // update to LOS height if available else - { UpdateGroundPositionZ(x, y, z); } + UpdateGroundPositionZ(x, y, z); if (fabs(init_z - z) < dist && IsWithinLOS(x, y, z)) - { return; } + return; first_los_conflict = true; // first point have LOS problems } @@ -1964,12 +1985,12 @@ void WorldObject::GetNearPoint(WorldObject const* searcher, float& x, float& y, z = GetPositionZ(); if (searcher) - searcher->UpdateAllowedPositionZ(x, y, z); // update to LOS height if available + searcher->UpdateAllowedPositionZ(x, y, z, GetMap()); // update to LOS height if available else - { UpdateGroundPositionZ(x, y, z); } + UpdateGroundPositionZ(x, y, z); if (fabs(init_z - z) < dist && IsWithinLOS(x, y, z)) - { return; } + return; } // BAD NEWS: not free pos (or used or have LOS problems) @@ -1980,9 +2001,9 @@ void WorldObject::GetNearPoint(WorldObject const* searcher, float& x, float& y, y = first_y; if (searcher) - searcher->UpdateAllowedPositionZ(x, y, z); // update to LOS height if available + searcher->UpdateAllowedPositionZ(x, y, z, GetMap()); // update to LOS height if available else - { UpdateGroundPositionZ(x, y, z); } + UpdateGroundPositionZ(x, y, z); return; } @@ -1996,12 +2017,12 @@ void WorldObject::GetNearPoint(WorldObject const* searcher, float& x, float& y, z = GetPositionZ(); if (searcher) - searcher->UpdateAllowedPositionZ(x, y, z); // update to LOS height if available + searcher->UpdateAllowedPositionZ(x, y, z, GetMap()); // update to LOS height if available else - { UpdateGroundPositionZ(x, y, z); } + UpdateGroundPositionZ(x, y, z); if (fabs(init_z - z) < dist && IsWithinLOS(x, y, z)) - { return; } + return; } // BAD BAD NEWS: all found pos (free and used) have LOS problem :( @@ -2009,9 +2030,9 @@ void WorldObject::GetNearPoint(WorldObject const* searcher, float& x, float& y, y = first_y; if (searcher) - searcher->UpdateAllowedPositionZ(x, y, z); // update to LOS height if available + searcher->UpdateAllowedPositionZ(x, y, z, GetMap());// update to LOS height if available else - { UpdateGroundPositionZ(x, y, z); } + UpdateGroundPositionZ(x, y, z); } void WorldObject::SetPhaseMask(uint32 newPhaseMask, bool update) diff --git a/src/game/Object/Object.h b/src/game/Object/Object.h index 226c64efd..089071813 100644 --- a/src/game/Object/Object.h +++ b/src/game/Object/Object.h @@ -536,7 +536,7 @@ class WorldObject : public Object bool IsPositionValid() const; void UpdateGroundPositionZ(float x, float y, float& z) const; - void UpdateAllowedPositionZ(float x, float y, float& z) const; + void UpdateAllowedPositionZ(float x, float y, float& z, Map* atMap = nullptr) const; void GetRandomPoint(float x, float y, float z, float distance, float& rand_x, float& rand_y, float& rand_z) const; diff --git a/src/game/Object/ObjectMgr.cpp b/src/game/Object/ObjectMgr.cpp index a7449c454..ed6c76883 100644 --- a/src/game/Object/ObjectMgr.cpp +++ b/src/game/Object/ObjectMgr.cpp @@ -1438,7 +1438,7 @@ void ObjectMgr::LoadCreatures() if (cInfo->RegenerateStats & REGEN_FLAG_HEALTH && data.curhealth < cInfo->MinLevelHealth) { - sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `creature_template`.`RegenHealth`=1 and low current health (%u), `creature_template`.`MinLevelHealth`=%u.", guid, data.id, data.curhealth, cInfo->MinLevelHealth); + sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `creature_template`.`RegenerateStats` & REGEN_FLAG_HEALTH and low current health (%u), `creature_template`.`MinLevelHealth`=%u.", guid, data.id, data.curhealth, cInfo->MinLevelHealth); data.curhealth = cInfo->MinLevelHealth; } @@ -1491,8 +1491,13 @@ void ObjectMgr::LoadCreatures() } if (gameEvent == 0 && GuidPoolId == 0 && EntryPoolId == 0) // if not this is to be managed by GameEvent System or Pool system + { AddCreatureToGrid(guid, &data); + if (cInfo->ExtraFlags & CREATURE_EXTRA_FLAG_ACTIVE) + m_activeCreatures.insert(ActiveCreatureGuidsOnMap::value_type(data.mapid, guid)); + } + ++count; } while (result->NextRow()); @@ -7580,9 +7585,9 @@ void ObjectMgr::LoadCreatureQuestRelations() { CreatureInfo const* cInfo = GetCreatureTemplate(itr->first); if (!cInfo) - sLog.outErrorDb("Table `creature_questrelation` have data for nonexistent creature entry (%u) and existing quest %u", itr->first, itr->second); + sLog.outErrorDb("Table `creature_involvedrelation` have data for nonexistent creature entry (%u) and existing quest %u", itr->first, itr->second); else if (!(cInfo->NpcFlags & UNIT_NPC_FLAG_QUESTGIVER)) - sLog.outErrorDb("Table `creature_questrelation` has creature entry (%u) for quest %u, but NpcFlags does not include UNIT_NPC_FLAG_QUESTGIVER", itr->first, itr->second); + sLog.outErrorDb("Table `creature_involvedrelation` has creature entry (%u) for quest %u, but NpcFlags does not include UNIT_NPC_FLAG_QUESTGIVER", itr->first, itr->second); } } @@ -9400,6 +9405,7 @@ void ObjectMgr::LoadTrainerTemplates() // post loading check std::set trainer_ids; + bool hasErrored = false; for (CacheTrainerSpellMap::const_iterator tItr = m_mCacheTrainerTemplateSpellMap.begin(); tItr != m_mCacheTrainerTemplateSpellMap.end(); ++tItr) trainer_ids.insert(tItr->first); @@ -9408,18 +9414,21 @@ void ObjectMgr::LoadTrainerTemplates() { if (CreatureInfo const* cInfo = sCreatureStorage.LookupEntry(i)) { - if (cInfo->TrainerTemplateId) - { if (m_mCacheTrainerTemplateSpellMap.find(cInfo->TrainerTemplateId) != m_mCacheTrainerTemplateSpellMap.end()) trainer_ids.erase(cInfo->TrainerTemplateId); else - sLog.outErrorDb("Creature (Entry: %u) has trainer_id = %u for nonexistent trainer template", cInfo->Entry, cInfo->TrainerTemplateId); - } + { + sLog.outErrorDb("Creature (Entry: %u) has TrainerTemplateId = %u for nonexistent trainer template", cInfo->Entry, cInfo->TrainerTemplateId); + hasErrored = true; + } } } for (std::set::const_iterator tItr = trainer_ids.begin(); tItr != trainer_ids.end(); ++tItr) sLog.outErrorDb("Table `npc_trainer_template` has trainer template %u not used by any trainers ", *tItr); + + if (hasErrored || !trainer_ids.empty()) // Append extra line in case of reported errors + sLog.outString(); } void ObjectMgr::LoadVendors(char const* tableName, bool isTemplates) @@ -9497,7 +9506,7 @@ void ObjectMgr::LoadVendorTemplates() if (m_mCacheVendorTemplateItemMap.find(cInfo->VendorTemplateId) != m_mCacheVendorTemplateItemMap.end()) vendor_ids.erase(cInfo->VendorTemplateId); else - sLog.outErrorDb("Creature (Entry: %u) has vendor_id = %u for nonexistent vendor template", cInfo->Entry, cInfo->VendorTemplateId); + sLog.outErrorDb("Creature (Entry: %u) has VendorTemplateId = %u for nonexistent vendor template", cInfo->Entry, cInfo->VendorTemplateId); } } } @@ -9506,6 +9515,56 @@ void ObjectMgr::LoadVendorTemplates() sLog.outErrorDb("Table `npc_vendor_template` has vendor template %u not used by any vendors ", *vItr); } +/* This function is supposed to take care of three things: + * 1) Load Transports on Map or on Continents + * 2) Load Active Npcs on Map or Continents + * 3) Load Everything dependend on config setting LoadAllGridsOnMaps + * + * This function is currently WIP, hence parts exist only as draft. + */ +void ObjectMgr::LoadActiveEntities(Map* _map) +{ + // Special case on startup - load continents + if (!_map) + { + uint32 continents[] = {0, 1, 530, 571}; + for (int i = 0; i < countof(continents); ++i) + { + _map = sMapMgr.FindMap(continents[i]); + if (!_map) + _map = sMapMgr.CreateMap(continents[i], nullptr); + + if (_map) + LoadActiveEntities(_map); + else + sLog.outError("ObjectMgr::LoadActiveEntities - Unable to create Map %u", continents[i]); + } + + return; + } + + // Load active objects for _map + if (sWorld.isForceLoadMap(_map->GetId())) + { + for (CreatureDataMap::const_iterator itr = mCreatureDataMap.begin(); itr != mCreatureDataMap.end(); ++itr) + { + if (itr->second.mapid == _map->GetId()) + _map->ForceLoadGrid(itr->second.posX, itr->second.posY); + } + } + else // Normal case - Load all npcs that are active + { + std::pair bounds = m_activeCreatures.equal_range(_map->GetId()); + for (ActiveCreatureGuidsOnMap::const_iterator itr = bounds.first; itr != bounds.second; ++itr) + { + CreatureData const& data = mCreatureDataMap[itr->second]; + _map->ForceLoadGrid(data.posX, data.posY); + } + } + + // Load Transports on Map _map +} + void ObjectMgr::LoadNpcGossips() { m_mCacheNpcTextIdMap.clear(); @@ -9568,11 +9627,9 @@ void ObjectMgr::LoadGossipMenu(std::set& gossipScriptSet) if (!result) { BarGoLink bar(1); - bar.step(); - - sLog.outString(); sLog.outErrorDb(">> Loaded gossip_menu, table is empty!"); + sLog.outString(); return; } @@ -9631,20 +9688,25 @@ void ObjectMgr::LoadGossipMenu(std::set& gossipScriptSet) delete result; - sLog.outString(); - sLog.outString(">> Loaded %u gossip_menu entries", count); - // post loading tests for (uint32 i = 1; i < sCreatureStorage.GetMaxEntry(); ++i) + { if (CreatureInfo const* cInfo = sCreatureStorage.LookupEntry(i)) if (cInfo->GossipMenuId) if (m_mGossipMenusMap.find(cInfo->GossipMenuId) == m_mGossipMenusMap.end()) - sLog.outErrorDb("Creature (Entry: %u) has gossip_menu_id = %u for nonexistent menu", cInfo->Entry, cInfo->GossipMenuId); + sLog.outErrorDb("Creature (Entry: %u) has GossipMenuId = %u for nonexistent menu", cInfo->Entry, cInfo->GossipMenuId); + } - for (SQLStorageBase::SQLSIterator itr = sGOStorage.getDataBegin(); itr < sGOStorage.getDataEnd(); ++itr) - if (uint32 menuid = itr->GetGossipMenuId()) - if (m_mGossipMenusMap.find(menuid) == m_mGossipMenusMap.end()) - ERROR_DB_STRICT_LOG("Gameobject (Entry: %u) has gossip_menu_id = %u for nonexistent menu", itr->id, menuid); + if (!sLog.HasLogFilter(LOG_FILTER_DB_STRICTED_CHECK)) + { + for (SQLStorageBase::SQLSIterator itr = sGOStorage.getDataBegin(); itr < sGOStorage.getDataEnd(); ++itr) + if (uint32 menuid = itr->GetGossipMenuId()) + if (m_mGossipMenusMap.find(menuid) == m_mGossipMenusMap.end()) + sLog.outErrorDb("Gameobject (Entry: %u) has gossip_menu_id = %u for nonexistent menu", itr->id, menuid); + } + + sLog.outString(">> Loaded %u gossip_menu entries", count); + sLog.outString(); } void ObjectMgr::LoadGossipMenuItems(std::set& gossipScriptSet) @@ -9660,11 +9722,9 @@ void ObjectMgr::LoadGossipMenuItems(std::set& gossipScriptSet) if (!result) { BarGoLink bar(1); - bar.step(); - - sLog.outString(); sLog.outErrorDb(">> Loaded gossip_menu_option, table is empty!"); + sLog.outString(); return; } @@ -9770,7 +9830,7 @@ void ObjectMgr::LoadGossipMenuItems(std::set& gossipScriptSet) } if (found_menu_uses && !found_flags_uses) - sLog.outErrorDb("Table gossip_menu_option for menu %u, id %u has `npc_option_NpcFlags` = %u but creatures using this menu does not have corresponding`NpcFlags`. Option will not accessible in game.", gMenuItem.menu_id, gMenuItem.id, gMenuItem.npc_option_NpcFlags); + sLog.outErrorDb("Table gossip_menu_option for menu %u, id %u has `npc_option_npcflag` = %u but creatures using this menu does not have corresponding `NpcFlags`. Option will not accessible in game.", gMenuItem.menu_id, gMenuItem.id, gMenuItem.npc_option_NpcFlags); } if (gMenuItem.action_poi_id && !GetPointOfInterest(gMenuItem.action_poi_id)) @@ -9804,7 +9864,6 @@ void ObjectMgr::LoadGossipMenuItems(std::set& gossipScriptSet) m_mGossipMenuItemsMap.insert(GossipMenuItemsMap::value_type(gMenuItem.menu_id, gMenuItem)); ++count; - } while (result->NextRow()); @@ -9816,8 +9875,8 @@ void ObjectMgr::LoadGossipMenuItems(std::set& gossipScriptSet) sLog.outErrorDb("Table `gossip_menu` contain unused (in creature or GO or menu options) menu id %u.", *itr); } - sLog.outString(); sLog.outString(">> Loaded %u gossip_menu_option entries", count); + sLog.outString(); } void ObjectMgr::LoadGossipMenus() diff --git a/src/game/Object/ObjectMgr.h b/src/game/Object/ObjectMgr.h index 257c3262d..911c5a024 100644 --- a/src/game/Object/ObjectMgr.h +++ b/src/game/Object/ObjectMgr.h @@ -831,6 +831,9 @@ class ObjectMgr void LoadTrainerTemplates(); void LoadTrainers() { LoadTrainers("npc_trainer", false); } + /// @param _map Map* of the map for which to load active entities. If nullptr active entities on continents are loaded + void LoadActiveEntities(Map* _map); + void LoadVehicleAccessory(); std::string GeneratePetName(uint32 entry); @@ -1340,10 +1343,13 @@ class ObjectMgr HalfNameMap PetHalfName0; HalfNameMap PetHalfName1; + typedef std::multimap ActiveCreatureGuidsOnMap; + // Array to store creature stats, Max creature level + 1 (for data alignement with in game level) CreatureClassLvlStats m_creatureClassLvlStats[DEFAULT_MAX_CREATURE_LEVEL + 1][MAX_CREATURE_CLASS][MAX_EXPANSION + 1]; MapObjectGuids mMapObjectGuids; + ActiveCreatureGuidsOnMap m_activeCreatures; CreatureDataMap mCreatureDataMap; CreatureLocaleMap mCreatureLocaleMap; GameObjectDataMap mGameObjectDataMap; diff --git a/src/game/Object/Pet.cpp b/src/game/Object/Pet.cpp index da2853338..5e15fd4fc 100644 --- a/src/game/Object/Pet.cpp +++ b/src/game/Object/Pet.cpp @@ -504,6 +504,7 @@ void Pet::SetDeathState(DeathState s) // overwrite virtual RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); CastPetAuras(true); } + CastOwnerTalentAuras(); } void Pet::Update(uint32 update_diff, uint32 diff) @@ -2091,6 +2092,40 @@ void Pet::SynchronizeLevelWithOwner() } } +void Pet::SetModeFlags(PetModeFlags mode) +{ + m_petModeFlags = mode; + + Unit* owner = GetOwner(); + if (!owner || owner->GetTypeId() != TYPEID_PLAYER) + return; + + WorldPacket data(SMSG_PET_MODE, 12); + data << GetObjectGuid(); + data << uint32(m_petModeFlags); + ((Player*)owner)->GetSession()->SendPacket(&data); +} + +void Pet::SetStayPosition(bool stay) +{ + if (stay) + { + m_stayPosX = GetPositionX(); + m_stayPosY = GetPositionY(); + m_stayPosZ = GetPositionZ(); + m_stayPosO = GetOrientation(); + } + else + { + m_stayPosX = 0; + m_stayPosY = 0; + m_stayPosZ = 0; + m_stayPosO = 0; + } + + m_stayPosSet = stay; +} + void Pet::ApplyModeFlags(PetModeFlags mode, bool apply) { if (apply) diff --git a/src/game/Object/Pet.h b/src/game/Object/Pet.h index 033b525ed..7265b1f66 100644 --- a/src/game/Object/Pet.h +++ b/src/game/Object/Pet.h @@ -206,6 +206,7 @@ class Pet : public Creature bool CanTakeMoreActiveSpells(uint32 SpellIconID); void ToggleAutocast(uint32 spellid, bool apply); + void SetModeFlags(PetModeFlags mode); void ApplyModeFlags(PetModeFlags mode, bool apply); PetModeFlags GetModeFlags() const { return m_petModeFlags; } @@ -231,6 +232,25 @@ class Pet : public Creature bool removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true); void CleanupActionBar(); + bool m_retreating; + + void SetIsRetreating(bool retreating = false) { m_retreating = retreating; } + bool GetIsRetreating() { return m_retreating; } + + bool m_stayPosSet; + float m_stayPosX; + float m_stayPosY; + float m_stayPosZ; + float m_stayPosO; + + void SetStayPosition(bool stay = false); + bool IsStayPosSet() { return m_stayPosSet; } + + float GetStayPosX() { return m_stayPosX; } + float GetStayPosY() { return m_stayPosY; } + float GetStayPosZ() { return m_stayPosZ; } + float GetStayPosO() { return m_stayPosO; } + PetSpellMap m_spells; AutoSpellList m_autospells; diff --git a/src/game/Object/PetAI.cpp b/src/game/Object/PetAI.cpp index 3e92e29d1..541f9ea3e 100644 --- a/src/game/Object/PetAI.cpp +++ b/src/game/Object/PetAI.cpp @@ -60,7 +60,7 @@ void PetAI::MoveInLineOfSight(Unit* u) return; if (u->IsTargetableForAttack() && m_creature->IsHostileTo(u) && - u->isInAccessablePlaceFor(m_creature)) + u->IsInAccessablePlaceFor(m_creature)) { float attackRadius = m_creature->GetAttackDistance(u); if (m_creature->IsWithinDistInMap(u, attackRadius) && m_creature->GetDistanceZ(u) <= CREATURE_Z_ATTACK_RANGE) diff --git a/src/game/Object/Player.cpp b/src/game/Object/Player.cpp index 34227e026..1478072f2 100644 --- a/src/game/Object/Player.cpp +++ b/src/game/Object/Player.cpp @@ -346,6 +346,12 @@ void TradeData::SetMoney(uint64 money) if (m_money == money) return; + if (money > m_player->GetMoney()) + { + m_player->GetSession()->SendTradeStatus(TRADE_STATUS_CLOSE_WINDOW); + return; + } + m_money = money; SetAccepted(false); @@ -1264,19 +1270,17 @@ void Player::Update(uint32 update_diff, uint32 p_time) RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); } } - } + }// Speed collect rest bonus (section/in hour) if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING)) { - if (roll_chance_i(3) && GetTimeInnEnter() > 0) // Freeze update + if (GetTimeInnEnter() > 0) // Freeze update { - time_t time_inn = time(NULL) - GetTimeInnEnter(); + time_t time_inn = now - GetTimeInnEnter(); if (time_inn >= 10) // Freeze update { - float bubble = 0.125f * sWorld.getConfig(CONFIG_FLOAT_RATE_REST_INGAME); - // Speed collect rest bonus (section/in hour) - SetRestBonus(float(GetRestBonus() + time_inn * (GetUInt32Value(PLAYER_NEXT_LEVEL_XP) / 72000) * bubble)); - UpdateInnerTime(time(NULL)); + SetRestBonus(GetRestBonus() + ComputeRest(time_inn)); + UpdateInnerTime(now); } } } @@ -2888,7 +2892,7 @@ void Player::InitStatsForLevel(bool reapplyMods) RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK | PLAYER_FLAGS_DND | PLAYER_FLAGS_GM | PLAYER_FLAGS_GHOST); RemoveStandFlags(UNIT_STAND_FLAGS_ALL); // one form stealth modified bytes - RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP | UNIT_BYTE2_FLAG_SANCTUARY); + RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP | UNIT_BYTE2_FLAG_SUPPORTABLE); // restore if need some important flags SetUInt32Value(PLAYER_FIELD_BYTES2, 0); // flags empty by default @@ -4147,14 +4151,14 @@ void Player::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) c { for (int i = 0; i < EQUIPMENT_SLOT_END; ++i) { - if (m_items[i] == NULL) + if (m_items[i] == nullptr) continue; m_items[i]->BuildCreateUpdateBlockForPlayer(data, target); } for (int i = INVENTORY_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i) { - if (m_items[i] == NULL) + if (m_items[i] == nullptr) continue; m_items[i]->BuildCreateUpdateBlockForPlayer(data, target); @@ -4555,6 +4559,38 @@ void Player::SetWaterWalk(bool enable) GetSession()->SendPacket(&data); } +void Player::SetLevitate(bool enable) +{ + WorldPacket data; + BuildMoveLevitatePacket(&data, enable, 0); + GetSession()->SendPacket(&data); +} + +void Player::SetCanFly(bool enable) +{ + WorldPacket data; + BuildMoveSetCanFlyPacket(&data, enable, 0); + GetSession()->SendPacket(&data); +} + +void Player::SetFeatherFall(bool enable) +{ + WorldPacket data; + BuildMoveFeatherFallPacket(&data, enable, 0); + SendMessageToSet(&data, true); + + // start fall from current height + if (!enable) + SetFallInformation(0, GetPositionZ()); +} + +void Player::SetHover(bool enable) +{ + WorldPacket data; + BuildMoveHoverPacket(&data, enable, 0); + GetSession()->SendPacket(&data); +} + /* Preconditions: - a resurrectable corpse must not be loaded for the player (only bones) - the player must be in world @@ -6184,9 +6220,6 @@ bool Player::SetPosition(float x, float y, float z, float orientation, bool tele // group update if (GetGroup() && (old_x != x || old_y != y)) SetGroupUpdateFlag(GROUP_UPDATE_FLAG_POSITION); - - if (GetTrader() && !IsWithinDistInMap(GetTrader(), INTERACTION_DISTANCE)) - GetSession()->SendCancelTrade(); // will close both side trade windows } if (m_positionStatusUpdateTimer) // Update position's state only on interval @@ -6966,13 +6999,13 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea) if (zone->flags & AREA_FLAG_SANCTUARY) // in sanctuary { - SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY); + SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SUPPORTABLE); if (sWorld.IsFFAPvPRealm()) SetFFAPvP(false); } else { - RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY); + RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SUPPORTABLE); } if (zone->flags & AREA_FLAG_CAPITAL) // in capital city @@ -7483,9 +7516,9 @@ void Player::ApplyItemEquipSpell(Item* item, bool apply, bool form_change) else { // at un-apply remove all spells (not only at-apply, so any at-use active affects from item and etc) - // except with at-use with negative charges, so allow consuming item spells (including with extra flag that prevent consume really) + // except on form change and with at-use with negative charges, so allow consuming item spells (including with extra flag that prevent consume really) // applied to player after item remove from equip slot - if (spellData.SpellTrigger == ITEM_SPELLTRIGGER_ON_USE && spellData.SpellCharges < 0) + if (spellData.SpellTrigger == ITEM_SPELLTRIGGER_ON_USE && (form_change || spellData.SpellCharges < 0)) continue; } @@ -14004,6 +14037,10 @@ bool Player::SatisfyQuestPreviousQuest(Quest const* qInfo, bool msg) const return true; // each-from-all exclusive group ( < 0) + // given a group with 2+ quests, and one of those has a branch that is not restricted by the group, return true + if (qInfo->GetPrevQuestId() != 0 && qPrevInfo->GetNextQuestId() != qInfo->GetPrevQuestId()) + return true; + // can be start if only all quests in prev quest exclusive group completed and rewarded ExclusiveQuestGroupsMapBounds bounds = sObjectMgr.GetExclusiveQuestGroupsMapBounds(qPrevInfo->GetExclusiveGroup()); @@ -14037,6 +14074,11 @@ bool Player::SatisfyQuestPreviousQuest(Quest const* qInfo, bool msg) const if (qPrevInfo->GetExclusiveGroup() >= 0) return true; + // each-from-all exclusive group ( < 0) + // given a group with 2+ quests, and one of those has a branch that is not restricted by the group, return true + if (qInfo->GetPrevQuestId() != 0 && qPrevInfo->GetNextQuestId() != abs(qInfo->GetPrevQuestId())) + return true; + // each-from-all exclusive group ( < 0) // can be start if only all quests in prev quest exclusive group active ExclusiveQuestGroupsMapBounds bounds = sObjectMgr.GetExclusiveQuestGroupsMapBounds(qPrevInfo->GetExclusiveGroup()); @@ -15494,7 +15536,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SqlQueryHolder* holder) m_bgData.bgTypeID = currentBg->GetTypeID(); // bg data not marked as modified // join player to battleground group - currentBg->EventPlayerLoggedIn(this, GetObjectGuid()); + currentBg->EventPlayerLoggedIn(this); currentBg->AddOrSetPlayerToCorrectBgGroup(this, GetObjectGuid(), m_bgData.bgTeam); SetInviteForBattleGroundQueueType(bgQueueTypeId, currentBg->GetInstanceID()); @@ -15722,17 +15764,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SqlQueryHolder* holder) m_rest_bonus = fields[21].GetFloat(); if (time_diff > 0) - { - // speed collect rest bonus in offline, in logout, far from tavern, city (section/in hour) - float bubble0 = 0.031f; - // speed collect rest bonus in offline, in logout, in tavern, city (section/in hour) - float bubble1 = 0.125f; - float bubble = fields[23].GetUInt32() > 0 - ? bubble1 * sWorld.getConfig(CONFIG_FLOAT_RATE_REST_OFFLINE_IN_TAVERN_OR_CITY) - : bubble0 * sWorld.getConfig(CONFIG_FLOAT_RATE_REST_OFFLINE_IN_WILDERNESS); - - SetRestBonus(GetRestBonus() + time_diff * ((float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP) / 72000)*bubble); - } + SetRestBonus(GetRestBonus() + ComputeRest(time_diff, true, (fields[23].GetInt32() > 0))); // load skills after InitStatsForLevel because it triggering aura apply also _LoadSkills(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSKILLS)); @@ -15933,6 +15965,43 @@ bool Player::LoadFromDB(ObjectGuid guid, SqlQueryHolder* holder) return true; } +bool Player::IsTappedByMeOrMyGroup(Creature* creature) +{ + /* Nobody tapped the monster (solo kill by another NPC) */ + if (!creature->HasFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TAPPED)) + { return false; } + + /* If there is a loot recipient, assign it to recipient */ + if (Player* recipient = creature->GetLootRecipient()) + { + /* See if we're in a group */ + if (Group* plr_group = recipient->GetGroup()) + { + /* Recipient is in a group... but is it ours? */ + if (Group* my_group = GetGroup()) + { + /* Check groups are the same */ + if (plr_group != my_group) + { return false; } // Cheater, deny loot + } + else + { return false; } // We're not in a group, probably cheater + + /* We're in the looters group, so mob is tapped by us */ + return true; + } + /* We're not in a group, check to make sure we're the recipient (prevent cheaters) */ + else if (recipient == this) + { return true; } + } + else + /* Don't know what happened to the recipient, probably disconnected + * Either way, it isn't us, so mark as tapped */ + { return false; } + + return false; +} + bool Player::isAllowedToLoot(Creature* creature) { // never tapped by any (mob solo kill) @@ -21716,6 +21785,123 @@ void Player::SetTitle(CharTitlesEntry const* title, bool lost) GetSession()->SendPacket(&data); } +void Player::UpdateRuneRegen(RuneType rune) +{ + if (rune >= RUNE_DEATH) + return; + + RuneType actualRune = rune; + float cooldown = RUNE_BASE_COOLDOWN; + for (uint8 i = 0; i < MAX_RUNES; i += 2) + { + if (GetBaseRune(i) != rune) + continue; + + uint32 cd = GetRuneCooldown(i); + uint32 secondRuneCd = GetRuneCooldown(i + 1); + if (!cd && !secondRuneCd) + actualRune = GetCurrentRune(i); + else if (secondRuneCd && (cd > secondRuneCd || !cd)) + { + cooldown = GetBaseRuneCooldown(i + 1); + actualRune = GetCurrentRune(i + 1); + } + else + { + cooldown = GetBaseRuneCooldown(i); + actualRune = GetCurrentRune(i); + } + + break; + } + + float auraMod = 1.0f; + Unit::AuraList const& regenAuras = GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT); + for (Unit::AuraList::const_iterator i = regenAuras.begin(); i != regenAuras.end(); ++i) + if ((*i)->GetMiscValue() == POWER_RUNE && (*i)->GetSpellEffect()->EffectMiscValueB == rune) + auraMod *= (100.0f + (*i)->GetModifier()->m_amount) / 100.0f; + + // Unholy Presence + if (Aura* aura = GetAura(48265, EFFECT_INDEX_0)) + auraMod *= (100.0f + aura->GetModifier()->m_amount) / 100.0f; + + // Runic Corruption + if (Aura* aura = GetAura(51460, EFFECT_INDEX_0)) + auraMod *= (100.0f + aura->GetModifier()->m_amount) / 100.0f; + + float hastePct = (100.0f - GetRatingBonusValue(CR_HASTE_MELEE)) / 100.0f; + if (hastePct < 0) + hastePct = 1.0f; + + cooldown *= hastePct / auraMod; + + float value = float(1 * IN_MILLISECONDS) / cooldown; + SetFloatValue(PLAYER_RUNE_REGEN_1 + uint8(actualRune), value); +} + +void Player::UpdateRuneRegen() +{ + for (uint8 i = 0; i < NUM_RUNE_TYPES; ++i) + UpdateRuneRegen(RuneType(i)); +} + +uint8 Player::GetRuneCooldownFraction(uint8 index) const +{ + uint16 baseCd = GetBaseRuneCooldown(index); + if (!baseCd || !GetRuneCooldown(index)) + return 255; + else if (baseCd == GetRuneCooldown(index)) + return 0; + + return uint8(float(baseCd - GetRuneCooldown(index)) / baseCd * 255); +} + +void Player::AddRuneByAuraEffect(uint8 index, RuneType newType, Aura const* aura) +{ + // Item - Death Knight T11 DPS 4P Bonus + if (newType == RUNE_DEATH && HasAura(90459)) + CastSpell(this, 90507, true); // Death Eater + + SetRuneConvertAura(index, aura); ConvertRune(index, newType); +} + +void Player::RemoveRunesByAuraEffect(Aura const* aura) +{ + for (uint8 i = 0; i < MAX_RUNES; ++i) + { + if (m_runes->runes[i].ConvertAura == aura) + { + ConvertRune(i, GetBaseRune(i)); + SetRuneConvertAura(i, NULL); + } + } +} + +void Player::RestoreBaseRune(uint8 index) +{ + Aura const* aura = m_runes->runes[index].ConvertAura; + // If rune was converted by a non-pasive aura that still active we should keep it converted + if (aura && !IsPassiveSpell(aura->GetSpellProto())) + return; + + // Blood of the North + if (aura->GetId() == 54637 && HasAura(54637)) + return; + + ConvertRune(index, GetBaseRune(index)); + SetRuneConvertAura(index, NULL); + // Don't drop passive talents providing rune convertion + if (!aura || aura->GetModifier()->m_auraname != SPELL_AURA_CONVERT_RUNE) + return; + + for (uint8 i = 0; i < MAX_RUNES; ++i) + if (aura == m_runes->runes[i].ConvertAura) + return; + + if (Unit* target = aura->GetTarget()) + target->RemoveSpellAuraHolder(const_cast(aura)->GetHolder()); +} + void Player::ConvertRune(uint8 index, RuneType newType) { SetCurrentRune(index, newType); @@ -21749,7 +21935,7 @@ void Player::ResyncRunes() for (uint32 i = 0; i < MAX_RUNES; ++i) { data << uint8(GetCurrentRune(i)); // rune type - data << uint8(255 - (GetRuneCooldown(i) * 51)); // passed cooldown time (0-255) + data << uint8(GetRuneCooldownFraction(i)); } GetSession()->SendPacket(&data); } @@ -21761,16 +21947,6 @@ void Player::AddRunePower(uint8 index) GetSession()->SendPacket(&data); } -static RuneType runeSlotTypes[MAX_RUNES] = -{ - /*0*/ RUNE_BLOOD, - /*1*/ RUNE_BLOOD, - /*2*/ RUNE_UNHOLY, - /*3*/ RUNE_UNHOLY, - /*4*/ RUNE_FROST, - /*5*/ RUNE_FROST -}; - void Player::InitRunes() { if (getClass() != CLASS_DEATH_KNIGHT) @@ -24066,6 +24242,77 @@ bool Player::FitArmorSpecializationRules(SpellEntry const * spellProto) const return true; } +float Player::ComputeRest(time_t timePassed, bool offline /*= false*/, bool inRestPlace /*= false*/) +{ + // Every 8h in resting zone we gain a bubble + // A bubble is 5% of the total xp so there is 20 bubbles + // So we gain (total XP/20 every 8h) (8h = 288800 sec) + // (TotalXP/20)/28800; simplified to (TotalXP/576000) per second + // Client automatically double the value sent so we have to divide it by 2 + // So final formula (TotalXP/1152000) + float bonus = timePassed * (GetUInt32Value(PLAYER_NEXT_LEVEL_XP) / 1152000.0f); // Get the gained rest xp for given second + if (!offline) + bonus *= sWorld.getConfig(CONFIG_FLOAT_RATE_REST_INGAME); // Apply the custom setting + else + { + if (inRestPlace) + bonus *= sWorld.getConfig(CONFIG_FLOAT_RATE_REST_OFFLINE_IN_TAVERN_OR_CITY); + else + bonus *= sWorld.getConfig(CONFIG_FLOAT_RATE_REST_OFFLINE_IN_WILDERNESS) / 4.0f; // bonus is reduced by 4 when not in rest place + } + return bonus; +} + +float Player::GetCollisionHeight(bool mounted) const +{ + if (mounted) + { + // mounted case + CreatureDisplayInfoEntry const* mountDisplayInfo = sCreatureDisplayInfoStore.LookupEntry(GetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID)); + if (!mountDisplayInfo) + return GetCollisionHeight(false); + + CreatureModelDataEntry const* mountModelData = sCreatureModelDataStore.LookupEntry(mountDisplayInfo->ModelId); + if (!mountModelData) + return GetCollisionHeight(false); + + CreatureDisplayInfoEntry const* displayInfo = sCreatureDisplayInfoStore.LookupEntry(GetNativeDisplayId()); + if (!displayInfo) + { + sLog.outError("GetCollisionHeight::Unable to find CreatureDisplayInfoEntry for %u", GetNativeDisplayId()); + return 0; + } + CreatureModelDataEntry const* modelData = sCreatureModelDataStore.LookupEntry(displayInfo->ModelId); + if (!modelData) + { + sLog.outError("GetCollisionHeight::Unable to find CreatureModelDataEntry for %u", displayInfo->ModelId); + return 0; + } + + float scaleMod = GetObjectScale(); // 99% sure about this + + return scaleMod * mountModelData->MountHeight + modelData->CollisionHeight * 0.5f; + } + else + { + // use native model collision height in dismounted case + CreatureDisplayInfoEntry const* displayInfo = sCreatureDisplayInfoStore.LookupEntry(GetNativeDisplayId()); + if (!displayInfo) + { + sLog.outError("GetCollisionHeight::Unable to find CreatureDisplayInfoEntry for %u", GetNativeDisplayId()); + return 0; + } + CreatureModelDataEntry const* modelData = sCreatureModelDataStore.LookupEntry(displayInfo->ModelId); + if (!modelData) + { + sLog.outError("GetCollisionHeight::Unable to find CreatureModelDataEntry for %u", displayInfo->ModelId); + return 0; + } + + return modelData->CollisionHeight; + } +} + void Player::SendPetitionSignResult(ObjectGuid petitionGuid, Player* player, uint32 result) { WorldPacket data(SMSG_PETITION_SIGN_RESULTS, 8 + 8 + 4); diff --git a/src/game/Object/Player.h b/src/game/Object/Player.h index 5004be09a..0b5fa81fc 100644 --- a/src/game/Object/Player.h +++ b/src/game/Object/Player.h @@ -353,7 +353,12 @@ struct Areas }; #define MAX_RUNES 6 -#define RUNE_COOLDOWN (2*5*IN_MILLISECONDS) // msec + +enum RuneCooldowns +{ + RUNE_BASE_COOLDOWN = 10000, + RUNE_MISS_COOLDOWN = 1500 // cooldown applied on runes when the spell misses +}; enum RuneType { @@ -364,17 +369,30 @@ enum RuneType NUM_RUNE_TYPES = 4 }; +static RuneType runeSlotTypes[MAX_RUNES] = +{ + /*0*/ RUNE_BLOOD, + /*1*/ RUNE_BLOOD, + /*2*/ RUNE_UNHOLY, + /*3*/ RUNE_UNHOLY, + /*4*/ RUNE_FROST, + /*5*/ RUNE_FROST +}; + struct RuneInfo { uint8 BaseRune; uint8 CurrentRune; + uint16 BaseCooldown; uint16 Cooldown; // msec + Aura const* ConvertAura; }; struct Runes { RuneInfo runes[MAX_RUNES]; uint8 runeState; // mask of available runes + uint32 lastUsedRuneMask; void SetRuneState(uint8 index, bool set = true) { @@ -1191,6 +1209,15 @@ class Player : public Unit } void SetRestBonus(float rest_bonus_new); + /** + * \brief: compute rest bonus + * \param: time_t timePassed > time from last check + * \param: bool offline > is the player was offline? + * \param: bool inRestPlace > if it was offline, is the player was in city/tavern/inn? + * \returns: float + **/ + float ComputeRest(time_t timePassed, bool offline = false, bool inRestPlace = false); + RestType GetRestType() const { return rest_type; @@ -1539,6 +1566,9 @@ class Player : public Unit void AddTimedQuest(uint32 quest_id) { m_timedquests.insert(quest_id); } void RemoveTimedQuest(uint32 quest_id) { m_timedquests.erase(quest_id); } + //! Return collision height sent to client + float GetCollisionHeight(bool mounted) const; + /*********************************************************/ /*** LOAD SYSTEM ***/ /*********************************************************/ @@ -2058,6 +2088,10 @@ class Player : public Unit StopMirrorTimer(FIRE_TIMER); } + void SetLevitate(bool enable) override; + void SetCanFly(bool enable) override; + void SetFeatherFall(bool enable) override; + void SetHover(bool enable) override; void SetRoot(bool enable) override; void SetWaterWalk(bool enable) override; @@ -2371,6 +2405,7 @@ class Player : public Unit bool isMoving() const { return m_movementInfo.HasMovementFlag(movementFlagsMask); } bool isMovingOrTurning() const { return m_movementInfo.HasMovementFlag(movementOrTurningFlagsMask); } + bool CanSwim() const { return true; } bool CanFly() const { return m_movementInfo.HasMovementFlag(MOVEFLAG_CAN_FLY); } bool IsFlying() const { return m_movementInfo.HasMovementFlag(MOVEFLAG_FLYING); } bool IsFreeFlying() const { return HasAuraType(SPELL_AURA_MOD_FLIGHT_SPEED_MOUNTED) || HasAuraType(SPELL_AURA_FLY); } @@ -2498,7 +2533,8 @@ class Player : public Unit GridReference& GetGridRef() { return m_gridRef; } MapReference& GetMapRef() { return m_mapRef; } - + + bool IsTappedByMeOrMyGroup(Creature* creature); bool isAllowedToLoot(Creature* creature); DeclinedName const* GetDeclinedNames() const { return m_declinedname; } @@ -2508,10 +2544,22 @@ class Player : public Unit RuneType GetBaseRune(uint8 index) const { return RuneType(m_runes->runes[index].BaseRune); } RuneType GetCurrentRune(uint8 index) const { return RuneType(m_runes->runes[index].CurrentRune); } uint16 GetRuneCooldown(uint8 index) const { return m_runes->runes[index].Cooldown; } + uint16 GetBaseRuneCooldown(uint8 index) const { return m_runes->runes[index].BaseCooldown; } + uint8 GetRuneCooldownFraction(uint8 index) const; + void UpdateRuneRegen(RuneType rune); + void UpdateRuneRegen(); bool IsBaseRuneSlotsOnCooldown(RuneType runeType) const; + void ClearLastUsedRuneMask() { m_runes->lastUsedRuneMask = 0; } + bool IsLastUsedRune(uint8 index) const { return (m_runes->lastUsedRuneMask & (1 << index)) != 0; } + void SetLastUsedRune(RuneType type) { m_runes->lastUsedRuneMask |= 1 << uint32(type); } 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 SetBaseRuneCooldown(uint8 index, uint16 cooldown) { m_runes->runes[index].BaseCooldown = cooldown; } + void SetRuneConvertAura(uint8 index, Aura const* aura) { m_runes->runes[index].ConvertAura = aura; } + void AddRuneByAuraEffect(uint8 index, RuneType newType, Aura const* aura); + void RemoveRunesByAuraEffect(Aura const* aura); + void RestoreBaseRune(uint8 index); void ConvertRune(uint8 index, RuneType newType); bool ActivateRunes(RuneType type, uint32 count); void ResyncRunes(); diff --git a/src/game/Object/SpellMgr.cpp b/src/game/Object/SpellMgr.cpp index ee1a8fba2..96c93636c 100644 --- a/src/game/Object/SpellMgr.cpp +++ b/src/game/Object/SpellMgr.cpp @@ -811,6 +811,7 @@ bool IsPositiveEffect(SpellEntry const* spellproto, SpellEffectIndex effIndex) case 13139: // net-o-matic special effect case 23445: // evil twin case 35679: // Protectorate Demolitionist + case 37695: // Stanky case 38637: // Nether Exhaustion (red) case 38638: // Nether Exhaustion (green) case 38639: // Nether Exhaustion (blue) @@ -843,6 +844,7 @@ bool IsPositiveEffect(SpellEntry const* spellproto, SpellEffectIndex effIndex) return false; break; case SPELL_AURA_MOD_DAMAGE_TAKEN: // dependent from bas point sign (positive -> negative) + case SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN: if (spellEffect->CalculateSimpleValue() < 0) return true; // let check by target modes (for Amplify Magic cases/etc) @@ -1406,8 +1408,8 @@ struct DoSpellProcEvent else ++count; } - - bool HasEntry(uint32 spellId) { return spe_map.count(spellId) > 0; } + + bool HasEntry(uint32 spellId) { return spe_map.find(spellId) != spe_map.end(); } bool SetStateToEntry(uint32 spellId) { return (state = spe_map.find(spellId)) != spe_map.end(); } SpellProcEventMap& spe_map; SpellProcEventMap::const_iterator state; @@ -1694,7 +1696,7 @@ void SpellMgr::LoadSpellBonuses() break; } } - direct_calc = CalculateDefaultCoefficient(spell, SPELL_DIRECT_DAMAGE) * (isHeal ? 1.88f : 1.0f); + direct_calc = CalculateDefaultCoefficient(spell, SPELL_DIRECT_DAMAGE) * (isHeal ? SCALE_SPELLPOWER_HEALING : 1.0f); direct_diff = std::abs(sbe.direct_damage - direct_calc); } @@ -1716,7 +1718,7 @@ void SpellMgr::LoadSpellBonuses() break; } } - dot_calc = CalculateDefaultCoefficient(spell, DOT) * (isHeal ? 1.88f : 1.0f); + dot_calc = CalculateDefaultCoefficient(spell, DOT) * (isHeal ? SCALE_SPELLPOWER_HEALING : 1.0f); dot_diff = std::abs(sbe.dot_damage - dot_calc); } @@ -2874,7 +2876,7 @@ SpellEntry const* SpellMgr::SelectAuraRankForLevel(SpellEntry const* spellInfo, break; // if found appropriate level - if (level + 10 >= spellInfo->GetSpellLevel()) + if (level + 10 >= nextSpellInfo->GetSpellLevel()) return nextSpellInfo; // one rank less then @@ -3562,7 +3564,7 @@ void SpellMgr::LoadSpellScriptTarget() { if (itr->spellId == 30427 && !cInfo->SkinningLootId) { - sLog.outErrorDb("Table `spell_script_target` has creature %u as a target of spellid 30427, but this creature has no skinLootid. Gas extraction will not work!", cInfo->Entry); + sLog.outErrorDb("Table `spell_script_target` has creature %u as a target of spellid 30427, but this creature has no SkinningLootId. Gas extraction will not work!", cInfo->Entry); sSpellScriptTargetStorage.EraseEntry(itr->spellId); continue; } diff --git a/src/game/Object/Unit.cpp b/src/game/Object/Unit.cpp index d1591bb81..dbf321230 100644 --- a/src/game/Object/Unit.cpp +++ b/src/game/Object/Unit.cpp @@ -976,8 +976,8 @@ uint32 Unit::DealDamage(Unit* pVictim, uint32 damage, CleanDamage const* cleanDa { SpellEntry const* shareSpell = (*itr)->GetSpellProto(); uint32 shareDamage = uint32(damage*(*itr)->GetModifier()->m_amount / 100.0f); - DealDamageMods(shareTarget, shareDamage, NULL); - DealDamage(shareTarget, shareDamage, 0, damagetype, GetSpellSchoolMask(shareSpell), shareSpell, false); + DealDamageMods(shareTarget, shareDamage, nullptr); + DealDamage(shareTarget, shareDamage, nullptr, damagetype, GetSpellSchoolMask(shareSpell), shareSpell, false); } } } @@ -1681,9 +1681,11 @@ void Unit::CalculateSpellDamage(SpellNonMeleeDamage* damageInfo, int32 damage, S { return; } if (!this || !pVictim) - { return; } - if (!this->IsAlive() || !pVictim->IsAlive()) - { return; } + return; + + // units which are not alive cannot deal damage except for dying creatures + if ((!this->IsAlive() || !pVictim->IsAlive()) && (this->GetTypeId() != TYPEID_UNIT || this->getDeathState() != DEAD)) + return; // Check spell crit chance bool crit = IsSpellCrit(pVictim, spellInfo, damageSchoolMask, attackType); @@ -3521,7 +3523,7 @@ SpellMissInfo Unit::SpellHitResult(Unit* pVictim, SpellEntry const* spell, bool return SPELL_MISS_EVADE; // Check for immune - if (pVictim->IsImmuneToSpell(spell, this == pVictim)) + if (pVictim->IsImmuneToSpell(spell, this == pVictim) && !spell->HasAttribute(SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY)) return SPELL_MISS_IMMUNE; // All positive spells can`t miss @@ -3530,7 +3532,7 @@ SpellMissInfo Unit::SpellHitResult(Unit* pVictim, SpellEntry const* spell, bool return SPELL_MISS_NONE; // Check for immune - if (pVictim->IsImmunedToDamage(GetSpellSchoolMask(spell))) + if (pVictim->IsImmunedToDamage(GetSpellSchoolMask(spell)) && !spell->HasAttribute(SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY)) return SPELL_MISS_IMMUNE; // Try victim reflect spell @@ -4027,7 +4029,7 @@ void Unit::SetFacingToObject(WorldObject* pObject) SetFacingTo(GetAngle(pObject)); } -bool Unit::isInAccessablePlaceFor(Creature const* c) const +bool Unit::IsInAccessablePlaceFor(Creature const* c) const { if (IsInWater()) return c->CanSwim(); @@ -5875,7 +5877,7 @@ bool Unit::IsHostileTo(Unit const* unit) const return false; // Sanctuary - if (pTarget->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY) && pTester->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY)) + if (pTarget->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SUPPORTABLE) && pTester->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SUPPORTABLE)) return false; // PvP FFA state @@ -5987,7 +5989,7 @@ bool Unit::IsFriendlyTo(Unit const* unit) const return true; // Sanctuary - if (pTarget->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY) && pTester->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY)) + if (pTarget->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SUPPORTABLE) && pTester->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SUPPORTABLE)) return true; // PvP FFA state @@ -6661,6 +6663,17 @@ void Unit::EnergizeBySpell(Unit* pVictim, uint32 SpellID, uint32 Damage, Powers pVictim->ModifyPower(powertype, Damage); } +/** Calculate spell coefficents and level penalties for spell/melee damage or heal + * + * this is the caster of the spell/ melee attacker + * @param spellProto SpellEntry of the used spell + * @param total current value onto which the Bonus and level penalty will be calculated + * @param benefit additional benefit from ie spellpower-auras + * @param ap_benefit additional melee attackpower benefit from auras + * @param damagetype what kind of damage + * @param donePart calculate for done or taken + * @param defCoeffMod default coefficient for additional scaling (i.e. normal player healing SCALE_SPELLPOWER_HEALING) + */ int32 Unit::SpellBonusWithCoeffs(SpellEntry const* spellProto, int32 total, int32 benefit, int32 ap_benefit, DamageEffectType damagetype, bool donePart, float defCoeffMod) { // Distribute Damage over multiple effects, reduce by AoE @@ -7119,7 +7132,7 @@ uint32 Unit::SpellDamageBonusTaken(Unit* pCaster, SpellEntry const* spellProto, int32 TakenAdvertisedBenefit = SpellBaseDamageBonusTaken(GetSpellSchoolMask(spellProto)); // apply benefit affected by spell power implicit coeffs and spell level penalties - TakenTotal = SpellBonusWithCoeffs(spellProto, TakenTotal, TakenAdvertisedBenefit, 0, damagetype, false); + TakenTotal = pCaster->SpellBonusWithCoeffs(spellProto, TakenTotal, TakenAdvertisedBenefit, 0, damagetype, false); float tmpDamage = (int32(pdamage) + TakenTotal * int32(stack)) * TakenTotalMod; @@ -7568,7 +7581,7 @@ uint32 Unit::SpellHealingBonusDone(Unit* pVictim, SpellEntry const* spellProto, int32 DoneAdvertisedBenefit = SpellBaseHealingBonusDone(GetSpellSchoolMask(spellProto)); // apply ap bonus and benefit affected by spell power implicit coeffs and spell level penalties - DoneTotal = SpellBonusWithCoeffs(spellProto, DoneTotal, DoneAdvertisedBenefit, 0, damagetype, true, 1.88f); + DoneTotal = SpellBonusWithCoeffs(spellProto, DoneTotal, DoneAdvertisedBenefit, 0, damagetype, true, SCALE_SPELLPOWER_HEALING); // use float as more appropriate for negative values and percent applying float heal = (healamount + DoneTotal * int32(stack)) * DoneTotalMod; @@ -7612,7 +7625,7 @@ uint32 Unit::SpellHealingBonusTaken(Unit* pCaster, SpellEntry const* spellProto, int32 TakenAdvertisedBenefit = SpellBaseHealingBonusTaken(GetSpellSchoolMask(spellProto)); // apply benefit affected by spell power implicit coeffs and spell level penalties - TakenTotal = SpellBonusWithCoeffs(spellProto, TakenTotal, TakenAdvertisedBenefit, 0, damagetype, false, 1.88f); + TakenTotal = pCaster->SpellBonusWithCoeffs(spellProto, TakenTotal, TakenAdvertisedBenefit, 0, damagetype, false, SCALE_SPELLPOWER_HEALING); AuraList const& mHealingGet = GetAurasByType(SPELL_AURA_MOD_HEALING_RECEIVED); for (AuraList::const_iterator i = mHealingGet.begin(); i != mHealingGet.end(); ++i) @@ -8155,7 +8168,7 @@ uint32 Unit::MeleeDamageBonusTaken(Unit* pCaster, uint32 pdamage, WeaponAttackTy if (!isWeaponDamageBasedSpell) { // apply benefit affected by spell power implicit coeffs and spell level penalties - TakenFlat = SpellBonusWithCoeffs(spellProto, 0, TakenFlat, 0, damagetype, false); + TakenFlat = pCaster->SpellBonusWithCoeffs(spellProto, 0, TakenFlat, 0, damagetype, false); } float tmpDamage = float(int32(pdamage) + TakenFlat * int32(stack)) * TakenPercent; @@ -8257,6 +8270,10 @@ void Unit::Mount(uint32 mount, uint32 spellId) pet->ApplyModeFlags(PET_MODE_DISABLE_ACTIONS, true); } } + + float height = ((Player*)this)->GetCollisionHeight(true); + if (height) + SendCollisionHeightUpdate(height); } } @@ -8284,9 +8301,17 @@ void Unit::Unmount(bool from_aura) if (GetTypeId() == TYPEID_PLAYER) { if (Pet* pet = GetPet()) - pet->ApplyModeFlags(PET_MODE_DISABLE_ACTIONS, false); + { + // Get reaction state and display appropriately + if (CharmInfo* charmInfo = pet->GetCharmInfo()) + pet->SetModeFlags(PetModeFlags(charmInfo->GetReactState() | charmInfo->GetCommandState() * 0x100)); + } else - { ((Player*)this)->ResummonPetTemporaryUnSummonedIfAny(); } + ((Player*)this)->ResummonPetTemporaryUnSummonedIfAny(); + + float height = ((Player*)this)->GetCollisionHeight(false); + if (height) + SendCollisionHeightUpdate(height); } } @@ -8687,8 +8712,23 @@ bool Unit::IsVisibleForOrDetect(Unit const* u, WorldObject const* viewPoint, boo if (m_Visibility == VISIBILITY_OFF) return false; + // grouped players should always see stealthed party members + if (GetTypeId() == TYPEID_PLAYER && u->GetTypeId() == TYPEID_PLAYER) + if (((Player*)this)->IsGroupVisibleFor(((Player*)u)) && u->IsFriendlyTo(this)) + return true; + // raw invisibility bool invisible = (m_invisibilityMask != 0 || u->m_invisibilityMask != 0); + if (u->GetTypeId() == TYPEID_PLAYER) // if object is player with mover, use its visibility masks, so that an invisible player MCing a creature can see stuff + { + if (Player* player = (Player*)u) + { + if (Unit* mover=player->GetMover()) + { + invisible= (m_invisibilityMask != 0 || mover->m_invisibilityMask != 0); + } + } + } // detectable invisibility case if (invisible && ( @@ -8705,35 +8745,18 @@ bool Unit::IsVisibleForOrDetect(Unit const* u, WorldObject const* viewPoint, boo // special cases for always overwrite invisibility/stealth if (invisible || m_Visibility == VISIBILITY_GROUP_STEALTH) { - // non-hostile case - if (!u->IsHostileTo(this)) - { - // player see other player with stealth/invisibility only if he in same group or raid or same team (raid/team case dependent from conf setting) - if (GetTypeId() == TYPEID_PLAYER && u->GetTypeId() == TYPEID_PLAYER) - { - if (((Player*)this)->IsGroupVisibleFor(((Player*)u))) - return true; - - // else apply same rules as for hostile case (detecting check for stealth) - } - } - // hostile case - else + if (u->IsHostileTo(this)) { // Hunter mark functionality AuraList const& auras = GetAurasByType(SPELL_AURA_MOD_STALKED); for (AuraList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter) if ((*iter)->GetCasterGuid() == u->GetObjectGuid()) return true; - - // else apply detecting check for stealth } // none other cases for detect invisibility, so invisible if (invisible) return false; - - // else apply stealth detecting check } // unit got in stealth in this moment and must ignore old detected state @@ -9518,7 +9541,7 @@ bool Unit::SelectHostileTarget() for (AuraList::const_reverse_iterator aura = tauntAuras.rbegin(); aura != tauntAuras.rend(); ++aura) { if ((caster = (*aura)->GetCaster()) && caster->IsInMap(this) && - caster->IsTargetableForAttack() && caster->isInAccessablePlaceFor((Creature*)this) && + caster->IsTargetableForAttack() && caster->IsInAccessablePlaceFor((Creature*)this) && !IsSecondChoiceTarget(caster, true)) { target = caster; @@ -9581,7 +9604,7 @@ bool Unit::SelectHostileTarget() { for (AttackerSet::const_iterator itr = m_attackers.begin(); itr != m_attackers.end(); ++itr) { - if ((*itr)->IsInMap(this) && (*itr)->IsTargetableForAttack() && (*itr)->isInAccessablePlaceFor((Creature*)this)) + if ((*itr)->IsInMap(this) && (*itr)->IsTargetableForAttack() && (*itr)->IsInAccessablePlaceFor((Creature*)this)) return false; } } @@ -12156,3 +12179,59 @@ void Unit::BuildMoveFeatherFallPacket(WorldPacket* data, bool apply, uint32 valu } } +void Unit::BuildMoveHoverPacket(WorldPacket* data, bool apply, uint32 value) +{ + ObjectGuid guid = GetObjectGuid(); + + if (apply) + { + data->Initialize(SMSG_MOVE_SET_HOVER, 8 + 4 + 1); + data->WriteGuidMask<1, 4, 2, 3, 0, 5, 6, 7>(guid); + data->WriteGuidBytes<5, 4, 1, 2, 3, 6, 0, 7>(guid); + *data << uint32(0); + } + else + { + data->Initialize(SMSG_MOVE_UNSET_HOVER, 8 + 4 + 1); + data->WriteGuidMask<4, 6, 3, 1, 2, 7, 5, 0>(guid); + data->WriteGuidBytes<4, 5, 3, 6, 7, 1, 2, 0>(guid); + *data << uint32(0); + } +} + +void Unit::BuildMoveLevitatePacket(WorldPacket* data, bool apply, uint32 value) +{ + ObjectGuid guid = GetObjectGuid(); + + if (apply) + { + data->Initialize(SMSG_MOVE_GRAVITY_ENABLE); + data->WriteGuidMask<1, 4, 7, 5, 2, 0, 3, 6>(GetObjectGuid()); + data->WriteGuidBytes<3>(GetObjectGuid()); + *data << uint32(value); + data->WriteGuidBytes<7, 6, 4, 0, 1, 5, 2>(GetObjectGuid()); + } + else + { + data->Initialize(SMSG_MOVE_GRAVITY_DISABLE); + data->WriteGuidMask<0, 1, 5, 7, 6, 4, 3, 2>(GetObjectGuid()); + data->WriteGuidBytes<7, 2, 0>(GetObjectGuid()); + *data << uint32(value); + data->WriteGuidBytes<5, 1, 3, 4, 6>(GetObjectGuid()); + } +} + +void Unit::SendCollisionHeightUpdate(float height) +{ + if (GetTypeId() == TYPEID_PLAYER) + { + WorldPacket data(SMSG_MOVE_SET_COLLISION_HGT, GetPackGUID().size() + 4 + 4); + data.WriteGuidMask<6, 1, 4, 7, 5, 2, 0, 3>(GetObjectGuid()); + data.WriteGuidBytes<6, 0, 4, 3, 5>(GetObjectGuid()); + data << uint32(sWorld.GetGameTime()); // Packet counter + data.WriteGuidBytes<1, 2, 7>(GetObjectGuid()); + data << ((Player*)this)->GetCollisionHeight(true); + ((Player*)this)->GetSession()->SendPacket(&data); + } +} + diff --git a/src/game/Object/Unit.h b/src/game/Object/Unit.h index fffdf6ac0..5cb45d069 100644 --- a/src/game/Object/Unit.h +++ b/src/game/Object/Unit.h @@ -150,6 +150,8 @@ enum SpellFacingFlags #define BASE_ATTACK_TIME 2000 #define BASE_BLOCK_DAMAGE_PERCENT 30 +#define SCALE_SPELLPOWER_HEALING 1.88f + /** * byte value (UNIT_FIELD_BYTES_1,0). * @@ -225,9 +227,9 @@ enum UnitPVPStateFlags UNIT_BYTE2_FLAG_PVP = 0x01, UNIT_BYTE2_FLAG_UNK1 = 0x02, UNIT_BYTE2_FLAG_FFA_PVP = 0x04, - UNIT_BYTE2_FLAG_SANCTUARY = 0x08, - UNIT_BYTE2_FLAG_UNK4 = 0x10, - UNIT_BYTE2_FLAG_UNK5 = 0x20, + UNIT_BYTE2_FLAG_SUPPORTABLE = 0x08, // allows for being targeted for healing/bandaging by friendlies + UNIT_BYTE2_FLAG_AURAS = 0x10, // show possitive auras as positive, and allow its dispel + UNIT_BYTE2_FLAG_UNK5 = 0x20, // show negative auras as positive, *not* allowing dispel (at least for pets) UNIT_BYTE2_FLAG_UNK6 = 0x40, UNIT_BYTE2_FLAG_UNK7 = 0x80 }; @@ -596,7 +598,7 @@ enum UnitFlags UNIT_FLAG_PVP = 0x00001000, // changed in 3.0.3 UNIT_FLAG_SILENCED = 0x00002000, // silenced, 2.1.1 UNIT_FLAG_UNK_14 = 0x00004000, // 2.0.8 - UNIT_FLAG_UNK_15 = 0x00008000, + UNIT_FLAG_UNK_15 = 0x00008000, // related to jerky movement in water? UNIT_FLAG_UNK_16 = 0x00010000, // removes attackable icon UNIT_FLAG_PACIFIED = 0x00020000, // 3.0.3 ok UNIT_FLAG_STUNNED = 0x00040000, // 3.0.3 ok @@ -2399,7 +2401,7 @@ class Unit : public WorldObject virtual bool IsInWater() const; virtual bool IsUnderWater() const; - bool isInAccessablePlaceFor(Creature const* c) const; + bool IsInAccessablePlaceFor(Creature const* c) const; void SendHealSpellLog(Unit* pVictim, uint32 SpellID, uint32 Damage, uint32 OverHeal, bool critical = false, uint32 absorb = 0); void SendEnergizeSpellLog(Unit* pVictim, uint32 SpellID, uint32 Damage, Powers powertype); @@ -2656,6 +2658,12 @@ class Unit : public WorldObject bool IsLevitating() const { return m_movementInfo.HasMovementFlag(MOVEFLAG_LEVITATING); } bool IsWalking() const { return m_movementInfo.HasMovementFlag(MOVEFLAG_WALK_MODE); } bool IsRooted() const { return m_movementInfo.HasMovementFlag(MOVEFLAG_ROOT); } + + virtual void SetLevitate(bool /*enabled*/) {} + virtual void SetSwim(bool /*enabled*/) {} + virtual void SetCanFly(bool /*enabled*/) {} + virtual void SetFeatherFall(bool /*enabled*/) {} + virtual void SetHover(bool /*enabled*/) {} virtual void SetRoot(bool /*enabled*/) {} /** * Changes this \ref Unit s ability to walk on water. @@ -3327,6 +3335,7 @@ class Unit : public WorldObject // at any changes to Scale and/or displayId void UpdateModelData(); + void SendCollisionHeightUpdate(float height); DynamicObject* GetDynObject(uint32 spellId, SpellEffectIndex effIndex); DynamicObject* GetDynObject(uint32 spellId); @@ -3485,6 +3494,9 @@ class Unit : public WorldObject bool IsLinkingEventTrigger() const { return m_isCreatureLinkingTrigger; } + virtual bool CanSwim() const = 0; + virtual bool CanFly() const = 0; + bool IsSplineEnabled() const; bool IsInWorgenForm(bool inPermanent = false) const; @@ -3496,6 +3508,8 @@ class Unit : public WorldObject void BuildSendPlayVisualPacket(WorldPacket* data, uint32 value, bool impact); void BuildMoveSetCanFlyPacket(WorldPacket* data, bool apply, uint32 value); void BuildMoveFeatherFallPacket(WorldPacket* data, bool apply, uint32 value); + void BuildMoveHoverPacket(WorldPacket* data, bool apply, uint32 value); + void BuildMoveLevitatePacket(WorldPacket* data, bool apply, uint32 value); protected: explicit Unit(); diff --git a/src/game/References/ThreatManager.cpp b/src/game/References/ThreatManager.cpp index 61bf55ca1..52b8f2122 100644 --- a/src/game/References/ThreatManager.cpp +++ b/src/game/References/ThreatManager.cpp @@ -148,7 +148,7 @@ void HostileReference::updateOnlineStatus() !getTarget()->IsTaxiFlying())) { Creature* creature = (Creature*) getSourceUnit(); - online = getTarget()->isInAccessablePlaceFor(creature); + online = getTarget()->IsInAccessablePlaceFor(creature); if (!online) { if (creature->AI()->canReachByRangeAttack(getTarget())) diff --git a/src/game/Server/DBCStores.cpp b/src/game/Server/DBCStores.cpp index e188296ed..3af678165 100644 --- a/src/game/Server/DBCStores.cpp +++ b/src/game/Server/DBCStores.cpp @@ -86,7 +86,8 @@ DBCStorage sCinematicSequencesStore(CinematicSequences DBCStorage sCreatureDisplayInfoStore(CreatureDisplayInfofmt); DBCStorage sCreatureDisplayInfoExtraStore(CreatureDisplayInfoExtrafmt); DBCStorage sCreatureFamilyStore(CreatureFamilyfmt); -DBCStorage sCreatureSpellDataStore(CreatureSpellDatafmt); +DBCStorage sCreatureModelDataStore(CreatureModelDatafmt); +DBCStorage sCreatureSpellDataStore(CreatureSpellDatafmt); // sCreatureModelDataStore DBCStorage sCreatureTypeStore(CreatureTypefmt); DBCStorage sCurrencyTypesStore(CurrencyTypesfmt); @@ -511,21 +512,22 @@ void LoadDBCStores(const std::string& dataPath) sChrClassXPowerTypesStore[entry->classId][entry->power] = index; sChrClassXPowerIndexStore[entry->classId][index] = entry->power; } - LoadDBC(availableDbcLocales, bar, bad_dbc_files, sChrRacesStore, dbcPath, "ChrRaces.dbc"); - LoadDBC(availableDbcLocales, bar, bad_dbc_files, sCinematicSequencesStore, dbcPath, "CinematicSequences.dbc"); - LoadDBC(availableDbcLocales, bar, bad_dbc_files, sCreatureDisplayInfoStore, dbcPath, "CreatureDisplayInfo.dbc"); - LoadDBC(availableDbcLocales, bar, bad_dbc_files, sCreatureDisplayInfoExtraStore, dbcPath, "CreatureDisplayInfoExtra.dbc"); - LoadDBC(availableDbcLocales, bar, bad_dbc_files, sCreatureFamilyStore, dbcPath, "CreatureFamily.dbc"); - LoadDBC(availableDbcLocales, bar, bad_dbc_files, sCreatureSpellDataStore, dbcPath, "CreatureSpellData.dbc"); - LoadDBC(availableDbcLocales, bar, bad_dbc_files, sCreatureTypeStore, dbcPath, "CreatureType.dbc"); - LoadDBC(availableDbcLocales, bar, bad_dbc_files, sCurrencyTypesStore, dbcPath, "CurrencyTypes.dbc"); - LoadDBC(availableDbcLocales, bar, bad_dbc_files, sDestructibleModelDataStore, dbcPath, "DestructibleModelData.dbc"); - LoadDBC(availableDbcLocales, bar, bad_dbc_files, sDungeonEncounterStore, dbcPath, "DungeonEncounter.dbc"); - LoadDBC(availableDbcLocales, bar, bad_dbc_files, sDurabilityCostsStore, dbcPath, "DurabilityCosts.dbc"); - LoadDBC(availableDbcLocales, bar, bad_dbc_files, sDurabilityQualityStore, dbcPath, "DurabilityQuality.dbc"); - LoadDBC(availableDbcLocales, bar, bad_dbc_files, sEmotesStore, dbcPath, "Emotes.dbc"); - LoadDBC(availableDbcLocales, bar, bad_dbc_files, sEmotesTextStore, dbcPath, "EmotesText.dbc"); - LoadDBC(availableDbcLocales, bar, bad_dbc_files, sFactionStore, dbcPath, "Faction.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sChrRacesStore, dbcPath,"ChrRaces.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCinematicSequencesStore, dbcPath,"CinematicSequences.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCreatureDisplayInfoStore, dbcPath,"CreatureDisplayInfo.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCreatureDisplayInfoExtraStore,dbcPath,"CreatureDisplayInfoExtra.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCreatureFamilyStore, dbcPath,"CreatureFamily.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCreatureModelDataStore, dbcPath,"CreatureModelData.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCreatureSpellDataStore, dbcPath,"CreatureSpellData.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCreatureTypeStore, dbcPath,"CreatureType.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCurrencyTypesStore, dbcPath,"CurrencyTypes.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sDestructibleModelDataStore,dbcPath,"DestructibleModelData.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sDungeonEncounterStore, dbcPath,"DungeonEncounter.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sDurabilityCostsStore, dbcPath,"DurabilityCosts.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sDurabilityQualityStore, dbcPath,"DurabilityQuality.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sEmotesStore, dbcPath,"Emotes.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sEmotesTextStore, dbcPath,"EmotesText.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sFactionStore, dbcPath,"Faction.dbc"); for (uint32 i = 0; i < sFactionStore.GetNumRows(); ++i) { FactionEntry const* faction = sFactionStore.LookupEntry(i); @@ -1004,6 +1006,87 @@ uint32 GetVirtualMapForMapAndZone(uint32 mapid, uint32 zoneId) return mapid; } +ContentLevels GetContentLevelsForMap(uint32 mapid) +{ + MapEntry const* mapEntry = sMapStore.LookupEntry(mapid); + if (!mapEntry) + return CONTENT_1_60; + + // exceptions for 648 - Goblin Starter area and 654 - Worgen Starter area + if (mapid == 648 || mapid == 654) + return CONTENT_1_60; + + switch (mapEntry->Expansion()) + { + default: return CONTENT_1_60; + case 1: return CONTENT_61_70; + case 2: return CONTENT_71_80; + case 3: return CONTENT_81_85; + } +} + +ChatChannelsEntry const* GetChannelEntryFor(uint32 channel_id) +{ + // not sorted, numbering index from 0 + for (uint32 i = 0; i < sChatChannelsStore.GetNumRows(); ++i) + { + ChatChannelsEntry const* ch = sChatChannelsStore.LookupEntry(i); + if (ch && ch->ChannelID == channel_id) + return ch; + } + return nullptr; +} + +bool IsTotemCategoryCompatiableWith(uint32 itemTotemCategoryId, uint32 requiredTotemCategoryId) +{ + if (requiredTotemCategoryId==0) + return true; + if (itemTotemCategoryId==0) + return false; + + TotemCategoryEntry const* itemEntry = sTotemCategoryStore.LookupEntry(itemTotemCategoryId); + if (!itemEntry) + return false; + TotemCategoryEntry const* reqEntry = sTotemCategoryStore.LookupEntry(requiredTotemCategoryId); + if (!reqEntry) + return false; + + if (itemEntry->categoryType!=reqEntry->categoryType) + return false; + + return (itemEntry->categoryMask & reqEntry->categoryMask)==reqEntry->categoryMask; +} + +bool Zone2MapCoordinates(float& x, float& y, uint32 zone) +{ + WorldMapAreaEntry const* maEntry = sWorldMapAreaStore.LookupEntry(zone); + + // if not listed then map coordinates (instance) + if (!maEntry || maEntry->x2 == maEntry->x1 || maEntry->y2 == maEntry->y1) + return false; + + std::swap(x, y); // at client map coords swapped + x = x * ((maEntry->x2-maEntry->x1) / 100) + maEntry->x1; + y = y * ((maEntry->y2-maEntry->y1) / 100) + maEntry->y1; // client y coord from top to down + + return true; +} + +bool Map2ZoneCoordinates(float& x,float& y,uint32 zone) +{ + WorldMapAreaEntry const* maEntry = sWorldMapAreaStore.LookupEntry(zone); + + // if not listed then map coordinates (instance) + if (!maEntry || maEntry->x2 == maEntry->x1 || maEntry->y2 == maEntry->y1) + return false; + + x = (x-maEntry->x1)/((maEntry->x2-maEntry->x1)/100); + y = (y-maEntry->y1)/((maEntry->y2-maEntry->y1)/100); // client y coord from top to down + std::swap(x,y); // client have map coords swapped + + return true; +} + ContentLevels GetContentLevelsForMapAndZone(uint32 mapId, uint32 zoneId) { MapEntry const* mapEntry = sMapStore.LookupEntry(mapId); @@ -1042,109 +1125,6 @@ ContentLevels GetContentLevelsForMapAndZone(uint32 mapId, uint32 zoneId) } } -ChatChannelsEntry const* GetChannelEntryFor(uint32 channel_id) -{ - // not sorted, numbering index from 0 - for (uint32 i = 0; i < sChatChannelsStore.GetNumRows(); ++i) - { - ChatChannelsEntry const* ch = sChatChannelsStore.LookupEntry(i); - if (ch && ch->ChannelID == channel_id) - return ch; - } - return NULL; -} - -/* -static ChatChannelsEntry worldCh = { 26, 4, "world" }; - -ChatChannelsEntry const* GetChannelEntryFor(const std::string& name) -{ - // not sorted, numbering index from 0 - for (uint32 i = 0; i < sChatChannelsStore.GetNumRows(); ++i) - { - ChatChannelsEntry const* ch = sChatChannelsStore.LookupEntry(i); - if (ch) - { - // need to remove %s from entryName if it exists before we match - std::string entryName(ch->pattern[0]); - std::size_t removeString = entryName.find("%s"); - - if (removeString != std::string::npos) - entryName.replace(removeString, 2, ""); - - if (name.find(entryName) != std::string::npos) - return ch; - } - } - - bool compare = true; // hack for world channel, TODO smth! - std::string world = "world"; - for (uint8 i = 0; i < name.length(); ++i) - { - if (tolower(name[i]) != world[i]) - { - compare = false; - break; - } - } - - if (compare) - return &worldCh; - - return NULL; -} -*/ - -bool IsTotemCategoryCompatiableWith(uint32 itemTotemCategoryId, uint32 requiredTotemCategoryId) -{ - if (requiredTotemCategoryId == 0) - return true; - if (itemTotemCategoryId == 0) - return false; - - TotemCategoryEntry const* itemEntry = sTotemCategoryStore.LookupEntry(itemTotemCategoryId); - if (!itemEntry) - return false; - TotemCategoryEntry const* reqEntry = sTotemCategoryStore.LookupEntry(requiredTotemCategoryId); - if (!reqEntry) - return false; - - if (itemEntry->categoryType != reqEntry->categoryType) - return false; - - return (itemEntry->categoryMask & reqEntry->categoryMask) == reqEntry->categoryMask; -} - -bool Zone2MapCoordinates(float& x, float& y, uint32 zone) -{ - WorldMapAreaEntry const* maEntry = sWorldMapAreaStore.LookupEntry(zone); - - // if not listed then map coordinates (instance) - if (!maEntry || maEntry->x2 == maEntry->x1 || maEntry->y2 == maEntry->y1) - return false; - - std::swap(x, y); // at client map coords swapped - x = x * ((maEntry->x2 - maEntry->x1) / 100) + maEntry->x1; - y = y * ((maEntry->y2 - maEntry->y1) / 100) + maEntry->y1; // client y coord from top to down - - return true; -} - -bool Map2ZoneCoordinates(float& x, float& y, uint32 zone) -{ - WorldMapAreaEntry const* maEntry = sWorldMapAreaStore.LookupEntry(zone); - - // if not listed then map coordinates (instance) - if (!maEntry || maEntry->x2 == maEntry->x1 || maEntry->y2 == maEntry->y1) - return false; - - x = (x - maEntry->x1) / ((maEntry->x2 - maEntry->x1) / 100); - y = (y - maEntry->y1) / ((maEntry->y2 - maEntry->y1) / 100); // client y coord from top to down - std::swap(x, y); // client have map coords swapped - - return true; -} - MapDifficultyEntry const* GetMapDifficultyData(uint32 mapId, Difficulty difficulty) { MapDifficultyMap::const_iterator itr = sMapDifficultyMap.find(MAKE_PAIR32(mapId, difficulty)); diff --git a/src/game/Server/DBCStores.h b/src/game/Server/DBCStores.h index 8027817b1..26dcd9b14 100644 --- a/src/game/Server/DBCStores.h +++ b/src/game/Server/DBCStores.h @@ -141,6 +141,7 @@ extern DBCStorage sCinematicSequencesStore; extern DBCStorage sCreatureDisplayInfoStore; extern DBCStorage sCreatureDisplayInfoExtraStore; extern DBCStorage sCreatureFamilyStore; +extern DBCStorage sCreatureModelDataStore; extern DBCStorage sCreatureSpellDataStore; extern DBCStorage sCreatureTypeStore; extern DBCStorage sCurrencyTypesStore; diff --git a/src/game/Server/DBCStructure.h b/src/game/Server/DBCStructure.h index a57e29668..163503276 100644 --- a/src/game/Server/DBCStructure.h +++ b/src/game/Server/DBCStructure.h @@ -751,19 +751,19 @@ struct CinematicSequencesEntry struct CreatureDisplayInfoEntry { uint32 Displayid; // 0 m_ID - // 1 m_modelID - // 2 m_soundID + uint32 ModelId; // 1 m_modelID + // 2 m_soundID uint32 ExtendedDisplayInfoID; // 3 m_extendedDisplayInfoID -> CreatureDisplayInfoExtraEntry::DisplayExtraId float Scale; // 4 m_creatureModelScale - // 5 m_creatureModelAlpha - // 6-8 m_textureVariation[3] - // 9 m_portraitTextureName - // 10 m_sizeClass - // 11 m_bloodID - // 12 m_NPCSoundID - // 13 m_particleColorID - // 14 m_creatureGeosetData - // 15 m_objectEffectPackageID + // 5 m_creatureModelAlpha + // 6-8 m_textureVariation[3] + // 9 m_portraitTextureName + // 10 m_sizeClass + // 11 m_bloodID + // 12 m_NPCSoundID + // 13 m_particleColorID + // 14 m_creatureGeosetData + // 15 m_objectEffectPackageID // 16 all 0 }; @@ -797,6 +797,28 @@ struct CreatureFamilyEntry // 11 m_iconFile }; +struct CreatureModelDataEntry +{ + uint32 Id; // 0 + //uint32 Flags; // 1 + //char* ModelPath // 2 + //uint32 InhabitType; // 3 model inhabit type + //float Scale; // 4 Used in calculation of unit collision data + //int32 Unk2 // 5 + //int32 Unk3 // 6 + //uint32 Unk4 // 7 + //uint32 Unk5 // 8 + //float Unk6 // 9 + //uint32 Unk7 // 10 + //float Unk8 // 11 + //uint32 Unk9 // 12 + //uint32 Unk10 // 13 + //float CollisionWidth; // 14 + float CollisionHeight; // 15 + float MountHeight; // 16 Used in calculation of unit collision data when mounted + //float Unks[14] // 17-30 +}; + #define MAX_CREATURE_SPELL_DATA_SLOT 4 struct CreatureSpellDataEntry @@ -861,7 +883,7 @@ struct DestructibleModelDataEntry // uint32 unk17; // 17 // uint32 unk18; // 18 // uint32 unk19; // 19 - //uint32 smokeDisplayId; // 20 + uint32 smokeDisplayId; // 20 // uint32 unk21; // 21 // uint32 unk22; // 22 // uint32 unk23; // 23 @@ -1380,7 +1402,6 @@ struct MapEntry MapID == 509 || MapID == 534 || MapID == 560 || // AhnQiraj, HyjalPast, HillsbradPast MapID == 568 || MapID == 580 || MapID == 595 || // ZulAman, Sunwell Plateau, Culling of Stratholme MapID == 603 || MapID == 615 || MapID == 616 || // Ulduar, The Obsidian Sanctum, The Eye Of Eternity - MapID == 603 || MapID == 615 || MapID == 616 || // Ulduar, The Obsidian Sanctum, The Eye Of Eternity MapID == 631 || MapID == 658 || MapID == 724 || // Icecrown Citadel, Pit of Saron, Ruby Sanctum MapID == 644 || MapID == 720 || MapID == 721 || // Halls of Origination, Firelands MapID == 734 || MapID == 755 || MapID == 859 || // Ahn'Qiraj Terrace, Lost City of Tol'Vir, Zul'Gurub @@ -1723,81 +1744,89 @@ struct ClassFamilyMask // SpellAuraOptions.dbc struct SpellAuraOptionsEntry { - //uint32 Id; // 0 m_ID - uint32 StackAmount; // 51 m_cumulativeAura - uint32 procChance; // 38 m_procChance - uint32 procCharges; // 39 m_procCharges - uint32 procFlags; // 37 m_procTypeMask + //uint32 Id; // 0 m_ID + uint32 StackAmount; // 1 m_cumulativeAura + uint32 procChance; // 2 m_procChance + uint32 procCharges; // 3 m_procCharges + uint32 procFlags; // 4 m_procTypeMask }; // SpellAuraRestrictions.dbc struct SpellAuraRestrictionsEntry { - //uint32 Id; // 0 m_ID - uint32 CasterAuraState; // 21 m_casterAuraState - uint32 TargetAuraState; // 22 m_targetAuraState - uint32 CasterAuraStateNot; // 23 m_excludeCasterAuraState - uint32 TargetAuraStateNot; // 24 m_excludeTargetAuraState - uint32 casterAuraSpell; // 25 m_casterAuraSpell - uint32 targetAuraSpell; // 26 m_targetAuraSpell - uint32 excludeCasterAuraSpell; // 27 m_excludeCasterAuraSpell - uint32 excludeTargetAuraSpell; // 28 m_excludeTargetAuraSpell + //uint32 Id; // 0 m_ID + uint32 CasterAuraState; // 1 m_casterAuraState + uint32 TargetAuraState; // 2 m_targetAuraState + uint32 CasterAuraStateNot; // 3 m_excludeCasterAuraState + uint32 TargetAuraStateNot; // 4 m_excludeTargetAuraState + uint32 casterAuraSpell; // 5 m_casterAuraSpell + uint32 targetAuraSpell; // 6 m_targetAuraSpell + uint32 excludeCasterAuraSpell; // 7 m_excludeCasterAuraSpell + uint32 excludeTargetAuraSpell; // 8 m_excludeTargetAuraSpell }; // SpellCastingRequirements.dbc struct SpellCastingRequirementsEntry { - //uint32 Id; // 0 m_ID - uint32 FacingCasterFlags; // 20 m_facingCasterFlags - //uint32 MinFactionId; // 159 m_minFactionID not used - //uint32 MinReputation; // 160 m_minReputation not used - int32 AreaGroupId; // 164 m_requiredAreaGroupId - //uint32 RequiredAuraVision; // 161 m_requiredAuraVision not used - uint32 RequiresSpellFocus; // 19 m_requiresSpellFocus + //uint32 Id; // 0 m_ID + uint32 FacingCasterFlags; // 1 m_facingCasterFlags + //uint32 MinFactionId; // 2 m_minFactionID not used + //uint32 MinReputation; // 3 m_minReputation not used + int32 AreaGroupId; // 4 m_requiredAreaGroupId + //uint32 RequiredAuraVision; // 5 m_requiredAuraVision not used + uint32 RequiresSpellFocus; // 6 m_requiresSpellFocus +}; + +// SpellCastTimes.dbc +struct SpellCastTimesEntry +{ + uint32 ID; // 0 m_ID + int32 CastTime; // 1 m_base + float CastTimePerLevel; // 2 m_perLevel + int32 MinCastTime; // 3 m_minimum }; // SpellCategories.dbc struct SpellCategoriesEntry { - //uint32 Id; // 0 m_ID + //uint32 Id; // 0 m_ID uint32 Category; // 1 m_category - uint32 DmgClass; // 153 m_defenseType - uint32 Dispel; // 2 m_dispelType - uint32 Mechanic; // 3 m_mechanic - uint32 PreventionType; // 154 m_preventionType - uint32 StartRecoveryCategory; // 145 m_startRecoveryCategory + uint32 DmgClass; // 2 m_defenseType + uint32 Dispel; // 3 m_dispelType + uint32 Mechanic; // 4 m_mechanic + uint32 PreventionType; // 5 m_preventionType + uint32 StartRecoveryCategory; // 6 m_startRecoveryCategory }; // SpellClassOptions.dbc struct SpellClassOptionsEntry { //uint32 Id; // 0 m_ID - //uint32 modalNextSpell; // 50 m_modalNextSpell not used - ClassFamilyMask SpellFamilyFlags; // 149-151 m_spellClassMask NOTE: size is 12 bytes!!! - uint32 SpellFamilyName; // 148 m_spellClassSet + //uint32 modalNextSpell; // 1 m_modalNextSpell not used + ClassFamilyMask SpellFamilyFlags; // 2-4 m_spellClassMask NOTE: size is 12 bytes!!! + uint32 SpellFamilyName; // 5 m_spellClassSet //char* Description; // 6 4.0.0 - // helpers - bool IsFitToFamilyMask(uint64 familyFlags, uint32 familyFlags2 = 0) const - { - return SpellFamilyFlags.IsFitToFamilyMask(familyFlags, familyFlags2); - } + bool IsFitToFamilyMask(uint64 familyFlags, uint32 familyFlags2 = 0) const + { + return SpellFamilyFlags.IsFitToFamilyMask(familyFlags, familyFlags2); + } - bool IsFitToFamily(SpellFamily family, uint64 familyFlags, uint32 familyFlags2 = 0) const - { - return SpellFamily(SpellFamilyName) == family && IsFitToFamilyMask(familyFlags, familyFlags2); - } + bool IsFitToFamily(SpellFamily family, uint64 familyFlags, uint32 familyFlags2 = 0) const + { + return SpellFamily(SpellFamilyName) == family && IsFitToFamilyMask(familyFlags, familyFlags2); + } - bool IsFitToFamilyMask(ClassFamilyMask const& mask) const - { - return SpellFamilyFlags.IsFitToFamilyMask(mask); - } + bool IsFitToFamilyMask(ClassFamilyMask const& mask) const + { + return SpellFamilyFlags.IsFitToFamilyMask(mask); + } - bool IsFitToFamily(SpellFamily family, ClassFamilyMask const& mask) const - { - return SpellFamily(SpellFamilyName) == family && IsFitToFamilyMask(mask); - } + bool IsFitToFamily(SpellFamily family, ClassFamilyMask const& mask) const + { + return SpellFamily(SpellFamilyName) == family && IsFitToFamilyMask(mask); + } private: // catch wrong uses @@ -1808,10 +1837,10 @@ struct SpellClassOptionsEntry // SpellCooldowns.dbc struct SpellCooldownsEntry { - //uint32 Id; // 0 m_ID - uint32 CategoryRecoveryTime; // 31 m_categoryRecoveryTime - uint32 RecoveryTime; // 30 m_recoveryTime - uint32 StartRecoveryTime; // 146 m_startRecoveryTime + //uint32 Id; // 0 m_ID + uint32 CategoryRecoveryTime; // 1 m_categoryRecoveryTime + uint32 RecoveryTime; // 2 m_recoveryTime + uint32 StartRecoveryTime; // 3 m_startRecoveryTime }; // SpellEffect.dbc @@ -1841,7 +1870,8 @@ struct SpellEffectEntry uint32 EffectImplicitTargetB; // 23 m_implicitTargetB uint32 EffectSpellId; // 24 m_spellId - spell.dbc uint32 EffectIndex; // 25 m_spellEffectIdx - // uint32 unk; // 26 4.2.0 only 0 or 1 + //uint32 unk; // 26 4.2.0 only 0 or 1 + // helpers int32 CalculateSimpleValue() const { return EffectBasePoints; } @@ -1858,66 +1888,138 @@ struct SpellEffectEntry // SpellEquippedItems.dbc struct SpellEquippedItemsEntry { - //uint32 Id; // 0 m_ID - int32 EquippedItemClass; // 70 m_equippedItemClass (value) - int32 EquippedItemInventoryTypeMask; // 72 m_equippedItemInvTypes (mask) - int32 EquippedItemSubClassMask; // 71 m_equippedItemSubclass (mask) + //uint32 Id; // 0 m_ID + int32 EquippedItemClass; // 1 m_equippedItemClass (value) + int32 EquippedItemInventoryTypeMask; // 2 m_equippedItemInvTypes (mask) + int32 EquippedItemSubClassMask; // 3 m_equippedItemSubclass (mask) +}; + +// SpellFocusObject.dbc +struct SpellFocusObjectEntry +{ + uint32 ID; // 0 m_ID + //char* Name; // 1 m_name_lang }; // SpellInterrupts.dbc struct SpellInterruptsEntry { - //uint32 Id; // 0 m_ID - uint32 AuraInterruptFlags; // 33 m_auraInterruptFlags - //uint32 // 34 4.0.0 - uint32 ChannelInterruptFlags; // 35 m_channelInterruptFlags - //uint32 // 36 4.0.0 - uint32 InterruptFlags; // 32 m_interruptFlags + //uint32 Id; // 0 m_ID + uint32 AuraInterruptFlags; // 1 m_auraInterruptFlags + //uint32 // 2 4.0.0 + uint32 ChannelInterruptFlags; // 3 m_channelInterruptFlags + //uint32 // 4 4.0.0 + uint32 InterruptFlags; // 5 m_interruptFlags +}; + +// SpellItemEnchantment.dbc +struct SpellItemEnchantmentEntry +{ + uint32 ID; // 0 m_ID + //uint32 charges; // 1 m_charges + uint32 type[3]; // 2-4 m_effect[3] + uint32 amount[3]; // 5-7 m_effectPointsMin[3] + //uint32 amount2[3] // 8-10 m_effectPointsMax[3] + uint32 spellid[3]; // 11-13 m_effectArg[3] + DBCString description; // 14 m_name_lang + uint32 aura_id; // 15 m_itemVisual + uint32 slot; // 16 m_flags + uint32 GemID; // 17 m_src_itemID + uint32 EnchantmentCondition; // 18 m_condition_id + uint32 requiredSkill; // 19 m_requiredSkillID + uint32 requiredSkillValue; // 20 m_requiredSkillRank + uint32 requiredLevel; // 21 m_requiredLevel - 3.1 + // 22 new in 3.1 +}; + +// SpellItemEnchantmentCondition.dbc +struct SpellItemEnchantmentConditionEntry +{ + uint32 ID; // 0 m_ID + uint8 Color[5]; // 1-5 m_lt_operandType[5] + //uint32 LT_Operand[5]; // 6-10 m_lt_operand[5] + uint8 Comparator[5]; // 11-15 m_operator[5] + uint8 CompareColor[5]; // 15-20 m_rt_operandType[5] + uint32 Value[5]; // 21-25 m_rt_operand[5] + //uint8 Logic[5] // 25-30 m_logic[5] }; // SpellLevels.dbc struct SpellLevelsEntry { - //uint32 Id; // 0 m_ID - uint32 baseLevel; // 41 m_baseLevel - uint32 maxLevel; // 40 m_maxLevel - uint32 spellLevel; // 42 m_spellLevel + //uint32 Id; // 0 m_ID + uint32 baseLevel; // 1 m_baseLevel + uint32 maxLevel; // 2 m_maxLevel + uint32 spellLevel; // 3 m_spellLevel }; // SpellPower.dbc struct SpellPowerEntry { - //uint32 Id; // 0 - m_ID + //uint32 Id; // 0 - m_ID uint32 manaCost; // 1 - m_manaCost uint32 manaCostPerlevel; // 2 - m_manaCostPerLevel uint32 ManaCostPercentage; // 3 - m_manaCostPct uint32 manaPerSecond; // 4 - m_manaPerSecond - //uint32 PowerDisplayId; // 5 - m_powerDisplayID - id from PowerDisplay.dbc, new in 3.1 - //uint32 unk1; // 6 - 4.0.0 - //unk // 7 - 4.3.0 + uint32 manaPerSecondPerLevel; // 5 m_manaPerSecondPerLevel + //uint32 PowerDisplayId; // 6 - m_powerDisplayID - id from PowerDisplay.dbc, new in 3.1 + float ManaCostPercentageFloat; // 7 4.3.0 +}; + +// SpellRadius.dbc +struct SpellRadiusEntry +{ + uint32 ID; // 0 m_ID + float Radius; // 1 m_radius + float RadiusPerLevel; // 2 m_radiusPerLevel + float RadiusMax; // 3 m_radiusMax +}; + +// SpellRange.dbc +struct SpellRangeEntry +{ + uint32 ID; // 0 m_ID + float minRange; // 1 m_rangeMin[2] + float minRangeFriendly; // 2 + float maxRange; // 3 m_rangeMax[2] + float maxRangeFriendly; // 4 + uint32 type; // 5 m_flags + //char* Name; // 6-21 m_displayName_lang + //char* ShortName; // 23-38 m_displayNameShort_lang }; // SpellReagents.dbc struct SpellReagentsEntry { - //uint32 Id; // 0 m_ID + //uint32 Id; // 0 m_ID int32 Reagent[MAX_SPELL_REAGENTS]; // 54-61 m_reagent uint32 ReagentCount[MAX_SPELL_REAGENTS]; // 62-69 m_reagentCount }; +// SpellRuneCost.dbc +struct SpellRuneCostEntry +{ + uint32 ID; // 0 m_ID + uint32 RuneCost[3]; // 1-3 m_blood m_unholy m_frost (0=blood, 1=frost, 2=unholy) + uint32 runePowerGain; // 4 m_runicPower + + bool NoRuneCost() const { return RuneCost[0] == 0 && RuneCost[1] == 0 && RuneCost[2] == 0; } + bool NoRunicPowerGain() const { return runePowerGain == 0; } +}; + // SpellScaling.dbc struct SpellScalingEntry { //uint32 Id; // 0 m_ID - int32 castTimeMin; // 1 - int32 castTimeMax; // 2 - int32 castScalingMaxLevel; // 3 - int32 playerClass; // 4 (index * 100) + charLevel => gtSpellScaling.dbc + uint32 castTimeMin; // 1 + uint32 castTimeMax; // 2 + uint32 castScalingMaxLevel; // 3 + uint32 playerClass; // 4 (index * 100) + charLevel => gtSpellScaling.dbc float coeff1[3]; // 5-7 float coeff2[3]; // 8-10 float coeff3[3]; // 11-13 float coefBase; // 14 some coefficient, mostly 1.0f - int32 coefLevelBase; // 15 some level + uint32 coefLevelBase; // 15 some level bool IsScalableEffect(SpellEffectIndex i) const { return coeff1[i] != 0.0f; }; }; @@ -1925,18 +2027,37 @@ struct SpellScalingEntry // SpellShapeshift.dbc struct SpellShapeshiftEntry { - //uint32 Id; // 0 m_ID - uint32 StancesNot; // 13 m_shapeshiftMask - // uint32 unk_320_2; // 14 3.2.0 - uint32 Stances; // 15 m_shapeshiftExclude - // uint32 unk_320_3; // 16 3.2.0 - // uint32 StanceBarOrder; // 155 m_stanceBarOrder not used + //uint32 Id; // 0 m_ID + uint32 StancesNot; // 1 m_shapeshiftMask + // uint32 unk_320_2; // 2 3.2.0 + uint32 Stances; // 3 m_shapeshiftExclude + // uint32 unk_320_3; // 4 3.2.0 + // uint32 StanceBarOrder; // 5 m_stanceBarOrder not used +}; + +// SpellShapeshiftForm.dbc +struct SpellShapeshiftFormEntry +{ + uint32 ID; // 0 m_ID + //uint32 buttonPosition; // 1 m_bonusActionBar + //char* Name; // 2 m_name_lang + uint32 flags1; // 3 m_flags + int32 creatureType; // 4 m_creatureType <=0 humanoid, other normal creature types + //uint32 unk1; // 5 m_attackIconID + uint32 attackSpeed; // 6 m_combatRoundTime + uint32 modelID_A; // 7 m_creatureDisplayID[4] + uint32 modelID_H; // 8 + //uint32 unk3; // 9 unused always 0 + //uint32 unk4; // 10 unused always 0 + uint32 spellId[8]; // 11-18 m_presetSpellID[8] + //uint32 unk5; // 19 unused, !=0 for flight forms + //uint32 unk6; // 20 }; // SpellTargetRestrictions.dbc struct SpellTargetRestrictionsEntry { - //uint32 Id; // 0 m_ID + //uint32 Id; // 0 m_ID float MaxTargetRadius; // 1 - m_maxTargetRadius uint32 MaxAffectedTargets; // 1 - m_maxTargets uint32 MaxTargetLevel; // 2 - m_maxTargetLevel @@ -1947,9 +2068,9 @@ struct SpellTargetRestrictionsEntry // SpellTotems.dbc struct SpellTotemsEntry { - //uint32 Id; // 0 m_ID - uint32 TotemCategory[MAX_SPELL_TOTEM_CATEGORIES]; // 162-163 m_requiredTotemCategoryID - uint32 Totem[MAX_SPELL_TOTEMS]; // 52-53 m_totem + //uint32 Id; // 0 m_ID + uint32 TotemCategory[MAX_SPELL_TOTEM_CATEGORIES]; // 1 2 m_requiredTotemCategoryID + uint32 Totem[MAX_SPELL_TOTEMS]; // 3 4 m_totem }; // Spell.dbc @@ -2116,68 +2237,6 @@ struct SpellEntry #define LOADED_SPELLDBC_FIELD_POS_EQUIPPED_ITEM_CLASS 65 // Must be converted to -1 #define LOADED_SPELLDBC_FIELD_POS_SPELLNAME_0 132 // Links to "MaNGOS server-side spell" -struct SpellCastTimesEntry -{ - uint32 ID; // 0 m_ID - int32 CastTime; // 1 m_base - float CastTimePerLevel; // 2 m_perLevel - int32 MinCastTime; // 3 m_minimum -}; - -struct SpellFocusObjectEntry -{ - uint32 ID; // 0 m_ID - //char* Name; // 1 m_name_lang -}; - -struct SpellRadiusEntry -{ - uint32 ID; // 0 m_ID - float Radius; // 1 m_radius - // 2 m_radiusPerLevel - //float RadiusMax; // 3 m_radiusMax -}; - -struct SpellRangeEntry -{ - uint32 ID; // 0 m_ID - float minRange; // 1 m_rangeMin[2] - float minRangeFriendly; // 2 - float maxRange; // 3 m_rangeMax[2] - float maxRangeFriendly; // 4 - //uint32 Flags; // 5 m_flags - //char* Name; // 6-21 m_displayName_lang - //char* ShortName; // 23-38 m_displayNameShort_lang -}; - -struct SpellRuneCostEntry -{ - uint32 ID; // 0 m_ID - uint32 RuneCost[3]; // 1-3 m_blood m_unholy m_frost (0=blood, 1=frost, 2=unholy) - uint32 runePowerGain; // 4 m_runicPower - - bool NoRuneCost() const { return RuneCost[0] == 0 && RuneCost[1] == 0 && RuneCost[2] == 0; } - bool NoRunicPowerGain() const { return runePowerGain == 0; } -}; - -struct SpellShapeshiftFormEntry -{ - uint32 ID; // 0 m_ID - //uint32 buttonPosition; // 1 m_bonusActionBar - //char* Name; // 2 m_name_lang - uint32 flags1; // 3 m_flags - int32 creatureType; // 4 m_creatureType <=0 humanoid, other normal creature types - //uint32 unk1; // 5 m_attackIconID - uint32 attackSpeed; // 6 m_combatRoundTime - uint32 modelID_A; // 7 m_creatureDisplayID[4] - uint32 modelID_H; // 8 - //uint32 unk3; // 9 unused always 0 - //uint32 unk4; // 10 unused always 0 - uint32 spellId[8]; // 11-18 m_presetSpellID[8] - //uint32 unk5; // 19 unused, !=0 for flight forms - //uint32 unk6; // 20 -}; - struct SpellDifficultyEntry { uint32 ID; // 0 m_ID @@ -2190,36 +2249,6 @@ struct SpellDurationEntry int32 Duration[3]; // m_duration, m_durationPerLevel, m_maxDuration }; -struct SpellItemEnchantmentEntry -{ - uint32 ID; // 0 m_ID - //uint32 charges; // 1 m_charges - uint32 type[3]; // 2-4 m_effect[3] - uint32 amount[3]; // 5-7 m_effectPointsMin[3] - //uint32 amount2[3] // 8-10 m_effectPointsMax[3] - uint32 spellid[3]; // 11-13 m_effectArg[3] - DBCString description; // 14 m_name_lang - uint32 aura_id; // 15 m_itemVisual - uint32 slot; // 16 m_flags - uint32 GemID; // 17 m_src_itemID - uint32 EnchantmentCondition; // 18 m_condition_id - //uint32 requiredSkill; // 19 m_requiredSkillID - //uint32 requiredSkillValue; // 20 m_requiredSkillRank - // 21 new in 3.1 - // 22 new in 3.1 -}; - -struct SpellItemEnchantmentConditionEntry -{ - uint32 ID; // 0 m_ID - uint8 Color[5]; // 1-5 m_lt_operandType[5] - //uint32 LT_Operand[5]; // 6-10 m_lt_operand[5] - uint8 Comparator[5]; // 11-15 m_operator[5] - uint8 CompareColor[5]; // 15-20 m_rt_operandType[5] - uint32 Value[5]; // 21-25 m_rt_operand[5] - //uint8 Logic[5] // 25-30 m_logic[5] -}; - struct SummonPropertiesEntry { uint32 Id; // 0 m_id diff --git a/src/game/Server/DBCfmt.h b/src/game/Server/DBCfmt.h index 7c0d0983b..7845141e8 100644 --- a/src/game/Server/DBCfmt.h +++ b/src/game/Server/DBCfmt.h @@ -44,11 +44,12 @@ const char ChrClassesEntryfmt[]="nixsxxxixiiiii"; const char ChrRacesEntryfmt[]="nxixiixixxxxixsxxxxxixxx"; const char ChrClassesXPowerTypesfmt[]="nii"; const char CinematicSequencesEntryfmt[] = "nxxxxxxxxx"; -const char CreatureDisplayInfofmt[]="nxxifxxxxxxxxxxxx"; +const char CreatureDisplayInfofmt[]="nixifxxxxxxxxxxxx"; const char CreatureDisplayInfoExtrafmt[] = "nixxxxxxxxxxxxxxxxxxx"; const char CreatureFamilyfmt[]="nfifiiiiixsx"; +const char CreatureModelDatafmt[] = "nxxxxxxxxxxxxxxffxxxxxxxxxxxxxx"; const char CreatureSpellDatafmt[] = "niiiixxxx"; -const char DestructibleModelDataFmt[] = "nixxxixxxxixxxxixxxxxxxx"; +const char DestructibleModelDataFmt[] = "nixxxixxxxixxxxixxxxixxx"; const char DungeonEncounterfmt[]="niiiisxx"; const char CreatureTypefmt[]="nxx"; const char CurrencyTypesfmt[]="nisxxxxiiix"; diff --git a/src/game/Server/Opcodes.cpp b/src/game/Server/Opcodes.cpp index 9f0009000..a2982751f 100644 --- a/src/game/Server/Opcodes.cpp +++ b/src/game/Server/Opcodes.cpp @@ -856,14 +856,14 @@ void InitializeOpcodes() //OPCODE(SMSG_SPLINE_SET_SWIM_BACK_SPEED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); //OPCODE(SMSG_SPLINE_SET_TURN_RATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); OPCODE(SMSG_SPLINE_MOVE_UNROOT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_SPLINE_MOVE_FEATHER_FALL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_SPLINE_MOVE_NORMAL_FALL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); + OPCODE(SMSG_SPLINE_MOVE_FEATHER_FALL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); + OPCODE(SMSG_SPLINE_MOVE_NORMAL_FALL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); OPCODE(SMSG_SPLINE_MOVE_SET_HOVER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); OPCODE(SMSG_SPLINE_MOVE_UNSET_HOVER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); OPCODE(SMSG_SPLINE_MOVE_WATER_WALK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); OPCODE(SMSG_SPLINE_MOVE_LAND_WALK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); OPCODE(SMSG_SPLINE_MOVE_START_SWIM, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(SMSG_SPLINE_MOVE_STOP_SWIM, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); + OPCODE(SMSG_SPLINE_MOVE_STOP_SWIM, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); OPCODE(SMSG_SPLINE_MOVE_SET_RUN_MODE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); OPCODE(SMSG_SPLINE_MOVE_SET_WALK_MODE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); //OPCODE(CMSG_GM_NUKE_ACCOUNT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); @@ -1268,7 +1268,7 @@ void InitializeOpcodes() OPCODE(SMSG_SERVER_FIRST_ACHIEVEMENT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); OPCODE(SMSG_PET_LEARNED_SPELL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); OPCODE(SMSG_PET_REMOVED_SPELL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleChangeSeatsOnControlledVehicle ); + OPCODE(CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleChangeSeatsOnControlledVehicle ); OPCODE(CMSG_HEARTH_AND_RESURRECT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleHearthandResurrect ); OPCODE(SMSG_ON_CANCEL_EXPECTED_RIDE_VEHICLE_AURA, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); OPCODE(SMSG_CRITERIA_DELETED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); @@ -1319,9 +1319,9 @@ void InitializeOpcodes() //OPCODE(CMSG_START_BATTLEFIELD_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); //OPCODE(CMSG_END_BATTLEFIELD_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); //OPCODE(SMSG_COMPOUND_MOVE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - OPCODE(SMSG_MOVE_GRAVITY_DISABLE, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); + OPCODE(SMSG_MOVE_GRAVITY_DISABLE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); OPCODE(CMSG_MOVE_GRAVITY_DISABLE_ACK, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - OPCODE(SMSG_MOVE_GRAVITY_ENABLE, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); + OPCODE(SMSG_MOVE_GRAVITY_ENABLE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); OPCODE(CMSG_MOVE_GRAVITY_ENABLE_ACK, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); //OPCODE(MSG_MOVE_GRAVITY_CHNG, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); OPCODE(SMSG_SPLINE_MOVE_GRAVITY_DISABLE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); @@ -1392,7 +1392,7 @@ void InitializeOpcodes() //OPCODE(SMSG_SEND_ALL_COMBAT_LOG, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); OPCODE(SMSG_OPEN_LFG_DUNGEON_FINDER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); OPCODE(SMSG_MOVE_SET_COLLISION_HGT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //OPCODE(CMSG_MOVE_SET_COLLISION_HGT_ACK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); + OPCODE(CMSG_MOVE_SET_COLLISION_HGT_ACK, STATUS_UNHANDLED,PROCESS_INPLACE, &WorldSession::Handle_NULL ); //OPCODE(MSG_MOVE_SET_COLLISION_HGT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); //OPCODE(CMSG_CLEAR_RANDOM_BG_WIN_TIME, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); //OPCODE(CMSG_CLEAR_HOLIDAY_BG_WIN_TIME, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); diff --git a/src/game/Server/Opcodes.h b/src/game/Server/Opcodes.h index 0301f3ebd..b4405d0ac 100644 --- a/src/game/Server/Opcodes.h +++ b/src/game/Server/Opcodes.h @@ -840,8 +840,8 @@ enum Opcodes SMSG_SPLINE_SET_SWIM_BACK_SPEED = 0x1303, SMSG_SPLINE_SET_TURN_RATE = 0x1304, SMSG_SPLINE_MOVE_UNROOT = 0x75B6, // 4.3.4 15595 - SMSG_SPLINE_MOVE_FEATHER_FALL = 0x1306, - SMSG_SPLINE_MOVE_NORMAL_FALL = 0x1307, + SMSG_SPLINE_MOVE_FEATHER_FALL = 0x3DA5, // 4.3.4 15595 + SMSG_SPLINE_MOVE_NORMAL_FALL = 0x38B2, // 4.3.4 15595 SMSG_SPLINE_MOVE_SET_HOVER = 0x14B6, // 4.3.4 15595 SMSG_SPLINE_MOVE_UNSET_HOVER = 0x7DA5, // 4.3.4 15595 SMSG_SPLINE_MOVE_WATER_WALK = 0x50A2, // 4.3.4 15595 @@ -1238,7 +1238,7 @@ enum Opcodes CMSG_REMOVE_GLYPH = 0x148B, CMSG_DUMP_OBJECTS = 0x148C, SMSG_DUMP_OBJECTS_DATA = 0x148D, - CMSG_DISMISS_CRITTER = 0x4227, + CMSG_DISMISS_CRITTER = 0x4227, // 4.3.4 15595 SMSG_NOTIFY_DEST_LOC_SPELL_CAST = 0x148F, CMSG_AUCTION_LIST_PENDING_SALES = 0x2C17, // 4.3.4 15595 SMSG_AUCTION_LIST_PENDING_SALES = 0x6A27, // 4.3.4 15595 @@ -1264,9 +1264,9 @@ enum Opcodes CMSG_SET_BREATH = 0x14A5, CMSG_QUERY_VEHICLE_STATUS = 0x14A6, SMSG_BATTLEGROUND_INFO_THROTTLED = 0x14A7, - SMSG_SET_VEHICLE_REC_ID = 0x14A8, - CMSG_RIDE_VEHICLE_INTERACT = 0x14A9, - CMSG_CONTROLLER_EJECT_PASSENGER = 0x14AA, + SMSG_SET_VEHICLE_REC_ID = 0x4115, // 4.3.4 15595 + CMSG_RIDE_VEHICLE_INTERACT = 0x2705, // 4.3.4 15595 + CMSG_CONTROLLER_EJECT_PASSENGER = 0x6927, // 4.3.4 15595 SMSG_PET_GUIDS = 0x2D26, // 4.3.4 15595 SMSG_CLIENTCACHE_VERSION = 0x2734, // 4.3.4 15595 CMSG_CHANGE_GDF_ARENA_RATING = 0x14AD, @@ -1376,7 +1376,7 @@ enum Opcodes SMSG_SEND_ALL_COMBAT_LOG = 0x1515, SMSG_OPEN_LFG_DUNGEON_FINDER = 0x0412, // 4.3.4 15595 SMSG_MOVE_SET_COLLISION_HGT = 0x11B0, // 4.3.4 15595 - CMSG_MOVE_SET_COLLISION_HGT_ACK = 0x1518, + CMSG_MOVE_SET_COLLISION_HGT_ACK = 0x7114, // 4.3.4 15595 MSG_MOVE_SET_COLLISION_HGT = 0x1519, CMSG_CLEAR_RANDOM_BG_WIN_TIME = 0x151A, CMSG_CLEAR_HOLIDAY_BG_WIN_TIME = 0x151B, diff --git a/src/game/Server/SharedDefines.h b/src/game/Server/SharedDefines.h index 9bf7c830c..1fa333c37 100644 --- a/src/game/Server/SharedDefines.h +++ b/src/game/Server/SharedDefines.h @@ -451,7 +451,7 @@ enum SpellAttributesEx4 enum SpellAttributesEx5 { - SPELL_ATTR_EX5_UNK0 = 0x00000001,// 0 + SPELL_ATTR_EX5_CAN_CHANNEL_WHEN_MOVING = 0x00000001,// 0 don't interrupt channeling spells when moving SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP = 0x00000002,// 1 not need reagents if UNIT_FLAG_PREPARATION SPELL_ATTR_EX5_UNK2 = 0x00000004,// 2 removed at enter arena (e.g. 31850 since 3.3.3) SPELL_ATTR_EX5_USABLE_WHILE_STUNNED = 0x00000008,// 3 usable while stunned @@ -3792,4 +3792,14 @@ enum TeleportLocation TELEPORT_LOCATION_BG_ENTRY_POINT = 1, }; +// For Loot system +enum CreatureLootStatus +{ + CREATURE_LOOT_STATUS_NONE = 0, + CREATURE_LOOT_STATUS_PICKPOCKETED = 1, + CREATURE_LOOT_STATUS_LOOTED = 2, + CREATURE_LOOT_STATUS_SKIN_AVAILABLE = 3, + CREATURE_LOOT_STATUS_SKINNED = 4 +}; + #endif diff --git a/src/game/Server/WorldSession.h b/src/game/Server/WorldSession.h index 14d9ac190..a701b5162 100644 --- a/src/game/Server/WorldSession.h +++ b/src/game/Server/WorldSession.h @@ -677,6 +677,7 @@ class WorldSession void HandleGossipHelloOpcode(WorldPacket& recvPacket); void HandleGossipSelectOptionOpcode(WorldPacket& recvPacket); void HandleSpiritHealerActivateOpcode(WorldPacket& recvPacket); + void HandleReturnToGraveyardOpcode(WorldPacket& recvPacket); void HandleNpcTextQueryOpcode(WorldPacket& recvPacket); void HandleBinderActivateOpcode(WorldPacket& recvPacket); void HandleListStabledPetsOpcode(WorldPacket& recvPacket); diff --git a/src/game/WorldHandlers/AccountMgr.cpp b/src/game/WorldHandlers/AccountMgr.cpp index 96577512d..f30a0688a 100644 --- a/src/game/WorldHandlers/AccountMgr.cpp +++ b/src/game/WorldHandlers/AccountMgr.cpp @@ -232,6 +232,7 @@ bool AccountMgr::CheckPassword(uint32 accid, std::string passwd) return false; normalizeString(passwd); + normalizeString(username); QueryResult* result = LoginDatabase.PQuery("SELECT 1 FROM account WHERE id='%u' AND sha_pass_hash='%s'", accid, CalculateShaPassHash(username, passwd).c_str()); if (result) diff --git a/src/game/WorldHandlers/ArenaTeamHandler.cpp b/src/game/WorldHandlers/ArenaTeamHandler.cpp index ba6cf4b5e..68cb0e534 100644 --- a/src/game/WorldHandlers/ArenaTeamHandler.cpp +++ b/src/game/WorldHandlers/ArenaTeamHandler.cpp @@ -40,15 +40,22 @@ void WorldSession::HandleInspectArenaTeamsOpcode(WorldPacket& recv_data) recv_data >> guid; DEBUG_LOG("Inspect Arena stats %s", guid.GetString().c_str()); - if (Player* plr = sObjectMgr.GetPlayer(guid)) + Player* player = sObjectMgr.GetPlayer(guid); + if (!player) + return; + + if (!_player->IsWithinDistInMap(player, INSPECT_DISTANCE, false)) + return; + + if (_player->IsHostileTo(player)) + return; + + for (uint8 i = 0; i < MAX_ARENA_SLOT; ++i) { - for (uint8 i = 0; i < MAX_ARENA_SLOT; ++i) + if (uint32 a_id = player->GetArenaTeamId(i)) { - if (uint32 a_id = plr->GetArenaTeamId(i)) - { - if (ArenaTeam* at = sObjectMgr.GetArenaTeamById(a_id)) - at->InspectStats(this, plr->GetObjectGuid()); - } + if (ArenaTeam* arenaTeam = sObjectMgr.GetArenaTeamById(a_id)) + arenaTeam->InspectStats(this, player->GetObjectGuid()); } } } diff --git a/src/game/WorldHandlers/Chat.cpp b/src/game/WorldHandlers/Chat.cpp index 91052a51c..35d881a36 100644 --- a/src/game/WorldHandlers/Chat.cpp +++ b/src/game/WorldHandlers/Chat.cpp @@ -311,7 +311,7 @@ ChatCommand* ChatHandler::getCommandTable() { "setphase", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectPhaseCommand, "", NULL }, { "target", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectTargetCommand, "", NULL }, { "turn", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectTurnCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } + { nullptr, 0, false, NULL, "", NULL } }; static ChatCommand guildCommandTable[] = @@ -413,6 +413,7 @@ ChatCommand* ChatHandler::getCommandTable() { "loadedtiles", SEC_GAMEMASTER, false, &ChatHandler::HandleMmapLoadedTilesCommand, "", NULL }, { "stats", SEC_GAMEMASTER, false, &ChatHandler::HandleMmapStatsCommand, "", NULL }, { "testarea", SEC_GAMEMASTER, false, &ChatHandler::HandleMmapTestArea, "", NULL }, + { "testheight", SEC_GAMEMASTER, false, &ChatHandler::HandleMmapTestHeight, "", NULL }, { "", SEC_ADMINISTRATOR, false, &ChatHandler::HandleMmap, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; diff --git a/src/game/WorldHandlers/Chat.h b/src/game/WorldHandlers/Chat.h index 044f1b661..8c472301e 100644 --- a/src/game/WorldHandlers/Chat.h +++ b/src/game/WorldHandlers/Chat.h @@ -639,6 +639,7 @@ class ChatHandler bool HandleMmapStatsCommand(char* args); bool HandleMmap(char* args); bool HandleMmapTestArea(char* args); + bool HandleMmapTestHeight(char* args); //! Development Commands bool HandleSaveAllCommand(char* args); diff --git a/src/game/WorldHandlers/CreatureLinkingMgr.cpp b/src/game/WorldHandlers/CreatureLinkingMgr.cpp index d41036085..28a7dac72 100644 --- a/src/game/WorldHandlers/CreatureLinkingMgr.cpp +++ b/src/game/WorldHandlers/CreatureLinkingMgr.cpp @@ -173,7 +173,14 @@ void CreatureLinkingMgr::LoadFromDB() delete result; } -// This function is used to check if a DB-Entry is valid +/** This function is used to check if a DB-Entry is valid + * + * @param byEntry: is the first parameter of the function a npc entry or a npc guid? + * @param slaveEntry: dependend on byEntry param this is either the slave's npc-entry or the npc-guid + * @param pTmp: Information about the CreatureLinking of the npc. Note that this information may be changed in some cases + * + * In case of checking by entry and in case of linked spawning and searchRange == 0, pTmp will be changed to keep information about the (unique!) master's db-guid + */ bool CreatureLinkingMgr::IsLinkingEntryValid(uint32 slaveEntry, CreatureLinkingInfo* pTmp, bool byEntry) { // Basic checks first @@ -238,14 +245,22 @@ bool CreatureLinkingMgr::IsLinkingEntryValid(uint32 slaveEntry, CreatureLinkingI // Check for uniqueness of mob whom is followed, on whom spawning is dependend if (pTmp->searchRange == 0 && pTmp->linkingFlag & (FLAG_FOLLOW | FLAG_CANT_SPAWN_IF_BOSS_DEAD | FLAG_CANT_SPAWN_IF_BOSS_ALIVE)) { - // Painfully slow, needs better idea - QueryResult* result = WorldDatabase.PQuery("SELECT COUNT(guid) FROM creature WHERE id=%u AND map=%u", pTmp->masterId, pTmp->mapId); - if (result) + QueryResult* result = WorldDatabase.PQuery("SELECT guid FROM creature WHERE id=%u AND map=%u LIMIT 2", pTmp->masterId, pTmp->mapId); + if (!result) { - if ((*result)[0].GetUInt32() > 1) - sLog.outErrorDb("`creature_linking_template` has FLAG_FOLLOW, but non unique master, (entry: %u, map: %u, master: %u)", slaveEntry, pTmp->mapId, pTmp->masterId); - delete result; + sLog.outErrorDb("`creature_linking_template` has FLAG_FOLLOW, but no master, (entry: %u, map: %u, master: %u)", slaveEntry, pTmp->mapId, pTmp->masterId); + return false; } + + if (result->GetRowCount() > 1) + { + sLog.outErrorDb("`creature_linking_template` has FLAG_FOLLOW, but non unique master, (entry: %u, map: %u, master: %u)", slaveEntry, pTmp->mapId, pTmp->masterId); + delete result; + return false; + } + Field* fields = result->Fetch(); + pTmp->masterDBGuid = fields[0].GetUInt32(); + delete result; } } @@ -265,7 +280,7 @@ enum EventMask }; // This functions checks if the NPC has linked NPCs for dynamic action -bool CreatureLinkingMgr::IsLinkedEventTrigger(Creature* pCreature) +bool CreatureLinkingMgr::IsLinkedEventTrigger(Creature* pCreature) const { // Entry case if (m_eventTriggers.find(pCreature->GetEntry()) != m_eventTriggers.end()) @@ -284,37 +299,43 @@ bool CreatureLinkingMgr::IsLinkedEventTrigger(Creature* pCreature) // This function check if the NPC is a master to other NPCs // return true only for masters stored by entry - this prevents adding them to master-holder maps -bool CreatureLinkingMgr::IsLinkedMaster(Creature* pCreature) +bool CreatureLinkingMgr::IsLinkedMaster(Creature* pCreature) const { return m_eventTriggers.find(pCreature->GetEntry()) != m_eventTriggers.end(); } // This function checks if the spawning of this NPC is dependend on other NPCs -bool CreatureLinkingMgr::IsSpawnedByLinkedMob(Creature* pCreature) +bool CreatureLinkingMgr::IsSpawnedByLinkedMob(Creature* pCreature) const +{ + return IsSpawnedByLinkedMob(GetLinkedTriggerInformation(pCreature)); +} +bool CreatureLinkingMgr::IsSpawnedByLinkedMob(CreatureLinkingInfo const* pInfo) const { - CreatureLinkingInfo const* pInfo = CreatureLinkingMgr::GetLinkedTriggerInformation(pCreature); - return pInfo && pInfo->linkingFlag & (FLAG_CANT_SPAWN_IF_BOSS_DEAD | FLAG_CANT_SPAWN_IF_BOSS_ALIVE) && (pInfo->masterDBGuid || pInfo->searchRange); } // This gives the information of a linked NPC (describes action when its ActionTrigger triggers) // Depends of the map -CreatureLinkingInfo const* CreatureLinkingMgr::GetLinkedTriggerInformation(Creature* pCreature) +CreatureLinkingInfo const* CreatureLinkingMgr::GetLinkedTriggerInformation(Creature* pCreature) const +{ + return GetLinkedTriggerInformation(pCreature->GetEntry(), pCreature->GetGUIDLow(), pCreature->GetMapId()); +} +CreatureLinkingInfo const* CreatureLinkingMgr::GetLinkedTriggerInformation(uint32 entry, uint32 lowGuid, uint32 mapId) const { // guid case - CreatureLinkingMapBounds bounds = m_creatureLinkingGuidMap.equal_range(pCreature->GetGUIDLow()); - for (CreatureLinkingMap::const_iterator iter = bounds.first; iter != bounds.second; ++iter) + CreatureLinkingMapBounds bounds = m_creatureLinkingGuidMap.equal_range(lowGuid); + for (CreatureLinkingMap::const_iterator iter = bounds.first; iter != bounds.second;) return &(iter->second); // entry case - bounds = m_creatureLinkingMap.equal_range(pCreature->GetEntry()); + bounds = m_creatureLinkingMap.equal_range(entry); for (CreatureLinkingMap::const_iterator iter = bounds.first; iter != bounds.second; ++iter) { - if (iter->second.mapId == pCreature->GetMapId()) + if (iter->second.mapId == mapId) return &(iter->second); } - return NULL; + return nullptr; } // Function to add slave-NPCs to the holder @@ -384,7 +405,7 @@ void CreatureLinkingHolder::AddMasterToHolder(Creature* pCreature) // Check, if already stored BossGuidMapBounds bounds = m_masterGuid.equal_range(pCreature->GetEntry()); - for (BossGuidMap::iterator itr = bounds.first; itr != bounds.second; ++itr) + for (BossGuidMap::const_iterator itr = bounds.first; itr != bounds.second; ++itr) if (itr->second == pCreature->GetObjectGuid()) return; // Already added @@ -432,11 +453,11 @@ void CreatureLinkingHolder::DoCreatureLinkingEvent(CreatureLinkingEvent eventTyp { if (pInfo->linkingFlag & reverseEventFlagFilter) { - Creature* pMaster = NULL; + Creature* pMaster = nullptr; if (pInfo->mapId != INVALID_MAP_ID) // entry case { BossGuidMapBounds finds = m_masterGuid.equal_range(pInfo->masterId); - for (BossGuidMap::iterator itr = finds.first; itr != finds.second; ++itr) + for (BossGuidMap::const_iterator itr = finds.first; itr != finds.second; ++itr) { pMaster = pSource->GetMap()->GetCreature(itr->second); if (pMaster && IsSlaveInRangeOfBoss(pSource, pMaster, pInfo->searchRange)) @@ -589,49 +610,93 @@ void CreatureLinkingHolder::SetFollowing(Creature* pWho, Creature* pWhom) } // Function to check if a slave belongs to a boss by range-issue -bool CreatureLinkingHolder::IsSlaveInRangeOfBoss(Creature* pSlave, Creature* pBoss, uint16 searchRange) +bool CreatureLinkingHolder::IsSlaveInRangeOfBoss(Creature const* pSlave, Creature const* pBoss, uint16 searchRange) const +{ + float sX, sY, sZ; + pSlave->GetRespawnCoord(sX, sY, sZ); + return IsSlaveInRangeOfBoss(pBoss, sX, sY, searchRange); +} +bool CreatureLinkingHolder::IsSlaveInRangeOfBoss(Creature const* pBoss, float sX, float sY, uint16 searchRange) const { if (!searchRange) return true; // Do some calculations - float sX, sY, sZ, mX, mY, mZ; - pSlave->GetRespawnCoord(sX, sY, sZ); + float mX, mY, mZ, dx, dy; pBoss->GetRespawnCoord(mX, mY, mZ); - float dx, dy; dx = sX - mX; dy = sY - mY; return dx * dx + dy * dy < searchRange * searchRange; } +// helper function to check if a lowguid can respawn +bool CreatureLinkingHolder::IsRespawnReady(uint32 dbLowGuid, Map* _map) const +{ + time_t respawnTime = _map->GetPersistentState()->GetCreatureRespawnTime(dbLowGuid); + return (!respawnTime || respawnTime <= time(nullptr)) && CanSpawn(dbLowGuid, _map, nullptr, 0.0f, 0.0f); +} + // Function to check if a passive spawning condition is met -bool CreatureLinkingHolder::CanSpawn(Creature* pCreature) +bool CreatureLinkingHolder::CanSpawn(Creature* pCreature) const { CreatureLinkingInfo const* pInfo = sCreatureLinkingMgr.GetLinkedTriggerInformation(pCreature); if (!pInfo) return true; + float sx, sy, sz; + pCreature->GetRespawnCoord(sx, sy, sz); + return CanSpawn(0, pCreature->GetMap(), pInfo, sx, sy); +} + +/** Worker function to check if a spawning condition is met + * + * This function is used directly from above function, and for recursive use + * in case of recursive use it is used only on _map with information of lowGuid. + * + * @param lowGuid (only relevant in case of recursive uses) -- db-guid of the npc that is checked + * @param _map Map on which things are checked + * @param pInfo (only shipped in case of initial use) -- used as marker of first use, also in first use filled directly + * @param sx, sy (spawn position of the checked npc with initial use) + */ +bool CreatureLinkingHolder::CanSpawn(uint32 lowGuid, Map* _map, CreatureLinkingInfo const* pInfo, float sx, float sy) const +{ + if (!pInfo) // Prepare data for recursive use + { + CreatureData const* data = sObjectMgr.GetCreatureData(lowGuid); + if (!data) + return true; + pInfo = sCreatureLinkingMgr.GetLinkedTriggerInformation(data->id, lowGuid, data->mapid); + if (!pInfo) + return true; + // Has lowGuid npc actually spawning linked? + if (!sCreatureLinkingMgr.IsSpawnedByLinkedMob(pInfo)) + return true; + + sx = data->posX; // Fill position data + sy = data->posY; + } + if (pInfo->searchRange == 0) // Map wide case { if (!pInfo->masterDBGuid) return false; // This should never happen if (pInfo->linkingFlag & FLAG_CANT_SPAWN_IF_BOSS_DEAD) - return pCreature->GetMap()->GetPersistentState()->GetCreatureRespawnTime(pInfo->masterDBGuid) == 0; + return IsRespawnReady(pInfo->masterDBGuid, _map); else if (pInfo->linkingFlag & FLAG_CANT_SPAWN_IF_BOSS_ALIVE) - return pCreature->GetMap()->GetPersistentState()->GetCreatureRespawnTime(pInfo->masterDBGuid) > 0; + return !IsRespawnReady(pInfo->masterDBGuid, _map); else return true; } // Search for nearby master BossGuidMapBounds finds = m_masterGuid.equal_range(pInfo->masterId); - for (BossGuidMap::iterator itr = finds.first; itr != finds.second; ++itr) + for (BossGuidMap::const_iterator itr = finds.first; itr != finds.second; ++itr) { - Creature* pMaster = pCreature->GetMap()->GetCreature(itr->second); - if (pMaster && IsSlaveInRangeOfBoss(pCreature, pMaster, pInfo->searchRange)) + Creature* pMaster = _map->GetCreature(itr->second); + if (pMaster && IsSlaveInRangeOfBoss(pMaster, sx, sy, pInfo->searchRange)) { if (pInfo->linkingFlag & FLAG_CANT_SPAWN_IF_BOSS_DEAD) return pMaster->IsAlive(); @@ -652,11 +717,11 @@ bool CreatureLinkingHolder::TryFollowMaster(Creature* pCreature) if (!pInfo || !(pInfo->linkingFlag & FLAG_FOLLOW)) return false; - Creature* pMaster = NULL; + Creature* pMaster = nullptr; if (pInfo->mapId != INVALID_MAP_ID) // entry case { BossGuidMapBounds finds = m_masterGuid.equal_range(pInfo->masterId); - for (BossGuidMap::iterator itr = finds.first; itr != finds.second; ++itr) + for (BossGuidMap::const_iterator itr = finds.first; itr != finds.second; ++itr) { pMaster = pCreature->GetMap()->GetCreature(itr->second); if (pMaster && IsSlaveInRangeOfBoss(pCreature, pMaster, pInfo->searchRange)) diff --git a/src/game/WorldHandlers/CreatureLinkingMgr.h b/src/game/WorldHandlers/CreatureLinkingMgr.h index c99d7540e..abebda67c 100644 --- a/src/game/WorldHandlers/CreatureLinkingMgr.h +++ b/src/game/WorldHandlers/CreatureLinkingMgr.h @@ -48,6 +48,7 @@ class Unit; class Creature; +class Map; // enum on which Events an action for linked NPCs can trigger enum CreatureLinkingEvent @@ -111,17 +112,19 @@ class CreatureLinkingMgr public: // Accessors // This functions checks if the NPC triggers actions for other NPCs - bool IsLinkedEventTrigger(Creature* pCreature); + bool IsLinkedEventTrigger(Creature* pCreature) const; // This function checks if the NPC is a master NPC. - bool IsLinkedMaster(Creature* pCreature); + bool IsLinkedMaster(Creature* pCreature) const; // This function checks if the spawning of this NPC is dependend on other NPCs - bool IsSpawnedByLinkedMob(Creature* pCreature); + bool IsSpawnedByLinkedMob(Creature* pCreature) const; + bool IsSpawnedByLinkedMob(CreatureLinkingInfo const* pInfo) const; // This gives the information of a linked NPC (describes action when its ActionTrigger triggers) // Depends of the map - CreatureLinkingInfo const* GetLinkedTriggerInformation(Creature* pCreature); + CreatureLinkingInfo const* GetLinkedTriggerInformation(Creature* pCreature) const; + CreatureLinkingInfo const* GetLinkedTriggerInformation(uint32 entry, uint32 lowGuid, uint32 mapId) const; private: typedef std::multimap < uint32 /*slaveEntry*/, CreatureLinkingInfo > CreatureLinkingMap; @@ -162,7 +165,7 @@ class CreatureLinkingHolder void DoCreatureLinkingEvent(CreatureLinkingEvent eventType, Creature* pSource, Unit* pEnemy = NULL); // Function to check if a passive spawning condition is met - bool CanSpawn(Creature* pCreature); + bool CanSpawn(Creature* pCreature) const; // This function lets a slave refollow his master bool TryFollowMaster(Creature* pCreature); @@ -185,7 +188,7 @@ class CreatureLinkingHolder typedef std::multimap < uint32 /*masterEntryOrGuid*/, InfoAndGuids > HolderMap; typedef std::pair HolderMapBounds; typedef std::multimap < uint32 /*Entry*/, ObjectGuid > BossGuidMap; - typedef std::pair BossGuidMapBounds; + typedef std::pair BossGuidMapBounds; // Helper function, to process a slave list void ProcessSlaveGuidList(CreatureLinkingEvent eventType, Creature* pSource, uint32 flag, uint16 searchRange, GuidList& slaveGuidList, Unit* pEnemy); @@ -194,7 +197,12 @@ class CreatureLinkingHolder // Helper function to set following void SetFollowing(Creature* pWho, Creature* pWhom); // Helper function to return if a slave is in range of a boss - bool IsSlaveInRangeOfBoss(Creature* pSlave, Creature* pBoss, uint16 searchRange); + bool IsSlaveInRangeOfBoss(Creature const* pSlave, Creature const* pBoss, uint16 searchRange) const; + bool IsSlaveInRangeOfBoss(Creature const* pBoss, float slaveX, float slaveY, uint16 searchRange) const; + // Another helper function + bool IsRespawnReady(uint32 dbLowGuid, Map* _map) const; + // Helper function for recursive spawning-checks of an additional linked + bool CanSpawn(uint32 lowGuid, Map* _map, CreatureLinkingInfo const* pInfo, float sx, float sy) const; // Storage of Data (boss, flag, searchRange, GuidList) for action triggering HolderMap m_holderMap; diff --git a/src/game/WorldHandlers/GridDefines.h b/src/game/WorldHandlers/GridDefines.h index 1a528c7ab..042e950e6 100644 --- a/src/game/WorldHandlers/GridDefines.h +++ b/src/game/WorldHandlers/GridDefines.h @@ -174,7 +174,7 @@ namespace MaNGOS inline bool IsValidMapCoord(float c) { - return finite(c) && (std::fabs(c) <= MAP_HALFSIZE - 0.5); + return std::isfinite(c) && (std::fabs(c) <= MAP_HALFSIZE - 0.5); } inline bool IsValidMapCoord(float x, float y) @@ -184,12 +184,12 @@ namespace MaNGOS inline bool IsValidMapCoord(float x, float y, float z) { - return IsValidMapCoord(x, y) && finite(z); + return IsValidMapCoord(x, y) && std::isfinite(z); } inline bool IsValidMapCoord(float x, float y, float z, float o) { - return IsValidMapCoord(x, y, z) && finite(o); + return IsValidMapCoord(x, y, z) && std::isfinite(o); } } #endif diff --git a/src/game/WorldHandlers/GridMap.cpp b/src/game/WorldHandlers/GridMap.cpp index 954219ed6..87972d449 100644 --- a/src/game/WorldHandlers/GridMap.cpp +++ b/src/game/WorldHandlers/GridMap.cpp @@ -42,6 +42,9 @@ char const* MAP_AREA_MAGIC = "AREA"; char const* MAP_HEIGHT_MAGIC = "MHGT"; char const* MAP_LIQUID_MAGIC = "MLIQ"; +static uint16 holetab_h[4] = { 0x1111, 0x2222, 0x4444, 0x8888 }; +static uint16 holetab_v[4] = { 0x000F, 0x00F0, 0x0F00, 0xF000 }; + GridMap::GridMap() { m_flags = 0; @@ -1061,6 +1064,23 @@ bool TerrainInfo::IsInWater(float x, float y, float pZ, GridMapLiquidData* data) return false; } +// check if creature is in water and have enough space to swim +bool TerrainInfo::IsSwimmable(float x, float y, float pZ, float radius /*= 1.5f*/, GridMapLiquidData* data /*= 0*/) const +{ + // Check surface in x, y point for liquid + if (const_cast(this)->GetGrid(x, y)) + { + GridMapLiquidData liquid_status; + GridMapLiquidData* liquid_ptr = data ? data : &liquid_status; + if (getLiquidStatus(x, y, pZ, MAP_ALL_LIQUIDS, liquid_ptr)) + { + if (liquid_ptr->level - liquid_ptr->depth_level > radius) // is unit have enough space to swim + return true; + } + } + return false; +} + bool TerrainInfo::IsUnderWater(float x, float y, float z) const { if (const_cast(this)->GetGrid(x, y)) diff --git a/src/game/WorldHandlers/GridMap.h b/src/game/WorldHandlers/GridMap.h index 52e928609..3bb9bc023 100644 --- a/src/game/WorldHandlers/GridMap.h +++ b/src/game/WorldHandlers/GridMap.h @@ -231,6 +231,7 @@ class TerrainInfo : public Referencable float GetWaterLevel(float x, float y, float z, float* pGround = NULL) const; float GetWaterOrGroundLevel(float x, float y, float z, float* pGround = NULL, bool swim = false) const; bool IsInWater(float x, float y, float z, GridMapLiquidData* data = 0) const; + bool IsSwimmable(float x, float y, float pZ, float radius = 1.5f, GridMapLiquidData* data = nullptr) const; bool IsUnderWater(float x, float y, float z) const; GridMapLiquidStatus getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, GridMapLiquidData* data = 0) const; diff --git a/src/game/WorldHandlers/Group.h b/src/game/WorldHandlers/Group.h index c7b749d14..858a85c55 100644 --- a/src/game/WorldHandlers/Group.h +++ b/src/game/WorldHandlers/Group.h @@ -57,7 +57,9 @@ enum LootMethod ROUND_ROBIN = 1, MASTER_LOOT = 2, GROUP_LOOT = 3, - NEED_BEFORE_GREED = 4 + NEED_BEFORE_GREED = 4, + + NOT_GROUP_TYPE_LOOT = 5 // internal use only }; enum RollVote diff --git a/src/game/WorldHandlers/MailHandler.cpp b/src/game/WorldHandlers/MailHandler.cpp index 331785fba..5dd4347de 100644 --- a/src/game/WorldHandlers/MailHandler.cpp +++ b/src/game/WorldHandlers/MailHandler.cpp @@ -71,7 +71,7 @@ bool WorldSession::CheckMailBox(ObjectGuid guid) Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE); if (!creature) { - DEBUG_LOG("%s not found or %s can't interact with him.", creature->GetGuidStr().c_str(), GetPlayer()->GetGuidStr().c_str()); + DEBUG_LOG("%s not found or %s can't interact with him.", guid.GetString().c_str(), GetPlayer()->GetGuidStr().c_str()); return false; } diff --git a/src/game/WorldHandlers/Map.cpp b/src/game/WorldHandlers/Map.cpp index 246a87819..7fb465a4f 100644 --- a/src/game/WorldHandlers/Map.cpp +++ b/src/game/WorldHandlers/Map.cpp @@ -298,12 +298,15 @@ bool Map::EnsureGridLoaded(const Cell& cell) return false; } -void Map::LoadGrid(const Cell& cell, bool no_unload) +void Map::ForceLoadGrid(float x, float y) { - EnsureGridLoaded(cell); - - if (no_unload) + if (!IsLoaded(x, y)) + { + CellPair p = MaNGOS::ComputeCellPair(x, y); + Cell cell(p); + EnsureGridLoadedAtEnter(cell); getNGrid(cell.GridX(), cell.GridY())->setUnloadExplicitLock(true); + } } bool Map::Add(Player* player) @@ -2116,6 +2119,61 @@ bool Map::GetHitPosition(float srcX, float srcY, float srcZ, float& destX, float return result0 || result1; } +// Find an height within a reasonable range of provided Z. This method may fail so we have to handle that case. +bool Map::GetHeightInRange(uint32 phasemask, float x, float y, float& z, float maxSearchDist /*= 4.0f*/) const +{ + float height, vmapHeight, mapHeight; + vmapHeight = VMAP_INVALID_HEIGHT_VALUE; + + VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); + if (!vmgr->isLineOfSightCalcEnabled()) + vmgr = nullptr; + + if (vmgr) + { + // pure vmap search + vmapHeight = vmgr->getHeight(i_id, x, y, z + 2.0f, maxSearchDist + 2.0f); + } + + // find raw height from .map file on X,Y coordinates + if (GridMap* gmap = const_cast(m_TerrainData)->GetGrid(x, y)) // TODO:: find a way to remove that const_cast + mapHeight = gmap->getHeight(x, y); + + float diffMaps = fabs(fabs(z) - fabs(mapHeight)); + float diffVmaps = fabs(fabs(z) - fabs(vmapHeight)); + if (diffVmaps < maxSearchDist) + { + if (diffMaps < maxSearchDist) + { + // well we simply have to take the highest as normally there we cannot be on top of cavern is maxSearchDist is not too big + if (vmapHeight > mapHeight) + height = vmapHeight; + else + height = mapHeight; + + //sLog.outString("vmap %5.4f, map %5.4f, height %5.4f", vmapHeight, mapHeight, height); + } + else + { + //sLog.outString("vmap %5.4f", vmapHeight); + height = vmapHeight; + } + } + else + { + if (diffMaps < maxSearchDist) + { + //sLog.outString("map %5.4f", mapHeight); + height = mapHeight; + } + else + return false; + } + + z = std::max(height, m_dyn_tree.getHeight(x, y, height + 1.0f, maxSearchDist, phasemask)); + return true; +} + float Map::GetHeight(uint32 phasemask, float x, float y, float z) const { float staticHeight = m_TerrainData->GetHeightStatic(x, y, z); diff --git a/src/game/WorldHandlers/Map.h b/src/game/WorldHandlers/Map.h index dc4ed4ee6..54df41039 100644 --- a/src/game/WorldHandlers/Map.h +++ b/src/game/WorldHandlers/Map.h @@ -157,7 +157,7 @@ class Map : public GridRefManager bool GetUnloadLock(const GridPair& p) const { return getNGrid(p.x_coord, p.y_coord)->getUnloadLock(); } void SetUnloadLock(const GridPair& p, bool on) { getNGrid(p.x_coord, p.y_coord)->setUnloadExplicitLock(on); } - void LoadGrid(const Cell& cell, bool no_unload = false); + void ForceLoadGrid(float x, float y); bool UnloadGrid(const uint32& x, const uint32& y, bool pForce); virtual void UnloadAll(bool pForce); @@ -194,7 +194,6 @@ class Map : public GridRefManager MapDifficultyEntry const* GetMapDifficulty() const; // dependent from map difficulty bool Instanceable() const { return i_mapEntry && i_mapEntry->Instanceable(); } - // NOTE: this duplicate of Instanceable(), but Instanceable() can be changed when BG also will be instanceable bool IsDungeon() const { return i_mapEntry && i_mapEntry->IsDungeon(); } bool IsRaid() const { return i_mapEntry && i_mapEntry->IsRaid(); } bool IsHeroic() const { return IsRaid() ? i_spawnMode >= RAID_DIFFICULTY_10MAN_HEROIC : i_spawnMode >= DUNGEON_DIFFICULTY_HEROIC; } @@ -203,6 +202,7 @@ class Map : public GridRefManager 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 IsContinent() const { return i_mapEntry && i_mapEntry->IsContinent(); } // can't be NULL for loaded map MapPersistentState* GetPersistentState() const { return m_persistentState; } diff --git a/src/game/WorldHandlers/MapManager.cpp b/src/game/WorldHandlers/MapManager.cpp index 344061949..023a39e9d 100644 --- a/src/game/WorldHandlers/MapManager.cpp +++ b/src/game/WorldHandlers/MapManager.cpp @@ -92,30 +92,30 @@ void MapManager::InitializeVisibilityDistanceInfo() (*iter).second->InitVisibilityDistance(); } +/// @param id - MapId of the to be created map. @param obj WorldObject for which the map is to be created. Must be player for Instancable maps. Map* MapManager::CreateMap(uint32 id, const WorldObject* obj) { - MANGOS_ASSERT(obj); - // if(!obj->IsInWorld()) sLog.outError("GetMap: called for map %d with object (typeid %d, guid %d, mapid %d, instanceid %d) who is not in world!", id, obj->GetTypeId(), obj->GetGUIDLow(), obj->GetMapId(), obj->GetInstanceId()); Guard _guard(*this); - Map* m = NULL; + Map* m = nullptr; const MapEntry* entry = sMapStore.LookupEntry(id); if (!entry) - return NULL; + return nullptr; if (entry->Instanceable()) { - MANGOS_ASSERT(obj->GetTypeId() == TYPEID_PLAYER); + MANGOS_ASSERT(obj && obj->GetTypeId() == TYPEID_PLAYER); // create DungeonMap object - if (obj->GetTypeId() == TYPEID_PLAYER) - m = CreateInstance(id, (Player*)obj); + m = CreateInstance(id, (Player*)obj); + // Load active objects for this map + sObjectMgr.LoadActiveEntities(m); } else { // create regular non-instanceable map m = FindMap(id); - if (m == NULL) + if (m == nullptr) { m = new WorldMap(id, i_gridCleanUpDelay); // add map into container diff --git a/src/game/WorldHandlers/MapManager.h b/src/game/WorldHandlers/MapManager.h index a481f790f..99a8b17e4 100644 --- a/src/game/WorldHandlers/MapManager.h +++ b/src/game/WorldHandlers/MapManager.h @@ -94,7 +94,6 @@ class MapManager : public MaNGOS::SingletonIsInCombat() || //...is in combat - GetPlayer()->duel || //...is in Duel //...is jumping ...is falling GetPlayer()->m_movementInfo.HasMovementFlag(MovementFlags(MOVEFLAG_FALLING | MOVEFLAG_FALLINGFAR))) { @@ -1082,12 +1081,16 @@ void WorldSession::HandleInspectOpcode(WorldPacket& recv_data) recv_data >> guid; DEBUG_LOG("Inspected guid is %s", guid.GetString().c_str()); - _player->SetSelectionGuid(guid); - Player* plr = sObjectMgr.GetPlayer(guid); if (!plr) // wrong player return; + if (!_player->IsWithinDistInMap(plr, INSPECT_DISTANCE, false)) + return; + + if (_player->IsHostileTo(plr)) + return; + WorldPacket data(SMSG_INSPECT_RESULTS, 50); data << plr->GetObjectGuid(); @@ -1119,13 +1122,18 @@ void WorldSession::HandleInspectHonorStatsOpcode(WorldPacket& recv_data) recv_data.ReadGuidBytes<4, 7, 0, 5, 1, 6, 2, 3>(guid); Player* player = sObjectMgr.GetPlayer(guid); - if (!player) { sLog.outError("InspectHonorStats: WTF, player not found..."); return; } + if (!_player->IsWithinDistInMap(player, INSPECT_DISTANCE, false)) + return; + + if (_player->IsHostileTo(player)) + return; + WorldPacket data(SMSG_INSPECT_HONOR_STATS, 18); data.WriteGuidMask<4, 3, 6, 2, 5, 0, 7, 1>(player->GetObjectGuid()); data << uint8(0); // rank @@ -1520,8 +1528,17 @@ void WorldSession::HandleQueryInspectAchievementsOpcode(WorldPacket& recv_data) recv_data >> guid.ReadAsPacked(); - if (Player* player = sObjectMgr.GetPlayer(guid)) - player->GetAchievementMgr().SendRespondInspectAchievements(_player); + Player* player = sObjectMgr.GetPlayer(guid); + if (!player) + return; + + if (!_player->IsWithinDistInMap(player, INSPECT_DISTANCE, false)) + return; + + if (_player->IsHostileTo(player)) + return; + + player->GetAchievementMgr().SendRespondInspectAchievements(_player); } void WorldSession::HandleUITimeRequestOpcode(WorldPacket& /*recv_data*/) diff --git a/src/game/WorldHandlers/NPCHandler.cpp b/src/game/WorldHandlers/NPCHandler.cpp index f18e6c19d..7a2973c17 100644 --- a/src/game/WorldHandlers/NPCHandler.cpp +++ b/src/game/WorldHandlers/NPCHandler.cpp @@ -477,6 +477,20 @@ void WorldSession::SendSpiritResurrect() } } +void WorldSession::HandleReturnToGraveyardOpcode(WorldPacket& recv_data) +{ + Corpse* corpse = _player->GetCorpse(); + if (!corpse) + return; + + WorldSafeLocsEntry const* corpseGrave = sObjectMgr.GetClosestGraveYard(corpse->GetPositionX(), corpse->GetPositionY(), + corpse->GetPositionZ(), corpse->GetMapId(), _player->GetTeam()); + if (!corpseGrave) + return; + + _player->TeleportTo(corpseGrave->map_id, corpseGrave->x, corpseGrave->y, corpseGrave->z, _player->GetOrientation()); +} + void WorldSession::HandleBinderActivateOpcode(WorldPacket& recv_data) { ObjectGuid npcGuid; diff --git a/src/game/WorldHandlers/SkillHandler.cpp b/src/game/WorldHandlers/SkillHandler.cpp index 8fc1b6fda..c6380c311 100644 --- a/src/game/WorldHandlers/SkillHandler.cpp +++ b/src/game/WorldHandlers/SkillHandler.cpp @@ -43,6 +43,10 @@ void WorldSession::HandleLearnTalentOpcode(WorldPacket& recv_data) _player->SendTalentsInfoData(false); else sLog.outError("WorldSession::HandleLearnTalentOpcode: learn talent %u rank %u failed for %s (account %u)", talent_id, requested_rank, GetPlayerName(), GetAccountId()); + + // if player has a pet, update owner talent auras + if (_player->GetPet()) + _player->GetPet()->CastOwnerTalentAuras(); } void WorldSession::HandleLearnPreviewTalents(WorldPacket& recvPacket) @@ -84,6 +88,10 @@ void WorldSession::HandleLearnPreviewTalents(WorldPacket& recvPacket) } _player->SendTalentsInfoData(false); + + // if player has a pet, update owner talent auras + if (_player->GetPet()) + _player->GetPet()->CastOwnerTalentAuras(); } void WorldSession::HandleTalentWipeConfirmOpcode(WorldPacket& recv_data) @@ -99,9 +107,8 @@ void WorldSession::HandleTalentWipeConfirmOpcode(WorldPacket& recv_data) return; } - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + if (!unit->CanTrainAndResetTalentsOf(_player)) + return; if (!(_player->resetTalents())) { @@ -114,6 +121,8 @@ void WorldSession::HandleTalentWipeConfirmOpcode(WorldPacket& recv_data) _player->SendTalentsInfoData(false); unit->CastSpell(_player, 14867, true); // spell: "Untalent Visual Effect" + if (_player->GetPet()) + _player->GetPet()->CastOwnerTalentAuras(); } void WorldSession::HandleUnlearnSkillOpcode(WorldPacket& recv_data) diff --git a/src/game/WorldHandlers/Spell.cpp b/src/game/WorldHandlers/Spell.cpp index f993498bf..bc78525bc 100644 --- a/src/game/WorldHandlers/Spell.cpp +++ b/src/game/WorldHandlers/Spell.cpp @@ -519,10 +519,10 @@ void Spell::FillTargetMap() // targets for TARGET_SCRIPT_COORDINATES (A) and TARGET_SCRIPT // for TARGET_FOCUS_OR_SCRIPTED_GAMEOBJECT (A) all is checked in Spell::CheckCast and in Spell::CheckItem // filled in Spell::CheckCast call - if(spellEffect->EffectImplicitTargetA == TARGET_SCRIPT_COORDINATES || - spellEffect->EffectImplicitTargetA == TARGET_SCRIPT || - spellEffect->EffectImplicitTargetA == TARGET_FOCUS_OR_SCRIPTED_GAMEOBJECT || - (spellEffect->EffectImplicitTargetB == TARGET_SCRIPT && spellEffect->EffectImplicitTargetA != TARGET_SELF)) + if (spellEffect->EffectImplicitTargetA == TARGET_SCRIPT_COORDINATES || + spellEffect->EffectImplicitTargetA == TARGET_FOCUS_OR_SCRIPTED_GAMEOBJECT || + (spellEffect->EffectImplicitTargetA == TARGET_SCRIPT && spellEffect->EffectImplicitTargetB != TARGET_SELF) || + (spellEffect->EffectImplicitTargetB == TARGET_SCRIPT && spellEffect->EffectImplicitTargetA != TARGET_SELF)) continue; // TODO: find a way so this is not needed? @@ -571,8 +571,9 @@ void Spell::FillTargetMap() case TARGET_SELF: switch(spellEffect->EffectImplicitTargetB) { - case TARGET_NONE: + case TARGET_NONE: // Fill Target based on A only case TARGET_EFFECT_SELECT: + case TARGET_SCRIPT: // B-target only used with CheckCast here SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetA, tmpUnitLists[i /*==effToIndex[i]*/]); break; case TARGET_AREAEFFECT_INSTANT: // use B case that not dependent from from A in fact @@ -716,6 +717,17 @@ void Spell::FillTargetMap() break; } break; + case TARGET_SCRIPT: + switch (spellEffect->EffectImplicitTargetB) + { + case TARGET_SELF: + // Fill target based on B only, A is only used with CheckCast here. + SetTargetMap(SpellEffectIndex(i), spellEffect->EffectImplicitTargetB, tmpUnitLists[i /*==effToIndex[i]*/]); + break; + default: + break; + } + break; default: switch(spellEffect->EffectImplicitTargetB) { @@ -3273,6 +3285,9 @@ void Spell::cast(bool skipCheck) // Chaos Bane strength buff else if (m_spellInfo->Id == 71904) AddTriggeredSpell(73422); + // Weak Alcohol + else if (m_spellInfo->SpellIconID == 1306 && m_spellInfo->SpellVisual[0] == 11359) + AddTriggeredSpell(51655); // BOTM - Create Empty Brew Bottle break; } case SPELLFAMILY_MAGE: @@ -3710,14 +3725,20 @@ void Spell::update(uint32 difftime) SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(EFFECT_INDEX_0); SpellInterruptsEntry const* spellInterrupts = m_spellInfo->GetSpellInterrupts(); - // check if the player caster has moved before the spell finished - if ((m_caster->GetTypeId() == TYPEID_PLAYER && m_timer != 0) && + if (m_CastItemGuid && !m_CastItem) + { + cancel(); + return; + } + + // check if the player or unit caster has moved before the spell finished (exclude casting on vehicles) + if (((m_caster->GetTypeId() == TYPEID_PLAYER || m_caster->GetTypeId() == TYPEID_UNIT) && m_timer != 0) && (m_castPositionX != m_caster->GetPositionX() || m_castPositionY != m_caster->GetPositionY() || m_castPositionZ != m_caster->GetPositionZ()) && ((spellEffect && spellEffect->Effect != SPELL_EFFECT_STUCK) || !((Player*)m_caster)->m_movementInfo.HasMovementFlag(MOVEFLAG_FALLINGFAR)) && !m_caster->HasAffectedAura(SPELL_AURA_ALLOW_CAST_WHILE_MOVING, m_spellInfo)) { // always cancel for channeled spells - if (m_spellState == SPELL_STATE_CASTING) + if (m_spellState == SPELL_STATE_CASTING && !m_spellInfo->HasAttribute(SPELL_ATTR_EX5_CAN_CHANNEL_WHEN_MOVING)) cancel(); // don't cancel for melee, autorepeat, triggered and instant spells else if(!IsNextMeleeSwingSpell() && !IsAutoRepeat() && !m_IsTriggeredSpell && (spellInterrupts && spellInterrupts->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT)) @@ -3743,16 +3764,20 @@ void Spell::update(uint32 difftime) { if (m_timer > 0) { - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_caster->GetTypeId() == TYPEID_PLAYER || m_caster->GetTypeId() == TYPEID_UNIT) { // check if player has jumped before the channeling finished - if (((Player*)m_caster)->m_movementInfo.HasMovementFlag(MOVEFLAG_FALLING) && + if (m_caster->m_movementInfo.HasMovementFlag(MOVEFLAG_FALLING) && !m_caster->HasAffectedAura(SPELL_AURA_ALLOW_CAST_WHILE_MOVING, m_spellInfo)) cancel(); // check for incapacitating player states if (m_caster->hasUnitState(UNIT_STAT_CAN_NOT_REACT)) - cancel(); + { + // certain channel spells are not interrupted + if (!m_spellInfo->HasAttribute(SPELL_ATTR_EX_CHANNELED_1) && !m_spellInfo->HasAttribute(SPELL_ATTR_EX3_UNK28)) + cancel(); + } // check if player has turned if flag is set if( spellInterrupts && (spellInterrupts->ChannelInterruptFlags & CHANNEL_FLAG_TURNING) && m_castOrientation != m_caster->GetOrientation() ) @@ -3790,7 +3815,7 @@ void Spell::update(uint32 difftime) continue; Unit* unit = m_caster->GetObjectGuid() == target.targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, target.targetGUID); - if (unit == NULL) + if (unit == nullptr) continue; p->RewardPlayerAndGroupAtCast(unit, m_spellInfo->Id); @@ -4082,7 +4107,7 @@ void Spell::SendSpellStart() data << uint8(m_runesState); data << uint8(caster->GetRunesState()); for (uint8 i = 0; i < MAX_RUNES; ++i) - data << uint8(255 - ((caster->GetRuneCooldown(i) / REGEN_TIME_FULL) * 51)); + data << uint8(caster->GetRuneCooldownFraction(i)); } else { @@ -4702,6 +4727,7 @@ void Spell::TakePower() if (m_spellInfo->powerType == POWER_HEALTH) { m_caster->ModifyHealth(-(int32)m_powerCost); + m_caster->SendSpellNonMeleeDamageLog(m_caster, m_spellInfo->Id, m_powerCost, GetSpellSchoolMask(m_spellInfo), 0, 0, false, 0, false); return; } @@ -4740,14 +4766,14 @@ void Spell::TakePower() if (powerType == POWER_RUNE) { - CheckOrTakeRunePower(hit); + TakeRunePower(hit); return; } m_caster->ModifyPower(powerType, -(int32)m_powerCost); } -SpellCastResult Spell::CheckOrTakeRunePower(bool take) +SpellCastResult Spell::CheckRunePower() { if (m_caster->GetTypeId() != TYPEID_PLAYER) return SPELL_CAST_OK; @@ -4758,83 +4784,127 @@ SpellCastResult Spell::CheckOrTakeRunePower(bool take) return SPELL_CAST_OK; SpellRuneCostEntry const* src = sSpellRuneCostStore.LookupEntry(m_spellInfo->runeCostID); - if (!src || (src->NoRuneCost() && (!take || src->NoRunicPowerGain()))) + + if (!src) return SPELL_CAST_OK; - m_runesState = plr->GetRunesState(); // store previous state + if (src->NoRuneCost()) + return SPELL_CAST_OK; - int32 runeCost[NUM_RUNE_TYPES]; // blood, frost, unholy, death - - // init cost data and apply mods - for (uint8 i = 0; i < RUNE_DEATH; ++i) + // at this moment for rune cost exist only no cost mods, and no percent mods + int32 runeCost[NUM_RUNE_TYPES]; // blood, frost, unholy, death + for (uint32 i = 0; i < RUNE_DEATH; ++i) { runeCost[i] = src->RuneCost[i]; - if (Player* modOwner = plr->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, runeCost[i], this); + if (Player* modOwner = m_caster->GetSpellModOwner()) + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, runeCost[i]); } - - runeCost[RUNE_DEATH] = MAX_RUNES; // calculated later + runeCost[RUNE_DEATH] = MAX_RUNES; // calculated later - // scan non-death runes (death rune not used explicitly in rune costs) - for (uint8 i = 0; i < MAX_RUNES; ++i) + for (uint32 i = 0; i < MAX_RUNES; ++i) { - // already used - if (plr->GetRuneCooldown(i) != 0) - continue; - RuneType rune = plr->GetCurrentRune(i); - if (plr->GetRuneCooldown(i) == 0 && runeCost[rune] > 0) + if (!plr->GetRuneCooldown(i) && runeCost[rune] > 0) + --runeCost[rune]; + } + + for (uint32 i = 0; i < RUNE_DEATH; ++i) + if (runeCost[i] > 0) + runeCost[RUNE_DEATH] += runeCost[i]; + + if (runeCost[RUNE_DEATH] > MAX_RUNES) + return SPELL_FAILED_NO_POWER; // not sure if result code is correct + + return SPELL_CAST_OK; +} + +void Spell::TakeRunePower(bool hit) +{ + if (m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + Player* plr = (Player*)m_caster; + + if (plr->getClass() != CLASS_DEATH_KNIGHT) + return; + + SpellRuneCostEntry const* src = sSpellRuneCostStore.LookupEntry(m_spellInfo->runeCostID); + + if (!src) + return; + + if (src->NoRuneCost() && src->NoRunicPowerGain()) + return; + + m_runesState = plr->GetRunesState(); // store previous state + + // at this moment for rune cost exist only no cost mods, and no percent mods + int32 runeCost[NUM_RUNE_TYPES]; // blood, frost, unholy, death + for (uint32 i = 0; i < RUNE_DEATH; ++i) + { + runeCost[i] = src->RuneCost[i]; + if (Player* modOwner = m_caster->GetSpellModOwner()) + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, runeCost[i]); + } + + runeCost[RUNE_DEATH] = 0; // calculated later + + plr->ClearLastUsedRuneMask(); + + for (uint32 i = 0; i < MAX_RUNES; ++i) + { + RuneType rune = plr->GetCurrentRune(i); + if (!plr->GetRuneCooldown(i) && runeCost[rune] > 0) { - RuneType rune = plr->GetCurrentRune(i); - if (runeCost[rune] <= 0) - continue; - - // already used - if (plr->GetRuneCooldown(i) != 0) - continue; - - if (take) - plr->SetRuneCooldown(i, RUNE_COOLDOWN); // 5*2=10 sec - + uint16 baseCd = hit ? uint16(RUNE_BASE_COOLDOWN) : uint16(RUNE_MISS_COOLDOWN); + plr->SetBaseRuneCooldown(i, baseCd); + plr->SetRuneCooldown(i, baseCd); + plr->SetLastUsedRune(rune); --runeCost[rune]; } } - // collect all not counted rune costs to death runes cost - for (uint8 i = 0; i < RUNE_DEATH; ++i) - if (runeCost[i] > 0) - runeCost[RUNE_DEATH] += runeCost[i]; + runeCost[RUNE_DEATH] = runeCost[RUNE_BLOOD] + runeCost[RUNE_UNHOLY] + runeCost[RUNE_FROST]; - // scan death runes if (runeCost[RUNE_DEATH] > 0) { - for (uint8 i = 0; i < MAX_RUNES; ++i) + for (uint32 i = 0; i < MAX_RUNES; ++i) { RuneType rune = plr->GetCurrentRune(i); if (!plr->GetRuneCooldown(i) && rune == RUNE_DEATH) { - plr->SetRuneCooldown(i, RUNE_COOLDOWN); // 5*2=10 sec - runeCost[rune]--; + uint16 baseCd = hit ? uint16(RUNE_BASE_COOLDOWN) : uint16(RUNE_MISS_COOLDOWN); + plr->SetBaseRuneCooldown(i, baseCd); + plr->SetRuneCooldown(i, baseCd); + plr->SetLastUsedRune(rune); + --runeCost[rune]; - if (take) - plr->ConvertRune(i, plr->GetBaseRune(i)); + // keep Death Rune type if missed + if (hit) + plr->RestoreBaseRune(i); if (runeCost[RUNE_DEATH] == 0) - return SPELL_FAILED_NO_POWER; + break; } } } - if (take) + if (hit) { // you can gain some runic power when use runes - float rp = float(src->runePowerGain); - rp *= sWorld.getConfig(CONFIG_FLOAT_RATE_POWER_RUNICPOWER_INCOME); - plr->ModifyPower(POWER_RUNIC_POWER, (int32)rp); - } + int32 rp = int32(src->runePowerGain); + if (rp) + { + if (Player* modOwner = m_caster->GetSpellModOwner()) + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, rp); - return SPELL_CAST_OK; + rp = int32(sWorld.getConfig(CONFIG_FLOAT_RATE_POWER_RUNICPOWER_INCOME) * rp); + rp += m_caster->GetTotalAuraModifier(SPELL_AURA_MOD_RUNIC_POWER_REGEN) * rp / 100; + if (rp > 0) + plr->ModifyPower(POWER_RUNIC_POWER, (int32)rp); + } + } } void Spell::TakeReagents() @@ -5437,12 +5507,12 @@ SpellCastResult Spell::CheckCast(bool strict) continue; if (spellEffect->EffectImplicitTargetA == TARGET_SCRIPT || - (spellEffect->EffectImplicitTargetB == TARGET_SCRIPT && spellEffect->EffectImplicitTargetA != TARGET_SELF) || + spellEffect->EffectImplicitTargetB == TARGET_SCRIPT || spellEffect->EffectImplicitTargetA == TARGET_SCRIPT_COORDINATES || spellEffect->EffectImplicitTargetB == TARGET_SCRIPT_COORDINATES || - spellEffect->EffectImplicitTargetA == TARGET_FOCUS_OR_SCRIPTED_GAMEOBJECT) + spellEffect->EffectImplicitTargetA == TARGET_FOCUS_OR_SCRIPTED_GAMEOBJECT || + spellEffect->EffectImplicitTargetB == TARGET_FOCUS_OR_SCRIPTED_GAMEOBJECT) { - SQLMultiStorage::SQLMSIteratorBounds bounds = sSpellScriptTargetStorage.getBounds(m_spellInfo->Id); if (bounds.first == bounds.second) @@ -5453,13 +5523,17 @@ SpellCastResult Spell::CheckCast(bool strict) if (spellEffect->EffectImplicitTargetA == TARGET_SCRIPT_COORDINATES || spellEffect->EffectImplicitTargetB == TARGET_SCRIPT_COORDINATES) sLog.outErrorDb("Spell entry %u, effect %i has EffectImplicitTargetA/EffectImplicitTargetB = TARGET_SCRIPT_COORDINATES, but gameobject or creature are not defined in `spell_script_target`", m_spellInfo->Id, j); - if (spellEffect->EffectImplicitTargetA == TARGET_FOCUS_OR_SCRIPTED_GAMEOBJECT) + if (spellEffect->EffectImplicitTargetA == TARGET_FOCUS_OR_SCRIPTED_GAMEOBJECT || spellEffect->EffectImplicitTargetB == TARGET_FOCUS_OR_SCRIPTED_GAMEOBJECT) sLog.outErrorDb("Spell entry %u, effect %i has EffectImplicitTargetA/EffectImplicitTargetB = TARGET_FOCUS_OR_SCRIPTED_GAMEOBJECT, but gameobject are not defined in `spell_script_target`", m_spellInfo->Id, j); } SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex); float range = GetSpellMaxRange(srange); + // override range with default when it's not provided + if (!range) + range = m_caster->GetMap()->IsDungeon() ? DEFAULT_VISIBILITY_INSTANCE : DEFAULT_VISIBILITY_DISTANCE; + Creature* targetExplicit = NULL; // used for cases where a target is provided (by script for example) Creature* creatureScriptTarget = NULL; GameObject* goScriptTarget = NULL; @@ -5905,12 +5979,6 @@ SpellCastResult Spell::CheckCast(bool strict) if (ReqValue > skillValue) return SPELL_FAILED_LOW_CASTLEVEL; - // chance for fail at orange skinning attempt - if ((m_selfContainer && (*m_selfContainer) == this) && - skillValue < sWorld.GetConfigMaxSkillValue() && - (ReqValue < 0 ? 0 : ReqValue) > irand(skillValue - 25, skillValue + 37)) - return SPELL_FAILED_TRY_AGAIN; - break; } case SPELL_EFFECT_OPEN_LOCK: @@ -5962,9 +6030,10 @@ SpellCastResult Spell::CheckCast(bool strict) if (res != SPELL_CAST_OK) return res; - // chance for fail at orange mining/herb/LockPicking gathering attempt + // Failing gathering attempt for mining and herbalism was removed in patch 3.1.0: http://wowwiki.wikia.com/wiki/Patch_3.1.0 + // chance for fail at orange LockPicking gathering attempt // second check prevent fail at rechecks - if (skillId != SKILL_NONE && (!m_selfContainer || ((*m_selfContainer) != this))) + if (m_spellState != SPELL_STATE_CREATED && skillId != SKILL_NONE && skillId != SKILL_HERBALISM && skillId != SKILL_MINING ) { bool canFailAtMax = skillId != SKILL_HERBALISM && skillId != SKILL_MINING; @@ -5972,7 +6041,6 @@ SpellCastResult Spell::CheckCast(bool strict) if ((canFailAtMax || skillValue < sWorld.GetConfigMaxSkillValue()) && reqSkillValue > irand(skillValue - 25, skillValue + 37)) return SPELL_FAILED_TRY_AGAIN; } - break; } case SPELL_EFFECT_SUMMON_DEAD_PET: { @@ -6823,7 +6891,7 @@ SpellCastResult Spell::CheckPower() // check rune cost only if a spell has PowerType == POWER_RUNE if (m_spellInfo->powerType == POWER_RUNE) { - SpellCastResult failReason = CheckOrTakeRunePower(false); + SpellCastResult failReason = CheckRunePower(); if (failReason != SPELL_CAST_OK) return failReason; } diff --git a/src/game/WorldHandlers/Spell.h b/src/game/WorldHandlers/Spell.h index 061f2f551..0dc599471 100644 --- a/src/game/WorldHandlers/Spell.h +++ b/src/game/WorldHandlers/Spell.h @@ -235,10 +235,12 @@ inline ByteBuffer& operator>> (ByteBuffer& buf, SpellCastTargetsReader const& ta enum SpellState { - SPELL_STATE_PREPARING = 0, // cast time delay period, non channeled spell - SPELL_STATE_CASTING = 1, // channeled time period spell casting state - SPELL_STATE_FINISHED = 2, // cast finished to success or fail - SPELL_STATE_DELAYED = 3 // spell casted but need time to hit target(s) + SPELL_STATE_CREATED = 0, // just created + SPELL_STATE_STARTING = 1, // doing initial check + SPELL_STATE_PREPARING = 2, // cast time delay period, non channeled spell + SPELL_STATE_CASTING = 3, // channeled time period spell casting state + SPELL_STATE_FINISHED = 4, // cast finished to success or fail + SPELL_STATE_DELAYED = 5 // spell casted but need time to hit target(s) }; enum SpellTargets @@ -396,6 +398,7 @@ class Spell void cast(bool skipCheck = false); void finish(bool ok = true); void TakePower(); + void TakeRunePower(bool hit); void TakeAmmo(); void TakeReagents(); void TakeCastItem(); @@ -413,7 +416,7 @@ class Spell SpellCastResult CheckItems(); SpellCastResult CheckRange(bool strict); SpellCastResult CheckPower(); - SpellCastResult CheckOrTakeRunePower(bool take); + SpellCastResult CheckRunePower(); SpellCastResult CheckCasterAuras() const; int32 CalculateDamage(SpellEffectIndex i, Unit* target) { return m_caster->CalculateSpellDamage(target, m_spellInfo, i, &m_currentBasePoints[i]); } @@ -455,6 +458,8 @@ class Spell SpellEntry const* m_triggeredBySpellInfo; SpellInterruptsEntry const* m_spellInterrupts; int32 m_currentBasePoints[MAX_EFFECT_INDEX]; // cache SpellEntry::CalculateSimpleValue and use for set custom base points + + ObjectGuid m_CastItemGuid; Item* m_CastItem; uint8 m_cast_count; uint32 m_glyphIndex; diff --git a/src/game/WorldHandlers/SpellAuras.cpp b/src/game/WorldHandlers/SpellAuras.cpp index 313e73b5c..dbc530d06 100644 --- a/src/game/WorldHandlers/SpellAuras.cpp +++ b/src/game/WorldHandlers/SpellAuras.cpp @@ -363,7 +363,7 @@ pAuraHandler AuraHandler[TOTAL_AURAS] = &Aura::HandleNULL, //297 14 spells in 4.3.4 &Aura::HandleUnused, //298 6 spells in 4.3.4 &Aura::HandleUnused, //299 unused (3.2.2a-4.3.4) - &Aura::HandleNULL, //300 21 spells (share damage?) + &Aura::HandleNoImmediateEffect, //300 SPELL_AURA_SHARE_DAMAGE_PCT 21 spells &Aura::HandleNULL, //301 SPELL_AURA_HEAL_ABSORB 31 spells &Aura::HandleUnused, //302 unused (3.2.2a-4.3.4) &Aura::HandleNULL, //303 35 spells increases damage done vs. creature type @@ -510,7 +510,8 @@ Aura::~Aura() } AreaAura::AreaAura(SpellEntry const* spellproto, SpellEffectIndex eff, int32* currentBasePoints, SpellAuraHolder* holder, Unit* target, - Unit* caster, Item* castItem) : Aura(spellproto, eff, currentBasePoints, holder, target, caster, castItem) + Unit* caster, Item* castItem, uint32 originalRankSpellId) + : Aura(spellproto, eff, currentBasePoints, holder, target, caster, castItem), m_originalRankSpellId(originalRankSpellId) { m_isAreaAura = true; @@ -751,18 +752,26 @@ void AreaAura::Update(uint32 diff) // flag for selection is need apply aura to current iteration target bool apply = true; + SpellEntry const* actualSpellInfo; + if (GetCasterGuid() == (*tIter)->GetObjectGuid()) // if caster is same as target then no need to change rank of the spell + actualSpellInfo = GetSpellProto(); + else + actualSpellInfo = sSpellMgr.SelectAuraRankForLevel(GetSpellProto(), (*tIter)->getLevel()); // use spell id according level of the target + if (!actualSpellInfo) + continue; + + Unit::SpellAuraHolderBounds spair = (*tIter)->GetSpellAuraHolderBounds(actualSpellInfo->Id); // we need ignore present caster self applied are auras sometime // in cases if this only auras applied for spell effect - Unit::SpellAuraHolderBounds spair = (*tIter)->GetSpellAuraHolderBounds(GetId()); for (Unit::SpellAuraHolderMap::const_iterator i = spair.first; i != spair.second; ++i) { if (i->second->IsDeleted()) - { continue; } + continue; Aura* aur = i->second->GetAuraByEffectIndex(m_effIndex); if (!aur) - { continue; } + continue; switch (m_areaAuraType) { @@ -773,7 +782,7 @@ void AreaAura::Update(uint32 diff) break; case AREA_AURA_RAID: // non caster self-casted auras (stacked from diff. casters) - if (aur->GetModifier()->m_auraname != SPELL_AURA_NONE || i->second->GetCasterGuid() == GetCasterGuid()) + if (aur->GetModifier()->m_auraname != SPELL_AURA_NONE || i->second->GetCasterGuid() == GetCasterGuid()) apply = false; break; default: @@ -787,43 +796,41 @@ void AreaAura::Update(uint32 diff) } if (!apply) - { continue; } + continue; // Skip some targets (TODO: Might require better checks, also unclear how the actual caster must/can be handled) - if (GetSpellProto()->HasAttribute(SPELL_ATTR_EX3_TARGET_ONLY_PLAYER) && (*tIter)->GetTypeId() != TYPEID_PLAYER) - { continue; } + if (actualSpellInfo->HasAttribute(SPELL_ATTR_EX3_TARGET_ONLY_PLAYER) && (*tIter)->GetTypeId() != TYPEID_PLAYER) + continue; - if (SpellEntry const* actualSpellInfo = sSpellMgr.SelectAuraRankForLevel(GetSpellProto(), (*tIter)->getLevel())) + int32 actualBasePoints = m_currentBasePoints; + // recalculate basepoints for lower rank (all AreaAura spell not use custom basepoints?) + if (actualSpellInfo != GetSpellProto()) + actualBasePoints = actualSpellInfo->CalculateSimpleValue(m_effIndex); + + SpellAuraHolder* holder = (*tIter)->GetSpellAuraHolder(actualSpellInfo->Id, GetCasterGuid()); + + bool addedToExisting = true; + if (!holder) { - int32 actualBasePoints = m_currentBasePoints; - // recalculate basepoints for lower rank (all AreaAura spell not use custom basepoints?) - if (actualSpellInfo != GetSpellProto()) - { actualBasePoints = actualSpellInfo->CalculateSimpleValue(m_effIndex); } - - SpellAuraHolder* holder = (*tIter)->GetSpellAuraHolder(actualSpellInfo->Id, GetCasterGuid()); - - bool addedToExisting = true; - if (!holder) - { - holder = CreateSpellAuraHolder(actualSpellInfo, (*tIter), caster); - addedToExisting = false; - } - - holder->SetAuraDuration(GetAuraDuration()); - - AreaAura* aur = new AreaAura(actualSpellInfo, m_effIndex, &actualBasePoints, holder, (*tIter), caster, NULL); - holder->AddAura(aur, m_effIndex); - - if (addedToExisting) - { - (*tIter)->AddAuraToModList(aur); - holder->SetInUse(true); - aur->ApplyModifier(true, true); - holder->SetInUse(false); - } - else - (*tIter)->AddSpellAuraHolder(holder); + holder = CreateSpellAuraHolder(actualSpellInfo, (*tIter), caster); + addedToExisting = false; } + + holder->SetAuraDuration(GetAuraDuration()); + + AreaAura* aur = new AreaAura(actualSpellInfo, m_effIndex, &actualBasePoints, holder, (*tIter), caster, nullptr, GetSpellProto()->Id); + holder->AddAura(aur, m_effIndex); + + if (addedToExisting) + { + (*tIter)->AddAuraToModList(aur); + holder->SetInUse(true); + aur->ApplyModifier(true, true); + holder->SetInUse(false); + } + else + (*tIter)->AddSpellAuraHolder(holder); + } } Aura::Update(diff); @@ -832,6 +839,7 @@ void AreaAura::Update(uint32 diff) { Unit* caster = GetCaster(); Unit* target = GetTarget(); + uint32 originalRankSpellId = m_originalRankSpellId ? m_originalRankSpellId : GetId(); // caster may have different spell id if target has lower level Aura::Update(diff); @@ -839,9 +847,10 @@ void AreaAura::Update(uint32 diff) // or caster is isolated or caster no longer has the aura // or caster is (no longer) friendly bool needFriendly = (m_areaAuraType == AREA_AURA_ENEMY ? false : true); - if (!caster || caster->hasUnitState(UNIT_STAT_ISOLATED) || - !caster->IsWithinDistInMap(target, m_radius) || - !caster->HasAura(GetId(), GetEffIndex()) || + if (!caster || + caster->hasUnitState(UNIT_STAT_ISOLATED) || + !caster->HasAura(originalRankSpellId, GetEffIndex()) || + !caster->IsWithinDistInMap(target, m_radius) || caster->IsFriendlyTo(target) != needFriendly ) { @@ -854,29 +863,29 @@ void AreaAura::Update(uint32 diff) { Player* check = caster->GetCharmerOrOwnerPlayerOrPlayerItself(); - Group* pGroup = check ? check->GetGroup() : NULL; + Group* pGroup = check ? check->GetGroup() : nullptr; if (pGroup) { Player* checkTarget = target->GetCharmerOrOwnerPlayerOrPlayerItself(); if (!checkTarget || !pGroup->SameSubGroup(check, checkTarget)) - { target->RemoveSingleAuraFromSpellAuraHolder(GetId(), GetEffIndex(), GetCasterGuid()); } + target->RemoveSingleAuraFromSpellAuraHolder(GetId(), GetEffIndex(), GetCasterGuid()); } else - { target->RemoveSingleAuraFromSpellAuraHolder(GetId(), GetEffIndex(), GetCasterGuid()); } + target->RemoveSingleAuraFromSpellAuraHolder(GetId(), GetEffIndex(), GetCasterGuid()); } } - else if (m_areaAuraType == AREA_AURA_RAID) // TODO: fix me! + else if (m_areaAuraType == AREA_AURA_RAID) // Check if on same raid group { // not check group if target == owner or target == pet if (caster->GetCharmerOrOwnerGuid() != target->GetObjectGuid() && caster->GetObjectGuid() != target->GetCharmerOrOwnerGuid()) { Player* check = caster->GetCharmerOrOwnerPlayerOrPlayerItself(); - Group* pGroup = check ? check->GetGroup() : NULL; + Group* pGroup = check ? check->GetGroup() : nullptr; if (pGroup) { Player* checkTarget = target->GetCharmerOrOwnerPlayerOrPlayerItself(); - if (!checkTarget) + if (!checkTarget || !checkTarget->GetGroup() || checkTarget->GetGroup()->GetId() != pGroup->GetId()) target->RemoveSingleAuraFromSpellAuraHolder(GetId(), GetEffIndex(), GetCasterGuid()); } else @@ -886,7 +895,7 @@ void AreaAura::Update(uint32 diff) else if (m_areaAuraType == AREA_AURA_PET || m_areaAuraType == AREA_AURA_OWNER) { if (target->GetObjectGuid() != caster->GetCharmerOrOwnerGuid()) - { target->RemoveSingleAuraFromSpellAuraHolder(GetId(), GetEffIndex(), GetCasterGuid()); } + target->RemoveSingleAuraFromSpellAuraHolder(GetId(), GetEffIndex(), GetCasterGuid()); } } } @@ -1191,7 +1200,7 @@ void Aura::TriggerSpell() { int32 damageForTick[8] = { 500, 500, 1000, 1000, 2000, 2000, 3000, 5000 }; triggerTarget->CastCustomSpell(triggerTarget, 19698, &damageForTick[GetAuraTicks() - 1], nullptr, nullptr, true, nullptr); - return; + return;; } // // Frostwolf Muzzle DND @@ -3526,62 +3535,18 @@ void Aura::HandleAuraFeatherFall(bool apply, bool Real) { // only at real add/remove aura if (!Real) - { return; } - Unit* target = GetTarget(); - WorldPacket data; - target->BuildMoveFeatherFallPacket(&data, apply, 0); - target->SendMessageToSet(&data, true); + return; - // start fall from current height - if (!apply && target->GetTypeId() == TYPEID_PLAYER) - ((Player*)target)->SetFallInformation(0, target->GetPositionZ()); + GetTarget()->SetFeatherFall(apply); } void Aura::HandleAuraHover(bool apply, bool Real) { // only at real add/remove aura if (!Real) - { return; } + return; - WorldPacket data; - if (apply) - { - GetTarget()->m_movementInfo.AddMovementFlag(MOVEFLAG_HOVER); - if (GetTarget()->GetTypeId() == TYPEID_PLAYER) - { - data.Initialize(SMSG_MOVE_SET_HOVER, 8 + 4 + 1); - data.WriteGuidMask<1, 4, 2, 3, 0, 5, 6, 7>(GetTarget()->GetObjectGuid()); - data.WriteGuidBytes<5, 4, 1, 2, 3, 6, 0, 7>(GetTarget()->GetObjectGuid()); - data << uint32(0); - } - else - { - data.Initialize(SMSG_SPLINE_MOVE_SET_HOVER, 8 + 4 + 1); - data.WriteGuidMask<3, 7, 0, 1, 4, 6, 2, 5>(GetTarget()->GetObjectGuid()); - data.WriteGuidBytes<2, 4, 3, 1, 7, 0, 5, 6>(GetTarget()->GetObjectGuid()); - GetTarget()->SetByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_FLY_ANIM); - } - } - else - { - GetTarget()->m_movementInfo.RemoveMovementFlag(MOVEFLAG_HOVER); - data.Initialize(GetTarget()->GetTypeId() == TYPEID_PLAYER ? SMSG_MOVE_UNSET_HOVER : SMSG_SPLINE_MOVE_UNSET_HOVER, 8+4); - if (GetTarget()->GetTypeId() == TYPEID_PLAYER) - { - data.Initialize(SMSG_MOVE_UNSET_HOVER, 8 + 4 + 1); - data.WriteGuidMask<4, 6, 3, 1, 2, 7, 5, 0>(GetTarget()->GetObjectGuid()); - data.WriteGuidBytes<4, 5, 3, 6, 7, 1, 2, 0>(GetTarget()->GetObjectGuid()); - data << uint32(0); - } - else - { - data.Initialize(SMSG_SPLINE_MOVE_UNSET_HOVER, 8 + 4 + 1); - data.WriteGuidMask<6, 7, 4, 0, 3, 1, 5, 2>(GetTarget()->GetObjectGuid()); - data.WriteGuidBytes<4, 5, 3, 0, 2, 7, 6, 1>(GetTarget()->GetObjectGuid()); - GetTarget()->RemoveByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_FLY_ANIM); - } - } - GetTarget()->SendMessageToSet(&data, true); + GetTarget()->SetHover(apply); } void Aura::HandleWaterBreathing(bool /*apply*/, bool /*Real*/) @@ -4069,8 +4034,7 @@ void Aura::HandleAuraTransform(bool apply, bool Real) if (Unit* caster = GetCaster()) if (caster->HasAura(52648)) // Glyph of the Penguin model_id = 26452; - else - if (caster->HasAura(57927)) // Glyph of the Monkey + else if (caster->HasAura(57927)) // Glyph of the Monkey model_id = 21362; target->SetDisplayId(model_id); @@ -5209,9 +5173,7 @@ void Aura::HandleAuraModIncreaseFlightSpeed(bool apply, bool Real) // Enable Fly mode for flying mounts if (m_modifier.m_auraname == SPELL_AURA_MOD_FLIGHT_SPEED_MOUNTED) { - WorldPacket data; - target->BuildMoveSetCanFlyPacket(&data, apply, 0); - target->SendMessageToSet(&data, true); + target->SetCanFly(apply); // Players on flying mounts must be immune to polymorph if (target->GetTypeId() == TYPEID_PLAYER) @@ -7109,10 +7071,7 @@ void Aura::HandleAuraAllowFlight(bool apply, bool Real) if (!Real) return; - // allow fly - WorldPacket data; - GetTarget()->BuildMoveSetCanFlyPacket(&data, apply, 0); - GetTarget()->SendMessageToSet(&data, true); + GetTarget()->SetCanFly(apply); } void Aura::HandleModRating(bool apply, bool Real) diff --git a/src/game/WorldHandlers/SpellAuras.h b/src/game/WorldHandlers/SpellAuras.h index 8195de6a1..86c835747 100644 --- a/src/game/WorldHandlers/SpellAuras.h +++ b/src/game/WorldHandlers/SpellAuras.h @@ -545,13 +545,14 @@ class Aura class AreaAura : public Aura { public: - AreaAura(SpellEntry const* spellproto, SpellEffectIndex eff, int32* currentBasePoints, SpellAuraHolder* holder, Unit* target, Unit* caster = NULL, Item* castItem = NULL); + AreaAura(SpellEntry const* spellproto, SpellEffectIndex eff, int32* currentBasePoints, SpellAuraHolder* holder, Unit* target, Unit* caster = nullptr, Item* castItem = nullptr, uint32 originalRankSpellId = 0); ~AreaAura(); protected: void Update(uint32 diff) override; private: float m_radius; AreaAuraType m_areaAuraType; + uint32 m_originalRankSpellId; }; class PersistentAreaAura : public Aura diff --git a/src/game/WorldHandlers/SpellEffects.cpp b/src/game/WorldHandlers/SpellEffects.cpp index 9f2960807..88cf11518 100644 --- a/src/game/WorldHandlers/SpellEffects.cpp +++ b/src/game/WorldHandlers/SpellEffects.cpp @@ -222,7 +222,7 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS] = &Spell::EffectQuestOffer, //150 SPELL_EFFECT_QUEST_OFFER &Spell::EffectTriggerRitualOfSummoning, //151 SPELL_EFFECT_TRIGGER_SPELL_2 &Spell::EffectNULL, //152 SPELL_EFFECT_152 summon Refer-a-Friend - &Spell::EffectNULL, //153 SPELL_EFFECT_CREATE_PET misc value is creature entry + &Spell::EffectCreateTamedPet, //153 SPELL_EFFECT_CREATE_PET misc value is creature entry &Spell::EffectTeachTaxiNode, //154 SPELL_EFFECT_TEACH_TAXI_NODE single spell: Teach River's Heart Taxi Path &Spell::EffectTitanGrip, //155 SPELL_EFFECT_TITAN_GRIP Allows you to equip two-handed axes, maces and swords in one hand, but you attack $49152s1% slower than normal. &Spell::EffectEnchantItemPrismatic, //156 SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC @@ -4191,17 +4191,12 @@ void Spell::EffectTriggerSpell(SpellEffectEntry const* effect) pet->CastSpell(pet, 28305, true); return; } - case 53258: // Empower Rune Weapon + case 58832: // Mirror Image { - // remove cooldown of frost/death, undead/blood activated in main spell - if (unitTarget->GetTypeId() == TYPEID_PLAYER) - { - bool res1 = ((Player*)unitTarget)->ActivateRunes(RUNE_FROST, 2); - bool res2 = ((Player*)unitTarget)->ActivateRunes(RUNE_DEATH, 2); - if (res1 || res2) - ((Player*)unitTarget)->ResyncRunes(); - } - return; + // Glyph of Mirror Image + if (m_caster->HasAura(63093)) + m_caster->CastSpell(m_caster, 65047, true, m_CastItem, nullptr, m_originalCasterGUID); + break; } } @@ -4339,7 +4334,11 @@ void Spell::EffectJump(SpellEffectEntry const* effect) return; } - m_caster->NearTeleportTo(x, y, z, o, true); // TODO Implement this as jump movement? + // Try to normalize Z coord because GetContactPoint do nothing with Z axis + m_caster->UpdateAllowedPositionZ(x, y, z); + + float speed = m_spellInfo->speed ? m_spellInfo->speed : 27.0f; + m_caster->GetMotionMaster()->MoveDestination(x, y, z, o, speed, 2.5f); } void Spell::EffectTeleportUnits(SpellEffectEntry const* effect) // TODO - Use target settings for this effect! @@ -6629,56 +6628,45 @@ void Spell::EffectSummonPet(SpellEffectEntry const* effect) { uint32 petentry = effect->EffectMiscValue; - Pet* OldSummon = m_caster->GetPet(); - - // if pet requested type already exist - if (OldSummon) - { - if ((petentry == 0 || OldSummon->GetEntry() == petentry) && OldSummon->getPetType() != SUMMON_PET) - { - // pet in corpse state can't be summoned - if (OldSummon->IsDead()) - return; - - OldSummon->GetMap()->Remove((Creature*)OldSummon, false); - - float px, py, pz; - m_caster->GetClosePoint(px, py, pz, OldSummon->GetObjectBoundingRadius()); - - OldSummon->Relocate(px, py, pz, OldSummon->GetOrientation()); - m_caster->GetMap()->Add((Creature*)OldSummon); - - if (m_caster->GetTypeId() == TYPEID_PLAYER && OldSummon->isControlled()) - { - ((Player*)m_caster)->PetSpellInitialize(); - } - return; - } - - if (m_caster->GetTypeId() == TYPEID_PLAYER) - OldSummon->Unsummon(OldSummon->getPetType() == HUNTER_PET ? PET_SAVE_AS_DELETED : PET_SAVE_NOT_IN_SLOT, m_caster); - else - return; - } - - CreatureInfo const* cInfo = ObjectMgr::GetCreatureTemplate(petentry); - - // == 0 in case call current pet, check only real summon case - if (petentry && !cInfo) - { - sLog.outErrorDb("EffectSummonPet: creature entry %u not found for spell %u.", petentry, m_spellInfo->Id); - return; - } - Pet* NewSummon = new Pet; - // petentry==0 for hunter "call pet" (current pet summoned if any) - if (m_caster->GetTypeId() == TYPEID_PLAYER && NewSummon->LoadPetFromDB((Player*)m_caster, petentry)) - return; - - // not error in case fail hunter call pet - if (!petentry) + if (m_caster->GetTypeId() == TYPEID_PLAYER) { + switch(m_caster->getClass()) + { + case CLASS_HUNTER: + { + // Everything already taken care of, we are only here because we loaded pet from db successfully + delete NewSummon; + return; + } + default: + { + if (Pet* OldSummon = m_caster->GetPet()) + OldSummon->Unsummon(PET_SAVE_NOT_IN_SLOT, m_caster); + + // Load pet from db; if any to load + if (NewSummon->LoadPetFromDB((Player*)m_caster, petentry)) + { + NewSummon->SetHealth(NewSummon->GetMaxHealth()); + NewSummon->SetPower(POWER_MANA, NewSummon->GetMaxPower(POWER_MANA)); + + NewSummon->SavePetToDB(PET_SAVE_AS_CURRENT); + + return; + } + + NewSummon->setPetType(SUMMON_PET); + } + } + } + else + NewSummon->setPetType(GUARDIAN_PET); + + CreatureInfo const* cInfo = petentry ? ObjectMgr::GetCreatureTemplate(petentry) : nullptr; + if (!cInfo) + { + sLog.outErrorDb("EffectSummonPet: creature entry %u not found for spell %u.", petentry, m_spellInfo->Id); delete NewSummon; return; } @@ -6695,69 +6683,65 @@ void Spell::EffectSummonPet(SpellEffectEntry const* effect) NewSummon->SetRespawnCoord(pos); - uint32 petlevel = m_caster->getLevel(); - NewSummon->setPetType(SUMMON_PET); - - uint32 faction = m_caster->getFaction(); - if (m_caster->GetTypeId() == TYPEID_UNIT) - { - if (((Creature*)m_caster)->IsTotem()) - NewSummon->GetCharmInfo()->SetReactState(REACT_AGGRESSIVE); - else - NewSummon->GetCharmInfo()->SetReactState(REACT_DEFENSIVE); - } + // Level of pet summoned + uint32 level = std::max(m_caster->getLevel() + effect->EffectMultipleValue, 1.0f); + NewSummon->GetCharmInfo()->SetReactState(REACT_DEFENSIVE); NewSummon->SetOwnerGuid(m_caster->GetObjectGuid()); NewSummon->SetCreatorGuid(m_caster->GetObjectGuid()); - NewSummon->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); - NewSummon->setFaction(faction); - NewSummon->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, uint32(time(NULL))); - NewSummon->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0); - NewSummon->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, 1000); + NewSummon->setFaction(m_caster->getFaction()); + NewSummon->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, uint32(time(nullptr))); NewSummon->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); - NewSummon->GetCharmInfo()->SetPetNumber(pet_number, true); - // this enables pet details window (Shift+P) - - if (m_caster->IsPvP()) - NewSummon->SetPvP(true); - - if (m_caster->IsFFAPvP()) - NewSummon->SetFFAPvP(true); - - NewSummon->InitStatsForLevel(petlevel, m_caster); + NewSummon->InitStatsForLevel(level); NewSummon->InitPetCreateSpells(); NewSummon->InitLevelupSpellsForLevel(); NewSummon->InitTalentForLevel(); - if (m_caster->GetTypeId() == TYPEID_PLAYER && NewSummon->getPetType() == SUMMON_PET) - { - // generate new name for summon pet - std::string new_name = sObjectMgr.GeneratePetName(petentry); - if (!new_name.empty()) - NewSummon->SetName(new_name); - } - - if (NewSummon->getPetType() == HUNTER_PET) - { - NewSummon->RemoveByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED); - NewSummon->SetByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_ABANDONED); - } - - NewSummon->AIM_Initialize(); - NewSummon->SetHealth(NewSummon->GetMaxHealth()); - NewSummon->SetPower(POWER_MANA, NewSummon->GetMaxPower(POWER_MANA)); - map->Add((Creature*)NewSummon); + NewSummon->AIM_Initialize(); m_caster->SetPet(NewSummon); DEBUG_LOG("New Pet has guid %u", NewSummon->GetGUIDLow()); if (m_caster->GetTypeId() == TYPEID_PLAYER) { + NewSummon->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); + + NewSummon->SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SUPPORTABLE | UNIT_BYTE2_FLAG_AURAS); + NewSummon->SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); + + NewSummon->GetCharmInfo()->SetPetNumber(pet_number, true); + + // generate new name for summon pet + NewSummon->SetName(sObjectMgr.GeneratePetName(petentry)); + + if (m_caster->IsPvP()) + NewSummon->SetPvP(true); + + if (m_caster->IsFFAPvP()) + NewSummon->SetFFAPvP(true); + NewSummon->SavePetToDB(PET_SAVE_AS_CURRENT); ((Player*)m_caster)->PetSpellInitialize(); } + else + { + // Notify Summoner + if (m_originalCaster && (m_originalCaster != m_caster) + && (m_originalCaster->GetTypeId() == TYPEID_UNIT) && ((Creature*)m_originalCaster)->AI()) + { + ((Creature*)m_originalCaster)->AI()->JustSummoned(NewSummon); + if (m_originalCaster->IsInCombat() && !(NewSummon->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE))) + ((Creature*)NewSummon)->AI()->AttackStart(m_originalCaster->getAttackerForHelper()); + } + else if ((m_caster->GetTypeId() == TYPEID_UNIT) && ((Creature*)m_caster)->AI()) + { + ((Creature*)m_caster)->AI()->JustSummoned(NewSummon); + if (m_caster->IsInCombat() && !(NewSummon->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE))) + ((Creature*)NewSummon)->AI()->AttackStart(m_caster->getAttackerForHelper()); + } + } } void Spell::EffectLearnPetSpell(SpellEffectEntry const* effect) @@ -11490,8 +11474,8 @@ void Spell::EffectActivateRune(SpellEffectEntry const* effect) return; int32 count = damage; // max amount of reset runes - if (plr->ActivateRunes(RuneType(effect->EffectMiscValue), count)) - plr->ResyncRunes(); + + plr->ResyncRunes(); } void Spell::EffectTitanGrip(SpellEffectEntry const* effect) diff --git a/src/game/WorldHandlers/TaxiHandler.cpp b/src/game/WorldHandlers/TaxiHandler.cpp index 171fd1080..7a20c652f 100644 --- a/src/game/WorldHandlers/TaxiHandler.cpp +++ b/src/game/WorldHandlers/TaxiHandler.cpp @@ -186,6 +186,14 @@ void WorldSession::HandleActivateTaxiExpressOpcode(WorldPacket& recv_data) { uint32 node; recv_data >> node; + + if (!_player->m_taxi.IsTaximaskNodeKnown(node) && !_player->IsTaxiCheater()) + { + SendActivateTaxiReply(ERR_TAXINOTVISITED); + recv_data.rpos(recv_data.wpos()); // prevent additional spam at rejected packet + return; + } + nodes.push_back(node); } @@ -282,5 +290,14 @@ void WorldSession::HandleActivateTaxiOpcode(WorldPacket& recv_data) return; } + if (!_player->IsTaxiCheater()) + { + if (!_player->m_taxi.IsTaximaskNodeKnown(nodes[0]) || !_player->m_taxi.IsTaximaskNodeKnown(nodes[1])) + { + SendActivateTaxiReply(ERR_TAXINOTVISITED); + return; + } + } + GetPlayer()->ActivateTaxiPathTo(nodes, npc); } diff --git a/src/game/WorldHandlers/VehicleHandler.cpp b/src/game/WorldHandlers/VehicleHandler.cpp index 81c4cd79e..591eba2d6 100644 --- a/src/game/WorldHandlers/VehicleHandler.cpp +++ b/src/game/WorldHandlers/VehicleHandler.cpp @@ -101,12 +101,16 @@ void WorldSession::HandleChangeSeatsOnControlledVehicle(WorldPacket& recvPacket) DEBUG_LOG("WORLD: Received opcode CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE"); recvPacket.hexlike(); + ObjectGuid srcVehicleGuid; MovementInfo movementInfo; + ObjectGuid destVehicleGuid; + uint8 seat; - recvPacket >> movementInfo; // Not used at the moment - ObjectGuid srcVehicleGuid = movementInfo.GetGuid(); - ObjectGuid destVehicleGuid = movementInfo.GetGuid2(); - uint8 seat = movementInfo.GetByteParam(); + recvPacket >> movementInfo; + + srcVehicleGuid = movementInfo.GetGuid(); + destVehicleGuid = movementInfo.GetGuid2(); + seat = movementInfo.GetByteParam(); TransportInfo* transportInfo = _player->GetTransportInfo(); if (!transportInfo || !transportInfo->IsOnVehicle()) diff --git a/src/game/WorldHandlers/Weather.cpp b/src/game/WorldHandlers/Weather.cpp index c45e7d1c3..0f38a92d9 100644 --- a/src/game/WorldHandlers/Weather.cpp +++ b/src/game/WorldHandlers/Weather.cpp @@ -34,6 +34,7 @@ #include "Log.h" // #include "ObjectMgr.h" // chucky delete ? #include "Util.h" +#include "ProgressBar.h" #ifdef ENABLE_ELUNA #include "LuaEngine.h" #endif /* ENABLE_ELUNA */ @@ -154,8 +155,8 @@ bool Weather::ReGenerate() uint32 chance1 = m_weatherChances->data[season].rainChance; uint32 chance2 = chance1 + m_weatherChances->data[season].snowChance; uint32 chance3 = chance2 + m_weatherChances->data[season].stormChance; - - uint32 rnd = urand(0, 99); + + uint32 rnd = urand(1, 100); if (rnd <= chance1) { m_type = WEATHER_TYPE_RAIN; diff --git a/src/game/WorldHandlers/World.cpp b/src/game/WorldHandlers/World.cpp index 6878efc43..c25d5564a 100644 --- a/src/game/WorldHandlers/World.cpp +++ b/src/game/WorldHandlers/World.cpp @@ -144,19 +144,17 @@ World::~World() // it is assumed that no other thread is accessing this data when the destructor is called. therefore, no locks are necessary ///- Empty the kicked session set - std::for_each(m_sessions.begin(), m_sessions.end(), [](const SessionMap::value_type &p) { delete p.second; }); - m_sessions.clear(); + for (auto const session : m_sessions) + delete session.second; - std::for_each(m_cliCommandQueue.begin(), m_cliCommandQueue.end(), [](const CliCommandHolder *p) { delete p; }); - m_cliCommandQueue.clear(); + for (auto const cliCommand : m_cliCommandQueue) + delete cliCommand; - std::for_each(m_sessionAddQueue.begin(), m_sessionAddQueue.end(), [](const WorldSession *s) { delete s; }); - m_sessionAddQueue.clear(); + for (auto const session : m_sessionAddQueue) + delete session; VMAP::VMapFactory::clear(); MMAP::MMapFactory::clear(); - - delete m_configForceLoadMapIds; } /// Cleanups before world stop @@ -577,6 +575,17 @@ void World::LoadConfigSettings(bool reload) setConfig(CONFIG_BOOL_ADDON_CHANNEL, "AddonChannel", true); setConfig(CONFIG_BOOL_CLEAN_CHARACTER_DB, "CleanCharacterDB", true); setConfig(CONFIG_BOOL_GRID_UNLOAD, "GridUnload", true); + + std::string forceLoadGridOnMaps = sConfig.GetStringDefault("LoadAllGridsOnMaps", ""); + if (!forceLoadGridOnMaps.empty()) + { + unsigned int pos = 0; + unsigned int id; + VMAP::VMapFactory::chompAndTrim(forceLoadGridOnMaps); + while (VMAP::VMapFactory::getNextId(forceLoadGridOnMaps, pos, id)) + m_configForceLoadMapIds.insert(id); + } + setConfig(CONFIG_UINT32_INTERVAL_SAVE, "PlayerSave.Interval", 15 * MINUTE * IN_MILLISECONDS); setConfigMinMax(CONFIG_UINT32_MIN_LEVEL_STAT_SAVE, "PlayerSave.Stats.MinLevel", 0, 0, MAX_LEVEL); setConfig(CONFIG_BOOL_STATS_SAVE_ONLY_ON_LOGOUT, "PlayerSave.Stats.SaveOnlyOnLogout", true); @@ -635,7 +644,7 @@ void World::LoadConfigSettings(bool reload) if (configNoReload(reload, CONFIG_UINT32_MAX_PLAYER_LEVEL, "MaxPlayerLevel", DEFAULT_MAX_LEVEL)) { setConfigMinMax(CONFIG_UINT32_MAX_PLAYER_LEVEL, "MaxPlayerLevel", DEFAULT_MAX_LEVEL, 1, DEFAULT_MAX_LEVEL); } - setConfigMinMax(CONFIG_UINT32_START_PLAYER_LEVEL, "StartPlayerLevel", 1, 1, getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL)); + setConfigMinMax(CONFIG_UINT32_START_PLAYER_LEVEL, "StartPlayerLevel", 1, 1, (uint32)getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL)); setConfigMinMax(CONFIG_UINT32_START_HEROIC_PLAYER_LEVEL, "StartHeroicPlayerLevel", 55, 1, getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL)); setConfigMinMax(CONFIG_UINT64_START_PLAYER_MONEY, "StartPlayerMoney", 0, 0, MAX_MONEY_AMOUNT); @@ -787,6 +796,7 @@ void World::LoadConfigSettings(bool reload) setConfig(CONFIG_BOOL_BATTLEGROUND_CAST_DESERTER, "Battleground.CastDeserter", true); setConfigMinMax(CONFIG_UINT32_BATTLEGROUND_QUEUE_ANNOUNCER_JOIN, "Battleground.QueueAnnouncer.Join", 0, 0, 2); setConfig(CONFIG_BOOL_BATTLEGROUND_QUEUE_ANNOUNCER_START, "Battleground.QueueAnnouncer.Start", false); + setConfig(CONFIG_BOOL_BATTLEGROUND_SCORE_STATISTICS, "Battleground.ScoreStatistics", false); 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); diff --git a/src/game/WorldHandlers/World.h b/src/game/WorldHandlers/World.h index 482809add..8581df617 100644 --- a/src/game/WorldHandlers/World.h +++ b/src/game/WorldHandlers/World.h @@ -612,6 +612,9 @@ class World /// Get a server configuration element (see #eConfigBoolValues) bool getConfig(eConfigBoolValues index) const { return m_configBoolValues[index]; } + /// Get configuration about force-loaded maps + bool isForceLoadMap(uint32 id) const { return m_configForceLoadMapIds.find(id) != m_configForceLoadMapIds.end(); } + /// Are we on a "Player versus Player" server? bool IsPvPRealm() { return (getConfig(CONFIG_UINT32_GAME_TYPE) == REALM_TYPE_PVP || getConfig(CONFIG_UINT32_GAME_TYPE) == REALM_TYPE_RPPVP || getConfig(CONFIG_UINT32_GAME_TYPE) == REALM_TYPE_FFA_PVP); } bool IsFFAPvPRealm() { return getConfig(CONFIG_UINT32_GAME_TYPE) == REALM_TYPE_FFA_PVP; } @@ -766,9 +769,10 @@ class World // used versions std::string m_DBVersion; + std::string m_CreatureEventAIVersion; // List of Maps that should be force-loaded on startup - std::set* m_configForceLoadMapIds; + std::set m_configForceLoadMapIds; }; extern uint32 realmID; diff --git a/src/game/movement/MoveSplineInit.cpp b/src/game/movement/MoveSplineInit.cpp index 5ff8912ab..f9b6664b8 100644 --- a/src/game/movement/MoveSplineInit.cpp +++ b/src/game/movement/MoveSplineInit.cpp @@ -171,7 +171,7 @@ namespace Movement { // mix existing state into new args.flags.walkmode = unit.m_movementInfo.HasMovementFlag(MOVEFLAG_WALK_MODE); - args.flags.flying = unit.m_movementInfo.HasMovementFlag((MovementFlags)(MOVEFLAG_FLYING | MOVEFLAG_LEVITATING)); + args.flags.flying = unit.m_movementInfo.HasMovementFlag((MovementFlags)(MOVEFLAG_CAN_FLY | MOVEFLAG_FLYING | MOVEFLAG_LEVITATING)); } void MoveSplineInit::SetFacing(const Unit* target) diff --git a/src/game/movement/MoveSplineInit.h b/src/game/movement/MoveSplineInit.h index 5b63034d2..5192dace4 100644 --- a/src/game/movement/MoveSplineInit.h +++ b/src/game/movement/MoveSplineInit.h @@ -43,7 +43,7 @@ namespace Movement * @brief Initializes and launches spline movement * */ - class MoveSplineInit + class MANGOS_DLL_SPEC MoveSplineInit { public: diff --git a/src/game/vmap/VMapFactory.cpp b/src/game/vmap/VMapFactory.cpp index c89b1cb0b..df24f5a9e 100644 --- a/src/game/vmap/VMapFactory.cpp +++ b/src/game/vmap/VMapFactory.cpp @@ -30,7 +30,7 @@ using namespace G3D; namespace VMAP { - void chompAndTrim(std::string& str) + void VMapFactory::chompAndTrim(std::string& str) { while (str.length() > 0) { @@ -64,7 +64,7 @@ namespace VMAP //=============================================== // result false, if no more id are found - bool getNextId(const std::string& pString, unsigned int& pStartPos, unsigned int& pId) + bool VMapFactory::getNextId(const std::string& pString, unsigned int& pStartPos, unsigned int& pId) { bool result = false; unsigned int i; diff --git a/src/game/vmap/VMapFactory.h b/src/game/vmap/VMapFactory.h index 7b184e1da..d0429662a 100644 --- a/src/game/vmap/VMapFactory.h +++ b/src/game/vmap/VMapFactory.h @@ -43,6 +43,9 @@ namespace VMAP static void preventSpellsFromBeingTestedForLoS(const char* pSpellIdString); static bool checkSpellForLoS(unsigned int pSpellId); + + static void chompAndTrim(std::string& str); + static bool getNextId(const std::string& pString, unsigned int& pStartPos, unsigned int& pId); }; } #endif diff --git a/src/mangosd/mangosd.conf.dist.in b/src/mangosd/mangosd.conf.dist.in index 4b578e113..b0f1e5d1d 100644 --- a/src/mangosd/mangosd.conf.dist.in +++ b/src/mangosd/mangosd.conf.dist.in @@ -113,6 +113,12 @@ BindIP = "0.0.0.0" # Default: 1 (unload grids) # 0 (do not unload grids) # +# LoadAllGridsOnMaps +# Load grids of maps at server startup (if you have lot memory you can try it to have a living world always loaded) +# This also allow ALL creatures on the given maps to update their grid without any player around. +# Default: "" (don't load all grids at startup) +# "mapId1[,mapId2[..]]" (DO load all grids on the given maps- Experimental and very resource consumming) +# # GridCleanUpDelay # Grid clean up delay (in milliseconds) # Default: 300000 (5 min) @@ -207,6 +213,7 @@ PlayerLimit = 100 SaveRespawnTimeImmediately = 1 MaxOverspeedPings = 2 GridUnload = 1 +LoadAllGridsOnMaps = "" GridCleanUpDelay = 300000 MapUpdateInterval = 100 ChangeWeatherInterval = 600000 @@ -1487,6 +1494,11 @@ Death.Ghost.RunSpeed.Battleground = 1.0 # 1 (send to joined player only) # 2 (send to all players) # +# Battleground.ScoreStatistics +# Enable Battleground scores storage in database +# Default: 0 (disable) +# 1 (enable) +# # Battleground.QueueAnnouncer.Start # Enable queue announcer posting to chat at BG start # Default: 0 (disable) @@ -1512,6 +1524,7 @@ Death.Ghost.RunSpeed.Battleground = 1.0 Battleground.CastDeserter = 1 Battleground.QueueAnnouncer.Join = 0 Battleground.QueueAnnouncer.Start = 0 +Battleground.ScoreStatistics = 0 Battleground.InvitationType = 0 BattleGround.PrematureFinishTimer = 300000 BattleGround.PremadeGroupWaitForMatch = 1800000 diff --git a/src/modules/Eluna/UnitMethods.h b/src/modules/Eluna/UnitMethods.h index efd656f83..3ecc30dc3 100644 --- a/src/modules/Eluna/UnitMethods.h +++ b/src/modules/Eluna/UnitMethods.h @@ -105,7 +105,7 @@ namespace LuaUnit Creature* creature = Eluna::CHECKOBJ(L, 2); #ifndef TRINITY - Eluna::Push(L, unit->isInAccessablePlaceFor(creature)); + Eluna::Push(L, unit->IsInAccessablePlaceFor(creature)); #else Eluna::Push(L, unit->isInAccessiblePlaceFor(creature)); #endif @@ -1707,12 +1707,12 @@ namespace LuaUnit if (apply) { - unit->SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY); + unit->SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SUPPORTABLE); unit->CombatStop(); unit->CombatStopWithPets(); } else - unit->RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY); + unit->RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SUPPORTABLE); return 0; } diff --git a/src/modules/SD3/base/escort_ai.cpp b/src/modules/SD3/base/escort_ai.cpp index f64ac70da..5db895290 100644 --- a/src/modules/SD3/base/escort_ai.cpp +++ b/src/modules/SD3/base/escort_ai.cpp @@ -184,7 +184,7 @@ bool npc_escortAI::AssistPlayerInCombat(Unit* pWho) void npc_escortAI::MoveInLineOfSight(Unit* pWho) { - if (pWho->IsTargetableForAttack() && pWho->isInAccessablePlaceFor(m_creature)) + if (pWho->IsTargetableForAttack() && pWho->IsInAccessablePlaceFor(m_creature)) { // AssistPlayerInCombat can start attack, so return if true if (HasEscortState(STATE_ESCORT_ESCORTING) && AssistPlayerInCombat(pWho)) diff --git a/src/modules/SD3/base/follower_ai.cpp b/src/modules/SD3/base/follower_ai.cpp index 00aa14aea..12fef1361 100644 --- a/src/modules/SD3/base/follower_ai.cpp +++ b/src/modules/SD3/base/follower_ai.cpp @@ -122,7 +122,7 @@ bool FollowerAI::AssistPlayerInCombat(Unit* pWho) void FollowerAI::MoveInLineOfSight(Unit* pWho) { - if (pWho->IsTargetableForAttack() && pWho->isInAccessablePlaceFor(m_creature)) + if (pWho->IsTargetableForAttack() && pWho->IsInAccessablePlaceFor(m_creature)) { // AssistPlayerInCombat can start attack, so return if true if (HasFollowState(STATE_FOLLOW_INPROGRESS) && AssistPlayerInCombat(pWho)) diff --git a/src/modules/SD3/base/pet_ai.cpp b/src/modules/SD3/base/pet_ai.cpp index 6db6dec65..aa0ebe7f1 100644 --- a/src/modules/SD3/base/pet_ai.cpp +++ b/src/modules/SD3/base/pet_ai.cpp @@ -56,7 +56,7 @@ void ScriptedPetAI::MoveInLineOfSight(Unit* pWho) } if (m_creature->CanInitiateAttack() && pWho->IsTargetableForAttack() && - m_creature->IsHostileTo(pWho) && pWho->isInAccessablePlaceFor(m_creature)) + m_creature->IsHostileTo(pWho) && pWho->IsInAccessablePlaceFor(m_creature)) { if (!m_creature->CanFly() && m_creature->GetDistanceZ(pWho) > CREATURE_Z_ATTACK_RANGE) { diff --git a/src/modules/SD3/include/sc_creature.cpp b/src/modules/SD3/include/sc_creature.cpp index b15f5c76b..625da696f 100644 --- a/src/modules/SD3/include/sc_creature.cpp +++ b/src/modules/SD3/include/sc_creature.cpp @@ -74,7 +74,7 @@ bool ScriptedAI::IsVisible(Unit* pWho) const void ScriptedAI::MoveInLineOfSight(Unit* pWho) { if (m_creature->CanInitiateAttack() && pWho->IsTargetableForAttack() && - m_creature->IsHostileTo(pWho) && pWho->isInAccessablePlaceFor(m_creature)) + m_creature->IsHostileTo(pWho) && pWho->IsInAccessablePlaceFor(m_creature)) { if (!m_creature->CanFly() && m_creature->GetDistanceZ(pWho) > CREATURE_Z_ATTACK_RANGE) { diff --git a/src/modules/SD3/scripts/eastern_kingdoms/burning_steppes.cpp b/src/modules/SD3/scripts/eastern_kingdoms/burning_steppes.cpp index ed2ed4c09..999a7c6ed 100644 --- a/src/modules/SD3/scripts/eastern_kingdoms/burning_steppes.cpp +++ b/src/modules/SD3/scripts/eastern_kingdoms/burning_steppes.cpp @@ -59,14 +59,14 @@ struct npc_ragged_john : public CreatureScript { if (who->HasAura(16468, EFFECT_INDEX_0)) { - if (who->GetTypeId() == TYPEID_PLAYER && m_creature->IsWithinDistInMap(who, 15) && who->isInAccessablePlaceFor(m_creature)) + if (who->GetTypeId() == TYPEID_PLAYER && m_creature->IsWithinDistInMap(who, 15) && who->IsInAccessablePlaceFor(m_creature)) { DoCastSpellIfCan(who, 16472); ((Player*)who)->AreaExploredOrEventHappens(4866); } } - if (!m_creature->getVictim() && who->IsTargetableForAttack() && (m_creature->IsHostileTo(who)) && who->isInAccessablePlaceFor(m_creature)) + if (!m_creature->getVictim() && who->IsTargetableForAttack() && (m_creature->IsHostileTo(who)) && who->IsInAccessablePlaceFor(m_creature)) { if (!m_creature->CanFly() && m_creature->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE) { diff --git a/src/modules/SD3/scripts/kalimdor/the_barrens.cpp b/src/modules/SD3/scripts/kalimdor/the_barrens.cpp index 6e29c2717..f42166ee1 100644 --- a/src/modules/SD3/scripts/kalimdor/the_barrens.cpp +++ b/src/modules/SD3/scripts/kalimdor/the_barrens.cpp @@ -1247,7 +1247,7 @@ struct horde_defender : public CreatureScript void MoveInLineOfSight(Unit* u) override { if (m_creature->CanInitiateAttack() && u->IsTargetableForAttack() && - m_creature->IsHostileTo(u) && u->isInAccessablePlaceFor(m_creature)) + m_creature->IsHostileTo(u) && u->IsInAccessablePlaceFor(m_creature)) { float attackRadius = 38.0f; if (m_creature->IsWithinDistInMap(u, attackRadius) && m_creature->IsWithinLOSInMap(u)) @@ -1330,7 +1330,7 @@ struct kolkar_invader : public CreatureScript void MoveInLineOfSight(Unit* u) override { if (m_creature->CanInitiateAttack() && u->IsTargetableForAttack() && - m_creature->IsHostileTo(u) && u->isInAccessablePlaceFor(m_creature)) + m_creature->IsHostileTo(u) && u->IsInAccessablePlaceFor(m_creature)) { float attackRadius = 38.0f; if (m_creature->IsWithinDistInMap(u, attackRadius) && m_creature->IsWithinLOSInMap(u)) diff --git a/src/modules/SD3/scripts/outland/auchindoun/sethekk_halls/boss_talon_king_ikiss.cpp b/src/modules/SD3/scripts/outland/auchindoun/sethekk_halls/boss_talon_king_ikiss.cpp index 7213a5e61..ee7485ee0 100644 --- a/src/modules/SD3/scripts/outland/auchindoun/sethekk_halls/boss_talon_king_ikiss.cpp +++ b/src/modules/SD3/scripts/outland/auchindoun/sethekk_halls/boss_talon_king_ikiss.cpp @@ -100,7 +100,7 @@ struct boss_talon_king_ikiss : public CreatureScript void MoveInLineOfSight(Unit* pWho) override { - if (!m_creature->getVictim() && pWho->IsTargetableForAttack() && (m_creature->IsHostileTo(pWho)) && pWho->isInAccessablePlaceFor(m_creature)) + if (!m_creature->getVictim() && pWho->IsTargetableForAttack() && (m_creature->IsHostileTo(pWho)) && pWho->IsInAccessablePlaceFor(m_creature)) { if (!m_bIntro && m_creature->IsWithinDistInMap(pWho, 100.0f)) { diff --git a/src/modules/SD3/scripts/outland/hellfire_peninsula.cpp b/src/modules/SD3/scripts/outland/hellfire_peninsula.cpp index 871e859ea..6579bb357 100644 --- a/src/modules/SD3/scripts/outland/hellfire_peninsula.cpp +++ b/src/modules/SD3/scripts/outland/hellfire_peninsula.cpp @@ -179,9 +179,12 @@ struct npc_ancestral_wolf : public CreatureScript void Reset() override { - m_creature->CastSpell(m_creature, SPELL_ANCESTRAL_WOLF_BUFF, true); + DoCastSpellIfCan(m_creature, SPELL_ANCESTRAL_WOLF_BUFF); } + void AttackStart(Unit* /*pWho*/) override { } + void MoveInLineOfSight(Unit* /*pWho*/) override { } + void WaypointReached(uint32 uiPointId) override { switch (uiPointId) diff --git a/src/shared/Common/Common.h b/src/shared/Common/Common.h index c53a4e3a2..f42591ac7 100644 --- a/src/shared/Common/Common.h +++ b/src/shared/Common/Common.h @@ -72,7 +72,7 @@ #include #include #include -#include +#include #include #include #include @@ -172,7 +172,7 @@ typedef off_t ACE_OFF_T; * @param f * @return float */ -inline float finiteAlways(float f) { return finite(f) ? f : 0.0f; } +inline float finiteAlways(float f) { return std::isfinite(f) ? f : 0.0f; } #define atol(a) strtoul( a, NULL, 10) diff --git a/src/shared/Utilities/Util.cpp b/src/shared/Utilities/Util.cpp index 91b0ac584..cbe522669 100644 --- a/src/shared/Utilities/Util.cpp +++ b/src/shared/Utilities/Util.cpp @@ -65,7 +65,7 @@ uint32 WorldTimer::getMSTime_internal(bool savetime /*= false*/) // calculate time diff between two world ticks // special case: curr_time < old_time - we suppose that our time has not ticked at all // this should be constant value otherwise it is possible that our time can start ticking backwards until next world tick!!! - uint64 diff = 0; + ACE_UINT64 diff = 0; (currTime - g_SystemTickTime).msec(diff); // lets calculate current world time diff --git a/src/shared/revision.h b/src/shared/revision.h index c9f5a184a..4bdf44c09 100644 --- a/src/shared/revision.h +++ b/src/shared/revision.h @@ -33,11 +33,11 @@ #define CHAR_DB_VERSION_NR 21 #define CHAR_DB_STRUCTURE_NR 2 - #define CHAR_DB_CONTENT_NR 1 + #define CHAR_DB_CONTENT_NR 2 #define CHAR_DB_UPDATE_DESCRIPTION "match_client_limits" #define WORLD_DB_VERSION_NR 21 - #define WORLD_DB_STRUCTURE_NR 1 - #define WORLD_DB_CONTENT_NR 0 + #define WORLD_DB_STRUCTURE_NR 2 + #define WORLD_DB_CONTENT_NR 3 #define WORLD_DB_UPDATE_DESCRIPTION "script_binding populated" #endif // __REVISION_H__ diff --git a/src/tools/Extractor_projects/Movemap-Generator/CMakeLists.txt b/src/tools/Extractor_projects/Movemap-Generator/CMakeLists.txt index ec5c72371..50ab3573a 100644 --- a/src/tools/Extractor_projects/Movemap-Generator/CMakeLists.txt +++ b/src/tools/Extractor_projects/Movemap-Generator/CMakeLists.txt @@ -67,19 +67,11 @@ else() target_link_libraries(${EXECUTABLE_NAME} ACE) endif() -target_link_libraries(${EXECUTABLE_NAME} g3dlite vmap detour recast zlib shared) - if(UNIX) - if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") - set(EXECUTABLE_LINK_FLAGS "-Wl,--no-as-needed -lrt -pthread ${EXECUTABLE_LINK_FLAGS}") - elseif(CMAKE_SYSTEM_NAME MATCHES "Linux") - set(EXECUTABLE_LINK_FLAGS "-Wl,--no-as-needed -ldl -pthread -lrt ${EXECUTABLE_LINK_FLAGS}") - endif() + target_link_libraries(${EXECUTABLE_NAME} rt) endif() -if(APPLE) - set(EXECUTABLE_LINK_FLAGS "-framework CoreServices ${EXECUTABLE_LINK_FLAGS}") -endif() +target_link_libraries(${EXECUTABLE_NAME} g3dlite vmap detour recast zlib shared) #Output the compiled exes to build/bin/$(Configuration)/tools directory on windows by default if(WIN32) diff --git a/src/tools/Extractor_projects/vmap-assembler/CMakeLists.txt b/src/tools/Extractor_projects/vmap-assembler/CMakeLists.txt index b3edd4610..96e65f549 100644 --- a/src/tools/Extractor_projects/vmap-assembler/CMakeLists.txt +++ b/src/tools/Extractor_projects/vmap-assembler/CMakeLists.txt @@ -47,22 +47,20 @@ else() target_link_libraries(${EXECUTABLE_NAME} ACE) endif() +if(UNIX) + target_link_libraries(${EXECUTABLE_NAME} rt dl) +endif() + target_link_libraries(${EXECUTABLE_NAME} vmap g3dlite zlib) set(EXECUTABLE_LINK_FLAGS "") if(UNIX) - if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") - set(EXECUTABLE_LINK_FLAGS "-Wl,--no-as-needed -lrt -pthread ${EXECUTABLE_LINK_FLAGS}") - elseif(CMAKE_SYSTEM_NAME MATCHES "Linux") - set(EXECUTABLE_LINK_FLAGS "-Wl,--no-as-needed -ldl -pthread -lrt ${EXECUTABLE_LINK_FLAGS}") + if(CMAKE_C_COMPILER MATCHES "clang" OR CMAKE_C_COMPILER_ID STREQUAL "Clang") + set(EXECUTABLE_LINK_FLAGS "-pthread${EXECUTABLE_LINK_FLAGS}") endif() endif() -if(APPLE) - set(EXECUTABLE_LINK_FLAGS "-framework CoreServices ${EXECUTABLE_LINK_FLAGS}") -endif() - set_target_properties(${EXECUTABLE_NAME} PROPERTIES LINK_FLAGS "${EXECUTABLE_LINK_FLAGS}") #Output the compiled exes to build/bin/$(Configuration)/tools directory on windows by default diff --git a/src/tools/Extractor_projects/vmap-extractor/CMakeLists.txt b/src/tools/Extractor_projects/vmap-extractor/CMakeLists.txt index db3be8eae..b63365c76 100644 --- a/src/tools/Extractor_projects/vmap-extractor/CMakeLists.txt +++ b/src/tools/Extractor_projects/vmap-extractor/CMakeLists.txt @@ -43,7 +43,7 @@ add_executable(${EXECUTABLE_NAME} ${EXECUTABLE_SRCS} ${CMAKE_SOURCE_DIR}/src/tools/Extractor_projects/shared/ExtractorCommon.cpp ) -target_link_libraries(${EXECUTABLE_NAME} StormLib loadlib bzip2 zlib) +target_link_libraries(${EXECUTABLE_NAME} loadlib bzip2 zlib StormLib) #Output the compiled exes to build/bin/$(Configuration)/tools directory on windows by default if(WIN32)