Merge pull request #160 from azerothcore/master

Update
This commit is contained in:
bashermens
2026-01-30 21:54:26 +01:00
committed by GitHub
44 changed files with 1956 additions and 1239 deletions
@@ -714,7 +714,9 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (me->IsRooted()) // Rooted inhabit type, never move/reposition
continue;
CAST_AI(SmartAI, me->AI())->SetCurrentRangeMode(true, std::max(spellMaxRange - NOMINAL_MELEE_RANGE, 0.0f));
if (e.action.cast.castFlags & SMARTCAST_COMBAT_MOVE)
CAST_AI(SmartAI, me->AI())->SetCurrentRangeMode(true, std::max(spellMaxRange - NOMINAL_MELEE_RANGE, 0.0f));
continue;
}
else if (distanceToTarget < spellMinRange || !(isWithinLOSInMap || isSpellIgnoreLOS))
@@ -336,6 +336,9 @@ inline void Battleground::_CheckSafePositions(uint32 diff)
for (auto const& [playerGuid, player] : GetPlayers())
{
if (player->IsGameMaster())
continue;
Position pos = player->GetPosition();
Position const* startPos = GetTeamStartPosition(player->GetBgTeamId());
+13 -1
View File
@@ -12852,9 +12852,21 @@ void Player::ResurectUsingRequestData()
void Player::SetClientControl(Unit* target, bool allowMove, bool packetOnly /*= false*/)
{
ASSERT(target);
// refuse to grant control if target has UNIT_STATE_CHARMED
if (target->HasUnitState(UNIT_STATE_CHARMED) && (GetGUID() != target->GetCharmerGUID()))
{
LOG_ERROR("entities.player", "Player '{}' attempt to client control '{}', which is charmed by GUID {}", GetName(), target->GetName(), target->GetCharmerGUID().ToString());
return;
}
// still affected by some aura that shouldn't allow control, only allow on last such aura to be removed
if (target->HasUnitState(UNIT_STATE_FLEEING | UNIT_STATE_CONFUSED))
allowMove = false;
WorldPacket data(SMSG_CLIENT_CONTROL_UPDATE, target->GetPackGUID().size() + 1);
data << target->GetPackGUID();
data << uint8((allowMove && !target->HasUnitState(UNIT_STATE_FLEEING | UNIT_STATE_CONFUSED)) ? 1 : 0);
data << uint8(allowMove ? 1 : 0);
SendDirectMessage(&data);
// We want to set the packet only
+1
View File
@@ -1447,6 +1447,7 @@ public:
bool SatisfyQuestConditions(Quest const* qInfo, bool msg);
bool SatisfyQuestTimed(Quest const* qInfo, bool msg) const;
bool SatisfyQuestExclusiveGroup(Quest const* qInfo, bool msg) const;
bool SatisfyQuestBreadcrumb(Quest const* qInfo, bool msg) const;
bool SatisfyQuestNextChain(Quest const* qInfo, bool msg) const;
bool SatisfyQuestPrevChain(Quest const* qInfo, bool msg) const;
bool SatisfyQuestDay(Quest const* qInfo, bool msg) const;
@@ -239,7 +239,7 @@ bool Player::CanSeeStartQuest(Quest const* quest)
if (!sDisableMgr->IsDisabledFor(DISABLE_TYPE_QUEST, quest->GetQuestId(), this) && SatisfyQuestClass(quest, false) && SatisfyQuestRace(quest, false) &&
SatisfyQuestSkill(quest, false) && SatisfyQuestExclusiveGroup(quest, false) && SatisfyQuestReputation(quest, false) &&
SatisfyQuestPreviousQuest(quest, false) && SatisfyQuestNextChain(quest, false) &&
SatisfyQuestPrevChain(quest, false) && SatisfyQuestDay(quest, false) && SatisfyQuestWeek(quest, false) &&
SatisfyQuestPrevChain(quest, false) && SatisfyQuestBreadcrumb(quest, false) && SatisfyQuestDay(quest, false) && SatisfyQuestWeek(quest, false) &&
SatisfyQuestMonth(quest, false) && SatisfyQuestSeasonal(quest, false))
{
return GetLevel() + sWorld->getIntConfig(CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF) >= quest->GetMinLevel();
@@ -256,6 +256,7 @@ bool Player::CanTakeQuest(Quest const* quest, bool msg)
&& SatisfyQuestSkill(quest, msg) && SatisfyQuestReputation(quest, msg)
&& SatisfyQuestPreviousQuest(quest, msg) && SatisfyQuestTimed(quest, msg)
&& SatisfyQuestNextChain(quest, msg) && SatisfyQuestPrevChain(quest, msg)
&& SatisfyQuestBreadcrumb(quest, msg)
&& SatisfyQuestDay(quest, msg) && SatisfyQuestWeek(quest, msg)
&& SatisfyQuestMonth(quest, msg) && SatisfyQuestSeasonal(quest, msg)
&& SatisfyQuestConditions(quest, msg);
@@ -1217,6 +1218,39 @@ bool Player::SatisfyQuestExclusiveGroup(Quest const* qInfo, bool msg) const
return true;
}
bool Player::SatisfyQuestBreadcrumb(Quest const* qInfo, bool msg) const
{
uint32 breadcrumbForQuestId = qInfo->GetBreadcrumbForQuestId();
if (breadcrumbForQuestId)
{
QuestStatus status = GetQuestStatus(breadcrumbForQuestId);
if (status != QUEST_STATUS_NONE || IsQuestRewarded(breadcrumbForQuestId))
{
if (msg)
SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
return false;
}
}
if (std::vector<uint32> const* breadcrumbs = sObjectMgr->GetBreadcrumbsForQuest(qInfo->GetQuestId()))
{
for (uint32 breadcrumbQuestId : *breadcrumbs)
{
QuestStatus status = GetQuestStatus(breadcrumbQuestId);
if (status == QUEST_STATUS_INCOMPLETE || status == QUEST_STATUS_COMPLETE || status == QUEST_STATUS_FAILED)
{
if (msg)
SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
return false;
}
}
}
return true;
}
bool Player::SatisfyQuestNextChain(Quest const* qInfo, bool msg) const
{
uint32 nextQuest = qInfo->GetNextQuestInChain();
+13 -2
View File
@@ -5177,7 +5177,7 @@ void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, U
// Xinef: if stealer has same aura
Aura* curAura = stealer->GetAura(aura->GetId());
if (!curAura || (!curAura->IsPermanent() && curAura->GetDuration() < (int32)dur))
if (Aura* newAura = Aura::TryRefreshStackOrCreate(aura->GetSpellInfo(), effMask, stealer, nullptr, &baseDamage[0], nullptr, aura->GetCasterGUID()))
if (Aura* newAura = Aura::TryRefreshStackOrCreate(aura->GetSpellInfo(), effMask, stealer, nullptr, &baseDamage[0], nullptr, stealer->GetGUID()))
{
// created aura must not be single target aura,, so stealer won't loose it on recast
if (newAura->IsSingleTarget())
@@ -5189,6 +5189,13 @@ void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, U
}
// FIXME: using aura->GetMaxDuration() maybe not blizzlike but it fixes stealing of spells like Innervate
newAura->SetLoadedState(aura->GetMaxDuration(), int32(dur), stealCharge ? 1 : aura->GetCharges(), 1, recalculateMask, &damage[0]);
// Reset periodic timers so stolen HoTs tick properly
// For stolen auras we need fresh tick counters since no ticks have occurred yet
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
if (AuraEffect* aurEff = newAura->GetEffect(i))
aurEff->ResetPeriodic(true);
newAura->ApplyForTargets();
}
}
@@ -11124,6 +11131,8 @@ void Unit::SetCharm(Unit* charm, bool apply)
}
else
{
charm->ClearUnitState(UNIT_STATE_CHARMED);
if (IsPlayer())
{
if (!RemoveGuidValue(UNIT_FIELD_CHARM, charm->GetGUID()))
@@ -18631,7 +18640,7 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au
ASSERT(type != CHARM_TYPE_POSSESS || charmer->IsPlayer());
if (type == CHARM_TYPE_VEHICLE && !IsVehicle()) // pussywizard
throw 1;
ASSERT((type == CHARM_TYPE_VEHICLE) == IsVehicle());
ASSERT((type == CHARM_TYPE_VEHICLE) == (GetVehicleKit() && GetVehicleKit()->IsControllableVehicle()));
LOG_DEBUG("entities.unit", "SetCharmedBy: charmer {} ({}), charmed {} ({}), type {}.",
charmer->GetEntry(), charmer->GetGUID().ToString(), GetEntry(), GetGUID().ToString(), uint32(type));
@@ -18804,6 +18813,8 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au
else if (IsPlayer())
RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT);
AddUnitState(UNIT_STATE_CHARMED);
if (Creature* creature = ToCreature())
creature->RefreshSwimmingFlag();
+2 -2
View File
@@ -169,7 +169,7 @@ enum UnitState
{
UNIT_STATE_DIED = 0x00000001, // player has fake death aura
UNIT_STATE_MELEE_ATTACKING = 0x00000002, // player is melee attacking someone
//UNIT_STATE_MELEE_ATTACK_BY = 0x00000004, // player is melee attack by someone
UNIT_STATE_CHARMED = 0x00000004, // having any kind of charm aura on self
UNIT_STATE_STUNNED = 0x00000008,
UNIT_STATE_ROAMING = 0x00000010,
UNIT_STATE_CHASE = 0x00000020,
@@ -201,7 +201,7 @@ enum UnitState
UNIT_STATE_NO_COMBAT_MOVEMENT = 0x40000000, // should not be changed outside the core and should be placed at the end
UNIT_STATE_LOGOUT_TIMER = 0x80000000, // Unit is logging out
UNIT_STATE_ALL_STATE_SUPPORTED = UNIT_STATE_DIED | UNIT_STATE_MELEE_ATTACKING | UNIT_STATE_STUNNED | UNIT_STATE_ROAMING | UNIT_STATE_CHASE
UNIT_STATE_ALL_STATE_SUPPORTED = UNIT_STATE_DIED | UNIT_STATE_MELEE_ATTACKING | UNIT_STATE_CHARMED | UNIT_STATE_STUNNED | UNIT_STATE_ROAMING | UNIT_STATE_CHASE
| UNIT_STATE_FLEEING | UNIT_STATE_IN_FLIGHT | UNIT_STATE_FOLLOW | UNIT_STATE_ROOT | UNIT_STATE_CONFUSED
| UNIT_STATE_DISTRACTED | UNIT_STATE_ISOLATED | UNIT_STATE_ATTACK_PLAYER | UNIT_STATE_CASTING
| UNIT_STATE_POSSESSED | UNIT_STATE_CHARGING | UNIT_STATE_JUMPING | UNIT_STATE_MOVE | UNIT_STATE_ROTATING
@@ -26,6 +26,7 @@
#include "TemporarySummon.h"
#include "Unit.h"
#include "Util.h"
#include <algorithm>
Vehicle::Vehicle(Unit* unit, VehicleEntry const* vehInfo, uint32 creatureEntry) :
_me(unit), _vehicleInfo(vehInfo), _usableSeatNum(0), _creatureEntry(creatureEntry), _status(STATUS_NONE)
@@ -562,6 +563,11 @@ bool Vehicle::IsVehicleInUse()
return false;
}
bool Vehicle::IsControllableVehicle() const
{
return std::ranges::any_of(Seats, [](auto const& seat) { return seat.second.SeatInfo->CanControl(); });
}
void Vehicle::TeleportVehicle(float x, float y, float z, float ang)
{
_me->GetMap()->LoadGrid(x, y);
@@ -51,6 +51,7 @@ public:
void RemoveAllPassengers();
void Dismiss();
bool IsVehicleInUse();
[[nodiscard]] bool IsControllableVehicle() const;
void TeleportVehicle(float x, float y, float z, float ang);
SeatMap Seats;
+46 -4
View File
@@ -4738,10 +4738,10 @@ void ObjectMgr::LoadQuests()
}
// Load `quest_template_addon`
// 0 1 2 3 4 5 6 7 8
result = WorldDatabase.Query("SELECT ID, MaxLevel, AllowableClasses, SourceSpellID, PrevQuestID, NextQuestID, ExclusiveGroup, RewardMailTemplateID, RewardMailDelay, "
//9 10 11 12 13 14 15 16 17
"RequiredSkillID, RequiredSkillPoints, RequiredMinRepFaction, RequiredMaxRepFaction, RequiredMinRepValue, RequiredMaxRepValue, ProvidedItemCount, RewardMailSenderEntry, SpecialFlags FROM quest_template_addon LEFT JOIN quest_mail_sender ON Id=QuestId");
// 0 1 2 3 4 5 6 7 8
result = WorldDatabase.Query("SELECT ID, MaxLevel, AllowableClasses, SourceSpellID, PrevQuestID, NextQuestID, ExclusiveGroup, BreadcrumbForQuestId, RewardMailTemplateID, "
//9 10 11 12 13 14 15 16 17 18
"RewardMailDelay, RequiredSkillID, RequiredSkillPoints, RequiredMinRepFaction, RequiredMaxRepFaction, RequiredMinRepValue, RequiredMaxRepValue, ProvidedItemCount, RewardMailSenderEntry, SpecialFlags FROM quest_template_addon LEFT JOIN quest_mail_sender ON Id=QuestId");
if (!result)
{
@@ -5318,12 +5318,54 @@ void ObjectMgr::LoadQuests()
if (qinfo->ExclusiveGroup)
mExclusiveQuestGroups.insert(std::pair<int32, uint32>(qinfo->ExclusiveGroup, qinfo->GetQuestId()));
if (uint32 breadcrumbForQuestId = qinfo->GetBreadcrumbForQuestId())
{
if (_questTemplates.find(breadcrumbForQuestId) == _questTemplates.end())
LOG_ERROR("sql.sql", "Quest {} has BreadcrumbForQuestId {}, but no such quest exists", qinfo->GetQuestId(), breadcrumbForQuestId);
else
_breadcrumbsForQuest[breadcrumbForQuestId].push_back(qinfo->GetQuestId());
if (qinfo->GetNextQuestId())
LOG_ERROR("sql.sql", "Quest {} is a breadcrumb quest but also has NextQuestID {} set", qinfo->GetQuestId(), qinfo->GetNextQuestId());
if (qinfo->GetExclusiveGroup())
LOG_ERROR("sql.sql", "Quest {} is a breadcrumb quest but also has ExclusiveGroup {} set", qinfo->GetQuestId(), qinfo->GetExclusiveGroup());
}
if (qinfo->TimeAllowed)
qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_TIMED);
if (qinfo->RequiredPlayerKills)
qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_PLAYER_KILL);
}
for (auto const& [questId, quest] : _questTemplates)
{
if (sDisableMgr->IsDisabledFor(DISABLE_TYPE_QUEST, questId, nullptr))
continue;
uint32 breadcrumbForQuestId = quest->GetBreadcrumbForQuestId();
if (!breadcrumbForQuestId)
continue;
std::set<uint32> visitedQuests;
visitedQuests.insert(questId);
while (breadcrumbForQuestId)
{
if (!visitedQuests.insert(breadcrumbForQuestId).second)
{
LOG_ERROR("sql.sql", "Breadcrumb quests {} and {} form a loop", questId, breadcrumbForQuestId);
break;
}
Quest const* targetQuest = GetQuestTemplate(breadcrumbForQuestId);
if (!targetQuest)
break;
breadcrumbForQuestId = targetQuest->GetBreadcrumbForQuestId();
}
}
// check QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT for spell with SPELL_EFFECT_QUEST_COMPLETE
for (uint32 i = 0; i < sSpellMgr->GetSpellInfoStoreSize(); ++i)
{
+12
View File
@@ -1146,6 +1146,18 @@ public:
ExclusiveQuestGroups mExclusiveQuestGroups;
typedef std::unordered_map<uint32, std::vector<uint32>> BreadcrumbQuestMap;
BreadcrumbQuestMap _breadcrumbsForQuest;
[[nodiscard]] std::vector<uint32> const* GetBreadcrumbsForQuest(uint32 questId) const
{
auto itr = _breadcrumbsForQuest.find(questId);
if (itr != _breadcrumbsForQuest.end())
return &itr->second;
return nullptr;
}
MailLevelReward const* GetMailLevelReward(uint32 level, uint32 raceMask)
{
MailLevelRewardContainer::const_iterator map_itr = _mailLevelRewardStore.find(level);
@@ -128,11 +128,11 @@ namespace Movement
// MoveSplineFlag::Enter_Cycle support dropped
//if (splineflags & SPLINEFLAG_ENTER_CYCLE)
//cyclic_point = 1; // shouldn't be modified, came from client
spline.init_cyclic_spline(&args.path[0], args.path.size(), modes[args.flags.isSmooth()], cyclic_point);
spline.init_cyclic_spline(&args.path[0], args.path.size(), modes[args.flags.isSmooth()], cyclic_point, args.initialOrientation);
}
else
{
spline.init_spline(&args.path[0], args.path.size(), modes[args.flags.isSmooth()]);
spline.init_spline(&args.path[0], args.path.size(), modes[args.flags.isSmooth()], args.initialOrientation);
}
// init spline timestamps
+10 -4
View File
@@ -199,18 +199,20 @@ namespace Movement
return length;
}
void SplineBase::init_spline(const Vector3* controls, index_type count, EvaluationMode m)
void SplineBase::init_spline(const Vector3* controls, index_type count, EvaluationMode m, float orientation)
{
m_mode = m;
cyclic = false;
initialOrientation = orientation;
(this->*initializers[m_mode])(controls, count, cyclic, 0);
}
void SplineBase::init_cyclic_spline(const Vector3* controls, index_type count, EvaluationMode m, index_type cyclic_point)
void SplineBase::init_cyclic_spline(const Vector3* controls, index_type count, EvaluationMode m, index_type cyclic_point, float orientation)
{
m_mode = m;
cyclic = true;
initialOrientation = orientation;
(this->*initializers[m_mode])(controls, count, cyclic, cyclic_point);
}
@@ -253,14 +255,18 @@ namespace Movement
if (cyclic_point == 0)
points[0] = controls[count - 1];
else
points[0] = controls[0].lerp(controls[1], -1);
points[0] = controls[0] - G3D::Vector3{ std::cos(initialOrientation), std::sin(initialOrientation), 0.0f };
points[high_index + 1] = controls[cyclic_point];
points[high_index + 2] = controls[cyclic_point + 1];
}
else
{
points[0] = controls[0].lerp(controls[1], -1);
if (m_mode == ModeCatmullrom)
points[0] = controls[0] - G3D::Vector3{ std::cos(initialOrientation), std::sin(initialOrientation), 0.0f };
else
points[0] = controls[0].lerp(controls[1], -1);
points[high_index + 1] = controls[count - 1];
}
+5 -4
View File
@@ -48,6 +48,7 @@ namespace Movement
uint8 m_mode{UninitializedMode};
bool cyclic{false};
float initialOrientation{0};
enum
{
@@ -116,8 +117,8 @@ namespace Movement
[[nodiscard]] const Vector3& getPoint(index_type i) const { return points[i];}
/** Initializes spline. Don't call other methods while spline not initialized. */
void init_spline(const Vector3* controls, index_type count, EvaluationMode m);
void init_cyclic_spline(const Vector3* controls, index_type count, EvaluationMode m, index_type cyclic_point);
void init_spline(const Vector3* controls, index_type count, EvaluationMode m, float orientation);
void init_cyclic_spline(const Vector3* controls, index_type count, EvaluationMode m, index_type cyclic_point, float orientation);
/** As i can see there are a lot of ways how spline can be initialized
would be no harm to have some custom initializers. */
@@ -170,8 +171,8 @@ namespace Movement
void computeIndex(float t, index_type& out_idx, float& out_u) const;
/** Initializes spline. Don't call other methods while spline not initialized. */
void init_spline(const Vector3* controls, index_type count, EvaluationMode m) { SplineBase::init_spline(controls, count, m);}
void init_cyclic_spline(const Vector3* controls, index_type count, EvaluationMode m, index_type cyclic_point) { SplineBase::init_cyclic_spline(controls, count, m, cyclic_point);}
void init_spline(const Vector3* controls, index_type count, EvaluationMode m, float orientation = 0) { SplineBase::init_spline(controls, count, m, orientation);}
void init_cyclic_spline(const Vector3* controls, index_type count, EvaluationMode m, index_type cyclic_point, float orientation = 0) { SplineBase::init_cyclic_spline(controls, count, m, cyclic_point, orientation);}
/** Initializes lengths with SplineBase::SegLength method. */
void initLengths();
+12 -11
View File
@@ -177,17 +177,18 @@ void Quest::LoadQuestTemplateAddon(Field* fields)
PrevQuestId = fields[4].Get<int32>();
NextQuestId = fields[5].Get<uint32>();
ExclusiveGroup = fields[6].Get<int32>();
RewardMailTemplateId = fields[7].Get<uint32>();
RewardMailDelay = fields[8].Get<uint32>();
RequiredSkillId = fields[9].Get<uint16>();
RequiredSkillPoints = fields[10].Get<uint16>();
RequiredMinRepFaction = fields[11].Get<uint16>();
RequiredMaxRepFaction = fields[12].Get<uint16>();
RequiredMinRepValue = fields[13].Get<int32>();
RequiredMaxRepValue = fields[14].Get<int32>();
StartItemCount = fields[15].Get<uint8>();
RewardMailSenderEntry = fields[16].Get<uint32>();
SpecialFlags = fields[17].Get<uint32>();
BreadcrumbForQuestId = fields[7].Get<uint32>();
RewardMailTemplateId = fields[8].Get<uint32>();
RewardMailDelay = fields[9].Get<uint32>();
RequiredSkillId = fields[10].Get<uint16>();
RequiredSkillPoints = fields[11].Get<uint16>();
RequiredMinRepFaction = fields[12].Get<uint16>();
RequiredMaxRepFaction = fields[13].Get<uint16>();
RequiredMinRepValue = fields[14].Get<int32>();
RequiredMaxRepValue = fields[15].Get<int32>();
StartItemCount = fields[16].Get<uint8>();
RewardMailSenderEntry = fields[17].Get<uint32>();
SpecialFlags = fields[18].Get<uint32>();
if ((SpecialFlags & QUEST_SPECIAL_FLAGS_AUTO_ACCEPT) && !sWorld->getBoolConfig(CONFIG_QUEST_IGNORE_AUTO_ACCEPT))
{
+2
View File
@@ -249,6 +249,7 @@ public:
[[nodiscard]] int32 GetPrevQuestId() const { return PrevQuestId; }
[[nodiscard]] uint32 GetNextQuestId() const { return NextQuestId; }
[[nodiscard]] int32 GetExclusiveGroup() const { return ExclusiveGroup; }
[[nodiscard]] uint32 GetBreadcrumbForQuestId() const { return BreadcrumbForQuestId; }
[[nodiscard]] uint32 GetNextQuestInChain() const { return RewardNextQuest; }
[[nodiscard]] uint32 GetCharTitleId() const { return RewardTitleId; }
[[nodiscard]] uint32 GetPlayersSlain() const { return RequiredPlayerKills; }
@@ -390,6 +391,7 @@ protected:
int32 PrevQuestId = 0;
uint32 NextQuestId = 0;
int32 ExclusiveGroup = 0;
uint32 BreadcrumbForQuestId = 0;
uint32 RewardMailTemplateId = 0;
uint32 RewardMailDelay = 0;
uint32 RequiredSkillId = 0;
@@ -89,14 +89,16 @@ struct boss_priestess_delrissa : public BossAI
void InitializeAI() override
{
ScriptedAI::InitializeAI();
std::list<uint32> helpersList;
for (uint8 i = 0; i < MAX_HELPERS_COUNT; ++i)
helpersList.push_back(helpersEntries[i]);
Acore::Containers::RandomResize(helpersList, MAX_ACTIVE_HELPERS);
uint8 j = 0;
for (std::list<uint32>::const_iterator itr = helpersList.begin(); itr != helpersList.end(); ++itr, ++j)
me->SummonCreature(*itr, helpersLocations[j], TEMPSUMMON_MANUAL_DESPAWN, 0);
if (instance->GetBossState(DATA_DELRISSA) != DONE)
{
std::vector<uint32> helpersList(std::begin(helpersEntries), std::end(helpersEntries));
Acore::Containers::RandomResize(helpersList, MAX_ACTIVE_HELPERS);
uint8 j = 0;
for (uint32 entry : helpersList)
me->SummonCreature(entry, helpersLocations[j++], TEMPSUMMON_MANUAL_DESPAWN, 0);
}
}
void JustSummoned(Creature* summon) override
@@ -52,6 +52,11 @@ DoorData const doorData[] =
{ 0, 0, DOOR_TYPE_ROOM } // END
};
BossBoundaryData const boundaries =
{
{ DATA_KAELTHAS, new RectangleBoundary(118.64f, 178.63f, 125.69f, 210.70f) }
};
Position const KalecgosSpawnPos = { 164.3747f, -397.1197f, 2.151798f, 1.66219f };
class instance_magisters_terrace : public InstanceMapScript
@@ -65,6 +70,7 @@ public:
{
SetHeaders(DataHeader);
SetBossNumber(MAX_ENCOUNTER);
LoadBossBoundaries(boundaries);
SetPersistentDataCount(MAX_PERSISTENT_DATA);
LoadObjectData(creatureData, gameobjectData);
LoadDoorData(doorData);
File diff suppressed because it is too large Load Diff
@@ -46,7 +46,6 @@ enum NPCs
NPC_HOVER_DISK = 30248,
NPC_ARCANE_OVERLOAD = 30282,
NPC_SURGE_OF_POWER = 30334,
NPC_WYRMREST_SKYTALON = 30161,
NPC_STATIC_FIELD = 30592,
NPC_ALEXSTRASZA = 32295,
};
@@ -75,6 +74,8 @@ enum eSpells
SPELL_HASTE = 57060,
SPELL_ALEXSTRASZA_GIFT = 61028,
SPELL_SUMMON_RED_DRAGON_BUDDY = 56070,
SPELL_RIDE_RED_DRAGON = 56072
};
enum eAchiev
@@ -104,20 +105,11 @@ const Position Phase2NorthPos = {837.22f, 1301.676f, 296.10f, M_PI};
const uint32 MalygosIntroIntervals[] = {18000, 19000, 21000, 18000, 15000};
class EoEDrakeEnterVehicleEvent : public BasicEvent
{
public:
EoEDrakeEnterVehicleEvent(Creature& owner, ObjectGuid playerGUID) : _owner(owner), _playerGUID(playerGUID) { }
bool Execute(uint64 /*eventTime*/, uint32 /*updateTime*/) override;
private:
Creature& _owner;
ObjectGuid _playerGUID;
};
template <class AI, class T>
inline AI* GetEyeOfEternityAI(T* obj)
{
return GetInstanceAI<AI>(obj, EyeOfEternityScriptName);
}
#define RegisterEoECreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetEyeOfEternityAI)
#endif
@@ -21,18 +21,6 @@
#include "Vehicle.h"
#include "eye_of_eternity.h"
bool EoEDrakeEnterVehicleEvent::Execute(uint64 /*eventTime*/, uint32 /*updateTime*/)
{
if (Player* p = ObjectAccessor::GetPlayer(_owner, _playerGUID))
if (p->IsInWorld() && !p->IsDuringRemoveFromWorld() && !p->isBeingLoaded() && p->FindMap() == _owner.FindMap())
{
p->CastCustomSpell(60683, SPELLVALUE_BASE_POINT0, 1, &_owner, true);
return true;
}
_owner.DespawnOrUnsummon(1ms);
return true;
}
class instance_eye_of_eternity : public InstanceMapScript
{
public:
@@ -68,7 +56,7 @@ public:
return EncounterStatus == IN_PROGRESS;
}
void OnPlayerEnter(Player* pPlayer) override
void OnPlayerEnter(Player* player) override
{
if (EncounterStatus == DONE)
{
@@ -79,18 +67,12 @@ public:
go->SetPhaseMask(2, true);
// no floor, so put players on drakes
if (pPlayer)
if (player)
{
if (!pPlayer->IsAlive())
if (!player->IsAlive())
return;
if (Creature* c = pPlayer->SummonCreature(NPC_WYRMREST_SKYTALON, pPlayer->GetPositionX(), pPlayer->GetPositionY(), pPlayer->GetPositionZ() - 20.0f, 0.0f, TEMPSUMMON_MANUAL_DESPAWN, 0))
{
c->SetCanFly(true);
c->SetFaction(pPlayer->GetFaction());
//pPlayer->CastCustomSpell(60683, SPELLVALUE_BASE_POINT0, 1, c, true);
c->m_Events.AddEventAtOffset(new EoEDrakeEnterVehicleEvent(*c, pPlayer->GetGUID()), 500ms);
}
player->CastSpell(player, SPELL_SUMMON_RED_DRAGON_BUDDY, true);
}
}
}
@@ -753,7 +753,6 @@ public:
if (cr)
{
cr->AI()->AttackStart(me);
cr->AddThreat(me, 0.0f);
cr->SetInCombatWithZone();
}
}
@@ -911,6 +910,28 @@ void brann_bronzebeard::brann_bronzebeardAI::WaypointReached(uint32 id)
}
}
// 51774 - Taunt
class spell_taunt_brann : public SpellScript
{
PrepareSpellScript(spell_taunt_brann);
void HandleScript(SpellEffIndex /*effIndex*/)
{
Unit* caster = GetCaster();
Unit* target = GetHitUnit();
if (!caster || !target)
return;
uint32 spellId = GetEffectValue(); // 51775
target->CastSpell(caster, spellId, true);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_taunt_brann::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
class spell_hos_dark_matter : public AuraScript
{
PrepareAuraScript(spell_hos_dark_matter);
@@ -953,4 +974,5 @@ void AddSC_brann_bronzebeard()
new brann_bronzebeard();
RegisterSpellScript(spell_hos_dark_matter);
RegisterSpellScript(spell_hos_dark_matter_size);
RegisterSpellScript(spell_taunt_brann);
}
@@ -558,8 +558,10 @@ public:
{
uint32 dist = urand(38, 48);
float o = rand_norm() * M_PI * 2;
float Zplus = (dist - 38) / 6.5f;
if (Creature* cr = me->SummonCreature(entry, me->GetPositionX() + dist * cos(o), me->GetPositionY() + dist * std::sin(o), 327.2 + Zplus, 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000))
float spawnX = me->GetPositionX() + dist * cos(o);
float spawnY = me->GetPositionY() + dist * std::sin(o);
float spawnZ = me->GetMap()->GetHeight(me->GetPhaseMask(), spawnX, spawnY, 330.0f);
if (Creature* cr = me->SummonCreature(entry, spawnX, spawnY, spawnZ, 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000))
{
cr->CastSpell(cr, SPELL_TENTACLE_ERUPT, true);
cr->CastSpell(cr, SPELL_VOID_ZONE_SMALL, true);
@@ -1472,6 +1472,11 @@ public:
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY1H);
_events.ScheduleEvent(EVENT_THASSARIAN_SCRIPT_1, 2s);
}
else if (type == POINT_MOTION_TYPE && param == 0)
{
if (Creature* talbot = ObjectAccessor::GetCreature(*me, _talbotGUID))
me->SetFacingToObject(talbot);
}
}
void UpdateAI(uint32 diff) override
@@ -1804,6 +1809,7 @@ public:
{
if (action == 0)
{
me->setActive(true);
_playerGUID = player->GetGUID();
CloseGossipMenuFor(player);
me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP);
@@ -291,10 +291,29 @@ class spell_tsh_shoot_flame_arrow : public SpellScript
unitList.remove_if([&](WorldObject* target) -> bool
{
return !target->SelectNearestPlayer(15.0f);
if (!target)
return true;
if (!target->SelectNearestPlayer(15.0f))
return true;
if (target->FindNearestGameObject(GO_BLAZE, 6.0f))
return true;
// Don't stack arrows on the same target
if (InstanceScript* instance = caster->GetInstanceScript())
if (target->GetGUID() == instance->GetGuidData(DATA_LAST_FLAME_ARROW))
return true;
return false;
});
Acore::Containers::RandomResize(unitList, 1);
// Replace last arrow GUID
if (!unitList.empty())
if (InstanceScript* instance = caster->GetInstanceScript())
instance->SetGuidData(DATA_LAST_FLAME_ARROW, unitList.front()->GetGUID());
}
void HandleScriptEffect(SpellEffIndex effIndex)
@@ -117,11 +117,25 @@ public:
return PrisonerGUID[data - DATA_PRISONER_1];
case DATA_EXECUTIONER:
return ExecutionerGUID;
case DATA_LAST_FLAME_ARROW:
return LastArrowGUID;
}
return ObjectGuid::Empty;
}
void SetGuidData(uint32 data, ObjectGuid value) override
{
switch (data)
{
case DATA_LAST_FLAME_ARROW:
LastArrowGUID = value;
break;
default:
break;
}
}
void Update(uint32 diff) override
{
if (RescueTimer && RescueTimer < 100 * MINUTE * IN_MILLISECONDS)
@@ -164,6 +178,7 @@ public:
}
protected:
ObjectGuid LastArrowGUID;
ObjectGuid ExecutionerGUID;
ObjectGuid PrisonerGUID[3];
uint32 RescueTimer;
@@ -37,7 +37,8 @@ enum DataTypes
DATA_EXECUTIONER = 14,
DATA_OMROGG_LEFT_HEAD = 15,
DATA_OMROGG_RIGHT_HEAD = 16,
DATA_WARCHIEF_PORTAL = 17
DATA_WARCHIEF_PORTAL = 17,
DATA_LAST_FLAME_ARROW = 18
};
enum CreatureIds
@@ -49,6 +50,7 @@ enum CreatureIds
NPC_SH_ZEALOT = 17462,
NPC_SH_ARCHER = 17427,
NPC_SH_SCOUT = 17693,
NPC_SH_FLAME_ARROW = 17687,
// Warchief Kargath
NPC_WARCHIEF_KARGATH = 16808,
@@ -76,7 +78,8 @@ enum CreatureIds
enum GameobjectIds
{
GO_GRAND_WARLOCK_CHAMBER_DOOR_1 = 182539,
GO_GRAND_WARLOCK_CHAMBER_DOOR_2 = 182540
GO_GRAND_WARLOCK_CHAMBER_DOOR_2 = 182540,
GO_BLAZE = 181915
};
enum SpellIds
@@ -146,25 +146,7 @@ public:
}
};
class spell_the_eye_countercharge_aura : public AuraScript
{
PrepareAuraScript(spell_the_eye_countercharge_aura);
bool PrepareProc(ProcEventInfo& /*eventInfo*/)
{
// xinef: prevent charge drop
PreventDefaultAction();
return true;
}
void Register() override
{
DoCheckProc += AuraCheckProcFn(spell_the_eye_countercharge_aura::PrepareProc);
}
};
void AddSC_instance_the_eye()
{
new instance_the_eye();
RegisterSpellScript(spell_the_eye_countercharge_aura);
}