Merge pull request #175 from azerothcore/master

Test-staging branch
This commit is contained in:
kadeshar
2026-03-07 11:34:13 +01:00
committed by GitHub
101 changed files with 4856 additions and 557 deletions
@@ -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
+9 -4
View File
@@ -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)
{
+3 -2
View File
@@ -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;
}
+87
View File
@@ -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)
{
+12
View File
@@ -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>)>
+4 -1
View File
@@ -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;
+8 -7
View File
@@ -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))
+15 -11
View File
@@ -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;
}
}
+54
View File
@@ -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
+19 -2
View File
@@ -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
+33 -24
View File
@@ -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;
+1 -1
View File
@@ -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))
{
+2 -1
View File
@@ -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;
+1
View File
@@ -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);
+14 -1
View File
@@ -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_
+8
View File
@@ -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);
-32
View File
@@ -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)
{
-1
View File
@@ -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;
+17 -14
View File
@@ -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;
}
+2
View File
@@ -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);
});
+20 -1
View File
@@ -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;
+13
View File
@@ -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();
}
+6 -3
View File
@@ -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;
}
+5 -2
View File
@@ -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);
+4
View File
@@ -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();
+1 -2
View File
@@ -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);
+1 -2
View File
@@ -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,
+39 -19
View File
@@ -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;
}
+13 -5
View File
@@ -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()
+28
View File
@@ -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);
}
+28 -24
View File
@@ -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;
};
+1 -1
View File
@@ -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);
+16 -29
View File
@@ -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);
}
};
+28 -1
View File
@@ -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);
+81 -27
View File
@@ -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:
+66 -1
View File
@@ -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);
}
+1 -1
View File
@@ -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)
+2 -1
View File
@@ -2674,7 +2674,8 @@ struct npc_controller : public PossessedAI
{
if (!apply)
{
me->GetCharmerOrOwner()->InterruptNonMeleeSpells(false);
if (Unit* charmerOrOwner = me->GetCharmerOrOwner())
charmerOrOwner->InterruptNonMeleeSpells(false);
}
}
};
+2 -2
View File
@@ -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
+1 -1
View File
@@ -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";
+8 -24
View File
@@ -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)