@@ -3406,6 +3406,16 @@ DungeonFinder.CastDeserter = 1
|
||||
|
||||
DungeonFinder.AllowCompleted = 1
|
||||
|
||||
#
|
||||
# DungeonFinder.DungeonSelectionCooldown
|
||||
#
|
||||
# Description: Duration in minutes for the dungeon selection cooldown. Players who complete a
|
||||
# random dungeon via LFG will not be assigned the same dungeon again for this
|
||||
# duration. This reduces the chance of getting the same dungeon in a row.
|
||||
# Default: 0 - (Disabled)
|
||||
|
||||
DungeonFinder.DungeonSelectionCooldown = 0
|
||||
|
||||
#
|
||||
###################################################################################################
|
||||
|
||||
@@ -4605,20 +4615,6 @@ Calculate.Creature.Zone.Area.Data = 0
|
||||
|
||||
Calculate.Gameoject.Zone.Area.Data = 0
|
||||
|
||||
#
|
||||
# TeleportTimeoutNear
|
||||
# Description: No description
|
||||
# Default: 25
|
||||
|
||||
TeleportTimeoutNear = 25
|
||||
|
||||
#
|
||||
# TeleportTimeoutFar
|
||||
# Description: No description
|
||||
# Default: 45
|
||||
|
||||
TeleportTimeoutFar = 45
|
||||
|
||||
#
|
||||
# DailyRBGArenaPoints.MinLevel
|
||||
# Description: Allows gaining arena points on the first RBG win at level 70.
|
||||
|
||||
@@ -503,6 +503,8 @@ void CharacterDatabaseConnection::DoPrepareStatements()
|
||||
PrepareStatement(CHAR_UDP_CHAR_HONOR_POINTS_ACCUMULATIVE, "UPDATE characters SET totalHonorPoints = totalHonorPoints + ? WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UDP_CHAR_ARENA_POINTS, "UPDATE characters SET arenaPoints = ? WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UDP_CHAR_ARENA_POINTS_ACCUMULATIVE, "UPDATE characters SET arenaPoints = arenaPoints + ? WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UPD_ALL_HONOR_POINTS, "UPDATE characters SET totalHonorPoints = 0", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_UPD_ALL_ARENA_POINTS, "UPDATE characters SET arenaPoints = 0", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_UDP_CHAR_MONEY, "UPDATE characters SET money = ? WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UDP_CHAR_MONEY_ACCUMULATIVE, "UPDATE characters SET money = money + ? WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UPD_CHAR_REMOVE_GHOST, "UPDATE characters SET playerFlags = (playerFlags & (~16)) WHERE guid = ?", CONNECTION_ASYNC);
|
||||
|
||||
@@ -427,6 +427,8 @@ enum CharacterDatabaseStatements : uint32
|
||||
CHAR_UDP_CHAR_HONOR_POINTS_ACCUMULATIVE,
|
||||
CHAR_UDP_CHAR_ARENA_POINTS,
|
||||
CHAR_UDP_CHAR_ARENA_POINTS_ACCUMULATIVE,
|
||||
CHAR_UPD_ALL_HONOR_POINTS,
|
||||
CHAR_UPD_ALL_ARENA_POINTS,
|
||||
CHAR_UDP_CHAR_MONEY,
|
||||
CHAR_UDP_CHAR_MONEY_ACCUMULATIVE,
|
||||
CHAR_UPD_CHAR_REMOVE_GHOST, // pussywizard
|
||||
|
||||
@@ -746,10 +746,15 @@ bool PetAI::CanAttack(Unit* target, SpellInfo const* spellInfo)
|
||||
|
||||
// Check if our owner selected this target and clicked "attack"
|
||||
Unit* ownerTarget = nullptr;
|
||||
if (Player* owner = me->GetCharmerOrOwner()->ToPlayer())
|
||||
ownerTarget = owner->GetSelectedUnit();
|
||||
else
|
||||
ownerTarget = me->GetCharmerOrOwner()->GetVictim();
|
||||
Unit* charmerOrOwner = me->GetCharmerOrOwner();
|
||||
|
||||
if (charmerOrOwner)
|
||||
{
|
||||
if (Player* owner = charmerOrOwner->ToPlayer())
|
||||
ownerTarget = owner->GetSelectedUnit();
|
||||
else
|
||||
ownerTarget = charmerOrOwner->GetVictim();
|
||||
}
|
||||
|
||||
if (ownerTarget && me->GetCharmInfo()->IsCommandAttack())
|
||||
return (target->GetGUID() == ownerTarget->GetGUID());
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "MapMgr.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Player.h"
|
||||
#include "RaceMgr.h"
|
||||
#include "ReputationMgr.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "SpellMgr.h"
|
||||
@@ -115,7 +116,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
|
||||
criteria->ID, criteria->requiredType, dataType, classRace.class_id);
|
||||
return false;
|
||||
}
|
||||
if (classRace.race_id && ((1 << (classRace.race_id - 1)) & RACEMASK_ALL_PLAYABLE) == 0)
|
||||
if (classRace.race_id && ((1 << (classRace.race_id - 1)) & sRaceMgr->GetPlayableRaceMask()) == 0)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: {} Type: {}) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE ({}) has non-existing race in value2 ({}), ignored.",
|
||||
criteria->ID, criteria->requiredType, dataType, classRace.race_id);
|
||||
@@ -275,7 +276,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
|
||||
criteria->ID, criteria->requiredType, dataType, classRace.class_id);
|
||||
return false;
|
||||
}
|
||||
if (classRace.race_id && ((1 << (classRace.race_id - 1)) & RACEMASK_ALL_PLAYABLE) == 0)
|
||||
if (classRace.race_id && ((1 << (classRace.race_id - 1)) & sRaceMgr->GetPlayableRaceMask()) == 0)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: {} Type: {}) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE ({}) has non-existing race in value2 ({}), ignored.",
|
||||
criteria->ID, criteria->requiredType, dataType, classRace.race_id);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "Battlefield.h"
|
||||
#include "BattlefieldMgr.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "CellImpl.h"
|
||||
#include "CreatureTextMgr.h"
|
||||
#include "GameGraveyard.h"
|
||||
@@ -81,6 +82,10 @@ Battlefield::~Battlefield()
|
||||
// Called when a player enters the zone
|
||||
void Battlefield::HandlePlayerEnterZone(Player* player, uint32 /*zone*/)
|
||||
{
|
||||
// Allow scripts to adjust the player's effective team or appearance before
|
||||
// any team-based battlefield containers (such as player lists or queues) are updated.
|
||||
sScriptMgr->OnBattlefieldPlayerEnterZone(this, player);
|
||||
|
||||
// Xinef: do not invite players on taxi
|
||||
if (!player->IsInFlight())
|
||||
{
|
||||
@@ -126,6 +131,7 @@ void Battlefield::HandlePlayerLeaveZone(Player* player, uint32 /*zone*/)
|
||||
group->RemoveMember(player->GetGUID());
|
||||
|
||||
OnPlayerLeaveWar(player);
|
||||
sScriptMgr->OnBattlefieldPlayerLeaveWar(this, player);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,6 +144,10 @@ void Battlefield::HandlePlayerLeaveZone(Player* player, uint32 /*zone*/)
|
||||
SendRemoveWorldStates(player);
|
||||
RemovePlayerFromResurrectQueue(player->GetGUID());
|
||||
OnPlayerLeaveZone(player);
|
||||
// Scripts must restore player->GetTeamId() here (e.g. ClearFakePlayer).
|
||||
// All Battlefield data-structure cleanup above has already completed using
|
||||
// the assigned team, so it is safe to restore the real team now.
|
||||
sScriptMgr->OnBattlefieldPlayerLeaveZone(this, player);
|
||||
}
|
||||
|
||||
bool Battlefield::Update(uint32 diff)
|
||||
@@ -302,6 +312,8 @@ void Battlefield::InvitePlayerToWar(Player* player)
|
||||
if (m_PlayersInWar[player->GetTeamId()].count(player->GetGUID()) || m_InvitedPlayers[player->GetTeamId()].count(player->GetGUID()))
|
||||
return;
|
||||
|
||||
sScriptMgr->OnBattlefieldBeforeInvitePlayerToWar(this, player);
|
||||
|
||||
m_PlayersWillBeKick[player->GetTeamId()].erase(player->GetGUID());
|
||||
m_InvitedPlayers[player->GetTeamId()][player->GetGUID()] = GameTime::GetGameTime().count() + m_TimeForAcceptInvite;
|
||||
player->GetSession()->SendBfInvitePlayerToWar(m_BattleId, m_ZoneId, m_TimeForAcceptInvite);
|
||||
@@ -424,6 +436,8 @@ void Battlefield::PlayerAcceptInviteToWar(Player* player)
|
||||
if (!IsWarTime())
|
||||
return;
|
||||
|
||||
sScriptMgr->OnBattlefieldPlayerJoinWar(this, player);
|
||||
|
||||
if (AddOrSetPlayerToCorrectBfGroup(player))
|
||||
{
|
||||
player->GetSession()->SendBfEntered(m_BattleId);
|
||||
|
||||
@@ -354,6 +354,13 @@ public:
|
||||
uint32 GetTimer() { return m_Timer; }
|
||||
void SetTimer(uint32 timer) { m_Timer = timer; }
|
||||
|
||||
// Returns combined count of players in war + invited (per team) for balance checking
|
||||
uint32 GetPlayersInWarCount(TeamId teamId) const { return static_cast<uint32>(m_PlayersInWar[teamId].size() + m_InvitedPlayers[teamId].size()); }
|
||||
// Returns total count of players in the battlefield zone per team
|
||||
uint32 GetPlayersInZoneCount(TeamId teamId) const { return static_cast<uint32>(m_players[teamId].size()); }
|
||||
// Returns the maximum players allowed per team
|
||||
uint32 GetMaxPlayersPerTeam() const { return m_MaxPlayer; }
|
||||
|
||||
void DoPlaySoundToAll(uint32 SoundID);
|
||||
|
||||
void InvitePlayerToQueue(Player* player);
|
||||
|
||||
@@ -718,6 +718,8 @@ void BattlegroundSA::DemolisherStartState(bool start)
|
||||
if (!BgCreatures[0])
|
||||
return;
|
||||
|
||||
// Only lock demolishers during warmup - they are attacker vehicles.
|
||||
// Cannons/turrets are defender weapons and must remain usable from the start.
|
||||
for (uint8 i = BG_SA_DEMOLISHER_1; i <= BG_SA_DEMOLISHER_4; i++)
|
||||
if (Creature* dem = GetBGCreature(i))
|
||||
{
|
||||
@@ -727,15 +729,6 @@ void BattlegroundSA::DemolisherStartState(bool start)
|
||||
dem->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
|
||||
}
|
||||
|
||||
for (uint8 i = BG_SA_GUN_1; i <= BG_SA_GUN_10; i++)
|
||||
if (Creature* gun = GetBGCreature(i))
|
||||
{
|
||||
if (start)
|
||||
gun->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
|
||||
else
|
||||
gun->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
|
||||
}
|
||||
|
||||
// xinef: enable first gates damaging at start
|
||||
if (!start)
|
||||
{
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "ObjectMgr.h"
|
||||
#include "Pet.h"
|
||||
#include "Player.h"
|
||||
#include "RaceMgr.h"
|
||||
#include "ReputationMgr.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "ScriptedCreature.h"
|
||||
@@ -2082,9 +2083,9 @@ bool ConditionMgr::isConditionTypeValid(Condition* cond)
|
||||
}
|
||||
case CONDITION_RACE:
|
||||
{
|
||||
if (!(cond->ConditionValue1 & RACEMASK_ALL_PLAYABLE))
|
||||
if (!(cond->ConditionValue1 & sRaceMgr->GetPlayableRaceMask()))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Race condition has non existing racemask ({}), skipped", cond->ConditionValue1 & ~RACEMASK_ALL_PLAYABLE);
|
||||
LOG_ERROR("sql.sql", "Race condition has non existing racemask ({}), skipped", cond->ConditionValue1 & ~sRaceMgr->GetPlayableRaceMask());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -165,6 +165,87 @@ namespace lfg
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
void LFGMgr::AddDungeonCooldown(ObjectGuid guid, uint32 dungeonId)
|
||||
{
|
||||
if (!sWorld->getIntConfig(CONFIG_LFG_DUNGEON_SELECTION_COOLDOWN))
|
||||
return;
|
||||
|
||||
DungeonCooldownStore[guid][dungeonId] = GameTime::Now();
|
||||
}
|
||||
|
||||
void LFGMgr::CleanupDungeonCooldowns()
|
||||
{
|
||||
if (!sWorld->getIntConfig(CONFIG_LFG_DUNGEON_SELECTION_COOLDOWN))
|
||||
return;
|
||||
|
||||
Seconds cooldownDuration = GetDungeonCooldownDuration();
|
||||
|
||||
for (auto itPlayer = DungeonCooldownStore.begin(); itPlayer != DungeonCooldownStore.end(); )
|
||||
{
|
||||
for (auto itDungeon = itPlayer->second.begin(); itDungeon != itPlayer->second.end(); )
|
||||
{
|
||||
if (GameTime::HasElapsed(itDungeon->second, cooldownDuration))
|
||||
itDungeon = itPlayer->second.erase(itDungeon);
|
||||
else
|
||||
++itDungeon;
|
||||
}
|
||||
|
||||
if (itPlayer->second.empty())
|
||||
itPlayer = DungeonCooldownStore.erase(itPlayer);
|
||||
else
|
||||
++itPlayer;
|
||||
}
|
||||
}
|
||||
|
||||
void LFGMgr::ClearDungeonCooldowns()
|
||||
{
|
||||
DungeonCooldownStore.clear();
|
||||
}
|
||||
|
||||
Seconds LFGMgr::GetDungeonCooldownDuration() const
|
||||
{
|
||||
return Seconds(sWorld->getIntConfig(CONFIG_LFG_DUNGEON_SELECTION_COOLDOWN) * MINUTE);
|
||||
}
|
||||
|
||||
LfgDungeonSet LFGMgr::FilterCooldownDungeons(LfgDungeonSet const& dungeons, LfgRolesMap const& players)
|
||||
{
|
||||
if (!sWorld->getIntConfig(CONFIG_LFG_DUNGEON_SELECTION_COOLDOWN))
|
||||
return dungeons;
|
||||
|
||||
Seconds cooldownDuration = GetDungeonCooldownDuration();
|
||||
|
||||
LfgDungeonSet filtered;
|
||||
for (uint32 dungeonId : dungeons)
|
||||
{
|
||||
bool onCooldown = false;
|
||||
for (auto const& playerPair : players)
|
||||
{
|
||||
auto itPlayer = DungeonCooldownStore.find(playerPair.first);
|
||||
if (itPlayer != DungeonCooldownStore.end())
|
||||
{
|
||||
auto itDungeon = itPlayer->second.find(dungeonId);
|
||||
if (itDungeon != itPlayer->second.end() && !GameTime::HasElapsed(itDungeon->second, cooldownDuration))
|
||||
{
|
||||
onCooldown = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!onCooldown)
|
||||
filtered.insert(dungeonId);
|
||||
}
|
||||
|
||||
// If all dungeons are on cooldown, return original set to avoid blocking the queue
|
||||
if (filtered.empty())
|
||||
{
|
||||
LOG_DEBUG("lfg", "LFGMgr::FilterCooldownDungeons: All {} dungeons on cooldown for group, bypassing cooldown filter", dungeons.size());
|
||||
return dungeons;
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
LFGDungeonData const* LFGMgr::GetLFGDungeon(uint32 id)
|
||||
{
|
||||
LFGDungeonContainer::const_iterator itr = LfgDungeonStore.find(id);
|
||||
@@ -329,6 +410,9 @@ namespace lfg
|
||||
BootsStore.erase(itBoot);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup expired dungeon cooldowns
|
||||
CleanupDungeonCooldowns();
|
||||
}
|
||||
else if (task == 1)
|
||||
{
|
||||
@@ -2263,6 +2347,9 @@ namespace lfg
|
||||
continue;
|
||||
}
|
||||
|
||||
// Record dungeon cooldown for this player (the actual dungeon completed, not the random entry)
|
||||
AddDungeonCooldown(guid, dungeonId);
|
||||
|
||||
Player* player = ObjectAccessor::FindPlayer(guid);
|
||||
if (!player || player->FindMap() != currMap) // pussywizard: currMap - multithreading crash if on other map (map id check is not enough, binding system is not reliable)
|
||||
{
|
||||
|
||||
@@ -450,6 +450,10 @@ namespace lfg
|
||||
void LoadRewards();
|
||||
/// Loads dungeons from dbc and adds teleport coords
|
||||
void LoadLFGDungeons(bool reload = false);
|
||||
/// Filters out recently completed dungeons from the proposal set for the given players
|
||||
LfgDungeonSet FilterCooldownDungeons(LfgDungeonSet const& dungeons, LfgRolesMap const& players);
|
||||
/// Clears all dungeon cooldowns for all players
|
||||
void ClearDungeonCooldowns();
|
||||
|
||||
// Multiple files
|
||||
/// Check if given guid applied for random dungeon
|
||||
@@ -636,6 +640,14 @@ namespace lfg
|
||||
LfgPlayerDataContainer PlayersStore; ///< Player data
|
||||
LfgGroupDataContainer GroupsStore; ///< Group data
|
||||
bool m_Testing;
|
||||
|
||||
// Dungeon cooldown system - prevents same dungeon being assigned in a row
|
||||
typedef std::unordered_map<uint32 /*dungeonId*/, TimePoint /*completionTime*/> LfgDungeonCooldownMap;
|
||||
typedef std::unordered_map<ObjectGuid /*playerGuid*/, LfgDungeonCooldownMap> LfgDungeonCooldownContainer;
|
||||
LfgDungeonCooldownContainer DungeonCooldownStore; ///< Stores dungeon cooldowns per player
|
||||
void AddDungeonCooldown(ObjectGuid guid, uint32 dungeonId);
|
||||
void CleanupDungeonCooldowns();
|
||||
[[nodiscard]] Seconds GetDungeonCooldownDuration() const;
|
||||
};
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_enum_v<T>)>
|
||||
|
||||
@@ -421,7 +421,10 @@ namespace lfg
|
||||
proposal.cancelTime = GameTime::GetGameTime().count() + LFG_TIME_PROPOSAL;
|
||||
proposal.state = LFG_PROPOSAL_INITIATING;
|
||||
proposal.leader.Clear();
|
||||
proposal.dungeonId = Acore::Containers::SelectRandomContainerElement(proposalDungeons);
|
||||
|
||||
// Filter out recently completed dungeons to prevent same dungeon in a row
|
||||
LfgDungeonSet filteredDungeons = sLFGMgr->FilterCooldownDungeons(proposalDungeons, proposalRoles);
|
||||
proposal.dungeonId = Acore::Containers::SelectRandomContainerElement(filteredDungeons);
|
||||
|
||||
uint32 completedEncounters = 0;
|
||||
bool leader = false;
|
||||
|
||||
@@ -1763,7 +1763,7 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, boo
|
||||
|
||||
// pussywizard: arena spectator
|
||||
if (obj->IsPlayer())
|
||||
if (((Player const*)obj)->IsSpectator() && ((Player const*)obj)->FindMap()->IsBattleArena())
|
||||
if (((Player const*)obj)->IsSpectator() && ((Player const*)obj)->FindMap() && ((Player const*)obj)->FindMap()->IsBattleArena())
|
||||
return false;
|
||||
|
||||
bool corpseVisibility = false;
|
||||
@@ -1807,11 +1807,12 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, boo
|
||||
return false;
|
||||
|
||||
// pussywizard: during arena preparation, don't allow to detect pets if can't see its owner (spoils enemy arena frames)
|
||||
if (target->IsPet() && target->GetOwnerGUID() && target->FindMap()->IsBattleArena() && GetGUID() != target->GetOwnerGUID())
|
||||
if (BattlegroundMap* bgmap = target->FindMap()->ToBattlegroundMap())
|
||||
if (Battleground* bg = bgmap->GetBG())
|
||||
if (bg->GetStatus() < STATUS_IN_PROGRESS && !thisPlayer->HaveAtClient(target->GetOwnerGUID()))
|
||||
return false;
|
||||
if (Map* targetMap = target->FindMap())
|
||||
if (target->IsPet() && target->GetOwnerGUID() && targetMap->IsBattleArena() && GetGUID() != target->GetOwnerGUID())
|
||||
if (BattlegroundMap* bgmap = targetMap->ToBattlegroundMap())
|
||||
if (Battleground* bg = bgmap->GetBG())
|
||||
if (bg->GetStatus() < STATUS_IN_PROGRESS && !thisPlayer->HaveAtClient(target->GetOwnerGUID()))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (thisPlayer->GetFarSightDistance() && !thisPlayer->isInFront(obj))
|
||||
@@ -1856,7 +1857,7 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, boo
|
||||
|
||||
// pussywizard: arena spectator
|
||||
if (this->IsPlayer())
|
||||
if (((Player const*)this)->IsSpectator() && ((Player const*)this)->FindMap()->IsBattleArena() && (obj->m_invisibility.GetFlags() || obj->m_stealth.GetFlags()))
|
||||
if (((Player const*)this)->IsSpectator() && ((Player const*)this)->FindMap() && ((Player const*)this)->FindMap()->IsBattleArena() && (obj->m_invisibility.GetFlags() || obj->m_stealth.GetFlags()))
|
||||
return false;
|
||||
|
||||
if (!CanDetect(obj, ignoreStealth, !distanceCheck, checkAlert))
|
||||
|
||||
@@ -5375,7 +5375,10 @@ void Player::SetSkill(uint16 id, uint16 step, uint16 newVal, uint16 maxVal)
|
||||
|
||||
// remove all spells that related to this skill
|
||||
for (SkillLineAbilityEntry const* pAbility : GetSkillLineAbilitiesBySkillLine(id))
|
||||
{
|
||||
removeSpell(sSpellMgr->GetFirstSpellInChain(pAbility->Spell), SPEC_MASK_ALL, false);
|
||||
RemoveAurasDueToSpell(pAbility->Spell);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (newVal) //add
|
||||
@@ -7468,6 +7471,10 @@ void Player::CastItemUseSpell(Item* item, SpellCastTargets const& targets, uint8
|
||||
|
||||
if (HasSpellCooldown(spellInfo->Id))
|
||||
{
|
||||
// Notify client so it can clean up the pending spell cast.
|
||||
// Without this the client orphans the cast and blocks auto-attack.
|
||||
Spell::SendCastResult(ToPlayer(), spellInfo, cast_count,
|
||||
SPELL_FAILED_NOT_READY);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -7514,7 +7521,11 @@ void Player::CastItemUseSpell(Item* item, SpellCastTargets const& targets, uint8
|
||||
}
|
||||
|
||||
if (HasSpellCooldown(spellInfo->Id))
|
||||
{
|
||||
Spell::SendCastResult(ToPlayer(), spellInfo, cast_count,
|
||||
SPELL_FAILED_NOT_READY);
|
||||
continue;
|
||||
}
|
||||
|
||||
Spell* spell = new Spell(this, spellInfo, (count > 0) ? TRIGGERED_FULL_MASK : TRIGGERED_NONE);
|
||||
spell->m_CastItem = item;
|
||||
@@ -9999,14 +10010,6 @@ void Player::RemoveSpellMods(Spell* spell)
|
||||
if (roll_chance_i(aurEff->GetAmount()))
|
||||
continue; // don't consume charge
|
||||
}
|
||||
// ROGUE MUTILATE WITH COLD BLOOD
|
||||
if (spellInfo->Id == 5374)
|
||||
{
|
||||
SpellInfo const* sp = mod->ownerAura->GetSpellInfo();
|
||||
if (sp->Id == 14177) // Cold Blood
|
||||
continue; // don't consume charge
|
||||
}
|
||||
|
||||
if (mod->ownerAura->DropCharge(AURA_REMOVE_BY_EXPIRE))
|
||||
itr = m_spellMods[i].begin();
|
||||
}
|
||||
@@ -10036,9 +10039,10 @@ bool Player::HasSpellModApplied(SpellModifier* mod, Spell* spell)
|
||||
|
||||
void Player::SetSpellModTakingSpell(Spell* spell, bool apply)
|
||||
{
|
||||
if (apply && m_spellModTakingSpell && m_spellModTakingSpell != spell)
|
||||
if (apply && m_spellModTakingSpell != nullptr)
|
||||
return;
|
||||
else if (!apply && m_spellModTakingSpell != spell)
|
||||
|
||||
if (!apply && (!m_spellModTakingSpell || m_spellModTakingSpell != spell))
|
||||
return;
|
||||
|
||||
m_spellModTakingSpell = apply ? spell : nullptr;
|
||||
@@ -11759,7 +11763,7 @@ void Player::ApplyEquipCooldown(Item* pItem)
|
||||
continue;
|
||||
|
||||
if (Aura* itemAura = GetAura(spellData.SpellId, GetGUID(), pItem->GetGUID()))
|
||||
itemAura->AddProcCooldown(std::chrono::steady_clock::now() + std::chrono::seconds(30));
|
||||
itemAura->AddProcCooldown(std::chrono::steady_clock::now() + procEntry->Cooldown);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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 General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "RaceMgr.h"
|
||||
#include "AccountMgr.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Player.h"
|
||||
#include "Util.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
|
||||
uint8 RaceMgr::_maxRaces = 0;
|
||||
|
||||
uint32 RaceMgr::_playableRaceMask = 0;
|
||||
uint32 RaceMgr::_allianceRaceMask = 0;
|
||||
uint32 RaceMgr::_hordeRaceMask = 0;
|
||||
|
||||
RaceMgr::RaceMgr()
|
||||
{
|
||||
}
|
||||
|
||||
RaceMgr::~RaceMgr()
|
||||
{
|
||||
}
|
||||
|
||||
RaceMgr* RaceMgr::instance()
|
||||
{
|
||||
static RaceMgr instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
void RaceMgr::LoadRaces()
|
||||
{
|
||||
SetMaxRaces(0 + 1);
|
||||
_playableRaceMask = 0;
|
||||
_hordeRaceMask = 0;
|
||||
_allianceRaceMask = 0;
|
||||
|
||||
for (auto const& raceEntry : sChrRacesStore)
|
||||
{
|
||||
if (!raceEntry)
|
||||
continue;
|
||||
|
||||
uint8 alliance = raceEntry->alliance;
|
||||
uint8 raceId = raceEntry->RaceID;
|
||||
|
||||
if (raceEntry->Flags & CHRRACES_FLAGS_NOT_PLAYABLE)
|
||||
continue;
|
||||
|
||||
if (GetMaxRaces() <= raceId)
|
||||
SetMaxRaces(raceId + 1);
|
||||
|
||||
uint32 raceBit = (1 << (raceId - 1));
|
||||
|
||||
_playableRaceMask |= raceBit;
|
||||
|
||||
if (alliance == ALLIANCE_HORDE)
|
||||
_hordeRaceMask |= raceBit;
|
||||
else if (alliance == ALLIANCE_ALLIANCE)
|
||||
_allianceRaceMask |= raceBit;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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 General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __ACORE_RACEMGR_H
|
||||
#define __ACORE_RACEMGR_H
|
||||
|
||||
#include "DatabaseEnv.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "SharedDefines.h"
|
||||
#include <map>
|
||||
|
||||
class Player;
|
||||
class WorldPacket;
|
||||
|
||||
class RaceMgr
|
||||
{
|
||||
private:
|
||||
RaceMgr();
|
||||
~RaceMgr();
|
||||
|
||||
public:
|
||||
static RaceMgr* instance();
|
||||
|
||||
static void LoadRaces();
|
||||
static uint8 GetMaxRaces() { return _maxRaces; }
|
||||
static void SetMaxRaces(uint8 max) { _maxRaces = max; }
|
||||
static uint32 GetPlayableRaceMask() { return _playableRaceMask; }
|
||||
static uint32 GetAllianceRaceMask() { return _allianceRaceMask; }
|
||||
static uint32 GetHordeRaceMask() { return _hordeRaceMask; }
|
||||
private:
|
||||
static uint8 _maxRaces; // Max playable race + 1
|
||||
|
||||
static uint32 _playableRaceMask;
|
||||
static uint32 _allianceRaceMask;
|
||||
static uint32 _hordeRaceMask;
|
||||
};
|
||||
|
||||
#define sRaceMgr RaceMgr::instance()
|
||||
|
||||
#endif
|
||||
@@ -13069,11 +13069,28 @@ void Unit::TriggerAurasProcOnEvent(std::list<AuraApplication*>* myProcAuras, std
|
||||
|
||||
void Unit::TriggerAurasProcOnEvent(ProcEventInfo& eventInfo, AuraApplicationProcContainer& aurasTriggeringProc)
|
||||
{
|
||||
Spell const* triggeringSpell = eventInfo.GetProcSpell();
|
||||
bool const disableProcs = triggeringSpell && triggeringSpell->IsProcDisabled();
|
||||
if (disableProcs)
|
||||
SetCantProc(true);
|
||||
|
||||
for (auto const& [procEffectMask, aurApp] : aurasTriggeringProc)
|
||||
{
|
||||
if (!aurApp->GetRemoveMode())
|
||||
aurApp->GetBase()->TriggerProcOnEvent(procEffectMask, aurApp, eventInfo);
|
||||
if (aurApp->GetRemoveMode())
|
||||
continue;
|
||||
|
||||
SpellInfo const* spellInfo = aurApp->GetBase()->GetSpellInfo();
|
||||
if (spellInfo->HasAttribute(SPELL_ATTR3_INSTANT_TARGET_PROCS))
|
||||
SetCantProc(true);
|
||||
|
||||
aurApp->GetBase()->TriggerProcOnEvent(procEffectMask, aurApp, eventInfo);
|
||||
|
||||
if (spellInfo->HasAttribute(SPELL_ATTR3_INSTANT_TARGET_PROCS))
|
||||
SetCantProc(false);
|
||||
}
|
||||
|
||||
if (disableProcs)
|
||||
SetCantProc(false);
|
||||
}
|
||||
|
||||
Player* Unit::GetSpellModOwner() const
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "MapMgr.h"
|
||||
#include "Pet.h"
|
||||
#include "PoolMgr.h"
|
||||
#include "RaceMgr.h"
|
||||
#include "ReputationMgr.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "Spell.h"
|
||||
@@ -308,8 +309,6 @@ ObjectMgr::ObjectMgr():
|
||||
for (uint8 i = 0; i < MAX_CLASSES; ++i)
|
||||
{
|
||||
_playerClassInfo[i] = nullptr;
|
||||
for (uint8 j = 0; j < MAX_RACES; ++j)
|
||||
_playerInfo[j][i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,7 +328,7 @@ ObjectMgr::~ObjectMgr()
|
||||
delete _playerClassInfo[class_];
|
||||
}
|
||||
|
||||
for (int race = 0; race < MAX_RACES; ++race)
|
||||
for (int race = 0; race < sRaceMgr->GetMaxRaces(); ++race)
|
||||
{
|
||||
for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
|
||||
{
|
||||
@@ -1111,8 +1110,8 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo)
|
||||
|
||||
if (!cInfo->unit_class || ((1 << (cInfo->unit_class - 1)) & CLASSMASK_ALL_CREATURES) == 0)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid unit_class ({}) in creature_template. Set to 1 (UNIT_CLASS_WARRIOR).", cInfo->Entry, cInfo->unit_class);
|
||||
const_cast<CreatureTemplate*>(cInfo)->unit_class = UNIT_CLASS_WARRIOR;
|
||||
LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid unit_class ({}) in creature_template. Set to 1 (CLASS_WARRIOR).", cInfo->Entry, cInfo->unit_class);
|
||||
const_cast<CreatureTemplate*>(cInfo)->unit_class = CLASS_WARRIOR;
|
||||
}
|
||||
|
||||
if (cInfo->dmgschool >= MAX_SPELL_SCHOOL)
|
||||
@@ -3492,7 +3491,7 @@ void ObjectMgr::LoadItemTemplates()
|
||||
if (!(itemTemplate.AllowableClass & CLASSMASK_ALL_PLAYABLE))
|
||||
LOG_ERROR("sql.sql", "Item (Entry: {}) does not have any playable classes ({}) in `AllowableClass` and can't be equipped or used.", entry, itemTemplate.AllowableClass);
|
||||
|
||||
if (!(itemTemplate.AllowableRace & RACEMASK_ALL_PLAYABLE))
|
||||
if (!(itemTemplate.AllowableRace & sRaceMgr->GetPlayableRaceMask()))
|
||||
LOG_ERROR("sql.sql", "Item (Entry: {}) does not have any playable races ({}) in `AllowableRace` and can't be equipped or used.", entry, itemTemplate.AllowableRace);
|
||||
}
|
||||
}
|
||||
@@ -4252,6 +4251,14 @@ void ObjectMgr::LoadPlayerInfo()
|
||||
{
|
||||
// Load playercreate
|
||||
{
|
||||
if (_playerInfo.empty() || _playerInfo.size() != sRaceMgr->GetMaxRaces())
|
||||
{
|
||||
_playerInfo.clear();
|
||||
_playerInfo.resize(sRaceMgr->GetMaxRaces());
|
||||
for (auto& classVec : _playerInfo)
|
||||
classVec.resize(MAX_CLASSES, nullptr);
|
||||
}
|
||||
|
||||
uint32 oldMSTime = getMSTime();
|
||||
// 0 1 2 3 4 5 6
|
||||
QueryResult result = WorldDatabase.Query("SELECT race, class, map, zone, position_x, position_y, position_z, orientation FROM playercreateinfo");
|
||||
@@ -4279,7 +4286,7 @@ void ObjectMgr::LoadPlayerInfo()
|
||||
float positionZ = fields[6].Get<float>();
|
||||
float orientation = fields[7].Get<float>();
|
||||
|
||||
if (current_race >= MAX_RACES)
|
||||
if (current_race >= sRaceMgr->GetMaxRaces())
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Wrong race {} in `playercreateinfo` table, ignoring.", current_race);
|
||||
continue;
|
||||
@@ -4357,7 +4364,7 @@ void ObjectMgr::LoadPlayerInfo()
|
||||
Field* fields = result->Fetch();
|
||||
|
||||
uint32 current_race = fields[0].Get<uint8>();
|
||||
if (current_race >= MAX_RACES)
|
||||
if (current_race >= sRaceMgr->GetMaxRaces())
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Wrong race {} in `playercreateinfo_item` table, ignoring.", current_race);
|
||||
continue;
|
||||
@@ -4389,7 +4396,7 @@ void ObjectMgr::LoadPlayerInfo()
|
||||
if (!current_race || !current_class)
|
||||
{
|
||||
uint32 min_race = current_race ? current_race : 1;
|
||||
uint32 max_race = current_race ? current_race + 1 : MAX_RACES;
|
||||
uint32 max_race = current_race ? current_race + 1 : sRaceMgr->GetMaxRaces();
|
||||
uint32 min_class = current_class ? current_class : 1;
|
||||
uint32 max_class = current_class ? current_class + 1 : MAX_CLASSES;
|
||||
for (uint32 r = min_race; r < max_race; ++r)
|
||||
@@ -4437,7 +4444,7 @@ void ObjectMgr::LoadPlayerInfo()
|
||||
continue;
|
||||
}
|
||||
|
||||
if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE))
|
||||
if (raceMask != 0 && !(raceMask & sRaceMgr->GetPlayableRaceMask()))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Wrong race mask {} in `playercreateinfo_skills` table, ignoring.", raceMask);
|
||||
continue;
|
||||
@@ -4455,7 +4462,7 @@ void ObjectMgr::LoadPlayerInfo()
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex)
|
||||
for (uint32 raceIndex = RACE_HUMAN; raceIndex < sRaceMgr->GetMaxRaces(); ++raceIndex)
|
||||
{
|
||||
if (raceMask == 0 || ((1 << (raceIndex - 1)) & raceMask))
|
||||
{
|
||||
@@ -4504,7 +4511,7 @@ void ObjectMgr::LoadPlayerInfo()
|
||||
uint32 classMask = fields[1].Get<uint32>();
|
||||
uint32 spellId = fields[2].Get<uint32>();
|
||||
|
||||
if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE))
|
||||
if (raceMask != 0 && !(raceMask & sRaceMgr->GetPlayableRaceMask()))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Wrong race mask {} in `playercreateinfo_spell_custom` table, ignoring.", raceMask);
|
||||
continue;
|
||||
@@ -4516,7 +4523,7 @@ void ObjectMgr::LoadPlayerInfo()
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex)
|
||||
for (uint32 raceIndex = RACE_HUMAN; raceIndex < sRaceMgr->GetMaxRaces(); ++raceIndex)
|
||||
{
|
||||
if (raceMask == 0 || ((1 << (raceIndex - 1)) & raceMask))
|
||||
{
|
||||
@@ -4562,7 +4569,7 @@ void ObjectMgr::LoadPlayerInfo()
|
||||
uint32 classMask = fields[1].Get<uint32>();
|
||||
uint32 spellId = fields[2].Get<uint32>();
|
||||
|
||||
if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE))
|
||||
if (raceMask != 0 && !(raceMask & sRaceMgr->GetPlayableRaceMask()))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Wrong race mask {} in `playercreateinfo_cast_spell` table, ignoring.", raceMask);
|
||||
continue;
|
||||
@@ -4574,7 +4581,7 @@ void ObjectMgr::LoadPlayerInfo()
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex)
|
||||
for (uint32 raceIndex = RACE_HUMAN; raceIndex < sRaceMgr->GetMaxRaces(); ++raceIndex)
|
||||
{
|
||||
if (raceMask == 0 || ((1 << (raceIndex - 1)) & raceMask))
|
||||
{
|
||||
@@ -4620,7 +4627,7 @@ void ObjectMgr::LoadPlayerInfo()
|
||||
Field* fields = result->Fetch();
|
||||
|
||||
uint32 current_race = fields[0].Get<uint8>();
|
||||
if (current_race >= MAX_RACES)
|
||||
if (current_race >= sRaceMgr->GetMaxRaces())
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Wrong race {} in `playercreateinfo_action` table, ignoring.", current_race);
|
||||
continue;
|
||||
@@ -4652,7 +4659,9 @@ void ObjectMgr::LoadPlayerInfo()
|
||||
int16 StatModifier[MAX_STATS];
|
||||
};
|
||||
|
||||
std::array<RaceStats, MAX_RACES> raceStatModifiers;
|
||||
std::vector<RaceStats> raceStatModifiers;
|
||||
|
||||
raceStatModifiers.resize(sRaceMgr->GetMaxRaces());
|
||||
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
@@ -4671,7 +4680,7 @@ void ObjectMgr::LoadPlayerInfo()
|
||||
Field* fields = raceStatsResult->Fetch();
|
||||
|
||||
uint32 current_race = fields[0].Get<uint8>();
|
||||
if (current_race >= MAX_RACES)
|
||||
if (current_race >= sRaceMgr->GetMaxRaces())
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Wrong race {} in `player_race_stats` table, ignoring.", current_race);
|
||||
continue;
|
||||
@@ -4745,7 +4754,7 @@ void ObjectMgr::LoadPlayerInfo()
|
||||
} while (result->NextRow());
|
||||
|
||||
// Fill gaps and check integrity
|
||||
for (int race = 0; race < MAX_RACES; ++race)
|
||||
for (int race = RACE_HUMAN; race < sRaceMgr->GetMaxRaces(); ++race)
|
||||
{
|
||||
// skip non existed races
|
||||
if (!sChrRacesStore.LookupEntry(race))
|
||||
@@ -4884,7 +4893,7 @@ void ObjectMgr::GetPlayerClassLevelInfo(uint32 class_, uint8 level, PlayerClassL
|
||||
|
||||
void ObjectMgr::GetPlayerLevelInfo(uint32 race, uint32 class_, uint8 level, PlayerLevelInfo* info) const
|
||||
{
|
||||
if (level < 1 || race >= MAX_RACES || class_ >= MAX_CLASSES)
|
||||
if (level < 1 || race >= sRaceMgr->GetMaxRaces() || class_ >= MAX_CLASSES)
|
||||
return;
|
||||
|
||||
PlayerInfo const* pInfo = _playerInfo[race][class_];
|
||||
@@ -5253,10 +5262,10 @@ void ObjectMgr::LoadQuests()
|
||||
qinfo->RequiredClasses = 0;
|
||||
}
|
||||
}
|
||||
// AllowableRaces, can be 0/RACEMASK_ALL_PLAYABLE to allow any race
|
||||
// AllowableRaces, can be 0/PlayableRaceMask to allow any race
|
||||
if (qinfo->AllowableRaces)
|
||||
{
|
||||
if (!(qinfo->AllowableRaces & RACEMASK_ALL_PLAYABLE))
|
||||
if (!(qinfo->AllowableRaces & sRaceMgr->GetPlayableRaceMask()))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Quest {} does not contain any playable races in `AllowableRaces` ({}), value set to 0 (all races).", qinfo->GetQuestId(), qinfo->AllowableRaces);
|
||||
qinfo->AllowableRaces = 0;
|
||||
@@ -9677,7 +9686,7 @@ void ObjectMgr::LoadMailLevelRewards()
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(raceMask & RACEMASK_ALL_PLAYABLE))
|
||||
if (!(raceMask & sRaceMgr->GetPlayableRaceMask()))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `mail_level_reward` have raceMask ({}) for level {} that not include any player races, ignoring.", raceMask, level);
|
||||
continue;
|
||||
@@ -10774,7 +10783,7 @@ VehicleAccessoryList const* ObjectMgr::GetVehicleAccessoryList(Vehicle* veh) con
|
||||
|
||||
PlayerInfo const* ObjectMgr::GetPlayerInfo(uint32 race, uint32 class_) const
|
||||
{
|
||||
if (race >= MAX_RACES)
|
||||
if (race >= sRaceMgr->GetMaxRaces())
|
||||
return nullptr;
|
||||
if (class_ >= MAX_CLASSES)
|
||||
return nullptr;
|
||||
|
||||
@@ -1603,7 +1603,7 @@ private:
|
||||
|
||||
void BuildPlayerLevelInfo(uint8 race, uint8 class_, uint8 level, PlayerLevelInfo* plinfo) const;
|
||||
|
||||
PlayerInfo* _playerInfo[MAX_RACES][MAX_CLASSES];
|
||||
std::vector<std::vector<PlayerInfo*>> _playerInfo;
|
||||
|
||||
typedef std::vector<uint32> PlayerXPperLevel; // [level]
|
||||
PlayerXPperLevel _playerXPperLevel;
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "Player.h"
|
||||
#include "PlayerDump.h"
|
||||
#include "QueryHolder.h"
|
||||
#include "RaceMgr.h"
|
||||
#include "Realm.h"
|
||||
#include "ReputationMgr.h"
|
||||
#include "ScriptMgr.h"
|
||||
@@ -2025,7 +2026,7 @@ void WorldSession::HandleCharFactionOrRaceChangeCallback(std::shared_ptr<Charact
|
||||
|
||||
for (uint8 i = 0; i < 2; ++i) // check both neutral and faction-specific AH
|
||||
{
|
||||
AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(i == 0 ? 0 : (((1 << (playerData->Race - 1)) & RACEMASK_ALLIANCE) ? 12 : 29));
|
||||
AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(i == 0 ? 0 : (((1 << (playerData->Race - 1)) & sRaceMgr->GetAllianceRaceMask()) ? 12 : 29));
|
||||
|
||||
for (auto const& [auID, Aentry] : auctionHouse->GetAuctions())
|
||||
{
|
||||
@@ -2413,7 +2414,7 @@ void WorldSession::HandleCharFactionOrRaceChangeCallback(std::shared_ptr<Charact
|
||||
// Disable all old-faction specific quests
|
||||
for (auto const& [questID, quest] : sObjectMgr->GetQuestTemplates())
|
||||
{
|
||||
uint32 newRaceMask = (newTeam == TEAM_ALLIANCE) ? RACEMASK_ALLIANCE : RACEMASK_HORDE;
|
||||
uint32 newRaceMask = (newTeam == TEAM_ALLIANCE) ? sRaceMgr->GetAllianceRaceMask() : sRaceMgr->GetHordeRaceMask();
|
||||
|
||||
if (quest->GetAllowableRaces() && !(quest->GetAllowableRaces() & newRaceMask))
|
||||
{
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "ObjectMgr.h"
|
||||
#include "Player.h"
|
||||
#include "QuestDef.h"
|
||||
#include "RaceMgr.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "Timer.h"
|
||||
|
||||
@@ -247,7 +248,7 @@ void ServerMailMgr::LoadMailServerTemplatesConditions()
|
||||
}
|
||||
break;
|
||||
case ServerMailConditionType::Race:
|
||||
if (conditionValue & ~RACEMASK_ALL_PLAYABLE)
|
||||
if (conditionValue & ~sRaceMgr->GetPlayableRaceMask())
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `mail_server_template_conditions` has conditionType 'Race' with invalid conditionValue ({}) for templateID {}, skipped.", conditionValue, templateID);
|
||||
continue;
|
||||
|
||||
@@ -239,6 +239,7 @@ public:
|
||||
// some calls like isInWater should not use vmaps due to processor power
|
||||
// can return INVALID_HEIGHT if under z+2 z coord not found height
|
||||
[[nodiscard]] float GetHeight(float x, float y, float z, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const;
|
||||
[[nodiscard]] float GetHeight(Position const& pos, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const { return GetHeight(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), checkVMap, maxSearchDist); }
|
||||
[[nodiscard]] float GetGridHeight(float x, float y) const;
|
||||
[[nodiscard]] float GetMinHeight(float x, float y) const;
|
||||
Transport* GetTransportForPos(uint32 phase, float x, float y, float z, WorldObject* worldobject = nullptr);
|
||||
|
||||
@@ -1183,7 +1183,18 @@ enum AcoreStrings
|
||||
LANG_CMD_QUEST_STATUS_LOG_FULL = 5109,
|
||||
LANG_CMD_QUEST_STATUS_COND_DETAIL = 5110,
|
||||
|
||||
// Room for more strings 5111-9999
|
||||
// Battlefield commands
|
||||
LANG_BF_NOT_FOUND = 5111,
|
||||
LANG_BF_STARTED = 5112,
|
||||
LANG_BF_STOPPED = 5113,
|
||||
LANG_BF_SWITCHED = 5114,
|
||||
LANG_BF_TIMER_SET = 5115,
|
||||
LANG_BF_ENABLED = 5116,
|
||||
LANG_BF_DISABLED = 5117,
|
||||
|
||||
LANG_RESETALL_HONOR = 5118,
|
||||
LANG_RESETALL_ARENA = 5119,
|
||||
// Room for more strings 5120-9999
|
||||
|
||||
// Level requirement notifications
|
||||
LANG_SAY_REQ = 6604,
|
||||
@@ -1347,6 +1358,8 @@ enum AcoreStrings
|
||||
LANG_BAN_IP_YOUBANNEDMESSAGE_WORLD = 11017,
|
||||
LANG_BAN_IP_YOUPERMBANNEDMESSAGE_WORLD = 11018,
|
||||
|
||||
LANG_LFG_COOLDOWN_CLEARED = 11019,
|
||||
|
||||
LANG_MUTED_PLAYER = 30000, // Mute for player 2 hour
|
||||
|
||||
// Instant Flight
|
||||
|
||||
@@ -240,7 +240,13 @@ void TimedFleeingMovementGenerator::Finalize(Unit* owner)
|
||||
|
||||
if (Unit* victim = owner->GetVictim())
|
||||
{
|
||||
owner->SetTarget(victim->GetGUID());
|
||||
if (owner->IsAlive())
|
||||
{
|
||||
owner->AttackStop();
|
||||
if (Creature* ownerCreature = owner->ToCreature())
|
||||
if (CreatureAI* AI = ownerCreature->AI())
|
||||
AI->AttackStart(victim);
|
||||
}
|
||||
}
|
||||
|
||||
if (Creature* ownerCreature = owner->ToCreature())
|
||||
|
||||
@@ -211,7 +211,8 @@ void RandomMovementGenerator<Creature>::_setRandomLocation(Creature* creature)
|
||||
}
|
||||
|
||||
_currentPoint = newPoint;
|
||||
G3D::Vector3& finalPoint = finalPath[finalPath.size() - 1];
|
||||
ASSERT(!finalPath.empty());
|
||||
G3D::Vector3 finalPoint = finalPath.back();
|
||||
_currDestPosition.Relocate(finalPoint.x, finalPoint.y, finalPoint.z);
|
||||
|
||||
creature->AddUnitState(UNIT_STATE_ROAMING_MOVE);
|
||||
@@ -239,12 +240,13 @@ void RandomMovementGenerator<Creature>::_setRandomLocation(Creature* creature)
|
||||
_moveCount = 0;
|
||||
_nextMoveTime.Reset(urand(4000, 8000));
|
||||
}
|
||||
if (sWorld->getBoolConfig(CONFIG_DONT_CACHE_RANDOM_MOVEMENT_PATHS))
|
||||
_preComputedPaths.erase(pathIdx);
|
||||
|
||||
//Call for creature group update
|
||||
if (creature->GetFormation() && creature->GetFormation()->GetLeader() == creature)
|
||||
creature->GetFormation()->LeaderMoveTo(finalPoint.x, finalPoint.y, finalPoint.z, 0);
|
||||
|
||||
if (sWorld->getBoolConfig(CONFIG_DONT_CACHE_RANDOM_MOVEMENT_PATHS))
|
||||
_preComputedPaths.erase(pathIdx);
|
||||
}
|
||||
|
||||
template<>
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "ArenaScript.h"
|
||||
#include "ArenaTeamScript.h"
|
||||
#include "AuctionHouseScript.h"
|
||||
#include "BattlefieldScript.h"
|
||||
#include "BattlegroundMapScript.h"
|
||||
#include "BattlegroundScript.h"
|
||||
#include "CommandScript.h"
|
||||
|
||||
@@ -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 General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "BattlefieldScript.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "ScriptMgrMacros.h"
|
||||
|
||||
void ScriptMgr::OnBattlefieldPlayerEnterZone(Battlefield* bf, Player* player)
|
||||
{
|
||||
CALL_ENABLED_HOOKS(BattlefieldScript, BATTLEFIELDHOOK_ON_PLAYER_ENTER_ZONE, script->OnBattlefieldPlayerEnterZone(bf, player));
|
||||
}
|
||||
|
||||
void ScriptMgr::OnBattlefieldPlayerLeaveZone(Battlefield* bf, Player* player)
|
||||
{
|
||||
CALL_ENABLED_HOOKS(BattlefieldScript, BATTLEFIELDHOOK_ON_PLAYER_LEAVE_ZONE, script->OnBattlefieldPlayerLeaveZone(bf, player));
|
||||
}
|
||||
|
||||
void ScriptMgr::OnBattlefieldPlayerJoinWar(Battlefield* bf, Player* player)
|
||||
{
|
||||
CALL_ENABLED_HOOKS(BattlefieldScript, BATTLEFIELDHOOK_ON_PLAYER_JOIN_WAR, script->OnBattlefieldPlayerJoinWar(bf, player));
|
||||
}
|
||||
|
||||
void ScriptMgr::OnBattlefieldPlayerLeaveWar(Battlefield* bf, Player* player)
|
||||
{
|
||||
CALL_ENABLED_HOOKS(BattlefieldScript, BATTLEFIELDHOOK_ON_PLAYER_LEAVE_WAR, script->OnBattlefieldPlayerLeaveWar(bf, player));
|
||||
}
|
||||
|
||||
void ScriptMgr::OnBattlefieldBeforeInvitePlayerToWar(Battlefield* bf, Player* player)
|
||||
{
|
||||
CALL_ENABLED_HOOKS(BattlefieldScript, BATTLEFIELDHOOK_BEFORE_INVITE_PLAYER_TO_WAR, script->OnBattlefieldBeforeInvitePlayerToWar(bf, player));
|
||||
}
|
||||
|
||||
BattlefieldScript::BattlefieldScript(char const* name, std::vector<uint16> enabledHooks) :
|
||||
ScriptObject(name, BATTLEFIELDHOOK_END)
|
||||
{
|
||||
// If empty - enable all available hooks.
|
||||
if (enabledHooks.empty())
|
||||
for (uint16 i = 0; i < BATTLEFIELDHOOK_END; ++i)
|
||||
enabledHooks.emplace_back(i);
|
||||
|
||||
ScriptRegistry<BattlefieldScript>::AddScript(this, std::move(enabledHooks));
|
||||
}
|
||||
|
||||
template class AC_GAME_API ScriptRegistry<BattlefieldScript>;
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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 General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SCRIPT_OBJECT_BATTLEFIELD_SCRIPT_H_
|
||||
#define SCRIPT_OBJECT_BATTLEFIELD_SCRIPT_H_
|
||||
|
||||
#include "ScriptObject.h"
|
||||
#include <vector>
|
||||
|
||||
enum BattlefieldHook
|
||||
{
|
||||
BATTLEFIELDHOOK_ON_PLAYER_ENTER_ZONE, // 0 - fires at start of HandlePlayerEnterZone, before team assignment
|
||||
BATTLEFIELDHOOK_ON_PLAYER_LEAVE_ZONE, // 1 - fires at end of HandlePlayerLeaveZone, after all cleanup
|
||||
BATTLEFIELDHOOK_ON_PLAYER_JOIN_WAR, // 2 - fires after player is added to the active war
|
||||
BATTLEFIELDHOOK_ON_PLAYER_LEAVE_WAR, // 3 - fires after player is removed from the active war
|
||||
BATTLEFIELDHOOK_BEFORE_INVITE_PLAYER_TO_WAR, // 4 - fires in InvitePlayerToWar before InvitedPlayers insert
|
||||
BATTLEFIELDHOOK_END
|
||||
};
|
||||
|
||||
class Battlefield;
|
||||
class Player;
|
||||
|
||||
class BattlefieldScript : public ScriptObject
|
||||
{
|
||||
protected:
|
||||
BattlefieldScript(const char* name, std::vector<uint16> enabledHooks = std::vector<uint16>());
|
||||
|
||||
public:
|
||||
[[nodiscard]] bool IsDatabaseBound() const override { return false; }
|
||||
|
||||
/**
|
||||
* @brief Called when a player enters the battlefield zone, before team-based data assignment.
|
||||
* This is the ideal place to reassign a player's team for cross-faction purposes.
|
||||
*
|
||||
* @param bf The Battlefield instance
|
||||
* @param player The player entering the zone
|
||||
*/
|
||||
virtual void OnBattlefieldPlayerEnterZone(Battlefield* /*bf*/, Player* /*player*/) { }
|
||||
|
||||
/**
|
||||
* @brief Called when a player leaves the battlefield zone, after all cleanup.
|
||||
* This is the ideal place to restore a player's original team/faction.
|
||||
*
|
||||
* @param bf The Battlefield instance
|
||||
* @param player The player leaving the zone
|
||||
*/
|
||||
virtual void OnBattlefieldPlayerLeaveZone(Battlefield* /*bf*/, Player* /*player*/) { }
|
||||
|
||||
/**
|
||||
* @brief Called after a player has been added to the active war (accepted the invitation).
|
||||
*
|
||||
* @param bf The Battlefield instance
|
||||
* @param player The player joining the war
|
||||
*/
|
||||
virtual void OnBattlefieldPlayerJoinWar(Battlefield* /*bf*/, Player* /*player*/) { }
|
||||
|
||||
/**
|
||||
* @brief Called after a player has been removed from the active war.
|
||||
*
|
||||
* @param bf The Battlefield instance
|
||||
* @param player The player leaving the war
|
||||
*/
|
||||
virtual void OnBattlefieldPlayerLeaveWar(Battlefield* /*bf*/, Player* /*player*/) { }
|
||||
|
||||
/**
|
||||
* @brief Called inside InvitePlayerToWar after the WillBeKick entry is erased
|
||||
* (using the player's current team) and before the player is inserted into
|
||||
* m_InvitedPlayers. This is the correct place to reassign a player's effective
|
||||
* team for pre-war zone players: the invite bucket write that follows will use
|
||||
* the newly assigned team, keeping all subsequent core operations consistent.
|
||||
*
|
||||
* @param bf The Battlefield instance
|
||||
* @param player The player being invited to war
|
||||
*/
|
||||
virtual void OnBattlefieldBeforeInvitePlayerToWar(Battlefield* /*bf*/, Player* /*player*/) { }
|
||||
};
|
||||
|
||||
#endif // SCRIPT_OBJECT_BATTLEFIELD_SCRIPT_H_
|
||||
@@ -43,6 +43,7 @@
|
||||
|
||||
class AuctionHouseObject;
|
||||
class AuraScript;
|
||||
class Battlefield;
|
||||
class Battleground;
|
||||
class BattlegroundMap;
|
||||
class BattlegroundQueue;
|
||||
@@ -598,6 +599,13 @@ public: /* AllMapScript */
|
||||
void OnBeforeCreateInstanceScript(InstanceMap* instanceMap, InstanceScript** instanceData, bool load, std::string data, uint32 completedEncounterMask);
|
||||
void OnDestroyInstance(MapInstanced* mapInstanced, Map* map);
|
||||
|
||||
public: /* BattlefieldScript */
|
||||
void OnBattlefieldPlayerEnterZone(Battlefield* bf, Player* player);
|
||||
void OnBattlefieldPlayerLeaveZone(Battlefield* bf, Player* player);
|
||||
void OnBattlefieldPlayerJoinWar(Battlefield* bf, Player* player);
|
||||
void OnBattlefieldPlayerLeaveWar(Battlefield* bf, Player* player);
|
||||
void OnBattlefieldBeforeInvitePlayerToWar(Battlefield* bf, Player* player);
|
||||
|
||||
public: /* BGScript */
|
||||
void OnBattlegroundStart(Battleground* bg);
|
||||
void OnBattlegroundEndReward(Battleground* bg, Player* player, TeamId winnerTeamId);
|
||||
|
||||
@@ -379,8 +379,6 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
|
||||
if (updater.ProcessUnsafe())
|
||||
UpdateTimeOutTime(diff);
|
||||
|
||||
HandleTeleportTimeout(updater.ProcessUnsafe());
|
||||
|
||||
///- Retrieve packets from the receive queue and call the appropriate handlers
|
||||
/// not process packets if socket already closed
|
||||
WorldPacket* packet = nullptr;
|
||||
@@ -635,36 +633,6 @@ bool WorldSession::IsSocketClosed() const
|
||||
return !m_Socket || !m_Socket->IsOpen();
|
||||
}
|
||||
|
||||
void WorldSession::HandleTeleportTimeout(bool updateInSessions)
|
||||
{
|
||||
// pussywizard: handle teleport ack timeout
|
||||
if (m_Socket && m_Socket->IsOpen() && GetPlayer() && GetPlayer()->IsBeingTeleported())
|
||||
{
|
||||
time_t currTime = GameTime::GetGameTime().count();
|
||||
if (updateInSessions) // session update from World::UpdateSessions
|
||||
{
|
||||
if (GetPlayer()->IsBeingTeleportedFar() && GetPlayer()->GetSemaphoreTeleportFar() + sWorld->getIntConfig(CONFIG_TELEPORT_TIMEOUT_FAR) < currTime)
|
||||
while (GetPlayer() && GetPlayer()->IsBeingTeleportedFar())
|
||||
HandleMoveWorldportAck();
|
||||
}
|
||||
else // session update from Map::Update
|
||||
{
|
||||
if (GetPlayer()->IsBeingTeleportedNear() && GetPlayer()->GetSemaphoreTeleportNear() + sWorld->getIntConfig(CONFIG_TELEPORT_TIMEOUT_NEAR) < currTime)
|
||||
while (GetPlayer() && GetPlayer()->IsInWorld() && GetPlayer()->IsBeingTeleportedNear())
|
||||
{
|
||||
Player* plMover = GetPlayer()->m_mover->ToPlayer();
|
||||
if (!plMover)
|
||||
break;
|
||||
WorldPacket pkt(MSG_MOVE_TELEPORT_ACK, 20);
|
||||
pkt << plMover->GetPackGUID();
|
||||
pkt << uint32(0); // flags
|
||||
pkt << uint32(0); // time
|
||||
HandleMoveTeleportAck(pkt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// %Log the player out
|
||||
void WorldSession::LogoutPlayer(bool save)
|
||||
{
|
||||
|
||||
@@ -1157,7 +1157,6 @@ public: // opcodes handlers
|
||||
void HandleEnterPlayerVehicle(WorldPacket& data);
|
||||
void HandleUpdateProjectilePosition(WorldPacket& recvPacket);
|
||||
|
||||
void HandleTeleportTimeout(bool updateInSessions);
|
||||
bool HandleSocketClosed();
|
||||
void SetOfflineTime(uint32 time) { _offlineTime = time; }
|
||||
uint32 GetOfflineTime() const { return _offlineTime; }
|
||||
|
||||
@@ -820,6 +820,17 @@ void AuraEffect::ApplySpellMod(Unit* target, bool apply)
|
||||
// Auras with charges do not mod amount of passive auras
|
||||
if (GetBase()->IsUsingCharges())
|
||||
return;
|
||||
|
||||
// Guard against infinite recursion: a spell mod recalculating an aura that
|
||||
// triggers ApplySpellMod again (self-referencing or mutual spell mods).
|
||||
if (m_isRecalculatingPassiveAuras)
|
||||
{
|
||||
LOG_DEBUG("spells.aura", "AuraEffect::ApplySpellMod: Recursion detected for spell {} effect {}, skipping passive aura recalculation",
|
||||
GetId(), GetEffIndex());
|
||||
return;
|
||||
}
|
||||
m_isRecalculatingPassiveAuras = true;
|
||||
|
||||
// reapply some passive spells after add/remove related spellmods
|
||||
// Warning: it is a dead loop if 2 auras each other amount-shouldn't happen
|
||||
switch (GetMiscValue())
|
||||
@@ -906,6 +917,8 @@ void AuraEffect::ApplySpellMod(Unit* target, bool apply)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
m_isRecalculatingPassiveAuras = false;
|
||||
}
|
||||
|
||||
void AuraEffect::Update(uint32 diff, Unit* caster)
|
||||
@@ -6756,7 +6769,9 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const
|
||||
// Set trigger flag
|
||||
uint32 procAttacker = PROC_FLAG_DONE_PERIODIC;
|
||||
uint32 procVictim = PROC_FLAG_TAKEN_PERIODIC;
|
||||
uint32 procEx = (crit ? PROC_EX_CRITICAL_HIT : PROC_EX_NORMAL_HIT) | PROC_EX_INTERNAL_DOT;
|
||||
uint32 procEx = PROC_EX_INTERNAL_DOT;
|
||||
if (damage)
|
||||
procEx |= crit ? PROC_EX_CRITICAL_HIT : PROC_EX_NORMAL_HIT;
|
||||
if (absorb > 0)
|
||||
procEx |= PROC_EX_ABSORB;
|
||||
|
||||
@@ -6843,7 +6858,9 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c
|
||||
// Set trigger flag
|
||||
uint32 procAttacker = PROC_FLAG_DONE_PERIODIC;
|
||||
uint32 procVictim = PROC_FLAG_TAKEN_PERIODIC;
|
||||
uint32 procEx = (crit ? PROC_EX_CRITICAL_HIT : PROC_EX_NORMAL_HIT) | PROC_EX_INTERNAL_DOT;
|
||||
uint32 procEx = PROC_EX_INTERNAL_DOT;
|
||||
if (dmgInfo.GetDamage())
|
||||
procEx |= crit ? PROC_EX_CRITICAL_HIT : PROC_EX_NORMAL_HIT;
|
||||
if (absorb > 0)
|
||||
procEx |= PROC_EX_ABSORB;
|
||||
|
||||
|
||||
@@ -144,6 +144,7 @@ private:
|
||||
uint8 const m_effIndex;
|
||||
bool m_canBeRecalculated;
|
||||
bool m_isPeriodic;
|
||||
bool m_isRecalculatingPassiveAuras = false;
|
||||
private:
|
||||
float CalcPeriodicCritChance(Unit const* caster, Unit const* target) const;
|
||||
|
||||
|
||||
@@ -2559,11 +2559,15 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
|
||||
if (effectUnit->IsAlive() != target->alive)
|
||||
return;
|
||||
|
||||
// Xinef: absorb delayed projectiles for 500ms
|
||||
if (getState() == SPELL_STATE_DELAYED && !m_spellInfo->IsTargetingArea() && !m_spellInfo->IsPositive() &&
|
||||
(GameTime::GetGameTimeMS().count() - target->timeDelay) <= effectUnit->m_lastSanctuaryTime && GameTime::GetGameTimeMS().count() < (effectUnit->m_lastSanctuaryTime + 500) &&
|
||||
effectUnit->FindMap() && !effectUnit->FindMap()->IsDungeon()
|
||||
)
|
||||
// Absorb delayed projectiles launched before Sanctuary (e.g. Vanish dodging a Frostbolt in flight)
|
||||
if (getState() == SPELL_STATE_DELAYED && !m_spellInfo->IsPositive() &&
|
||||
(GameTime::GetGameTimeMS().count() - target->timeDelay) <= effectUnit->m_lastSanctuaryTime)
|
||||
return; // No missinfo in that case
|
||||
|
||||
// Absorb instant hostile spells on application within brief window after Sanctuary
|
||||
if (getState() != SPELL_STATE_DELAYED && !m_spellInfo->IsPositive() &&
|
||||
effectUnit->m_lastSanctuaryTime &&
|
||||
GameTime::GetGameTimeMS().count() <= (effectUnit->m_lastSanctuaryTime + 400))
|
||||
return; // No missinfo in that case
|
||||
|
||||
// Get original caster (if exist) and calculate damage/healing from him data
|
||||
@@ -3069,9 +3073,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA
|
||||
}
|
||||
|
||||
if (m_caster != unit && m_caster->IsHostileTo(unit) && !m_spellInfo->IsPositive() && !m_triggeredByAuraSpell && !m_spellInfo->HasAttribute(SPELL_ATTR0_CU_DONT_BREAK_STEALTH))
|
||||
{
|
||||
unit->RemoveAurasByType(SPELL_AURA_MOD_STEALTH);
|
||||
}
|
||||
|
||||
if (aura_effmask)
|
||||
{
|
||||
@@ -3907,9 +3909,6 @@ void Spell::_cast(bool skipCheck)
|
||||
// we must send smsg_spell_go packet before m_castItem delete in TakeCastItem()...
|
||||
SendSpellGo();
|
||||
|
||||
if (modOwner)
|
||||
modOwner->SetSpellModTakingSpell(this, true);
|
||||
|
||||
bool resetAttackTimers = IsAutoActionResetSpell() && !m_spellInfo->HasAttribute(SPELL_ATTR2_DO_NOT_RESET_COMBAT_TIMERS);
|
||||
if (resetAttackTimers)
|
||||
{
|
||||
@@ -4098,6 +4097,14 @@ void Spell::handle_immediate()
|
||||
// process immediate effects (items, ground, etc.) also initialize some variables
|
||||
_handle_immediate_phase();
|
||||
|
||||
// Sync persistent area aura duration with hasted channel duration
|
||||
if (m_spellInfo->IsChanneled() && m_spellAura && m_channeledDuration > 0
|
||||
&& m_spellAura->GetType() == DYNOBJ_AURA_TYPE)
|
||||
{
|
||||
m_spellAura->SetMaxDuration(m_channeledDuration);
|
||||
m_spellAura->SetDuration(m_channeledDuration);
|
||||
}
|
||||
|
||||
for (std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
|
||||
DoAllEffectOnTarget(&(*ihit));
|
||||
|
||||
@@ -4472,10 +4479,6 @@ void Spell::finish(bool ok)
|
||||
if (m_spellInfo->IsCooldownStartedOnEvent())
|
||||
m_caster->ToPlayer()->SendCooldownEvent(m_spellInfo, 0, 0, false);
|
||||
|
||||
// Rogue fix: Remove Cold Blood if Mutilate off-hand failed
|
||||
if (m_spellInfo->Id == 27576) // Mutilate, off-hand
|
||||
if (m_caster->HasAura(14177))
|
||||
m_caster->RemoveAura(14177);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -509,6 +509,7 @@ public:
|
||||
void SendPetCastResult(SpellCastResult result);
|
||||
void SendSpellStart();
|
||||
void SendSpellGo();
|
||||
|
||||
void SendSpellCooldown();
|
||||
void SendLogExecute();
|
||||
void ExecuteLogEffectTakeTargetPower(uint8 effIndex, Unit* target, uint32 PowerType, uint32 powerTaken, float gainMultiplier);
|
||||
@@ -564,6 +565,7 @@ public:
|
||||
bool IsNextMeleeSwingSpell() const;
|
||||
bool IsTriggered() const { return HasTriggeredCastFlag(TRIGGERED_FULL_MASK); };
|
||||
bool HasTriggeredCastFlag(TriggerCastFlags flag) const { return _triggeredCastFlags & flag; };
|
||||
[[nodiscard]] bool IsProcDisabled() const { return HasTriggeredCastFlag(TRIGGERED_DISALLOW_PROC_EVENTS); }
|
||||
bool IsChannelActive() const { return m_caster->GetUInt32Value(UNIT_CHANNEL_SPELL) != 0; }
|
||||
bool IsAutoActionResetSpell() const;
|
||||
bool IsIgnoringCooldowns() const;
|
||||
|
||||
@@ -1885,9 +1885,8 @@ void SpellMgr::LoadSpellInfoCorrections()
|
||||
spellInfo->Effects[EFFECT_1].Effect = 0;
|
||||
});
|
||||
|
||||
// Ulduar, Mimiron, Magnetic Core (summon)
|
||||
// Meeting Stone Summon
|
||||
ApplySpellFix({ 64444, 23598 }, [](SpellInfo* spellInfo)
|
||||
ApplySpellFix({ 23598 }, [](SpellInfo* spellInfo)
|
||||
{
|
||||
spellInfo->Effects[EFFECT_0].TargetA = SpellImplicitTargetInfo(TARGET_DEST_CASTER);
|
||||
});
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "InstanceScript.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Player.h"
|
||||
#include "RaceMgr.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "Spell.h"
|
||||
@@ -2028,6 +2029,24 @@ void SpellMgr::LoadSpellProcs()
|
||||
|
||||
if (!addTriggerFlag && isAlwaysTriggeredAura[auraName])
|
||||
addTriggerFlag = true;
|
||||
|
||||
// Many proc auras with taken procFlag mask don't have
|
||||
// attribute "can proc with triggered" — they should
|
||||
// proc nevertheless (e.g. mage armor spells with
|
||||
// judgement)
|
||||
if (!addTriggerFlag
|
||||
&& (spellInfo->ProcFlags & TAKEN_HIT_PROC_FLAG_MASK))
|
||||
{
|
||||
switch (auraName)
|
||||
{
|
||||
case SPELL_AURA_PROC_TRIGGER_SPELL:
|
||||
case SPELL_AURA_PROC_TRIGGER_DAMAGE:
|
||||
addTriggerFlag = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
@@ -2779,7 +2798,7 @@ void SpellMgr::LoadSpellAreas()
|
||||
}
|
||||
}
|
||||
|
||||
if (spellArea.raceMask && (spellArea.raceMask & RACEMASK_ALL_PLAYABLE) == 0)
|
||||
if (spellArea.raceMask && (spellArea.raceMask & sRaceMgr->GetPlayableRaceMask()) == 0)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Spell {} listed in `spell_area` have wrong race mask ({}) requirement", spell, spellArea.raceMask);
|
||||
continue;
|
||||
|
||||
@@ -41,6 +41,19 @@ namespace GameTime
|
||||
/// Uptime
|
||||
AC_GAME_API Seconds GetUptime();
|
||||
|
||||
/// Uptime since a given time point
|
||||
inline Microseconds Elapsed(TimePoint start)
|
||||
{
|
||||
return std::chrono::duration_cast<Microseconds>(Now() - start);
|
||||
}
|
||||
|
||||
/// Check if a duration has elapsed since a given time point
|
||||
template<class T>
|
||||
inline bool HasElapsed(TimePoint start, T duration)
|
||||
{
|
||||
return (Now() - start) >= duration;
|
||||
}
|
||||
|
||||
/// Update all timers
|
||||
void UpdateGameTimers();
|
||||
}
|
||||
|
||||
@@ -38,9 +38,12 @@ Warden::Warden() : _session(nullptr), _checkTimer(10000/*10 sec*/), _clientRespo
|
||||
|
||||
Warden::~Warden()
|
||||
{
|
||||
delete[] _module->CompressedData;
|
||||
delete _module;
|
||||
_module = nullptr;
|
||||
if (_module)
|
||||
{
|
||||
delete[] _module->CompressedData;
|
||||
delete _module;
|
||||
_module = nullptr;
|
||||
}
|
||||
_initialized = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,9 +72,12 @@ ClientWardenModule* WardenMac::GetModuleForClient()
|
||||
{
|
||||
auto mod = new ClientWardenModule;
|
||||
|
||||
uint32 length = Module_0DBBF209A27B1E279A9FEC5C168A15F7_Data.size();
|
||||
|
||||
// data assign
|
||||
mod->CompressedSize = Module_0DBBF209A27B1E279A9FEC5C168A15F7_Data.size();
|
||||
mod->CompressedData = Module_0DBBF209A27B1E279A9FEC5C168A15F7_Data.data();
|
||||
mod->CompressedSize = length;
|
||||
mod->CompressedData = new uint8[length];
|
||||
memcpy(mod->CompressedData, Module_0DBBF209A27B1E279A9FEC5C168A15F7_Data.data(), length);
|
||||
|
||||
// md5 hash
|
||||
mod->Id = Acore::Crypto::MD5::GetDigestOf(mod->CompressedData, mod->CompressedSize);
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
#include "Player.h"
|
||||
#include "PlayerDump.h"
|
||||
#include "PoolMgr.h"
|
||||
#include "RaceMgr.h"
|
||||
#include "Realm.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "ServerMailMgr.h"
|
||||
@@ -386,6 +387,9 @@ void World::SetInitialWorldSettings()
|
||||
// Load cinematic cameras
|
||||
LoadM2Cameras(_dataPath);
|
||||
|
||||
LOG_INFO("server.loading", "Loading Player race data...");
|
||||
sRaceMgr->LoadRaces();
|
||||
|
||||
// Load IP Location Database
|
||||
sIPLocation->Load();
|
||||
|
||||
|
||||
@@ -304,8 +304,6 @@ void WorldConfig::BuildConfigCache()
|
||||
SetConfigValue<uint32>(CONFIG_LOGDB_CLEARINTERVAL, "LogDB.Opt.ClearInterval", 10, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value > 0; }, "> 0");
|
||||
SetConfigValue<uint32>(CONFIG_LOGDB_CLEARTIME, "LogDB.Opt.ClearTime", 1209600);
|
||||
|
||||
SetConfigValue<uint32>(CONFIG_TELEPORT_TIMEOUT_NEAR, "TeleportTimeoutNear", 25);
|
||||
SetConfigValue<uint32>(CONFIG_TELEPORT_TIMEOUT_FAR, "TeleportTimeoutFar", 45);
|
||||
SetConfigValue<uint32>(CONFIG_MAX_ALLOWED_MMR_DROP, "MaxAllowedMMRDrop", 500);
|
||||
SetConfigValue<bool>(CONFIG_ENABLE_LOGIN_AFTER_DC, "EnableLoginAfterDC", true);
|
||||
SetConfigValue<bool>(CONFIG_DONT_CACHE_RANDOM_MOVEMENT_PATHS, "DontCacheRandomMovementPaths", false);
|
||||
@@ -562,6 +560,7 @@ void WorldConfig::BuildConfigCache()
|
||||
SetConfigValue<uint32>(CONFIG_LFG_OPTIONSMASK, "DungeonFinder.OptionsMask", 5);
|
||||
SetConfigValue<bool>(CONFIG_LFG_CAST_DESERTER, "DungeonFinder.CastDeserter", true);
|
||||
SetConfigValue<bool>(CONFIG_LFG_ALLOW_COMPLETED, "DungeonFinder.AllowCompleted", true);
|
||||
SetConfigValue<uint32>(CONFIG_LFG_DUNGEON_SELECTION_COOLDOWN, "DungeonFinder.DungeonSelectionCooldown", 0);
|
||||
|
||||
// DBC_ItemAttributes
|
||||
SetConfigValue<bool>(CONFIG_DBC_ENFORCE_ITEM_ATTRIBUTES, "DBC.EnforceItemAttributes", true);
|
||||
|
||||
@@ -300,8 +300,6 @@ enum ServerConfigs
|
||||
CONFIG_NUMTHREADS,
|
||||
CONFIG_LOGDB_CLEARINTERVAL,
|
||||
CONFIG_LOGDB_CLEARTIME,
|
||||
CONFIG_TELEPORT_TIMEOUT_NEAR,
|
||||
CONFIG_TELEPORT_TIMEOUT_FAR,
|
||||
CONFIG_MAX_ALLOWED_MMR_DROP,
|
||||
CONFIG_CLIENTCACHE_VERSION,
|
||||
CONFIG_GUILD_EVENT_LOG_COUNT,
|
||||
@@ -321,6 +319,7 @@ enum ServerConfigs
|
||||
CONFIG_PRESERVE_CUSTOM_CHANNEL_DURATION,
|
||||
CONFIG_PERSISTENT_CHARACTER_CLEAN_FLAGS,
|
||||
CONFIG_LFG_OPTIONSMASK,
|
||||
CONFIG_LFG_DUNGEON_SELECTION_COOLDOWN,
|
||||
CONFIG_MAX_INSTANCES_PER_HOUR,
|
||||
CONFIG_WINTERGRASP_PLR_MAX,
|
||||
CONFIG_WINTERGRASP_PLR_MIN,
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "BattlefieldMgr.h"
|
||||
#include "Chat.h"
|
||||
#include "CommandScript.h"
|
||||
#include "Language.h"
|
||||
|
||||
using namespace Acore::ChatCommands;
|
||||
|
||||
@@ -30,11 +31,11 @@ public:
|
||||
{
|
||||
static ChatCommandTable battlefieldcommandTable =
|
||||
{
|
||||
{ "start", HandleBattlefieldStart, SEC_ADMINISTRATOR, Console::No },
|
||||
{ "stop", HandleBattlefieldEnd, SEC_ADMINISTRATOR, Console::No },
|
||||
{ "switch", HandleBattlefieldSwitch, SEC_ADMINISTRATOR, Console::No },
|
||||
{ "timer", HandleBattlefieldTimer, SEC_ADMINISTRATOR, Console::No },
|
||||
{ "enable", HandleBattlefieldEnable, SEC_ADMINISTRATOR, Console::No }
|
||||
{ "start", HandleBattlefieldStart, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "stop", HandleBattlefieldEnd, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "switch", HandleBattlefieldSwitch, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "timer", HandleBattlefieldTimer, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "enable", HandleBattlefieldEnable, SEC_ADMINISTRATOR, Console::Yes }
|
||||
};
|
||||
static ChatCommandTable commandTable =
|
||||
{
|
||||
@@ -48,12 +49,15 @@ public:
|
||||
Battlefield* bf = sBattlefieldMgr->GetBattlefieldByBattleId(battleId);
|
||||
|
||||
if (!bf)
|
||||
{
|
||||
handler->SendErrorMessage(LANG_BF_NOT_FOUND, battleId);
|
||||
return false;
|
||||
}
|
||||
|
||||
bf->StartBattle();
|
||||
|
||||
if (battleId == 1)
|
||||
handler->SendGlobalGMSysMessage("Wintergrasp (Command start used)");
|
||||
handler->SendWorldText(LANG_BF_STARTED, battleId);
|
||||
if (handler->IsConsole())
|
||||
handler->PSendSysMessage(LANG_BF_STARTED, battleId);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -63,12 +67,15 @@ public:
|
||||
Battlefield* bf = sBattlefieldMgr->GetBattlefieldByBattleId(battleId);
|
||||
|
||||
if (!bf)
|
||||
{
|
||||
handler->SendErrorMessage(LANG_BF_NOT_FOUND, battleId);
|
||||
return false;
|
||||
}
|
||||
|
||||
bf->EndBattle(true);
|
||||
|
||||
if (battleId == 1)
|
||||
handler->SendGlobalGMSysMessage("Wintergrasp (Command stop used)");
|
||||
handler->SendWorldText(LANG_BF_STOPPED, battleId);
|
||||
if (handler->IsConsole())
|
||||
handler->PSendSysMessage(LANG_BF_STOPPED, battleId);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -78,19 +85,24 @@ public:
|
||||
Battlefield* bf = sBattlefieldMgr->GetBattlefieldByBattleId(battleId);
|
||||
|
||||
if (!bf)
|
||||
{
|
||||
handler->SendErrorMessage(LANG_BF_NOT_FOUND, battleId);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bf->IsEnabled())
|
||||
{
|
||||
bf->ToggleBattlefield(false);
|
||||
if (battleId == 1)
|
||||
handler->SendGlobalGMSysMessage("Wintergrasp is disabled");
|
||||
handler->SendWorldText(LANG_BF_DISABLED, battleId);
|
||||
if (handler->IsConsole())
|
||||
handler->PSendSysMessage(LANG_BF_DISABLED, battleId);
|
||||
}
|
||||
else
|
||||
{
|
||||
bf->ToggleBattlefield(true);
|
||||
if (battleId == 1)
|
||||
handler->SendGlobalGMSysMessage("Wintergrasp is enabled");
|
||||
handler->SendWorldText(LANG_BF_ENABLED, battleId);
|
||||
if (handler->IsConsole())
|
||||
handler->PSendSysMessage(LANG_BF_ENABLED, battleId);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -101,11 +113,15 @@ public:
|
||||
Battlefield* bf = sBattlefieldMgr->GetBattlefieldByBattleId(battleId);
|
||||
|
||||
if (!bf)
|
||||
{
|
||||
handler->SendErrorMessage(LANG_BF_NOT_FOUND, battleId);
|
||||
return false;
|
||||
}
|
||||
|
||||
bf->EndBattle(false);
|
||||
if (battleId == 1)
|
||||
handler->SendGlobalGMSysMessage("Wintergrasp (Command switch used)");
|
||||
handler->SendWorldText(LANG_BF_SWITCHED, battleId);
|
||||
if (handler->IsConsole())
|
||||
handler->PSendSysMessage(LANG_BF_SWITCHED, battleId);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -138,12 +154,16 @@ public:
|
||||
Battlefield* bf = sBattlefieldMgr->GetBattlefieldByBattleId(battleId);
|
||||
|
||||
if (!bf)
|
||||
{
|
||||
handler->SendErrorMessage(LANG_BF_NOT_FOUND, battleId);
|
||||
return false;
|
||||
}
|
||||
|
||||
bf->SetTimer(time * IN_MILLISECONDS);
|
||||
bf->SendInitWorldStatesToAll();
|
||||
if (battleId == 1)
|
||||
handler->SendGlobalGMSysMessage("Wintergrasp (Command timer used)");
|
||||
handler->SendWorldText(LANG_BF_TIMER_SET, battleId, time);
|
||||
if (handler->IsConsole())
|
||||
handler->PSendSysMessage(LANG_BF_TIMER_SET, battleId, time);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -47,11 +47,12 @@ public:
|
||||
{
|
||||
static ChatCommandTable lfgCommandTable =
|
||||
{
|
||||
{ "player", HandleLfgPlayerInfoCommand, SEC_MODERATOR, Console::No },
|
||||
{ "group", HandleLfgGroupInfoCommand, SEC_MODERATOR, Console::No },
|
||||
{ "queue", HandleLfgQueueInfoCommand, SEC_MODERATOR, Console::Yes },
|
||||
{ "clean", HandleLfgCleanCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "options", HandleLfgOptionsCommand, SEC_GAMEMASTER, Console::Yes },
|
||||
{ "player", HandleLfgPlayerInfoCommand, SEC_MODERATOR, Console::No },
|
||||
{ "group", HandleLfgGroupInfoCommand, SEC_MODERATOR, Console::No },
|
||||
{ "queue", HandleLfgQueueInfoCommand, SEC_MODERATOR, Console::Yes },
|
||||
{ "clean", HandleLfgCleanCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "options", HandleLfgOptionsCommand, SEC_GAMEMASTER, Console::Yes },
|
||||
{ "cooldown", HandleLfgCooldownClearCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
};
|
||||
|
||||
static ChatCommandTable commandTable =
|
||||
@@ -126,6 +127,13 @@ public:
|
||||
sLFGMgr->Clean();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleLfgCooldownClearCommand(ChatHandler* handler)
|
||||
{
|
||||
sLFGMgr->ClearDungeonCooldowns();
|
||||
handler->SendSysMessage(LANG_LFG_COOLDOWN_CLEARED);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_lfg_commandscript()
|
||||
|
||||
@@ -286,6 +286,34 @@ public:
|
||||
if (!handler->GetSession())
|
||||
handler->SendSysMessage(LANG_RESETALL_TALENTS);
|
||||
}
|
||||
else if (caseName == "honor")
|
||||
{
|
||||
CharacterDatabase.Execute(CharacterDatabase.GetPreparedStatement(CHAR_UPD_ALL_HONOR_POINTS));
|
||||
|
||||
sWorldSessionMgr->DoForAllOnlinePlayers([](Player* player)
|
||||
{
|
||||
player->SetHonorPoints(0);
|
||||
});
|
||||
|
||||
handler->SendWorldText(LANG_RESETALL_HONOR);
|
||||
if (!handler->GetSession())
|
||||
handler->SendSysMessage(LANG_RESETALL_HONOR);
|
||||
return true;
|
||||
}
|
||||
else if (caseName == "arena")
|
||||
{
|
||||
CharacterDatabase.Execute(CharacterDatabase.GetPreparedStatement(CHAR_UPD_ALL_ARENA_POINTS));
|
||||
|
||||
sWorldSessionMgr->DoForAllOnlinePlayers([](Player* player)
|
||||
{
|
||||
player->SetArenaPoints(0);
|
||||
});
|
||||
|
||||
handler->SendWorldText(LANG_RESETALL_ARENA);
|
||||
if (!handler->GetSession())
|
||||
handler->SendSysMessage(LANG_RESETALL_ARENA);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
handler->SendErrorMessage(LANG_RESETALL_UNKNOWN_CASE, caseName);
|
||||
|
||||
@@ -90,7 +90,6 @@ enum GosNpcs
|
||||
{
|
||||
NPC_FLAME_LEVIATHAN_TURRET = 33139,
|
||||
NPC_SEAT = 33114,
|
||||
NPC_MECHANOLIFT = 33214,
|
||||
NPC_LIQUID = 33189,
|
||||
|
||||
// Starting event
|
||||
@@ -116,14 +115,13 @@ enum Events
|
||||
EVENT_MISSILE = 2,
|
||||
EVENT_VENT = 3,
|
||||
EVENT_SPEED = 4,
|
||||
EVENT_SUMMON = 5,
|
||||
EVENT_REINSTALL = 6,
|
||||
EVENT_HODIRS_FURY = 7,
|
||||
EVENT_FREYA = 8,
|
||||
EVENT_MIMIRONS_INFERNO = 9,
|
||||
EVENT_THORIMS_HAMMER = 10,
|
||||
EVENT_SOUND_BEGINNING = 11,
|
||||
EVENT_POSITION_CHECK = 12,
|
||||
EVENT_REINSTALL = 5,
|
||||
EVENT_HODIRS_FURY = 6,
|
||||
EVENT_FREYA = 7,
|
||||
EVENT_MIMIRONS_INFERNO = 8,
|
||||
EVENT_THORIMS_HAMMER = 9,
|
||||
EVENT_SOUND_BEGINNING = 10,
|
||||
EVENT_POSITION_CHECK = 11,
|
||||
};
|
||||
|
||||
enum Texts
|
||||
@@ -153,12 +151,6 @@ enum Texts
|
||||
BRANN_RADIO_SAY_FL_START_0 = 0,
|
||||
BRANN_RADIO_SAY_FL_START_1 = 1,
|
||||
BRANN_RADIO_SAY_FL_START_2 = 2,
|
||||
BRANN_RADIO_SAY_GENERATORS = 3,
|
||||
BRANN_RADIO_SAY_STATIONS = 4,
|
||||
BRANN_RADIO_SAY_TOWER_THORIM = 5,
|
||||
BRANN_RADIO_SAY_TOWER_HODIR = 6,
|
||||
BRANN_RADIO_SAY_TOWER_FREYA = 7,
|
||||
BRANN_RADIO_SAY_TOWER_MIMIRON = 8,
|
||||
|
||||
// Vehicle Repair - Said by a spell, BroadcastTextID, same as FLAME_LEVIATHAN_EMOTE_REPAIR
|
||||
VEHICLE_EMOTE_REPAIR = 33538,
|
||||
@@ -413,13 +405,6 @@ struct boss_flame_leviathan : public BossAI
|
||||
me->CastSpell(me, SPELL_FLAME_VENTS, false);
|
||||
events.Repeat(20s);
|
||||
return;
|
||||
case EVENT_SUMMON:
|
||||
if (summons.size() < 20)
|
||||
if (Creature* lift = DoSummonFlyer(NPC_MECHANOLIFT, me, 30.0f, 50.0f, 0))
|
||||
lift->GetMotionMaster()->MoveRandom(100);
|
||||
|
||||
events.Repeat(4s);
|
||||
return;
|
||||
case EVENT_SOUND_BEGINNING:
|
||||
if (_towersCount)
|
||||
Talk(FLAME_LEVIATHAN_SAY_HARDMODE);
|
||||
@@ -591,7 +576,6 @@ void boss_flame_leviathan::ScheduleEvents()
|
||||
events.RescheduleEvent(EVENT_MISSILE, 5s);
|
||||
events.RescheduleEvent(EVENT_VENT, 20s);
|
||||
events.RescheduleEvent(EVENT_SPEED, 15s);
|
||||
events.RescheduleEvent(EVENT_SUMMON, 10s);
|
||||
events.RescheduleEvent(EVENT_SOUND_BEGINNING, 10s);
|
||||
events.RescheduleEvent(EVENT_POSITION_CHECK, 5s);
|
||||
|
||||
@@ -1092,87 +1076,6 @@ struct npc_pool_of_tar : public NullCreatureAI
|
||||
}
|
||||
};
|
||||
|
||||
struct npc_brann_radio : public NullCreatureAI
|
||||
{
|
||||
npc_brann_radio(Creature* c) : NullCreatureAI(c)
|
||||
{
|
||||
_lock = (me->GetInstanceScript() && me->GetInstanceScript()->GetBossState(BOSS_LEVIATHAN) > NOT_STARTED);
|
||||
_helpLock = _lock;
|
||||
}
|
||||
|
||||
bool _lock;
|
||||
bool _helpLock;
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
me->SetReactState(REACT_AGGRESSIVE);
|
||||
}
|
||||
|
||||
void MoveInLineOfSight(Unit* who) override
|
||||
{
|
||||
if (!_lock)
|
||||
{
|
||||
if (!who->IsPlayer() && !who->IsVehicle())
|
||||
return;
|
||||
|
||||
// MIMIRON
|
||||
else if (me->GetDistance2d(-81.9207f, 111.432f) < 5.0f)
|
||||
{
|
||||
if (me->GetDistance2d(who) <= 60.0f && who->GetPositionZ() > 430.0f)
|
||||
{
|
||||
Talk(BRANN_RADIO_SAY_TOWER_MIMIRON);
|
||||
_lock = true;
|
||||
}
|
||||
}
|
||||
// FREYA
|
||||
else if (me->GetDistance2d(-221.475f, -271.087f) < 5.0f)
|
||||
{
|
||||
if (me->GetDistance2d(who) <= 60.0f && who->GetPositionZ() < 380.0f)
|
||||
{
|
||||
Talk(BRANN_RADIO_SAY_TOWER_FREYA);
|
||||
_lock = true;
|
||||
}
|
||||
}
|
||||
// STATIONS
|
||||
else if (me->GetDistance2d(73.8978f, -29.3306f) < 5.0f)
|
||||
{
|
||||
if (me->GetDistance2d(who) <= 40.0f)
|
||||
{
|
||||
Talk(BRANN_RADIO_SAY_STATIONS);
|
||||
_lock = true;
|
||||
}
|
||||
}
|
||||
// HODIR
|
||||
else if (me->GetDistance2d(68.7679f, -325.026f) < 5.0f)
|
||||
{
|
||||
if (me->GetDistance2d(who) <= 40.0f)
|
||||
{
|
||||
Talk(BRANN_RADIO_SAY_TOWER_HODIR);
|
||||
_lock = true;
|
||||
}
|
||||
}
|
||||
// THORIM
|
||||
else if (me->GetDistance2d(174.442f, 345.679f) < 5.0f)
|
||||
{
|
||||
if (me->GetDistance2d(who) <= 60.0f)
|
||||
{
|
||||
Talk(BRANN_RADIO_SAY_TOWER_THORIM);
|
||||
_lock = true;
|
||||
}
|
||||
}
|
||||
// COME A BIT CLOSER
|
||||
else if (me->GetDistance2d(-508.898f, -32.9631f) < 5.0f)
|
||||
{
|
||||
if (who->GetPositionX() >= -480.0f)
|
||||
{
|
||||
Talk(BRANN_RADIO_SAY_GENERATORS);
|
||||
_lock = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct npc_storm_beacon_spawn : public NullCreatureAI
|
||||
{
|
||||
npc_storm_beacon_spawn(Creature* c) : NullCreatureAI(c)
|
||||
@@ -1240,43 +1143,6 @@ struct boss_flame_leviathan_safety_container : public NullCreatureAI
|
||||
}
|
||||
};
|
||||
|
||||
struct npc_mechanolift : public NullCreatureAI
|
||||
{
|
||||
npc_mechanolift(Creature* c) : NullCreatureAI(c)
|
||||
{
|
||||
me->SetSpeed(MOVE_RUN, rand_norm() + 0.5f);
|
||||
}
|
||||
|
||||
int32 _startTimer;
|
||||
uint32 _evadeTimer;
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
_startTimer = urand(1, 5000);
|
||||
_evadeTimer = 0;
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (_startTimer)
|
||||
{
|
||||
_startTimer -= diff;
|
||||
if (_startTimer <= 0)
|
||||
{
|
||||
me->GetMotionMaster()->MoveWaypoint(3000000 + urand(0, 11), true);
|
||||
_startTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
_evadeTimer += diff;
|
||||
if (_evadeTimer >= 10000)
|
||||
{
|
||||
_EnterEvadeMode();
|
||||
_evadeTimer = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class go_ulduar_tower : public GameObjectScript
|
||||
{
|
||||
public:
|
||||
@@ -1427,7 +1293,7 @@ public:
|
||||
|
||||
bool operator()(WorldObject* target) const
|
||||
{
|
||||
//! No players, only vehicles (todo: check if blizzlike)
|
||||
//! No players, only vehicles. Pursue is never cast on players.
|
||||
Creature* creatureTarget = target->ToCreature();
|
||||
if (!creatureTarget)
|
||||
return true;
|
||||
@@ -1462,12 +1328,7 @@ class spell_pursue : public SpellScript
|
||||
void FilterTargets(std::list<WorldObject*>& targets)
|
||||
{
|
||||
targets.remove_if(FlameLeviathanPursuedTargetSelector());
|
||||
if (targets.empty())
|
||||
{
|
||||
if (Creature* caster = GetCaster()->ToCreature())
|
||||
caster->AI()->EnterEvadeMode();
|
||||
}
|
||||
else
|
||||
if (!targets.empty())
|
||||
{
|
||||
//! In the end, only one target should be selected
|
||||
WorldObject* _target = Acore::Containers::SelectRandomContainerElement(targets);
|
||||
@@ -1877,10 +1738,8 @@ void AddSC_boss_flame_leviathan()
|
||||
RegisterUlduarCreatureAI(npc_hodirs_fury);
|
||||
|
||||
// Helpers
|
||||
RegisterUlduarCreatureAI(npc_brann_radio);
|
||||
RegisterUlduarCreatureAI(npc_storm_beacon_spawn);
|
||||
RegisterUlduarCreatureAI(boss_flame_leviathan_safety_container);
|
||||
RegisterUlduarCreatureAI(npc_mechanolift);
|
||||
|
||||
// GOs
|
||||
new go_ulduar_tower();
|
||||
|
||||
@@ -62,10 +62,12 @@ enum SpellData
|
||||
SPELL_SPINNING_UP = 63414,
|
||||
|
||||
// PHASE 3:
|
||||
SPELL_PLASMA_BALL = 63689,
|
||||
SPELL_PLASMA_BALL_P1 = 63689,
|
||||
SPELL_PLASMA_BALL_P2 = 65647,
|
||||
|
||||
SPELL_MAGNETIC_CORE = 64436,
|
||||
SPELL_SPINNING = 64438,
|
||||
SPELL_MAGNETIC_CORE_VISUAL = 64438,
|
||||
SPELL_MAGNETIC_CORE_SUMMON = 64444,
|
||||
|
||||
SPELL_SUMMON_BOMB_BOT = 63811,
|
||||
SPELL_BB_EXPLODE = 63801,
|
||||
@@ -194,9 +196,6 @@ enum EVENTS
|
||||
EVENT_BOMB_BOT_RELOCATE = 30,
|
||||
EVENT_SUMMON_ASSAULT_BOT = 40,
|
||||
EVENT_SUMMON_JUNK_BOT = 41,
|
||||
EVENT_MAGNETIC_CORE_PULL_DOWN = 42,
|
||||
EVENT_MAGNETIC_CORE_FREE = 43,
|
||||
EVENT_MAGNETIC_CORE_REMOVE_IMMOBILIZE = 44,
|
||||
|
||||
// Hard mode:
|
||||
EVENT_COMPUTER_SAY_INITIATED = 60,
|
||||
@@ -212,6 +211,12 @@ enum EVENTS
|
||||
EVENT_EMERGENCY_BOT_ATTACK = 70,
|
||||
};
|
||||
|
||||
enum Actions
|
||||
{
|
||||
DO_DISABLE_AERIAL = 1,
|
||||
DO_ENABLE_AERIAL,
|
||||
};
|
||||
|
||||
enum Texts
|
||||
{
|
||||
// Mimiron
|
||||
@@ -255,6 +260,8 @@ enum Texts
|
||||
#define GetVX001() instance->GetCreature(DATA_MIMIRON_VX001)
|
||||
#define GetACU() instance->GetCreature(DATA_MIMIRON_ACU)
|
||||
|
||||
Position const ACUSummonPos = { 2742.6265f, 2568.0571f, 377.22076f, 0.0f }; /// @todo: replace with sniffed position
|
||||
|
||||
struct boss_mimiron : public BossAI
|
||||
{
|
||||
boss_mimiron(Creature* pCreature) : BossAI(pCreature, BOSS_MIMIRON)
|
||||
@@ -563,12 +570,9 @@ struct boss_mimiron : public BossAI
|
||||
break;
|
||||
case EVENT_GET_OUT_VX001:
|
||||
if (Creature* VX001 = GetVX001())
|
||||
if (Creature* ACU = me->SummonCreature(NPC_AERIAL_COMMAND_UNIT, 2743.91f, 2568.78f, 391.34f, M_PI, TEMPSUMMON_MANUAL_DESPAWN))
|
||||
if (me->SummonCreature(NPC_AERIAL_COMMAND_UNIT, ACUSummonPos, TEMPSUMMON_MANUAL_DESPAWN))
|
||||
{
|
||||
me->EnterVehicle(VX001, 4);
|
||||
float speed = ACU->GetDistance(2737.75f, 2574.22f, 381.34f) / 2.0f;
|
||||
ACU->GetMotionMaster()->MovePoint(0, 2737.75f, 2574.22f, 381.34f, FORCED_MOVEMENT_NONE, speed);
|
||||
ACU->SetPosition(2737.75f, 2574.22f, 381.34f, M_PI);
|
||||
events.ScheduleEvent(EVENT_SAY_VX001_DEAD, 2s);
|
||||
break;
|
||||
}
|
||||
@@ -1499,7 +1503,6 @@ struct npc_ulduar_aerial_command_unit : public ScriptedAI
|
||||
{
|
||||
instance = me->GetInstanceScript();
|
||||
bIsEvading = false;
|
||||
immobilized = false;
|
||||
me->SetDisableGravity(true);
|
||||
}
|
||||
|
||||
@@ -1508,19 +1511,13 @@ struct npc_ulduar_aerial_command_unit : public ScriptedAI
|
||||
SummonList summons;
|
||||
bool bIsEvading;
|
||||
uint8 Phase;
|
||||
bool immobilized;
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
Phase = 0;
|
||||
events.Reset();
|
||||
summons.DespawnAll();
|
||||
}
|
||||
|
||||
void AttackStart(Unit* who) override
|
||||
{
|
||||
if (who)
|
||||
me->Attack(who, true); // skip following
|
||||
me->SetCombatMovement(false); /// @todo: research ACU behaviour
|
||||
}
|
||||
|
||||
void SetData(uint32 id, uint32 value) override
|
||||
@@ -1532,16 +1529,12 @@ struct npc_ulduar_aerial_command_unit : public ScriptedAI
|
||||
case 0:
|
||||
Phase = 0;
|
||||
events.Reset();
|
||||
immobilized = false;
|
||||
break;
|
||||
case 3:
|
||||
Phase = 3;
|
||||
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
|
||||
if (Unit* target = SelectTargetFromPlayerList(75.0f))
|
||||
AttackStart(target);
|
||||
DoZoneInCombat();
|
||||
events.Reset();
|
||||
events.ScheduleEvent(EVENT_SPELL_PLASMA_BALL, 0ms);
|
||||
events.ScheduleEvent(EVENT_SUMMON_BOMB_BOT, 15s);
|
||||
events.ScheduleEvent(EVENT_SUMMON_ASSAULT_BOT, 1s);
|
||||
events.ScheduleEvent(EVENT_SUMMON_JUNK_BOT, 10s);
|
||||
@@ -1561,17 +1554,31 @@ struct npc_ulduar_aerial_command_unit : public ScriptedAI
|
||||
events.ScheduleEvent(EVENT_SPELL_PLASMA_BALL, 0ms);
|
||||
}
|
||||
}
|
||||
else if (id == 2 && !immobilized && Phase == 3) // magnetic core
|
||||
{
|
||||
immobilized = true;
|
||||
events.ScheduleEvent(EVENT_MAGNETIC_CORE_PULL_DOWN, 2s);
|
||||
}
|
||||
}
|
||||
|
||||
void DoAction(int32 param) override
|
||||
{
|
||||
if (param == 1337)
|
||||
summons.DespawnAll();
|
||||
switch (param)
|
||||
{
|
||||
case DO_DISABLE_AERIAL:
|
||||
me->CastStop();
|
||||
me->AttackStop();
|
||||
me->SetReactState(REACT_PASSIVE);
|
||||
me->SetDisableGravity(false);
|
||||
me->GetMotionMaster()->MoveFall();
|
||||
events.DelayEvents(25s);
|
||||
break;
|
||||
case DO_ENABLE_AERIAL:
|
||||
me->SetDisableGravity(true);
|
||||
me->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 10.0f);
|
||||
me->m_Events.AddEventAtOffset([&] {
|
||||
me->SetReactState(REACT_AGGRESSIVE);
|
||||
}, 2s);
|
||||
break;
|
||||
case 1337:
|
||||
summons.DespawnAll();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override
|
||||
@@ -1624,42 +1631,11 @@ struct npc_ulduar_aerial_command_unit : public ScriptedAI
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (!UpdateVictim())
|
||||
if (me->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE) || me->HasAura(SPELL_MAGNETIC_CORE))
|
||||
return;
|
||||
|
||||
// following :D
|
||||
if (Phase == 3 && !immobilized)
|
||||
if (Unit* victim = me->GetVictim())
|
||||
if (me->GetExactDist2d(victim) > 25.0f )
|
||||
{
|
||||
float angle = victim->GetAngle(me->GetPositionX(), me->GetPositionY());
|
||||
me->SetOrientation( me->GetAngle(victim->GetPositionX(), victim->GetPositionY()));
|
||||
float x = victim->GetPositionX() + 15.0f * cos(angle);
|
||||
float y = victim->GetPositionY() + 15.0f * std::sin(angle);
|
||||
|
||||
// check if there's magnetic core in line of movement
|
||||
Creature* mc = nullptr;
|
||||
std::list<Creature*> cl;
|
||||
me->GetCreaturesWithEntryInRange(cl, me->GetExactDist2d(victim), NPC_MAGNETIC_CORE);
|
||||
for( std::list<Creature*>::iterator itr = cl.begin(); itr != cl.end(); ++itr )
|
||||
{
|
||||
if ((*itr)->IsInBetween(me, victim, 4.0f) && (*itr)->GetExactDist2d(victim) >= 10.0f) // don't come very close just because there's a magnetic core
|
||||
{
|
||||
x = (*itr)->GetPositionX();
|
||||
y = (*itr)->GetPositionY();
|
||||
mc = (*itr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float speed = me->GetExactDist(x, y, 381.34f);
|
||||
me->GetMotionMaster()->MovePoint(0, x, y, 381.34f, FORCED_MOVEMENT_NONE, speed);
|
||||
if (mc)
|
||||
{
|
||||
mc->AI()->SetData(0, 0);
|
||||
SetData(2, 1);
|
||||
}
|
||||
}
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
events.Update(diff);
|
||||
|
||||
@@ -1670,14 +1646,8 @@ struct npc_ulduar_aerial_command_unit : public ScriptedAI
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
case EVENT_SPELL_PLASMA_BALL:
|
||||
if (!immobilized)
|
||||
DoCastVictim(SPELL_PLASMA_BALL);
|
||||
events.Repeat(3s);
|
||||
break;
|
||||
case EVENT_SUMMON_BOMB_BOT:
|
||||
if (!immobilized)
|
||||
me->CastSpell(me, SPELL_SUMMON_BOMB_BOT, false);
|
||||
me->CastSpell(me, SPELL_SUMMON_BOMB_BOT, false);
|
||||
events.Repeat(15s);
|
||||
break;
|
||||
case EVENT_SUMMON_ASSAULT_BOT:
|
||||
@@ -1702,21 +1672,9 @@ struct npc_ulduar_aerial_command_unit : public ScriptedAI
|
||||
events.Repeat(45s);
|
||||
}
|
||||
break;
|
||||
case EVENT_MAGNETIC_CORE_PULL_DOWN:
|
||||
me->CastSpell(me, SPELL_MAGNETIC_CORE, true);
|
||||
me->CastSpell(me, SPELL_SPINNING, true);
|
||||
me->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), 365.34f, FORCED_MOVEMENT_NONE, me->GetExactDist(me->GetPositionX(), me->GetPositionY(), 365.34f));
|
||||
events.ScheduleEvent(EVENT_MAGNETIC_CORE_FREE, 20s);
|
||||
break;
|
||||
case EVENT_MAGNETIC_CORE_FREE:
|
||||
me->RemoveAura(SPELL_SPINNING);
|
||||
me->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), 381.34f, FORCED_MOVEMENT_NONE, me->GetDistance(me->GetPositionX(), me->GetPositionY(), 381.34f));
|
||||
events.ScheduleEvent(EVENT_MAGNETIC_CORE_REMOVE_IMMOBILIZE, 1s);
|
||||
break;
|
||||
case EVENT_MAGNETIC_CORE_REMOVE_IMMOBILIZE:
|
||||
immobilized = false;
|
||||
break;
|
||||
}
|
||||
|
||||
DoSpellAttackIfReady(Phase == 3 ? SPELL_PLASMA_BALL_P1 : SPELL_PLASMA_BALL_P2);
|
||||
}
|
||||
|
||||
void MoveInLineOfSight(Unit* /*mover*/) override {}
|
||||
@@ -1871,44 +1829,6 @@ struct npc_ulduar_mimiron_rocket : public NullCreatureAI
|
||||
}
|
||||
};
|
||||
|
||||
struct npc_ulduar_magnetic_core : public NullCreatureAI
|
||||
{
|
||||
npc_ulduar_magnetic_core(Creature* pCreature) : NullCreatureAI(pCreature)
|
||||
{
|
||||
instance = me->GetInstanceScript();
|
||||
if (Creature* c = GetACU())
|
||||
if (c->GetExactDist2d(me) <= 10.0f)
|
||||
{
|
||||
me->SendMonsterMove(c->GetPositionX(), c->GetPositionY(), 364.313f, 1);
|
||||
me->UpdatePosition(c->GetPositionX(), c->GetPositionY(), 364.313f, me->GetOrientation(), true);
|
||||
me->StopMovingOnCurrentPos();
|
||||
c->AI()->SetData(2, 1);
|
||||
despawnTimer = 20000;
|
||||
return;
|
||||
}
|
||||
despawnTimer = 60000;
|
||||
}
|
||||
|
||||
InstanceScript* instance;
|
||||
uint16 despawnTimer;
|
||||
|
||||
void SetData(uint32 /*id*/, uint32 /*value*/) override
|
||||
{
|
||||
despawnTimer = 20000;
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (despawnTimer <= diff)
|
||||
{
|
||||
despawnTimer = 60000;
|
||||
me->DespawnOrUnsummon(1ms);
|
||||
}
|
||||
else
|
||||
despawnTimer -= diff;
|
||||
}
|
||||
};
|
||||
|
||||
struct npc_ulduar_bot_summon_trigger : public NullCreatureAI
|
||||
{
|
||||
npc_ulduar_bot_summon_trigger(Creature* pCreature) : NullCreatureAI(pCreature) { }
|
||||
@@ -1965,6 +1885,67 @@ struct npc_ulduar_bot_summon_trigger : public NullCreatureAI
|
||||
}
|
||||
};
|
||||
|
||||
// 64444 - Magnetic Core Summon
|
||||
class spell_mimiron_magnetic_core_summon : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_mimiron_magnetic_core_summon);
|
||||
|
||||
void ModDest(SpellDestination& dest)
|
||||
{
|
||||
Unit* caster = GetCaster();
|
||||
Position pos = caster->GetPosition();
|
||||
pos.m_positionZ = caster->GetMap()->GetHeight(pos);
|
||||
dest.Relocate(pos);
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_mimiron_magnetic_core_summon::ModDest, EFFECT_0, TARGET_DEST_NEARBY_ENTRY);
|
||||
}
|
||||
};
|
||||
|
||||
// 64436 - Magnetic Core (aura)
|
||||
class spell_mimiron_magnetic_core_aura : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_mimiron_magnetic_core_aura);
|
||||
|
||||
bool Validate(SpellInfo const* /*spell*/) override
|
||||
{
|
||||
return ValidateSpellInfo({ SPELL_MAGNETIC_CORE_VISUAL });
|
||||
}
|
||||
|
||||
void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
|
||||
{
|
||||
if (Creature* target = GetTarget()->ToCreature())
|
||||
{
|
||||
target->AI()->DoAction(DO_DISABLE_AERIAL);
|
||||
target->CastSpell(target, SPELL_MAGNETIC_CORE_VISUAL, true);
|
||||
}
|
||||
}
|
||||
|
||||
void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
|
||||
{
|
||||
if (Creature* target = GetTarget()->ToCreature())
|
||||
{
|
||||
target->AI()->DoAction(DO_ENABLE_AERIAL);
|
||||
target->RemoveAurasDueToSpell(SPELL_MAGNETIC_CORE_VISUAL);
|
||||
}
|
||||
}
|
||||
|
||||
void OnRemoveSelf(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
|
||||
{
|
||||
if (TempSummon* summ = GetTarget()->ToTempSummon())
|
||||
summ->DespawnOrUnsummon();
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
AfterEffectApply += AuraEffectApplyFn(spell_mimiron_magnetic_core_aura::OnApply, EFFECT_1, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, AURA_EFFECT_HANDLE_REAL);
|
||||
AfterEffectRemove += AuraEffectRemoveFn(spell_mimiron_magnetic_core_aura::OnRemove, EFFECT_1, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, AURA_EFFECT_HANDLE_REAL);
|
||||
AfterEffectRemove += AuraEffectRemoveFn(spell_mimiron_magnetic_core_aura::OnRemoveSelf, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
|
||||
}
|
||||
};
|
||||
|
||||
class spell_mimiron_rapid_burst_aura : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_mimiron_rapid_burst_aura);
|
||||
@@ -2325,8 +2306,9 @@ void AddSC_boss_mimiron()
|
||||
|
||||
RegisterUlduarCreatureAI(npc_ulduar_proximity_mine);
|
||||
RegisterUlduarCreatureAI(npc_ulduar_mimiron_rocket);
|
||||
RegisterUlduarCreatureAI(npc_ulduar_magnetic_core);
|
||||
RegisterUlduarCreatureAI(npc_ulduar_bot_summon_trigger);
|
||||
RegisterSpellScript(spell_mimiron_magnetic_core_summon);
|
||||
RegisterSpellScript(spell_mimiron_magnetic_core_aura);
|
||||
RegisterSpellScript(spell_mimiron_rapid_burst_aura);
|
||||
RegisterSpellScript(spell_mimiron_p3wx2_laser_barrage_aura);
|
||||
RegisterSpellScript(spell_ulduar_mimiron_mine_explosion);
|
||||
|
||||
@@ -170,6 +170,7 @@ struct boss_razorscale : public BossAI
|
||||
CommanderGUID.Clear();
|
||||
bGroundPhase = false;
|
||||
flyTimes = 0;
|
||||
me->SetAnimTier(AnimTier::Fly);
|
||||
}
|
||||
|
||||
void AttackStart(Unit* who) override
|
||||
@@ -610,7 +611,7 @@ public:
|
||||
{
|
||||
razorscale->AI()->AttackStart(player);
|
||||
razorscale->GetMotionMaster()->MoveIdle();
|
||||
razorscale->GetMotionMaster()->MovePoint(POINT_RAZORSCALE_INIT, 588.0f, -178.0f, 490.0f, FORCED_MOVEMENT_NONE, 0.f, 0.f, false, false);
|
||||
razorscale->GetMotionMaster()->MovePoint(POINT_RAZORSCALE_INIT, CORDS_AIR.GetPositionX(), CORDS_AIR.GetPositionY(), CORDS_AIR.GetPositionZ(), FORCED_MOVEMENT_NONE, 0.f, 0.f, false, false, MOTION_SLOT_ACTIVE, AnimTier::Fly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "ObjectMgr.h"
|
||||
#include "Player.h"
|
||||
#include "PoolMgr.h"
|
||||
#include "RaceMgr.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "ScriptedGossip.h"
|
||||
#include "SpellScript.h"
|
||||
@@ -859,8 +860,8 @@ public:
|
||||
|
||||
bool IsFriendly(Unit* passenger)
|
||||
{
|
||||
return ((me->GetUInt32Value(GAMEOBJECT_FACTION) == WintergraspFaction[TEAM_HORDE] && passenger->getRaceMask() & RACEMASK_HORDE) ||
|
||||
(me->GetUInt32Value(GAMEOBJECT_FACTION) == WintergraspFaction[TEAM_ALLIANCE] && passenger->getRaceMask() & RACEMASK_ALLIANCE));
|
||||
return ((me->GetUInt32Value(GAMEOBJECT_FACTION) == WintergraspFaction[TEAM_HORDE] && passenger->getRaceMask() & sRaceMgr->GetHordeRaceMask()) ||
|
||||
(me->GetUInt32Value(GAMEOBJECT_FACTION) == WintergraspFaction[TEAM_ALLIANCE] && passenger->getRaceMask() & sRaceMgr->GetAllianceRaceMask()));
|
||||
}
|
||||
|
||||
Creature* IsValidVehicle(Creature* cVeh)
|
||||
|
||||
@@ -401,6 +401,26 @@ class spell_lady_vashj_summons : public SpellScript
|
||||
}
|
||||
};
|
||||
|
||||
// Spell 38132 - Paralyze (applied to player when looting Tainted Core item 31088)
|
||||
class spell_lady_vashj_tainted_core_paralyze : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_lady_vashj_tainted_core_paralyze);
|
||||
|
||||
void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
|
||||
{
|
||||
if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_DEATH)
|
||||
return;
|
||||
|
||||
if (Player* player = GetTarget()->ToPlayer())
|
||||
player->DestroyItemCount(ITEM_TAINTED_CORE, -1, true);
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
AfterEffectRemove += AuraEffectRemoveFn(spell_lady_vashj_tainted_core_paralyze::HandleEffectRemove, EFFECT_0, SPELL_AURA_MOD_ROOT, AURA_EFFECT_HANDLE_REAL);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_boss_lady_vashj()
|
||||
{
|
||||
RegisterSerpentShrineAI(boss_lady_vashj);
|
||||
@@ -409,4 +429,5 @@ void AddSC_boss_lady_vashj()
|
||||
RegisterSpellScript(spell_lady_vashj_summon_sporebat);
|
||||
RegisterSpellScript(spell_lady_vashj_spore_drop_effect);
|
||||
RegisterSpellScript(spell_lady_vashj_summons);
|
||||
RegisterSpellScript(spell_lady_vashj_tainted_core_paralyze);
|
||||
}
|
||||
|
||||
@@ -20,11 +20,14 @@
|
||||
#include "Group.h"
|
||||
#include "PassiveAI.h"
|
||||
#include "Player.h"
|
||||
#include "RaceMgr.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "ScriptedGossip.h"
|
||||
#include "SpellAuras.h"
|
||||
#include "SpellScript.h"
|
||||
#include "SpellScriptLoader.h"
|
||||
#include <unordered_map>
|
||||
|
||||
/*
|
||||
* Ordered alphabetically using scriptname.
|
||||
* Scriptnames of files in this file should be prefixed with "npc_pet_gen_".
|
||||
@@ -128,20 +131,17 @@ struct argentPonyBanner
|
||||
const char* text;
|
||||
};
|
||||
|
||||
static argentPonyBanner argentBanners[MAX_RACES] =
|
||||
{
|
||||
{0, 0, ""},
|
||||
{2781, 62594, "Stormwind Champion's Pennant"},
|
||||
{2783, 63433, "Orgrimmar Champion's Pennant"},
|
||||
{2780, 63427, "Ironforge Champion's Pennant"},
|
||||
{2777, 63406, "Darnassus Champion's Pennant"},
|
||||
{2787, 63430, "Forsaken Champion's Pennant"},
|
||||
{2786, 63436, "Thunder Bluff Champion's Pennant"},
|
||||
{2779, 63396, "Gnomeregan Champion's Pennant"},
|
||||
{2784, 63399, "Darkspear Champion's Pennant"},
|
||||
{0, 0, ""},
|
||||
{2785, 63403, "Silvermoon Champion's Pennant"},
|
||||
{2778, 63423, "Exodar Champion's Pennant"}
|
||||
static std::unordered_map<uint8, argentPonyBanner> argentBanners = {
|
||||
{RACE_HUMAN, {2781, 62594, "Stormwind Champion's Pennant"}},
|
||||
{RACE_ORC, {2783, 63433, "Orgrimmar Champion's Pennant"}},
|
||||
{RACE_DWARF, {2780, 63427, "Ironforge Champion's Pennant"}},
|
||||
{RACE_NIGHTELF, {2777, 63406, "Darnassus Champion's Pennant"}},
|
||||
{RACE_UNDEAD_PLAYER, {2787, 63430, "Forsaken Champion's Pennant"}},
|
||||
{RACE_TAUREN, {2786, 63436, "Thunder Bluff Champion's Pennant"}},
|
||||
{RACE_GNOME, {2779, 63396, "Gnomeregan Champion's Pennant"}},
|
||||
{RACE_TROLL, {2784, 63399, "Darkspear Champion's Pennant"}},
|
||||
{RACE_BLOODELF, {2785, 63403, "Silvermoon Champion's Pennant"}},
|
||||
{RACE_DRAENEI, {2778, 63423, "Exodar Champion's Pennant"}}
|
||||
};
|
||||
|
||||
struct npc_pet_gen_argent_pony_bridle : public ScriptedAI
|
||||
@@ -152,7 +152,6 @@ struct npc_pet_gen_argent_pony_bridle : public ScriptedAI
|
||||
_init = false;
|
||||
_mountTimer = 4000;
|
||||
_lastAura = 0;
|
||||
memset(_banners, 0, sizeof(_banners));
|
||||
}
|
||||
|
||||
void EnterEvadeMode(EvadeReason /*why*/) override
|
||||
@@ -211,10 +210,12 @@ struct npc_pet_gen_argent_pony_bridle : public ScriptedAI
|
||||
}
|
||||
|
||||
// Generate Banners
|
||||
uint32 mask = player->GetTeamId(true) ? RACEMASK_HORDE : RACEMASK_ALLIANCE;
|
||||
for (uint8 i = 1; i < MAX_RACES; ++i)
|
||||
if (mask & (1 << (i - 1)) && player->HasAchieved(argentBanners[i].achievement))
|
||||
_banners[i] = true;
|
||||
uint32 mask = player->GetTeamId(true) ? sRaceMgr->GetHordeRaceMask() : sRaceMgr->GetAllianceRaceMask();
|
||||
for (auto const& [raceId, banner] : argentBanners)
|
||||
{
|
||||
if ((mask & (1 << (raceId - 1))) && player->HasAchieved(banner.achievement))
|
||||
_banners[raceId] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (duration && aura)
|
||||
@@ -247,7 +248,8 @@ struct npc_pet_gen_argent_pony_bridle : public ScriptedAI
|
||||
if (param == 0)
|
||||
return _state;
|
||||
|
||||
return _banners[param];
|
||||
auto itr = _banners.find(param);
|
||||
return (itr != _banners.end() && itr->second) ? 1 : 0;
|
||||
}
|
||||
|
||||
void DoAction(int32 param) override
|
||||
@@ -279,9 +281,11 @@ struct npc_pet_gen_argent_pony_bridle : public ScriptedAI
|
||||
AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Visit a mailbox.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_MAILBOX);
|
||||
}
|
||||
|
||||
for (uint8 i = RACE_HUMAN; i < MAX_RACES; ++i)
|
||||
if (creature->AI()->GetData(i) == uint32(true))
|
||||
AddGossipItemFor(player, GOSSIP_ICON_CHAT, argentBanners[i].text, GOSSIP_SENDER_MAIN, argentBanners[i].spell);
|
||||
for (auto const& [raceId, banner] : argentBanners)
|
||||
{
|
||||
if (creature->AI()->GetData(raceId) == uint32(true))
|
||||
AddGossipItemFor(player, GOSSIP_ICON_CHAT, banner.text, GOSSIP_SENDER_MAIN, banner.spell);
|
||||
}
|
||||
|
||||
SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID());
|
||||
return true;
|
||||
@@ -336,7 +340,7 @@ private:
|
||||
bool _init;
|
||||
uint8 _state;
|
||||
int32 _mountTimer;
|
||||
bool _banners[MAX_RACES];
|
||||
std::unordered_map<uint8, bool> _banners;
|
||||
uint32 _lastAura;
|
||||
};
|
||||
|
||||
|
||||
@@ -2559,7 +2559,7 @@ class spell_gen_vehicle_scaling_aura: public AuraScript
|
||||
|
||||
bool Load() override
|
||||
{
|
||||
return GetCaster() && GetCaster()->IsPlayer() && GetOwner()->IsCreature();
|
||||
return GetCaster() && GetCaster()->IsPlayer() && GetOwner() && GetOwner()->IsCreature();
|
||||
}
|
||||
|
||||
void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/)
|
||||
|
||||
@@ -1518,6 +1518,30 @@ class spell_hun_explosive_shot : public SpellScript
|
||||
}
|
||||
};
|
||||
|
||||
// 34026 - Kill Command
|
||||
class spell_hun_kill_command : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_hun_kill_command);
|
||||
|
||||
SpellCastResult CheckCast()
|
||||
{
|
||||
Unit* caster = GetCaster();
|
||||
if (!caster || !caster->IsPlayer())
|
||||
return SPELL_FAILED_NO_VALID_TARGETS;
|
||||
|
||||
Pet* pet = caster->ToPlayer()->GetPet();
|
||||
if (!pet)
|
||||
return SPELL_FAILED_NO_PET;
|
||||
|
||||
return SPELL_CAST_OK;
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnCheckCast += SpellCheckCastFn(spell_hun_kill_command::CheckCast);
|
||||
}
|
||||
};
|
||||
|
||||
// 58914 - Kill Command (Pet Aura)
|
||||
class spell_hun_kill_command_pet : public AuraScript
|
||||
{
|
||||
@@ -1704,6 +1728,7 @@ void AddSC_hunter_spell_scripts()
|
||||
RegisterSpellScript(spell_hun_glyph_of_mend_pet);
|
||||
// Proc system scripts
|
||||
RegisterSpellScript(spell_hun_rapid_recuperation);
|
||||
RegisterSpellScript(spell_hun_kill_command);
|
||||
RegisterSpellScript(spell_hun_kill_command_pet);
|
||||
RegisterSpellScript(spell_hun_piercing_shots);
|
||||
RegisterSpellScript(spell_hun_rapid_recuperation_trigger);
|
||||
|
||||
@@ -655,23 +655,11 @@ class spell_item_lil_phylactery : public AuraScript
|
||||
}
|
||||
};
|
||||
|
||||
// 45042 - Power Circle (Shifting Naaru Sliver base aura)
|
||||
class spell_item_shifting_naaru_silver : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_item_shifting_naaru_silver);
|
||||
|
||||
void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
|
||||
{
|
||||
if (GetTarget() == GetCaster())
|
||||
if (Aura* aur = GetTarget()->AddAura(45044 /*Limitless Power*/, GetTarget()))
|
||||
aur->SetDuration(GetDuration());
|
||||
}
|
||||
|
||||
void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
|
||||
{
|
||||
if (Aura* aur = GetTarget()->GetAura(45044 /*Limitless Power*/, GetTarget()->GetGUID()))
|
||||
aur->SetDuration(0);
|
||||
}
|
||||
|
||||
void OnBaseRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
|
||||
{
|
||||
GetUnitOwner()->RemoveDynObject(45043);
|
||||
@@ -679,13 +667,7 @@ class spell_item_shifting_naaru_silver : public AuraScript
|
||||
|
||||
void Register() override
|
||||
{
|
||||
if (m_scriptSpellId == 45043)
|
||||
{
|
||||
OnEffectApply += AuraEffectApplyFn(spell_item_shifting_naaru_silver::OnApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
|
||||
AfterEffectRemove += AuraEffectRemoveFn(spell_item_shifting_naaru_silver::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
|
||||
}
|
||||
else
|
||||
AfterEffectRemove += AuraEffectRemoveFn(spell_item_shifting_naaru_silver::OnBaseRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
|
||||
AfterEffectRemove += AuraEffectRemoveFn(spell_item_shifting_naaru_silver::OnBaseRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4879,7 +4861,7 @@ class spell_item_ultrasafe_transporter : public SpellScript
|
||||
}
|
||||
};
|
||||
|
||||
// 45043 - Power Circle (Mage T5 Set Bonus)
|
||||
// 45043 - Power Circle (Shifting Naaru Sliver)
|
||||
class spell_item_power_circle : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_item_power_circle);
|
||||
@@ -4889,23 +4871,28 @@ class spell_item_power_circle : public AuraScript
|
||||
return ValidateSpellInfo({ SPELL_LIMITLESS_POWER });
|
||||
}
|
||||
|
||||
void OnAuraInit()
|
||||
bool CheckCaster(Unit* target)
|
||||
{
|
||||
Unit* caster = GetCaster();
|
||||
if (!caster)
|
||||
return;
|
||||
|
||||
caster->CastSpell(caster, SPELL_LIMITLESS_POWER, true);
|
||||
return target->GetGUID() == GetCasterGUID();
|
||||
}
|
||||
|
||||
void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
|
||||
void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
|
||||
{
|
||||
GetTarget()->CastSpell(GetTarget(), SPELL_LIMITLESS_POWER, true);
|
||||
if (Aura* buff = GetTarget()->GetAura(SPELL_LIMITLESS_POWER))
|
||||
buff->SetDuration(GetDuration());
|
||||
}
|
||||
|
||||
void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
|
||||
{
|
||||
GetTarget()->RemoveAurasDueToSpell(SPELL_LIMITLESS_POWER);
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
AfterEffectRemove += AuraEffectRemoveFn(spell_item_power_circle::HandleRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
|
||||
DoCheckAreaTarget += AuraCheckAreaTargetFn(spell_item_power_circle::CheckCaster);
|
||||
AfterEffectApply += AuraEffectApplyFn(spell_item_power_circle::OnApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
|
||||
AfterEffectRemove += AuraEffectRemoveFn(spell_item_power_circle::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -71,7 +71,8 @@ enum MageSpells
|
||||
SPELL_MAGE_CHILLED_R3 = 12486,
|
||||
SPELL_MAGE_MANA_SURGE = 37445,
|
||||
SPELL_MAGE_FROST_NOVA = 122,
|
||||
SPELL_MAGE_LIVING_BOMB_R1 = 44457
|
||||
SPELL_MAGE_LIVING_BOMB_R1 = 44457,
|
||||
SPELL_MAGE_MISSILE_BARRAGE_PROC = 44401
|
||||
};
|
||||
|
||||
enum MageSpellIcons
|
||||
@@ -1516,6 +1517,31 @@ class spell_mage_ice_block : public SpellScript
|
||||
}
|
||||
};
|
||||
|
||||
// 12536 - Clearcasting
|
||||
class spell_mage_clearcasting : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_mage_clearcasting);
|
||||
|
||||
bool CheckProc(ProcEventInfo& eventInfo)
|
||||
{
|
||||
SpellInfo const* spellInfo = eventInfo.GetSpellInfo();
|
||||
if (!spellInfo)
|
||||
return true;
|
||||
|
||||
// Missile Barrage has priority over Clearcasting for Arcane Missiles
|
||||
if (spellInfo->SpellFamilyName == SPELLFAMILY_MAGE && (spellInfo->SpellFamilyFlags[0] & 0x800))
|
||||
if (GetTarget()->HasAura(SPELL_MAGE_MISSILE_BARRAGE_PROC))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
DoCheckProc += AuraCheckProcFn(spell_mage_clearcasting::CheckProc);
|
||||
}
|
||||
};
|
||||
|
||||
// 44401 - Missile Barrage (proc buff)
|
||||
class spell_mage_missile_barrage_proc : public AuraScript
|
||||
{
|
||||
@@ -1587,6 +1613,7 @@ void AddSC_mage_spell_scripts()
|
||||
RegisterSpellScript(spell_mage_ice_block);
|
||||
RegisterSpellScript(spell_mage_imp_blizzard);
|
||||
RegisterSpellScript(spell_mage_imp_mana_gems);
|
||||
RegisterSpellScript(spell_mage_clearcasting);
|
||||
RegisterSpellScript(spell_mage_missile_barrage);
|
||||
RegisterSpellScript(spell_mage_missile_barrage_proc);
|
||||
RegisterSpellScript(spell_mage_blast_wave);
|
||||
|
||||
@@ -96,12 +96,15 @@ enum PaladinSpells
|
||||
SPELL_PALADIN_HOLY_VENGEANCE = 31803,
|
||||
SPELL_PALADIN_BLOOD_CORRUPTION = 53742,
|
||||
SPELL_PALADIN_SEAL_OF_VENGEANCE_EFFECT = 42463,
|
||||
SPELL_PALADIN_SEAL_OF_CORRUPTION_EFFECT = 53739
|
||||
SPELL_PALADIN_SEAL_OF_CORRUPTION_EFFECT = 53739,
|
||||
|
||||
SPELL_PALADIN_SEAL_OF_COMMAND = 20375
|
||||
};
|
||||
|
||||
enum PaladinSpellIcons
|
||||
{
|
||||
PALADIN_ICON_ID_RETRIBUTION_AURA = 555,
|
||||
PALADIN_ICON_JUDGEMENTS_OF_THE_JUST = 3015,
|
||||
PALADIN_ICON_JUDGEMENTS_OF_THE_WISE = 3017,
|
||||
PALADIN_ICON_HAMMER_OF_THE_RIGHTEOUS = 3023,
|
||||
PALADIN_ICON_RIGHTEOUS_VENGEANCE = 3025,
|
||||
@@ -148,6 +151,20 @@ enum PaladinProcSpells
|
||||
SPELL_PALADIN_T8_HOLY_4P_BONUS = 64895
|
||||
};
|
||||
|
||||
static bool IsJudgementDamageSpell(SpellInfo const* spellInfo)
|
||||
{
|
||||
return spellInfo &&
|
||||
spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN &&
|
||||
(spellInfo->SpellFamilyFlags[0] & 0x800000);
|
||||
}
|
||||
|
||||
static bool HasJudgementsOfTheJust(Unit const* caster)
|
||||
{
|
||||
return caster && caster->GetAuraEffect(
|
||||
SPELL_AURA_ADD_FLAT_MODIFIER, SPELLFAMILY_PALADIN,
|
||||
PALADIN_ICON_JUDGEMENTS_OF_THE_JUST, 0) != nullptr;
|
||||
}
|
||||
|
||||
class spell_pal_seal_of_command_aura : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_pal_seal_of_command_aura);
|
||||
@@ -173,13 +190,15 @@ class spell_pal_seal_of_command_aura : public AuraScript
|
||||
void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
|
||||
{
|
||||
PreventDefaultAction();
|
||||
// All melee procs should cleave except Hammer of the Righteous.
|
||||
// Judgement cleave is handled separately via JotJ code path.
|
||||
int32 targets = 3;
|
||||
if (SpellInfo const* procSpell = eventInfo.GetSpellInfo())
|
||||
{
|
||||
if (procSpell->IsAffectingArea())
|
||||
{
|
||||
// HotR: flag1 0x40000, DS: flag1 0x20000
|
||||
if (procSpell->SpellFamilyName == SPELLFAMILY_PALADIN &&
|
||||
(procSpell->SpellFamilyFlags[1] & 0x60000))
|
||||
targets = 1;
|
||||
}
|
||||
}
|
||||
|
||||
Unit* target = eventInfo.GetActionTarget();
|
||||
@@ -959,27 +978,26 @@ public:
|
||||
GetCaster()->CastSpell(GetCaster(), SPELL_IMPROVED_JUDGEMENT_ENERGIZE, true);
|
||||
}
|
||||
|
||||
// Judgement of the Just
|
||||
if (GetCaster()->GetAuraEffect(SPELL_AURA_ADD_FLAT_MODIFIER, SPELLFAMILY_PALADIN, 3015, 0))
|
||||
// Judgements of the Just
|
||||
if (HasJudgementsOfTheJust(GetCaster()))
|
||||
{
|
||||
if (GetCaster()->CastSpell(GetHitUnit(), SPELL_JUDGEMENTS_OF_THE_JUST, true) && (spellId2 == SPELL_JUDGEMENT_OF_VENGEANCE_EFFECT || spellId2 == SPELL_JUDGEMENT_OF_CORRUPTION_EFFECT))
|
||||
{
|
||||
//hidden effect only cast when spellcast of judgements of the just is succesful
|
||||
GetCaster()->CastSpell(GetHitUnit(), SealApplication(spellId2), true); //add hidden seal apply effect for vengeance and corruption
|
||||
}
|
||||
}
|
||||
}
|
||||
GetCaster()->CastSpell(GetHitUnit(), SPELL_JUDGEMENTS_OF_THE_JUST, true);
|
||||
|
||||
uint32 SealApplication(uint32 correspondingSpellId)
|
||||
{
|
||||
switch (correspondingSpellId)
|
||||
{
|
||||
case SPELL_JUDGEMENT_OF_VENGEANCE_EFFECT:
|
||||
return SPELL_HOLY_VENGEANCE;
|
||||
case SPELL_JUDGEMENT_OF_CORRUPTION_EFFECT:
|
||||
return SPELL_BLOOD_CORRUPTION;
|
||||
default:
|
||||
return 0;
|
||||
// JotJ makes Judgements trigger Seal of Command's
|
||||
// cleave effect
|
||||
if (AuraEffect const* socEff =
|
||||
GetCaster()->GetAuraEffect(
|
||||
SPELL_PALADIN_SEAL_OF_COMMAND, EFFECT_0))
|
||||
{
|
||||
if (GetHitUnit()->IsAlive())
|
||||
{
|
||||
GetCaster()->CastCustomSpell(
|
||||
socEff->GetSpellInfo()->Effects[EFFECT_0]
|
||||
.TriggerSpell,
|
||||
SPELLVALUE_MAX_TARGETS, 3,
|
||||
GetHitUnit(), true, nullptr, socEff);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1164,11 +1182,14 @@ class spell_pal_seal_of_righteousness : public AuraScript
|
||||
DamageInfo* damageInfo = eventInfo.GetDamageInfo();
|
||||
|
||||
if (!damageInfo || !damageInfo->GetDamage())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return target->IsAlive() && !eventInfo.GetTriggerAuraSpell() && (damageInfo->GetDamage() || (eventInfo.GetHitMask() & PROC_EX_ABSORB));
|
||||
if (IsJudgementDamageSpell(eventInfo.GetSpellInfo()))
|
||||
return target->IsAlive();
|
||||
|
||||
return target->IsAlive() && !eventInfo.GetTriggerAuraSpell() &&
|
||||
(damageInfo->GetDamage() ||
|
||||
(eventInfo.GetHitMask() & PROC_EX_ABSORB));
|
||||
}
|
||||
|
||||
void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
|
||||
@@ -1187,6 +1208,17 @@ class spell_pal_seal_of_righteousness : public AuraScript
|
||||
|
||||
int32 bp = std::max<int32>(0, int32((ap * 0.022f + 0.044f * holy) * GetTarget()->GetAttackTime(BASE_ATTACK) / 1000));
|
||||
GetTarget()->CastCustomSpell(SPELL_PALADIN_SEAL_OF_RIGHTEOUSNESS, SPELLVALUE_BASE_POINT0, bp, eventInfo.GetProcTarget(), true, nullptr, aurEff);
|
||||
|
||||
// Judgements of the Just: Seal of Righteousness procs
|
||||
// twice from Judgements
|
||||
if (IsJudgementDamageSpell(eventInfo.GetSpellInfo()) &&
|
||||
HasJudgementsOfTheJust(GetTarget()))
|
||||
{
|
||||
GetTarget()->CastCustomSpell(
|
||||
SPELL_PALADIN_SEAL_OF_RIGHTEOUSNESS,
|
||||
SPELLVALUE_BASE_POINT0, bp,
|
||||
eventInfo.GetProcTarget(), true, nullptr, aurEff);
|
||||
}
|
||||
}
|
||||
|
||||
void Register() override
|
||||
@@ -1884,6 +1916,25 @@ class spell_pal_seal_of_vengeance_aura : public AuraScript
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CheckProc(ProcEventInfo& eventInfo)
|
||||
{
|
||||
SpellInfo const* procSpell = eventInfo.GetSpellInfo();
|
||||
if (procSpell &&
|
||||
procSpell->SpellFamilyName == SPELLFAMILY_PALADIN)
|
||||
{
|
||||
// Block re-proc from seal damage effects
|
||||
if ((procSpell->SpellFamilyFlags[1] & 0x800) &&
|
||||
!(procSpell->SpellFamilyFlags[0] & 0x800000))
|
||||
return false;
|
||||
|
||||
// Judgements only trigger seal procs with JotJ
|
||||
if (procSpell->SpellFamilyFlags[0] & 0x800000)
|
||||
return HasJudgementsOfTheJust(eventInfo.GetActor());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void HandleApplyDoT(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
|
||||
{
|
||||
PreventDefaultAction();
|
||||
@@ -1925,8 +1976,11 @@ class spell_pal_seal_of_vengeance_aura : public AuraScript
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectProc += AuraEffectProcFn(spell_pal_seal_of_vengeance_aura::HandleApplyDoT, EFFECT_0, SPELL_AURA_DUMMY);
|
||||
DoCheckProc += AuraCheckProcFn(spell_pal_seal_of_vengeance_aura::CheckProc);
|
||||
// HandleSeal reads stacks BEFORE HandleApplyDoT increments them,
|
||||
// so the attacking hit does not benefit from its own stack application.
|
||||
OnEffectProc += AuraEffectProcFn(spell_pal_seal_of_vengeance_aura::HandleSeal, EFFECT_0, SPELL_AURA_DUMMY);
|
||||
OnEffectProc += AuraEffectProcFn(spell_pal_seal_of_vengeance_aura::HandleApplyDoT, EFFECT_0, SPELL_AURA_DUMMY);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -53,7 +53,8 @@ enum RogueSpells
|
||||
SPELL_ROGUE_TURN_THE_TABLES_R3 = 52915,
|
||||
SPELL_ROGUE_OVERKILL_TRIGGERED = 58427,
|
||||
SPELL_ROGUE_HONOR_AMONG_THIEVES_PROC = 52916,
|
||||
SPELL_ROGUE_HONOR_AMONG_THIEVES_TRIGGERED = 51699
|
||||
SPELL_ROGUE_HONOR_AMONG_THIEVES_TRIGGERED = 51699,
|
||||
SPELL_ROGUE_COLD_BLOOD = 14177
|
||||
};
|
||||
|
||||
enum RogueSpellIcons
|
||||
@@ -1086,6 +1087,68 @@ class spell_rog_focused_attacks : public AuraScript
|
||||
}
|
||||
};
|
||||
|
||||
// 14177 - Cold Blood
|
||||
class spell_rog_cold_blood : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_rog_cold_blood);
|
||||
|
||||
bool _usedByMutilate = false;
|
||||
|
||||
public:
|
||||
bool WasUsedByMutilate() const { return _usedByMutilate; }
|
||||
|
||||
bool CheckProc(ProcEventInfo& eventInfo)
|
||||
{
|
||||
SpellInfo const* spellInfo = eventInfo.GetSpellInfo();
|
||||
if (!spellInfo)
|
||||
return true;
|
||||
|
||||
// Block Mutilate MH (0x2) and OH (0x4) from consuming the charge
|
||||
if (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE
|
||||
&& (spellInfo->SpellFamilyFlags[1] & 0x6))
|
||||
{
|
||||
_usedByMutilate = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
DoCheckProc += AuraCheckProcFn(spell_rog_cold_blood::CheckProc);
|
||||
}
|
||||
};
|
||||
|
||||
// 1329 - Mutilate (parent spell, all ranks)
|
||||
class spell_rog_mutilate : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_rog_mutilate);
|
||||
|
||||
void HandleAfterCast()
|
||||
{
|
||||
Unit* caster = GetCaster();
|
||||
if (!caster)
|
||||
return;
|
||||
|
||||
Aura* cb = caster->GetAura(SPELL_ROGUE_COLD_BLOOD);
|
||||
if (!cb)
|
||||
return;
|
||||
|
||||
auto* script = dynamic_cast<spell_rog_cold_blood*>(
|
||||
cb->GetScriptByName("spell_rog_cold_blood"));
|
||||
if (!script || !script->WasUsedByMutilate())
|
||||
return;
|
||||
|
||||
cb->Remove();
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
AfterCast += SpellCastFn(spell_rog_mutilate::HandleAfterCast);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_rogue_spell_scripts()
|
||||
{
|
||||
RegisterSpellScript(spell_rog_savage_combat);
|
||||
@@ -1117,4 +1180,6 @@ void AddSC_rogue_spell_scripts()
|
||||
RegisterSpellScript(spell_rog_turn_the_tables);
|
||||
RegisterSpellScript(spell_rog_turn_the_tables_proc);
|
||||
RegisterSpellScript(spell_rog_focused_attacks);
|
||||
RegisterSpellScript(spell_rog_mutilate);
|
||||
RegisterSpellScript(spell_rog_cold_blood);
|
||||
}
|
||||
|
||||
@@ -1384,7 +1384,7 @@ class spell_sha_frozen_power : public AuraScript
|
||||
if (GetTarget()->GetDistance(target) < 15.0f)
|
||||
return false;
|
||||
|
||||
return roll_chance_i(GetEffect(EFFECT_0)->GetAmount());
|
||||
return roll_chance_i(GetEffect(EFFECT_1)->GetAmount());
|
||||
}
|
||||
|
||||
void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
|
||||
|
||||
@@ -2674,7 +2674,8 @@ struct npc_controller : public PossessedAI
|
||||
{
|
||||
if (!apply)
|
||||
{
|
||||
me->GetCharmerOrOwner()->InterruptNonMeleeSpells(false);
|
||||
if (Unit* charmerOrOwner = me->GetCharmerOrOwner())
|
||||
charmerOrOwner->InterruptNonMeleeSpells(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -714,8 +714,8 @@ struct ChrRacesEntry
|
||||
uint32 TeamID; // 7 (7-Alliance 1-Horde)
|
||||
// 8-11 unused
|
||||
uint32 CinematicSequence; // 12 id from CinematicSequences.dbc
|
||||
//uint32 alliance; // 13 faction (0 alliance, 1 horde, 2 not available?)
|
||||
char const* name[16]; // 14-29 used for DBC language detection/selection
|
||||
uint32 alliance; // 13 faction (0 alliance, 1 horde, 2 not available?)
|
||||
char const* name[16]; // 14-29 used for DBC language detection/selection
|
||||
// 30 string flags, unused
|
||||
//char const* nameFemale[16]; // 31-46, if different from base (male) case
|
||||
// 47 string flags, unused
|
||||
|
||||
@@ -33,7 +33,7 @@ char constexpr CharSectionsEntryfmt[] = "diiixxxiii";
|
||||
char constexpr CharTitlesEntryfmt[] = "nxssssssssssssssssxssssssssssssssssxi";
|
||||
char constexpr ChatChannelsEntryfmt[] = "nixssssssssssssssssxxxxxxxxxxxxxxxxxx"; // ChatChannelsEntryfmt, index not used (more compact store)
|
||||
char constexpr ChrClassesEntryfmt[] = "nxixssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixii";
|
||||
char constexpr ChrRacesEntryfmt[] = "niixiixixxxxixssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxi";
|
||||
char constexpr ChrRacesEntryfmt[] = "niixiixixxxxiissssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxi";
|
||||
char constexpr CinematicCameraEntryfmt[] = "nsiffff";
|
||||
char constexpr CinematicSequencesEntryfmt[] = "nxixxxxxxx";
|
||||
char constexpr CreatureDisplayInfofmt[] = "nixifxxxxxxxxxxx";
|
||||
|
||||
@@ -91,21 +91,6 @@ enum Races
|
||||
//RACE_ICE_TROLL = 21
|
||||
};
|
||||
|
||||
// max+1 for player race
|
||||
#define MAX_RACES 12
|
||||
|
||||
#define RACEMASK_ALL_PLAYABLE \
|
||||
((1<<(RACE_HUMAN-1)) |(1<<(RACE_ORC-1)) |(1<<(RACE_DWARF-1)) | \
|
||||
(1<<(RACE_NIGHTELF-1))|(1<<(RACE_UNDEAD_PLAYER-1))|(1<<(RACE_TAUREN-1)) | \
|
||||
(1<<(RACE_GNOME-1)) |(1<<(RACE_TROLL-1)) |(1<<(RACE_BLOODELF-1))| \
|
||||
(1<<(RACE_DRAENEI-1)))
|
||||
|
||||
#define RACEMASK_ALLIANCE \
|
||||
((1<<(RACE_HUMAN-1)) | (1<<(RACE_DWARF-1)) | (1<<(RACE_NIGHTELF-1)) | \
|
||||
(1<<(RACE_GNOME-1)) | (1<<(RACE_DRAENEI-1)))
|
||||
|
||||
#define RACEMASK_HORDE RACEMASK_ALL_PLAYABLE & ~RACEMASK_ALLIANCE
|
||||
|
||||
// DisplayRace values from CreatureDisplayInfoExtra.dbc
|
||||
enum class DisplayRace : uint8
|
||||
{
|
||||
@@ -161,15 +146,7 @@ enum Classes
|
||||
(1<<(CLASS_DEATH_KNIGHT-1)))
|
||||
|
||||
// valid classes for creature_template.unit_class
|
||||
enum UnitClass
|
||||
{
|
||||
UNIT_CLASS_WARRIOR = 1,
|
||||
UNIT_CLASS_PALADIN = 2,
|
||||
UNIT_CLASS_ROGUE = 4,
|
||||
UNIT_CLASS_MAGE = 8,
|
||||
};
|
||||
|
||||
#define CLASSMASK_ALL_CREATURES ((1<<(UNIT_CLASS_WARRIOR-1)) | (1<<(UNIT_CLASS_PALADIN-1)) | (1<<(UNIT_CLASS_ROGUE-1)) | (1<<(UNIT_CLASS_MAGE-1)))
|
||||
#define CLASSMASK_ALL_CREATURES ((1<<(CLASS_WARRIOR-1)) | (1<<(CLASS_PALADIN-1)) | (1<<(CLASS_ROGUE-1)) | (1<<(CLASS_MAGE-1)))
|
||||
|
||||
#define CLASSMASK_WAND_USERS ((1<<(CLASS_PRIEST-1))|(1<<(CLASS_MAGE-1))|(1<<(CLASS_WARLOCK-1)))
|
||||
|
||||
@@ -3698,6 +3675,13 @@ enum PvPTeamId
|
||||
PVP_TEAM_NEUTRAL = 2 // Battleground: Neutral, Arena: None
|
||||
};
|
||||
|
||||
enum AllianceId
|
||||
{
|
||||
ALLIANCE_ALLIANCE = 0,
|
||||
ALLIANCE_HORDE = 1,
|
||||
ALLIANCE_NEUTRAL = 2
|
||||
};
|
||||
|
||||
uint8 constexpr PVP_TEAMS_COUNT = 2;
|
||||
|
||||
inline PvPTeamId GetPvPTeamId(TeamId teamId)
|
||||
|
||||
Reference in New Issue
Block a user