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