aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/server/game/Achievements/AchievementMgr.cpp34
-rwxr-xr-xsrc/server/game/Achievements/AchievementMgr.h4
-rwxr-xr-xsrc/server/game/Battlegrounds/Battleground.cpp2
-rwxr-xr-xsrc/server/game/Chat/Chat.cpp8
-rwxr-xr-xsrc/server/game/Entities/Creature/Creature.cpp18
-rwxr-xr-xsrc/server/game/Entities/Player/Player.cpp142
-rwxr-xr-xsrc/server/game/Entities/Player/Player.h12
-rwxr-xr-xsrc/server/game/Entities/Unit/Unit.cpp128
-rwxr-xr-xsrc/server/game/Events/GameEventMgr.cpp60
-rwxr-xr-xsrc/server/game/Events/GameEventMgr.h3
-rwxr-xr-xsrc/server/game/Globals/ObjectMgr.h3
-rwxr-xr-xsrc/server/game/Maps/MapInstanced.cpp12
-rwxr-xr-xsrc/server/game/Maps/ZoneScript.h2
-rwxr-xr-xsrc/server/game/Quests/QuestDef.cpp6
-rwxr-xr-xsrc/server/game/Quests/QuestDef.h1
-rw-r--r--[-rwxr-xr-x]src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp12
-rwxr-xr-xsrc/server/game/Spells/Auras/SpellAuras.cpp10
-rwxr-xr-xsrc/server/game/Spells/Spell.cpp2
-rwxr-xr-xsrc/server/game/Spells/SpellEffects.cpp43
-rw-r--r--src/server/game/Spells/SpellInfo.cpp8
-rw-r--r--src/server/game/Spells/SpellInfo.h1
-rwxr-xr-xsrc/server/game/Spells/SpellMgr.cpp11
-rwxr-xr-xsrc/server/game/World/World.cpp11
-rwxr-xr-xsrc/server/game/World/World.h1
-rw-r--r--src/server/scripts/Commands/cs_account.cpp1
-rw-r--r--src/server/scripts/Commands/cs_reload.cpp18
-rwxr-xr-xsrc/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp6
-rw-r--r--src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp212
-rw-r--r--src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp6
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp2
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp6
-rw-r--r--src/server/scripts/Outland/blades_edge_mountains.cpp561
-rw-r--r--src/server/scripts/Spells/spell_paladin.cpp39
-rw-r--r--src/server/scripts/Spells/spell_shaman.cpp30
-rw-r--r--[-rwxr-xr-x]src/server/shared/Database/Implementation/CharacterDatabase.cpp10
-rw-r--r--[-rwxr-xr-x]src/server/shared/Database/Implementation/CharacterDatabase.h10
36 files changed, 1211 insertions, 224 deletions
diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp
index 7efd5481ae8..02686993fbb 100755
--- a/src/server/game/Achievements/AchievementMgr.cpp
+++ b/src/server/game/Achievements/AchievementMgr.cpp
@@ -130,19 +130,19 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE:
if (!classRace.class_id && !classRace.race_id)
{
- sLog->outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_CLASS_RACE (%u) must not have 0 in either value field, ignored.",
+ sLog->outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE (%u) must not have 0 in either value field, ignored.",
criteria->ID, criteria->requiredType, dataType);
return false;
}
if (classRace.class_id && ((1 << (classRace.class_id-1)) & CLASSMASK_ALL_PLAYABLE) == 0)
{
- sLog->outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_CLASS_RACE (%u) has non-existing class in value1 (%u), ignored.",
+ sLog->outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE (%u) has non-existing class in value1 (%u), ignored.",
criteria->ID, criteria->requiredType, dataType, classRace.class_id);
return false;
}
if (classRace.race_id && ((1 << (classRace.race_id-1)) & RACEMASK_ALL_PLAYABLE) == 0)
{
- sLog->outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_CLASS_RACE (%u) has non-existing race in value2 (%u), ignored.",
+ sLog->outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE (%u) has non-existing race in value2 (%u), ignored.",
criteria->ID, criteria->requiredType, dataType, classRace.race_id);
return false;
}
@@ -277,6 +277,26 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
return false;
}
return true;
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE:
+ if (!classRace.class_id && !classRace.race_id)
+ {
+ sLog->outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE (%u) must not have 0 in either value field, ignored.",
+ criteria->ID, criteria->requiredType, dataType);
+ return false;
+ }
+ if (classRace.class_id && ((1 << (classRace.class_id-1)) & CLASSMASK_ALL_PLAYABLE) == 0)
+ {
+ sLog->outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE (%u) has non-existing class in value1 (%u), ignored.",
+ criteria->ID, criteria->requiredType, dataType, classRace.class_id);
+ return false;
+ }
+ if (classRace.race_id && ((1 << (classRace.race_id-1)) & RACEMASK_ALL_PLAYABLE) == 0)
+ {
+ sLog->outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE (%u) has non-existing race in value2 (%u), ignored.",
+ criteria->ID, criteria->requiredType, dataType, classRace.race_id);
+ return false;
+ }
+ return true;
default:
sLog->outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) has data for non-supported data type (%u), ignored.", criteria->ID, criteria->requiredType, dataType);
return false;
@@ -301,6 +321,14 @@ bool AchievementCriteriaData::Meets(uint32 criteria_id, Player const* source, Un
if (classRace.race_id && classRace.race_id != target->ToPlayer()->getRace())
return false;
return true;
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE:
+ if (!source || source->GetTypeId() != TYPEID_PLAYER)
+ return false;
+ if (classRace.class_id && classRace.class_id != source->ToPlayer()->getClass())
+ return false;
+ if (classRace.race_id && classRace.race_id != source->ToPlayer()->getRace())
+ return false;
+ return true;
case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH:
if (!target || target->GetTypeId() != TYPEID_PLAYER)
return false;
diff --git a/src/server/game/Achievements/AchievementMgr.h b/src/server/game/Achievements/AchievementMgr.h
index 640b5de98c9..c7d838fcb44 100755
--- a/src/server/game/Achievements/AchievementMgr.h
+++ b/src/server/game/Achievements/AchievementMgr.h
@@ -63,9 +63,10 @@ enum AchievementCriteriaDataType
ACHIEVEMENT_CRITERIA_DATA_INSTANCE_SCRIPT = 18, // 0 0 maker instance script call for check current criteria requirements fit
ACHIEVEMENT_CRITERIA_DATA_TYPE_S_EQUIPED_ITEM = 19, // item_level item_quality for equipped item in slot to check item level and quality
ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_ID = 20, // map_id 0 player must be on map with id in map_id
+ ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE = 21, // class_id race_id
};
-#define MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE 21 // maximum value in AchievementCriteriaDataType enum
+#define MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE 22 // maximum value in AchievementCriteriaDataType enum
class Player;
class Unit;
@@ -82,6 +83,7 @@ struct AchievementCriteriaData
uint32 id;
} creature;
// ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE = 2
+ // ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE = 21
struct
{
uint32 class_id;
diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp
index 8ca4b73d105..6a758695116 100755
--- a/src/server/game/Battlegrounds/Battleground.cpp
+++ b/src/server/game/Battlegrounds/Battleground.cpp
@@ -1245,8 +1245,6 @@ void Battleground::EventPlayerLoggedOut(Player* player)
if (GetAlivePlayersCountByTeam(player->GetTeam()) <= 1 && GetPlayersCountByTeam(GetOtherTeam(player->GetTeam())))
EndBattleground(GetOtherTeam(player->GetTeam()));
}
-
- player->LeaveBattleground();
}
// This method should be called only once ... it adds pointer to queue
diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp
index 91ccb42f1d0..035fbaf5b78 100755
--- a/src/server/game/Chat/Chat.cpp
+++ b/src/server/game/Chat/Chat.cpp
@@ -885,11 +885,11 @@ bool ChatHandler::ShowHelpForSubCommands(ChatCommand* table, char const* cmd, ch
std::string list;
for (uint32 i = 0; table[i].Name != NULL; ++i)
{
- // must be available (ignore handler existence for show command with possibe avalable subcomands
+ // must be available (ignore handler existence for show command with possible available subcommands)
if (!isAvailable(table[i]))
continue;
- /// for empty subcmd show all available
+ // for empty subcmd show all available
if (*subcmd && !hasStringAbbr(table[i].Name, subcmd))
continue;
@@ -924,7 +924,7 @@ bool ChatHandler::ShowHelpForCommand(ChatCommand* table, const char* cmd)
{
for (uint32 i = 0; table[i].Name != NULL; ++i)
{
- // must be available (ignore handler existence for show command with possibe avalable subcomands
+ // must be available (ignore handler existence for show command with possible available subcommands)
if (!isAvailable(table[i]))
continue;
@@ -954,7 +954,7 @@ bool ChatHandler::ShowHelpForCommand(ChatCommand* table, const char* cmd)
{
for (uint32 i = 0; table[i].Name != NULL; ++i)
{
- // must be available (ignore handler existence for show command with possibe avalable subcomands
+ // must be available (ignore handler existence for show command with possible available subcommands)
if (!isAvailable(table[i]))
continue;
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index 96890eb3095..3e06f9e73db 100755
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -1507,9 +1507,6 @@ void Creature::setDeathState(DeathState s)
if (m_formation && m_formation->getLeader() == this)
m_formation->FormationReset(true);
- if (ZoneScript* zoneScript = GetZoneScript())
- zoneScript->OnCreatureDeath(this);
-
if ((canFly() || IsFlying()) && FallGround())
return;
@@ -1644,7 +1641,20 @@ bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo)
if (!spellInfo)
return false;
- if (GetCreatureInfo()->MechanicImmuneMask & (1 << (spellInfo->Mechanic - 1)))
+ // Spells that don't have effectMechanics.
+ if (!spellInfo->HasAnyEffectMechanic() && GetCreatureInfo()->MechanicImmuneMask & (1 << (spellInfo->Mechanic - 1)))
+ return true;
+
+ // This check must be done instead of 'if (GetCreatureInfo()->MechanicImmuneMask & (1 << (spellInfo->Mechanic - 1)))' for not break
+ // the check of mechanic immunity on DB (tested) because GetCreatureInfo()->MechanicImmuneMask and m_spellImmune[IMMUNITY_MECHANIC] don't have same data.
+ bool immunedToAllEffects = true;
+ for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
+ if (!IsImmunedToSpellEffect(spellInfo, i))
+ {
+ immunedToAllEffects = false;
+ break;
+ }
+ if (immunedToAllEffects)
return true;
return Unit::IsImmunedToSpell(spellInfo);
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 10ec8500923..09b19223039 100755
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -14529,7 +14529,7 @@ bool Player::CanSeeStartQuest(Quest const* quest)
SatisfyQuestExclusiveGroup(quest, false) && SatisfyQuestReputation(quest, false) &&
SatisfyQuestPreviousQuest(quest, false) && SatisfyQuestNextChain(quest, false) &&
SatisfyQuestPrevChain(quest, false) && SatisfyQuestDay(quest, false) && SatisfyQuestWeek(quest, false) &&
- !DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, quest->GetQuestId(), this))
+ SatisfyQuestSeasonal(quest, false) && !DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, quest->GetQuestId(), this))
{
return getLevel() + sWorld->getIntConfig(CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF) >= quest->GetMinLevel();
}
@@ -14545,7 +14545,7 @@ bool Player::CanTakeQuest(Quest const* quest, bool msg)
&& SatisfyQuestPreviousQuest(quest, msg) && SatisfyQuestTimed(quest, msg)
&& SatisfyQuestNextChain(quest, msg) && SatisfyQuestPrevChain(quest, msg)
&& SatisfyQuestDay(quest, msg) && SatisfyQuestWeek(quest, msg)
- && !DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, quest->GetQuestId(), this)
+ && SatisfyQuestSeasonal(quest,msg) && !DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, quest->GetQuestId(), this)
&& SatisfyQuestConditions(quest, msg);
}
@@ -14669,7 +14669,7 @@ bool Player::CanRewardQuest(Quest const* quest, bool msg)
return false;
// daily quest can't be rewarded (25 daily quest already completed)
- if (!SatisfyQuestDay(quest, true) || !SatisfyQuestWeek(quest, true))
+ if (!SatisfyQuestDay(quest, true) || !SatisfyQuestWeek(quest, true) || !SatisfyQuestSeasonal(quest,true))
return false;
// rewarded and not repeatable quest (only cheating case, then ignore without message)
@@ -14978,6 +14978,8 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
}
else if (quest->IsWeekly())
SetWeeklyQuestStatus(quest_id);
+ else if (quest->IsSeasonal())
+ SetSeasonalQuestStatus(quest_id);
RemoveActiveQuest(quest_id);
m_RewardedQuests.insert(quest_id);
@@ -15333,7 +15335,7 @@ bool Player::SatisfyQuestExclusiveGroup(Quest const* qInfo, bool msg)
// not allow have daily quest if daily quest from exclusive group already recently completed
Quest const* Nquest = sObjectMgr->GetQuestTemplate(exclude_Id);
- if (!SatisfyQuestDay(Nquest, false) || !SatisfyQuestWeek(Nquest, false))
+ if (!SatisfyQuestDay(Nquest, false) || !SatisfyQuestWeek(Nquest, false) || !SatisfyQuestSeasonal(Nquest,false))
{
if (msg)
SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
@@ -15342,7 +15344,7 @@ bool Player::SatisfyQuestExclusiveGroup(Quest const* qInfo, bool msg)
}
// alternative quest already started or completed - but don't check rewarded states if both are repeatable
- if (GetQuestStatus(exclude_Id) != QUEST_STATUS_NONE || (!(qInfo->IsRepeatable() && Nquest->IsRepeatable()) && m_RewardedQuests.find(exclude_Id) != m_RewardedQuests.end()))
+ if (GetQuestStatus(exclude_Id) != QUEST_STATUS_NONE || (!(qInfo->IsRepeatable() && Nquest->IsRepeatable()) && (m_RewardedQuests.find(exclude_Id) != m_RewardedQuests.end())))
{
if (msg)
SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
@@ -15443,6 +15445,19 @@ bool Player::SatisfyQuestWeek(Quest const* qInfo, bool /*msg*/)
return m_weeklyquests.find(qInfo->GetQuestId()) == m_weeklyquests.end();
}
+bool Player::SatisfyQuestSeasonal(Quest const* qInfo, bool /*msg*/)
+{
+ if (!qInfo->IsSeasonal() || m_seasonalquests.empty())
+ return true;
+
+ uint16 eventId = sGameEventMgr->GetEventIdForQuest(qInfo);
+ if (m_seasonalquests.find(eventId) == m_seasonalquests.end())
+ return false;
+
+ // if not found in cooldown list
+ return m_seasonalquests[eventId].find(qInfo->GetQuestId()) == m_seasonalquests[eventId].end();
+}
+
bool Player::GiveQuestSourceItem(Quest const* quest)
{
uint32 srcitem = quest->GetSrcItemId();
@@ -16659,10 +16674,10 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder)
if (player_at_bg && currentBg->GetStatus() != STATUS_WAIT_LEAVE)
{
- BattlegroundQueueTypeId bgQueueTypeId = sBattlegroundMgr->BGQueueTypeId(currentBg->GetTypeID(true), currentBg->GetArenaType());
+ BattlegroundQueueTypeId bgQueueTypeId = sBattlegroundMgr->BGQueueTypeId(currentBg->GetTypeID(), currentBg->GetArenaType());
AddBattlegroundQueueId(bgQueueTypeId);
- m_bgData.bgTypeID = currentBg->GetTypeID(true);
+ m_bgData.bgTypeID = currentBg->GetTypeID();
//join player to battleground group
currentBg->EventPlayerLoggedIn(this);
@@ -16834,7 +16849,7 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder)
}
// if the player is in an instance and it has been reset in the meantime teleport him to the entrance
- if (instanceId && !sInstanceSaveMgr->GetInstanceSave(instanceId))
+ if (instanceId && !sInstanceSaveMgr->GetInstanceSave(instanceId) && !map->IsBattlegroundOrArena())
{
AreaTrigger const* at = sObjectMgr->GetMapEntranceTrigger(mapId);
if (at)
@@ -16980,7 +16995,8 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder)
_LoadQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS));
_LoadQuestStatusRewarded(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADQUESTSTATUSREW));
_LoadDailyQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS));
- _LoadWeeklyQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADWEKLYQUESTSTATUS));
+ _LoadWeeklyQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADWEEKLYQUESTSTATUS));
+ _LoadSeasonalQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADSEASONALQUESTSTATUS));
_LoadRandomBGStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADRANDOMBG));
// after spell and quest load
@@ -17832,7 +17848,8 @@ void Player::_LoadWeeklyQuestStatus(PreparedQueryResult result)
{
do
{
- uint32 quest_id = (*result)[0].GetUInt32();
+ Field* fields = result->Fetch();
+ uint32 quest_id = fields[0].GetUInt32();
Quest const* quest = sObjectMgr->GetQuestTemplate(quest_id);
if (!quest)
continue;
@@ -17846,6 +17863,30 @@ void Player::_LoadWeeklyQuestStatus(PreparedQueryResult result)
m_WeeklyQuestChanged = false;
}
+void Player::_LoadSeasonalQuestStatus(PreparedQueryResult result)
+{
+ m_seasonalquests.clear();
+
+ if (result)
+ {
+ do
+ {
+ Field* fields = result->Fetch();
+ uint32 quest_id = fields[0].GetUInt32();
+ uint32 event_id = fields[1].GetUInt32();
+ Quest const* quest = sObjectMgr->GetQuestTemplate(quest_id);
+ if (!quest)
+ continue;
+
+ m_seasonalquests[event_id].insert(quest_id);
+ sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "Seasonal quest {%u} cooldown for player (GUID: %u)", quest_id, GetGUIDLow());
+ }
+ while (result->NextRow());
+ }
+
+ m_SeasonalQuestChanged = false;
+}
+
void Player::_LoadSpells(PreparedQueryResult result)
{
//QueryResult* result = CharacterDatabase.PQuery("SELECT spell, active, disabled FROM character_spell WHERE guid = '%u'", GetGUIDLow());
@@ -18572,6 +18613,7 @@ void Player::SaveToDB(bool create /*=false*/)
_SaveQuestStatus(trans);
_SaveDailyQuestStatus(trans);
_SaveWeeklyQuestStatus(trans);
+ _SaveSeasonalQuestStatus(trans);
_SaveTalents(trans);
_SaveSpells(trans);
_SaveSpellCooldowns(trans);
@@ -18910,18 +18952,28 @@ void Player::_SaveDailyQuestStatus(SQLTransaction& trans)
// save last daily quest time for all quests: we need only mostly reset time for reset check anyway
// we don't need transactions here.
- trans->PAppend("DELETE FROM character_queststatus_daily WHERE guid = '%u'", GetGUIDLow());
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_DAILY_CHAR);
+ stmt->setUInt32(0, GetGUIDLow());
+ trans->Append(stmt);
for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
if (GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx))
- trans->PAppend("INSERT INTO character_queststatus_daily (guid, quest, time) VALUES ('%u', '%u', '" UI64FMTD "')",
- GetGUIDLow(), GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx), uint64(m_lastDailyQuestTime));
+ {
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_DAILYQUESTSTATUS);
+ stmt->setUInt32(0, GetGUIDLow());
+ stmt->setUInt32(1, GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx));
+ stmt->setUInt64(2, uint64(m_lastDailyQuestTime));
+ trans->Append(stmt);
+ }
if (!m_DFQuests.empty())
{
for (DFQuestsDoneList::iterator itr = m_DFQuests.begin(); itr != m_DFQuests.end(); ++itr)
{
- trans->PAppend("INSERT INTO character_queststatus_daily (guid, quest, time) VALUES ('%u', '%u', '" UI64FMTD "')",
- GetGUIDLow(), (*itr), uint64(m_lastDailyQuestTime));
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_DAILYQUESTSTATUS);
+ stmt->setUInt32(0, GetGUIDLow());
+ stmt->setUInt32(1, (*itr));
+ stmt->setUInt64(2, uint64(m_lastDailyQuestTime));
+ trans->Append(stmt);
}
}
}
@@ -18932,18 +18984,51 @@ void Player::_SaveWeeklyQuestStatus(SQLTransaction& trans)
return;
// we don't need transactions here.
- trans->PAppend("DELETE FROM character_queststatus_weekly WHERE guid = '%u'", GetGUIDLow());
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_WEEKLY_CHAR);
+ stmt->setUInt32(0, GetGUIDLow());
+ trans->Append(stmt);
for (QuestSet::const_iterator iter = m_weeklyquests.begin(); iter != m_weeklyquests.end(); ++iter)
{
uint32 quest_id = *iter;
- trans->PAppend("INSERT INTO character_queststatus_weekly (guid, quest) VALUES ('%u', '%u')", GetGUIDLow(), quest_id);
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_WEEKLYQUESTSTATUS);
+ stmt->setUInt32(0, GetGUIDLow());
+ stmt->setUInt32(1, quest_id);
+ trans->Append(stmt);
}
m_WeeklyQuestChanged = false;
}
+void Player::_SaveSeasonalQuestStatus(SQLTransaction& trans)
+{
+ if (!m_SeasonalQuestChanged || m_seasonalquests.empty())
+ return;
+
+ // we don't need transactions here.
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_SEASONAL_CHAR);
+ stmt->setUInt32(0, GetGUIDLow());
+ trans->Append(stmt);
+
+ for (SeasonalEventQuestMap::const_iterator iter = m_seasonalquests.begin(); iter != m_seasonalquests.end(); ++iter)
+ {
+ uint16 event_id = iter->first;
+ for (SeasonalQuestSet::const_iterator itr = iter->second.begin(); itr != iter->second.end(); ++itr)
+ {
+ uint32 quest_id = (*itr);
+
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_SEASONALQUESTSTATUS);
+ stmt->setUInt32(0, GetGUIDLow());
+ stmt->setUInt32(1, quest_id);
+ stmt->setUInt32(2, event_id);
+ trans->Append(stmt);
+ }
+ }
+
+ m_SeasonalQuestChanged = false;
+}
+
void Player::_SaveSkills(SQLTransaction& trans)
{
// we don't need transactions here.
@@ -22036,6 +22121,16 @@ void Player::SetWeeklyQuestStatus(uint32 quest_id)
m_WeeklyQuestChanged = true;
}
+void Player::SetSeasonalQuestStatus(uint32 quest_id)
+{
+ Quest const* quest = sObjectMgr->GetQuestTemplate(quest_id);
+ if (!quest)
+ return;
+
+ m_seasonalquests[sGameEventMgr->GetEventIdForQuest(quest)].insert(quest_id);
+ m_SeasonalQuestChanged = true;
+}
+
void Player::ResetDailyQuestStatus()
{
for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
@@ -22058,6 +22153,16 @@ void Player::ResetWeeklyQuestStatus()
m_WeeklyQuestChanged = false;
}
+void Player::ResetSeasonalQuestStatus(uint16 event_id)
+{
+ if (m_seasonalquests.empty() || m_seasonalquests[event_id].empty())
+ return;
+
+ m_seasonalquests.erase(event_id);
+ // DB data deleted in caller
+ m_SeasonalQuestChanged = false;
+}
+
Battleground* Player::GetBattleground() const
{
if (GetBattlegroundId() == 0)
@@ -23201,6 +23306,9 @@ void Player::RemoveRunesByAuraEffect(AuraEffect const* aura)
void Player::RestoreBaseRune(uint8 index)
{
AuraEffect const* aura = m_runes->runes[index].ConvertAura;
+ // If rune was converted by a non-pasive aura that still active we should keep it converted
+ if (aura && !(aura->GetSpellInfo()->Attributes & SPELL_ATTR0_PASSIVE))
+ return;
ConvertRune(index, GetBaseRune(index));
SetRuneConvertAura(index, NULL);
// Don't drop passive talents providing rune convertion
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 7da008d597b..21c87a993dc 100755
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -815,11 +815,12 @@ enum PlayerLoginQueryIndex
PLAYER_LOGIN_QUERY_LOADTALENTS = 23,
PLAYER_LOGIN_QUERY_LOADACCOUNTDATA = 24,
PLAYER_LOGIN_QUERY_LOADSKILLS = 25,
- PLAYER_LOGIN_QUERY_LOADWEKLYQUESTSTATUS = 26,
+ PLAYER_LOGIN_QUERY_LOADWEEKLYQUESTSTATUS = 26,
PLAYER_LOGIN_QUERY_LOADRANDOMBG = 27,
PLAYER_LOGIN_QUERY_LOADBANNED = 28,
PLAYER_LOGIN_QUERY_LOADQUESTSTATUSREW = 29,
PLAYER_LOGIN_QUERY_LOADINSTANCELOCKTIMES = 30,
+ PLAYER_LOGIN_QUERY_LOADSEASONALQUESTSTATUS = 31,
MAX_PLAYER_LOGIN_QUERY,
};
@@ -1422,6 +1423,7 @@ class Player : public Unit, public GridObject<Player>
bool SatisfyQuestPrevChain(Quest const* qInfo, bool msg);
bool SatisfyQuestDay(Quest const* qInfo, bool msg);
bool SatisfyQuestWeek(Quest const* qInfo, bool msg);
+ bool SatisfyQuestSeasonal(Quest const* qInfo, bool msg);
bool GiveQuestSourceItem(Quest const* quest);
bool TakeQuestSourceItem(uint32 questId, bool msg);
bool GetQuestRewardStatus(uint32 quest_id) const;
@@ -1432,8 +1434,10 @@ class Player : public Unit, public GridObject<Player>
void SetDailyQuestStatus(uint32 quest_id);
void SetWeeklyQuestStatus(uint32 quest_id);
+ void SetSeasonalQuestStatus(uint32 quest_id);
void ResetDailyQuestStatus();
void ResetWeeklyQuestStatus();
+ void ResetSeasonalQuestStatus(uint16 event_id);
uint16 FindQuestSlot(uint32 quest_id) const;
uint32 GetQuestSlotQuestId(uint16 slot) const { return GetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot * MAX_QUEST_OFFSET + QUEST_ID_OFFSET); }
@@ -2558,8 +2562,11 @@ class Player : public Unit, public GridObject<Player>
//We allow only one timed quest active at the same time. Below can then be simple value instead of set.
typedef std::set<uint32> QuestSet;
+ typedef std::set<uint32> SeasonalQuestSet;
+ typedef UNORDERED_MAP<uint32,SeasonalQuestSet> SeasonalEventQuestMap;
QuestSet m_timedquests;
QuestSet m_weeklyquests;
+ SeasonalEventQuestMap m_seasonalquests;
uint64 m_divider;
uint32 m_ingametime;
@@ -2580,6 +2587,7 @@ class Player : public Unit, public GridObject<Player>
void _LoadQuestStatusRewarded(PreparedQueryResult result);
void _LoadDailyQuestStatus(PreparedQueryResult result);
void _LoadWeeklyQuestStatus(PreparedQueryResult result);
+ void _LoadSeasonalQuestStatus(PreparedQueryResult result);
void _LoadRandomBGStatus(PreparedQueryResult result);
void _LoadGroup(PreparedQueryResult result);
void _LoadSkills(PreparedQueryResult result);
@@ -2605,6 +2613,7 @@ class Player : public Unit, public GridObject<Player>
void _SaveQuestStatus(SQLTransaction& trans);
void _SaveDailyQuestStatus(SQLTransaction& trans);
void _SaveWeeklyQuestStatus(SQLTransaction& trans);
+ void _SaveSeasonalQuestStatus(SQLTransaction& trans);
void _SaveSkills(SQLTransaction& trans);
void _SaveSpells(SQLTransaction& trans);
void _SaveEquipmentSets(SQLTransaction& trans);
@@ -2716,6 +2725,7 @@ class Player : public Unit, public GridObject<Player>
bool m_DailyQuestChanged;
bool m_WeeklyQuestChanged;
+ bool m_SeasonalQuestChanged;
time_t m_lastDailyQuestTime;
uint32 m_drunkTimer;
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 7d470d663d2..fdc6eef8407 100755
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -1127,7 +1127,7 @@ void Unit::DealSpellDamage(SpellNonMeleeDamage* damageInfo, bool durabilityLoss)
SpellInfo const* spellProto = sSpellMgr->GetSpellInfo(damageInfo->SpellID);
if (spellProto == NULL)
{
- sLog->outDebug(LOG_FILTER_UNITS, "Unit::DealSpellDamage have wrong damageInfo->SpellID: %u", damageInfo->SpellID);
+ sLog->outDebug(LOG_FILTER_UNITS, "Unit::DealSpellDamage has wrong damageInfo->SpellID: %u", damageInfo->SpellID);
return;
}
@@ -2397,7 +2397,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spell)
case MELEE_HIT_BLOCK: canBlock = false; break;
case MELEE_HIT_PARRY: canParry = false; break;
default:
- sLog->outStaticDebug("Spell %u SPELL_AURA_IGNORE_COMBAT_RESULT have unhandled state %d", (*i)->GetId(), (*i)->GetMiscValue());
+ sLog->outStaticDebug("Spell %u SPELL_AURA_IGNORE_COMBAT_RESULT has unhandled state %d", (*i)->GetId(), (*i)->GetMiscValue());
break;
}
}
@@ -4643,7 +4643,7 @@ void Unit::AddGameObject(GameObject* gameObj)
SpellInfo const* createBySpell = sSpellMgr->GetSpellInfo(gameObj->GetSpellId());
// Need disable spell use for owner
if (createBySpell && createBySpell->Attributes & SPELL_ATTR0_DISABLED_WHILE_ACTIVE)
- // note: item based cooldowns and cooldown spell mods with charges ignored (unknown existed cases)
+ // note: item based cooldowns and cooldown spell mods with charges ignored (unknown existing cases)
ToPlayer()->AddSpellAndCategoryCooldowns(createBySpell, 0, NULL, true);
}
}
@@ -4673,7 +4673,7 @@ void Unit::RemoveGameObject(GameObject* gameObj, bool del)
SpellInfo const* createBySpell = sSpellMgr->GetSpellInfo(spellid);
// Need activate spell use for owner
if (createBySpell && createBySpell->Attributes & SPELL_ATTR0_DISABLED_WHILE_ACTIVE)
- // note: item based cooldowns and cooldown spell mods with charges ignored (unknown existed cases)
+ // note: item based cooldowns and cooldown spell mods with charges ignored (unknown existing cases)
ToPlayer()->SendCooldownEvent(createBySpell);
}
}
@@ -4968,7 +4968,7 @@ bool Unit::HandleHasteAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
if (!triggerEntry)
{
- sLog->outError("Unit::HandleHasteAuraProc: Spell %u have not existed triggered spell %u", hasteSpell->Id, triggered_spell_id);
+ sLog->outError("Unit::HandleHasteAuraProc: Spell %u has non-existing triggered spell %u", hasteSpell->Id, triggered_spell_id);
return false;
}
@@ -5026,7 +5026,7 @@ bool Unit::HandleSpellCritChanceAuraProc(Unit* victim, uint32 /*damage*/, AuraEf
if (!triggerEntry)
{
- sLog->outError("Unit::HandleHasteAuraProc: Spell %u have not existed triggered spell %u", triggeredByAuraSpell->Id, triggered_spell_id);
+ sLog->outError("Unit::HandleHasteAuraProc: Spell %u has non-existing triggered spell %u", triggeredByAuraSpell->Id, triggered_spell_id);
return false;
}
@@ -5622,7 +5622,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
case SPELLFAMILY_MAGE:
{
// Magic Absorption
- if (dummySpell->SpellIconID == 459) // only this spell have SpellIconID == 459 and dummy aura
+ if (dummySpell->SpellIconID == 459) // only this spell has SpellIconID == 459 and dummy aura
{
if (getPowerType() != POWER_MANA)
return false;
@@ -7003,26 +7003,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
{
switch (dummySpell->Id)
{
- // Earthen Power (Rank 1, 2)
- case 51523:
- case 51524:
- {
- // Totem itself must be a caster of this spell
- Unit* caster = NULL;
- for (ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr) {
- if ((*itr)->GetEntry() != 2630)
- continue;
-
- caster = *itr;
- break;
- }
-
- if (!caster)
- return false;
-
- caster->CastSpell(caster, 59566, true, castItem, triggeredByAura, originalCaster);
- return true;
- }
// Tidal Force
case 55198:
{
@@ -7069,7 +7049,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
triggered_spell_id = 28850;
break;
}
- // Windfury Weapon (Passive) 1-5 Ranks
+ // Windfury Weapon (Passive) 1-8 Ranks
case 33757:
{
Player* player = ToPlayer();
@@ -7084,8 +7064,10 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
return false;
WeaponAttackType attType = WeaponAttackType(player->GetAttackBySlot(castItem->GetSlot()));
- if ((attType != BASE_ATTACK && attType != OFF_ATTACK) || !isAttackReady(attType))
- return false;
+ if ((attType != BASE_ATTACK && attType != OFF_ATTACK)
+ || attType == BASE_ATTACK && procFlag & PROC_FLAG_DONE_OFFHAND_ATTACK
+ || attType == OFF_ATTACK && procFlag & PROC_FLAG_DONE_MAINHAND_ATTACK)
+ return false;
// Now compute real proc chance...
uint32 chance = 20;
@@ -7124,15 +7106,20 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
SpellInfo const* windfurySpellInfo = sSpellMgr->GetSpellInfo(spellId);
if (!windfurySpellInfo)
{
- sLog->outError("Unit::HandleDummyAuraProc: non existed spell id: %u (Windfury)", spellId);
+ sLog->outError("Unit::HandleDummyAuraProc: non-existing spell id: %u (Windfury)", spellId);
return false;
}
int32 extra_attack_power = CalculateSpellDamage(victim, windfurySpellInfo, 1);
// Value gained from additional AP
- basepoints0 = int32(extra_attack_power / 14.0f * GetAttackTime(BASE_ATTACK) / 1000);
- triggered_spell_id = 25504;
+ basepoints0 = int32(extra_attack_power / 14.0f * GetAttackTime(attType) / 1000);
+
+ if (procFlag & PROC_FLAG_DONE_MAINHAND_ATTACK)
+ triggered_spell_id = 25504;
+
+ if (procFlag & PROC_FLAG_DONE_OFFHAND_ATTACK)
+ triggered_spell_id = 33750;
// apply cooldown before cast to prevent processing itself
if (cooldown)
@@ -7370,6 +7357,13 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
if (GetTypeId() != TYPEID_PLAYER || !victim || !victim->isAlive() || !castItem || !castItem->IsEquipped())
return false;
+ Player* player = ToPlayer();
+ WeaponAttackType attType = WeaponAttackType(player->GetAttackBySlot(castItem->GetSlot()));
+ if ((attType != BASE_ATTACK && attType != OFF_ATTACK)
+ || attType == BASE_ATTACK && procFlag & PROC_FLAG_DONE_OFFHAND_ATTACK
+ || attType == OFF_ATTACK && procFlag & PROC_FLAG_DONE_MAINHAND_ATTACK)
+ return false;
+
float fire_onhit = float(CalculatePctF(dummySpell->Effects[EFFECT_0]. CalcValue(), 1.0f));
float add_spellpower = (float)(SpellBaseDamageBonus(SPELL_SCHOOL_MASK_FIRE)
@@ -7379,7 +7373,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
ApplyPctF(add_spellpower, 3.84f);
// Enchant on Off-Hand and ready?
- if (castItem->GetSlot() == EQUIPMENT_SLOT_OFFHAND && isAttackReady(OFF_ATTACK))
+ if (castItem->GetSlot() == EQUIPMENT_SLOT_OFFHAND && procFlag & PROC_FLAG_DONE_OFFHAND_ATTACK)
{
float BaseWeaponSpeed = GetAttackTime(OFF_ATTACK) / 1000.0f;
@@ -7389,7 +7383,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
}
// Enchant on Main-Hand and ready?
- else if (castItem->GetSlot() == EQUIPMENT_SLOT_MAINHAND && isAttackReady(BASE_ATTACK))
+ else if (castItem->GetSlot() == EQUIPMENT_SLOT_MAINHAND && procFlag & PROC_FLAG_DONE_MAINHAND_ATTACK)
{
float BaseWeaponSpeed = GetAttackTime(BASE_ATTACK) / 1000.0f;
@@ -7431,7 +7425,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
return false;
}
// Lightning Overload
- if (dummySpell->SpellIconID == 2018) // only this spell have SpellFamily Shaman SpellIconID == 2018 and dummy aura
+ if (dummySpell->SpellIconID == 2018) // only this spell has SpellFamily Shaman SpellIconID == 2018 and dummy aura
{
if (!procSpell || GetTypeId() != TYPEID_PLAYER || !victim)
return false;
@@ -7483,7 +7477,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
if (!roll_chance_f(chance))
return false;
- // Remove cooldown (Chain Lightning - have Category Recovery time)
+ // Remove cooldown (Chain Lightning - has Category Recovery time)
ToPlayer()->RemoveSpellCooldown(spellId);
}
@@ -7794,7 +7788,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id);
if (!triggerEntry)
{
- sLog->outError("Unit::HandleDummyAuraProc: Spell %u have not existed triggered spell %u", dummySpell->Id, triggered_spell_id);
+ sLog->outError("Unit::HandleDummyAuraProc: Spell %u has non-existing triggered spell %u", dummySpell->Id, triggered_spell_id);
return false;
}
@@ -7852,7 +7846,7 @@ bool Unit::HandleObsModEnergyAuraProc(Unit* victim, uint32 /*damage*/, AuraEffec
// Try handle unknown trigger spells
if (!triggerEntry)
{
- sLog->outError("Unit::HandleObsModEnergyAuraProc: Spell %u have not existed triggered spell %u", dummySpell->Id, triggered_spell_id);
+ sLog->outError("Unit::HandleObsModEnergyAuraProc: Spell %u has non-existing triggered spell %u", dummySpell->Id, triggered_spell_id);
return false;
}
@@ -7905,7 +7899,7 @@ bool Unit::HandleModDamagePctTakenAuraProc(Unit* victim, uint32 /*damage*/, Aura
if (!triggerEntry)
{
- sLog->outError("Unit::HandleModDamagePctTakenAuraProc: Spell %u have not existed triggered spell %u", dummySpell->Id, triggered_spell_id);
+ sLog->outError("Unit::HandleModDamagePctTakenAuraProc: Spell %u has non-existing triggered spell %u", dummySpell->Id, triggered_spell_id);
return false;
}
@@ -8651,8 +8645,8 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg
SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(trigger_spell_id);
if (triggerEntry == NULL)
{
- // Not cast unknown spell
- // sLog->outError("Unit::HandleProcTriggerSpell: Spell %u have 0 in EffectTriggered[%d], not handled custom case?", auraSpellInfo->Id, triggeredByAura->GetEffIndex());
+ // Don't cast unknown spell
+ // sLog->outError("Unit::HandleProcTriggerSpell: Spell %u has 0 in EffectTriggered[%d]. Unhandled custom case?", auraSpellInfo->Id, triggeredByAura->GetEffIndex());
return false;
}
@@ -8739,7 +8733,7 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg
if (!victim || !victim->isAlive())
return false;
- // Not give if target already have full health
+ // Doesn't proc if target already has full health
if (victim->IsFullHealth())
return false;
// If your Greater Heal brings the target to full health, you gain $37595s1 mana.
@@ -8950,7 +8944,7 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg
if ((maelstrom->GetStackAmount() == maelstrom->GetSpellInfo()->StackAmount) && roll_chance_i(aurEff->GetAmount()))
CastSpell(this, 70831, true, castItem, triggeredByAura);
- // have rank dependent proc chance, ignore too often cases
+ // has rank dependant proc chance, ignore too often cases
// PPM = 2.5 * (rank of talent),
uint32 rank = auraSpellInfo->GetRank();
// 5 rank -> 100% 4 rank -> 80% and etc from full rate
@@ -9304,7 +9298,7 @@ ReputationRank Unit::GetReactionTo(Unit const* target) const
&& selfPlayerOwner->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP))
return REP_HOSTILE;
- // if faction have reputation then hostile state dependent only from at_war state
+ // if faction has reputation, hostile state depends only from AtWar state
if (selfPlayerOwner->GetReputationMgr().IsAtWar(targetFactionEntry))
return REP_HOSTILE;
return REP_FRIENDLY;
@@ -10450,7 +10444,7 @@ uint32 Unit::SpellDamageBonus(Unit* victim, SpellInfo const* spellProto, uint32
{
if (victim->HasAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, spellProto, this))
{
- // effect 0 have expected value but in negative state
+ // effect 0 has expected value but in negative state
int32 bonus = -(*i)->GetBase()->GetEffect(0)->GetAmount();
AddPctN(DoneTotalMod, bonus);
}
@@ -11481,7 +11475,8 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo)
return true;
}
- if (spellInfo->Mechanic)
+ // Spells that don't have effectMechanics.
+ if (!spellInfo->HasAnyEffectMechanic() && spellInfo->Mechanic)
{
SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC];
for (SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr)
@@ -11489,14 +11484,19 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo)
return true;
}
- for (int i = 0; i < MAX_SPELL_EFFECTS; ++i)
+ bool immuneToAllEffects = true;
+ for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
// State/effect immunities applied by aura expect full spell immunity
// Ignore effects with mechanic, they are supposed to be checked separately
- if (!spellInfo->Effects[i].Mechanic)
- if (IsImmunedToSpellEffect(spellInfo, i))
- return true;
+ if (spellInfo->Effects[i].Mechanic || !IsImmunedToSpellEffect(spellInfo, i))
+ {
+ immuneToAllEffects = false;
+ break;
+ }
}
+ if (immuneToAllEffects) //Return immune only if the target is immune to all spell effects.
+ return true;
if (spellInfo->Id != 42292 && spellInfo->Id !=59752)
{
@@ -11708,7 +11708,7 @@ void Unit::MeleeDamageBonus(Unit* victim, uint32 *pdamage, WeaponAttackType attT
{
if (victim->HasAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, spellProto, this))
{
- // effect 0 have expected value but in negative state
+ // effect 0 has expected value but in negative state
int32 bonus = -(*i)->GetBase()->GetEffect(0)->GetAmount();
AddPctN(DoneTotalMod, bonus);
}
@@ -12750,6 +12750,10 @@ void Unit::setDeathState(DeathState s)
// do not why since in IncreaseMaxHealth currenthealth is checked
SetHealth(0);
SetPower(getPowerType(), 0);
+
+ // players in instance don't have ZoneScript, but they have InstanceScript
+ if (ZoneScript* zoneScript = GetZoneScript() ? GetZoneScript() : (ZoneScript*)GetInstanceScript())
+ zoneScript->OnUnitDeath(this);
}
else if (s == JUST_ALIVED)
RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); // clear skinnable for creature and player (at battleground)
@@ -12973,7 +12977,7 @@ Unit* Creature::SelectVictim()
// last case when creature must not go to evade mode:
// it in combat but attacker not make any damage and not enter to aggro radius to have record in threat list
// for example at owner command to pet attack some far away creature
- // Note: creature not have targeted movement generator but have attacker in this case
+ // Note: creature does not have targeted movement generator but has attacker in this case
for (AttackerSet::const_iterator itr = m_attackers.begin(); itr != m_attackers.end(); ++itr)
{
if ((*itr) && !canCreatureAttack(*itr) && (*itr)->GetTypeId() != TYPEID_PLAYER
@@ -13359,7 +13363,7 @@ bool Unit::HandleStatModifier(UnitMods unitMod, UnitModifierType modifierType, f
{
if (unitMod >= UNIT_MOD_END || modifierType >= MODIFIER_TYPE_END)
{
- sLog->outError("ERROR in HandleStatModifier(): non existed UnitMods or wrong UnitModifierType!");
+ sLog->outError("ERROR in HandleStatModifier(): non-existing UnitMods or wrong UnitModifierType!");
return false;
}
@@ -13424,7 +13428,7 @@ float Unit::GetModifierValue(UnitMods unitMod, UnitModifierType modifierType) co
{
if (unitMod >= UNIT_MOD_END || modifierType >= MODIFIER_TYPE_END)
{
- sLog->outError("trial to access non existed modifier value from UnitMods!");
+ sLog->outError("attempt to access non-existing modifier value from UnitMods!");
return 0.0f;
}
@@ -13454,7 +13458,7 @@ float Unit::GetTotalAuraModValue(UnitMods unitMod) const
{
if (unitMod >= UNIT_MOD_END)
{
- sLog->outError("trial to access non existed UnitMods in GetTotalAuraModValue()!");
+ sLog->outError("attempt to access non-existing UnitMods in GetTotalAuraModValue()!");
return 0.0f;
}
@@ -13683,7 +13687,7 @@ void Unit::SetMaxPower(Powers power, uint32 val)
uint32 Unit::GetCreatePowers(Powers power) const
{
- // POWER_FOCUS and POWER_HAPPINESS only have hunter pet
+ // Only hunter pets have POWER_FOCUS and POWER_HAPPINESS
switch (power)
{
case POWER_MANA: return GetCreateMana();
@@ -14111,12 +14115,12 @@ bool InitTriggerAuraData()
isTriggerAura[SPELL_AURA_DUMMY] = true;
isTriggerAura[SPELL_AURA_MOD_CONFUSE] = true;
isTriggerAura[SPELL_AURA_MOD_THREAT] = true;
- isTriggerAura[SPELL_AURA_MOD_STUN] = true; // Aura not have charges but need remove him on trigger
+ isTriggerAura[SPELL_AURA_MOD_STUN] = true; // Aura does not have charges but needs to be removed on trigger
isTriggerAura[SPELL_AURA_MOD_DAMAGE_DONE] = true;
isTriggerAura[SPELL_AURA_MOD_DAMAGE_TAKEN] = true;
isTriggerAura[SPELL_AURA_MOD_RESISTANCE] = true;
isTriggerAura[SPELL_AURA_MOD_STEALTH] = true;
- isTriggerAura[SPELL_AURA_MOD_FEAR] = true; // Aura not have charges but need remove him on trigger
+ isTriggerAura[SPELL_AURA_MOD_FEAR] = true; // Aura does not have charges but needs to be removed on trigger
isTriggerAura[SPELL_AURA_MOD_ROOT] = true;
isTriggerAura[SPELL_AURA_TRANSFORM] = true;
isTriggerAura[SPELL_AURA_REFLECT_SPELLS] = true;
@@ -15325,7 +15329,7 @@ bool Unit::HandleAuraRaidProcFromCharge(AuraEffect* triggeredByAura)
damageSpellId = 43594;
break;
default:
- sLog->outError("Unit::HandleAuraRaidProcFromCharge, received not handled spell: %u", spellProto->Id);
+ sLog->outError("Unit::HandleAuraRaidProcFromCharge, received unhandled spell: %u", spellProto->Id);
return false;
}
@@ -15738,7 +15742,7 @@ void Unit::SetStunned(bool apply)
if (!owner || (owner->GetTypeId() == TYPEID_PLAYER && !owner->ToPlayer()->IsMounted()))
RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);
- if (!HasUnitState(UNIT_STAT_ROOT)) // prevent allow move if have also root effect
+ if (!HasUnitState(UNIT_STAT_ROOT)) // prevent moving if it also has root effect
{
WorldPacket data(SMSG_FORCE_MOVE_UNROOT, 8+4);
data.append(GetPackGUID());
@@ -15780,7 +15784,7 @@ void Unit::SetRooted(bool apply)
}
else
{
- if (!HasUnitState(UNIT_STAT_STUNNED)) // prevent allow move if have also stun effect
+ if (!HasUnitState(UNIT_STAT_STUNNED)) // prevent moving if it also has stun effect
{
if (GetTypeId() == TYPEID_PLAYER)
{
diff --git a/src/server/game/Events/GameEventMgr.cpp b/src/server/game/Events/GameEventMgr.cpp
index 8b9e2fe047b..6f5dc0e511e 100755
--- a/src/server/game/Events/GameEventMgr.cpp
+++ b/src/server/game/Events/GameEventMgr.cpp
@@ -759,6 +759,50 @@ void GameEventMgr::LoadFromDB()
}
}
+ sLog->outString("Loading Game Event Seasonal Quest Relations...");
+ {
+ uint32 oldMSTime = getMSTime();
+
+ // 0 1
+ QueryResult result = WorldDatabase.Query("SELECT questId, eventEntry FROM game_event_seasonal_questrelation");
+
+ if (!result)
+ {
+ sLog->outString(">> Loaded 0 seasonal quests additions in game events. DB table `game_event_seasonal_questrelation` is empty.");
+ sLog->outString();
+ }
+ else
+ {
+ uint32 count = 0;
+ do
+ {
+ Field* fields = result->Fetch();
+
+ uint32 questId = fields[0].GetUInt32();
+ uint16 eventEntry = fields[1].GetUInt16();
+
+ if (!sObjectMgr->GetQuestTemplate(questId))
+ {
+ sLog->outErrorDb("`game_event_seasonal_questrelation` quest id (%u) does not exist in `quest_template`", questId);
+ continue;
+ }
+
+ if (eventEntry >= mGameEvent.size())
+ {
+ sLog->outErrorDb("`game_event_seasonal_questrelation` event id (%u) is out of range compared to max event in `game_event`", eventEntry);
+ continue;
+ }
+
+ _questToEventLinks[questId] = eventEntry;
+ ++count;
+ }
+ while (result->NextRow());
+
+ sLog->outString(">> Loaded %u quests additions in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ sLog->outString();
+ }
+ }
+
sLog->outString("Loading Game Event Vendor Additions Data...");
{
uint32 oldMSTime = getMSTime();
@@ -1079,6 +1123,8 @@ void GameEventMgr::UnApplyEvent(uint16 event_id)
UpdateEventNPCVendor(event_id, false);
// update bg holiday
UpdateBattlegroundSettings();
+ // check for seasonal quest reset.
+ sWorld->ResetEventSeasonalQuests(event_id);
}
void GameEventMgr::ApplyNewEvent(uint16 event_id)
@@ -1628,6 +1674,18 @@ void GameEventMgr::RunSmartAIScripts(uint16 event_id, bool activate)
}
}
+uint16 GameEventMgr::GetEventIdForQuest(Quest const* quest) const
+{
+ if (!quest)
+ return 0;
+
+ UNORDERED_MAP<uint32, uint16>::const_iterator itr = _questToEventLinks.find(quest->GetQuestId());
+ if (itr == _questToEventLinks.end())
+ return 0;
+
+ return itr->second;
+}
+
bool IsHolidayActive(HolidayIds id)
{
if (id == HOLIDAY_NONE)
@@ -1643,7 +1701,7 @@ bool IsHolidayActive(HolidayIds id)
return false;
}
- bool IsEventActive(uint16 event_id)
+bool IsEventActive(uint16 event_id)
{
GameEventMgr::ActiveEvents const& ae = sGameEventMgr->GetActiveEventList();
return ae.find(event_id) != ae.end();
diff --git a/src/server/game/Events/GameEventMgr.h b/src/server/game/Events/GameEventMgr.h
index 34258bac499..d9b5890bfe5 100755
--- a/src/server/game/Events/GameEventMgr.h
+++ b/src/server/game/Events/GameEventMgr.h
@@ -89,6 +89,7 @@ struct NPCVendorEntry
class Player;
class Creature;
+class Quest;
class GameEventMgr
{
@@ -118,6 +119,7 @@ class GameEventMgr
void HandleWorldEventGossip(Player* player, Creature* c);
uint32 GetNPCFlag(Creature* cr);
uint32 GetNpcTextId(uint32 guid);
+ uint16 GetEventIdForQuest(Quest const* quest) const;
private:
void SendWorldStateUpdate(Player* player, uint16 event_id);
void AddActiveEvent(uint16 event_id) { m_ActiveEvents.insert(event_id); }
@@ -169,6 +171,7 @@ class GameEventMgr
QuestIdToEventConditionMap mQuestToEventConditions;
GameEventNPCFlagMap mGameEventNPCFlags;
ActiveEvents m_ActiveEvents;
+ UNORDERED_MAP<uint32, uint16> _questToEventLinks;
bool isSystemInit;
public:
GameEventGuidMap mGameEventCreatureGuids;
diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h
index 57c6fe1ad2f..126cca59ef0 100755
--- a/src/server/game/Globals/ObjectMgr.h
+++ b/src/server/game/Globals/ObjectMgr.h
@@ -670,6 +670,7 @@ class ObjectMgr
return NULL;
return info;
}
+
void GetPlayerLevelInfo(uint32 race, uint32 class_, uint8 level, PlayerLevelInfo* info) const;
uint64 GetPlayerGUIDByName(std::string name) const;
@@ -687,6 +688,7 @@ class ObjectMgr
QuestMap::const_iterator itr = mQuestTemplates.find(quest_id);
return itr != mQuestTemplates.end() ? itr->second : NULL;
}
+
QuestMap const& GetQuestTemplates() const { return mQuestTemplates; }
uint32 GetQuestForAreaTrigger(uint32 Trigger_ID) const
@@ -696,6 +698,7 @@ class ObjectMgr
return itr->second;
return 0;
}
+
bool IsTavernAreaTrigger(uint32 Trigger_ID) const
{
return mTavernAreaTriggerSet.find(Trigger_ID) != mTavernAreaTriggerSet.end();
diff --git a/src/server/game/Maps/MapInstanced.cpp b/src/server/game/Maps/MapInstanced.cpp
index 2dafd44b0d1..fbe609bee23 100755
--- a/src/server/game/Maps/MapInstanced.cpp
+++ b/src/server/game/Maps/MapInstanced.cpp
@@ -124,9 +124,17 @@ Map* MapInstanced::CreateInstanceForPlayer(const uint32 mapId, Player* player)
// the instance id is set in battlegroundid
NewInstanceId = player->GetBattlegroundId();
if (!NewInstanceId) return NULL;
- map = FindInstanceMap(NewInstanceId);
+ map = sMapMgr->FindMap(mapId, NewInstanceId);
if (!map)
- map = CreateBattleground(NewInstanceId, player->GetBattleground());
+ {
+ if (Battleground* bg = player->GetBattleground())
+ map = CreateBattleground(NewInstanceId, bg);
+ else
+ {
+ player->TeleportToBGEntryPoint();
+ return NULL;
+ }
+ }
}
else
{
diff --git a/src/server/game/Maps/ZoneScript.h b/src/server/game/Maps/ZoneScript.h
index 8112d101c24..df6349a7664 100755
--- a/src/server/game/Maps/ZoneScript.h
+++ b/src/server/game/Maps/ZoneScript.h
@@ -37,7 +37,7 @@ class ZoneScript
virtual void OnGameObjectCreate(GameObject* /*go*/) {}
virtual void OnGameObjectRemove(GameObject* /*go*/) {}
- virtual void OnCreatureDeath(Creature* /*creature*/) {}
+ virtual void OnUnitDeath(Unit* /*unit*/) {}
//All-purpose data storage 64 bit
virtual uint64 GetData64(uint32 /*DataId*/) { return 0; }
diff --git a/src/server/game/Quests/QuestDef.cpp b/src/server/game/Quests/QuestDef.cpp
index 14a6577b1b5..0467eaa3253 100755
--- a/src/server/game/Quests/QuestDef.cpp
+++ b/src/server/game/Quests/QuestDef.cpp
@@ -77,7 +77,7 @@ Quest::Quest(Field* questRecord)
for (int i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i)
RewardChoiceItemCount[i] = questRecord[57+i].GetUInt32();
-
+
for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i)
RewardFactionId[i] = questRecord[63+i].GetUInt32();
@@ -86,7 +86,7 @@ Quest::Quest(Field* questRecord)
for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i)
RewardFactionValueIdOverride[i] = questRecord[73+i].GetInt32();
-
+
PointMapId = questRecord[78].GetUInt32();
PointX = questRecord[79].GetFloat();
PointY = questRecord[80].GetFloat();
@@ -202,7 +202,7 @@ uint32 Quest::XPValue(Player* player) const
return 0;
}
-int32 Quest::GetRewOrReqMoney() const
+int32 Quest::GetRewOrReqMoney() const
{
if (RewardOrRequiredMoney <= 0)
return RewardOrRequiredMoney;
diff --git a/src/server/game/Quests/QuestDef.h b/src/server/game/Quests/QuestDef.h
index 90905f2fa34..3e142e1d84c 100755
--- a/src/server/game/Quests/QuestDef.h
+++ b/src/server/game/Quests/QuestDef.h
@@ -251,6 +251,7 @@ class Quest
uint32 GetFlags() const { return Flags; }
bool IsDaily() const { return Flags & QUEST_FLAGS_DAILY; }
bool IsWeekly() const { return Flags & QUEST_FLAGS_WEEKLY; }
+ bool IsSeasonal() const { return ZoneOrSort == -QUEST_SORT_SEASONAL; }
bool IsDailyOrWeekly() const { return Flags & (QUEST_FLAGS_DAILY | QUEST_FLAGS_WEEKLY); }
bool IsAutoAccept() const { return Flags & QUEST_FLAGS_AUTO_ACCEPT; }
bool IsRaidQuest() const { return Type == QUEST_TYPE_RAID || Type == QUEST_TYPE_RAID_10 || Type == QUEST_TYPE_RAID_25; }
diff --git a/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp b/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp
index 11de2c68ca1..fd649175f76 100755..100644
--- a/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp
+++ b/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp
@@ -93,9 +93,13 @@ bool LoginQueryHolder::Initialize()
stmt->setUInt32(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS, stmt);
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_WEKLYQUESTSTATUS);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_WEEKLYQUESTSTATUS);
stmt->setUInt32(0, lowGuid);
- res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADWEKLYQUESTSTATUS, stmt);
+ res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADWEEKLYQUESTSTATUS, stmt);
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_SEASONALQUESTSTATUS);
+ stmt->setUInt32(0, lowGuid);
+ res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADSEASONALQUESTSTATUS, stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_REPUTATION);
stmt->setUInt32(0, lowGuid);
@@ -1796,7 +1800,9 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recv_data)
if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD))
{
// Reset guild
- trans->PAppend("DELETE FROM `guild_member` WHERE `guid`= '%u'", lowGuid);
+ if (QueryResult result = CharacterDatabase.PQuery("SELECT guildid FROM `guild_member` WHERE guid ='%u'", lowGuid))
+ if (Guild* guild = sGuildMgr->GetGuildById((result->Fetch()[0]).GetUInt32()))
+ guild->DeleteMember(MAKE_NEW_GUID(lowGuid, 0, HIGHGUID_PLAYER));
}
if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND))
diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp
index 3d964a6e14e..5577422919f 100755
--- a/src/server/game/Spells/Auras/SpellAuras.cpp
+++ b/src/server/game/Spells/Auras/SpellAuras.cpp
@@ -1567,6 +1567,16 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b
break;
}
break;
+ case SPELLFAMILY_DRUID:
+ // Enrage
+ if ((GetSpellInfo()->SpellFamilyFlags[0] & 0x80000) && GetSpellInfo()->SpellIconID == 961)
+ {
+ if (target->HasAura(70726)) // Item - Druid T10 Feral 4P Bonus
+ if (apply)
+ target->CastSpell(target, 70725, true);
+ break;
+ }
+ break;
case SPELLFAMILY_ROGUE:
// Stealth
if (GetSpellInfo()->SpellFamilyFlags[0] & 0x00400000)
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index bbc07c0572e..f76dbd7be47 100755
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -1562,7 +1562,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, const uint32 effectMask, bool
}
for (uint32 effectNumber = 0; effectNumber < MAX_SPELL_EFFECTS; ++effectNumber)
- if (effectMask & (1 << effectNumber))
+ if (effectMask & (1 << effectNumber) && !unit->IsImmunedToSpellEffect(m_spellInfo, effectNumber)) //Handle effect only if the target isn't immune.
HandleEffects(unit, NULL, NULL, effectNumber, SPELL_EFFECT_HANDLE_HIT_TARGET);
return SPELL_MISS_NONE;
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index 2fa7bf37de9..40b3101cfd5 100755
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -1145,9 +1145,13 @@ void Spell::EffectDummy(SpellEffIndex effIndex)
return;
m_caster->CastCustomSpell(unitTarget, 52752, &damage, NULL, NULL, true);
return;
- case 54171: //Divine Storm
+ case 54171: // Divine Storm
{
- m_caster->CastCustomSpell(unitTarget, 54172, &damage, 0, 0, true);
+ if (m_UniqueTargetInfo.size())
+ {
+ int32 heal = damage / m_UniqueTargetInfo.size();
+ m_caster->CastCustomSpell(unitTarget, 54172, &heal, NULL, NULL, true);
+ }
return;
}
case 58418: // Portal to Orgrimmar
@@ -1374,16 +1378,6 @@ void Spell::EffectDummy(SpellEffIndex effIndex)
}
break;
case SPELLFAMILY_PALADIN:
- // Divine Storm
- if (m_spellInfo->SpellFamilyFlags[1] & SPELLFAMILYFLAG1_PALADIN_DIVINESTORM && effIndex == 1)
- {
- int32 dmg = CalculatePctN(m_damage, damage);
- if (!unitTarget)
- unitTarget = m_caster;
- m_caster->CastCustomSpell(unitTarget, 54171, &dmg, 0, 0, true);
- return;
- }
-
switch (m_spellInfo->Id)
{
case 31789: // Righteous Defense (step 1)
@@ -7002,10 +6996,35 @@ void Spell::EffectActivateRune(SpellEffIndex effIndex)
{
if (player->GetRuneCooldown(j) && player->GetCurrentRune(j) == RuneType(m_spellInfo->Effects[effIndex].MiscValue))
{
+ if (m_spellInfo->Id == 45529)
+ if (player->GetBaseRune(j) != RuneType(m_spellInfo->Effects[effIndex].MiscValueB))
+ continue;
player->SetRuneCooldown(j, 0);
--count;
}
}
+
+ // Blood Tap
+ if (m_spellInfo->Id == 45529 && count > 0)
+ {
+ for (uint32 l = 0; l < MAX_RUNES && count > 0; ++l)
+ {
+ // Check if both runes are on cd as that is the only time when this needs to come into effect
+ if ((player->GetRuneCooldown(l) && player->GetCurrentRune(l) == RuneType(m_spellInfo->Effects[effIndex].MiscValueB)) && (player->GetRuneCooldown(l+1) && player->GetCurrentRune(l+1) == RuneType(m_spellInfo->Effects[effIndex].MiscValueB)))
+ {
+ // Should always update the rune with the lowest cd
+ if (player->GetRuneCooldown(l) >= player->GetRuneCooldown(l+1))
+ l++;
+ player->SetRuneCooldown(l, 0);
+ --count;
+ // is needed to push through to the client that the rune is active
+ player->ResyncRunes(MAX_RUNES);
+ }
+ else
+ break;
+ }
+ }
+
// Empower rune weapon
if (m_spellInfo->Id == 47568)
{
diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp
index 7836c177c26..fb8018fb5ae 100644
--- a/src/server/game/Spells/SpellInfo.cpp
+++ b/src/server/game/Spells/SpellInfo.cpp
@@ -1755,6 +1755,14 @@ Mechanics SpellInfo::GetEffectMechanic(uint8 effIndex) const
return MECHANIC_NONE;
}
+bool SpellInfo::HasAnyEffectMechanic() const
+{
+ for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
+ if (Effects[i].Mechanic)
+ return true;
+ return false;
+}
+
uint32 SpellInfo::GetDispelMask() const
{
return GetDispelMask(DispelType(Dispel));
diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h
index cdc24f9ceac..90b79d4da28 100644
--- a/src/server/game/Spells/SpellInfo.h
+++ b/src/server/game/Spells/SpellInfo.h
@@ -446,6 +446,7 @@ public:
uint32 GetEffectMechanicMask(uint8 effIndex) const;
uint32 GetSpellMechanicMaskByEffectMask(uint32 effectMask) const;
Mechanics GetEffectMechanic(uint8 effIndex) const;
+ bool HasAnyEffectMechanic() const;
uint32 GetDispelMask() const;
static uint32 GetDispelMask(DispelType type);
uint32 GetExplicitTargetMask() const;
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp
index b7c31ee29c6..e57cbf4a390 100755
--- a/src/server/game/Spells/SpellMgr.cpp
+++ b/src/server/game/Spells/SpellMgr.cpp
@@ -2960,8 +2960,10 @@ void SpellMgr::LoadDbcDataCorrections()
switch (spellInfo->Id)
{
- case 42835: // Spout
- spellInfo->Effect[0] = 0; // remove damage effect, only anim is needed
+ case 40244: case 40245: // Simon Game Visual
+ case 40246: case 40247: // Simon Game Visual
+ case 42835: // Spout, remove damage effect, only anim is needed
+ spellInfo->Effect[0] = 0;
break;
case 30657: // Quake
spellInfo->EffectTriggerSpell[0] = 30571;
@@ -3495,6 +3497,11 @@ void SpellMgr::LoadDbcDataCorrections()
case 72405: // Broken Frostmourne
spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_200_YARDS; // 200yd
break;
+ case 40055: // Introspection
+ case 40165: // Introspection
+ case 40166: // Introspection
+ case 40167: // Introspection
+ spellInfo->Attributes |= SPELL_ATTR0_NEGATIVE_1;
default:
break;
}
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index 388bafc503d..426a93fda57 100755
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -2748,6 +2748,17 @@ void World::ResetWeeklyQuests()
sPoolMgr->ChangeWeeklyQuests();
}
+void World::ResetEventSeasonalQuests(uint16 event_id)
+{
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_SEASONAL);
+ stmt->setUInt16(0,event_id);
+ CharacterDatabase.Execute(stmt);
+
+ for (SessionMap::const_iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
+ if (itr->second->GetPlayer())
+ itr->second->GetPlayer()->ResetSeasonalQuestStatus(event_id);
+}
+
void World::ResetRandomBG()
{
sLog->outDetail("Random BG status reset for all characters.");
diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h
index 02438375058..bedd8a87b20 100755
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -746,6 +746,7 @@ class World
uint32 GetCleaningFlags() const { return m_CleaningFlags; }
void SetCleaningFlags(uint32 flags) { m_CleaningFlags = flags; }
+ void ResetEventSeasonalQuests(uint16 event_id);
protected:
void _UpdateGameTime();
// callback for UpdateRealmCharacters
diff --git a/src/server/scripts/Commands/cs_account.cpp b/src/server/scripts/Commands/cs_account.cpp
index 3ab0306f55a..bd415c0f79f 100644
--- a/src/server/scripts/Commands/cs_account.cpp
+++ b/src/server/scripts/Commands/cs_account.cpp
@@ -109,6 +109,7 @@ public:
{
case AOR_OK:
handler->PSendSysMessage(LANG_ACCOUNT_CREATED, accountName);
+ sLog->outChar("Account: %d (IP: %s) Character:[%s] (GUID: %u) Change Password.", handler->GetSession()->GetAccountId(),handler->GetSession()->GetRemoteAddress().c_str(), handler->GetSession()->GetPlayer()->GetName(), handler->GetSession()->GetPlayer()->GetGUIDLow());
break;
case AOR_NAME_TOO_LONG:
handler->SendSysMessage(LANG_ACCOUNT_TOO_LONG);
diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp
index b1aaf82e35b..4d8ce4ef4ed 100644
--- a/src/server/scripts/Commands/cs_reload.cpp
+++ b/src/server/scripts/Commands/cs_reload.cpp
@@ -36,6 +36,7 @@ EndScriptData */
#include "SkillDiscovery.h"
#include "SkillExtraItems.h"
#include "Chat.h"
+#include "WaypointManager.h"
class reload_commandscript : public CommandScript
{
@@ -144,13 +145,14 @@ public:
{ "spell_linked_spell", SEC_ADMINISTRATOR, true, &HandleReloadSpellLinkedSpellCommand, "", NULL },
{ "spell_pet_auras", SEC_ADMINISTRATOR, true, &HandleReloadSpellPetAurasCommand, "", NULL },
{ "spell_proc_event", SEC_ADMINISTRATOR, true, &HandleReloadSpellProcEventCommand, "", NULL },
- { "spell_proc", SEC_ADMINISTRATOR, true, &HandleReloadSpellProcsCommand, "", NULL },
+ { "spell_proc", SEC_ADMINISTRATOR, true, &HandleReloadSpellProcsCommand, "", NULL },
{ "spell_scripts", SEC_ADMINISTRATOR, true, &HandleReloadSpellScriptsCommand, "", NULL },
{ "spell_target_position", SEC_ADMINISTRATOR, true, &HandleReloadSpellTargetPositionCommand, "", NULL },
{ "spell_threats", SEC_ADMINISTRATOR, true, &HandleReloadSpellThreatsCommand, "", NULL },
{ "spell_group_stack_rules", SEC_ADMINISTRATOR, true, &HandleReloadSpellGroupStackRulesCommand, "", NULL },
{ "trinity_string", SEC_ADMINISTRATOR, true, &HandleReloadTrinityStringCommand, "", NULL },
{ "waypoint_scripts", SEC_ADMINISTRATOR, true, &HandleReloadWpScriptsCommand, "", NULL },
+ { "waypoint_data", SEC_ADMINISTRATOR, true, &HandleReloadWpCommand, "", NULL },
{ "vehicle_accessory", SEC_ADMINISTRATOR, true, &HandleReloadVehicleAccessoryCommand, "", NULL },
{ "vehicle_template_accessory", SEC_ADMINISTRATOR, true, &HandleReloadVehicleTemplateAccessoryCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
@@ -264,6 +266,7 @@ public:
handler->SendGlobalGMSysMessage("DB tables `*_scripts` reloaded.");
HandleReloadDbScriptStringCommand(handler, "a");
HandleReloadWpScriptsCommand(handler, "a");
+ HandleReloadWpCommand(handler, "a");
return true;
}
@@ -986,6 +989,19 @@ public:
return true;
}
+ static bool HandleReloadWpCommand(ChatHandler* handler, const char* args)
+ {
+ if (*args != 'a')
+ sLog->outString("Re-Loading Waypoints data from 'waypoints_data'");
+
+ sWaypointMgr->Load();
+
+ if (*args != 'a')
+ sLog->outString("DB Table 'waypoint_data' reloaded.");
+
+ return true;
+ }
+
static bool HandleReloadEventAITextsCommand(ChatHandler* handler, const char* /*args*/)
{
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
index 946321c3de3..2a61d00d119 100755
--- a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
@@ -335,8 +335,12 @@ class instance_icecrown_citadel : public InstanceMapScript
return entry;
}
- void OnCreatureDeath(Creature* creature)
+ void OnUnitDeath(Unit* unit)
{
+ Creature* creature = unit->ToCreature();
+ if (!creature)
+ return;
+
switch (creature->GetEntry())
{
case NPC_YMIRJAR_BATTLE_MAIDEN:
diff --git a/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp b/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp
index cc03caef58e..32cb279ce5f 100644
--- a/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp
@@ -121,24 +121,24 @@ public:
LoadMinionData(minionData);
}
- std::set<uint64> HeiganEruptionGUID[4];
- uint64 GothikGateGUID;
- uint64 HorsemenChestGUID;
- uint64 SapphironGUID;
- uint64 uiFaerlina;
- uint64 uiThane;
- uint64 uiLady;
- uint64 uiBaron;
- uint64 uiSir;
-
- uint64 uiThaddius;
- uint64 uiHeigan;
- uint64 uiFeugen;
- uint64 uiStalagg;
-
- uint64 uiKelthuzad;
- uint64 uiKelthuzadTrigger;
- uint64 uiPortals[4];
+ std::set<uint64> heiganEruptionGUID[4];
+ uint64 gothikGateGUID;
+ uint64 horsemenChestGUID;
+ uint64 sapphironGUID;
+ uint64 faerlinaGUID;
+ uint64 thaneGUID;
+ uint64 ladyGUID;
+ uint64 baronGUID;
+ uint64 sirGUID;
+
+ uint64 thaddiusGUID;
+ uint64 heiganGUID;
+ uint64 feugenGUID;
+ uint64 stalaggGUID;
+
+ uint64 kelthuzadGUID;
+ uint64 kelthuzadTriggerGUID;
+ uint64 portalsGUID[4];
uint32 AbominationCount;
@@ -147,41 +147,46 @@ public:
time_t minHorsemenDiedTime;
time_t maxHorsemenDiedTime;
+ uint32 playerDied;
+
void Initialize()
{
- GothikGateGUID = 0;
- HorsemenChestGUID = 0;
- SapphironGUID = 0;
- uiFaerlina = 0;
- uiThane = 0;
- uiLady = 0;
- uiBaron = 0;
- uiSir = 0;
- uiThaddius = 0;
- uiHeigan = 0;
- uiFeugen = 0;
- uiStalagg = 0;
- uiKelthuzad = 0;
- uiKelthuzadTrigger = 0;
-
- memset(uiPortals, 0, sizeof(uiPortals));
+ gothikGateGUID = 0;
+ horsemenChestGUID = 0;
+ sapphironGUID = 0;
+ faerlinaGUID = 0;
+ thaneGUID = 0;
+ ladyGUID = 0;
+ baronGUID = 0;
+ sirGUID = 0;
+ thaddiusGUID = 0;
+ heiganGUID = 0;
+ feugenGUID = 0;
+ stalaggGUID = 0;
+ kelthuzadGUID = 0;
+ kelthuzadTriggerGUID = 0;
+
+ playerDied = 0;
+ gothikDoorState = GO_STATE_ACTIVE;
+
+ memset(portalsGUID, 0, sizeof(portalsGUID));
}
void OnCreatureCreate(Creature* creature)
{
switch (creature->GetEntry())
{
- case 15989: SapphironGUID = creature->GetGUID(); return;
- case 15953: uiFaerlina = creature->GetGUID(); return;
- case 16064: uiThane = creature->GetGUID(); return;
- case 16065: uiLady = creature->GetGUID(); return;
- case 30549: uiBaron = creature->GetGUID(); return;
- case 16063: uiSir = creature->GetGUID(); return;
- case 15928: uiThaddius = creature->GetGUID(); return;
- case 15936: uiHeigan = creature->GetGUID(); return;
- case 15930: uiFeugen = creature->GetGUID(); return;
- case 15929: uiStalagg = creature->GetGUID(); return;
- case 15990: uiKelthuzad = creature->GetGUID(); return;
+ case 15989: sapphironGUID = creature->GetGUID(); return;
+ case 15953: faerlinaGUID = creature->GetGUID(); return;
+ case 16064: thaneGUID = creature->GetGUID(); return;
+ case 16065: ladyGUID = creature->GetGUID(); return;
+ case 30549: baronGUID = creature->GetGUID(); return;
+ case 16063: sirGUID = creature->GetGUID(); return;
+ case 15928: thaddiusGUID = creature->GetGUID(); return;
+ case 15936: heiganGUID = creature->GetGUID(); return;
+ case 15930: feugenGUID = creature->GetGUID(); return;
+ case 15929: stalaggGUID = creature->GetGUID(); return;
+ case 15990: kelthuzadGUID = creature->GetGUID(); return;
}
AddMinion(creature, true);
@@ -197,7 +202,7 @@ public:
if (go->GetGOInfo()->displayId == 6785 || go->GetGOInfo()->displayId == 1287)
{
uint32 section = GetEruptionSection(go->GetPositionX(), go->GetPositionY());
- HeiganEruptionGUID[section].insert(go->GetGUID());
+ heiganEruptionGUID[section].insert(go->GetGUID());
return;
}
@@ -205,29 +210,29 @@ public:
switch (go->GetEntry())
{
case GO_GOTHIK_GATE:
- GothikGateGUID = go->GetGUID();
+ gothikGateGUID = go->GetGUID();
go->SetGoState(gothikDoorState);
break;
case GO_HORSEMEN_CHEST:
- HorsemenChestGUID = go->GetGUID();
+ horsemenChestGUID = go->GetGUID();
break;
case GO_HORSEMEN_CHEST_HERO:
- HorsemenChestGUID = go->GetGUID();
+ horsemenChestGUID = go->GetGUID();
break;
case GO_KELTHUZAD_PORTAL01:
- uiPortals[0] = go->GetGUID();
+ portalsGUID[0] = go->GetGUID();
break;
case GO_KELTHUZAD_PORTAL02:
- uiPortals[1] = go->GetGUID();
+ portalsGUID[1] = go->GetGUID();
break;
case GO_KELTHUZAD_PORTAL03:
- uiPortals[2] = go->GetGUID();
+ portalsGUID[2] = go->GetGUID();
break;
case GO_KELTHUZAD_PORTAL04:
- uiPortals[3] = go->GetGUID();
+ portalsGUID[3] = go->GetGUID();
break;
case GO_KELTHUZAD_TRIGGER:
- uiKelthuzadTrigger = go->GetGUID();
+ kelthuzadTriggerGUID = go->GetGUID();
break;
default:
break;
@@ -242,16 +247,16 @@ public:
{
uint32 section = GetEruptionSection(go->GetPositionX(), go->GetPositionY());
- HeiganEruptionGUID[section].erase(go->GetGUID());
+ heiganEruptionGUID[section].erase(go->GetGUID());
return;
}
switch (go->GetEntry())
{
case GO_BIRTH:
- if (SapphironGUID)
+ if (sapphironGUID)
{
- if (Creature* pSapphiron = instance->GetCreature(SapphironGUID))
+ if (Creature* pSapphiron = instance->GetCreature(sapphironGUID))
pSapphiron->AI()->DoAction(DATA_SAPPHIRON_BIRTH);
return;
}
@@ -263,6 +268,15 @@ public:
AddDoor(go, false);
}
+ void OnUnitDeath(Unit* unit)
+ {
+ if (unit->GetTypeId() == TYPEID_PLAYER && IsEncounterInProgress())
+ {
+ playerDied = 1;
+ SaveToDB();
+ }
+ }
+
void SetData(uint32 id, uint32 value)
{
switch (id)
@@ -271,11 +285,10 @@ public:
HeiganErupt(value);
break;
case DATA_GOTHIK_GATE:
- if (GameObject* gothikGate = instance->GetGameObject(GothikGateGUID))
+ if (GameObject* gothikGate = instance->GetGameObject(gothikGateGUID))
gothikGate->SetGoState(GOState(value));
gothikDoorState = GOState(value);
break;
-
case DATA_HORSEMEN0:
case DATA_HORSEMEN1:
case DATA_HORSEMEN2:
@@ -319,35 +332,35 @@ public:
switch (id)
{
case DATA_FAERLINA:
- return uiFaerlina;
+ return faerlinaGUID;
case DATA_THANE:
- return uiThane;
+ return thaneGUID;
case DATA_LADY:
- return uiLady;
+ return ladyGUID;
case DATA_BARON:
- return uiBaron;
+ return baronGUID;
case DATA_SIR:
- return uiSir;
+ return sirGUID;
case DATA_THADDIUS:
- return uiThaddius;
+ return thaddiusGUID;
case DATA_HEIGAN:
- return uiHeigan;
+ return heiganGUID;
case DATA_FEUGEN:
- return uiFeugen;
+ return feugenGUID;
case DATA_STALAGG:
- return uiStalagg;
+ return stalaggGUID;
case DATA_KELTHUZAD:
- return uiKelthuzad;
+ return kelthuzadGUID;
case DATA_KELTHUZAD_PORTAL01:
- return uiPortals[0];
+ return portalsGUID[0];
case DATA_KELTHUZAD_PORTAL02:
- return uiPortals[1];
+ return portalsGUID[1];
case DATA_KELTHUZAD_PORTAL03:
- return uiPortals[2];
+ return portalsGUID[2];
case DATA_KELTHUZAD_PORTAL04:
- return uiPortals[3];
+ return portalsGUID[3];
case DATA_KELTHUZAD_TRIGGER:
- return uiKelthuzadTrigger;
+ return kelthuzadTriggerGUID;
}
return 0;
}
@@ -359,7 +372,7 @@ public:
if (id == BOSS_HORSEMEN && state == DONE)
{
- if (GameObject* pHorsemenChest = instance->GetGameObject(HorsemenChestGUID))
+ if (GameObject* pHorsemenChest = instance->GetGameObject(horsemenChestGUID))
pHorsemenChest->SetRespawnTime(pHorsemenChest->GetRespawnDelay());
}
@@ -373,7 +386,7 @@ public:
if (i == section)
continue;
- for (std::set<uint64>::const_iterator itr = HeiganEruptionGUID[i].begin(); itr != HeiganEruptionGUID[i].end(); ++itr)
+ for (std::set<uint64>::const_iterator itr = heiganEruptionGUID[i].begin(); itr != heiganEruptionGUID[i].end(); ++itr)
{
if (GameObject* pHeiganEruption = instance->GetGameObject(*itr))
{
@@ -384,6 +397,21 @@ public:
}
}
+ // This Function is called in CheckAchievementCriteriaMeet and CheckAchievementCriteriaMeet is called before SetBossState(bossId, DONE),
+ // so to check if all bosses are done the checker must exclude 1 boss, the last done, if there is at most 1 encouter in progress when is
+ // called this function then all bosses are done. The one boss that check is the boss that calls this function, so it is dead.
+ bool AreAllEncoutersDone()
+ {
+ uint32 numBossAlive = 0;
+ for (uint32 i = 0; i < MAX_BOSS_NUMBER; ++i)
+ if (GetBossState(i) != DONE)
+ numBossAlive++;
+
+ if (numBossAlive > 1)
+ return false;
+ return true;
+ }
+
bool CheckAchievementCriteriaMeet(uint32 criteria_id, Player const* /*source*/, Unit const* /*target = NULL*/, uint32 /*miscvalue1 = 0*/)
{
switch (criteria_id)
@@ -396,12 +424,22 @@ public:
if (Difficulty(instance->GetSpawnMode()) == RAID_DIFFICULTY_25MAN_NORMAL && (maxHorsemenDiedTime - minHorsemenDiedTime) < 15)
return true;
return false;
- case 13233: // Criteria for achievement 2186: The Immortal (25-man)
- // TODO.
- break;
- case 13237: // Criteria for achievement 2187: The Undying (10-man)
- // TODO.
- break;
+ // Difficulty checks are done on DB.
+ // Criteria for achievement 2186: The Immortal (25-man)
+ case 13233: // The Four Horsemen
+ case 13234: // Maexxna
+ case 13235: // Thaddius
+ case 13236: // Loatheb
+ case 7616: // Kel'Thuzad
+ // Criteria for achievement 2187: The Undying (10-man)
+ case 13237: // The Four Horsemen
+ case 13238: // Maexxna
+ case 13239: // Loatheb
+ case 13240: // Thaddius
+ case 7617: // Kel'Thuzad
+ if (AreAllEncoutersDone() && !playerDied)
+ return true;
+ return false;
}
return false;
}
@@ -409,16 +447,22 @@ public:
std::string GetSaveData()
{
std::ostringstream saveStream;
- saveStream << GetBossSaveData() << ' ' << gothikDoorState;
+ saveStream << GetBossSaveData() << gothikDoorState << ' ' << playerDied;
return saveStream.str();
}
void Load(const char * data)
{
std::istringstream loadStream(LoadBossState(data));
- uint32 buff;
+ uint32 temp, buff, buff2;
+
+ for (uint32 i = 0; i < MAX_BOSS_NUMBER; ++i)
+ loadStream >> temp;
+
loadStream >> buff;
gothikDoorState = GOState(buff);
+ loadStream >> buff2;
+ playerDied = buff2;
}
};
diff --git a/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp b/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp
index 1a62a6236e1..46498a69c6f 100644
--- a/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp
+++ b/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp
@@ -58,8 +58,12 @@ public:
gameObjectList.clear();
}
- void OnCreatureDeath(Creature* creature)
+ void OnUnitDeath(Unit* unit)
{
+ Creature* creature = unit->ToCreature();
+ if (!creature)
+ return;
+
if (creature->GetEntry() != NPC_CENTRIFUGE_CONSTRUCT)
return;
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp
index 7c8121f475f..f62c2d6c596 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp
@@ -436,7 +436,7 @@ class boss_freya : public CreatureScript
break;
case EVENT_WAVE:
SpawnWave();
- if (waveCount < 6)
+ if (waveCount <= 6) // If set to 6 The Bombs appear during the Final Add wave
events.ScheduleEvent(EVENT_WAVE, WAVE_TIME);
else
events.ScheduleEvent(EVENT_NATURE_BOMB, urand(10000, 20000));
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp
index a6f7af6e492..e12393f047f 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp
@@ -358,8 +358,12 @@ class instance_ulduar : public InstanceMapScript
}
}
- void OnCreatureDeath(Creature* creature)
+ void OnUnitDeath(Unit* unit)
{
+ Creature* creature = unit->ToCreature();
+ if (!creature)
+ return;
+
switch (creature->GetEntry())
{
case NPC_CORRUPTED_SERVITOR:
diff --git a/src/server/scripts/Outland/blades_edge_mountains.cpp b/src/server/scripts/Outland/blades_edge_mountains.cpp
index d78d4afbfb8..97ce9f45430 100644
--- a/src/server/scripts/Outland/blades_edge_mountains.cpp
+++ b/src/server/scripts/Outland/blades_edge_mountains.cpp
@@ -562,6 +562,564 @@ class go_thunderspike : public GameObjectScript
}
};
+enum SimonGame
+{
+ NPC_SIMON_BUNNY = 22923,
+ NPC_APEXIS_GUARDIAN = 22275,
+
+ GO_APEXIS_RELIC = 185890,
+ GO_APEXIS_MONUMENT = 185944,
+ GO_AURA_BLUE = 185872,
+ GO_AURA_GREEN = 185873,
+ GO_AURA_RED = 185874,
+ GO_AURA_YELLOW = 185875,
+
+ GO_BLUE_CLUSTER_DISPLAY = 7369,
+ GO_GREEN_CLUSTER_DISPLAY = 7371,
+ GO_RED_CLUSTER_DISPLAY = 7373,
+ GO_YELLOW_CLUSTER_DISPLAY = 7375,
+ GO_BLUE_CLUSTER_DISPLAY_LARGE = 7364,
+ GO_GREEN_CLUSTER_DISPLAY_LARGE = 7365,
+ GO_RED_CLUSTER_DISPLAY_LARGE = 7366,
+ GO_YELLOW_CLUSTER_DISPLAY_LARGE = 7367,
+
+ SPELL_PRE_GAME_BLUE = 40176,
+ SPELL_PRE_GAME_GREEN = 40177,
+ SPELL_PRE_GAME_RED = 40178,
+ SPELL_PRE_GAME_YELLOW = 40179,
+ SPELL_VISUAL_BLUE = 40244,
+ SPELL_VISUAL_GREEN = 40245,
+ SPELL_VISUAL_RED = 40246,
+ SPELL_VISUAL_YELLOW = 40247,
+
+ SOUND_BLUE = 11588,
+ SOUND_GREEN = 11589,
+ SOUND_RED = 11590,
+ SOUND_YELLOW = 11591,
+ SOUND_DISABLE_NODE = 11758,
+
+ SPELL_AUDIBLE_GAME_TICK = 40391,
+ SPELL_VISUAL_START_PLAYER_LEVEL = 40436,
+ SPELL_VISUAL_START_AI_LEVEL = 40387,
+
+ SPELL_BAD_PRESS_TRIGGER = 41241,
+ SPELL_BAD_PRESS_DAMAGE = 40065,
+ SPELL_REWARD_BUFF_1 = 40310,
+ SPELL_REWARD_BUFF_2 = 40311,
+ SPELL_REWARD_BUFF_3 = 40312,
+};
+
+enum SimonEvents
+{
+ EVENT_SIMON_SETUP_PRE_GAME = 1,
+ EVENT_SIMON_PLAY_SEQUENCE = 2,
+ EVENT_SIMON_RESET_CLUSTERS = 3,
+ EVENT_SIMON_PERIODIC_PLAYER_CHECK = 4,
+ EVENT_SIMON_TOO_LONG_TIME = 5,
+ EVENT_SIMON_GAME_TICK = 6,
+ EVENT_SIMON_ROUND_FINISHED = 7,
+
+ ACTION_SIMON_CORRECT_FULL_SEQUENCE = 8,
+ ACTION_SIMON_WRONG_SEQUENCE = 9,
+ ACTION_SIMON_ROUND_FINISHED = 10,
+};
+
+enum SimonColors
+{
+ SIMON_BLUE = 0,
+ SIMON_RED = 1,
+ SIMON_GREEN = 2,
+ SIMON_YELLOW = 3,
+ SIMON_MAX_COLORS = 4,
+};
+
+class npc_simon_bunny : public CreatureScript
+{
+ public:
+ npc_simon_bunny() : CreatureScript("npc_simon_bunny") { }
+
+ struct npc_simon_bunnyAI : public ScriptedAI
+ {
+ npc_simon_bunnyAI(Creature* creature) : ScriptedAI(creature) { }
+
+ bool large;
+ bool listening;
+ uint8 gameLevel;
+ uint8 fails;
+ uint8 gameTicks;
+ uint64 playerGUID;
+ uint32 clusterIds[SIMON_MAX_COLORS];
+ float zCoordCorrection;
+ float searchDistance;
+ EventMap _events;
+ std::list<uint8> colorSequence, playableSequence, playerSequence;
+
+ void UpdateAI(const uint32 diff)
+ {
+ _events.Update(diff);
+
+ switch(_events.ExecuteEvent())
+ {
+ case EVENT_SIMON_PERIODIC_PLAYER_CHECK:
+ if (!CheckPlayer())
+ ResetNode();
+ else
+ _events.ScheduleEvent(EVENT_SIMON_PERIODIC_PLAYER_CHECK, 2000);
+ break;
+ case EVENT_SIMON_SETUP_PRE_GAME:
+ SetUpPreGame();
+ _events.CancelEvent(EVENT_SIMON_GAME_TICK);
+ _events.ScheduleEvent(EVENT_SIMON_PLAY_SEQUENCE, 1000);
+ break;
+ case EVENT_SIMON_PLAY_SEQUENCE:
+ if (!playableSequence.empty())
+ {
+ PlayNextColor();
+ _events.ScheduleEvent(EVENT_SIMON_PLAY_SEQUENCE, 1500);
+ }
+ else
+ {
+ listening = true;
+ DoCast(SPELL_VISUAL_START_PLAYER_LEVEL);
+ playerSequence.clear();
+ PrepareClusters();
+ gameTicks = 0;
+ _events.ScheduleEvent(EVENT_SIMON_GAME_TICK, 3000);
+ }
+ break;
+ case EVENT_SIMON_GAME_TICK:
+ DoCast(SPELL_AUDIBLE_GAME_TICK);
+
+ if (gameTicks > gameLevel)
+ _events.ScheduleEvent(EVENT_SIMON_TOO_LONG_TIME, 500);
+ else
+ _events.ScheduleEvent(EVENT_SIMON_GAME_TICK, 3000);
+ gameTicks++;
+ break;
+ case EVENT_SIMON_RESET_CLUSTERS:
+ PrepareClusters(true);
+ break;
+ case EVENT_SIMON_TOO_LONG_TIME:
+ DoAction(ACTION_SIMON_WRONG_SEQUENCE);
+ break;
+ case EVENT_SIMON_ROUND_FINISHED:
+ DoAction(ACTION_SIMON_ROUND_FINISHED);
+ break;
+ }
+ }
+
+ void DoAction(const int32 action)
+ {
+ switch (action)
+ {
+ case ACTION_SIMON_ROUND_FINISHED:
+ listening = false;
+ DoCast(SPELL_VISUAL_START_AI_LEVEL);
+ GiveRewardForLevel(gameLevel);
+ _events.CancelEventGroup(0);
+ if (gameLevel == 10)
+ ResetNode();
+ else
+ _events.ScheduleEvent(EVENT_SIMON_SETUP_PRE_GAME, 1000);
+ break;
+ case ACTION_SIMON_CORRECT_FULL_SEQUENCE:
+ gameLevel++;
+ DoAction(ACTION_SIMON_ROUND_FINISHED);
+ break;
+ case ACTION_SIMON_WRONG_SEQUENCE:
+ GivePunishment();
+ DoAction(ACTION_SIMON_ROUND_FINISHED);
+ break;
+ }
+ }
+
+ // Called by color clusters script (go_simon_cluster) and used for knowing the button pressed by player
+ void SetData(uint32 type, uint32 /*data*/)
+ {
+ if (!listening)
+ return;
+
+ uint8 pressedColor;
+
+ if (type == clusterIds[SIMON_RED])
+ pressedColor = SIMON_RED;
+ else if (type == clusterIds[SIMON_BLUE])
+ pressedColor = SIMON_BLUE;
+ else if (type == clusterIds[SIMON_GREEN])
+ pressedColor = SIMON_GREEN;
+ else if (type == clusterIds[SIMON_YELLOW])
+ pressedColor = SIMON_YELLOW;
+
+ PlayColor(pressedColor);
+ playerSequence.push_back(pressedColor);
+ _events.ScheduleEvent(EVENT_SIMON_RESET_CLUSTERS, 500);
+ CheckPlayerSequence();
+ }
+
+ // Used for getting involved player guid. Parameter id is used for defining if is a large(Monument) or small(Relic) node
+ void SetGUID(uint64 guid, int32 id)
+ {
+ me->SetFlying(true);
+
+ large = (bool)id;
+ playerGUID = guid;
+ StartGame();
+ }
+
+ /*
+ Resets all variables and also find the ids of the four closests color clusters, since every simon
+ node have diferent ids for clusters this is absolutely NECESSARY.
+ */
+ void StartGame()
+ {
+ listening = false;
+ gameLevel = 0;
+ fails = 0;
+ gameTicks = 0;
+ zCoordCorrection = large ? 8.0f : 2.75f;
+ searchDistance = large ? 13.0f : 5.0f;
+ colorSequence.clear();
+ playableSequence.clear();
+ playerSequence.clear();
+ me->SetFloatValue(OBJECT_FIELD_SCALE_X, large ? 2 : 1);
+
+ std::list<WorldObject*> ClusterList;
+ Trinity::AllWorldObjectsInRange objects(me, searchDistance);
+ Trinity::WorldObjectListSearcher<Trinity::AllWorldObjectsInRange> searcher(me, ClusterList, objects);
+ me->VisitNearbyObject(searchDistance, searcher);
+
+ for (std::list<WorldObject*>::const_iterator i = ClusterList.begin(); i != ClusterList.end(); ++i)
+ {
+ if (GameObject* go = (*i)->ToGameObject())
+ {
+ // We are checking for displayid because all simon nodes have 4 clusters with different entries
+ if (large)
+ {
+ switch (go->GetGOInfo()->displayId)
+ {
+ case GO_BLUE_CLUSTER_DISPLAY_LARGE: clusterIds[SIMON_BLUE] = go->GetEntry(); break;
+ case GO_RED_CLUSTER_DISPLAY_LARGE: clusterIds[SIMON_RED] = go->GetEntry(); break;
+ case GO_GREEN_CLUSTER_DISPLAY_LARGE: clusterIds[SIMON_GREEN] = go->GetEntry(); break;
+ case GO_YELLOW_CLUSTER_DISPLAY_LARGE: clusterIds[SIMON_YELLOW] = go->GetEntry(); break;
+ }
+ }
+ else
+ {
+ switch (go->GetGOInfo()->displayId)
+ {
+ case GO_BLUE_CLUSTER_DISPLAY: clusterIds[SIMON_BLUE] = go->GetEntry(); break;
+ case GO_RED_CLUSTER_DISPLAY: clusterIds[SIMON_RED] = go->GetEntry(); break;
+ case GO_GREEN_CLUSTER_DISPLAY: clusterIds[SIMON_GREEN] = go->GetEntry(); break;
+ case GO_YELLOW_CLUSTER_DISPLAY: clusterIds[SIMON_YELLOW] = go->GetEntry(); break;
+ }
+ }
+ }
+ }
+
+ _events.Reset();
+ _events.ScheduleEvent(EVENT_SIMON_ROUND_FINISHED, 1000);
+ _events.ScheduleEvent(EVENT_SIMON_PERIODIC_PLAYER_CHECK, 2000);
+
+ if (GameObject* relic = me->FindNearestGameObject(large ? GO_APEXIS_MONUMENT : GO_APEXIS_RELIC, searchDistance))
+ relic->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
+ }
+
+ // Called when despawning the bunny. Sets all the node GOs to their default states.
+ void ResetNode()
+ {
+ DoPlaySoundToSet(me, SOUND_DISABLE_NODE);
+
+ for (uint32 clusterId = SIMON_BLUE; clusterId < SIMON_MAX_COLORS; clusterId++)
+ if (GameObject* cluster = me->FindNearestGameObject(clusterIds[clusterId], searchDistance))
+ cluster->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
+
+ for (uint32 auraId = GO_AURA_BLUE; auraId <= GO_AURA_YELLOW; auraId++)
+ if (GameObject* auraGo = me->FindNearestGameObject(auraId, searchDistance))
+ auraGo->RemoveFromWorld();
+
+ if (GameObject* relic = me->FindNearestGameObject(large ? GO_APEXIS_MONUMENT : GO_APEXIS_RELIC, searchDistance))
+ relic->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
+
+ me->ForcedDespawn(1000);
+ }
+
+ /*
+ Called on every button click of player. Adds the clicked color to the player created sequence and
+ checks if it corresponds to the AI created sequence. If so, incremente gameLevel and start a new
+ round, if not, give punishment and restart current level.
+ */
+ void CheckPlayerSequence()
+ {
+ bool correct = true;
+ if (playerSequence.size() <= colorSequence.size())
+ for (std::list<uint8>::const_iterator i = playerSequence.begin(), j = colorSequence.begin(); i != playerSequence.end(); ++i, ++j)
+ if ((*i) != (*j))
+ correct = false;
+
+ if (correct && (playerSequence.size() == colorSequence.size()))
+ DoAction(ACTION_SIMON_CORRECT_FULL_SEQUENCE);
+ else if (!correct)
+ DoAction(ACTION_SIMON_WRONG_SEQUENCE);
+ }
+
+ /*
+ Generates a random sequence of colors depending on the gameLevel. We also copy this sequence to
+ the playableSequence wich will be used when playing the sequence to the player.
+ */
+ void GenerateColorSequence()
+ {
+ colorSequence.clear();
+ for (uint8 i = 0; i <= gameLevel; i++)
+ colorSequence.push_back(RAND(SIMON_BLUE, SIMON_RED, SIMON_GREEN, SIMON_YELLOW));
+
+ for (std::list<uint8>::const_iterator i = colorSequence.begin(); i != colorSequence.end(); ++i)
+ playableSequence.push_back(*i);
+ }
+
+
+ // Remove any existant glowing auras over clusters and set clusters ready for interating with them.
+ void PrepareClusters(bool clustersOnly = false)
+ {
+ for (uint32 clusterId = SIMON_BLUE; clusterId < SIMON_MAX_COLORS; clusterId++)
+ if (GameObject* cluster = me->FindNearestGameObject(clusterIds[clusterId], searchDistance))
+ cluster->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
+
+ if (clustersOnly)
+ return;
+
+ for (uint32 auraId = GO_AURA_BLUE; auraId <= GO_AURA_YELLOW; auraId++)
+ if (GameObject* auraGo = me->FindNearestGameObject(auraId, searchDistance))
+ auraGo->RemoveFromWorld();
+ }
+
+ /*
+ Called when AI is playing the sequence for player. We cast the visual spell and then remove the
+ casted color from the casting sequence.
+ */
+ void PlayNextColor()
+ {
+ PlayColor(*playableSequence.begin());
+ playableSequence.erase(playableSequence.begin());
+ }
+
+ // Casts a spell and plays a sound depending on parameter color.
+ void PlayColor(uint8 color)
+ {
+ switch (color)
+ {
+ case SIMON_BLUE:
+ DoCast(SPELL_VISUAL_BLUE);
+ DoPlaySoundToSet(me, SOUND_BLUE);
+ break;
+ case SIMON_GREEN:
+ DoCast(SPELL_VISUAL_GREEN);
+ DoPlaySoundToSet(me, SOUND_GREEN);
+ break;
+ case SIMON_RED:
+ DoCast(SPELL_VISUAL_RED);
+ DoPlaySoundToSet(me, SOUND_RED);
+ break;
+ case SIMON_YELLOW:
+ DoCast(SPELL_VISUAL_YELLOW);
+ DoPlaySoundToSet(me, SOUND_YELLOW);
+ break;
+ }
+ }
+
+ /*
+ Creates the transparent glowing auras on every cluster of this node.
+ After calling this function bunny is teleported to the center of the node.
+ */
+ void SetUpPreGame()
+ {
+ for (uint32 clusterId = SIMON_BLUE; clusterId < SIMON_MAX_COLORS; clusterId++)
+ {
+ if (GameObject* cluster = me->FindNearestGameObject(clusterIds[clusterId], 2.0f*searchDistance))
+ {
+ cluster->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
+
+ // break since we don't need glowing auras for large clusters
+ if (large)
+ break;
+
+ float x, y, z, o;
+ cluster->GetPosition(x, y, z, o);
+ me->NearTeleportTo(x, y, z, o);
+
+ uint32 preGameSpellId;
+ if (cluster->GetEntry() == clusterIds[SIMON_RED])
+ preGameSpellId = SPELL_PRE_GAME_RED;
+ else if (cluster->GetEntry() == clusterIds[SIMON_BLUE])
+ preGameSpellId = SPELL_PRE_GAME_BLUE;
+ else if (cluster->GetEntry() == clusterIds[SIMON_GREEN])
+ preGameSpellId = SPELL_PRE_GAME_GREEN;
+ else if (cluster->GetEntry() == clusterIds[SIMON_YELLOW])
+ preGameSpellId = SPELL_PRE_GAME_YELLOW;
+ else break;
+
+ me->CastSpell(cluster, preGameSpellId, true);
+ }
+ }
+
+ if (GameObject* relic = me->FindNearestGameObject(large ? GO_APEXIS_MONUMENT : GO_APEXIS_RELIC, searchDistance))
+ {
+ float x, y, z, o;
+ relic->GetPosition(x, y, z, o);
+ me->NearTeleportTo(x, y, z + zCoordCorrection, o);
+ }
+
+ GenerateColorSequence();
+ }
+
+ // Handles the spell rewards. The spells also have the QuestCompleteEffect, so quests credits are working.
+ void GiveRewardForLevel(uint8 level)
+ {
+ uint32 rewSpell;
+ switch (level)
+ {
+ case 6:
+ if (large)
+ GivePunishment();
+ else
+ rewSpell = SPELL_REWARD_BUFF_1;
+ break;
+ case 8:
+ rewSpell = SPELL_REWARD_BUFF_2;
+ break;
+ case 10:
+ rewSpell = SPELL_REWARD_BUFF_3;
+ break;
+ default:
+ rewSpell = 0;
+ }
+
+ if (rewSpell)
+ if (Player* player = me->GetPlayer(*me, playerGUID))
+ DoCast(player, rewSpell, true);
+ }
+
+ /*
+ Depending on the number of failed pushes for player the damage of the spell scales, so we first
+ cast the spell on the target that hits for 50 and shows the visual and then forces the player
+ to cast the damaging spell on it self with the modified basepoints.
+ 4 fails = death.
+ On large nodes punishment and reward are the same, summoning the Apexis Guardian.
+ */
+ void GivePunishment()
+ {
+ if (large)
+ {
+ if (Player* player = me->GetPlayer(*me, playerGUID))
+ if (Creature* guardian = me->SummonCreature(NPC_APEXIS_GUARDIAN, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() - zCoordCorrection, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 20000))
+ guardian->AI()->AttackStart(player);
+
+ ResetNode();
+ }
+ else
+ {
+ fails++;
+
+ if (Player* player = me->GetPlayer(*me, playerGUID))
+ DoCast(player, SPELL_BAD_PRESS_TRIGGER, true);
+
+ if (fails >= 4)
+ ResetNode();
+ }
+ }
+
+ void SpellHitTarget(Unit* target, const SpellInfo* spell)
+ {
+ // Cast SPELL_BAD_PRESS_DAMAGE with scaled basepoints when the visual hits the target.
+ // Need Fix: When SPELL_BAD_PRESS_TRIGGER hits target it triggers spell SPELL_BAD_PRESS_DAMAGE by itself
+ // so player gets damage equal to calculated damage dbc basepoints for SPELL_BAD_PRESS_DAMAGE (~50)
+ if (spell->Id == SPELL_BAD_PRESS_TRIGGER)
+ {
+ int32 bp = (int32)((float)(fails)*0.33f*target->GetMaxHealth());
+ target->CastCustomSpell(target, SPELL_BAD_PRESS_DAMAGE, &bp, NULL, NULL, true);
+ }
+ }
+
+ // Checks if player has already die or has get too far from the current node
+ bool CheckPlayer()
+ {
+ if (Player* player = me->GetPlayer(*me, playerGUID))
+ {
+ if (player->isDead())
+ return false;
+ if (player->GetDistance2d(me) >= 2.0f*searchDistance)
+ {
+ GivePunishment();
+ return false;
+ }
+ }
+ else
+ return false;
+
+ return true;
+ }
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return new npc_simon_bunnyAI(creature);
+ }
+};
+
+class go_simon_cluster : public GameObjectScript
+{
+ public:
+ go_simon_cluster() : GameObjectScript("go_simon_cluster") { }
+
+ bool OnGossipHello(Player* player, GameObject* go)
+ {
+ if (Creature* bunny = go->FindNearestCreature(NPC_SIMON_BUNNY, 12.0f, true))
+ bunny->AI()->SetData(go->GetEntry(), 0);
+
+ player->CastSpell(player, go->GetGOInfo()->goober.spellId, true);
+ go->AddUse();
+ return true;
+ }
+};
+
+enum ApexisRelic
+{
+ QUEST_CRYSTALS = 11025,
+ GOSSIP_TEXT_ID = 10948,
+
+ ITEM_APEXIS_SHARD = 32569,
+ SPELL_TAKE_REAGENTS_SOLO = 41145,
+ SPELL_TAKE_REAGENTS_GROUP = 41146,
+};
+
+class go_apexis_relic : public GameObjectScript
+{
+ public:
+ go_apexis_relic() : GameObjectScript("go_apexis_relic") { }
+
+ bool OnGossipHello(Player* player, GameObject* go)
+ {
+ player->PrepareGossipMenu(go, go->GetGOInfo()->questgiver.gossipID);
+ player->SendPreparedGossip(go);
+ return true;
+ }
+
+ bool OnGossipSelect(Player* player, GameObject* go, uint32 /*sender*/, uint32 /*action*/)
+ {
+ player->CLOSE_GOSSIP_MENU();
+
+ bool large = (go->GetEntry() == GO_APEXIS_MONUMENT);
+ if (player->HasItemCount(ITEM_APEXIS_SHARD, large ? 35 : 1))
+ {
+ player->CastSpell(player, large ? SPELL_TAKE_REAGENTS_GROUP : SPELL_TAKE_REAGENTS_SOLO, false);
+
+ if (Creature* bunny = player->SummonCreature(NPC_SIMON_BUNNY, go->GetPositionX(), go->GetPositionY(), go->GetPositionZ()))
+ bunny->AI()->SetGUID(player->GetGUID(), large);
+ }
+
+ return true;
+ }
+};
+
void AddSC_blades_edge_mountains()
{
new mobs_bladespire_ogre();
@@ -573,4 +1131,7 @@ void AddSC_blades_edge_mountains()
new npc_bloodmaul_brutebane();
new npc_ogre_brute();
new go_thunderspike();
+ new npc_simon_bunny();
+ new go_simon_cluster();
+ new go_apexis_relic();
}
diff --git a/src/server/scripts/Spells/spell_paladin.cpp b/src/server/scripts/Spells/spell_paladin.cpp
index f7cdfa552ee..39360e04aa1 100644
--- a/src/server/scripts/Spells/spell_paladin.cpp
+++ b/src/server/scripts/Spells/spell_paladin.cpp
@@ -37,6 +37,10 @@ enum PaladinSpells
SPELL_BLESSING_OF_LOWER_CITY_PALADIN = 37879,
SPELL_BLESSING_OF_LOWER_CITY_PRIEST = 37880,
SPELL_BLESSING_OF_LOWER_CITY_SHAMAN = 37881,
+
+ SPELL_DIVINE_STORM = 53385,
+ SPELL_DIVINE_STORM_DUMMY = 54171,
+ SPELL_DIVINE_STORM_HEAL = 54172,
};
// 31850 - Ardent Defender
@@ -327,6 +331,40 @@ public:
}
};
+class spell_pal_divine_storm : public SpellScriptLoader
+{
+public:
+ spell_pal_divine_storm() : SpellScriptLoader("spell_pal_divine_storm") { }
+
+ class spell_pal_divine_storm_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_pal_divine_storm_SpellScript);
+
+ uint32 healPct;
+
+ bool Load()
+ {
+ healPct = GetSpellInfo()->Effects[EFFECT_1].CalcValue(GetCaster());
+ return true;
+ }
+
+ void TriggerHeal()
+ {
+ GetCaster()->CastCustomSpell(SPELL_DIVINE_STORM_DUMMY, SPELLVALUE_BASE_POINT0, (GetHitDamage() * healPct) / 100, GetCaster(), true);
+ }
+
+ void Register()
+ {
+ AfterHit += SpellHitFn(spell_pal_divine_storm_SpellScript::TriggerHeal);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_pal_divine_storm_SpellScript();
+ }
+};
+
void AddSC_paladin_spell_scripts()
{
new spell_pal_ardent_defender();
@@ -335,4 +373,5 @@ void AddSC_paladin_spell_scripts()
new spell_pal_guarded_by_the_light();
new spell_pal_holy_shock();
new spell_pal_judgement_of_command();
+ new spell_pal_divine_storm();
}
diff --git a/src/server/scripts/Spells/spell_shaman.cpp b/src/server/scripts/Spells/spell_shaman.cpp
index 9e8b8a9eda4..b0116d6d33d 100644
--- a/src/server/scripts/Spells/spell_shaman.cpp
+++ b/src/server/scripts/Spells/spell_shaman.cpp
@@ -26,16 +26,16 @@
enum ShamanSpells
{
- SHAMAN_SPELL_GLYPH_OF_MANA_TIDE = 55441,
- SHAMAN_SPELL_MANA_TIDE_TOTEM = 39609,
- SHAMAN_SPELL_FIRE_NOVA_R1 = 1535,
- SHAMAN_SPELL_FIRE_NOVA_TRIGGERED_R1 = 8349,
- SHAMAN_SPELL_SATED = 57724,
- SHAMAN_SPELL_EXHAUSTION = 57723,
-
- //For Earthen Power
- SHAMAN_TOTEM_SPELL_EARTHBIND_TOTEM = 6474, //Spell casted by totem
- SHAMAN_TOTEM_SPELL_EARTHEN_POWER = 59566, //Spell witch remove snare effect
+ SHAMAN_SPELL_GLYPH_OF_MANA_TIDE = 55441,
+ SHAMAN_SPELL_MANA_TIDE_TOTEM = 39609,
+ SHAMAN_SPELL_FIRE_NOVA_R1 = 1535,
+ SHAMAN_SPELL_FIRE_NOVA_TRIGGERED_R1 = 8349,
+ SHAMAN_SPELL_SATED = 57724,
+ SHAMAN_SPELL_EXHAUSTION = 57723,
+
+ // For Earthen Power
+ SHAMAN_TOTEM_SPELL_EARTHBIND_TOTEM = 6474,
+ SHAMAN_TOTEM_SPELL_EARTHEN_POWER = 59566,
};
// 51474 - Astral shift
@@ -125,7 +125,7 @@ class spell_sha_fire_nova : public SpellScriptLoader
{
Creature* totem = caster->GetMap()->GetCreature(caster->m_SummonSlot[1]);
if (totem && totem->isTotem())
- totem->CastSpell(totem, spellId, true);
+ caster->CastSpell(totem, spellId, true);
}
}
@@ -215,9 +215,11 @@ class spell_sha_earthbind_totem : public SpellScriptLoader
{
Unit* target = GetTarget();
if (Unit* caster = aurEff->GetBase()->GetCaster())
- if (AuraEffect* aur = caster->GetDummyAuraEffect(SPELLFAMILY_SHAMAN, 2289, 0))
- if (roll_chance_i(aur->GetBaseAmount()))
- target->CastSpell(target, SHAMAN_TOTEM_SPELL_EARTHEN_POWER, true, NULL, aurEff);
+ if (TempSummon* summon = caster->ToTempSummon())
+ if (Unit* owner = summon->GetOwner())
+ if (AuraEffect* aur = owner->GetDummyAuraEffect(SPELLFAMILY_SHAMAN, 2289, 0))
+ if (roll_chance_i(aur->GetBaseAmount()) && target->HasAuraWithMechanic(1 << MECHANIC_SNARE))
+ caster->CastSpell(caster, SHAMAN_TOTEM_SPELL_EARTHEN_POWER, true, NULL, aurEff);
}
void Register()
diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp
index b5b174a19eb..a24f17a8b76 100755..100644
--- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp
+++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp
@@ -46,6 +46,10 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PREPARE_STATEMENT(CHAR_SEL_GUID_RACE_ACC_BY_NAME, "SELECT guid, race, account FROM characters WHERE name = ?", CONNECTION_ASYNC);
PREPARE_STATEMENT(CHAR_DEL_QUEST_STATUS_DAILY, "DELETE FROM character_queststatus_daily", CONNECTION_ASYNC);
PREPARE_STATEMENT(CHAR_DEL_QUEST_STATUS_WEEKLY, "DELETE FROM character_queststatus_weekly", CONNECTION_ASYNC);
+ PREPARE_STATEMENT(CHAR_DEL_QUEST_STATUS_SEASONAL, "DELETE FROM character_queststatus_seasonal WHERE event = ?", CONNECTION_ASYNC);
+ PREPARE_STATEMENT(CHAR_DEL_QUEST_STATUS_DAILY_CHAR, "DELETE FROM character_queststatus_daily WHERE guid = ?", CONNECTION_ASYNC);
+ PREPARE_STATEMENT(CHAR_DEL_QUEST_STATUS_WEEKLY_CHAR, "DELETE FROM character_queststatus_weekly WHERE guid = ?", CONNECTION_ASYNC);
+ PREPARE_STATEMENT(CHAR_DEL_QUEST_STATUS_SEASONAL_CHAR, "DELETE FROM character_queststatus_seasonal WHERE guid = ?", CONNECTION_ASYNC);
PREPARE_STATEMENT(CHAR_DEL_BATTLEGROUND_RANDOM, "DELETE FROM character_battleground_random", CONNECTION_ASYNC);
PREPARE_STATEMENT(CHAR_INS_BATTLEGROUND_RANDOM, "INSERT INTO character_battleground_random (guid) VALUES (?)", CONNECTION_ASYNC);
@@ -63,7 +67,11 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PREPARE_STATEMENT(CHAR_SEL_CHARACTER_QUESTSTATUS, "SELECT quest, status, explored, timer, mobcount1, mobcount2, mobcount3, mobcount4, "
"itemcount1, itemcount2, itemcount3, itemcount4, playercount FROM character_queststatus WHERE guid = ?", CONNECTION_ASYNC)
PREPARE_STATEMENT(CHAR_SEL_CHARACTER_DAILYQUESTSTATUS, "SELECT quest, time FROM character_queststatus_daily WHERE guid = ?", CONNECTION_ASYNC)
- PREPARE_STATEMENT(CHAR_SEL_CHARACTER_WEKLYQUESTSTATUS, "SELECT quest FROM character_queststatus_weekly WHERE guid = ?", CONNECTION_ASYNC)
+ PREPARE_STATEMENT(CHAR_SEL_CHARACTER_WEEKLYQUESTSTATUS, "SELECT quest FROM character_queststatus_weekly WHERE guid = ?", CONNECTION_ASYNC)
+ PREPARE_STATEMENT(CHAR_SEL_CHARACTER_SEASONALQUESTSTATUS, "SELECT quest, event FROM character_queststatus_seasonal WHERE guid = ?", CONNECTION_ASYNC)
+ PREPARE_STATEMENT(CHAR_INS_CHARACTER_DAILYQUESTSTATUS, "INSERT INTO character_queststatus_daily (guid, quest, time) VALUES (?, ?, ?)", CONNECTION_ASYNC)
+ PREPARE_STATEMENT(CHAR_INS_CHARACTER_WEEKLYQUESTSTATUS, "INSERT INTO character_queststatus_weekly (guid, quest) VALUES (?, ?)", CONNECTION_ASYNC)
+ PREPARE_STATEMENT(CHAR_INS_CHARACTER_SEASONALQUESTSTATUS, "INSERT INTO character_queststatus_seasonal (guid, quest, event) VALUES (?, ?, ?)", CONNECTION_ASYNC)
PREPARE_STATEMENT(CHAR_SEL_CHARACTER_REPUTATION, "SELECT faction, standing, flags FROM character_reputation WHERE guid = ?", CONNECTION_ASYNC)
PREPARE_STATEMENT(CHAR_SEL_CHARACTER_INVENTORY, "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, bag, slot, "
"item, itemEntry FROM character_inventory ci JOIN item_instance ii ON ci.item = ii.guid WHERE ci.guid = ? ORDER BY bag, slot", CONNECTION_ASYNC)
diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.h b/src/server/shared/Database/Implementation/CharacterDatabase.h
index a0f5b9a9954..a239e274a54 100755..100644
--- a/src/server/shared/Database/Implementation/CharacterDatabase.h
+++ b/src/server/shared/Database/Implementation/CharacterDatabase.h
@@ -66,6 +66,10 @@ enum CharacterDatabaseStatements
CHAR_SEL_GUID_RACE_ACC_BY_NAME,
CHAR_DEL_QUEST_STATUS_DAILY,
CHAR_DEL_QUEST_STATUS_WEEKLY,
+ CHAR_DEL_QUEST_STATUS_SEASONAL,
+ CHAR_DEL_QUEST_STATUS_DAILY_CHAR,
+ CHAR_DEL_QUEST_STATUS_WEEKLY_CHAR,
+ CHAR_DEL_QUEST_STATUS_SEASONAL_CHAR,
CHAR_DEL_BATTLEGROUND_RANDOM,
CHAR_INS_BATTLEGROUND_RANDOM,
@@ -76,7 +80,11 @@ enum CharacterDatabaseStatements
CHAR_SEL_CHARACTER_SPELL,
CHAR_SEL_CHARACTER_QUESTSTATUS,
CHAR_SEL_CHARACTER_DAILYQUESTSTATUS,
- CHAR_SEL_CHARACTER_WEKLYQUESTSTATUS,
+ CHAR_SEL_CHARACTER_WEEKLYQUESTSTATUS,
+ CHAR_SEL_CHARACTER_SEASONALQUESTSTATUS,
+ CHAR_INS_CHARACTER_DAILYQUESTSTATUS,
+ CHAR_INS_CHARACTER_WEEKLYQUESTSTATUS,
+ CHAR_INS_CHARACTER_SEASONALQUESTSTATUS,
CHAR_SEL_CHARACTER_REPUTATION,
CHAR_SEL_CHARACTER_INVENTORY,
CHAR_SEL_CHARACTER_ACTIONS,