Merge branch 'master' into Playerbot
# Conflicts: # src/server/game/Guilds/Guild.cpp
This commit is contained in:
@@ -195,13 +195,14 @@ void AuthSession::CheckIpCallback(PreparedQueryResult result)
|
||||
{
|
||||
bool banned = false;
|
||||
|
||||
do
|
||||
for (auto const& fields : *result)
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
if (fields[0].Get<uint64>() != 0)
|
||||
{
|
||||
banned = true;
|
||||
|
||||
} while (result->NextRow());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (banned)
|
||||
{
|
||||
|
||||
@@ -167,7 +167,7 @@ SourceDirectory = ""
|
||||
# Description: The path to your MySQL CLI binary.
|
||||
# If the path is left empty, built-in path from cmake is used.
|
||||
# Example: "C:/Program Files/MariaDB 10.5/bin/mysql.exe"
|
||||
# "C:/Program Files/MySQL/MySQL Server 5.6/bin/mysql.exe"
|
||||
# "C:/Program Files/MySQL/MySQL Server 8.0/bin/mysql.exe"
|
||||
# "mysql.exe"
|
||||
# "/usr/bin/mysql"
|
||||
# Default: ""
|
||||
@@ -175,6 +175,16 @@ SourceDirectory = ""
|
||||
|
||||
MySQLExecutable = ""
|
||||
|
||||
#
|
||||
# TempDir
|
||||
# Description: Temp directory setting.
|
||||
# Important: TempDir needs to be quoted, as the string might contain space characters.
|
||||
# TempDir directory must exists, or the server can't work properly
|
||||
# Example: "/home/youruser/azerothcore/temp"
|
||||
# Default: "" - (Temp files will be stored in the current path)
|
||||
|
||||
TempDir = ""
|
||||
|
||||
#
|
||||
# IPLocationFile
|
||||
# Description: The path to your IP2Location database CSV file.
|
||||
|
||||
@@ -81,6 +81,16 @@ DataDir = "."
|
||||
|
||||
LogsDir = ""
|
||||
|
||||
#
|
||||
# TempDir
|
||||
# Description: Temp directory setting.
|
||||
# Important: TempDir needs to be quoted, as the string might contain space characters.
|
||||
# TempDir directory must exists, or the server can't work properly
|
||||
# Example: "/home/youruser/azerothcore/temp"
|
||||
# Default: "" - (Temp files will be stored in the current path)
|
||||
|
||||
TempDir = ""
|
||||
|
||||
#
|
||||
# LoginDatabaseInfo
|
||||
# WorldDatabaseInfo
|
||||
@@ -193,7 +203,7 @@ SourceDirectory = ""
|
||||
# MySQLExecutable
|
||||
# Description: The path to your MySQL CLI binary.
|
||||
# If the path is left empty, built-in path from cmake is used.
|
||||
# Example: "C:/Program Files/MySQL/MySQL Server 5.7/bin/mysql.exe"
|
||||
# Example: "C:/Program Files/MySQL/MySQL Server 8.0/bin/mysql.exe"
|
||||
# "mysql.exe"
|
||||
# "/usr/bin/mysql"
|
||||
# Default: ""
|
||||
@@ -2909,8 +2919,9 @@ Battleground.SpeedBuffRespawn = 150
|
||||
#
|
||||
# Wintergrasp.Enable
|
||||
# Description: Enable the Wintergrasp battlefield.
|
||||
# Default: 0 - (Disabled)
|
||||
# 1 - (Enabled, Experimental as of still being in development)
|
||||
# Default: 1 - (Enabled, Experimental as of still being in development)
|
||||
# 0 - (Battleground disabled, Wintergrasp world processing still occurs)
|
||||
# 2 - (Disable all Wintergrasp processing)
|
||||
|
||||
Wintergrasp.Enable = 1
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
|
||||
PrepareStatement(CHAR_SEL_MAIL_SERVER_CHARACTER, "SELECT mailId from mail_server_character WHERE guid = ? and mailId = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_REP_MAIL_SERVER_CHARACTER, "REPLACE INTO mail_server_character (guid, mailId) values (?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_SOCIALLIST, "SELECT friend, flags, note FROM character_social JOIN characters ON characters.guid = character_social.friend WHERE character_social.guid = ? AND deleteinfos_name IS NULL LIMIT 255", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_HOMEBIND, "SELECT mapId, zoneId, posX, posY, posZ FROM character_homebind WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_HOMEBIND, "SELECT mapId, zoneId, posX, posY, posZ, posO FROM character_homebind WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_SPELLCOOLDOWNS, "SELECT spell, category, item, time, needSend FROM character_spell_cooldown WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_DECLINEDNAMES, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_declinedname WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_ACHIEVEMENTS, "SELECT achievement, date FROM character_achievement WHERE guid = ?", CONNECTION_ASYNC);
|
||||
@@ -290,8 +290,8 @@ void CharacterDatabaseConnection::DoPrepareStatements()
|
||||
PrepareStatement(CHAR_DEL_PLAYER_ENTRY_POINT, "DELETE FROM character_entry_point WHERE guid = ?", CONNECTION_ASYNC);
|
||||
|
||||
// Character homebind
|
||||
PrepareStatement(CHAR_INS_PLAYER_HOMEBIND, "INSERT INTO character_homebind (guid, mapId, zoneId, posX, posY, posZ) VALUES (?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UPD_PLAYER_HOMEBIND, "UPDATE character_homebind SET mapId = ?, zoneId = ?, posX = ?, posY = ?, posZ = ? WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_INS_PLAYER_HOMEBIND, "INSERT INTO character_homebind (guid, mapId, zoneId, posX, posY, posZ, posO) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UPD_PLAYER_HOMEBIND, "UPDATE character_homebind SET mapId = ?, zoneId = ?, posX = ?, posY = ?, posZ = ?, posO = ? WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_PLAYER_HOMEBIND, "DELETE FROM character_homebind WHERE guid = ?", CONNECTION_ASYNC);
|
||||
|
||||
// Corpse
|
||||
@@ -407,7 +407,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
|
||||
PrepareStatement(CHAR_SEL_PINFO_BANS, "SELECT unbandate, bandate = unbandate, bannedby, banreason FROM character_banned WHERE guid = ? AND active ORDER BY bandate ASC LIMIT 1", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_PINFO_MAILS, "SELECT SUM(CASE WHEN (checked & 1) THEN 1 ELSE 0 END) AS 'readmail', COUNT(*) AS 'totalmail' FROM mail WHERE `receiver` = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_PINFO_XP, "SELECT a.xp, b.guid FROM characters a LEFT JOIN guild_member b ON a.guid = b.guid WHERE a.guid = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_CHAR_HOMEBIND, "SELECT mapId, zoneId, posX, posY, posZ FROM character_homebind WHERE guid = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_CHAR_HOMEBIND, "SELECT mapId, zoneId, posX, posY, posZ, posO FROM character_homebind WHERE guid = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_CHAR_GUID_NAME_BY_ACC, "SELECT guid, name FROM characters WHERE account = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_POOL_QUEST_SAVE, "SELECT quest_id FROM pool_quest_save WHERE pool_id = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_AT_LOGIN, "SELECT at_login FROM characters WHERE guid = ?", CONNECTION_SYNCH);
|
||||
|
||||
@@ -24,6 +24,27 @@
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
template<typename T>
|
||||
struct ResultIterator
|
||||
{
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = T;
|
||||
using pointer = T*;
|
||||
using reference = T&;
|
||||
|
||||
explicit ResultIterator(pointer ptr) : _ptr(ptr) { }
|
||||
|
||||
reference operator*() const { return *_ptr; }
|
||||
pointer operator->() { return _ptr; }
|
||||
ResultIterator& operator++() { if (!_ptr->NextRow()) _ptr = nullptr; return *this; }
|
||||
|
||||
bool operator!=(const ResultIterator& right) { return _ptr != right._ptr; }
|
||||
|
||||
private:
|
||||
pointer _ptr;
|
||||
};
|
||||
|
||||
class AC_DATABASE_API ResultSet
|
||||
{
|
||||
public:
|
||||
@@ -54,6 +75,9 @@ public:
|
||||
return theTuple;
|
||||
}
|
||||
|
||||
auto begin() { return ResultIterator<ResultSet>(this); }
|
||||
static auto end() { return ResultIterator<ResultSet>(nullptr); }
|
||||
|
||||
protected:
|
||||
std::vector<QueryResultFieldMetadata> _fieldMetadata;
|
||||
uint64 _rowCount;
|
||||
@@ -100,6 +124,9 @@ public:
|
||||
return theTuple;
|
||||
}
|
||||
|
||||
auto begin() { return ResultIterator<PreparedResultSet>(this); }
|
||||
static auto end() { return ResultIterator<PreparedResultSet>(nullptr); }
|
||||
|
||||
protected:
|
||||
std::vector<QueryResultFieldMetadata> m_fieldMetadata;
|
||||
std::vector<Field> m_rows;
|
||||
|
||||
@@ -228,11 +228,13 @@ BaseLocation DBUpdater<T>::GetBaseLocationType()
|
||||
template<class T>
|
||||
bool DBUpdater<T>::Create(DatabaseWorkerPool<T>& pool)
|
||||
{
|
||||
LOG_WARN("sql.updates", "Database \"{}\" does not exist, do you want to create it? [yes (default) / no]: ",
|
||||
pool.GetConnectionInfo()->database);
|
||||
LOG_WARN("sql.updates", "Database \"{}\" does not exist", pool.GetConnectionInfo()->database);
|
||||
|
||||
if (!sConfigMgr->isDryRun())
|
||||
const char* disableInteractive = std::getenv("AC_DISABLE_INTERACTIVE");
|
||||
|
||||
if (!sConfigMgr->isDryRun() && (disableInteractive == nullptr || std::strcmp(disableInteractive, "1") != 0))
|
||||
{
|
||||
std::cout << "Do you want to create it? [yes (default) / no]:" << std::endl;
|
||||
std::string answer;
|
||||
std::getline(std::cin, answer);
|
||||
if (!answer.empty() && !(answer.substr(0, 1) == "y"))
|
||||
@@ -498,16 +500,29 @@ template<class T>
|
||||
void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& host, std::string const& user,
|
||||
std::string const& password, std::string const& port_or_socket, std::string const& database, std::string const& ssl, Path const& path)
|
||||
{
|
||||
std::string configTempDir = sConfigMgr->GetOption<std::string>("TempDir", "");
|
||||
|
||||
auto tempDir = configTempDir.empty() ? std::filesystem::temp_directory_path().string() : configTempDir;
|
||||
|
||||
tempDir = Acore::String::AddSuffixIfNotExists(tempDir, std::filesystem::path::preferred_separator);
|
||||
|
||||
std::string confFileName = "mysql_ac.conf";
|
||||
|
||||
std::ofstream outfile (tempDir + confFileName);
|
||||
|
||||
outfile << "[client]\npassword = \"" << password << '"' << std::endl;
|
||||
|
||||
outfile.close();
|
||||
|
||||
std::vector<std::string> args;
|
||||
args.reserve(9);
|
||||
|
||||
args.emplace_back("--defaults-extra-file="+tempDir + confFileName+"");
|
||||
|
||||
// CLI Client connection info
|
||||
args.emplace_back("-h" + host);
|
||||
args.emplace_back("-u" + user);
|
||||
|
||||
if (!password.empty())
|
||||
args.emplace_back("-p" + password);
|
||||
|
||||
// Check if we want to connect through ip or socket (Unix only)
|
||||
#ifdef _WIN32
|
||||
|
||||
|
||||
@@ -64,14 +64,14 @@ void SummonList::DespawnEntry(uint32 entry)
|
||||
}
|
||||
}
|
||||
|
||||
void SummonList::DespawnAll()
|
||||
void SummonList::DespawnAll(uint32 delay /*= 0*/)
|
||||
{
|
||||
while (!storage_.empty())
|
||||
{
|
||||
Creature* summon = ObjectAccessor::GetCreature(*me, storage_.front());
|
||||
storage_.pop_front();
|
||||
if (summon)
|
||||
summon->DespawnOrUnsummon();
|
||||
summon->DespawnOrUnsummon(delay);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,11 +384,35 @@ SpellInfo const* ScriptedAI::SelectSpell(Unit* target, uint32 school, uint32 mec
|
||||
return apSpell[urand(0, spellCount - 1)];
|
||||
}
|
||||
|
||||
void ScriptedAI::DoResetThreat()
|
||||
void ScriptedAI::DoAddThreat(Unit* unit, float amount)
|
||||
{
|
||||
if (!unit)
|
||||
return;
|
||||
|
||||
me->GetThreatMgr().AddThreat(unit, amount);
|
||||
}
|
||||
|
||||
void ScriptedAI::DoModifyThreatByPercent(Unit* unit, int32 pct)
|
||||
{
|
||||
if (!unit)
|
||||
return;
|
||||
|
||||
me->GetThreatMgr().ModifyThreatByPercent(unit, pct);
|
||||
}
|
||||
|
||||
void ScriptedAI::DoResetThreat(Unit* unit)
|
||||
{
|
||||
if (!unit)
|
||||
return;
|
||||
|
||||
me->GetThreatMgr().ResetThreat(unit);
|
||||
}
|
||||
|
||||
void ScriptedAI::DoResetThreatList()
|
||||
{
|
||||
if (!me->CanHaveThreatList() || me->GetThreatMgr().isThreatListEmpty())
|
||||
{
|
||||
LOG_ERROR("entities.unit.ai", "DoResetThreat called for creature that either cannot have threat list or has empty threat list (me entry = {})", me->GetEntry());
|
||||
LOG_ERROR("entities.unit.ai", "DoResetThreatList called for creature that either cannot have threat list or has empty threat list (me entry = {})", me->GetEntry());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -399,14 +423,8 @@ float ScriptedAI::DoGetThreat(Unit* unit)
|
||||
{
|
||||
if (!unit)
|
||||
return 0.0f;
|
||||
return me->GetThreatMgr().GetThreat(unit);
|
||||
}
|
||||
|
||||
void ScriptedAI::DoModifyThreatPercent(Unit* unit, int32 pct)
|
||||
{
|
||||
if (!unit)
|
||||
return;
|
||||
me->GetThreatMgr().ModifyThreatByPercent(unit, pct);
|
||||
return me->GetThreatMgr().GetThreat(unit);
|
||||
}
|
||||
|
||||
void ScriptedAI::DoTeleportPlayer(Unit* unit, float x, float y, float z, float o)
|
||||
|
||||
@@ -89,7 +89,7 @@ public:
|
||||
void Summon(Creature const* summon) { storage_.push_back(summon->GetGUID()); }
|
||||
void Despawn(Creature const* summon) { storage_.remove(summon->GetGUID()); }
|
||||
void DespawnEntry(uint32 entry);
|
||||
void DespawnAll();
|
||||
void DespawnAll(uint32 delay = 0);
|
||||
bool IsAnyCreatureAlive() const;
|
||||
bool IsAnyCreatureWithEntryAlive(uint32 entry) const;
|
||||
bool IsAnyCreatureInCombat() const;
|
||||
@@ -304,11 +304,20 @@ struct ScriptedAI : public CreatureAI
|
||||
//Plays music for all players in the zone (zone = true) or the area (zone = false)
|
||||
void DoPlayMusic(uint32 soundId, bool zone);
|
||||
|
||||
//Drops all threat to 0%. Does not remove players from the threat list
|
||||
void DoResetThreat();
|
||||
// Add specified amount of threat directly to victim (ignores redirection effects) - also puts victim in combat and engages them if necessary
|
||||
void DoAddThreat(Unit* unit, float amount);
|
||||
|
||||
// Adds/removes the specified percentage from the specified victim's threat (to who, or me if not specified)
|
||||
void DoModifyThreatByPercent(Unit* unit, int32 pct);
|
||||
|
||||
//Drops all threat to 0%. Does not remove players from the threat list
|
||||
void DoResetThreat(Unit* unit);
|
||||
|
||||
// Resets the specified unit's threat list (me if not specified) - does not delete entries, just sets their threat to zero
|
||||
void DoResetThreatList();
|
||||
|
||||
// Returns the threat level of victim towards who (or me if not specified)
|
||||
float DoGetThreat(Unit* unit);
|
||||
void DoModifyThreatPercent(Unit* unit, int32 pct);
|
||||
|
||||
//Teleports a player without dropping threat (only teleports to same map)
|
||||
void DoTeleportPlayer(Unit* unit, float x, float y, float z, float o);
|
||||
|
||||
@@ -40,6 +40,12 @@ BattlefieldMgr* BattlefieldMgr::instance()
|
||||
|
||||
void BattlefieldMgr::InitBattlefield()
|
||||
{
|
||||
if (sWorld->getIntConfig(CONFIG_WINTERGRASP_ENABLE) == 2)
|
||||
{
|
||||
LOG_INFO("server.loading", "Battlefield: Wintergrasp is disabled.");
|
||||
LOG_INFO("server.loading", " ");
|
||||
return;
|
||||
}
|
||||
Battlefield* pBf = new BattlefieldWG;
|
||||
// respawn, init variables
|
||||
if (!pBf->SetupBattlefield())
|
||||
|
||||
@@ -50,7 +50,7 @@ bool BattlefieldWG::SetupBattlefield()
|
||||
InitStalker(BATTLEFIELD_WG_NPC_STALKER, WintergraspStalkerPos[0], WintergraspStalkerPos[1], WintergraspStalkerPos[2], WintergraspStalkerPos[3]);
|
||||
|
||||
m_MaxPlayer = sWorld->getIntConfig(CONFIG_WINTERGRASP_PLR_MAX);
|
||||
m_IsEnabled = sWorld->getBoolConfig(CONFIG_WINTERGRASP_ENABLE);
|
||||
m_IsEnabled = sWorld->getIntConfig(CONFIG_WINTERGRASP_ENABLE) == 1;
|
||||
m_MinPlayer = sWorld->getIntConfig(CONFIG_WINTERGRASP_PLR_MIN);
|
||||
m_MinLevel = sWorld->getIntConfig(CONFIG_WINTERGRASP_PLR_MIN_LVL);
|
||||
m_BattleTime = sWorld->getIntConfig(CONFIG_WINTERGRASP_BATTLETIME) * MINUTE * IN_MILLISECONDS;
|
||||
|
||||
@@ -729,8 +729,8 @@ void BattlegroundAV::ChangeMineOwner(uint8 mine, TeamId teamId, bool initial)
|
||||
{
|
||||
m_Mine_Reclaim_Timer[mine] = AV_MINE_RECLAIM_TIMER;
|
||||
char buf[256];
|
||||
sprintf(buf, GetAcoreString(LANG_BG_AV_MINE_TAKEN), GetAcoreString((mine == AV_NORTH_MINE) ? LANG_BG_AV_MINE_NORTH : LANG_BG_AV_MINE_SOUTH),
|
||||
(teamId == TEAM_ALLIANCE) ? GetAcoreString(LANG_BG_AV_ALLY) : GetAcoreString(LANG_BG_AV_HORDE));
|
||||
sprintf(buf, GetAcoreString(LANG_BG_AV_MINE_TAKEN), (teamId == TEAM_ALLIANCE) ? GetAcoreString(LANG_BG_AV_ALLY) : GetAcoreString(LANG_BG_AV_HORDE),
|
||||
GetAcoreString((mine == AV_NORTH_MINE) ? LANG_BG_AV_MINE_NORTH : LANG_BG_AV_MINE_SOUTH));
|
||||
Creature* creature = GetBGCreature(AV_CPLACE_HERALD);
|
||||
if (creature)
|
||||
YellToAll(creature, buf, LANG_UNIVERSAL);
|
||||
|
||||
@@ -101,6 +101,10 @@ void BattlegroundWS::PostUpdateImpl(uint32 diff)
|
||||
player->CastSpell(player, BG_WS_SPELL_BRUTAL_ASSAULT, true);
|
||||
}
|
||||
break;
|
||||
case BG_WS_EVENT_DESPAWN_DOORS:
|
||||
SpawnBGObject(BG_WS_OBJECT_DOOR_H_1, RESPAWN_ONE_DAY);
|
||||
SpawnBGObject(BG_WS_OBJECT_DOOR_H_2, RESPAWN_ONE_DAY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,6 +139,7 @@ void BattlegroundWS::StartingEventOpenDoors()
|
||||
UpdateWorldState(BG_WS_STATE_TIMER_ACTIVE, 1);
|
||||
_bgEvents.ScheduleEvent(BG_WS_EVENT_UPDATE_GAME_TIME, 0);
|
||||
_bgEvents.ScheduleEvent(BG_WS_EVENT_NO_TIME_LEFT, BG_WS_TOTAL_GAME_TIME - 2 * MINUTE * IN_MILLISECONDS); // 27 - 2 = 25 minutes
|
||||
_bgEvents.ScheduleEvent(BG_WS_EVENT_DESPAWN_DOORS, BG_WS_DOOR_DESPAWN_TIME);
|
||||
}
|
||||
|
||||
void BattlegroundWS::AddPlayer(Player* player)
|
||||
|
||||
@@ -30,7 +30,8 @@ enum BG_WS_Events
|
||||
BG_WS_EVENT_ALLIANCE_DROP_FLAG = 4,
|
||||
BG_WS_EVENT_HORDE_DROP_FLAG = 5,
|
||||
BG_WS_EVENT_BOTH_FLAGS_KEPT10 = 6,
|
||||
BG_WS_EVENT_BOTH_FLAGS_KEPT15 = 7
|
||||
BG_WS_EVENT_BOTH_FLAGS_KEPT15 = 7,
|
||||
BG_WS_EVENT_DESPAWN_DOORS = 8
|
||||
};
|
||||
|
||||
enum BG_WS_TimerOrScore
|
||||
@@ -41,7 +42,8 @@ enum BG_WS_TimerOrScore
|
||||
BG_WS_FLAG_RESPAWN_TIME = 23 * IN_MILLISECONDS,
|
||||
BG_WS_FLAG_DROP_TIME = 10 * IN_MILLISECONDS,
|
||||
BG_WS_SPELL_FORCE_TIME = 10 * MINUTE * IN_MILLISECONDS,
|
||||
BG_WS_SPELL_BRUTAL_TIME = 15 * MINUTE * IN_MILLISECONDS
|
||||
BG_WS_SPELL_BRUTAL_TIME = 15 * MINUTE * IN_MILLISECONDS,
|
||||
BG_WS_DOOR_DESPAWN_TIME = 5 * IN_MILLISECONDS
|
||||
};
|
||||
|
||||
enum BG_WS_BroadcastTexts
|
||||
|
||||
@@ -2302,11 +2302,6 @@ bool ConditionMgr::isConditionTypeValid(Condition* cond)
|
||||
break;
|
||||
case CONDITION_WORLD_STATE:
|
||||
{
|
||||
if (!sWorld->getWorldState(cond->ConditionValue1))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "World state condition has non existing world state in value1 ({}), skipped", cond->ConditionValue1);
|
||||
return false;
|
||||
}
|
||||
if (cond->ConditionValue3)
|
||||
LOG_ERROR("sql.sql", "World state condition has useless data in value3 ({})!", cond->ConditionValue3);
|
||||
break;
|
||||
|
||||
@@ -105,7 +105,7 @@ namespace lfg
|
||||
sLFGMgr->LeaveLfg(player->GetGUID());
|
||||
sLFGMgr->LeaveAllLfgQueues(player->GetGUID(), true);
|
||||
player->RemoveAurasDueToSpell(LFG_SPELL_LUCK_OF_THE_DRAW);
|
||||
player->TeleportTo(player->m_homebindMapId, player->m_homebindX, player->m_homebindY, player->m_homebindZ, 0.0f);
|
||||
player->TeleportTo(player->m_homebindMapId, player->m_homebindX, player->m_homebindY, player->m_homebindZ, player->m_homebindO);
|
||||
LOG_DEBUG("lfg", "LFGPlayerScript::OnMapChanged, Player {} ({}) is in LFG dungeon map but does not have a valid group! Teleporting to homebind.",
|
||||
player->GetName(), player->GetGUID().ToString());
|
||||
return;
|
||||
|
||||
@@ -646,7 +646,7 @@ void Creature::Update(uint32 diff)
|
||||
if (!allowed) // Will be rechecked on next Update call
|
||||
break;
|
||||
|
||||
ObjectGuid dbtableHighGuid = ObjectGuid::Create<HighGuid::Unit>(GetEntry(), m_spawnId);
|
||||
ObjectGuid dbtableHighGuid = ObjectGuid::Create<HighGuid::Unit>(m_creatureData ? m_creatureData->id1 : GetEntry(), m_spawnId);
|
||||
time_t linkedRespawntime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid);
|
||||
if (!linkedRespawntime) // Can respawn
|
||||
Respawn();
|
||||
@@ -1139,8 +1139,6 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, u
|
||||
break;
|
||||
}
|
||||
|
||||
LoadCreaturesAddon();
|
||||
|
||||
uint32 displayID = GetNativeDisplayId();
|
||||
if (sObjectMgr->GetCreatureModelRandomGender(&displayID) && !IsTotem()) // Cancel load if no model defined or if totem
|
||||
{
|
||||
@@ -1148,6 +1146,8 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, u
|
||||
SetNativeDisplayId(displayID);
|
||||
}
|
||||
|
||||
LoadCreaturesAddon();
|
||||
|
||||
//! Need to be called after LoadCreaturesAddon - MOVEMENTFLAG_HOVER is set there
|
||||
m_positionZ += GetHoverHeight();
|
||||
|
||||
@@ -2032,11 +2032,15 @@ void Creature::Respawn(bool force)
|
||||
setDeathState(JUST_RESPAWNED);
|
||||
|
||||
// MDic - Acidmanifesto
|
||||
uint32 displayID = GetNativeDisplayId();
|
||||
if (sObjectMgr->GetCreatureModelRandomGender(&displayID)) // Cancel load if no model defined
|
||||
// Do not override transform auras
|
||||
if (GetAuraEffectsByType(SPELL_AURA_TRANSFORM).empty())
|
||||
{
|
||||
SetDisplayId(displayID);
|
||||
SetNativeDisplayId(displayID);
|
||||
uint32 displayID = GetNativeDisplayId();
|
||||
if (sObjectMgr->GetCreatureModelRandomGender(&displayID)) // Cancel load if no model defined
|
||||
{
|
||||
SetDisplayId(displayID);
|
||||
SetNativeDisplayId(displayID);
|
||||
}
|
||||
}
|
||||
|
||||
GetMotionMaster()->InitDefault();
|
||||
@@ -2157,7 +2161,7 @@ void Creature::LoadSpellTemplateImmunity()
|
||||
}
|
||||
}
|
||||
|
||||
bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo)
|
||||
bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
|
||||
{
|
||||
if (!spellInfo)
|
||||
return false;
|
||||
@@ -2179,7 +2183,7 @@ bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo)
|
||||
if (immunedToAllEffects)
|
||||
return true;
|
||||
|
||||
return Unit::IsImmunedToSpell(spellInfo);
|
||||
return Unit::IsImmunedToSpell(spellInfo, spell);
|
||||
}
|
||||
|
||||
bool Creature::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const
|
||||
|
||||
@@ -96,7 +96,7 @@ public:
|
||||
[[nodiscard]] bool IsValidTrainerForPlayer(Player* player, uint32* npcFlags = nullptr) const;
|
||||
bool CanCreatureAttack(Unit const* victim, bool skipDistCheck = false) const;
|
||||
void LoadSpellTemplateImmunity();
|
||||
bool IsImmunedToSpell(SpellInfo const* spellInfo) override;
|
||||
bool IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell = nullptr) override;
|
||||
|
||||
[[nodiscard]] bool HasMechanicTemplateImmunity(uint32 mask) const;
|
||||
// redefine Unit::IsImmunedToSpell
|
||||
@@ -155,7 +155,7 @@ public:
|
||||
return (getLevel() / 2 + uint32(GetStat(STAT_STRENGTH) / 20));
|
||||
}
|
||||
|
||||
[[nodiscard]] SpellSchoolMask GetMeleeDamageSchoolMask() const override { return m_meleeDamageSchoolMask; }
|
||||
[[nodiscard]] SpellSchoolMask GetMeleeDamageSchoolMask(WeaponAttackType /*attackType*/ = BASE_ATTACK, uint8 /*damageIndex*/ = 0) const override { return m_meleeDamageSchoolMask; }
|
||||
void SetMeleeDamageSchool(SpellSchools school) { m_meleeDamageSchoolMask = SpellSchoolMask(1 << school); }
|
||||
|
||||
void _AddCreatureSpellCooldown(uint32 spell_id, uint16 categoryId, uint32 end_time);
|
||||
@@ -178,7 +178,7 @@ public:
|
||||
void UpdateMaxHealth() override;
|
||||
void UpdateMaxPower(Powers power) override;
|
||||
void UpdateAttackPowerAndDamage(bool ranged = false) override;
|
||||
void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage) override;
|
||||
void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage, uint8 damageIndex) override;
|
||||
|
||||
void SetCanDualWield(bool value) override;
|
||||
[[nodiscard]] int8 GetOriginalEquipmentId() const { return m_originalEquipmentId; }
|
||||
|
||||
@@ -370,11 +370,7 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint16 flags) const
|
||||
// 0x08000000
|
||||
if (unit->m_movementInfo.GetMovementFlags() & MOVEMENTFLAG_SPLINE_ENABLED)
|
||||
{
|
||||
const G3D::Vector3* p = &unit->movespline->_Spline().getPoints(true)[0];
|
||||
if (unit->movespline->_Spline().getPoints(true).empty() || (!unit->movespline->_Spline().getPoints(true).empty() && !p))
|
||||
const_cast<Unit*>(unit)->DisableSpline();
|
||||
else
|
||||
Movement::PacketBuilder::WriteCreate(*unit->movespline, *data);
|
||||
Movement::PacketBuilder::WriteCreate(*unit->movespline, *data);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1237,11 +1233,11 @@ bool WorldObject::_IsWithinDist(WorldObject const* obj, float dist2compare, bool
|
||||
return distsq < maxdist * maxdist;
|
||||
}
|
||||
|
||||
Position WorldObject::GetHitSpherePointFor(Position const& dest, Optional<float> collisionHeight) const
|
||||
Position WorldObject::GetHitSpherePointFor(Position const& dest, Optional<float> collisionHeight, Optional<float> combatReach) const
|
||||
{
|
||||
G3D::Vector3 vThis(GetPositionX(), GetPositionY(), GetPositionZ() + (collisionHeight ? *collisionHeight : GetCollisionHeight()));
|
||||
G3D::Vector3 vObj(dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ());
|
||||
G3D::Vector3 contactPoint = vThis + (vObj - vThis).directionOrZero() * std::min(dest.GetExactDist(this), GetCombatReach());
|
||||
G3D::Vector3 contactPoint = vThis + (vObj - vThis).directionOrZero() * std::min(dest.GetExactDist(this), (combatReach ? *combatReach : GetCombatReach()));
|
||||
|
||||
return Position(contactPoint.x, contactPoint.y, contactPoint.z, GetAngle(contactPoint.x, contactPoint.y));
|
||||
}
|
||||
@@ -1348,7 +1344,7 @@ bool WorldObject::IsWithinLOS(float ox, float oy, float oz, VMAP::ModelIgnoreFla
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WorldObject::IsWithinLOSInMap(WorldObject const* obj, VMAP::ModelIgnoreFlags ignoreFlags, LineOfSightChecks checks, Optional<float> collisionHeight /*= {}*/) const
|
||||
bool WorldObject::IsWithinLOSInMap(WorldObject const* obj, VMAP::ModelIgnoreFlags ignoreFlags, LineOfSightChecks checks, Optional<float> collisionHeight /*= { }*/, Optional<float> combatReach /*= { }*/) const
|
||||
{
|
||||
if (!IsInMap(obj))
|
||||
return false;
|
||||
@@ -1369,14 +1365,14 @@ bool WorldObject::IsWithinLOSInMap(WorldObject const* obj, VMAP::ModelIgnoreFlag
|
||||
z += GetCollisionHeight();
|
||||
}
|
||||
else
|
||||
GetHitSpherePointFor({ obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ() + obj->GetCollisionHeight() }, x, y, z, collisionHeight);
|
||||
GetHitSpherePointFor({ obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ() + obj->GetCollisionHeight() }, x, y, z, collisionHeight, combatReach);
|
||||
|
||||
return GetMap()->isInLineOfSight(x, y, z, ox, oy, oz, GetPhaseMask(), checks, ignoreFlags);
|
||||
}
|
||||
|
||||
void WorldObject::GetHitSpherePointFor(Position const& dest, float& x, float& y, float& z, Optional<float> collisionHeight) const
|
||||
void WorldObject::GetHitSpherePointFor(Position const& dest, float& x, float& y, float& z, Optional<float> collisionHeight, Optional<float> combatReach) const
|
||||
{
|
||||
Position pos = GetHitSpherePointFor(dest, collisionHeight);
|
||||
Position pos = GetHitSpherePointFor(dest, collisionHeight, combatReach);
|
||||
x = pos.GetPositionX();
|
||||
y = pos.GetPositionY();
|
||||
z = pos.GetPositionZ();
|
||||
|
||||
@@ -465,9 +465,9 @@ public:
|
||||
bool IsWithinDist(WorldObject const* obj, float dist2compare, bool is3D = true, bool useBoundingRadius = true) const;
|
||||
bool IsWithinDistInMap(WorldObject const* obj, float dist2compare, bool is3D = true, bool useBoundingRadius = true) const;
|
||||
[[nodiscard]] bool IsWithinLOS(float x, float y, float z, VMAP::ModelIgnoreFlags ignoreFlags = VMAP::ModelIgnoreFlags::Nothing, LineOfSightChecks checks = LINEOFSIGHT_ALL_CHECKS) const;
|
||||
[[nodiscard]] bool IsWithinLOSInMap(WorldObject const* obj, VMAP::ModelIgnoreFlags ignoreFlags = VMAP::ModelIgnoreFlags::Nothing, LineOfSightChecks checks = LINEOFSIGHT_ALL_CHECKS, Optional<float> collisionHeight = { }) const;
|
||||
[[nodiscard]] Position GetHitSpherePointFor(Position const& dest, Optional<float> collisionHeight = { }) const;
|
||||
void GetHitSpherePointFor(Position const& dest, float& x, float& y, float& z, Optional<float> collisionHeight = { }) const;
|
||||
[[nodiscard]] bool IsWithinLOSInMap(WorldObject const* obj, VMAP::ModelIgnoreFlags ignoreFlags = VMAP::ModelIgnoreFlags::Nothing, LineOfSightChecks checks = LINEOFSIGHT_ALL_CHECKS, Optional<float> collisionHeight = { }, Optional<float> combatReach = { }) const;
|
||||
[[nodiscard]] Position GetHitSpherePointFor(Position const& dest, Optional<float> collisionHeight = { }, Optional<float> combatReach = { }) const;
|
||||
void GetHitSpherePointFor(Position const& dest, float& x, float& y, float& z, Optional<float> collisionHeight = { }, Optional<float> combatReach = { }) const;
|
||||
bool GetDistanceOrder(WorldObject const* obj1, WorldObject const* obj2, bool is3D = true) const;
|
||||
bool IsInRange(WorldObject const* obj, float minRange, float maxRange, bool is3D = true) const;
|
||||
[[nodiscard]] bool IsInRange2d(float x, float y, float minRange, float maxRange) const;
|
||||
|
||||
@@ -1052,7 +1052,14 @@ bool Guardian::InitStatsForLevel(uint8 petlevel)
|
||||
|
||||
uint32 creature_ID = (petType == HUNTER_PET) ? 1 : cinfo->Entry;
|
||||
|
||||
SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool));
|
||||
if (petType == HUNTER_PET)
|
||||
{
|
||||
SetMeleeDamageSchool(SPELL_SCHOOL_NORMAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool));
|
||||
}
|
||||
|
||||
SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(petlevel * 50));
|
||||
|
||||
@@ -1492,7 +1499,7 @@ void Pet::_SaveSpellCooldowns(CharacterDatabaseTransaction trans)
|
||||
{
|
||||
m_CreatureSpellCooldowns.erase(itr2);
|
||||
}
|
||||
else if (itr2->second.end <= infTime && (itr2->second.end > (curMSTime + 30 * IN_MILLISECONDS)))
|
||||
else if (itr2->second.end <= infTime)
|
||||
{
|
||||
uint32 cooldown = ((itr2->second.end - curMSTime) / IN_MILLISECONDS) + curTime;
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PET_SPELL_COOLDOWN);
|
||||
|
||||
@@ -349,6 +349,7 @@ Player::Player(WorldSession* session): Unit(true), m_mover(this)
|
||||
m_homebindX = 0;
|
||||
m_homebindY = 0;
|
||||
m_homebindZ = 0;
|
||||
m_homebindO = 0;
|
||||
|
||||
m_contestedPvPTimer = 0;
|
||||
|
||||
@@ -1585,7 +1586,7 @@ bool Player::TeleportToEntryPoint()
|
||||
|
||||
if (loc.m_mapId == MAPID_INVALID)
|
||||
{
|
||||
return TeleportTo(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ, GetOrientation());
|
||||
return TeleportTo(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ, m_homebindO);
|
||||
}
|
||||
|
||||
return TeleportTo(loc);
|
||||
@@ -4853,7 +4854,7 @@ void Player::RepopAtGraveyard()
|
||||
}
|
||||
}
|
||||
else if (GetPositionZ() < GetMap()->GetMinHeight(GetPositionX(), GetPositionY()))
|
||||
TeleportTo(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ, GetOrientation());
|
||||
TeleportTo(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ, m_homebindO);
|
||||
|
||||
RemovePlayerFlag(PLAYER_FLAGS_IS_OUT_OF_BOUNDS);
|
||||
}
|
||||
@@ -6742,21 +6743,11 @@ void Player::_ApplyItemBonuses(ItemTemplate const* proto, uint8 slot, bool apply
|
||||
if (proto->ArcaneRes)
|
||||
HandleStatModifier(UNIT_MOD_RESISTANCE_ARCANE, BASE_VALUE, float(proto->ArcaneRes), apply);
|
||||
|
||||
WeaponAttackType attType = BASE_ATTACK;
|
||||
|
||||
if (slot == EQUIPMENT_SLOT_RANGED && (
|
||||
proto->InventoryType == INVTYPE_RANGED || proto->InventoryType == INVTYPE_THROWN ||
|
||||
proto->InventoryType == INVTYPE_RANGEDRIGHT))
|
||||
uint8 attType = Player::GetAttackBySlot(slot);
|
||||
if (attType != MAX_ATTACK)
|
||||
{
|
||||
attType = RANGED_ATTACK;
|
||||
}
|
||||
else if (slot == EQUIPMENT_SLOT_OFFHAND)
|
||||
{
|
||||
attType = OFF_ATTACK;
|
||||
}
|
||||
|
||||
if (CanUseAttackType(attType))
|
||||
_ApplyWeaponDamage(slot, proto, ssv, apply);
|
||||
}
|
||||
|
||||
// Druids get feral AP bonus from weapon dps (also use DPS from ScalingStatValue)
|
||||
if (getClass() == CLASS_DRUID)
|
||||
@@ -6799,45 +6790,56 @@ void Player::_ApplyWeaponDamage(uint8 slot, ItemTemplate const* proto, ScalingSt
|
||||
ssv = ScalingStatValue ? sScalingStatValuesStore.LookupEntry(ssd_level) : nullptr;
|
||||
}
|
||||
|
||||
WeaponAttackType attType = BASE_ATTACK;
|
||||
float damage = 0.0f;
|
||||
|
||||
if (slot == EQUIPMENT_SLOT_RANGED && (
|
||||
proto->InventoryType == INVTYPE_RANGED || proto->InventoryType == INVTYPE_THROWN ||
|
||||
proto->InventoryType == INVTYPE_RANGEDRIGHT))
|
||||
uint8 attType = Player::GetAttackBySlot(slot);
|
||||
if (!IsInFeralForm() && apply && !CanUseAttackType(attType))
|
||||
{
|
||||
attType = RANGED_ATTACK;
|
||||
}
|
||||
else if (slot == EQUIPMENT_SLOT_OFFHAND)
|
||||
{
|
||||
attType = OFF_ATTACK;
|
||||
return;
|
||||
}
|
||||
|
||||
float minDamage = proto->Damage[0].DamageMin;
|
||||
float maxDamage = proto->Damage[0].DamageMax;
|
||||
|
||||
// If set dpsMod in ScalingStatValue use it for min (70% from average), max (130% from average) damage
|
||||
if (ssv)
|
||||
for (uint8 i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i)
|
||||
{
|
||||
int32 extraDPS = ssv->getDPSMod(ScalingStatValue);
|
||||
if (extraDPS)
|
||||
float minDamage = proto->Damage[i].DamageMin;
|
||||
float maxDamage = proto->Damage[i].DamageMax;
|
||||
|
||||
// If set dpsMod in ScalingStatValue use it for min (70% from average), max (130% from average) damage
|
||||
if (ssv)
|
||||
{
|
||||
float average = extraDPS * proto->Delay / 1000.0f;
|
||||
minDamage = 0.7f * average;
|
||||
maxDamage = 1.3f * average;
|
||||
int32 extraDPS = ssv->getDPSMod(ScalingStatValue);
|
||||
if (extraDPS)
|
||||
{
|
||||
float average = extraDPS * proto->Delay / 1000.0f;
|
||||
minDamage = 0.7f * average;
|
||||
maxDamage = 1.3f * average;
|
||||
}
|
||||
}
|
||||
|
||||
if (apply)
|
||||
{
|
||||
if (minDamage > 0.f)
|
||||
{
|
||||
SetBaseWeaponDamage(WeaponAttackType(attType), MINDAMAGE, minDamage, i);
|
||||
}
|
||||
|
||||
if (maxDamage > 0.f)
|
||||
{
|
||||
SetBaseWeaponDamage(WeaponAttackType(attType), MAXDAMAGE, maxDamage, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (minDamage > 0)
|
||||
if (!apply)
|
||||
{
|
||||
damage = apply ? minDamage : BASE_MINDAMAGE;
|
||||
SetBaseWeaponDamage(attType, MINDAMAGE, damage);
|
||||
}
|
||||
for (uint8 i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i)
|
||||
{
|
||||
SetBaseWeaponDamage(WeaponAttackType(attType), MINDAMAGE, 0.f, i);
|
||||
SetBaseWeaponDamage(WeaponAttackType(attType), MAXDAMAGE, 0.f, i);
|
||||
}
|
||||
|
||||
if (maxDamage > 0)
|
||||
{
|
||||
damage = apply ? maxDamage : BASE_MAXDAMAGE;
|
||||
SetBaseWeaponDamage(attType, MAXDAMAGE, damage);
|
||||
if (attType == BASE_ATTACK)
|
||||
{
|
||||
SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, BASE_MINDAMAGE);
|
||||
SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, BASE_MAXDAMAGE);
|
||||
}
|
||||
}
|
||||
|
||||
if (proto->Delay && !IsInFeralForm())
|
||||
@@ -6854,8 +6856,18 @@ void Player::_ApplyWeaponDamage(uint8 slot, ItemTemplate const* proto, ScalingSt
|
||||
if (IsInFeralForm())
|
||||
return;
|
||||
|
||||
if (CanModifyStats() && (damage || proto->Delay))
|
||||
UpdateDamagePhysical(attType);
|
||||
if (CanModifyStats() && (GetWeaponDamageRange(WeaponAttackType(attType), MAXDAMAGE) || proto->Delay))
|
||||
UpdateDamagePhysical(WeaponAttackType(attType));
|
||||
}
|
||||
|
||||
SpellSchoolMask Player::GetMeleeDamageSchoolMask(WeaponAttackType attackType /*= BASE_ATTACK*/, uint8 damageIndex /*= 0*/) const
|
||||
{
|
||||
if (Item const* weapon = GetWeaponForAttack(attackType, true))
|
||||
{
|
||||
return SpellSchoolMask(1 << weapon->GetTemplate()->Damage[damageIndex].DamageType);
|
||||
}
|
||||
|
||||
return SPELL_SCHOOL_MASK_NORMAL;
|
||||
}
|
||||
|
||||
void Player::_ApplyWeaponDependentAuraMods(Item* item, WeaponAttackType attackType, bool apply)
|
||||
@@ -8688,7 +8700,7 @@ void Player::SendBGWeekendWorldStates()
|
||||
void Player::SendBattlefieldWorldStates()
|
||||
{
|
||||
/// Send misc stuff that needs to be sent on every login, like the battle timers.
|
||||
if (sWorld->getBoolConfig(CONFIG_WINTERGRASP_ENABLE))
|
||||
if (sWorld->getIntConfig(CONFIG_WINTERGRASP_ENABLE) == 1)
|
||||
{
|
||||
if (BattlefieldWG* wg = (BattlefieldWG*)sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG))
|
||||
{
|
||||
@@ -11045,7 +11057,7 @@ void Player::SetEntryPoint()
|
||||
}
|
||||
|
||||
if (m_entryPointData.joinPos.m_mapId == MAPID_INVALID)
|
||||
m_entryPointData.joinPos = WorldLocation(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ, 0.0f);
|
||||
m_entryPointData.joinPos = WorldLocation(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ, m_homebindO);
|
||||
}
|
||||
|
||||
void Player::LeaveBattleground(Battleground* bg)
|
||||
@@ -13306,14 +13318,21 @@ uint32 Player::CalculateTalentsPoints() const
|
||||
{
|
||||
uint32 base_talent = getLevel() < 10 ? 0 : getLevel() - 9;
|
||||
|
||||
uint32 talentPointsForLevel = 0;
|
||||
if (getClass() != CLASS_DEATH_KNIGHT || GetMapId() != 609)
|
||||
return uint32(base_talent * sWorld->getRate(RATE_TALENT));
|
||||
|
||||
uint32 talentPointsForLevel = getLevel() < 56 ? 0 : getLevel() - 55;
|
||||
talentPointsForLevel += m_questRewardTalentCount;
|
||||
|
||||
if (talentPointsForLevel > base_talent)
|
||||
{
|
||||
talentPointsForLevel = base_talent;
|
||||
}
|
||||
else
|
||||
{
|
||||
talentPointsForLevel = getLevel() < 56 ? 0 : getLevel() - 55;
|
||||
talentPointsForLevel += m_questRewardTalentCount;
|
||||
|
||||
if (talentPointsForLevel > base_talent)
|
||||
{
|
||||
talentPointsForLevel = base_talent;
|
||||
}
|
||||
}
|
||||
|
||||
talentPointsForLevel += m_extraBonusTalentCount;
|
||||
return uint32(talentPointsForLevel * sWorld->getRate(RATE_TALENT));
|
||||
@@ -13611,16 +13630,23 @@ void Player::CompletedAchievement(AchievementEntry const* entry)
|
||||
m_achievementMgr->CompletedAchievement(entry);
|
||||
}
|
||||
|
||||
void Player::LearnTalent(uint32 talentId, uint32 talentRank)
|
||||
void Player::LearnTalent(uint32 talentId, uint32 talentRank, bool command /*= false*/)
|
||||
{
|
||||
uint32 CurTalentPoints = GetFreeTalentPoints();
|
||||
|
||||
// xinef: check basic data
|
||||
if (CurTalentPoints == 0)
|
||||
return;
|
||||
if (!command)
|
||||
{
|
||||
// xinef: check basic data
|
||||
if (!CurTalentPoints)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (talentRank >= MAX_TALENT_RANK)
|
||||
return;
|
||||
if (talentRank >= MAX_TALENT_RANK)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
|
||||
if (!talentInfo)
|
||||
@@ -13649,10 +13675,15 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank)
|
||||
if (currentTalentRank >= talentRank + 1)
|
||||
return;
|
||||
|
||||
// xinef: check if we have enough free talent points
|
||||
uint32 talentPointsChange = (talentRank - currentTalentRank + 1);
|
||||
if (CurTalentPoints < talentPointsChange)
|
||||
return;
|
||||
if (!command)
|
||||
{
|
||||
// xinef: check if we have enough free talent points
|
||||
if (CurTalentPoints < talentPointsChange)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// xinef: check if talent deponds on another talent
|
||||
if (talentInfo->DependsOn > 0)
|
||||
@@ -13674,23 +13705,26 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank)
|
||||
return;
|
||||
}
|
||||
|
||||
// xinef: check amount of points spent in current talent tree
|
||||
// xinef: be smart and quick
|
||||
uint32 spentPoints = 0;
|
||||
if (talentInfo->Row > 0)
|
||||
if (!command)
|
||||
{
|
||||
const PlayerTalentMap& talentMap = GetTalentMap();
|
||||
for (PlayerTalentMap::const_iterator itr = talentMap.begin(); itr != talentMap.end(); ++itr)
|
||||
if (TalentSpellPos const* talentPos = GetTalentSpellPos(itr->first))
|
||||
if (TalentEntry const* itrTalentInfo = sTalentStore.LookupEntry(talentPos->talent_id))
|
||||
if (itrTalentInfo->TalentTab == talentInfo->TalentTab)
|
||||
if (itr->second->State != PLAYERSPELL_REMOVED && itr->second->IsInSpec(GetActiveSpec())) // pussywizard
|
||||
spentPoints += talentPos->rank + 1;
|
||||
}
|
||||
// xinef: check amount of points spent in current talent tree
|
||||
// xinef: be smart and quick
|
||||
uint32 spentPoints = 0;
|
||||
if (talentInfo->Row > 0)
|
||||
{
|
||||
const PlayerTalentMap& talentMap = GetTalentMap();
|
||||
for (PlayerTalentMap::const_iterator itr = talentMap.begin(); itr != talentMap.end(); ++itr)
|
||||
if (TalentSpellPos const* talentPos = GetTalentSpellPos(itr->first))
|
||||
if (TalentEntry const* itrTalentInfo = sTalentStore.LookupEntry(talentPos->talent_id))
|
||||
if (itrTalentInfo->TalentTab == talentInfo->TalentTab)
|
||||
if (itr->second->State != PLAYERSPELL_REMOVED && itr->second->IsInSpec(GetActiveSpec())) // pussywizard
|
||||
spentPoints += talentPos->rank + 1;
|
||||
}
|
||||
|
||||
// xinef: we do not have enough talent points to add talent of this tier
|
||||
if (spentPoints < (talentInfo->Row * MAX_TALENT_RANK))
|
||||
return;
|
||||
// xinef: we do not have enough talent points to add talent of this tier
|
||||
if (spentPoints < (talentInfo->Row * MAX_TALENT_RANK))
|
||||
return;
|
||||
}
|
||||
|
||||
// xinef: hacking attempt, tries to learn unknown rank
|
||||
uint32 spellId = talentInfo->RankID[talentRank];
|
||||
@@ -13723,7 +13757,11 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank)
|
||||
|
||||
// xinef: update free talent points count
|
||||
m_usedTalentCount += talentPointsChange;
|
||||
SetFreeTalentPoints(CurTalentPoints - talentPointsChange);
|
||||
|
||||
if (!command)
|
||||
{
|
||||
SetFreeTalentPoints(CurTalentPoints - talentPointsChange);
|
||||
}
|
||||
|
||||
sScriptMgr->OnPlayerLearnTalents(this, talentId, talentRank, spellId);
|
||||
}
|
||||
|
||||
@@ -1677,7 +1677,7 @@ public:
|
||||
void BuildPlayerTalentsInfoData(WorldPacket* data);
|
||||
void BuildPetTalentsInfoData(WorldPacket* data);
|
||||
void SendTalentsInfoData(bool pet);
|
||||
void LearnTalent(uint32 talentId, uint32 talentRank);
|
||||
void LearnTalent(uint32 talentId, uint32 talentRank, bool command = false);
|
||||
void LearnPetTalent(ObjectGuid petGuid, uint32 talentId, uint32 talentRank);
|
||||
|
||||
bool addTalent(uint32 spellId, uint8 addSpecMask, uint8 oldTalentRank);
|
||||
@@ -1905,7 +1905,7 @@ public:
|
||||
void UpdateRating(CombatRating cr);
|
||||
void UpdateAllRatings();
|
||||
|
||||
void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage) override;
|
||||
void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage, uint8 damageIndex) override;
|
||||
|
||||
void UpdateDefenseBonusesMod();
|
||||
inline void RecalculateRating(CombatRating cr) { ApplyRatingMod(cr, 0, true);}
|
||||
@@ -2147,6 +2147,8 @@ public:
|
||||
|
||||
void ResetAllPowers();
|
||||
|
||||
SpellSchoolMask GetMeleeDamageSchoolMask(WeaponAttackType attackType = BASE_ATTACK, uint8 damageIndex = 0) const override;
|
||||
|
||||
void _ApplyWeaponDependentAuraMods(Item* item, WeaponAttackType attackType, bool apply);
|
||||
void _ApplyWeaponDependentAuraCritMod(Item* item, WeaponAttackType attackType, AuraEffect const* aura, bool apply);
|
||||
void _ApplyWeaponDependentAuraDamageMod(Item* item, WeaponAttackType attackType, AuraEffect const* aura, bool apply);
|
||||
@@ -2317,6 +2319,7 @@ public:
|
||||
float m_homebindX;
|
||||
float m_homebindY;
|
||||
float m_homebindZ;
|
||||
float m_homebindO;
|
||||
|
||||
[[nodiscard]] WorldLocation GetStartPosition() const;
|
||||
|
||||
|
||||
@@ -78,6 +78,13 @@ PlayerSetting Player::GetPlayerSetting(std::string source, uint8 index)
|
||||
return GetPlayerSetting(source, index);
|
||||
}
|
||||
|
||||
PlayerSettingVector settingVector = itr->second;
|
||||
if (settingVector.size() < (uint8)(index + 1))
|
||||
{
|
||||
UpdatePlayerSetting(source, index, 0);
|
||||
return GetPlayerSetting(source, index);
|
||||
}
|
||||
|
||||
return itr->second[index];
|
||||
}
|
||||
|
||||
|
||||
@@ -2593,6 +2593,8 @@ Item* Player::StoreNewItem(ItemPosCountVec const& dest, uint32 item, bool update
|
||||
stmt->SetData(1, ss.str());
|
||||
CharacterDatabase.Execute(stmt);
|
||||
}
|
||||
|
||||
sScriptMgr->OnStoreNewItem(this, pItem, count);
|
||||
}
|
||||
return pItem;
|
||||
}
|
||||
@@ -4922,7 +4924,7 @@ bool Player::LoadPositionFromDB(uint32& mapid, float& x, float& y, float& z, flo
|
||||
|
||||
void Player::SetHomebind(WorldLocation const& loc, uint32 areaId)
|
||||
{
|
||||
loc.GetPosition(m_homebindX, m_homebindY, m_homebindZ);
|
||||
loc.GetPosition(m_homebindX, m_homebindY, m_homebindZ, m_homebindO);
|
||||
m_homebindMapId = loc.GetMapId();
|
||||
m_homebindAreaId = areaId;
|
||||
|
||||
@@ -4930,10 +4932,11 @@ void Player::SetHomebind(WorldLocation const& loc, uint32 areaId)
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_PLAYER_HOMEBIND);
|
||||
stmt->SetData(0, m_homebindMapId);
|
||||
stmt->SetData(1, m_homebindAreaId);
|
||||
stmt->SetData (2, m_homebindX);
|
||||
stmt->SetData (3, m_homebindY);
|
||||
stmt->SetData (4, m_homebindZ);
|
||||
stmt->SetData(5, GetGUID().GetCounter());
|
||||
stmt->SetData(2, m_homebindX);
|
||||
stmt->SetData(3, m_homebindY);
|
||||
stmt->SetData(4, m_homebindZ);
|
||||
stmt->SetData(5, m_homebindO);
|
||||
stmt->SetData(6, GetGUID().GetCounter());
|
||||
CharacterDatabase.Execute(stmt);
|
||||
}
|
||||
|
||||
@@ -5104,7 +5107,7 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons
|
||||
|
||||
std::string taxi_nodes = fields[42].Get<std::string>();
|
||||
|
||||
auto RelocateToHomebind = [this, &mapId, &instanceId]() { mapId = m_homebindMapId; instanceId = 0; Relocate(m_homebindX, m_homebindY, m_homebindZ); };
|
||||
auto RelocateToHomebind = [this, &mapId, &instanceId]() { mapId = m_homebindMapId; instanceId = 0; Relocate(m_homebindX, m_homebindY, m_homebindZ, m_homebindO); };
|
||||
|
||||
_LoadGroup();
|
||||
|
||||
@@ -5426,7 +5429,8 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons
|
||||
? bubble1 * sWorld->getRate(RATE_REST_OFFLINE_IN_TAVERN_OR_CITY)
|
||||
: bubble0 * sWorld->getRate(RATE_REST_OFFLINE_IN_WILDERNESS);
|
||||
|
||||
SetRestBonus(GetRestBonus() + time_diff * ((float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP) / 72000)*bubble);
|
||||
// Client automatically doubles the value sent so we have to divide it by 2
|
||||
SetRestBonus(GetRestBonus() + time_diff * ((float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP) / 144000)*bubble);
|
||||
}
|
||||
|
||||
uint32 innTriggerId = fields[72].Get<uint32>();
|
||||
@@ -7004,7 +7008,7 @@ bool Player::_LoadHomeBind(PreparedQueryResult result)
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
// SELECT mapId, zoneId, posX, posY, posZ FROM character_homebind WHERE guid = ?
|
||||
// SELECT mapId, zoneId, posX, posY, posZ, pozO FROM character_homebind WHERE guid = ?
|
||||
if (result)
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
@@ -7014,11 +7018,12 @@ bool Player::_LoadHomeBind(PreparedQueryResult result)
|
||||
m_homebindX = fields[2].Get<float>();
|
||||
m_homebindY = fields[3].Get<float>();
|
||||
m_homebindZ = fields[4].Get<float>();
|
||||
m_homebindO = fields[5].Get<float>();
|
||||
|
||||
MapEntry const* bindMapEntry = sMapStore.LookupEntry(m_homebindMapId);
|
||||
|
||||
// accept saved data only for valid position (and non instanceable), and accessable
|
||||
if (MapMgr::IsValidMapCoord(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ) &&
|
||||
if (MapMgr::IsValidMapCoord(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ, m_homebindO) &&
|
||||
!bindMapEntry->Instanceable() && GetSession()->Expansion() >= bindMapEntry->Expansion())
|
||||
ok = true;
|
||||
else
|
||||
@@ -7036,19 +7041,20 @@ bool Player::_LoadHomeBind(PreparedQueryResult result)
|
||||
m_homebindX = info->positionX;
|
||||
m_homebindY = info->positionY;
|
||||
m_homebindZ = info->positionZ;
|
||||
|
||||
m_homebindO = info->orientation;
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PLAYER_HOMEBIND);
|
||||
stmt->SetData(0, GetGUID().GetCounter());
|
||||
stmt->SetData(1, m_homebindMapId);
|
||||
stmt->SetData(2, m_homebindAreaId);
|
||||
stmt->SetData (3, m_homebindX);
|
||||
stmt->SetData (4, m_homebindY);
|
||||
stmt->SetData (5, m_homebindZ);
|
||||
stmt->SetData(3, m_homebindX);
|
||||
stmt->SetData(4, m_homebindY);
|
||||
stmt->SetData(5, m_homebindZ);
|
||||
stmt->SetData(6, m_homebindO);
|
||||
CharacterDatabase.Execute(stmt);
|
||||
}
|
||||
|
||||
LOG_DEBUG("entities.player", "Setting player home position - mapid: {}, areaid: {}, X: {}, Y: {}, Z: {}",
|
||||
m_homebindMapId, m_homebindAreaId, m_homebindX, m_homebindY, m_homebindZ);
|
||||
LOG_DEBUG("entities.player", "Setting player home position - mapid: {}, areaid: {}, X: {}, Y: {}, Z: {}, O: {}",
|
||||
m_homebindMapId, m_homebindAreaId, m_homebindX, m_homebindY, m_homebindZ, m_homebindO);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1019,7 +1019,7 @@ void Player::UpdateCombatSkills(Unit* victim, WeaponAttackType attType, bool def
|
||||
|
||||
chance = chance < 1.0f ? 1.0f : chance; // minimum chance to increase skill is 1%
|
||||
|
||||
LOG_DEBUG("entities.player", "Player::UpdateCombatSkills(defence:{}, playerLevel:{}, moblevel:{}) -> ({}/{}) chance to increase skill is {}\%", defence, playerLevel, moblevel, currentSkillValue, currentSkillMax, chance);
|
||||
LOG_DEBUG("entities.player", "Player::UpdateCombatSkills(defence:{}, playerLevel:{}, moblevel:{}) -> ({}/{}) chance to increase skill is {}%", defence, playerLevel, moblevel, currentSkillValue, currentSkillMax, chance);
|
||||
|
||||
if (roll_chance_f(chance))
|
||||
{
|
||||
|
||||
@@ -59,25 +59,31 @@ void Unit::UpdateAllResistances()
|
||||
|
||||
void Unit::UpdateDamagePhysical(WeaponAttackType attType)
|
||||
{
|
||||
float minDamage = 0.0f;
|
||||
float maxDamage = 0.0f;
|
||||
float totalMin = 0.f;
|
||||
float totalMax = 0.f;
|
||||
|
||||
CalculateMinMaxDamage(attType, false, true, minDamage, maxDamage);
|
||||
float tmpMin, tmpMax;
|
||||
for (uint8 i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i)
|
||||
{
|
||||
CalculateMinMaxDamage(attType, false, true, tmpMin, tmpMax, i);
|
||||
totalMin += tmpMin;
|
||||
totalMax += tmpMax;
|
||||
}
|
||||
|
||||
switch (attType)
|
||||
{
|
||||
case BASE_ATTACK:
|
||||
default:
|
||||
SetStatFloatValue(UNIT_FIELD_MINDAMAGE, minDamage);
|
||||
SetStatFloatValue(UNIT_FIELD_MAXDAMAGE, maxDamage);
|
||||
SetStatFloatValue(UNIT_FIELD_MINDAMAGE, totalMin);
|
||||
SetStatFloatValue(UNIT_FIELD_MAXDAMAGE, totalMax);
|
||||
break;
|
||||
case OFF_ATTACK:
|
||||
SetStatFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE, minDamage);
|
||||
SetStatFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE, maxDamage);
|
||||
SetStatFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE, totalMin);
|
||||
SetStatFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE, totalMax);
|
||||
break;
|
||||
case RANGED_ATTACK:
|
||||
SetStatFloatValue(UNIT_FIELD_MINRANGEDDAMAGE, minDamage);
|
||||
SetStatFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE, maxDamage);
|
||||
SetStatFloatValue(UNIT_FIELD_MINRANGEDDAMAGE, totalMin);
|
||||
SetStatFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE, totalMax);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -525,8 +531,23 @@ void Player::UpdateShieldBlockValue()
|
||||
SetUInt32Value(PLAYER_SHIELD_BLOCK, GetShieldBlockValue());
|
||||
}
|
||||
|
||||
void Player::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage)
|
||||
void Player::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage, uint8 damageIndex)
|
||||
{
|
||||
// Only proto damage, not affected by any mods
|
||||
if (damageIndex != 0)
|
||||
{
|
||||
minDamage = 0.0f;
|
||||
maxDamage = 0.0f;
|
||||
|
||||
if (!IsInFeralForm() && CanUseAttackType(attType))
|
||||
{
|
||||
minDamage = GetWeaponDamageRange(attType, MINDAMAGE, damageIndex);
|
||||
maxDamage = GetWeaponDamageRange(attType, MAXDAMAGE, damageIndex);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
UnitMods unitMod;
|
||||
|
||||
switch (attType)
|
||||
@@ -1075,8 +1096,16 @@ void Creature::UpdateAttackPowerAndDamage(bool ranged)
|
||||
}
|
||||
}
|
||||
|
||||
void Creature::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage)
|
||||
void Creature::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage, uint8 damageIndex /*= 0*/)
|
||||
{
|
||||
// creatures only have one damage
|
||||
if (damageIndex != 0)
|
||||
{
|
||||
minDamage = 0.f;
|
||||
maxDamage = 0.f;
|
||||
return;
|
||||
}
|
||||
|
||||
UnitMods unitMod;
|
||||
float variance = 1.0f;
|
||||
switch (attType)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -23,6 +23,7 @@
|
||||
#include "FollowerRefMgr.h"
|
||||
#include "FollowerReference.h"
|
||||
#include "HostileRefMgr.h"
|
||||
#include "ItemTemplate.h"
|
||||
#include "MotionMaster.h"
|
||||
#include "Object.h"
|
||||
#include "Optional.h"
|
||||
@@ -251,7 +252,9 @@ enum UnitModifierType
|
||||
enum WeaponDamageRange
|
||||
{
|
||||
MINDAMAGE,
|
||||
MAXDAMAGE
|
||||
MAXDAMAGE,
|
||||
|
||||
MAX_WEAPON_DAMAGE_RANGE
|
||||
};
|
||||
|
||||
enum UnitMods
|
||||
@@ -714,6 +717,12 @@ enum MeleeHitOutcome
|
||||
MELEE_HIT_GLANCING, MELEE_HIT_CRIT, MELEE_HIT_CRUSHING, MELEE_HIT_NORMAL
|
||||
};
|
||||
|
||||
enum ExtraAttackSpells
|
||||
{
|
||||
SPELL_SWORD_SPECIALIZATION = 16459,
|
||||
SPELL_HACK_AND_SLASH = 66923
|
||||
};
|
||||
|
||||
class DispelInfo
|
||||
{
|
||||
public:
|
||||
@@ -762,9 +771,14 @@ private:
|
||||
uint32 m_resist;
|
||||
uint32 m_block;
|
||||
uint32 m_cleanDamage;
|
||||
|
||||
// amalgamation constructor (used for proc)
|
||||
DamageInfo(DamageInfo const& dmg1, DamageInfo const& dmg2);
|
||||
|
||||
public:
|
||||
explicit DamageInfo(Unit* _attacker, Unit* _victim, uint32 _damage, SpellInfo const* _spellInfo, SpellSchoolMask _schoolMask, DamageEffectType _damageType, uint32 cleanDamage = 0);
|
||||
explicit DamageInfo(CalcDamageInfo& dmgInfo);
|
||||
explicit DamageInfo(CalcDamageInfo const& dmgInfo); // amalgamation wrapper
|
||||
DamageInfo(CalcDamageInfo const& dmgInfo, uint8 damageIndex);
|
||||
DamageInfo(SpellNonMeleeDamage const& spellNonMeleeDamage, DamageEffectType damageType);
|
||||
|
||||
void ModifyDamage(int32 amount);
|
||||
@@ -868,10 +882,15 @@ struct CalcDamageInfo
|
||||
{
|
||||
Unit* attacker; // Attacker
|
||||
Unit* target; // Target for damage
|
||||
uint32 damageSchoolMask;
|
||||
uint32 damage;
|
||||
uint32 absorb;
|
||||
uint32 resist;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 damageSchoolMask;
|
||||
uint32 damage;
|
||||
uint32 absorb;
|
||||
uint32 resist;
|
||||
} damages[MAX_ITEM_PROTO_DAMAGES];
|
||||
|
||||
uint32 blocked_amount;
|
||||
uint32 HitInfo;
|
||||
uint32 TargetState;
|
||||
@@ -1331,7 +1350,7 @@ public:
|
||||
bool IsWithinCombatRange(Unit const* obj, float dist2compare) const;
|
||||
bool IsWithinMeleeRange(Unit const* obj, float dist = 0.f) const;
|
||||
float GetMeleeRange(Unit const* target) const;
|
||||
[[nodiscard]] virtual SpellSchoolMask GetMeleeDamageSchoolMask() const;
|
||||
virtual SpellSchoolMask GetMeleeDamageSchoolMask(WeaponAttackType attackType = BASE_ATTACK, uint8 damageIndex = 0) const = 0;
|
||||
bool GetRandomContactPoint(Unit const* target, float& x, float& y, float& z, bool force = false) const;
|
||||
uint32 m_extraAttacks;
|
||||
bool m_canDualWield;
|
||||
@@ -1541,7 +1560,7 @@ public:
|
||||
void HandleEmoteCommand(uint32 emoteId);
|
||||
void AttackerStateUpdate (Unit* victim, WeaponAttackType attType = BASE_ATTACK, bool extra = false, bool ignoreCasting = false);
|
||||
|
||||
void CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* damageInfo, WeaponAttackType attackType = BASE_ATTACK, const bool sittingVictim = false);
|
||||
void CalculateMeleeDamage(Unit* victim, CalcDamageInfo* damageInfo, WeaponAttackType attackType = BASE_ATTACK, const bool sittingVictim = false);
|
||||
void DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss);
|
||||
|
||||
void HandleProcExtraAttackFor(Unit* victim, uint32 count);
|
||||
@@ -1571,10 +1590,11 @@ public:
|
||||
|
||||
static void ApplyResilience(Unit const* victim, float* crit, int32* damage, bool isCrit, CombatRating type);
|
||||
|
||||
float MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, int32 skillDiff, uint32 spellId) const;
|
||||
SpellMissInfo MeleeSpellHitResult(Unit* victim, SpellInfo const* spell);
|
||||
SpellMissInfo MagicSpellHitResult(Unit* victim, SpellInfo const* spell);
|
||||
SpellMissInfo SpellHitResult(Unit* victim, SpellInfo const* spell, bool canReflect = false);
|
||||
[[nodiscard]] float MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, int32 skillDiff, uint32 spellId) const;
|
||||
[[nodiscard]] SpellMissInfo MeleeSpellHitResult(Unit* victim, SpellInfo const* spell);
|
||||
[[nodiscard]] SpellMissInfo MagicSpellHitResult(Unit* victim, SpellInfo const* spell);
|
||||
[[nodiscard]] SpellMissInfo SpellHitResult(Unit* victim, SpellInfo const* spell, bool canReflect = false);
|
||||
[[nodiscard]] SpellMissInfo SpellHitResult(Unit* victim, Spell const* spell, bool canReflect = false);
|
||||
|
||||
[[nodiscard]] float GetUnitDodgeChance() const;
|
||||
[[nodiscard]] float GetUnitParryChance() const;
|
||||
@@ -1732,7 +1752,7 @@ public:
|
||||
void SendAttackStateUpdate(uint32 HitInfo, Unit* target, uint8 SwingType, SpellSchoolMask damageSchoolMask, uint32 Damage, uint32 AbsorbDamage, uint32 Resist, VictimState TargetState, uint32 BlockedAmount);
|
||||
void SendSpellNonMeleeDamageLog(SpellNonMeleeDamage* log);
|
||||
void SendSpellNonMeleeReflectLog(SpellNonMeleeDamage* log, Unit* attacker);
|
||||
void SendSpellNonMeleeDamageLog(Unit* target, SpellInfo const* spellInfo, uint32 Damage, SpellSchoolMask damageSchoolMask, uint32 AbsorbedDamage, uint32 Resist, bool PhysicalDamage, uint32 Blocked, bool CriticalHit = false);
|
||||
void SendSpellNonMeleeDamageLog(Unit* target, SpellInfo const* spellInfo, uint32 Damage, SpellSchoolMask damageSchoolMask, uint32 AbsorbedDamage, uint32 Resist, bool PhysicalDamage, uint32 Blocked, bool CriticalHit = false, bool Split = false);
|
||||
void SendPeriodicAuraLog(SpellPeriodicAuraLogInfo* pInfo);
|
||||
void SendSpellMiss(Unit* target, uint32 spellID, SpellMissInfo missInfo);
|
||||
void SendSpellDamageResist(Unit* target, uint32 spellId);
|
||||
@@ -2093,10 +2113,10 @@ public:
|
||||
virtual void UpdateAttackPowerAndDamage(bool ranged = false) = 0;
|
||||
virtual void UpdateDamagePhysical(WeaponAttackType attType);
|
||||
float GetTotalAttackPowerValue(WeaponAttackType attType, Unit* pVictim = nullptr) const;
|
||||
[[nodiscard]] float GetWeaponDamageRange(WeaponAttackType attType, WeaponDamageRange type) const;
|
||||
void SetBaseWeaponDamage(WeaponAttackType attType, WeaponDamageRange damageRange, float value) { m_weaponDamage[attType][damageRange] = value; }
|
||||
virtual void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage) = 0;
|
||||
uint32 CalculateDamage(WeaponAttackType attType, bool normalized, bool addTotalPct);
|
||||
[[nodiscard]] float GetWeaponDamageRange(WeaponAttackType attType, WeaponDamageRange type, uint8 damageIndex = 0) const;
|
||||
void SetBaseWeaponDamage(WeaponAttackType attType, WeaponDamageRange damageRange, float value, uint8 damageIndex = 0) { m_weaponDamage[attType][damageRange][damageIndex] = value; }
|
||||
virtual void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage, uint8 damageIndex = 0) = 0;
|
||||
uint32 CalculateDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, uint8 itemDamagesMask = 0);
|
||||
float GetAPMultiplier(WeaponAttackType attType, bool normalized);
|
||||
|
||||
bool isInFrontInMap(Unit const* target, float distance, float arc = M_PI) const;
|
||||
@@ -2181,8 +2201,8 @@ public:
|
||||
uint32 SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, uint32 healamount, DamageEffectType damagetype, uint8 effIndex, float TotalMod = 0.0f, uint32 stack = 1);
|
||||
uint32 SpellHealingBonusTaken(Unit* caster, SpellInfo const* spellProto, uint32 healamount, DamageEffectType damagetype, uint32 stack = 1);
|
||||
|
||||
uint32 MeleeDamageBonusDone(Unit* pVictim, uint32 damage, WeaponAttackType attType, SpellInfo const* spellProto = nullptr);
|
||||
uint32 MeleeDamageBonusTaken(Unit* attacker, uint32 pdamage, WeaponAttackType attType, SpellInfo const* spellProto = nullptr);
|
||||
uint32 MeleeDamageBonusDone(Unit* pVictim, uint32 damage, WeaponAttackType attType, SpellInfo const* spellProto = nullptr, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NORMAL);
|
||||
uint32 MeleeDamageBonusTaken(Unit* attacker, uint32 pdamage, WeaponAttackType attType, SpellInfo const* spellProto = nullptr, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NORMAL);
|
||||
|
||||
bool isSpellBlocked(Unit* victim, SpellInfo const* spellProto, WeaponAttackType attackType = BASE_ATTACK);
|
||||
bool isBlockCritical();
|
||||
@@ -2203,12 +2223,14 @@ public:
|
||||
|
||||
void ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply, SpellImmuneBlockType blockType = SPELL_BLOCK_TYPE_ALL);
|
||||
void ApplySpellDispelImmunity(SpellInfo const* spellProto, DispelType type, bool apply);
|
||||
virtual bool IsImmunedToSpell(SpellInfo const* spellInfo);
|
||||
virtual bool IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell = nullptr);
|
||||
// redefined in Creature
|
||||
[[nodiscard]] bool IsImmunedToDamage(SpellSchoolMask meleeSchoolMask) const;
|
||||
bool IsImmunedToDamage(SpellInfo const* spellInfo) const;
|
||||
[[nodiscard]] bool IsImmunedToDamage(SpellInfo const* spellInfo) const;
|
||||
[[nodiscard]] bool IsImmunedToDamage(Spell const* spell) const;
|
||||
[[nodiscard]] bool IsImmunedToSchool(SpellSchoolMask meleeSchoolMask) const;
|
||||
bool IsImmunedToSchool(SpellInfo const* spellInfo) const;
|
||||
[[nodiscard]] bool IsImmunedToSchool(SpellInfo const* spellInfo) const;
|
||||
[[nodiscard]] bool IsImmunedToSchool(Spell const* spell) const;
|
||||
[[nodiscard]] bool IsImmunedToDamageOrSchool(SpellSchoolMask meleeSchoolMask) const;
|
||||
bool IsImmunedToDamageOrSchool(SpellInfo const* spellInfo) const;
|
||||
virtual bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const;
|
||||
@@ -2490,7 +2512,7 @@ protected:
|
||||
uint32 m_interruptMask;
|
||||
|
||||
float m_auraModifiersGroup[UNIT_MOD_END][MODIFIER_TYPE_END];
|
||||
float m_weaponDamage[MAX_ATTACK][2];
|
||||
float m_weaponDamage[MAX_ATTACK][MAX_WEAPON_DAMAGE_RANGE][MAX_ITEM_PROTO_DAMAGES];
|
||||
bool m_canModifyStats;
|
||||
VisibleAuraMap m_visibleAuras;
|
||||
|
||||
|
||||
@@ -100,6 +100,11 @@ namespace PlayerNameMapHolder
|
||||
PlayerNameMap.erase(p->GetName());
|
||||
}
|
||||
|
||||
void RemoveByName(std::string const& name)
|
||||
{
|
||||
PlayerNameMap.erase(name);
|
||||
}
|
||||
|
||||
Player* Find(std::string const& name)
|
||||
{
|
||||
std::string charName(name);
|
||||
@@ -290,3 +295,9 @@ void ObjectAccessor::RemoveObject(Player* player)
|
||||
HashMapHolder<Player>::Remove(player);
|
||||
PlayerNameMapHolder::Remove(player);
|
||||
}
|
||||
|
||||
void ObjectAccessor::UpdatePlayerNameMapReference(std::string oldname, Player* player)
|
||||
{
|
||||
PlayerNameMapHolder::RemoveByName(oldname);
|
||||
PlayerNameMapHolder::Insert(player);
|
||||
}
|
||||
|
||||
@@ -106,6 +106,8 @@ namespace ObjectAccessor
|
||||
|
||||
template<>
|
||||
void RemoveObject(Player* player);
|
||||
|
||||
void UpdatePlayerNameMapReference(std::string oldname, Player* player);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6320,7 +6320,7 @@ void ObjectMgr::LoadTavernAreaTriggers()
|
||||
|
||||
_tavernAreaTriggerStore.clear(); // need for reload case
|
||||
|
||||
QueryResult result = WorldDatabase.Query("SELECT id FROM areatrigger_tavern");
|
||||
QueryResult result = WorldDatabase.Query("SELECT id, faction FROM areatrigger_tavern");
|
||||
|
||||
if (!result)
|
||||
{
|
||||
@@ -6346,7 +6346,9 @@ void ObjectMgr::LoadTavernAreaTriggers()
|
||||
continue;
|
||||
}
|
||||
|
||||
_tavernAreaTriggerStore.insert(Trigger_ID);
|
||||
uint32 faction = fields[1].Get<uint32>();
|
||||
|
||||
_tavernAreaTriggerStore.emplace(Trigger_ID, faction);
|
||||
} while (result->NextRow());
|
||||
|
||||
LOG_INFO("server.loading", ">> Loaded {} Tavern Triggers in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
|
||||
|
||||
@@ -840,9 +840,15 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsTavernAreaTrigger(uint32 Trigger_ID) const
|
||||
[[nodiscard]] bool IsTavernAreaTrigger(uint32 triggerID, uint32 faction) const
|
||||
{
|
||||
return _tavernAreaTriggerStore.find(Trigger_ID) != _tavernAreaTriggerStore.end();
|
||||
auto itr = _tavernAreaTriggerStore.find(triggerID);
|
||||
if (itr != _tavernAreaTriggerStore.end())
|
||||
{
|
||||
return (itr->second & faction) != 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] GossipText const* GetGossipText(uint32 Text_ID) const;
|
||||
@@ -1449,7 +1455,7 @@ private:
|
||||
|
||||
typedef std::unordered_map<uint32, GossipText> GossipTextContainer;
|
||||
typedef std::unordered_map<uint32, uint32> QuestAreaTriggerContainer;
|
||||
typedef std::set<uint32> TavernAreaTriggerContainer;
|
||||
typedef std::unordered_map<uint32, uint32> TavernAreaTriggerContainer;
|
||||
|
||||
QuestAreaTriggerContainer _questAreaTriggerStore;
|
||||
TavernAreaTriggerContainer _tavernAreaTriggerStore;
|
||||
|
||||
@@ -1827,8 +1827,14 @@ void Guild::SendBankTabData(WorldSession* session, uint8 tabId, bool sendAllSlot
|
||||
_SendBankContent(session, tabId, sendAllSlots);
|
||||
}
|
||||
|
||||
void Guild::SendBankTabsInfo(WorldSession* session, bool sendAllSlots /*= false*/) const
|
||||
void Guild::SendBankTabsInfo(WorldSession* session, bool sendAllSlots /*= false*/)
|
||||
{
|
||||
Member* member = GetMember(session->GetPlayer()->GetGUID());
|
||||
if (!member)
|
||||
return;
|
||||
|
||||
member->SubscribeToGuildBankUpdatePackets();
|
||||
|
||||
_SendBankList(session, 0, sendAllSlots);
|
||||
}
|
||||
|
||||
@@ -1838,12 +1844,17 @@ void Guild::SendBankTabText(WorldSession* session, uint8 tabId) const
|
||||
tab->SendText(this, session);
|
||||
}
|
||||
|
||||
void Guild::SendPermissions(WorldSession* session) const
|
||||
void Guild::SendPermissions(WorldSession* session)
|
||||
{
|
||||
Member const* member = GetMember(session->GetPlayer()->GetGUID());
|
||||
Member* member = GetMember(session->GetPlayer()->GetGUID());
|
||||
if (!member)
|
||||
return;
|
||||
|
||||
// We are unsubscribing here since it is the only reliable way to handle /reload from player as
|
||||
// GuildPermissionsQuery is sent on each reload, and we don't want to send partial changes while client
|
||||
// doesn't know the full state
|
||||
member->UnsubscribeFromGuildBankUpdatePackets();
|
||||
|
||||
uint8 rankId = member->GetRankId();
|
||||
|
||||
WorldPackets::Guild::GuildPermissionsQueryResults queryResult;
|
||||
@@ -1886,8 +1897,6 @@ void Guild::SendLoginInfo(WorldSession* session)
|
||||
|
||||
LOG_DEBUG("guild", "SMSG_GUILD_EVENT [{}] MOTD", session->GetPlayerInfo());
|
||||
|
||||
SendBankTabsInfo(session);
|
||||
|
||||
Player* player = session->GetPlayer();
|
||||
|
||||
HandleRoster(session);
|
||||
@@ -2920,13 +2929,17 @@ void Guild::_SendBankList(WorldSession* session /* = nullptr*/, uint8 tabId /*=
|
||||
LOG_DEBUG("guild", "SMSG_GUILD_BANK_LIST [{}]: TabId: {}, FullSlots: {}, slots: {}",
|
||||
session->GetPlayerInfo(), tabId, sendAllSlots, packet.WithdrawalsRemaining);
|
||||
}
|
||||
else /// @todo - Probably this is just sent to session + those that have sent CMSG_GUILD_BANKER_ACTIVATE
|
||||
else
|
||||
{
|
||||
packet.Write();
|
||||
for (auto const& [guid, member] : m_members)
|
||||
{
|
||||
if (!member.ShouldReceiveBankPartialUpdatePackets())
|
||||
continue;
|
||||
|
||||
if (!MemberHasTabRights(member.GetGUID(), tabId, GUILD_BANK_RIGHT_VIEW_TAB))
|
||||
continue;
|
||||
|
||||
Player* player = member.FindPlayer();
|
||||
if (!player)
|
||||
continue;
|
||||
|
||||
@@ -307,7 +307,8 @@ public: // pussywizard: public class Member
|
||||
m_class(0),
|
||||
m_flags(GUILDMEMBER_STATUS_NONE),
|
||||
m_accountId(0),
|
||||
m_rankId(rankId)
|
||||
m_rankId(rankId),
|
||||
receiveGuildBankUpdatePackets(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -354,6 +355,10 @@ public: // pussywizard: public class Member
|
||||
|
||||
inline Player* FindPlayer() const { return ObjectAccessor::FindConnectedPlayer(m_guid); }
|
||||
|
||||
void SubscribeToGuildBankUpdatePackets() { receiveGuildBankUpdatePackets = true; }
|
||||
void UnsubscribeFromGuildBankUpdatePackets() { receiveGuildBankUpdatePackets = false; }
|
||||
[[nodiscard]] bool ShouldReceiveBankPartialUpdatePackets() const { return receiveGuildBankUpdatePackets; }
|
||||
|
||||
private:
|
||||
uint32 m_guildId;
|
||||
// Fields from characters table
|
||||
@@ -372,6 +377,8 @@ public: // pussywizard: public class Member
|
||||
std::string m_officerNote;
|
||||
|
||||
std::array<int32, GUILD_BANK_MAX_TABS + 1> m_bankWithdraw = {};
|
||||
|
||||
bool receiveGuildBankUpdatePackets;
|
||||
};
|
||||
|
||||
// pussywizard: public GetMember
|
||||
@@ -721,10 +728,10 @@ public:
|
||||
void SendInfo(WorldSession* session) const;
|
||||
void SendEventLog(WorldSession* session) const;
|
||||
void SendBankLog(WorldSession* session, uint8 tabId) const;
|
||||
void SendBankTabsInfo(WorldSession* session, bool showTabs = false) const;
|
||||
void SendBankTabsInfo(WorldSession* session, bool showTabs = false);
|
||||
void SendBankTabData(WorldSession* session, uint8 tabId, bool sendAllSlots) const;
|
||||
void SendBankTabText(WorldSession* session, uint8 tabId) const;
|
||||
void SendPermissions(WorldSession* session) const;
|
||||
void SendPermissions(WorldSession* session);
|
||||
void SendMoneyInfo(WorldSession* session) const;
|
||||
void SendLoginInfo(WorldSession* session);
|
||||
|
||||
|
||||
@@ -888,7 +888,7 @@ void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder const& holder)
|
||||
if (at)
|
||||
pCurrChar->TeleportTo(at->target_mapId, at->target_X, at->target_Y, at->target_Z, pCurrChar->GetOrientation());
|
||||
else
|
||||
pCurrChar->TeleportTo(pCurrChar->m_homebindMapId, pCurrChar->m_homebindX, pCurrChar->m_homebindY, pCurrChar->m_homebindZ, pCurrChar->GetOrientation());
|
||||
pCurrChar->TeleportTo(pCurrChar->m_homebindMapId, pCurrChar->m_homebindX, pCurrChar->m_homebindY, pCurrChar->m_homebindZ, pCurrChar->m_homebindO);
|
||||
}
|
||||
|
||||
pCurrChar->SendInitialPacketsAfterAddToMap();
|
||||
@@ -2338,6 +2338,7 @@ void WorldSession::HandleCharFactionOrRaceChangeCallback(std::shared_ptr<Charact
|
||||
stmt->SetData(3, loc.GetPositionX());
|
||||
stmt->SetData(4, loc.GetPositionY());
|
||||
stmt->SetData(5, loc.GetPositionZ());
|
||||
stmt->SetData(6, loc.GetOrientation());
|
||||
trans->Append(stmt);
|
||||
|
||||
Player::SavePositionInDB(loc, zoneId, factionChangeInfo->Guid, trans);
|
||||
|
||||
@@ -290,7 +290,7 @@ void WorldSession::HandleGuildBankerActivate(WorldPackets::Guild::GuildBankActiv
|
||||
guild->SendBankTabsInfo(this, packet.FullUpdate);
|
||||
}
|
||||
|
||||
// Called when opening guild bank tab only (first one)
|
||||
// Called when opening guild bank tab only
|
||||
void WorldSession::HandleGuildBankQueryTab(WorldPackets::Guild::GuildBankQueryTab& packet)
|
||||
{
|
||||
LOG_DEBUG("guild", "CMSG_GUILD_BANK_QUERY_TAB [{}]: Go: [{}], TabId: {}, ShowTabs: {}"
|
||||
|
||||
@@ -747,7 +747,8 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPacket& recv_data)
|
||||
return;
|
||||
}
|
||||
|
||||
bool isTavernAreatrigger = sObjectMgr->IsTavernAreaTrigger(triggerId);
|
||||
uint32 teamFaction = player->GetTeamId(true) == TEAM_ALLIANCE ? FACTION_MASK_ALLIANCE : FACTION_MASK_HORDE;
|
||||
bool isTavernAreatrigger = sObjectMgr->IsTavernAreaTrigger(triggerId, teamFaction);
|
||||
if (!player->IsInAreaTriggerRadius(atEntry, isTavernAreatrigger ? 5.f : 0.f))
|
||||
{
|
||||
LOG_DEBUG("network", "HandleAreaTriggerOpcode: Player {} ({}) too far (trigger map: {} player map: {}), ignore Area Trigger ID: {}",
|
||||
@@ -1702,7 +1703,7 @@ void WorldSession::HandleHearthAndResurrect(WorldPacket& /*recv_data*/)
|
||||
_player->BuildPlayerRepop();
|
||||
_player->ResurrectPlayer(1.0f);
|
||||
_player->SpawnCorpseBones();
|
||||
_player->TeleportTo(_player->m_homebindMapId, _player->m_homebindX, _player->m_homebindY, _player->m_homebindZ, _player->GetOrientation());
|
||||
_player->TeleportTo(_player->m_homebindMapId, _player->m_homebindX, _player->m_homebindY, _player->m_homebindZ, _player->m_homebindO);
|
||||
}
|
||||
|
||||
void WorldSession::HandleInstanceLockResponse(WorldPacket& recvPacket)
|
||||
|
||||
@@ -92,7 +92,7 @@ void WorldSession::HandleMoveWorldportAck()
|
||||
if (!newMap || newMap->CannotEnter(GetPlayer(), false))
|
||||
{
|
||||
LOG_ERROR("network.opcode", "Map {} could not be created for player {}, porting player to homebind", loc.GetMapId(), GetPlayer()->GetGUID().ToString());
|
||||
GetPlayer()->TeleportTo(GetPlayer()->m_homebindMapId, GetPlayer()->m_homebindX, GetPlayer()->m_homebindY, GetPlayer()->m_homebindZ, GetPlayer()->GetOrientation());
|
||||
GetPlayer()->TeleportTo(GetPlayer()->m_homebindMapId, GetPlayer()->m_homebindX, GetPlayer()->m_homebindY, GetPlayer()->m_homebindZ, GetPlayer()->m_homebindO);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ void WorldSession::HandleMoveWorldportAck()
|
||||
GetPlayer()->GetName(), GetPlayer()->GetGUID().ToString(), loc.GetMapId());
|
||||
GetPlayer()->ResetMap();
|
||||
GetPlayer()->SetMap(oldMap);
|
||||
GetPlayer()->TeleportTo(GetPlayer()->m_homebindMapId, GetPlayer()->m_homebindX, GetPlayer()->m_homebindY, GetPlayer()->m_homebindZ, GetPlayer()->GetOrientation());
|
||||
GetPlayer()->TeleportTo(GetPlayer()->m_homebindMapId, GetPlayer()->m_homebindX, GetPlayer()->m_homebindY, GetPlayer()->m_homebindZ, GetPlayer()->m_homebindO);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -197,6 +197,7 @@ bool LootItemStorage::LoadStoredLoot(Item* item, Player* player)
|
||||
li.randomPropertyId = it2->randomPropertyId;
|
||||
li.randomSuffix = it2->randomSuffix;
|
||||
li.rollWinnerGUID = ObjectGuid::Empty;
|
||||
li.groupid = 0;
|
||||
|
||||
// Copy the extra loot conditions from the item in the loot template
|
||||
lt->CopyConditions(&li, it2->conditionLootId);
|
||||
|
||||
@@ -71,7 +71,7 @@ struct LootGroupInvalidSelector : public Acore::unary_function<LootStoreItem*, b
|
||||
|
||||
uint8 foundDuplicates = 0;
|
||||
for (std::vector<LootItem>::const_iterator itr = _loot.items.begin(); itr != _loot.items.end(); ++itr)
|
||||
if (itr->itemid == item->itemid)
|
||||
if (itr->itemid == item->itemid && itr->groupid == item->groupid)
|
||||
{
|
||||
++foundDuplicates;
|
||||
if (_proto->InventoryType == 0 && foundDuplicates == 3 && _proto->ItemId != 47242 /*Trophy of the Crusade*/) // Non-equippable items are limited to 3 drops
|
||||
@@ -401,6 +401,7 @@ LootItem::LootItem(LootStoreItem const& li)
|
||||
is_underthreshold = 0;
|
||||
is_counted = 0;
|
||||
rollWinnerGUID = ObjectGuid::Empty;
|
||||
groupid = li.groupid;
|
||||
}
|
||||
|
||||
// Basic checks for player/item compatibility - if false no chance to see the item in the loot
|
||||
|
||||
@@ -168,6 +168,7 @@ struct LootItem
|
||||
bool is_counted : 1;
|
||||
bool needs_quest : 1; // quest drop
|
||||
bool follow_loot_rules : 1;
|
||||
uint8 groupid : 7;
|
||||
|
||||
// Constructor, copies most fields from LootStoreItem, generates random count and random suffixes/properties
|
||||
// Should be called for non-reference LootStoreItem entries only (reference = 0)
|
||||
|
||||
@@ -1269,7 +1269,7 @@ void Map::RemoveAllPlayers()
|
||||
{
|
||||
// this is happening for bg
|
||||
LOG_ERROR("maps", "Map::UnloadAll: player {} is still in map {} during unload, this should not happen!", player->GetName(), GetId());
|
||||
player->TeleportTo(player->m_homebindMapId, player->m_homebindX, player->m_homebindY, player->m_homebindZ, player->GetOrientation());
|
||||
player->TeleportTo(player->m_homebindMapId, player->m_homebindX, player->m_homebindY, player->m_homebindZ, player->m_homebindO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "MapUpdater.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "LFGMgr.h"
|
||||
#include "Map.h"
|
||||
#include "MapUpdater.h"
|
||||
#include "Metric.h"
|
||||
|
||||
class UpdateRequest
|
||||
@@ -140,6 +141,10 @@ void MapUpdater::update_finished()
|
||||
|
||||
void MapUpdater::WorkerThread()
|
||||
{
|
||||
LoginDatabase.WarnAboutSyncQueries(true);
|
||||
CharacterDatabase.WarnAboutSyncQueries(true);
|
||||
WorldDatabase.WarnAboutSyncQueries(true);
|
||||
|
||||
while (1)
|
||||
{
|
||||
UpdateRequest* request = nullptr;
|
||||
|
||||
@@ -1146,6 +1146,8 @@ enum AcoreStrings
|
||||
|
||||
LANG_CMD_GOQUEST_INVALID_SYNTAX = 5082,
|
||||
|
||||
LANG_CMD_CHAR_CHANGE_ACC_SUCCESS = 5083,
|
||||
|
||||
// Room for more strings 5083-9999
|
||||
|
||||
// Level requirement notifications
|
||||
|
||||
@@ -93,7 +93,7 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
|
||||
{
|
||||
if (Creature* cOwner2 = owner->ToCreature())
|
||||
{
|
||||
cOwner2->SetCannotReachTarget(i_path && i_path->GetPathType() & PATHFIND_INCOMPLETE ? target->GetGUID() : ObjectGuid::Empty);
|
||||
cOwner2->SetCannotReachTarget();
|
||||
}
|
||||
|
||||
i_recalculateTravel = false;
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace Movement
|
||||
|
||||
void MoveSpline::computeFallElevation(float& el) const
|
||||
{
|
||||
float z_now = spline.getPoint(spline.first(), false).z - Movement::computeFallElevation(MSToSec(time_passed), false);
|
||||
float z_now = spline.getPoint(spline.first()).z - Movement::computeFallElevation(MSToSec(time_passed), false);
|
||||
float final_z = FinalDestination().z;
|
||||
el = std::max(z_now, final_z);
|
||||
}
|
||||
@@ -96,7 +96,7 @@ namespace Movement
|
||||
float start_elevation;
|
||||
inline int32 operator()(Spline<int32>& s, int32 i)
|
||||
{
|
||||
return Movement::computeFallTime(start_elevation - s.getPoint(i + 1, false).z, false) * 1000.f;
|
||||
return Movement::computeFallTime(start_elevation - s.getPoint(i + 1).z, false) * 1000.f;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -138,7 +138,7 @@ namespace Movement
|
||||
// init spline timestamps
|
||||
if (splineflags.falling)
|
||||
{
|
||||
FallInitializer init(spline.getPoint(spline.first(), false).z);
|
||||
FallInitializer init(spline.getPoint(spline.first()).z);
|
||||
spline.initLengths(init);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace Movement
|
||||
void init_spline(const MoveSplineInitArgs& args);
|
||||
|
||||
protected:
|
||||
[[nodiscard]] const MySpline::ControlArray& getPath(bool visual) const { return spline.getPoints(visual); }
|
||||
[[nodiscard]] const MySpline::ControlArray& getPath() const { return spline.getPoints(); }
|
||||
void computeParabolicElevation(float& el) const;
|
||||
void computeFallElevation(float& el) const;
|
||||
|
||||
@@ -117,8 +117,8 @@ namespace Movement
|
||||
[[nodiscard]] bool isCyclic() const { return splineflags.cyclic; }
|
||||
[[nodiscard]] bool isFalling() const { return splineflags.falling; }
|
||||
[[nodiscard]] bool isWalking() const { return splineflags.walkmode; }
|
||||
[[nodiscard]] Vector3 FinalDestination() const { return Initialized() ? spline.getPoint(spline.last(), false) : Vector3(); }
|
||||
[[nodiscard]] Vector3 CurrentDestination() const { return Initialized() ? spline.getPoint(point_Idx + 1, false) : Vector3(); }
|
||||
[[nodiscard]] Vector3 FinalDestination() const { return Initialized() ? spline.getPoint(spline.last()) : Vector3(); }
|
||||
[[nodiscard]] Vector3 CurrentDestination() const { return Initialized() ? spline.getPoint(point_Idx + 1) : Vector3(); }
|
||||
[[nodiscard]] int32 currentPathIdx() const;
|
||||
|
||||
[[nodiscard]] bool HasAnimation() const { return splineflags.animation; }
|
||||
|
||||
@@ -135,11 +135,6 @@ namespace Movement
|
||||
data << int8(unit->GetTransSeat());
|
||||
}
|
||||
|
||||
Movement::SplineBase::ControlArray* visualPoints = const_cast<Movement::SplineBase::ControlArray*>(move_spline._Spline().allocateVisualPoints());
|
||||
visualPoints->resize(move_spline._Spline().getPointCount());
|
||||
// Xinef: Apply hover in creature movement packet
|
||||
std::copy(move_spline._Spline().getPoints(false).begin(), move_spline._Spline().getPoints(false).end(), visualPoints->begin());
|
||||
|
||||
PacketBuilder::WriteMonsterMove(move_spline, data);
|
||||
unit->SendMessageToSet(&data, true);
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace Movement
|
||||
MoveSplineFlag splineflags = move_spline.splineflags;
|
||||
|
||||
data << uint8(0); // sets/unsets MOVEMENTFLAG2_UNK7 (0x40)
|
||||
data << move_spline.spline.getPoint(move_spline.spline.first(), true);
|
||||
data << move_spline.spline.getPoint(move_spline.spline.first());
|
||||
data << move_spline.GetId();
|
||||
|
||||
switch (splineflags & MoveSplineFlag::Mask_Final_Facing)
|
||||
@@ -99,7 +99,7 @@ namespace Movement
|
||||
void WriteLinearPath(const Spline<int32>& spline, ByteBuffer& data)
|
||||
{
|
||||
uint32 last_idx = spline.getPointCount() - 3;
|
||||
const Vector3* real_path = &spline.getPoint(1, true);
|
||||
const Vector3* real_path = &spline.getPoint(1);
|
||||
|
||||
data << last_idx;
|
||||
data << real_path[last_idx]; // destination
|
||||
@@ -120,7 +120,7 @@ namespace Movement
|
||||
{
|
||||
uint32 count = spline.getPointCount() - 3;
|
||||
data << count;
|
||||
data.append<Vector3>(&spline.getPoint(2, true), count);
|
||||
data.append<Vector3>(&spline.getPoint(2), count);
|
||||
}
|
||||
|
||||
void WriteCatmullRomCyclicPath(const Spline<int32>& spline, ByteBuffer& data, bool flying)
|
||||
@@ -129,12 +129,12 @@ namespace Movement
|
||||
data << uint32(count + 1);
|
||||
if (flying)
|
||||
{
|
||||
data << spline.getPoint(1, true); // fake point, client will erase it from the spline after first cycle done
|
||||
data.append<Vector3>(&spline.getPoint(2, true), count);
|
||||
data << spline.getPoint(1); // fake point, client will erase it from the spline after first cycle done
|
||||
data.append<Vector3>(&spline.getPoint(2), count);
|
||||
}
|
||||
else
|
||||
{
|
||||
data.append<Vector3>(&spline.getPoint(2, true), count);
|
||||
data.append<Vector3>(&spline.getPoint(2), count);
|
||||
data << Vector3::zero(); //Xinef: fake point
|
||||
}
|
||||
}
|
||||
@@ -189,9 +189,9 @@ namespace Movement
|
||||
data << move_spline.vertical_acceleration; // added in 3.1
|
||||
data << move_spline.effect_start_time; // added in 3.1
|
||||
|
||||
uint32 nodes = move_spline.getPath(true).size();
|
||||
uint32 nodes = move_spline.getPath().size();
|
||||
data << nodes;
|
||||
data.append<Vector3>(&move_spline.getPath(true)[0], nodes);
|
||||
data.append<Vector3>(&move_spline.getPath()[0], nodes);
|
||||
data << uint8(move_spline.spline.mode()); // added in 3.1
|
||||
data << (move_spline.isCyclic() ? Vector3::zero() : move_spline.FinalDestination());
|
||||
}
|
||||
|
||||
@@ -286,7 +286,6 @@ namespace Movement
|
||||
index_lo = 0;
|
||||
index_hi = 0;
|
||||
points.clear();
|
||||
pointsVisual.clear();
|
||||
}
|
||||
|
||||
std::string SplineBase::ToString() const
|
||||
|
||||
@@ -42,7 +42,6 @@ namespace Movement
|
||||
|
||||
protected:
|
||||
ControlArray points;
|
||||
ControlArray pointsVisual;
|
||||
|
||||
index_type index_lo{0};
|
||||
index_type index_hi{0};
|
||||
@@ -112,10 +111,9 @@ namespace Movement
|
||||
[[nodiscard]] bool isCyclic() const { return cyclic;}
|
||||
|
||||
// Xinef: DO NOT USE EXCEPT FOR SPLINE INITIALIZATION!!!!!!
|
||||
[[nodiscard]] const ControlArray* allocateVisualPoints() const { return &pointsVisual; }
|
||||
[[nodiscard]] const ControlArray& getPoints(bool visual) const { return visual ? pointsVisual : points;}
|
||||
[[nodiscard]] const ControlArray& getPoints() const { return points;}
|
||||
[[nodiscard]] index_type getPointCount() const { return points.size();}
|
||||
[[nodiscard]] const Vector3& getPoint(index_type i, bool visual) const { return visual ? pointsVisual[i] : points[i];}
|
||||
[[nodiscard]] const Vector3& getPoint(index_type i) const { return points[i];}
|
||||
|
||||
/** Initializes spline. Don't call other methods while spline not initialized. */
|
||||
void init_spline(const Vector3* controls, index_type count, EvaluationMode m);
|
||||
|
||||
@@ -131,6 +131,16 @@ Quest::Quest(Field* questRecord)
|
||||
}
|
||||
|
||||
_eventIdForQuest = 0;
|
||||
|
||||
if (sWorld->getBoolConfig(CONFIG_QUEST_IGNORE_AUTO_ACCEPT))
|
||||
{
|
||||
Flags &= ~QUEST_FLAGS_AUTO_ACCEPT;
|
||||
}
|
||||
|
||||
if (sWorld->getBoolConfig(CONFIG_QUEST_IGNORE_AUTO_COMPLETE))
|
||||
{
|
||||
Flags &= ~QUEST_FLAGS_AUTOCOMPLETE;
|
||||
}
|
||||
}
|
||||
|
||||
void Quest::LoadQuestDetails(Field* fields)
|
||||
@@ -180,8 +190,10 @@ void Quest::LoadQuestTemplateAddon(Field* fields)
|
||||
RewardMailSenderEntry = fields[16].Get<uint32>();
|
||||
SpecialFlags = fields[17].Get<uint8>();
|
||||
|
||||
if (SpecialFlags & QUEST_SPECIAL_FLAGS_AUTO_ACCEPT)
|
||||
if ((SpecialFlags & QUEST_SPECIAL_FLAGS_AUTO_ACCEPT) && !sWorld->getBoolConfig(CONFIG_QUEST_IGNORE_AUTO_ACCEPT))
|
||||
{
|
||||
Flags |= QUEST_FLAGS_AUTO_ACCEPT;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 Quest::XPValue(uint8 playerLevel) const
|
||||
@@ -253,12 +265,12 @@ uint32 Quest::GetRewMoneyMaxLevel() const
|
||||
|
||||
bool Quest::IsAutoAccept() const
|
||||
{
|
||||
return sWorld->getBoolConfig(CONFIG_QUEST_IGNORE_AUTO_ACCEPT) ? false : (Flags & QUEST_FLAGS_AUTO_ACCEPT);
|
||||
return HasFlag(QUEST_FLAGS_AUTO_ACCEPT);
|
||||
}
|
||||
|
||||
bool Quest::IsAutoComplete() const
|
||||
{
|
||||
return sWorld->getBoolConfig(CONFIG_QUEST_IGNORE_AUTO_COMPLETE) ? false : HasFlag(QUEST_FLAGS_AUTOCOMPLETE);
|
||||
return HasFlag(QUEST_FLAGS_AUTOCOMPLETE);
|
||||
}
|
||||
|
||||
bool Quest::IsRaidQuest(Difficulty difficulty) const
|
||||
|
||||
@@ -590,6 +590,14 @@ void ScriptMgr::OnLootItem(Player* player, Item* item, uint32 count, ObjectGuid
|
||||
});
|
||||
}
|
||||
|
||||
void ScriptMgr::OnStoreNewItem(Player* player, Item* item, uint32 count)
|
||||
{
|
||||
ExecuteScript<PlayerScript>([&](PlayerScript* script)
|
||||
{
|
||||
script->OnStoreNewItem(player, item, count);
|
||||
});
|
||||
}
|
||||
|
||||
void ScriptMgr::OnCreateItem(Player* player, Item* item, uint32 count)
|
||||
{
|
||||
ExecuteScript<PlayerScript>([&](PlayerScript* script)
|
||||
|
||||
@@ -69,19 +69,19 @@ void ScriptMgr::ModifyMeleeDamage(Unit* target, Unit* attacker, uint32& damage)
|
||||
});
|
||||
}
|
||||
|
||||
void ScriptMgr::ModifySpellDamageTaken(Unit* target, Unit* attacker, int32& damage)
|
||||
void ScriptMgr::ModifySpellDamageTaken(Unit* target, Unit* attacker, int32& damage, SpellInfo const* spellInfo)
|
||||
{
|
||||
ExecuteScript<UnitScript>([&](UnitScript* script)
|
||||
{
|
||||
script->ModifySpellDamageTaken(target, attacker, damage);
|
||||
script->ModifySpellDamageTaken(target, attacker, damage, spellInfo);
|
||||
});
|
||||
}
|
||||
|
||||
void ScriptMgr::ModifyHealRecieved(Unit* target, Unit* attacker, uint32& damage)
|
||||
void ScriptMgr::ModifyHealReceived(Unit* target, Unit* healer, uint32& heal, SpellInfo const* spellInfo)
|
||||
{
|
||||
ExecuteScript<UnitScript>([&](UnitScript* script)
|
||||
{
|
||||
script->ModifyHealRecieved(target, attacker, damage);
|
||||
script->ModifyHealReceived(target, healer, heal, spellInfo);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -242,3 +242,11 @@ void ScriptMgr::OnUnitDeath(Unit* unit, Unit* killer)
|
||||
script->OnUnitDeath(unit, killer);
|
||||
});
|
||||
}
|
||||
|
||||
void ScriptMgr::OnAuraApply(Unit* unit, Aura* aura)
|
||||
{
|
||||
ExecuteScript<UnitScript>([&](UnitScript* script)
|
||||
{
|
||||
script->OnAuraApply(unit, aura);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -429,16 +429,18 @@ public:
|
||||
virtual void ModifyMeleeDamage(Unit* /*target*/, Unit* /*attacker*/, uint32& /*damage*/) { }
|
||||
|
||||
// Called when Spell Damage is being Dealt
|
||||
virtual void ModifySpellDamageTaken(Unit* /*target*/, Unit* /*attacker*/, int32& /*damage*/) { }
|
||||
virtual void ModifySpellDamageTaken(Unit* /*target*/, Unit* /*attacker*/, int32& /*damage*/, SpellInfo const* /*spellInfo*/) { }
|
||||
|
||||
// Called when Heal is Recieved
|
||||
virtual void ModifyHealRecieved(Unit* /*target*/, Unit* /*attacker*/, uint32& /*damage*/) { }
|
||||
virtual void ModifyHealReceived(Unit* /*target*/, Unit* /*healer*/, uint32& /*heal*/, SpellInfo const* /*spellInfo*/) { }
|
||||
|
||||
//Called when Damage is Dealt
|
||||
virtual uint32 DealDamage(Unit* /*AttackerUnit*/, Unit* /*pVictim*/, uint32 damage, DamageEffectType /*damagetype*/) { return damage; }
|
||||
|
||||
virtual void OnBeforeRollMeleeOutcomeAgainst(Unit const* /*attacker*/, Unit const* /*victim*/, WeaponAttackType /*attType*/, int32& /*attackerMaxSkillValueForLevel*/, int32& /*victimMaxSkillValueForLevel*/, int32& /*attackerWeaponSkill*/, int32& /*victimDefenseSkill*/, int32& /*crit_chance*/, int32& /*miss_chance*/, int32& /*dodge_chance*/, int32& /*parry_chance*/, int32& /*block_chance*/ ) { };
|
||||
|
||||
virtual void OnAuraApply(Unit* /*unit*/, Aura* /*aura*/) { }
|
||||
|
||||
virtual void OnAuraRemove(Unit* /*unit*/, AuraApplication* /*aurApp*/, AuraRemoveMode /*mode*/) { }
|
||||
|
||||
[[nodiscard]] virtual bool IfNormalReaction(Unit const* /*unit*/, Unit const* /*target*/, ReputationRank& /*repRank*/) { return true; }
|
||||
@@ -1180,6 +1182,9 @@ public:
|
||||
//After looting item
|
||||
virtual void OnLootItem(Player* /*player*/, Item* /*item*/, uint32 /*count*/, ObjectGuid /*lootguid*/) { }
|
||||
|
||||
//After looting item (includes master loot).
|
||||
virtual void OnStoreNewItem(Player* /*player*/, Item* /*item*/, uint32 /*count*/) { }
|
||||
|
||||
//After creating item (eg profession item creation)
|
||||
virtual void OnCreateItem(Player* /*player*/, Item* /*item*/, uint32 /*count*/) { }
|
||||
|
||||
@@ -2314,6 +2319,7 @@ public: /* PlayerScript */
|
||||
void GetCustomArenaPersonalRating(Player const* player, uint8 slot, uint32& rating) const;
|
||||
void OnGetMaxPersonalArenaRatingRequirement(Player const* player, uint32 minSlot, uint32& maxArenaRating) const;
|
||||
void OnLootItem(Player* player, Item* item, uint32 count, ObjectGuid lootguid);
|
||||
void OnStoreNewItem(Player* player, Item* item, uint32 count);
|
||||
void OnCreateItem(Player* player, Item* item, uint32 count);
|
||||
void OnQuestRewardItem(Player* player, Item* item, uint32 count);
|
||||
void OnGroupRollRewardItem(Player* player, Item* item, uint32 count, RollVote voteType, Roll* roll);
|
||||
@@ -2471,10 +2477,11 @@ public: /* UnitScript */
|
||||
void OnDamage(Unit* attacker, Unit* victim, uint32& damage);
|
||||
void ModifyPeriodicDamageAurasTick(Unit* target, Unit* attacker, uint32& damage);
|
||||
void ModifyMeleeDamage(Unit* target, Unit* attacker, uint32& damage);
|
||||
void ModifySpellDamageTaken(Unit* target, Unit* attacker, int32& damage);
|
||||
void ModifyHealRecieved(Unit* target, Unit* attacker, uint32& addHealth);
|
||||
void ModifySpellDamageTaken(Unit* target, Unit* attacker, int32& damage, SpellInfo const* spellInfo);
|
||||
void ModifyHealReceived(Unit* target, Unit* healer, uint32& addHealth, SpellInfo const* spellInfo);
|
||||
uint32 DealDamage(Unit* AttackerUnit, Unit* pVictim, uint32 damage, DamageEffectType damagetype);
|
||||
void OnBeforeRollMeleeOutcomeAgainst(Unit const* attacker, Unit const* victim, WeaponAttackType attType, int32& attackerMaxSkillValueForLevel, int32& victimMaxSkillValueForLevel, int32& attackerWeaponSkill, int32& victimDefenseSkill, int32& crit_chance, int32& miss_chance, int32& dodge_chance, int32& parry_chance, int32& block_chance);
|
||||
void OnAuraApply(Unit* /*unit*/, Aura* /*aura*/);
|
||||
void OnAuraRemove(Unit* unit, AuraApplication* aurApp, AuraRemoveMode mode);
|
||||
bool IfNormalReaction(Unit const* unit, Unit const* target, ReputationRank& repRank);
|
||||
bool IsNeedModSpellDamagePercent(Unit const* unit, AuraEffect* auraEff, float& doneTotalMod, SpellInfo const* spellProto);
|
||||
|
||||
@@ -6619,6 +6619,7 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const
|
||||
|
||||
// Script Hook For HandlePeriodicDamageAurasTick -- Allow scripts to change the Damage pre class mitigation calculations
|
||||
sScriptMgr->ModifyPeriodicDamageAurasTick(target, caster, heal);
|
||||
sScriptMgr->ModifyHealReceived(target, caster, heal, GetSpellInfo());
|
||||
|
||||
HealInfo healInfo(caster, target, heal, GetSpellInfo(), GetSpellInfo()->GetSchoolMask());
|
||||
Unit::CalcHealAbsorb(healInfo);
|
||||
|
||||
@@ -976,7 +976,7 @@ uint8 Aura::CalcMaxCharges(Unit* caster) const
|
||||
{
|
||||
uint32 maxProcCharges = m_spellInfo->ProcCharges;
|
||||
if (SpellProcEntry const* procEntry = sSpellMgr->GetSpellProcEntry(GetId()))
|
||||
maxProcCharges = procEntry->charges;
|
||||
maxProcCharges = procEntry->Charges;
|
||||
|
||||
if (caster)
|
||||
if (Player* modOwner = caster->GetSpellModOwner())
|
||||
@@ -2202,9 +2202,9 @@ bool Aura::IsProcOnCooldown() const
|
||||
return false;
|
||||
}
|
||||
|
||||
void Aura::AddProcCooldown(uint32 /*msec*/)
|
||||
void Aura::AddProcCooldown(Milliseconds /*msec*/)
|
||||
{
|
||||
//m_procCooldown = GameTime::GetGameTime().count() + msec;
|
||||
//m_procCooldown = std:chrono::steady_clock::now() + msec;
|
||||
}
|
||||
|
||||
void Aura::PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo)
|
||||
@@ -2225,7 +2225,7 @@ void Aura::PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInf
|
||||
ASSERT(procEntry);
|
||||
|
||||
// cooldowns should be added to the whole aura (see 51698 area aura)
|
||||
AddProcCooldown(procEntry->cooldown);
|
||||
AddProcCooldown(procEntry->Cooldown);
|
||||
}
|
||||
|
||||
bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo) const
|
||||
@@ -2314,16 +2314,16 @@ bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventI
|
||||
|
||||
float Aura::CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const
|
||||
{
|
||||
float chance = procEntry.chance;
|
||||
float chance = procEntry.Chance;
|
||||
// calculate chances depending on unit with caster's data
|
||||
// so talents modifying chances and judgements will have properly calculated proc chance
|
||||
if (Unit* caster = GetCaster())
|
||||
{
|
||||
// calculate ppm chance if present and we're using weapon
|
||||
if (eventInfo.GetDamageInfo() && procEntry.ratePerMinute != 0)
|
||||
if (eventInfo.GetDamageInfo() && procEntry.ProcsPerMinute != 0)
|
||||
{
|
||||
uint32 WeaponSpeed = caster->GetAttackTime(eventInfo.GetDamageInfo()->GetAttackType());
|
||||
chance = caster->GetPPMProcChance(WeaponSpeed, procEntry.ratePerMinute, GetSpellInfo());
|
||||
chance = caster->GetPPMProcChance(WeaponSpeed, procEntry.ProcsPerMinute, GetSpellInfo());
|
||||
}
|
||||
// apply chance modifer aura, applies also to ppm chance (see improved judgement of light spell)
|
||||
if (Player* modOwner = caster->GetSpellModOwner())
|
||||
@@ -2923,15 +2923,20 @@ void DynObjAura::FillTargetMap(std::map<Unit*, uint8>& targets, Unit* /*caster*/
|
||||
|
||||
for (UnitList::iterator itr = targetList.begin(); itr != targetList.end(); ++itr)
|
||||
{
|
||||
// xinef: check z level and los dependence
|
||||
Unit* target = *itr;
|
||||
float zLevel = GetDynobjOwner()->GetPositionZ();
|
||||
if (target->GetPositionZ() + 3.0f < zLevel || target->GetPositionZ() - 5.0f > zLevel)
|
||||
|
||||
Optional<float> collisionHeight = { };
|
||||
Optional<float> combatReach = { };
|
||||
if (Unit* dynObjCaster = GetDynobjOwner()->GetCaster())
|
||||
{
|
||||
if (!spellInfo->HasAttribute(SPELL_ATTR2_IGNORE_LINE_OF_SIGHT) && !spellInfo->HasAttribute(SPELL_ATTR5_ALWAYS_AOE_LINE_OF_SIGHT) && !target->IsWithinLOSInMap(GetDynobjOwner()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
collisionHeight = dynObjCaster->GetCollisionHeight();
|
||||
combatReach = dynObjCaster->GetCombatReach();
|
||||
}
|
||||
|
||||
if (!spellInfo->HasAttribute(SPELL_ATTR2_IGNORE_LINE_OF_SIGHT) && !spellInfo->HasAttribute(SPELL_ATTR5_ALWAYS_AOE_LINE_OF_SIGHT) &&
|
||||
!GetDynobjOwner()->IsWithinLOSInMap(target, VMAP::ModelIgnoreFlags::Nothing, LINEOFSIGHT_ALL_CHECKS, collisionHeight, combatReach))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::map<Unit*, uint8>::iterator existing = targets.find(*itr);
|
||||
|
||||
@@ -196,7 +196,7 @@ public:
|
||||
// and some dependant problems fixed before it can replace old proc system (for example cooldown handling)
|
||||
// currently proc system functionality is implemented in Unit::ProcDamageAndSpell
|
||||
bool IsProcOnCooldown() const;
|
||||
void AddProcCooldown(uint32 msec);
|
||||
void AddProcCooldown(Milliseconds msec);
|
||||
bool IsUsingCharges() const { return m_isUsingCharges; }
|
||||
void SetUsingCharges(bool val) { m_isUsingCharges = val; }
|
||||
void PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo);
|
||||
|
||||
@@ -1344,7 +1344,7 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici
|
||||
break;
|
||||
case TARGET_DEST_HOME:
|
||||
if (Player* playerCaster = m_caster->ToPlayer())
|
||||
dest = SpellDestination(playerCaster->m_homebindX, playerCaster->m_homebindY, playerCaster->m_homebindZ, playerCaster->GetOrientation(), playerCaster->m_homebindMapId);
|
||||
dest = SpellDestination(playerCaster->m_homebindX, playerCaster->m_homebindY, playerCaster->m_homebindZ, playerCaster->m_homebindO, playerCaster->m_homebindMapId);
|
||||
break;
|
||||
case TARGET_DEST_DB:
|
||||
if (SpellTargetPosition const* st = sSpellMgr->GetSpellTargetPosition(m_spellInfo->Id, effIndex))
|
||||
@@ -2428,9 +2428,11 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*=
|
||||
// Calculate hit result
|
||||
if (m_originalCaster)
|
||||
{
|
||||
targetInfo.missCondition = m_originalCaster->SpellHitResult(target, m_spellInfo, m_canReflect);
|
||||
targetInfo.missCondition = m_originalCaster->SpellHitResult(target, this, m_canReflect);
|
||||
if (m_skipCheck && targetInfo.missCondition != SPELL_MISS_IMMUNE)
|
||||
{
|
||||
targetInfo.missCondition = SPELL_MISS_NONE;
|
||||
}
|
||||
}
|
||||
else
|
||||
targetInfo.missCondition = SPELL_MISS_EVADE; //SPELL_MISS_NONE;
|
||||
@@ -2458,7 +2460,7 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*=
|
||||
if (targetInfo.missCondition == SPELL_MISS_REFLECT)
|
||||
{
|
||||
// Calculate reflected spell result on caster
|
||||
targetInfo.reflectResult = m_caster->SpellHitResult(m_caster, m_spellInfo, m_canReflect);
|
||||
targetInfo.reflectResult = m_caster->SpellHitResult(m_caster, this, m_canReflect);
|
||||
|
||||
if (targetInfo.reflectResult == SPELL_MISS_REFLECT) // Impossible reflect again, so simply deflect spell
|
||||
targetInfo.reflectResult = SPELL_MISS_PARRY;
|
||||
@@ -2971,8 +2973,10 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA
|
||||
return SPELL_MISS_EVADE;
|
||||
|
||||
// For delayed spells immunity may be applied between missile launch and hit - check immunity for that case
|
||||
if (m_spellInfo->Speed && ((m_damage > 0 && unit->IsImmunedToDamage(m_spellInfo)) || unit->IsImmunedToSchool(m_spellInfo) || unit->IsImmunedToSpell(m_spellInfo)))
|
||||
if (m_spellInfo->Speed && ((m_damage > 0 && unit->IsImmunedToDamage(this)) || unit->IsImmunedToSchool(this) || unit->IsImmunedToSpell(m_spellInfo, this)))
|
||||
{
|
||||
return SPELL_MISS_IMMUNE;
|
||||
}
|
||||
|
||||
// disable effects to which unit is immune
|
||||
SpellMissInfo returnVal = SPELL_MISS_IMMUNE;
|
||||
@@ -3970,6 +3974,20 @@ void Spell::_cast(bool skipCheck)
|
||||
if (modOwner)
|
||||
modOwner->SetSpellModTakingSpell(this, true);
|
||||
|
||||
bool resetAttackTimers = IsAutoActionResetSpell() && !m_spellInfo->HasAttribute(SPELL_ATTR2_DO_NOT_RESET_COMBAT_TIMERS);
|
||||
if (resetAttackTimers)
|
||||
{
|
||||
Unit::AuraEffectList const& vIgnoreReset = m_caster->GetAuraEffectsByType(SPELL_AURA_IGNORE_MELEE_RESET);
|
||||
for (Unit::AuraEffectList::const_iterator i = vIgnoreReset.begin(); i != vIgnoreReset.end(); ++i)
|
||||
{
|
||||
if ((*i)->IsAffectedOnSpell(m_spellInfo))
|
||||
{
|
||||
resetAttackTimers = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Okay, everything is prepared. Now we need to distinguish between immediate and evented delayed spells
|
||||
if ((m_spellInfo->Speed > 0.0f && !m_spellInfo->IsChanneled())/* xinef: we dont need this || m_spellInfo->Id == 14157*/)
|
||||
{
|
||||
@@ -4002,38 +4020,23 @@ void Spell::_cast(bool skipCheck)
|
||||
handle_immediate();
|
||||
}
|
||||
|
||||
if (IsAutoActionResetSpell())
|
||||
if (resetAttackTimers)
|
||||
{
|
||||
bool found = false;
|
||||
Unit::AuraEffectList const& vIgnoreReset = m_caster->GetAuraEffectsByType(SPELL_AURA_IGNORE_MELEE_RESET);
|
||||
for (Unit::AuraEffectList::const_iterator i = vIgnoreReset.begin(); i != vIgnoreReset.end(); ++i)
|
||||
if (m_casttime == 0 && m_spellInfo->CalcCastTime())
|
||||
{
|
||||
if ((*i)->IsAffectedOnSpell(m_spellInfo))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
resetAttackTimers = false;
|
||||
}
|
||||
|
||||
if (!found && !m_spellInfo->HasAttribute(SPELL_ATTR2_DO_NOT_RESET_COMBAT_TIMERS))
|
||||
if (resetAttackTimers)
|
||||
{
|
||||
bool allow = true;
|
||||
if (m_casttime == 0 && m_spellInfo->CalcCastTime())
|
||||
m_caster->resetAttackTimer(BASE_ATTACK);
|
||||
|
||||
if (m_caster->haveOffhandWeapon())
|
||||
{
|
||||
allow = false;
|
||||
m_caster->resetAttackTimer(OFF_ATTACK);
|
||||
}
|
||||
|
||||
if (allow)
|
||||
{
|
||||
m_caster->resetAttackTimer(BASE_ATTACK);
|
||||
|
||||
if (m_caster->haveOffhandWeapon())
|
||||
{
|
||||
m_caster->resetAttackTimer(OFF_ATTACK);
|
||||
}
|
||||
|
||||
m_caster->resetAttackTimer(RANGED_ATTACK);
|
||||
}
|
||||
m_caster->resetAttackTimer(RANGED_ATTACK);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -589,6 +589,8 @@ public:
|
||||
|
||||
[[nodiscard]] TriggerCastFlags GetTriggeredCastFlags() const { return _triggeredCastFlags; }
|
||||
|
||||
[[nodiscard]] SpellSchoolMask GetSpellSchoolMask() const { return m_spellSchoolMask; }
|
||||
|
||||
protected:
|
||||
bool HasGlobalCooldown() const;
|
||||
void TriggerGlobalCooldown();
|
||||
|
||||
@@ -319,7 +319,7 @@ void Spell::EffectEnvironmentalDMG(SpellEffIndex /*effIndex*/)
|
||||
Unit::DealDamageMods(unitTarget, envDamage, &absorb);
|
||||
damage = envDamage;
|
||||
|
||||
m_caster->SendSpellNonMeleeDamageLog(unitTarget, m_spellInfo, damage, m_spellInfo->GetSchoolMask(), absorb, resist, false, 0, false);
|
||||
m_caster->SendSpellNonMeleeDamageLog(unitTarget, m_spellInfo, damage, m_spellInfo->GetSchoolMask(), absorb, resist, false, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -593,8 +593,14 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex)
|
||||
if (Player* caster = m_caster->ToPlayer())
|
||||
{
|
||||
// Add Ammo and Weapon damage plus RAP * 0.1
|
||||
float dmg_min = caster->GetWeaponDamageRange(RANGED_ATTACK, MINDAMAGE);
|
||||
float dmg_max = caster->GetWeaponDamageRange(RANGED_ATTACK, MAXDAMAGE);
|
||||
float dmg_min = 0.f;
|
||||
float dmg_max = 0.f;
|
||||
for (uint8 i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i)
|
||||
{
|
||||
dmg_min += caster->GetWeaponDamageRange(RANGED_ATTACK, MINDAMAGE, i);
|
||||
dmg_max += caster->GetWeaponDamageRange(RANGED_ATTACK, MAXDAMAGE, i);
|
||||
}
|
||||
|
||||
if (dmg_max == 0.0f && dmg_min > dmg_max)
|
||||
{
|
||||
damage += int32(dmg_min);
|
||||
@@ -616,9 +622,17 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex)
|
||||
// Add main hand dps * effect[2] amount
|
||||
if (Player* player = m_caster->ToPlayer())
|
||||
{
|
||||
float mindamage, maxdamage;
|
||||
player->CalculateMinMaxDamage(BASE_ATTACK, false, false, mindamage, maxdamage);
|
||||
float average = (mindamage + maxdamage) / 2;
|
||||
float minTotal = 0.f;
|
||||
float maxTotal = 0.f;
|
||||
for (uint8 i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i)
|
||||
{
|
||||
float tmpMin, tmpMax;
|
||||
player->CalculateMinMaxDamage(BASE_ATTACK, false, false, tmpMin, tmpMax, i);
|
||||
minTotal += tmpMin;
|
||||
maxTotal += tmpMax;
|
||||
}
|
||||
|
||||
float average = (minTotal + maxTotal) / 2;
|
||||
int32 count = m_caster->CalculateSpellDamage(unitTarget, m_spellInfo, EFFECT_2);
|
||||
damage += count * int32(average * IN_MILLISECONDS) / m_caster->GetAttackTime(BASE_ATTACK);
|
||||
}
|
||||
@@ -3644,8 +3658,8 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex)
|
||||
uint32 eff_damage(std::max(weaponDamage, 0));
|
||||
|
||||
// Add melee damage bonuses (also check for negative)
|
||||
eff_damage = m_caster->MeleeDamageBonusDone(unitTarget, eff_damage, m_attackType, m_spellInfo);
|
||||
eff_damage = unitTarget->MeleeDamageBonusTaken(m_caster, eff_damage, m_attackType, m_spellInfo);
|
||||
eff_damage = m_caster->MeleeDamageBonusDone(unitTarget, eff_damage, m_attackType, m_spellInfo, m_spellSchoolMask);
|
||||
eff_damage = unitTarget->MeleeDamageBonusTaken(m_caster, eff_damage, m_attackType, m_spellInfo, m_spellSchoolMask);
|
||||
|
||||
// Meteor like spells (divided damage to targets)
|
||||
if (m_spellInfo->HasAttribute(SPELL_ATTR0_CU_SHARE_DAMAGE))
|
||||
@@ -5516,6 +5530,11 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex)
|
||||
|
||||
ExecuteLogEffectSummonObject(effIndex, linkedTrap);
|
||||
}
|
||||
|
||||
if (Player* player = m_caster->ToPlayer())
|
||||
{
|
||||
player->SetCanTeleport(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Spell::EffectProspecting(SpellEffIndex /*effIndex*/)
|
||||
|
||||
@@ -4415,6 +4415,26 @@ void SpellMgr::LoadSpellInfoCorrections()
|
||||
spellInfo->AuraInterruptFlags &= ~AURA_INTERRUPT_FLAG_NOT_ABOVEWATER;
|
||||
});
|
||||
|
||||
// Summon Worm
|
||||
ApplySpellFix({ 518, 25831, 25832 }, [](SpellInfo* spellInfo)
|
||||
{
|
||||
spellInfo->Effects[EFFECT_0].MiscValueB = 64;
|
||||
});
|
||||
|
||||
// Uppercut
|
||||
ApplySpellFix({ 26007 }, [](SpellInfo* spellInfo)
|
||||
{
|
||||
spellInfo->AttributesEx3 |= SPELL_ATTR3_SUPRESS_CASTER_PROCS;
|
||||
});
|
||||
|
||||
// Digestive Acid (Temporary)
|
||||
ApplySpellFix({ 26476 }, [](SpellInfo* spellInfo)
|
||||
{
|
||||
spellInfo->Attributes |= SPELL_ATTR0_NO_IMMUNITIES;
|
||||
spellInfo->AttributesEx2 |= SPELL_ATTR2_IGNORE_LINE_OF_SIGHT;
|
||||
spellInfo->AttributesEx3 |= SPELL_ATTR3_ALWAYS_HIT;
|
||||
});
|
||||
|
||||
for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i)
|
||||
{
|
||||
SpellInfo* spellInfo = mSpellInfoMap[i];
|
||||
|
||||
@@ -898,11 +898,11 @@ SpellProcEntry const* SpellMgr::GetSpellProcEntry(uint32 spellId) const
|
||||
bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const
|
||||
{
|
||||
// proc type doesn't match
|
||||
if (!(eventInfo.GetTypeMask() & procEntry.typeMask))
|
||||
if (!(eventInfo.GetTypeMask() & procEntry.ProcFlags))
|
||||
return false;
|
||||
|
||||
// check XP or honor target requirement
|
||||
if (procEntry.attributesMask & PROC_ATTR_REQ_EXP_OR_HONOR)
|
||||
if (procEntry.AttributesMask & PROC_ATTR_REQ_EXP_OR_HONOR)
|
||||
if (Player* actor = eventInfo.GetActor()->ToPlayer())
|
||||
if (eventInfo.GetActionTarget() && !actor->isHonorOrXPTarget(eventInfo.GetActionTarget()))
|
||||
return false;
|
||||
@@ -912,37 +912,37 @@ bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcE
|
||||
return true;
|
||||
|
||||
// check school mask (if set) for other trigger types
|
||||
if (procEntry.schoolMask && !(eventInfo.GetSchoolMask() & procEntry.schoolMask))
|
||||
if (procEntry.SchoolMask && !(eventInfo.GetSchoolMask() & procEntry.SchoolMask))
|
||||
return false;
|
||||
|
||||
// check spell family name/flags (if set) for spells
|
||||
if (eventInfo.GetTypeMask() & (PERIODIC_PROC_FLAG_MASK | SPELL_PROC_FLAG_MASK | PROC_FLAG_DONE_TRAP_ACTIVATION))
|
||||
{
|
||||
if (procEntry.spellFamilyName && (procEntry.spellFamilyName != eventInfo.GetSpellInfo()->SpellFamilyName))
|
||||
if (procEntry.SpellFamilyName && (procEntry.SpellFamilyName != eventInfo.GetSpellInfo()->SpellFamilyName))
|
||||
return false;
|
||||
|
||||
if (procEntry.spellFamilyMask && !(procEntry.spellFamilyMask & eventInfo.GetSpellInfo()->SpellFamilyFlags))
|
||||
if (procEntry.SpellFamilyMask && !(procEntry.SpellFamilyMask & eventInfo.GetSpellInfo()->SpellFamilyFlags))
|
||||
return false;
|
||||
}
|
||||
|
||||
// check spell type mask (if set)
|
||||
if (eventInfo.GetTypeMask() & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK))
|
||||
{
|
||||
if (procEntry.spellTypeMask && !(eventInfo.GetSpellTypeMask() & procEntry.spellTypeMask))
|
||||
if (procEntry.SpellTypeMask && !(eventInfo.GetSpellTypeMask() & procEntry.SpellTypeMask))
|
||||
return false;
|
||||
}
|
||||
|
||||
// check spell phase mask
|
||||
if (eventInfo.GetTypeMask() & REQ_SPELL_PHASE_PROC_FLAG_MASK)
|
||||
{
|
||||
if (!(eventInfo.GetSpellPhaseMask() & procEntry.spellPhaseMask))
|
||||
if (!(eventInfo.GetSpellPhaseMask() & procEntry.SpellPhaseMask))
|
||||
return false;
|
||||
}
|
||||
|
||||
// check hit mask (on taken hit or on done hit, but not on spell cast phase)
|
||||
if ((eventInfo.GetTypeMask() & TAKEN_HIT_PROC_FLAG_MASK) || ((eventInfo.GetTypeMask() & DONE_HIT_PROC_FLAG_MASK) && !(eventInfo.GetSpellPhaseMask() & PROC_SPELL_PHASE_CAST)))
|
||||
{
|
||||
uint32 hitMask = procEntry.hitMask;
|
||||
uint32 hitMask = procEntry.HitMask;
|
||||
// get default values if hit mask not set
|
||||
if (!hitMask)
|
||||
{
|
||||
@@ -1143,7 +1143,7 @@ bool SpellArea::IsFitToRequirements(Player const* player, uint32 newZone, uint32
|
||||
if (!player)
|
||||
return false;
|
||||
|
||||
if (!sWorld->getBoolConfig(CONFIG_WINTERGRASP_ENABLE))
|
||||
if (sWorld->getIntConfig(CONFIG_WINTERGRASP_ENABLE) != 1)
|
||||
return false;
|
||||
|
||||
Battlefield* Bf = sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG);
|
||||
@@ -1833,8 +1833,8 @@ void SpellMgr::LoadSpellProcs()
|
||||
|
||||
mSpellProcMap.clear(); // need for reload case
|
||||
|
||||
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
||||
QueryResult result = WorldDatabase.Query("SELECT spellId, schoolMask, spellFamilyName, spellFamilyMask0, spellFamilyMask1, spellFamilyMask2, typeMask, spellTypeMask, spellPhaseMask, hitMask, attributesMask, ratePerMinute, chance, cooldown, charges FROM spell_proc");
|
||||
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
||||
QueryResult result = WorldDatabase.Query("SELECT SpellId, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, ProcFlags, SpellTypeMask, SpellPhaseMask, HitMask, AttributesMask, ProcsPerMinute, Chance, Cooldown, Charges FROM spell_proc");
|
||||
if (!result)
|
||||
{
|
||||
LOG_WARN("server.loading", ">> Loaded 0 Spell Proc Conditions And Data. DB table `spell_proc` Is Empty.");
|
||||
@@ -1874,21 +1874,20 @@ void SpellMgr::LoadSpellProcs()
|
||||
|
||||
SpellProcEntry baseProcEntry;
|
||||
|
||||
baseProcEntry.schoolMask = fields[1].Get<int8>();
|
||||
baseProcEntry.spellFamilyName = fields[2].Get<uint16>();
|
||||
baseProcEntry.spellFamilyMask[0] = fields[3].Get<uint32>();
|
||||
baseProcEntry.spellFamilyMask[1] = fields[4].Get<uint32>();
|
||||
baseProcEntry.spellFamilyMask[2] = fields[5].Get<uint32>();
|
||||
baseProcEntry.typeMask = fields[6].Get<uint32>();
|
||||
baseProcEntry.spellTypeMask = fields[7].Get<uint32>();
|
||||
baseProcEntry.spellPhaseMask = fields[8].Get<uint32>();
|
||||
baseProcEntry.hitMask = fields[9].Get<uint32>();
|
||||
baseProcEntry.attributesMask = fields[10].Get<uint32>();
|
||||
baseProcEntry.ratePerMinute = fields[11].Get<float>();
|
||||
baseProcEntry.chance = fields[12].Get<float>();
|
||||
float cooldown = fields[13].Get<float>();
|
||||
baseProcEntry.cooldown = uint32(cooldown);
|
||||
baseProcEntry.charges = fields[14].Get<uint32>();
|
||||
baseProcEntry.SchoolMask = fields[1].Get<int8>();
|
||||
baseProcEntry.SpellFamilyName = fields[2].Get<uint16>();
|
||||
baseProcEntry.SpellFamilyMask[0] = fields[3].Get<uint32>();
|
||||
baseProcEntry.SpellFamilyMask[1] = fields[4].Get<uint32>();
|
||||
baseProcEntry.SpellFamilyMask[2] = fields[5].Get<uint32>();
|
||||
baseProcEntry.ProcFlags = fields[6].Get<uint32>();
|
||||
baseProcEntry.SpellTypeMask = fields[7].Get<uint32>();
|
||||
baseProcEntry.SpellPhaseMask = fields[8].Get<uint32>();
|
||||
baseProcEntry.HitMask = fields[9].Get<uint32>();
|
||||
baseProcEntry.AttributesMask = fields[10].Get<uint32>();
|
||||
baseProcEntry.ProcsPerMinute = fields[11].Get<float>();
|
||||
baseProcEntry.Chance = fields[12].Get<float>();
|
||||
baseProcEntry.Cooldown = Milliseconds(fields[13].Get<uint32>());
|
||||
baseProcEntry.Charges = fields[14].Get<uint32>();
|
||||
|
||||
while (spellInfo)
|
||||
{
|
||||
@@ -1900,56 +1899,51 @@ void SpellMgr::LoadSpellProcs()
|
||||
SpellProcEntry procEntry = SpellProcEntry(baseProcEntry);
|
||||
|
||||
// take defaults from dbcs
|
||||
if (!procEntry.typeMask)
|
||||
procEntry.typeMask = spellInfo->ProcFlags;
|
||||
if (!procEntry.charges)
|
||||
procEntry.charges = spellInfo->ProcCharges;
|
||||
if (!procEntry.chance && !procEntry.ratePerMinute)
|
||||
procEntry.chance = float(spellInfo->ProcChance);
|
||||
if (!procEntry.ProcFlags)
|
||||
procEntry.ProcFlags = spellInfo->ProcFlags;
|
||||
if (!procEntry.Charges)
|
||||
procEntry.Charges = spellInfo->ProcCharges;
|
||||
if (!procEntry.Chance && !procEntry.ProcsPerMinute)
|
||||
procEntry.Chance = float(spellInfo->ProcChance);
|
||||
|
||||
// validate data
|
||||
if (procEntry.schoolMask & ~SPELL_SCHOOL_MASK_ALL)
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} has wrong `schoolMask` set: {}", spellId, procEntry.schoolMask);
|
||||
if (procEntry.spellFamilyName && (procEntry.spellFamilyName < 3 || procEntry.spellFamilyName > 17 || procEntry.spellFamilyName == 14 || procEntry.spellFamilyName == 16))
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} has wrong `spellFamilyName` set: {}", spellId, procEntry.spellFamilyName);
|
||||
if (procEntry.chance < 0)
|
||||
if (procEntry.SchoolMask & ~SPELL_SCHOOL_MASK_ALL)
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} has wrong `SchoolMask` set: {}", spellId, procEntry.SchoolMask);
|
||||
if (procEntry.SpellFamilyName && (procEntry.SpellFamilyName < 3 || procEntry.SpellFamilyName > 17 || procEntry.SpellFamilyName == 14 || procEntry.SpellFamilyName == 16))
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} has wrong `SpellFamilyName` set: {}", spellId, procEntry.SpellFamilyName);
|
||||
if (procEntry.Chance < 0)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} has negative value in `chance` field", spellId);
|
||||
procEntry.chance = 0;
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} has negative value in `Chance` field", spellId);
|
||||
procEntry.Chance = 0;
|
||||
}
|
||||
if (procEntry.ratePerMinute < 0)
|
||||
if (procEntry.ProcsPerMinute < 0)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} has negative value in `ratePerMinute` field", spellId);
|
||||
procEntry.ratePerMinute = 0;
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} has negative value in `ProcsPerMinute` field", spellId);
|
||||
procEntry.ProcsPerMinute = 0;
|
||||
}
|
||||
if (cooldown < 0)
|
||||
if (procEntry.Chance == 0 && procEntry.ProcsPerMinute == 0)
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} doesn't have `Chance` and `ProcsPerMinute` values defined, proc will not be triggered", spellId);
|
||||
if (procEntry.Charges > 99)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} has negative value in `cooldown` field", spellId);
|
||||
procEntry.cooldown = 0;
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} has too big value in `Charges` field", spellId);
|
||||
procEntry.Charges = 99;
|
||||
}
|
||||
if (procEntry.chance == 0 && procEntry.ratePerMinute == 0)
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} doesn't have `chance` and `ratePerMinute` values defined, proc will not be triggered", spellId);
|
||||
if (procEntry.charges > 99)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} has too big value in `charges` field", spellId);
|
||||
procEntry.charges = 99;
|
||||
}
|
||||
if (!procEntry.typeMask)
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} doesn't have `typeMask` value defined, proc will not be triggered", spellId);
|
||||
if (procEntry.spellTypeMask & ~PROC_SPELL_TYPE_MASK_ALL)
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} has wrong `spellTypeMask` set: {}", spellId, procEntry.spellTypeMask);
|
||||
if (procEntry.spellTypeMask && !(procEntry.typeMask & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK)))
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} has `spellTypeMask` value defined, but it won't be used for defined `typeMask` value", spellId);
|
||||
if (!procEntry.spellPhaseMask && procEntry.typeMask & REQ_SPELL_PHASE_PROC_FLAG_MASK)
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} doesn't have `spellPhaseMask` value defined, but it's required for defined `typeMask` value, proc will not be triggered", spellId);
|
||||
if (procEntry.spellPhaseMask & ~PROC_SPELL_PHASE_MASK_ALL)
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} has wrong `spellPhaseMask` set: {}", spellId, procEntry.spellPhaseMask);
|
||||
if (procEntry.spellPhaseMask && !(procEntry.typeMask & REQ_SPELL_PHASE_PROC_FLAG_MASK))
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} has `spellPhaseMask` value defined, but it won't be used for defined `typeMask` value", spellId);
|
||||
if (procEntry.hitMask & ~PROC_HIT_MASK_ALL)
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} has wrong `hitMask` set: {}", spellId, procEntry.hitMask);
|
||||
if (procEntry.hitMask && !(procEntry.typeMask & TAKEN_HIT_PROC_FLAG_MASK || (procEntry.typeMask & DONE_HIT_PROC_FLAG_MASK && (!procEntry.spellPhaseMask || procEntry.spellPhaseMask & (PROC_SPELL_PHASE_HIT | PROC_SPELL_PHASE_FINISH)))))
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} has `hitMask` value defined, but it won't be used for defined `typeMask` and `spellPhaseMask` values", spellId);
|
||||
if (!procEntry.ProcFlags)
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} doesn't have `ProcFlags` value defined, proc will not be triggered", spellId);
|
||||
if (procEntry.SpellTypeMask & ~PROC_SPELL_TYPE_MASK_ALL)
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} has wrong `SpellTypeMask` set: {}", spellId, procEntry.SpellTypeMask);
|
||||
if (procEntry.SpellTypeMask && !(procEntry.ProcFlags & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK)))
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} has `SpellTypeMask` value defined, but it won't be used for defined `ProcFlags` value", spellId);
|
||||
if (!procEntry.SpellPhaseMask && procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK)
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} doesn't have `SpellPhaseMask` value defined, but it's required for defined `ProcFlags` value, proc will not be triggered", spellId);
|
||||
if (procEntry.SpellPhaseMask & ~PROC_SPELL_PHASE_MASK_ALL)
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} has wrong `SpellPhaseMask` set: {}", spellId, procEntry.SpellPhaseMask);
|
||||
if (procEntry.SpellPhaseMask && !(procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK))
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} has `SpellPhaseMask` value defined, but it won't be used for defined `ProcFlags` value", spellId);
|
||||
if (procEntry.HitMask & ~PROC_HIT_MASK_ALL)
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} has wrong `HitMask` set: {}", spellId, procEntry.HitMask);
|
||||
if (procEntry.HitMask && !(procEntry.ProcFlags & TAKEN_HIT_PROC_FLAG_MASK || (procEntry.ProcFlags & DONE_HIT_PROC_FLAG_MASK && (!procEntry.SpellPhaseMask || procEntry.SpellPhaseMask & (PROC_SPELL_PHASE_HIT | PROC_SPELL_PHASE_FINISH)))))
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} has `HitMask` value defined, but it won't be used for defined `ProcFlags` and `SpellPhaseMask` values", spellId);
|
||||
|
||||
mSpellProcMap[spellInfo->Id] = procEntry;
|
||||
|
||||
|
||||
@@ -263,7 +263,7 @@ enum ProcFlagsHit
|
||||
PROC_HIT_REFLECT = 0x0000800,
|
||||
PROC_HIT_INTERRUPT = 0x0001000, // (not used atm)
|
||||
PROC_HIT_FULL_BLOCK = 0x0002000,
|
||||
PROC_HIT_MASK_ALL = 0x2FFF,
|
||||
PROC_HIT_MASK_ALL = 0x0002FFF,
|
||||
};
|
||||
|
||||
enum ProcAttributes
|
||||
@@ -288,18 +288,18 @@ typedef std::unordered_map<uint32, SpellProcEventEntry> SpellProcEventMap;
|
||||
|
||||
struct SpellProcEntry
|
||||
{
|
||||
uint32 schoolMask; // if nonzero - bitmask for matching proc condition based on spell's school
|
||||
uint32 spellFamilyName; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyName
|
||||
flag96 spellFamilyMask; // if nonzero - bitmask for matching proc condition based on candidate spell's SpellFamilyFlags
|
||||
uint32 typeMask; // if nonzero - owerwrite procFlags field for given Spell.dbc entry, bitmask for matching proc condition, see enum ProcFlags
|
||||
uint32 spellTypeMask; // if nonzero - bitmask for matching proc condition based on candidate spell's damage/heal effects, see enum ProcFlagsSpellType
|
||||
uint32 spellPhaseMask; // if nonzero - bitmask for matching phase of a spellcast on which proc occurs, see enum ProcFlagsSpellPhase
|
||||
uint32 hitMask; // if nonzero - bitmask for matching proc condition based on hit result, see enum ProcFlagsHit
|
||||
uint32 attributesMask; // bitmask, see ProcAttributes
|
||||
float ratePerMinute; // if nonzero - chance to proc is equal to value * aura caster's weapon speed / 60
|
||||
float chance; // if nonzero - owerwrite procChance field for given Spell.dbc entry, defines chance of proc to occur, not used if perMinuteRate set
|
||||
uint32 cooldown; // if nonzero - cooldown in secs for aura proc, applied to aura
|
||||
uint32 charges; // if nonzero - owerwrite procCharges field for given Spell.dbc entry, defines how many times proc can occur before aura remove, 0 - infinite
|
||||
uint32 SchoolMask; // if nonzero - bitmask for matching proc condition based on spell's school
|
||||
uint32 SpellFamilyName; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyName
|
||||
flag96 SpellFamilyMask; // if nonzero - bitmask for matching proc condition based on candidate spell's SpellFamilyFlags
|
||||
uint32 ProcFlags; // if nonzero - owerwrite procFlags field for given Spell.dbc entry, bitmask for matching proc condition, see enum ProcFlags
|
||||
uint32 SpellTypeMask; // if nonzero - bitmask for matching proc condition based on candidate spell's damage/heal effects, see enum ProcFlagsSpellType
|
||||
uint32 SpellPhaseMask; // if nonzero - bitmask for matching phase of a spellcast on which proc occurs, see enum ProcFlagsSpellPhase
|
||||
uint32 HitMask; // if nonzero - bitmask for matching proc condition based on hit result, see enum ProcFlagsHit
|
||||
uint32 AttributesMask; // bitmask, see ProcAttributes
|
||||
float ProcsPerMinute; // if nonzero - chance to proc is equal to value * aura caster's weapon speed / 60
|
||||
float Chance; // if nonzero - owerwrite procChance field for given Spell.dbc entry, defines chance of proc to occur, not used if ProcsPerMinute set
|
||||
Milliseconds Cooldown; // if nonzero - cooldown in secs for aura proc, applied to aura
|
||||
uint32 Charges; // if nonzero - owerwrite procCharges field for given Spell.dbc entry, defines how many times proc can occur before aura remove, 0 - infinite
|
||||
};
|
||||
|
||||
typedef std::unordered_map<uint32, SpellProcEntry> SpellProcMap;
|
||||
|
||||
@@ -139,7 +139,6 @@ enum WorldBoolConfigs
|
||||
CONFIG_ALLOW_TICKETS,
|
||||
CONFIG_DELETE_CHARACTER_TICKET_TRACE,
|
||||
CONFIG_PRESERVE_CUSTOM_CHANNELS,
|
||||
CONFIG_WINTERGRASP_ENABLE,
|
||||
CONFIG_PDUMP_NO_PATHS,
|
||||
CONFIG_PDUMP_NO_OVERWRITE,
|
||||
CONFIG_ENABLE_MMAPS, // pussywizard
|
||||
@@ -312,6 +311,7 @@ enum WorldIntConfigs
|
||||
CONFIG_BATTLEGROUND_SPEED_BUFF_RESPAWN,
|
||||
CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_LIMIT_MIN_LEVEL,
|
||||
CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_LIMIT_MIN_PLAYERS,
|
||||
CONFIG_WINTERGRASP_ENABLE,
|
||||
CONFIG_ARENA_MAX_RATING_DIFFERENCE,
|
||||
CONFIG_ARENA_RATING_DISCARD_TIMER,
|
||||
CONFIG_ARENA_PREV_OPPONENTS_DISCARD_TIMER,
|
||||
|
||||
@@ -1385,7 +1385,7 @@ void World::LoadConfigSettings(bool reload)
|
||||
MMAP::MMapFactory::InitializeDisabledMaps();
|
||||
|
||||
// Wintergrasp
|
||||
m_bool_configs[CONFIG_WINTERGRASP_ENABLE] = sConfigMgr->GetOption<bool>("Wintergrasp.Enable", false);
|
||||
m_int_configs[CONFIG_WINTERGRASP_ENABLE] = sConfigMgr->GetOption<int32>("Wintergrasp.Enable", 1);
|
||||
m_int_configs[CONFIG_WINTERGRASP_PLR_MAX] = sConfigMgr->GetOption<int32>("Wintergrasp.PlayerMax", 100);
|
||||
m_int_configs[CONFIG_WINTERGRASP_PLR_MIN] = sConfigMgr->GetOption<int32>("Wintergrasp.PlayerMin", 0);
|
||||
m_int_configs[CONFIG_WINTERGRASP_PLR_MIN_LVL] = sConfigMgr->GetOption<int32>("Wintergrasp.PlayerMinLvl", 77);
|
||||
|
||||
@@ -72,6 +72,7 @@ public:
|
||||
{ "customize", HandleCharacterCustomizeCommand, SEC_GAMEMASTER, Console::Yes },
|
||||
{ "changefaction", HandleCharacterChangeFactionCommand, SEC_GAMEMASTER, Console::Yes },
|
||||
{ "changerace", HandleCharacterChangeRaceCommand, SEC_GAMEMASTER, Console::Yes },
|
||||
{ "changeaccount", HandleCharacterChangeAccountCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "check", characterCheckCommandTable },
|
||||
{ "erase", HandleCharacterEraseCommand, SEC_CONSOLE, Console::Yes },
|
||||
{ "deleted", characterDeletedCommandTable },
|
||||
@@ -381,6 +382,8 @@ public:
|
||||
{
|
||||
target->SetName(newName);
|
||||
|
||||
ObjectAccessor::UpdatePlayerNameMapReference(player->GetName(), target);
|
||||
|
||||
if (WorldSession* session = target->GetSession())
|
||||
session->KickPlayer("HandleCharacterRenameCommand GM Command renaming character");
|
||||
}
|
||||
@@ -1059,6 +1062,55 @@ public:
|
||||
handler->PSendSysMessage("--------------------------------------");
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleCharacterChangeAccountCommand(ChatHandler* handler, std::string accountName, Optional<PlayerIdentifier> player)
|
||||
{
|
||||
if (!player)
|
||||
{
|
||||
player = PlayerIdentifier::FromTargetOrSelf(handler);
|
||||
}
|
||||
|
||||
if (!player)
|
||||
{
|
||||
handler->SendSysMessage(LANG_PLAYER_NOT_FOUND);
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (uint32 accountId = AccountMgr::GetId(accountName))
|
||||
{
|
||||
if (AccountMgr::GetCharactersCount(accountId) >= 10)
|
||||
{
|
||||
handler->PSendSysMessage(LANG_ACCOUNT_CHARACTER_LIST_FULL, accountName, accountId);
|
||||
handler->SetSentErrorMessage(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CharacterCacheEntry const* cache = sCharacterCache->GetCharacterCacheByName(player->GetName()))
|
||||
{
|
||||
std::string accName;
|
||||
AccountMgr::GetName(cache->AccountId, accName);
|
||||
handler->PSendSysMessage(LANG_CMD_CHAR_CHANGE_ACC_SUCCESS, player->GetName(), player->GetGUID().GetCounter(), accName, cache->AccountId, accountName, accountId);
|
||||
}
|
||||
|
||||
if (player->IsConnected())
|
||||
{
|
||||
player->GetConnectedPlayer()->GetSession()->KickPlayer("CMD char changeaccount");
|
||||
}
|
||||
|
||||
CharacterDatabase.Query("UPDATE characters SET account = {} WHERE guid = {}", accountId, player->GetGUID().GetCounter());
|
||||
sCharacterCache->UpdateCharacterAccountId(player->GetGUID(), accountId);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName);
|
||||
handler->SetSentErrorMessage(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_character_commandscript()
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
{
|
||||
static ChatCommandTable groupCommandTable =
|
||||
{
|
||||
{ "list", HandleGroupListCommand, SEC_GAMEMASTER, Console::No },
|
||||
{ "list", HandleGroupListCommand, SEC_GAMEMASTER, Console::Yes },
|
||||
{ "join", HandleGroupJoinCommand, SEC_GAMEMASTER, Console::No },
|
||||
{ "remove", HandleGroupRemoveCommand, SEC_GAMEMASTER, Console::No },
|
||||
{ "disband", HandleGroupDisbandCommand, SEC_GAMEMASTER, Console::No },
|
||||
@@ -219,7 +219,7 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
handler->PSendSysMessage(LANG_GROUP_TYPE, (groupTarget->isRaidGroup() ? "raid" : "party"));
|
||||
handler->PSendSysMessage(LANG_GROUP_TYPE, (groupTarget->isRaidGroup() ? "Raid" : "Party"), groupTarget->GetMembersCount());
|
||||
|
||||
for (auto const& slot : groupTarget->GetMemberSlots())
|
||||
{
|
||||
|
||||
@@ -195,9 +195,12 @@ public:
|
||||
if (!spellInfo || !SpellMgr::IsSpellValid(spellInfo))
|
||||
continue;
|
||||
|
||||
player->LearnTalent(talentInfo->TalentID, rankId);
|
||||
player->LearnTalent(talentInfo->TalentID, rankId, true);
|
||||
}
|
||||
|
||||
player->SetFreeTalentPoints(0);
|
||||
player->SendTalentsInfoData(false);
|
||||
|
||||
handler->SendSysMessage(LANG_COMMAND_LEARN_CLASS_TALENTS);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1399,7 +1399,7 @@ public:
|
||||
|
||||
if (location->empty() || *location == "inn")
|
||||
{
|
||||
player->TeleportTo(player->m_homebindMapId, player->m_homebindX, player->m_homebindY, player->m_homebindZ, player->GetOrientation());
|
||||
player->TeleportTo(player->m_homebindMapId, player->m_homebindX, player->m_homebindY, player->m_homebindZ, player->m_homebindO);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ public:
|
||||
return commandTable;
|
||||
}
|
||||
|
||||
static bool HandleMmapPathCommand(ChatHandler* handler, Optional<std::string> para)
|
||||
static bool HandleMmapPathCommand(ChatHandler* handler, Optional<std::string> param)
|
||||
{
|
||||
if (!MMAP::MMapFactory::createOrGetMMapMgr()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId()))
|
||||
{
|
||||
@@ -81,13 +81,19 @@ public:
|
||||
}
|
||||
|
||||
bool useStraightPath = false;
|
||||
if (StringStartsWith("true", *para))
|
||||
useStraightPath = true;
|
||||
|
||||
bool useRaycast = false;
|
||||
if (StringStartsWith("line", *para) || StringStartsWith("ray", *para) || StringStartsWith("raycast", *para))
|
||||
if (param)
|
||||
{
|
||||
useRaycast = true;
|
||||
auto paramValue = param.value();
|
||||
if (paramValue.starts_with("true"))
|
||||
{
|
||||
useStraightPath = true;
|
||||
}
|
||||
|
||||
if (paramValue.starts_with("line") || paramValue.starts_with("ray") || paramValue.starts_with("raycast"))
|
||||
{
|
||||
useRaycast = true;
|
||||
}
|
||||
}
|
||||
|
||||
// unit locations
|
||||
|
||||
@@ -252,7 +252,7 @@ public:
|
||||
else if (targetGuid)
|
||||
{
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG);
|
||||
stmt->SetData(0, uint16(AT_LOGIN_NONE | AT_LOGIN_RESET_PET_TALENTS));
|
||||
stmt->SetData(0, uint16(AT_LOGIN_RESET_TALENTS | AT_LOGIN_RESET_PET_TALENTS));
|
||||
stmt->SetData(1, targetGuid.GetCounter());
|
||||
CharacterDatabase.Execute(stmt);
|
||||
|
||||
|
||||
@@ -182,7 +182,7 @@ public:
|
||||
if (where.index() == 1) // References target's homebind
|
||||
{
|
||||
if (Player* target = player->GetConnectedPlayer())
|
||||
target->TeleportTo(target->m_homebindMapId, target->m_homebindX, target->m_homebindY, target->m_homebindZ, target->GetOrientation());
|
||||
target->TeleportTo(target->m_homebindMapId, target->m_homebindX, target->m_homebindY, target->m_homebindZ, target->m_homebindO);
|
||||
else
|
||||
{
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_HOMEBIND);
|
||||
|
||||
+1
-1
@@ -115,7 +115,7 @@ public:
|
||||
case EVENT_KNOCKBACK:
|
||||
DoCastVictim(SPELL_KNOCKBACK);
|
||||
if (DoGetThreat(me->GetVictim()))
|
||||
DoModifyThreatPercent(me->GetVictim(), -50);
|
||||
DoModifyThreatByPercent(me->GetVictim(), -50);
|
||||
events.ScheduleEvent(EVENT_KNOCKBACK, 15000, 30000);
|
||||
break;
|
||||
case EVENT_CHECK:
|
||||
|
||||
@@ -72,7 +72,7 @@ public:
|
||||
case EVENT_WINGBUFFET:
|
||||
DoCastVictim(SPELL_WINGBUFFET);
|
||||
if (DoGetThreat(me->GetVictim()))
|
||||
DoModifyThreatPercent(me->GetVictim(), -75);
|
||||
DoModifyThreatByPercent(me->GetVictim(), -75);
|
||||
events.ScheduleEvent(EVENT_WINGBUFFET, 30000);
|
||||
break;
|
||||
case EVENT_FLAMEBUFFET:
|
||||
|
||||
+1
-1
@@ -77,7 +77,7 @@ public:
|
||||
case EVENT_WINGBUFFET:
|
||||
DoCastVictim(SPELL_WINGBUFFET);
|
||||
if (DoGetThreat(me->GetVictim()))
|
||||
DoModifyThreatPercent(me->GetVictim(), -75);
|
||||
DoModifyThreatByPercent(me->GetVictim(), -75);
|
||||
events.ScheduleEvent(EVENT_WINGBUFFET, 30000);
|
||||
break;
|
||||
case EVENT_FRENZY:
|
||||
|
||||
@@ -411,7 +411,7 @@ public:
|
||||
me->SetReactState(REACT_PASSIVE);
|
||||
me->InterruptNonMeleeSpells(false);
|
||||
me->AttackStop();
|
||||
DoResetThreat();
|
||||
DoResetThreatList();
|
||||
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE);
|
||||
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_SUBMERGED);
|
||||
DoCastSelf(SPELL_RAGNA_SUBMERGE_VISUAL, true);
|
||||
|
||||
+5
-1
@@ -250,7 +250,11 @@ public:
|
||||
|
||||
if (bossId == DATA_MAJORDOMO_EXECUTUS && state == DONE)
|
||||
{
|
||||
DoRespawnGameObject(_cacheOfTheFirelordGUID, 7 * DAY);
|
||||
if (GameObject* cache = instance->GetGameObject(_cacheOfTheFirelordGUID))
|
||||
{
|
||||
cache->SetRespawnTime(7 * DAY);
|
||||
cache->SetLootRecipient(instance);
|
||||
}
|
||||
}
|
||||
else if (bossId == DATA_GOLEMAGG)
|
||||
{
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "ScriptMgr.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SpellScript.h"
|
||||
#include "TaskScheduler.h"
|
||||
|
||||
enum Texts
|
||||
{
|
||||
@@ -34,6 +35,13 @@ enum Spells
|
||||
SPELL_FULL_HEALTH = 17683,
|
||||
SPELL_FIRE_NOVA_VISUAL = 19823,
|
||||
SPELL_PLAY_DEAD_PACIFY = 19951, // Server side spell
|
||||
|
||||
// Lava Spawn
|
||||
SPELL_FIREBALL = 19391,
|
||||
SPELL_SPLIT_1 = 19569,
|
||||
SPELL_SPLIT_2 = 19570,
|
||||
|
||||
TALK_0 = 0
|
||||
};
|
||||
|
||||
// Serrated Bites timer may be wrong
|
||||
@@ -207,10 +215,60 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
struct npc_lava_spawn : public ScriptedAI
|
||||
{
|
||||
npc_lava_spawn(Creature* creature) : ScriptedAI(creature)
|
||||
{
|
||||
}
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
_scheduler.CancelAll();
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* /*who*/) override
|
||||
{
|
||||
_scheduler.Schedule(15s, [this](TaskContext context)
|
||||
{
|
||||
std::list<Creature*> lavaSpawns;
|
||||
me->GetCreatureListWithEntryInGrid(lavaSpawns, me->GetEntry(), 100.f);
|
||||
if (lavaSpawns.size() < 16)
|
||||
{
|
||||
Talk(TALK_0);
|
||||
|
||||
DoCastSelf(SPELL_SPLIT_1, true);
|
||||
DoCastSelf(SPELL_SPLIT_2, true);
|
||||
|
||||
me->DespawnOrUnsummon();
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Repeat(15s);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (!UpdateVictim())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_scheduler.Update(diff);
|
||||
|
||||
DoSpellAttackIfReady(SPELL_FIREBALL);
|
||||
}
|
||||
|
||||
private:
|
||||
TaskScheduler _scheduler;
|
||||
};
|
||||
|
||||
void AddSC_molten_core()
|
||||
{
|
||||
// Creatures
|
||||
new npc_mc_core_hound();
|
||||
RegisterCreatureAI(npc_lava_spawn);
|
||||
|
||||
// Spells
|
||||
new spell_mc_play_dead();
|
||||
|
||||
@@ -496,7 +496,7 @@ public:
|
||||
{
|
||||
if (me->GetVictim())
|
||||
{
|
||||
DoModifyThreatPercent(me->GetVictim(), -100);
|
||||
DoModifyThreatByPercent(me->GetVictim(), -100);
|
||||
}
|
||||
|
||||
me->AddThreat(target, 1000000.0f);
|
||||
|
||||
@@ -915,7 +915,7 @@ public:
|
||||
DoCast(target, SPELL_LITTLE_RED_RIDING_HOOD, true);
|
||||
TempThreat = DoGetThreat(target);
|
||||
if (TempThreat)
|
||||
DoModifyThreatPercent(target, -100);
|
||||
DoModifyThreatByPercent(target, -100);
|
||||
HoodGUID = target->GetGUID();
|
||||
me->AddThreat(target, 1000000.0f);
|
||||
ChaseTimer = 20000;
|
||||
@@ -930,7 +930,7 @@ public:
|
||||
{
|
||||
HoodGUID.Clear();
|
||||
if (DoGetThreat(target))
|
||||
DoModifyThreatPercent(target, -100);
|
||||
DoModifyThreatByPercent(target, -100);
|
||||
me->AddThreat(target, TempThreat);
|
||||
TempThreat = 0;
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@ public:
|
||||
{
|
||||
case EVENT_SPELL_VANISH:
|
||||
me->CastSpell(me, SPELL_VANISH, false);
|
||||
DoResetThreat();
|
||||
DoResetThreatList();
|
||||
if (Unit* unit = SelectTarget(SelectTargetMethod::Random, 0))
|
||||
me->AddThreat(unit, 1000.0f);
|
||||
|
||||
|
||||
@@ -313,7 +313,7 @@ public:
|
||||
case 3:
|
||||
case 4:
|
||||
me->NearTeleportTo(CENTER_X, CENTER_Y, CENTER_Z, me->GetOrientation());
|
||||
DoResetThreat();
|
||||
DoResetThreatList();
|
||||
me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, 0);
|
||||
me->RemoveAurasDueToSpell(Transform[Phase].unaura);
|
||||
DoCast(me, Transform[Phase].spell);
|
||||
|
||||
@@ -232,7 +232,7 @@ public:
|
||||
me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(EQUIP_UNEQUIP));
|
||||
me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, uint32(EQUIP_UNEQUIP));
|
||||
me->AttackStop();
|
||||
DoResetThreat();
|
||||
DoResetThreatList();
|
||||
me->SetReactState(REACT_PASSIVE);
|
||||
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
|
||||
DoCastSelf(SPELL_VANISH_VISUAL);
|
||||
|
||||
@@ -91,7 +91,7 @@ public:
|
||||
}
|
||||
DoCast(me, SPELL_AVATAR);
|
||||
me->SetReactState(REACT_PASSIVE);
|
||||
DoResetThreat();
|
||||
DoResetThreatList();
|
||||
events.ScheduleEvent(EVENT_START_PURSUIT, 2s);
|
||||
events.ScheduleEvent(EVENT_STOP_PURSUIT, 15s);
|
||||
events.ScheduleEvent(EVENT_AVATAR, 45s, 50s);
|
||||
|
||||
@@ -164,7 +164,7 @@ struct boss_jeklik : public BossAI
|
||||
if (events.IsInPhase(PHASE_ONE) && !HealthAbovePct(50))
|
||||
{
|
||||
me->RemoveAurasDueToSpell(SPELL_BAT_FORM);
|
||||
DoResetThreat();
|
||||
DoResetThreatList();
|
||||
events.SetPhase(PHASE_TWO);
|
||||
events.CancelEventGroup(PHASE_ONE);
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ public:
|
||||
{
|
||||
if (!target->IsWithinMeleeRange(me))
|
||||
{
|
||||
_thousandBladesTargets.push_back(target);
|
||||
_thousandBladesTargets.push_back(target->GetGUID());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ public:
|
||||
{
|
||||
if (target->IsWithinMeleeRange(me))
|
||||
{
|
||||
_thousandBladesTargets.push_back(target);
|
||||
_thousandBladesTargets.push_back(target->GetGUID());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -204,12 +204,12 @@ public:
|
||||
|
||||
if (!_thousandBladesTargets.empty())
|
||||
{
|
||||
std::vector<Unit*>::iterator itr = _thousandBladesTargets.begin();
|
||||
GuidVector::iterator itr = _thousandBladesTargets.begin();
|
||||
std::advance(itr, urand(0, _thousandBladesTargets.size() - 1));
|
||||
|
||||
if (Unit* target = *itr)
|
||||
if (Unit* target = ObjectAccessor::GetUnit(*me, *itr))
|
||||
{
|
||||
DoCast(target, SPELL_THOUSAND_BLADES, false);
|
||||
DoCast(target, SPELL_THOUSAND_BLADES);
|
||||
}
|
||||
|
||||
if (_thousandBladesTargets.erase(itr) != _thousandBladesTargets.end())
|
||||
@@ -242,7 +242,7 @@ public:
|
||||
bool _enraged;
|
||||
uint32 _dynamicFlags;
|
||||
uint8 _thousandBladesCount;
|
||||
std::vector<Unit*> _thousandBladesTargets;
|
||||
GuidVector _thousandBladesTargets;
|
||||
};
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
|
||||
@@ -163,7 +163,7 @@ public:
|
||||
me->SetReactState(REACT_PASSIVE);
|
||||
me->SetStandState(UNIT_STAND_STATE_SLEEP);
|
||||
me->AttackStop();
|
||||
DoResetThreat();
|
||||
DoResetThreatList();
|
||||
WasDead = true;
|
||||
CheckPhaseTransition();
|
||||
Talk(EMOTE_THEKAL_DIES);
|
||||
@@ -245,7 +245,7 @@ public:
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
|
||||
{
|
||||
DoCast(target, SPELL_CHARGE);
|
||||
DoResetThreat();
|
||||
DoResetThreatList();
|
||||
AttackStart(target);
|
||||
}
|
||||
context.Repeat(15s, 22s);
|
||||
@@ -407,7 +407,7 @@ public:
|
||||
|
||||
if (DoGetThreat(me->GetVictim()))
|
||||
{
|
||||
DoModifyThreatPercent(me->GetVictim(), -100);
|
||||
DoModifyThreatByPercent(me->GetVictim(), -100);
|
||||
}
|
||||
|
||||
context.Repeat(17s, 27s);
|
||||
|
||||
@@ -252,7 +252,7 @@ public:
|
||||
// shapeshift at 50% health
|
||||
DoCast(me, SPELL_VENOXIS_TRANSFORM);
|
||||
Talk(SAY_VENOXIS_TRANSFORM);
|
||||
DoResetThreat();
|
||||
DoResetThreatList();
|
||||
|
||||
// phase two events (snakeform)
|
||||
events.ScheduleEvent(EVENT_VENOM_SPIT, 5000, 0, PHASE_TWO);
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "GridNotifiers.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "SpellScript.h"
|
||||
|
||||
enum Spells
|
||||
{
|
||||
SPELL_AXE_FLURRY_STUN = 24020
|
||||
};
|
||||
|
||||
class spell_axe_flurry : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_axe_flurry);
|
||||
|
||||
void FilterTargets(std::list<WorldObject*>& targets)
|
||||
{
|
||||
targets.remove_if(Acore::UnitAuraCheck(true, SPELL_AXE_FLURRY_STUN));
|
||||
Acore::Containers::RandomResize(targets, 1);
|
||||
}
|
||||
|
||||
void HandleEffect(SpellEffIndex /*effIndex*/)
|
||||
{
|
||||
if (Unit* caster = GetCaster())
|
||||
{
|
||||
if (Unit* target = GetHitUnit())
|
||||
{
|
||||
caster->CastSpell(target, SPELL_AXE_FLURRY_STUN, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_axe_flurry::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
|
||||
OnEffectHitTarget += SpellEffectFn(spell_axe_flurry::HandleEffect, EFFECT_0, SPELL_EFFECT_DUMMY);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_zulgurub()
|
||||
{
|
||||
RegisterSpellScript(spell_axe_flurry);
|
||||
}
|
||||
@@ -141,6 +141,7 @@ void AddSC_boss_hazzarah();
|
||||
void AddSC_boss_renataki();
|
||||
void AddSC_boss_wushoolay();
|
||||
void AddSC_instance_zulgurub();
|
||||
void AddSC_zulgurub();
|
||||
// void AddSC_alterac_mountains();
|
||||
void AddSC_arathi_highlands();
|
||||
void AddSC_blasted_lands();
|
||||
@@ -292,6 +293,7 @@ void AddEasternKingdomsScripts()
|
||||
AddSC_boss_renataki();
|
||||
AddSC_boss_wushoolay();
|
||||
AddSC_instance_zulgurub();
|
||||
AddSC_zulgurub();
|
||||
//AddSC_alterac_mountains();
|
||||
AddSC_arathi_highlands();
|
||||
AddSC_blasted_lands();
|
||||
|
||||
@@ -233,8 +233,6 @@ enum costumedOrphan
|
||||
|
||||
// Actions
|
||||
ACTION_START_EVENT = 1,
|
||||
DATA_EVENT = 1,
|
||||
DATA_ALLOW_START = 2,
|
||||
|
||||
// Talks
|
||||
TALK_SHADE_CONFLAGRATION = 0,
|
||||
@@ -323,7 +321,7 @@ class spell_hallows_end_base_fire : public AuraScript
|
||||
|
||||
struct npc_costumed_orphan_matron : public ScriptedAI
|
||||
{
|
||||
npc_costumed_orphan_matron(Creature* c) : ScriptedAI(c) { }
|
||||
npc_costumed_orphan_matron(Creature* c) : ScriptedAI(c) {}
|
||||
|
||||
uint32 eventStarted;
|
||||
bool allowQuest;
|
||||
@@ -340,48 +338,48 @@ struct npc_costumed_orphan_matron : public ScriptedAI
|
||||
{
|
||||
switch (me->GetAreaId())
|
||||
{
|
||||
case 87: // Goldshire
|
||||
x = -9494.4f;
|
||||
y = 48.53f;
|
||||
z = 70.5f;
|
||||
o = 0.5f;
|
||||
path = 235431;
|
||||
break;
|
||||
case 131: // Kharanos
|
||||
x = -5558.34f;
|
||||
y = -499.46f;
|
||||
z = 414.12f;
|
||||
o = 2.08f;
|
||||
path = 235432;
|
||||
break;
|
||||
case 3576: // Azure Watch
|
||||
x = -4163.58f;
|
||||
y = -12460.30f;
|
||||
z = 63.02f;
|
||||
o = 4.31f;
|
||||
path = 235433;
|
||||
break;
|
||||
case 362: // Razor Hill
|
||||
x = 373.2f;
|
||||
y = -4723.4f;
|
||||
z = 31.2f;
|
||||
o = 3.2f;
|
||||
path = 235434;
|
||||
break;
|
||||
case 159: // Brill
|
||||
x = 2195.2f;
|
||||
y = 264.0f;
|
||||
z = 55.62f;
|
||||
o = 0.15f;
|
||||
path = 235435;
|
||||
break;
|
||||
case 3665: // Falcon Wing Square
|
||||
x = 9547.91f;
|
||||
y = -6809.9f;
|
||||
z = 27.96f;
|
||||
o = 3.4f;
|
||||
path = 235436;
|
||||
break;
|
||||
case 87: // Goldshire
|
||||
x = -9494.4f;
|
||||
y = 48.53f;
|
||||
z = 70.5f;
|
||||
o = 0.5f;
|
||||
path = 235431;
|
||||
break;
|
||||
case 131: // Kharanos
|
||||
x = -5558.34f;
|
||||
y = -499.46f;
|
||||
z = 414.12f;
|
||||
o = 2.08f;
|
||||
path = 235432;
|
||||
break;
|
||||
case 3576: // Azure Watch
|
||||
x = -4163.58f;
|
||||
y = -12460.30f;
|
||||
z = 63.02f;
|
||||
o = 4.31f;
|
||||
path = 235433;
|
||||
break;
|
||||
case 362: // Razor Hill
|
||||
x = 373.2f;
|
||||
y = -4723.4f;
|
||||
z = 31.2f;
|
||||
o = 3.2f;
|
||||
path = 235434;
|
||||
break;
|
||||
case 159: // Brill
|
||||
x = 2195.2f;
|
||||
y = 264.0f;
|
||||
z = 55.62f;
|
||||
o = 0.15f;
|
||||
path = 235435;
|
||||
break;
|
||||
case 3665: // Falcon Wing Square
|
||||
x = 9547.91f;
|
||||
y = -6809.9f;
|
||||
z = 27.96f;
|
||||
o = 3.4f;
|
||||
path = 235436;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,14 +401,6 @@ struct npc_costumed_orphan_matron : public ScriptedAI
|
||||
}
|
||||
}
|
||||
|
||||
uint32 GetData(uint32 param) const override
|
||||
{
|
||||
if (param == DATA_ALLOW_START)
|
||||
return allowQuest;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (eventStarted)
|
||||
@@ -423,67 +413,78 @@ struct npc_costumed_orphan_matron : public ScriptedAI
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool OnGossipHello(Player* player, Creature* creature)
|
||||
{
|
||||
QuestRelationBounds pObjectQR = sObjectMgr->GetCreatureQuestRelationBounds(creature->GetEntry());
|
||||
QuestRelationBounds pObjectQIR = sObjectMgr->GetCreatureQuestInvolvedRelationBounds(creature->GetEntry());
|
||||
|
||||
QuestMenu& qm = player->PlayerTalkClass->GetQuestMenu();
|
||||
qm.ClearMenu();
|
||||
|
||||
for (QuestRelations::const_iterator i = pObjectQIR.first; i != pObjectQIR.second; ++i)
|
||||
void sGossipHello(Player* player) override
|
||||
{
|
||||
uint32 quest_id = i->second;
|
||||
QuestStatus status = player->GetQuestStatus(quest_id);
|
||||
if (status == QUEST_STATUS_COMPLETE)
|
||||
qm.AddMenuItem(quest_id, 4);
|
||||
else if (status == QUEST_STATUS_INCOMPLETE)
|
||||
qm.AddMenuItem(quest_id, 4);
|
||||
}
|
||||
QuestRelationBounds pObjectQR = sObjectMgr->GetCreatureQuestRelationBounds(me->GetEntry());
|
||||
QuestRelationBounds pObjectQIR = sObjectMgr->GetCreatureQuestInvolvedRelationBounds(me->GetEntry());
|
||||
|
||||
for (QuestRelations::const_iterator i = pObjectQR.first; i != pObjectQR.second; ++i)
|
||||
{
|
||||
uint32 quest_id = i->second;
|
||||
Quest const* pQuest = sObjectMgr->GetQuestTemplate(quest_id);
|
||||
if (!pQuest)
|
||||
continue;
|
||||
QuestMenu& qm = player->PlayerTalkClass->GetQuestMenu();
|
||||
qm.ClearMenu();
|
||||
|
||||
if (!player->CanTakeQuest(pQuest, false))
|
||||
continue;
|
||||
else if (player->GetQuestStatus(quest_id) == QUEST_STATUS_NONE)
|
||||
for (QuestRelations::const_iterator i = pObjectQIR.first; i != pObjectQIR.second; ++i)
|
||||
{
|
||||
switch (quest_id)
|
||||
uint32 quest_id = i->second;
|
||||
QuestStatus status = player->GetQuestStatus(quest_id);
|
||||
if (status == QUEST_STATUS_COMPLETE)
|
||||
{
|
||||
case QUEST_LET_THE_FIRES_COME_A:
|
||||
case QUEST_LET_THE_FIRES_COME_H:
|
||||
if (!creature->AI()->GetData(DATA_ALLOW_START))
|
||||
qm.AddMenuItem(quest_id, 2);
|
||||
break;
|
||||
case QUEST_STOP_THE_FIRES_A:
|
||||
case QUEST_STOP_THE_FIRES_H:
|
||||
if (creature->AI()->GetData(DATA_ALLOW_START))
|
||||
qm.AddMenuItem(quest_id, 2);
|
||||
break;
|
||||
default:
|
||||
qm.AddMenuItem(quest_id, 2);
|
||||
break;
|
||||
qm.AddMenuItem(quest_id, 4);
|
||||
}
|
||||
else if (status == QUEST_STATUS_INCOMPLETE)
|
||||
{
|
||||
qm.AddMenuItem(quest_id, 4);
|
||||
}
|
||||
}
|
||||
|
||||
for (QuestRelations::const_iterator i = pObjectQR.first; i != pObjectQR.second; ++i)
|
||||
{
|
||||
uint32 quest_id = i->second;
|
||||
Quest const* pQuest = sObjectMgr->GetQuestTemplate(quest_id);
|
||||
if (!pQuest)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!player->CanTakeQuest(pQuest, false))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (player->GetQuestStatus(quest_id) == QUEST_STATUS_NONE)
|
||||
{
|
||||
switch (quest_id)
|
||||
{
|
||||
case QUEST_LET_THE_FIRES_COME_A:
|
||||
case QUEST_LET_THE_FIRES_COME_H:
|
||||
if (!allowQuest)
|
||||
{
|
||||
qm.AddMenuItem(quest_id, 2);
|
||||
}
|
||||
break;
|
||||
case QUEST_STOP_THE_FIRES_A:
|
||||
case QUEST_STOP_THE_FIRES_H:
|
||||
if (allowQuest)
|
||||
{
|
||||
qm.AddMenuItem(quest_id, 2);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
qm.AddMenuItem(quest_id, 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
player->SendPreparedQuest(me->GetGUID());
|
||||
}
|
||||
|
||||
player->SendPreparedQuest(creature->GetGUID());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnQuestAccept(Player* /*player*/, Creature* creature, Quest const* quest)
|
||||
{
|
||||
if ((quest->GetQuestId() == QUEST_LET_THE_FIRES_COME_A || quest->GetQuestId() == QUEST_LET_THE_FIRES_COME_H) && !creature->AI()->GetData(DATA_ALLOW_START))
|
||||
creature->AI()->DoAction(ACTION_START_EVENT);
|
||||
|
||||
return true;
|
||||
}
|
||||
void sQuestAccept(Player* /*player*/, Quest const* quest) override
|
||||
{
|
||||
if ((quest->GetQuestId() == QUEST_LET_THE_FIRES_COME_A || quest->GetQuestId() == QUEST_LET_THE_FIRES_COME_H) && !allowQuest)
|
||||
{
|
||||
DoAction(ACTION_START_EVENT);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct npc_soh_fire_trigger : public NullCreatureAI
|
||||
{
|
||||
|
||||
@@ -354,7 +354,7 @@ public:
|
||||
me->AttackStop();
|
||||
me->SetReactState(REACT_PASSIVE);
|
||||
me->StopMoving();
|
||||
DoResetThreat();
|
||||
DoResetThreatList();
|
||||
me->GetMotionMaster()->MovePoint(10, OnyxiaMoveData[0].x, OnyxiaMoveData[0].y, OnyxiaMoveData[0].z);
|
||||
break;
|
||||
}
|
||||
@@ -404,7 +404,7 @@ public:
|
||||
Talk(SAY_PHASE_3_TRANS);
|
||||
me->SendMeleeAttackStop(me->GetVictim());
|
||||
me->GetMotionMaster()->MoveLand(13, OnyxiaMoveData[0].x + 1.0f, OnyxiaMoveData[0].y, OnyxiaMoveData[0].z, 12.0f);
|
||||
DoResetThreat();
|
||||
DoResetThreatList();
|
||||
break;
|
||||
}
|
||||
case EVENT_SPELL_FIREBALL_FIRST:
|
||||
|
||||
@@ -221,7 +221,7 @@ struct boss_ayamiss : public BossAI
|
||||
me->SetCanFly(false);
|
||||
me->SetDisableGravity(false);
|
||||
me->GetMotionMaster()->MovePath(me->GetEntry() * 10, false);
|
||||
DoResetThreat();
|
||||
DoResetThreatList();
|
||||
_scheduler.CancelGroup(PHASE_AIR);
|
||||
}
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ struct boss_buru : public BossAI
|
||||
events.ScheduleEvent(EVENT_GATHERING_SPEED, 2s);
|
||||
if (Unit* victim = SelectTarget(SelectTargetMethod::Random, 0, 0.f, true))
|
||||
{
|
||||
DoResetThreat();
|
||||
DoResetThreatList();
|
||||
AttackStart(victim);
|
||||
me->AddThreat(victim, 1000000.f);
|
||||
Talk(EMOTE_TARGET, victim);
|
||||
@@ -143,7 +143,7 @@ struct boss_buru : public BossAI
|
||||
events.Reset();
|
||||
_phase = PHASE_TRANSFORM;
|
||||
instance->SetData(DATA_BURU_PHASE, _phase);
|
||||
DoResetThreat();
|
||||
DoResetThreatList();
|
||||
events.ScheduleEvent(EVENT_CREEPING_PLAGUE, 2s);
|
||||
DoCastSelf(SPELL_BURU_TRANSFORM);
|
||||
}
|
||||
|
||||
@@ -79,15 +79,29 @@ public:
|
||||
|
||||
void EnterCombatWithTrio(Unit* who)
|
||||
{
|
||||
BossAI::EnterCombat(who);
|
||||
|
||||
if (Creature* vem = instance->GetCreature(DATA_VEM))
|
||||
{
|
||||
if (vem->GetGUID() != me->GetGUID())
|
||||
{
|
||||
vem->GetAI()->AttackStart(who);
|
||||
}
|
||||
}
|
||||
if (Creature* kri = instance->GetCreature(DATA_KRI))
|
||||
{
|
||||
if (kri->GetGUID() != me->GetGUID())
|
||||
{
|
||||
kri->GetAI()->AttackStart(who);
|
||||
}
|
||||
}
|
||||
if (Creature* yauj = instance->GetCreature(DATA_YAUJ))
|
||||
{
|
||||
if (yauj->GetGUID() != me->GetGUID())
|
||||
{
|
||||
yauj->GetAI()->AttackStart(who);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EvadeAllBosses(EvadeReason why)
|
||||
@@ -152,7 +166,7 @@ public:
|
||||
me->SetSpeed(MOVE_RUN, 15.f/7.f); // From sniffs
|
||||
DoCastSelf(SPELL_FULL_HEAL, true);
|
||||
if (me->GetThreatMgr().GetThreatListSize())
|
||||
DoResetThreat();
|
||||
DoResetThreatList();
|
||||
if (Creature* dying = instance->GetCreature(_creatureDying))
|
||||
{
|
||||
dying->AI()->DoAction(ACTION_EXPLODE);
|
||||
@@ -405,7 +419,7 @@ struct boss_yauj : public boss_bug_trio
|
||||
.Schedule(12s, [this](TaskContext context)
|
||||
{
|
||||
DoCastAOE(SPELL_FEAR);
|
||||
DoResetThreat();
|
||||
DoResetThreatList();
|
||||
context.Repeat(20600ms);
|
||||
})
|
||||
.Schedule(11s, 14500ms, [this](TaskContext context)
|
||||
|
||||
@@ -25,6 +25,7 @@ EndScriptData */
|
||||
#include "Player.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SpellScript.h"
|
||||
#include "TaskScheduler.h"
|
||||
#include "temple_of_ahnqiraj.h"
|
||||
|
||||
@@ -101,6 +102,7 @@ enum Misc
|
||||
{
|
||||
MAX_TENTACLE_GROUPS = 5,
|
||||
NPC_TRIGGER = 15384,
|
||||
NPC_EXIT_TRIGGER = 15800
|
||||
};
|
||||
|
||||
enum Yells
|
||||
@@ -149,6 +151,7 @@ struct boss_eye_of_cthun : public BossAI
|
||||
boss_eye_of_cthun(Creature* creature) : BossAI(creature, DATA_CTHUN)
|
||||
{
|
||||
SetCombatMovement(false);
|
||||
me->m_SightDistance = 90.f;
|
||||
}
|
||||
|
||||
void Reset() override
|
||||
@@ -187,7 +190,7 @@ struct boss_eye_of_cthun : public BossAI
|
||||
|
||||
void EnterCombat(Unit* who) override
|
||||
{
|
||||
ScheduleTasks();
|
||||
ScheduleTasks(true);
|
||||
BossAI::EnterCombat(who);
|
||||
_beamTarget = who->GetGUID();
|
||||
}
|
||||
@@ -197,7 +200,7 @@ struct boss_eye_of_cthun : public BossAI
|
||||
if (who->GetTypeId() == TYPEID_PLAYER && !me->IsInCombat())
|
||||
{
|
||||
// Z checks are necessary here because AQ maps do funky stuff.
|
||||
if (me->IsWithinLOSInMap(who) && me->IsWithinDist2d(who, 50.0f) && who->GetPositionZ() > 100.0f)
|
||||
if (me->IsWithinLOSInMap(who) && me->IsWithinDist2d(who, 90.0f) && who->GetPositionZ() > 100.0f)
|
||||
{
|
||||
AttackStart(who);
|
||||
}
|
||||
@@ -218,12 +221,22 @@ struct boss_eye_of_cthun : public BossAI
|
||||
}
|
||||
}
|
||||
|
||||
void ScheduleTasks()
|
||||
void JustSummoned(Creature* summon) override
|
||||
{
|
||||
summons.Summon(summon);
|
||||
|
||||
if (Creature* cthun = instance->GetCreature(DATA_CTHUN))
|
||||
{
|
||||
cthun->AI()->JustSummoned(summon);
|
||||
}
|
||||
}
|
||||
|
||||
void ScheduleTasks(bool onEngage = false)
|
||||
{
|
||||
_scheduler.
|
||||
Schedule(3s, [this](TaskContext task)
|
||||
Schedule(3s, [this, onEngage](TaskContext task)
|
||||
{
|
||||
if (task.GetRepeatCounter() < 3)
|
||||
if (task.GetRepeatCounter() < 3 && onEngage)
|
||||
{
|
||||
if (Unit* target = ObjectAccessor::GetUnit(*me, _beamTarget))
|
||||
{
|
||||
@@ -249,7 +262,7 @@ struct boss_eye_of_cthun : public BossAI
|
||||
{
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true))
|
||||
{
|
||||
if (Creature* tentacle = me->SummonCreature(NPC_CLAW_TENTACLE, *target, TEMPSUMMON_CORPSE_DESPAWN, 1000))
|
||||
if (Creature* tentacle = me->SummonCreature(NPC_CLAW_TENTACLE, *target, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000))
|
||||
{
|
||||
tentacle->AI()->AttackStart(target);
|
||||
}
|
||||
@@ -273,6 +286,9 @@ struct boss_eye_of_cthun : public BossAI
|
||||
me->InterruptNonMeleeSpells(false);
|
||||
me->SetTarget(ObjectGuid::Empty);
|
||||
|
||||
//Freeze animation
|
||||
DoCast(me, SPELL_FREEZE_ANIM, true);
|
||||
|
||||
_scheduler.Schedule(1s, [this](TaskContext /*task*/)
|
||||
{
|
||||
//Select random target for dark beam to start on
|
||||
@@ -283,14 +299,9 @@ struct boss_eye_of_cthun : public BossAI
|
||||
DarkGlareTick = 0;
|
||||
ClockWise = RAND(true, false);
|
||||
|
||||
me->SetTarget(target->GetGUID());
|
||||
|
||||
//Add red coloration to C'thun
|
||||
DoCast(me, SPELL_RED_COLORATION, true);
|
||||
|
||||
//Freeze animation
|
||||
DoCast(me, SPELL_FREEZE_ANIM, true);
|
||||
|
||||
me->StopMoving();
|
||||
me->SetFacingToObject(target);
|
||||
me->SetOrientation(DarkGlareAngle);
|
||||
@@ -301,19 +312,12 @@ struct boss_eye_of_cthun : public BossAI
|
||||
me->SetTarget(ObjectGuid::Empty);
|
||||
me->StopMoving();
|
||||
|
||||
if (ClockWise)
|
||||
{
|
||||
me->SetFacingTo(DarkGlareAngle + DarkGlareTick * float(M_PI) / 35);
|
||||
me->SetOrientation(DarkGlareAngle + DarkGlareTick * float(M_PI) / 35);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
me->SetFacingTo(DarkGlareAngle - DarkGlareTick * float(M_PI) / 35);
|
||||
me->SetOrientation(DarkGlareAngle - DarkGlareTick * float(M_PI) / 35);
|
||||
}
|
||||
float angle = ClockWise ? DarkGlareAngle + DarkGlareTick * float(M_PI) / 35 : DarkGlareAngle - DarkGlareTick * float(M_PI) / 35;
|
||||
me->SetFacingTo(angle);
|
||||
me->SetOrientation(angle);
|
||||
|
||||
DoCastSelf(SPELL_DARK_GLARE);
|
||||
|
||||
++DarkGlareTick;
|
||||
|
||||
if (tasker.GetRepeatCounter() >= 35)
|
||||
@@ -344,7 +348,7 @@ struct boss_eye_of_cthun : public BossAI
|
||||
void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override
|
||||
{
|
||||
//Only if it will kill
|
||||
if (damage < me->GetHealth())
|
||||
if (damage < me->GetHealth() || !me->GetHealth())
|
||||
return;
|
||||
|
||||
//Fake death in phase 0 or 1 (green beam or dark glare phase)
|
||||
@@ -366,10 +370,13 @@ struct boss_eye_of_cthun : public BossAI
|
||||
me->RemoveAllAuras();
|
||||
_scheduler.CancelAll();
|
||||
|
||||
if (Creature* cthun = instance->GetCreature(DATA_CTHUN))
|
||||
me->m_Events.AddEventAtOffset([this]()
|
||||
{
|
||||
cthun->AI()->DoAction(ACTION_START_PHASE_TWO);
|
||||
}
|
||||
if (Creature* cthun = instance->GetCreature(DATA_CTHUN))
|
||||
{
|
||||
cthun->AI()->DoAction(ACTION_START_PHASE_TWO);
|
||||
}
|
||||
}, 3s);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -445,10 +452,9 @@ struct boss_cthun : public BossAI
|
||||
|
||||
target->m_Events.AddEventAtOffset([target, this]()
|
||||
{
|
||||
DoCast(target, SPELL_DIGESTIVE_ACID, true);
|
||||
DoTeleportPlayer(target, STOMACH_X, STOMACH_Y, STOMACH_Z, STOMACH_O);
|
||||
target->RemoveAurasDueToSpell(SPELL_MIND_FLAY);
|
||||
//Cast digestive acid on them
|
||||
DoCast(target, SPELL_DIGESTIVE_ACID, true);
|
||||
}, 3800ms);
|
||||
}
|
||||
|
||||
@@ -466,7 +472,7 @@ struct boss_cthun : public BossAI
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, -SPELL_DIGESTIVE_ACID))
|
||||
{
|
||||
//Spawn claw tentacle on the random target
|
||||
if (Creature* spawned = me->SummonCreature(NPC_GIANT_CLAW_TENTACLE, *target, TEMPSUMMON_CORPSE_DESPAWN, 500))
|
||||
if (Creature* spawned = me->SummonCreature(NPC_GIANT_CLAW_TENTACLE, *target, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000))
|
||||
{
|
||||
spawned->AI()->AttackStart(target);
|
||||
}
|
||||
@@ -478,7 +484,7 @@ struct boss_cthun : public BossAI
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, -SPELL_DIGESTIVE_ACID))
|
||||
{
|
||||
//Spawn claw tentacle on the random target
|
||||
if (Creature* spawned = me->SummonCreature(NPC_GIANT_EYE_TENTACLE, *target, TEMPSUMMON_CORPSE_DESPAWN, 500))
|
||||
if (Creature* spawned = me->SummonCreature(NPC_GIANT_EYE_TENTACLE, *target, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000))
|
||||
{
|
||||
spawned->AI()->AttackStart(target);
|
||||
}
|
||||
@@ -534,6 +540,13 @@ struct boss_cthun : public BossAI
|
||||
{
|
||||
pPortal->DespawnOrUnsummon();
|
||||
}
|
||||
|
||||
if (Creature* eye = instance->GetCreature(DATA_EYE_OF_CTHUN))
|
||||
{
|
||||
eye->DespawnOrUnsummon();
|
||||
}
|
||||
|
||||
instance->SetData(DATA_CTHUN_PHASE, PHASE_CTHUN_DONE);
|
||||
}
|
||||
|
||||
void SummonedCreatureDies(Creature* creature, Unit* /*killer*/) override
|
||||
@@ -560,11 +573,11 @@ struct boss_cthun : public BossAI
|
||||
ScheduleTasks();
|
||||
//Remove purple coloration
|
||||
me->RemoveAurasDueToSpell(SPELL_PURPLE_COLORATION);
|
||||
|
||||
DoCastSelf(SPELL_CARAPACE_CTHUN, true);
|
||||
//Spawn flesh tentacle
|
||||
for (uint8 i = 0; i < 2; i++)
|
||||
{
|
||||
me->SummonCreature(NPC_FLESH_TENTACLE, FleshTentaclePos[i], TEMPSUMMON_CORPSE_DESPAWN);
|
||||
me->SummonCreature(NPC_FLESH_TENTACLE, FleshTentaclePos[i], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -589,11 +602,14 @@ struct npc_eye_tentacle : public ScriptedAI
|
||||
portal->SetReactState(REACT_PASSIVE);
|
||||
_portalGUID = portal->GetGUID();
|
||||
|
||||
if (Unit* summoner = me->ToTempSummon()->GetSummonerUnit())
|
||||
if (me->ToTempSummon())
|
||||
{
|
||||
if (Creature* creature = summoner->ToCreature())
|
||||
if (Unit* summoner = me->ToTempSummon()->GetSummonerUnit())
|
||||
{
|
||||
creature->AI()->JustSummoned(portal);
|
||||
if (Creature* creature = summoner->ToCreature())
|
||||
{
|
||||
creature->AI()->JustSummoned(portal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -660,11 +676,14 @@ struct npc_claw_tentacle : public ScriptedAI
|
||||
portal->SetReactState(REACT_PASSIVE);
|
||||
_portalGUID = portal->GetGUID();
|
||||
|
||||
if (Unit* summoner = me->ToTempSummon()->GetSummonerUnit())
|
||||
if (me->ToTempSummon())
|
||||
{
|
||||
if (Creature* creature = summoner->ToCreature())
|
||||
if (Unit* summoner = me->ToTempSummon()->GetSummonerUnit())
|
||||
{
|
||||
creature->AI()->JustSummoned(portal);
|
||||
if (Creature* creature = summoner->ToCreature())
|
||||
{
|
||||
creature->AI()->JustSummoned(portal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -727,14 +746,19 @@ struct npc_giant_claw_tentacle : public ScriptedAI
|
||||
portal->SetReactState(REACT_PASSIVE);
|
||||
_portalGUID = portal->GetGUID();
|
||||
|
||||
if (Unit* summoner = me->ToTempSummon()->GetSummonerUnit())
|
||||
if (me->ToTempSummon())
|
||||
{
|
||||
if (Creature* creature = summoner->ToCreature())
|
||||
if (Unit* summoner = me->ToTempSummon()->GetSummonerUnit())
|
||||
{
|
||||
creature->AI()->JustSummoned(portal);
|
||||
if (Creature* creature = summoner->ToCreature())
|
||||
{
|
||||
creature->AI()->JustSummoned(portal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_canAttack = false;
|
||||
}
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
@@ -756,21 +780,10 @@ struct npc_giant_claw_tentacle : public ScriptedAI
|
||||
void EnterCombat(Unit* /*who*/) override
|
||||
{
|
||||
DoZoneInCombat();
|
||||
|
||||
_scheduler.Schedule(2s, [this](TaskContext context)
|
||||
{
|
||||
DoCastVictim(SPELL_HAMSTRING);
|
||||
context.Repeat(10s);
|
||||
}).Schedule(5s, [this](TaskContext context)
|
||||
{
|
||||
DoCastSelf(SPELL_THRASH);
|
||||
context.Repeat(10s);
|
||||
});
|
||||
|
||||
ScheduleMeleeCheck();
|
||||
ScheduleTasks();
|
||||
}
|
||||
|
||||
void ScheduleMeleeCheck()
|
||||
void ScheduleTasks()
|
||||
{
|
||||
// Check if a target is in melee range
|
||||
_scheduler.Schedule(10s, [this](TaskContext task)
|
||||
@@ -792,6 +805,17 @@ struct npc_giant_claw_tentacle : public ScriptedAI
|
||||
}
|
||||
|
||||
task.Repeat();
|
||||
}).Schedule(2s, [this](TaskContext context)
|
||||
{
|
||||
DoCastVictim(SPELL_HAMSTRING);
|
||||
context.Repeat(10s);
|
||||
}).Schedule(5s, [this](TaskContext context)
|
||||
{
|
||||
DoCastSelf(SPELL_THRASH);
|
||||
context.Repeat(10s);
|
||||
}).Schedule(3s, [this](TaskContext /*context*/)
|
||||
{
|
||||
_canAttack = true;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -813,6 +837,7 @@ struct npc_giant_claw_tentacle : public ScriptedAI
|
||||
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE);
|
||||
|
||||
_scheduler.CancelAll();
|
||||
_canAttack = false;
|
||||
|
||||
_scheduler.Schedule(5s, [this](TaskContext /*task*/)
|
||||
{
|
||||
@@ -837,7 +862,7 @@ struct npc_giant_claw_tentacle : public ScriptedAI
|
||||
DoCastAOE(SPELL_MASSIVE_GROUND_RUPTURE, true);
|
||||
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE);
|
||||
|
||||
ScheduleMeleeCheck();
|
||||
ScheduleTasks();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -849,12 +874,16 @@ struct npc_giant_claw_tentacle : public ScriptedAI
|
||||
|
||||
_scheduler.Update(diff);
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
if (_canAttack)
|
||||
{
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
TaskScheduler _scheduler;
|
||||
ObjectGuid _portalGUID;
|
||||
bool _canAttack;
|
||||
};
|
||||
|
||||
struct npc_giant_eye_tentacle : public ScriptedAI
|
||||
@@ -868,11 +897,14 @@ struct npc_giant_eye_tentacle : public ScriptedAI
|
||||
portal->SetReactState(REACT_PASSIVE);
|
||||
_portalGUID = portal->GetGUID();
|
||||
|
||||
if (Unit* summoner = me->ToTempSummon()->GetSummonerUnit())
|
||||
if (me->ToTempSummon())
|
||||
{
|
||||
if (Creature* creature = summoner->ToCreature())
|
||||
if (Unit* summoner = me->ToTempSummon()->GetSummonerUnit())
|
||||
{
|
||||
creature->AI()->JustSummoned(portal);
|
||||
if (Creature* creature = summoner->ToCreature())
|
||||
{
|
||||
creature->AI()->JustSummoned(portal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -921,6 +953,43 @@ private:
|
||||
ObjectGuid _portalGUID;
|
||||
};
|
||||
|
||||
class spell_cthun_dark_glare : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_cthun_dark_glare);
|
||||
|
||||
void FilterTargets(std::list<WorldObject*>& targets)
|
||||
{
|
||||
Unit* caster = GetCaster();
|
||||
targets.remove_if([caster](WorldObject const* target) { return !caster->HasInLine(target, 5.0f); });
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_cthun_dark_glare::FilterTargets, EFFECT_ALL, TARGET_UNIT_CONE_ENEMY_24);
|
||||
}
|
||||
};
|
||||
|
||||
class spell_cthun_digestive_acid : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_cthun_digestive_acid);
|
||||
|
||||
void OnPeriodic(AuraEffect const* /*aurEff*/)
|
||||
{
|
||||
if (InstanceScript* instance = GetUnitOwner()->GetInstanceScript())
|
||||
{
|
||||
if (Creature* cthun = instance->GetCreature(DATA_CTHUN))
|
||||
{
|
||||
cthun->CastSpell(GetUnitOwner(), SPELL_DIGESTIVE_ACID, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectPeriodic += AuraEffectPeriodicFn(spell_cthun_digestive_acid::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE);
|
||||
}
|
||||
};
|
||||
|
||||
// 4033 - At C'thun's stomach
|
||||
class at_cthun_stomach_exit : public AreaTriggerScript
|
||||
{
|
||||
@@ -933,30 +1002,37 @@ public:
|
||||
{
|
||||
if (Creature* cthun = instance->GetCreature(DATA_CTHUN))
|
||||
{
|
||||
player->CastSpell(player, SPELL_RUBBLE_ROCKY, true);
|
||||
|
||||
player->m_Events.AddEventAtOffset([player]()
|
||||
if (Creature* trigger = player->FindNearestCreature(NPC_TRIGGER, 15.0f))
|
||||
{
|
||||
if (Creature* trigger = player->FindNearestCreature(NPC_TRIGGER, 15.0f))
|
||||
trigger->CastSpell(player, SPELL_EXIT_STOMACH, true);
|
||||
|
||||
if (Creature* exittrigger = player->FindNearestCreature(NPC_EXIT_TRIGGER, 15.0f))
|
||||
{
|
||||
trigger->CastSpell(player, SPELL_EXIT_STOMACH, true);
|
||||
exittrigger->CastSpell(player, SPELL_RUBBLE_ROCKY, true);
|
||||
}
|
||||
}, 3s);
|
||||
|
||||
player->m_Events.AddEventAtOffset([player]()
|
||||
{
|
||||
player->JumpTo(0.0f, 80.0f, false);
|
||||
}, 5s);
|
||||
}
|
||||
|
||||
player->m_Events.AddEventAtOffset([player, cthun]()
|
||||
{
|
||||
if (cthun)
|
||||
if (player->FindNearestCreature(NPC_EXIT_TRIGGER, 10.0f))
|
||||
{
|
||||
player->NearTeleportTo(cthun->GetPositionX(), cthun->GetPositionY(), cthun->GetPositionZ() + 10, float(rand32() % 6));
|
||||
}
|
||||
player->JumpTo(0.0f, 80.0f, false);
|
||||
|
||||
player->RemoveAurasDueToSpell(SPELL_DIGESTIVE_ACID);
|
||||
}, 6s);
|
||||
player->m_Events.AddEventAtOffset([player, cthun]()
|
||||
{
|
||||
if (cthun)
|
||||
{
|
||||
player->NearTeleportTo(cthun->GetPositionX(), cthun->GetPositionY(), cthun->GetPositionZ() + 10, float(rand32() % 6));
|
||||
}
|
||||
|
||||
player->RemoveAurasDueToSpell(SPELL_DIGESTIVE_ACID);
|
||||
}, 1s);
|
||||
}
|
||||
else
|
||||
{
|
||||
player->m_Events.KillAllEvents(false);
|
||||
}
|
||||
}, 3s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -975,7 +1051,10 @@ public:
|
||||
{
|
||||
if (Creature* cthun = instance->GetCreature(DATA_CTHUN))
|
||||
{
|
||||
cthun->CastSpell(player, SPELL_SPIT_OUT, true);
|
||||
if (cthun->IsAlive())
|
||||
{
|
||||
cthun->CastSpell(player, SPELL_SPIT_OUT, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -991,6 +1070,8 @@ void AddSC_boss_cthun()
|
||||
RegisterTempleOfAhnQirajCreatureAI(npc_claw_tentacle);
|
||||
RegisterTempleOfAhnQirajCreatureAI(npc_giant_claw_tentacle);
|
||||
RegisterTempleOfAhnQirajCreatureAI(npc_giant_eye_tentacle);
|
||||
RegisterSpellScript(spell_cthun_dark_glare);
|
||||
RegisterSpellScript(spell_cthun_digestive_acid);
|
||||
new at_cthun_stomach_exit();
|
||||
new at_cthun_center();
|
||||
}
|
||||
|
||||
@@ -49,7 +49,10 @@ enum Events
|
||||
|
||||
struct boss_huhuran : public BossAI
|
||||
{
|
||||
boss_huhuran(Creature* creature) : BossAI(creature, DATA_HUHURAN) { }
|
||||
boss_huhuran(Creature* creature) : BossAI(creature, DATA_HUHURAN)
|
||||
{
|
||||
me->m_CombatDistance = 90.f;
|
||||
}
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
@@ -58,8 +61,9 @@ struct boss_huhuran : public BossAI
|
||||
_hardEnrage = false;
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* /*who*/) override
|
||||
void EnterCombat(Unit* who) override
|
||||
{
|
||||
BossAI::EnterCombat(who);
|
||||
events.ScheduleEvent(EVENT_FRENZY, 12s, 21s);
|
||||
events.ScheduleEvent(EVENT_WYVERN_STING, 25s, 43s);
|
||||
events.ScheduleEvent(EVENT_ACID_SPIT, 1s, 20s);
|
||||
@@ -153,6 +157,7 @@ class spell_huhuran_wyvern_sting : public AuraScript
|
||||
};
|
||||
|
||||
// 26052 - Poison Bolt
|
||||
// 26180 - Wyvern Sting
|
||||
class spell_huhuran_poison_bolt : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_huhuran_poison_bolt);
|
||||
|
||||
@@ -78,8 +78,15 @@ struct npc_ouro_spawner : public ScriptedAI
|
||||
// Spawn Ouro on LoS check
|
||||
if (!hasSummoned && who->GetTypeId() == TYPEID_PLAYER && me->IsWithinDistInMap(who, 40.0f) && !who->ToPlayer()->IsGameMaster())
|
||||
{
|
||||
DoCastSelf(SPELL_SUMMON_OURO);
|
||||
hasSummoned = true;
|
||||
if (InstanceScript* instance = me->GetInstanceScript())
|
||||
{
|
||||
Creature* ouro = instance->GetCreature(DATA_OURO);
|
||||
if (instance->GetBossState(DATA_OURO) != IN_PROGRESS && !ouro)
|
||||
{
|
||||
DoCastSelf(SPELL_SUMMON_OURO);
|
||||
hasSummoned = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScriptedAI::MoveInLineOfSight(who);
|
||||
@@ -105,6 +112,11 @@ struct boss_ouro : public BossAI
|
||||
_scheduler.SetValidator([this] { return !me->HasUnitState(UNIT_STATE_CASTING); });
|
||||
}
|
||||
|
||||
bool CanAIAttack(Unit const* victim) const override
|
||||
{
|
||||
return me->IsWithinMeleeRange(victim);
|
||||
}
|
||||
|
||||
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType, SpellSchoolMask) override
|
||||
{
|
||||
if (me->HealthBelowPctDamaged(20, damage) && !_enraged)
|
||||
@@ -129,6 +141,9 @@ struct boss_ouro : public BossAI
|
||||
|
||||
void Submerge()
|
||||
{
|
||||
if (_enraged || _submerged)
|
||||
return;
|
||||
|
||||
me->AttackStop();
|
||||
me->SetReactState(REACT_PASSIVE);
|
||||
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE);
|
||||
@@ -180,7 +195,9 @@ struct boss_ouro : public BossAI
|
||||
void SpellHitTarget(Unit* target, SpellInfo const* spellInfo) override
|
||||
{
|
||||
if (spellInfo->Id == SPELL_SAND_BLAST && target)
|
||||
me->GetThreatMgr().ModifyThreatByPercent(target, 100);
|
||||
{
|
||||
me->GetThreatMgr().ModifyThreatByPercent(target, -100);
|
||||
}
|
||||
}
|
||||
|
||||
void Emerge()
|
||||
@@ -192,7 +209,21 @@ struct boss_ouro : public BossAI
|
||||
_scheduler
|
||||
.Schedule(20s, GROUP_EMERGED, [this](TaskContext context)
|
||||
{
|
||||
DoCastVictim(SPELL_SAND_BLAST);
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, 0, 0.0f, true))
|
||||
{
|
||||
me->SetTarget(target->GetGUID());
|
||||
}
|
||||
|
||||
DoCastAOE(SPELL_SAND_BLAST);
|
||||
|
||||
me->m_Events.AddEventAtOffset([this]()
|
||||
{
|
||||
if (Unit* victim = me->GetVictim())
|
||||
{
|
||||
me->SetTarget(victim->GetGUID());
|
||||
}
|
||||
}, 3s);
|
||||
|
||||
context.Repeat();
|
||||
})
|
||||
.Schedule(22s, GROUP_EMERGED, [this](TaskContext context)
|
||||
@@ -206,6 +237,9 @@ struct boss_ouro : public BossAI
|
||||
})
|
||||
.Schedule(3s, GROUP_PHASE_TRANSITION, [this](TaskContext context)
|
||||
{
|
||||
if (_enraged)
|
||||
return;
|
||||
|
||||
if (!IsPlayerWithinMeleeRange() && !_submerged)
|
||||
{
|
||||
if (_submergeMelee < 10)
|
||||
@@ -214,8 +248,7 @@ struct boss_ouro : public BossAI
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_enraged)
|
||||
Submerge();
|
||||
Submerge();
|
||||
_submergeMelee = 0;
|
||||
}
|
||||
}
|
||||
@@ -240,11 +273,14 @@ struct boss_ouro : public BossAI
|
||||
|
||||
void EnterEvadeMode(EvadeReason /*why*/) override
|
||||
{
|
||||
DoCastSelf(SPELL_OURO_SUBMERGE_VISUAL);
|
||||
me->DespawnOrUnsummon(1000);
|
||||
instance->SetBossState(DATA_OURO, FAIL);
|
||||
if (GameObject* base = me->FindNearestGameObject(GO_SANDWORM_BASE, 200.f))
|
||||
base->DespawnOrUnsummon();
|
||||
if (me->GetThreatMgr().GetThreatList().empty())
|
||||
{
|
||||
DoCastSelf(SPELL_OURO_SUBMERGE_VISUAL);
|
||||
me->DespawnOrUnsummon(1000);
|
||||
instance->SetBossState(DATA_OURO, FAIL);
|
||||
if (GameObject* base = me->FindNearestGameObject(GO_SANDWORM_BASE, 200.f))
|
||||
base->DespawnOrUnsummon();
|
||||
}
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* who) override
|
||||
@@ -256,9 +292,7 @@ struct boss_ouro : public BossAI
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
//Return since we have no target
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
UpdateVictim();
|
||||
|
||||
_scheduler.Update(diff, [this]
|
||||
{
|
||||
@@ -318,7 +352,7 @@ struct npc_dirt_mound : ScriptedAI
|
||||
|
||||
void ChaseNewTarget()
|
||||
{
|
||||
DoResetThreat();
|
||||
DoResetThreatList();
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 200.f, true))
|
||||
{
|
||||
me->AddThreat(target, 1000000.f);
|
||||
|
||||
@@ -34,7 +34,6 @@ enum Spells
|
||||
SPELL_ENRAGE = 8269,
|
||||
SPELL_BERSERK = 27680,
|
||||
SPELL_SUNDERING_CLEAVE = 25174,
|
||||
|
||||
// Sartura's Royal Guard
|
||||
SPELL_GUARD_WHIRLWIND = 26038,
|
||||
SPELL_GUARD_KNOCKBACK = 26027
|
||||
@@ -44,20 +43,15 @@ enum events
|
||||
{
|
||||
// Battleguard Sartura
|
||||
EVENT_SARTURA_WHIRLWIND = 1,
|
||||
EVENT_SARTURA_WHIRLWIND_RANDOM = 2,
|
||||
EVENT_SARTURA_WHIRLWIND_END = 3,
|
||||
EVENT_SPELL_BERSERK = 4,
|
||||
EVENT_SARTURA_AGGRO_RESET = 5,
|
||||
EVENT_SARTURA_AGGRO_RESET_END = 6,
|
||||
EVENT_SARTURA_SUNDERING_CLEAVE = 7,
|
||||
|
||||
EVENT_SARTURA_WHIRLWIND_RANDOM,
|
||||
EVENT_SARTURA_WHIRLWIND_END,
|
||||
EVENT_SPELL_BERSERK,
|
||||
EVENT_SARTURA_SUNDERING_CLEAVE,
|
||||
// Sartura's Royal Guard
|
||||
EVENT_GUARD_WHIRLWIND = 8,
|
||||
EVENT_GUARD_WHIRLWIND_RANDOM = 9,
|
||||
EVENT_GUARD_WHIRLWIND_END = 10,
|
||||
EVENT_GUARD_KNOCKBACK = 11,
|
||||
EVENT_GUARD_AGGRO_RESET = 12,
|
||||
EVENT_GUARD_AGGRO_RESET_END = 13
|
||||
EVENT_GUARD_WHIRLWIND,
|
||||
EVENT_GUARD_WHIRLWIND_RANDOM,
|
||||
EVENT_GUARD_WHIRLWIND_END,
|
||||
EVENT_GUARD_KNOCKBACK,
|
||||
};
|
||||
|
||||
struct boss_sartura : public BossAI
|
||||
@@ -74,23 +68,10 @@ struct boss_sartura : public BossAI
|
||||
void Reset() override
|
||||
{
|
||||
_Reset();
|
||||
whirlwind = false;
|
||||
enraged = false;
|
||||
berserked = false;
|
||||
aggroReset = false;
|
||||
MinionReset();
|
||||
_savedTargetGUID.Clear();
|
||||
_savedTargetThreat = 0.f;
|
||||
}
|
||||
|
||||
void MinionReset()
|
||||
{
|
||||
std::list<Creature*> royalGuards;
|
||||
me->GetCreaturesWithEntryInRange(royalGuards, 200.0f, NPC_SARTURA_ROYAL_GUARD);
|
||||
for (Creature* minion : royalGuards)
|
||||
{
|
||||
minion->Respawn();
|
||||
}
|
||||
me->SetReactState(REACT_AGGRESSIVE);
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* who) override
|
||||
@@ -98,8 +79,7 @@ struct boss_sartura : public BossAI
|
||||
BossAI::EnterCombat(who);
|
||||
Talk(SAY_AGGRO);
|
||||
events.ScheduleEvent(EVENT_SARTURA_WHIRLWIND, 12s, 22s);
|
||||
events.ScheduleEvent(EVENT_SARTURA_AGGRO_RESET, urand(45000, 55000));
|
||||
events.ScheduleEvent(EVENT_SPELL_BERSERK, 10 * 60000);
|
||||
events.ScheduleEvent(EVENT_SPELL_BERSERK, 10min);
|
||||
events.ScheduleEvent(EVENT_SARTURA_SUNDERING_CLEAVE, 2400ms, 3s);
|
||||
}
|
||||
|
||||
@@ -135,70 +115,38 @@ struct boss_sartura : public BossAI
|
||||
switch (eventId)
|
||||
{
|
||||
case EVENT_SARTURA_WHIRLWIND:
|
||||
DoCastSelf(SPELL_WHIRLWIND, true);
|
||||
whirlwind = true;
|
||||
events.ScheduleEvent(EVENT_SARTURA_WHIRLWIND_RANDOM, urand(3000, 7000));
|
||||
events.ScheduleEvent(EVENT_SARTURA_WHIRLWIND_END, 15000);
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100.0f, true))
|
||||
{
|
||||
me->GetThreatMgr().ResetAllThreat();
|
||||
me->AddThreat(target, 1000.0f);
|
||||
}
|
||||
DoCastSelf(SPELL_WHIRLWIND);
|
||||
events.ScheduleEvent(EVENT_SARTURA_WHIRLWIND_RANDOM, 2s, 7s);
|
||||
events.ScheduleEvent(EVENT_SARTURA_WHIRLWIND_END, 15s);
|
||||
break;
|
||||
case EVENT_SARTURA_WHIRLWIND_RANDOM:
|
||||
if (whirlwind == true)
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true))
|
||||
{
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100.0f, true))
|
||||
{
|
||||
me->AddThreat(target, 1.0f);
|
||||
me->TauntApply(target);
|
||||
AttackStart(target);
|
||||
}
|
||||
events.RepeatEvent(urand(3000, 7000));
|
||||
me->GetThreatMgr().ResetAllThreat();
|
||||
me->AddThreat(target, 1000.0f);
|
||||
}
|
||||
events.Repeat(2s, 7s);
|
||||
break;
|
||||
case EVENT_SARTURA_WHIRLWIND_END:
|
||||
me->GetThreatMgr().ResetAllThreat();
|
||||
me->SetReactState(REACT_AGGRESSIVE);
|
||||
events.CancelEvent(EVENT_SARTURA_WHIRLWIND_RANDOM);
|
||||
whirlwind = false;
|
||||
events.ScheduleEvent(EVENT_SARTURA_WHIRLWIND, 5s, 11s);
|
||||
break;
|
||||
case EVENT_SARTURA_AGGRO_RESET:
|
||||
if (aggroReset == false)
|
||||
{
|
||||
if (Unit* originalTarget = SelectTarget(SelectTargetMethod::Random, 0))
|
||||
{
|
||||
_savedTargetGUID = originalTarget->GetGUID();
|
||||
_savedTargetThreat = me->GetThreatMgr().GetThreat(originalTarget);
|
||||
me->GetThreatMgr().ModifyThreatByPercent(originalTarget, -100);
|
||||
}
|
||||
aggroReset = true;
|
||||
events.ScheduleEvent(EVENT_SARTURA_AGGRO_RESET_END, 5000);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100.0f, true))
|
||||
{
|
||||
me->AddThreat(target, 1.0f);
|
||||
me->TauntApply(target);
|
||||
AttackStart(target);
|
||||
}
|
||||
}
|
||||
events.RepeatEvent(urand(1000, 2000));
|
||||
break;
|
||||
case EVENT_SARTURA_AGGRO_RESET_END:
|
||||
events.CancelEvent(EVENT_SARTURA_AGGRO_RESET);
|
||||
if (Unit* originalTarget = ObjectAccessor::GetUnit(*me, _savedTargetGUID))
|
||||
{
|
||||
me->GetThreatMgr().AddThreat(originalTarget, _savedTargetThreat);
|
||||
_savedTargetGUID.Clear();
|
||||
}
|
||||
aggroReset = false;
|
||||
events.RescheduleEvent(EVENT_SARTURA_AGGRO_RESET, urand(30000, 40000));
|
||||
break;
|
||||
case EVENT_SPELL_BERSERK:
|
||||
if (!berserked)
|
||||
{
|
||||
DoCastSelf(SPELL_BERSERK);
|
||||
DoCastSelf(SPELL_BERSERK, true);
|
||||
berserked = true;
|
||||
}
|
||||
break;
|
||||
case EVENT_SARTURA_SUNDERING_CLEAVE:
|
||||
if (whirlwind)
|
||||
if (me->HasReactState(REACT_PASSIVE))
|
||||
{
|
||||
Milliseconds whirlwindTimer = events.GetTimeUntilEvent(EVENT_SARTURA_WHIRLWIND_END);
|
||||
events.RescheduleEvent(EVENT_SARTURA_SUNDERING_CLEAVE, whirlwindTimer + 500ms);
|
||||
@@ -213,15 +161,13 @@ struct boss_sartura : public BossAI
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
};
|
||||
|
||||
private:
|
||||
bool whirlwind;
|
||||
bool enraged;
|
||||
bool berserked;
|
||||
bool aggroReset;
|
||||
ObjectGuid _savedTargetGUID;
|
||||
float _savedTargetThreat;
|
||||
};
|
||||
|
||||
struct npc_sartura_royal_guard : public ScriptedAI
|
||||
@@ -231,16 +177,13 @@ struct npc_sartura_royal_guard : public ScriptedAI
|
||||
void Reset() override
|
||||
{
|
||||
events.Reset();
|
||||
whirlwind = false;
|
||||
aggroReset = false;
|
||||
_savedTargetGUID.Clear();
|
||||
_savedTargetThreat = 0.f;
|
||||
|
||||
me->SetReactState(REACT_AGGRESSIVE);
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* /*who*/) override
|
||||
{
|
||||
events.ScheduleEvent(EVENT_GUARD_WHIRLWIND, 6s, 10s);
|
||||
events.ScheduleEvent(EVENT_GUARD_AGGRO_RESET, urand(45000, 55000));
|
||||
events.ScheduleEvent(EVENT_GUARD_KNOCKBACK, 12s, 16s);
|
||||
}
|
||||
|
||||
@@ -256,74 +199,38 @@ struct npc_sartura_royal_guard : public ScriptedAI
|
||||
switch (eventid)
|
||||
{
|
||||
case EVENT_GUARD_WHIRLWIND:
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100.0f, true))
|
||||
{
|
||||
me->GetThreatMgr().ResetAllThreat();
|
||||
me->AddThreat(target, 1000.0f);
|
||||
}
|
||||
DoCastSelf(SPELL_GUARD_WHIRLWIND);
|
||||
whirlwind = true;
|
||||
events.ScheduleEvent(EVENT_GUARD_WHIRLWIND_RANDOM, urand(3000, 7000));
|
||||
events.ScheduleEvent(EVENT_GUARD_WHIRLWIND_RANDOM, 2s, 7s);
|
||||
events.ScheduleEvent(EVENT_GUARD_WHIRLWIND_END, 8s);
|
||||
break;
|
||||
case EVENT_GUARD_WHIRLWIND_RANDOM:
|
||||
if (whirlwind == true)
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true))
|
||||
{
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100.0f, true))
|
||||
{
|
||||
me->AddThreat(target, 1.0f);
|
||||
me->TauntApply(target);
|
||||
AttackStart(target);
|
||||
}
|
||||
events.RepeatEvent(urand(3000, 7000));
|
||||
me->GetThreatMgr().ResetAllThreat();
|
||||
me->AddThreat(target, 1000.0f);
|
||||
}
|
||||
events.Repeat(2s, 7s);
|
||||
break;
|
||||
case EVENT_GUARD_WHIRLWIND_END:
|
||||
me->GetThreatMgr().ResetAllThreat();
|
||||
me->SetReactState(REACT_AGGRESSIVE);
|
||||
events.CancelEvent(EVENT_GUARD_WHIRLWIND_RANDOM);
|
||||
whirlwind = false;
|
||||
events.ScheduleEvent(EVENT_GUARD_WHIRLWIND, 500ms, 9s);
|
||||
break;
|
||||
case EVENT_GUARD_AGGRO_RESET:
|
||||
if (aggroReset == true)
|
||||
{
|
||||
if (Unit* originalTarget = SelectTarget(SelectTargetMethod::Random, 0))
|
||||
{
|
||||
_savedTargetGUID = originalTarget->GetGUID();
|
||||
_savedTargetThreat = me->GetThreatMgr().GetThreat(originalTarget);
|
||||
me->GetThreatMgr().ModifyThreatByPercent(originalTarget, -100);
|
||||
}
|
||||
aggroReset = true;
|
||||
events.ScheduleEvent(EVENT_GUARD_AGGRO_RESET_END, 5000);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100.0f, true))
|
||||
{
|
||||
me->AddThreat(target, 1.0f);
|
||||
me->TauntApply(target);
|
||||
AttackStart(target);
|
||||
}
|
||||
}
|
||||
events.RepeatEvent(urand(1000, 2000));
|
||||
break;
|
||||
case EVENT_GUARD_AGGRO_RESET_END:
|
||||
events.CancelEvent(EVENT_GUARD_AGGRO_RESET);
|
||||
if (Unit* originalTarget = ObjectAccessor::GetUnit(*me, _savedTargetGUID))
|
||||
{
|
||||
me->GetThreatMgr().AddThreat(originalTarget, _savedTargetThreat);
|
||||
_savedTargetGUID.Clear();
|
||||
}
|
||||
aggroReset = false;
|
||||
events.RescheduleEvent(EVENT_GUARD_AGGRO_RESET, urand(30000, 40000));
|
||||
break;
|
||||
case EVENT_GUARD_KNOCKBACK:
|
||||
DoCastVictim(SPELL_GUARD_KNOCKBACK);
|
||||
events.Repeat(21s, 37s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
private:
|
||||
bool whirlwind;
|
||||
bool aggroReset;
|
||||
ObjectGuid _savedTargetGUID;
|
||||
float _savedTargetThreat;
|
||||
};
|
||||
|
||||
void AddSC_boss_sartura()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user