Merge remote-tracking branch 'acore/master' into test-staging

This commit is contained in:
Celandriel
2026-04-21 23:57:34 +00:00
30 changed files with 708 additions and 525 deletions
+3 -6
View File
@@ -309,12 +309,9 @@ void CreatureAI::EngagementOver()
void CreatureAI::JustExitedCombat()
{
EngagementOver();
// If creature is alive, in world, and not already evading, trigger evade to return home
// Check IsInWorld to avoid evade during server shutdown/cleanup
if (me->IsAlive() && me->IsInWorld() && !me->IsInEvadeMode())
EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
// No-op: synchronous EnterEvadeMode cascades via MemberEvaded and frees
// refs held by upstream iterators (StopAttackFaction crash). EngagementOver
// here also resets scripted fights on brief combat gaps (Valithria).
}
/*void CreatureAI::AttackedBy(Unit* attacker)
@@ -189,6 +189,15 @@ void npc_escortAI::ReturnToLastPoint()
me->GetMotionMaster()->MovePoint(POINT_LAST_POINT, x, y, z, FORCED_MOVEMENT_RUN);
}
void npc_escortAI::JustExitedCombat()
{
// Evade synchronously so UpdateAI does not push a waypoint spline before
// SelectVictim's evade fallback fires; stacked motion intents twitch.
EngagementOver();
if (me->IsAlive() && me->IsInWorld() && !me->IsInEvadeMode())
EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
}
void npc_escortAI::EnterEvadeMode(EvadeReason /*why*/)
{
me->GetThreatMgr().ClearAllThreat();
@@ -549,8 +558,6 @@ void npc_escortAI::GenerateWaypointArray(Movement::PointsArray* points)
if (WaypointList.empty())
return;
uint32 startingWaypointId = CurrentWP->id;
// Flying unit, just fill array
if (me->m_movementInfo.HasMovementFlag((MovementFlags)(MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_DISABLE_GRAVITY)))
{
@@ -562,12 +569,13 @@ void npc_escortAI::GenerateWaypointArray(Movement::PointsArray* points)
}
else
{
uint32 remainingWaypoints = std::distance(CurrentWP, WaypointList.end());
for (float size = 1.0f; size; size *= 0.5f)
{
std::vector<G3D::Vector3> pVector;
// xinef: first point in vector is unit real position
pVector.push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()));
uint32 length = (WaypointList.size() - startingWaypointId) * size;
uint32 length = remainingWaypoints * size;
uint32 cnt = 0;
for (std::list<Escort_Waypoint>::const_iterator itr = CurrentWP; itr != WaypointList.end() && cnt <= length; ++itr, ++cnt)
@@ -70,6 +70,8 @@ public:
void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override;
void JustExitedCombat() override;
void UpdateAI(uint32 diff) override; //the "internal" update, calls UpdateEscortAI()
virtual void UpdateEscortAI(uint32 diff); //used when it's needed to add code in update (abilities, scripted events, etc)
@@ -1711,7 +1711,6 @@ bool Battleground::AddSpiritGuide(uint32 type, float x, float y, float z, float
if (Creature* creature = AddCreature(entry, type, x, y, z, o))
{
creature->setDeathState(DeathState::Dead);
creature->SetGuidValue(UNIT_FIELD_CHANNEL_OBJECT, creature->GetGUID());
// aura
/// @todo: Fix display here
@@ -745,6 +745,31 @@ void BattlegroundIC::HandleContestedNodes(ICNodePoint* nodePoint)
(*itr)->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
}
}
else if (nodePoint->nodeType == NODE_TYPE_DOCKS)
{
auto removeUnusedVehicles = [&](uint8 baseType, uint8 count)
{
for (uint8 i = 0; i < count; ++i)
{
uint8 type = baseType + i;
if (Creature* vehicle = GetBgMap()->GetCreature(BgCreatures[type]))
{
if (Vehicle* veh = vehicle->GetVehicleKit())
if (!veh->IsVehicleInUse())
{
respawnMap.erase(vehicle->GetGUID());
DelCreature(type);
}
}
}
};
uint8 glaiveBase = (nodePoint->faction == TEAM_ALLIANCE ? BG_IC_NPC_GLAIVE_THROWER_1_H : BG_IC_NPC_GLAIVE_THROWER_1_A);
removeUnusedVehicles(glaiveBase, MAX_GLAIVE_THROWERS_SPAWNS_PER_FACTION);
uint8 catapultBase = (nodePoint->faction == TEAM_ALLIANCE ? BG_IC_NPC_CATAPULT_1_H : BG_IC_NPC_CATAPULT_1_A);
removeUnusedVehicles(catapultBase, MAX_CATAPULTS_SPAWNS_PER_FACTION);
}
else if (nodePoint->nodeType == NODE_TYPE_REFINERY || nodePoint->nodeType == NODE_TYPE_QUARRY)
{
// nodePoint->faction is the assaulting team (set before this call);
+12 -6
View File
@@ -707,10 +707,15 @@ void Creature::Update(uint32 diff)
{
if (IsAIEnabled && TriggerJustRespawned && getDeathState() != DeathState::Dead)
{
if (_respawnCompatibilityMode && m_vehicleKit)
m_vehicleKit->Reset();
TriggerJustRespawned = false;
AI()->JustRespawned();
// Skip for temp summons: InitializeAI already reset them, and JustRespawned would clobber state set synchronously during SUMMON.
if (!IsSummon())
{
if (_respawnCompatibilityMode && m_vehicleKit)
m_vehicleKit->Reset();
AI()->JustRespawned();
}
}
switch (m_deathState)
@@ -1113,9 +1118,6 @@ bool Creature::AIM_Initialize(CreatureAI* ai)
IsAIEnabled = true;
i_AI->InitializeAI();
// Xinef: Initialize vehicle if it is not summoned!
if (GetVehicleKit() && m_spawnId)
GetVehicleKit()->Reset();
return true;
}
@@ -2052,6 +2054,10 @@ void Creature::Respawn(bool force)
if (getDeathState() == DeathState::Dead)
{
// TempSummons (no m_spawnId) shouldn't be resurrected here; TempSummon::Update UnSummons them on the next tick once deathState is Dead.
if (!m_spawnId && !force)
return;
if (m_spawnId)
{
GetMap()->RemoveCreatureRespawnTime(m_spawnId);
@@ -318,6 +318,9 @@ void MotionTransport::RemovePassenger(WorldObject* passenger, bool withAll)
Creature* MotionTransport::CreateNPCPassenger(ObjectGuid::LowType guid, CreatureData const* data)
{
Map* map = GetMap();
if (map->GetCreatureRespawnTime(guid))
return nullptr;
Creature* creature = new Creature();
if (!creature->LoadCreatureFromDB(guid, map, false))
@@ -366,6 +369,9 @@ Creature* MotionTransport::CreateNPCPassenger(ObjectGuid::LowType guid, Creature
GameObject* MotionTransport::CreateGOPassenger(ObjectGuid::LowType guid, GameObjectData const* data)
{
Map* map = GetMap();
if (map->GetGORespawnTime(guid))
return nullptr;
GameObject* go = new GameObject();
ASSERT(!sObjectMgr->IsGameObjectStaticTransport(data->id));
+11 -5
View File
@@ -16130,13 +16130,19 @@ void Unit::StopAttackFaction(uint32 faction_id)
++itr;
}
// End combat and threat references with creatures in this faction
std::vector<CombatReference*> refsToEnd;
// Collect GUIDs, not pointers: EndCombat can cascade through AI callbacks
// (formation MemberEvaded -> CombatStop) and free other refs in the list.
std::vector<ObjectGuid> guidsToEnd;
for (auto const& pair : m_combatManager.GetPvECombatRefs())
if (pair.second->GetOther(this)->GetFactionTemplateEntry()->faction == faction_id)
refsToEnd.push_back(pair.second);
for (CombatReference* ref : refsToEnd)
ref->EndCombat();
guidsToEnd.push_back(pair.first);
for (ObjectGuid const& guid : guidsToEnd)
{
auto const& refs = m_combatManager.GetPvECombatRefs();
auto it = refs.find(guid);
if (it != refs.end())
it->second->EndCombat();
}
for (ControlSet::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
(*itr)->StopAttackFaction(faction_id);
+13 -3
View File
@@ -29,7 +29,8 @@
#include <algorithm>
Vehicle::Vehicle(Unit* unit, VehicleEntry const* vehInfo, uint32 creatureEntry) :
_me(unit), _vehicleInfo(vehInfo), _usableSeatNum(0), _creatureEntry(creatureEntry), _status(STATUS_NONE)
_me(unit), _vehicleInfo(vehInfo), _usableSeatNum(0), _creatureEntry(creatureEntry), _status(STATUS_NONE),
_accessoriesInstalled(false)
{
for (uint32 i = 0; i < MAX_VEHICLE_SEATS; ++i)
{
@@ -87,8 +88,16 @@ void Vehicle::Install()
void Vehicle::InstallAllAccessories(bool evading)
{
if (GetBase()->IsPlayer() || !evading)
RemoveAllPassengers(); // We might have aura's saved in the DB with now invalid casters - remove
// Clear stale control-vehicle auras only on the first non-evade reset;
// a re-Reset would otherwise eject a just-seated passenger and, for
// accessories, fire a spurious SMART_EVENT_PASSENGER_REMOVED.
if (GetBase()->IsPlayer() || (!evading && !_accessoriesInstalled))
RemoveAllPassengers();
// Mark the initial reset as done even if this vehicle has no accessory
// list, so subsequent Resets don't eject passengers on vehicles without
// accessories (e.g. WG demolisher/catapult).
_accessoriesInstalled = true;
VehicleAccessoryList const* accessories = sObjectMgr->GetVehicleAccessoryList(this);
if (!accessories)
@@ -111,6 +120,7 @@ void Vehicle::Uninstall()
_status = STATUS_UNINSTALLING;
LOG_DEBUG("vehicles", "Vehicle::Uninstall {}", _me->GetGUID().ToString());
RemoveAllPassengers();
_accessoriesInstalled = false;
if (_me && _me->IsCreature())
{
@@ -96,6 +96,7 @@ private:
uint32 _usableSeatNum; // Number of seats that match VehicleSeatEntry::UsableByPlayer, used for proper display flags
uint32 _creatureEntry; // Can be different than me->GetBase()->GetEntry() in case of players
Status _status;
bool _accessoriesInstalled;
};
class VehicleDespawnEvent : public BasicEvent
+18 -7
View File
@@ -2422,9 +2422,14 @@ void ObjectMgr::LoadCreatures()
data.spawntimesecs = 14 * DAY;
// Skip spawnMask check for transport maps
if (!_transportMaps.count(data.mapid) && data.spawnMask & ~spawnMasks[data.mapid])
LOG_ERROR("sql.sql", "Table `creature` have creature (SpawnId: {}) that have wrong spawn mask {} including not supported difficulty modes for map (Id: {}).",
spawnId, data.spawnMask, data.mapid);
if (!_transportMaps.count(data.mapid))
{
if (data.spawnMask & ~spawnMasks[data.mapid])
LOG_ERROR("sql.sql", "Table `creature` have creature (SpawnId: {}) that have wrong spawn mask {} including not supported difficulty modes for map (Id: {}).",
spawnId, data.spawnMask, data.mapid);
}
else
data.spawnGroupId = 1; // force compatibility group for transport spawns
bool ok = true;
for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1 && ok; ++diff)
@@ -2955,8 +2960,13 @@ void ObjectMgr::LoadGameobjects()
data.spawnMask = fields[14].Get<uint8>();
if (!_transportMaps.count(data.mapid) && data.spawnMask & ~spawnMasks[data.mapid])
LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) that has wrong spawn mask {} including not supported difficulty modes for map (Id: {}), skip", guid, data.id, data.spawnMask, data.mapid);
if (!_transportMaps.count(data.mapid))
{
if (data.spawnMask & ~spawnMasks[data.mapid])
LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) that has wrong spawn mask {} including not supported difficulty modes for map (Id: {}), skip", guid, data.id, data.spawnMask, data.mapid);
}
else
data.spawnGroupId = 1; // force compatibility group for transport spawns
data.phaseMask = fields[15].Get<uint32>();
int16 gameEvent = fields[16].Get<int16>();
@@ -8771,11 +8781,12 @@ void ObjectMgr::LoadSpawnGroups()
uint32 oldMSTime = getMSTime();
// Reset prior state for hot-reload support
// Preserve the forced legacy group for spawns on transport maps (set in LoadCreatures/LoadGameobjects).
_spawnGroupMapStore.clear();
for (auto& [id, data] : _creatureDataStore)
data.spawnGroupId = 0;
data.spawnGroupId = _transportMaps.count(data.mapid) ? 1 : 0;
for (auto& [id, data] : _gameObjectDataStore)
data.spawnGroupId = 0;
data.spawnGroupId = _transportMaps.count(data.mapid) ? 1 : 0;
// 0 1 2
QueryResult result = WorldDatabase.Query("SELECT groupId, spawnType, spawnId FROM spawn_group");
@@ -24,6 +24,7 @@
#include "GridNotifiers.h"
#include "ObjectMgr.h"
#include "Transport.h"
#include "Vehicle.h"
template <class T>
void GridObjectLoader::AddObjectHelper(Map* map, T* obj)
@@ -53,6 +54,10 @@ void GridObjectLoader::LoadCreatures(CellGuidSet const& guid_set, Map* map)
AddObjectHelper<Creature>(map, obj);
// Grid load bypasses Map::AddToMap, so seat accessories here.
if (Vehicle* vehicle = obj->GetVehicleKit())
vehicle->Reset();
if (!obj->IsMoveInLineOfSightDisabled() && obj->GetDefaultMovementType() == IDLE_MOTION_TYPE && !obj->isNeedNotify(NOTIFY_VISIBILITY_CHANGED | NOTIFY_AI_RELOCATION))
{
if (obj->IsAlive() && !obj->HasUnitState(UNIT_STATE_SIGHTLESS) && obj->HasReactState(REACT_AGGRESSIVE) && !obj->IsImmuneToNPC())
+1 -2
View File
@@ -346,8 +346,7 @@ bool Map::AddToMap(T* obj, bool checkTransport)
//also, trigger needs to cast spell, if not update, cannot see visual
obj->UpdateObjectVisibility(true);
// Xinef: little hack for vehicles, accessories have to be added after visibility update so they wont fall off the vehicle, moved from Creature::AIM_Initialize
// Initialize vehicle, this is done only for summoned npcs, DB creatures are handled by grid loaders
// Post-visibility so accessories seat after the vehicle's create packet reaches clients.
if (obj->IsCreature())
if (Vehicle* vehicle = obj->ToCreature()->GetVehicleKit())
vehicle->Reset();
+5 -5
View File
@@ -235,11 +235,11 @@ public:
data.posZ = chr->GetTransOffsetZ();
data.orientation = chr->GetTransOffsetO();
Creature* creature = trans->CreateNPCPassenger(guid, &data);
creature->SaveToDB(trans->GetGOInfo()->moTransport.mapID, 1 << map->GetSpawnMode(), chr->GetPhaseMaskForSpawn());
sObjectMgr->AddCreatureToGrid(guid, &data);
if (Creature* creature = trans->CreateNPCPassenger(guid, &data))
{
creature->SaveToDB(trans->GetGOInfo()->moTransport.mapID, 1 << map->GetSpawnMode(), chr->GetPhaseMaskForSpawn());
sObjectMgr->AddCreatureToGrid(guid, &data);
}
return true;
}
@@ -39,8 +39,11 @@ struct go_shadowforge_brazier : public GameObjectAI
{
go_shadowforge_brazier(GameObject* go) : GameObjectAI(go) {}
bool GossipHello(Player* /*player*/, bool /*reportUse*/) override
bool GossipHello(Player* /*player*/, bool reportUse) override
{
if (reportUse)
return false;
if (InstanceScript* instance = me->GetInstanceScript())
{
GameObject* northBrazier = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_SF_BRAZIER_N));
@@ -314,286 +314,7 @@ public:
enum UnworthyInitiate
{
SPELL_SOUL_PRISON_CHAIN = 54612,
SPELL_DK_INITIATE_VISUAL = 51519,
SPELL_ICY_TOUCH = 52372,
SPELL_PLAGUE_STRIKE = 52373,
SPELL_BLOOD_STRIKE = 52374,
SPELL_DEATH_COIL = 52375,
SAY_EVENT_START = 0,
SAY_EVENT_ATTACK = 1,
EVENT_ICY_TOUCH = 1,
EVENT_PLAGUE_STRIKE = 2,
EVENT_BLOOD_STRIKE = 3,
EVENT_DEATH_COIL = 4
};
enum UnworthyInitiatePhase
{
PHASE_CHAINED,
PHASE_TO_EQUIP,
PHASE_EQUIPING,
PHASE_TO_ATTACK,
PHASE_ATTACKING,
};
uint32 acherus_soul_prison[12] =
{
191577,
191580,
191581,
191582,
191583,
191584,
191585,
191586,
191587,
191588,
191589,
191590
};
//uint32 acherus_unworthy_initiate[5] =
//{
// 29519,
// 29520,
// 29565,
// 29566,
// 29567
//};
class npc_unworthy_initiate : public CreatureScript
{
public:
npc_unworthy_initiate() : CreatureScript("npc_unworthy_initiate") { }
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_unworthy_initiateAI(creature);
}
struct npc_unworthy_initiateAI : public ScriptedAI
{
npc_unworthy_initiateAI(Creature* creature) : ScriptedAI(creature)
{
me->SetReactState(REACT_PASSIVE);
if (!me->GetCurrentEquipmentId())
me->SetCurrentEquipmentId(me->GetOriginalEquipmentId());
}
ObjectGuid playerGUID;
UnworthyInitiatePhase phase;
uint32 wait_timer;
float anchorX, anchorY;
ObjectGuid anchorGUID;
EventMap events;
void Reset() override
{
anchorGUID.Clear();
phase = PHASE_CHAINED;
events.Reset();
me->SetFaction(FACTION_CREATURE);
me->SetImmuneToPC(true);
me->SetUInt32Value(UNIT_FIELD_BYTES_1, 8);
me->LoadEquipment(0, true);
}
void JustEngagedWith(Unit* /*who*/) override
{
events.ScheduleEvent(EVENT_ICY_TOUCH, 1s, GCD_CAST);
events.ScheduleEvent(EVENT_PLAGUE_STRIKE, 3s, GCD_CAST);
events.ScheduleEvent(EVENT_BLOOD_STRIKE, 2s, GCD_CAST);
events.ScheduleEvent(EVENT_DEATH_COIL, 5s, GCD_CAST);
}
void MovementInform(uint32 type, uint32 id) override
{
if (type != POINT_MOTION_TYPE)
return;
if (id == 1)
{
wait_timer = 5000;
me->LoadEquipment(1);
me->CastSpell(me, SPELL_DK_INITIATE_VISUAL, true);
if (Player* starter = ObjectAccessor::GetPlayer(*me, playerGUID))
Talk(SAY_EVENT_ATTACK, starter);
phase = PHASE_TO_ATTACK;
}
}
void EventStart(Creature* anchor, Player* target)
{
wait_timer = 5000;
phase = PHASE_TO_EQUIP;
me->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
me->RemoveAurasDueToSpell(SPELL_SOUL_PRISON_CHAIN);
float z;
anchor->GetContactPoint(me, anchorX, anchorY, z, 1.0f);
playerGUID = target->GetGUID();
Talk(SAY_EVENT_START, target);
}
void UpdateAI(uint32 diff) override
{
switch (phase)
{
case PHASE_CHAINED:
if (!anchorGUID)
{
if (Creature* anchor = me->FindNearestCreature(29521, 30))
{
anchor->AI()->SetGUID(me->GetGUID());
anchor->CastSpell(me, SPELL_SOUL_PRISON_CHAIN, true);
anchorGUID = anchor->GetGUID();
}
float dist = 99.0f;
GameObject* prison = nullptr;
for (uint8 i = 0; i < 12; ++i)
{
if (GameObject* temp_prison = me->FindNearestGameObject(acherus_soul_prison[i], 100))
{
if (me->IsWithinDist(temp_prison, dist, false))
{
dist = me->GetDistance2d(temp_prison);
prison = temp_prison;
}
}
}
// Must check for loot state as out of order updates will reset
// the prison gameobject during spawn causing invalid state
if (prison && prison->getLootState() != GO_NOT_READY)
prison->ResetDoorOrButton();
}
break;
case PHASE_TO_EQUIP:
if (wait_timer)
{
if (wait_timer > diff)
wait_timer -= diff;
else
{
me->GetMotionMaster()->MovePoint(1, anchorX, anchorY, me->GetPositionZ());
//LOG_DEBUG("scripts.ai", "npc_unworthy_initiateAI: move to {} {} {}", anchorX, anchorY, me->GetPositionZ());
phase = PHASE_EQUIPING;
wait_timer = 0;
}
}
break;
case PHASE_TO_ATTACK:
if (wait_timer)
{
if (wait_timer > diff)
wait_timer -= diff;
else
{
me->SetFaction(FACTION_MONSTER);
me->SetImmuneToPC(false);
phase = PHASE_ATTACKING;
if (Player* target = ObjectAccessor::GetPlayer(*me, playerGUID))
AttackStart(target);
wait_timer = 0;
}
}
break;
case PHASE_ATTACKING:
if (!UpdateVictim())
return;
events.Update(diff);
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_ICY_TOUCH:
DoCastVictim(SPELL_ICY_TOUCH);
events.DelayEvents(1s, GCD_CAST);
events.ScheduleEvent(EVENT_ICY_TOUCH, 5s, GCD_CAST);
break;
case EVENT_PLAGUE_STRIKE:
DoCastVictim(SPELL_PLAGUE_STRIKE);
events.DelayEvents(1s, GCD_CAST);
events.ScheduleEvent(EVENT_PLAGUE_STRIKE, 5s, GCD_CAST);
break;
case EVENT_BLOOD_STRIKE:
DoCastVictim(SPELL_BLOOD_STRIKE);
events.DelayEvents(1s, GCD_CAST);
events.ScheduleEvent(EVENT_BLOOD_STRIKE, 5s, GCD_CAST);
break;
case EVENT_DEATH_COIL:
DoCastVictim(SPELL_DEATH_COIL);
events.DelayEvents(1s, GCD_CAST);
events.ScheduleEvent(EVENT_DEATH_COIL, 5s, GCD_CAST);
break;
}
}
DoMeleeAttackIfReady();
break;
default:
break;
}
}
};
};
class npc_unworthy_initiate_anchor : public CreatureScript
{
public:
npc_unworthy_initiate_anchor() : CreatureScript("npc_unworthy_initiate_anchor") { }
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_unworthy_initiate_anchorAI(creature);
}
struct npc_unworthy_initiate_anchorAI : public PassiveAI
{
npc_unworthy_initiate_anchorAI(Creature* creature) : PassiveAI(creature) {}
ObjectGuid prisonerGUID;
void SetGUID(ObjectGuid const& guid, int32 /*id*/) override
{
if (!prisonerGUID)
prisonerGUID = guid;
}
ObjectGuid GetGUID(int32 /*id*/) const override
{
return prisonerGUID;
}
};
};
class go_acherus_soul_prison : public GameObjectScript
{
public:
go_acherus_soul_prison() : GameObjectScript("go_acherus_soul_prison") { }
bool OnGossipHello(Player* player, GameObject* go) override
{
if (Creature* anchor = go->FindNearestCreature(29521, 15))
if (ObjectGuid prisonerGUID = anchor->AI()->GetGUID())
if (Creature* prisoner = ObjectAccessor::GetCreature(*player, prisonerGUID))
CAST_AI(npc_unworthy_initiate::npc_unworthy_initiateAI, prisoner->AI())->EventStart(anchor, player);
return false;
}
};
class spell_death_knight_initiate_visual : public SpellScript
@@ -732,9 +453,6 @@ void AddSC_the_scarlet_enclave_c1()
RegisterSpellScript(spell_q12698_the_gift_that_keeps_on_giving);
new npc_scarlet_ghoul();
new npc_dkc1_gothik();
new npc_unworthy_initiate();
new npc_unworthy_initiate_anchor();
new go_acherus_soul_prison();
RegisterSpellScript(spell_death_knight_initiate_visual);
RegisterSpellScript(spell_lich_king_whisper);
RegisterSpellScript(spell_lich_king_vo_blocker);
@@ -229,9 +229,6 @@ struct boss_kaelthas : public BossAI
{
advisor->Respawn(true);
advisor->StopMovingOnCurrentPos();
advisor->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
advisor->SetReactState(REACT_PASSIVE);
summons.Summon(advisor);
}
}
}
@@ -550,7 +547,7 @@ struct boss_kaelthas : public BossAI
void IntroduceNewAdvisor(KTYells talkIntroduction, KTActions kaelAction)
{
std::chrono::milliseconds attackStartTimer = 0ms;
EyeNPCs advisorNPCId = NPC_THALADRED;
uint32 dataIdx = DATA_THALADRED;
scheduler.Schedule(2s, [this, talkIntroduction](TaskContext)
{
Talk(talkIntroduction);
@@ -560,26 +557,26 @@ struct boss_kaelthas : public BossAI
{
case ACTION_START_THALADRED:
attackStartTimer = 7s;
advisorNPCId = NPC_THALADRED;
dataIdx = DATA_THALADRED;
break;
case ACTION_START_SANGUINAR:
attackStartTimer = 14500ms;
advisorNPCId = NPC_LORD_SANGUINAR;
dataIdx = DATA_LORD_SANGUINAR;
break;
case ACTION_START_CAPERNIAN:
attackStartTimer = 9s;
advisorNPCId = NPC_CAPERNIAN;
dataIdx = DATA_CAPERNIAN;
break;
case ACTION_START_TELONICUS:
attackStartTimer = 10400ms;
advisorNPCId = NPC_TELONICUS;
dataIdx = DATA_TELONICUS;
break;
default:
break;
}
scheduler.Schedule(attackStartTimer, [this, advisorNPCId](TaskContext)
scheduler.Schedule(attackStartTimer, [this, dataIdx](TaskContext)
{
if (Creature* advisor = summons.GetCreatureWithEntry(advisorNPCId))
if (Creature* advisor = instance->GetCreature(dataIdx))
{
advisor->SetReactState(REACT_AGGRESSIVE);
advisor->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
@@ -737,6 +734,13 @@ struct advisor_baseAI : public ScriptedAI
scheduler.CancelAll();
}
void JustRespawned() override
{
ScriptedAI::JustRespawned();
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetReactState(REACT_PASSIVE);
}
void JustEngagedWith(Unit* /*who*/) override { ScheduleEvents(); }
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageEffectType*/, SpellSchoolMask /*spellSchoolMask*/) override
@@ -67,42 +67,10 @@ public:
LoadBossBoundaries(boundaries);
}
ObjectGuid ThaladredTheDarkenerGUID;
ObjectGuid LordSanguinarGUID;
ObjectGuid GrandAstromancerCapernianGUID;
ObjectGuid MasterEngineerTelonicusGUID;
ObjectGuid AlarGUID;
ObjectGuid KaelthasGUID;
ObjectGuid BridgeWindowGUID;
ObjectGuid KaelStateRightGUID;
ObjectGuid KaelStateLeftGUID;
void OnCreatureCreate(Creature* creature) override
{
switch (creature->GetEntry())
{
case NPC_ALAR:
AlarGUID = creature->GetGUID();
break;
case NPC_KAELTHAS:
KaelthasGUID = creature->GetGUID();
break;
case NPC_THALADRED:
ThaladredTheDarkenerGUID = creature->GetGUID();
break;
case NPC_TELONICUS:
MasterEngineerTelonicusGUID = creature->GetGUID();
break;
case NPC_CAPERNIAN:
GrandAstromancerCapernianGUID = creature->GetGUID();
break;
case NPC_LORD_SANGUINAR:
LordSanguinarGUID = creature->GetGUID();
break;
}
InstanceScript::OnCreatureCreate(creature);
}
void OnGameObjectCreate(GameObject* gobject) override
{
switch (gobject->GetEntry())
@@ -130,10 +98,6 @@ public:
return KaelStateRightGUID;
case GO_KAEL_STATUE_LEFT:
return KaelStateLeftGUID;
case NPC_ALAR:
return AlarGUID;
case NPC_KAELTHAS:
return KaelthasGUID;
}
return ObjectGuid::Empty;
@@ -588,10 +588,8 @@ public:
{
for (Creature* whelp : creatureList)
{
if (whelp->IsAlive() && !whelp->IsInCombat() && whelp->GetMotionMaster()->GetCurrentMovementGeneratorType() != HOME_MOTION_TYPE)
{
if (whelp && whelp->IsAlive() && !whelp->IsInCombat() && whelp->GetMotionMaster()->GetCurrentMovementGeneratorType() != HOME_MOTION_TYPE)
whelp->GetMotionMaster()->MovePoint(0, me->GetNearPosition(4.0f, whelp->GetOrientation()));
}
}
}
}
@@ -600,10 +598,8 @@ public:
{
for (Creature* whelp : creatureList)
{
if (whelp->IsAlive() && !whelp->IsInCombat() && whelp->GetMotionMaster()->GetCurrentMovementGeneratorType() != HOME_MOTION_TYPE)
{
if (whelp && whelp->IsAlive() && !whelp->IsInCombat() && whelp->GetMotionMaster()->GetCurrentMovementGeneratorType() != HOME_MOTION_TYPE)
whelp->GetMotionMaster()->MoveTargetedHome();
}
}
}
}
+17 -147
View File
@@ -214,27 +214,11 @@ enum UnstablePower
SPELL_UNSTABLE_POWER_AURA = 24659
};
enum CommendationOfKaelthas
{
SPELL_COMMENDATION_OF_KAELTHAS = 45480
};
enum CorpseTongueCoin
{
SPELL_CORPSE_TONGUE_COIN = 71633,
SPELL_CORPSE_TONGUE_COIN_HERO = 71634
};
enum CrystalSpireOfKarabor
{
SPELL_CRYSTAL_SPIRE_OF_KARABOR_MANA = 35476
};
enum SoulHarvestersCharm
{
SPELL_SOUL_HARVESTERS_CHARM = 60513
};
enum SunwellExaltedNeck
{
SPELL_LIGHTS_WRATH = 45479,
@@ -260,12 +244,6 @@ enum TotemOfFlowingWater
SPELL_TOTEM_OF_FLOWING_WATER_MANA = 28857
};
enum PetrifiedTwilightScale
{
SPELL_PETRIFIED_TWILIGHT_SCALE_HC = 75480,
SPELL_PETRIFIED_TWILIGHT_SCALE = 75477
};
enum ShardOfTheScale
{
SPELL_PURIFIED_CAUTERIZING_HEAL = 69733,
@@ -5706,67 +5684,30 @@ class spell_item_unstable_power : public AuraScript
}
};
// 45478 - Commendation of Kael'thas
class spell_item_commendation_of_kaelthas : public AuraScript
// 45057 - Evasive Maneuvers (Commendation of Kael'thas)
// 52420 - Deflection (Soul Harvester's Charm)
// 71634 - Item - Icecrown 25 Normal Tank Trinket 1 (Corpse Tongue Coin)
// 71640 - Item - Icecrown 25 Heroic Tank Trinket 1 (Corpse Tongue Coin (Heroic))
// 75475 - Item - Chamber of Aspects 25 Tank Trinket (Petrified Twilight Scale)
// 75481 - Item - Chamber of Aspects 25 Heroic Tank Trinket (Petrified Twilight Scale (Heroic))
// Trinkets that proc only when a melee hit drops you below EFFECT_0 % HP.
class spell_item_proc_below_pct_damaged : public AuraScript
{
PrepareAuraScript(spell_item_commendation_of_kaelthas);
PrepareAuraScript(spell_item_proc_below_pct_damaged);
bool CheckProc(ProcEventInfo& eventInfo)
{
if (HealInfo* healInfo = eventInfo.GetHealInfo())
if (Unit* target = healInfo->GetTarget())
if (target->GetHealth() + healInfo->GetEffectiveHeal() >= target->GetMaxHealth())
return true;
return false;
DamageInfo* damageInfo = eventInfo.GetDamageInfo();
if (!damageInfo || !damageInfo->GetDamage())
return false;
int32 pct = GetSpellInfo()->Effects[EFFECT_0].CalcValue();
return eventInfo.GetActionTarget()->HealthBelowPctDamaged(pct, damageInfo->GetDamage());
}
void Register() override
{
DoCheckProc += AuraCheckProcFn(spell_item_commendation_of_kaelthas::CheckProc);
}
};
// 71632 - Corpse Tongue Coin
class spell_item_corpse_tongue_coin : public AuraScript
{
PrepareAuraScript(spell_item_corpse_tongue_coin);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_CORPSE_TONGUE_COIN });
}
void HandleProc(ProcEventInfo& eventInfo)
{
PreventDefaultAction();
eventInfo.GetActor()->CastSpell((Unit*)nullptr, SPELL_CORPSE_TONGUE_COIN, true, nullptr, GetEffect(EFFECT_0));
}
void Register() override
{
OnProc += AuraProcFn(spell_item_corpse_tongue_coin::HandleProc);
}
};
// 71639 - Corpse Tongue Coin (Heroic)
class spell_item_corpse_tongue_coin_heroic : public AuraScript
{
PrepareAuraScript(spell_item_corpse_tongue_coin_heroic);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_CORPSE_TONGUE_COIN_HERO });
}
void HandleProc(ProcEventInfo& eventInfo)
{
PreventDefaultAction();
eventInfo.GetActor()->CastSpell((Unit*)nullptr, SPELL_CORPSE_TONGUE_COIN_HERO, true, nullptr, GetEffect(EFFECT_0));
}
void Register() override
{
OnProc += AuraProcFn(spell_item_corpse_tongue_coin_heroic::HandleProc);
DoCheckProc += AuraCheckProcFn(spell_item_proc_below_pct_damaged::CheckProc);
}
};
@@ -5788,28 +5729,6 @@ class spell_item_crystal_spire_of_karabor : public AuraScript
}
};
// 60512 - Soul Harvester's Charm
class spell_item_soul_harvesters_charm : public AuraScript
{
PrepareAuraScript(spell_item_soul_harvesters_charm);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_SOUL_HARVESTERS_CHARM });
}
void HandleProc(ProcEventInfo& eventInfo)
{
PreventDefaultAction();
eventInfo.GetActor()->CastSpell((Unit*)nullptr, SPELL_SOUL_HARVESTERS_CHARM, true, nullptr, GetEffect(EFFECT_0));
}
void Register() override
{
OnProc += AuraProcFn(spell_item_soul_harvesters_charm::HandleProc);
}
};
// 45481 - Sunwell Exalted Caster Neck
class spell_item_sunwell_exalted_caster_neck : public AuraScript
{
@@ -6025,50 +5944,6 @@ class spell_item_totem_of_flowing_water : public AuraScript
}
};
// 75466 - Petrified Twilight Scale
class spell_item_petrified_twilight_scale : public AuraScript
{
PrepareAuraScript(spell_item_petrified_twilight_scale);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_PETRIFIED_TWILIGHT_SCALE });
}
void HandleProc(ProcEventInfo& eventInfo)
{
PreventDefaultAction();
eventInfo.GetActionTarget()->CastSpell((Unit*)nullptr, SPELL_PETRIFIED_TWILIGHT_SCALE, true, nullptr, GetEffect(EFFECT_0));
}
void Register() override
{
OnProc += AuraProcFn(spell_item_petrified_twilight_scale::HandleProc);
}
};
// 75473 - Petrified Twilight Scale (Heroic)
class spell_item_petrified_twilight_scale_heroic : public AuraScript
{
PrepareAuraScript(spell_item_petrified_twilight_scale_heroic);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_PETRIFIED_TWILIGHT_SCALE_HC });
}
void HandleProc(ProcEventInfo& eventInfo)
{
PreventDefaultAction();
eventInfo.GetActionTarget()->CastSpell((Unit*)nullptr, SPELL_PETRIFIED_TWILIGHT_SCALE_HC, true, nullptr, GetEffect(EFFECT_0));
}
void Register() override
{
OnProc += AuraProcFn(spell_item_petrified_twilight_scale_heroic::HandleProc);
}
};
// 69739 - Purified Shard of the Scale
class spell_item_purified_shard_of_the_scale : public AuraScript
{
@@ -6355,11 +6230,8 @@ void AddSC_item_spell_scripts()
RegisterSpellScript(spell_item_pet_healing);
RegisterSpellScript(spell_item_restless_strength);
RegisterSpellScript(spell_item_unstable_power);
RegisterSpellScript(spell_item_commendation_of_kaelthas);
RegisterSpellScript(spell_item_corpse_tongue_coin);
RegisterSpellScript(spell_item_corpse_tongue_coin_heroic);
RegisterSpellScript(spell_item_proc_below_pct_damaged);
RegisterSpellScript(spell_item_crystal_spire_of_karabor);
RegisterSpellScript(spell_item_soul_harvesters_charm);
RegisterSpellScript(spell_item_sunwell_exalted_caster_neck);
RegisterSpellScript(spell_item_sunwell_exalted_healer_neck);
RegisterSpellScript(spell_item_sunwell_exalted_melee_neck);
@@ -6368,8 +6240,6 @@ void AddSC_item_spell_scripts()
RegisterSpellScript(spell_item_tiny_abomination_in_a_jar);
RegisterSpellScript(spell_item_tiny_abomination_in_a_jar_hero);
RegisterSpellScript(spell_item_totem_of_flowing_water);
RegisterSpellScript(spell_item_petrified_twilight_scale);
RegisterSpellScript(spell_item_petrified_twilight_scale_heroic);
RegisterSpellScript(spell_item_purified_shard_of_the_scale);
RegisterSpellScript(spell_item_shiny_shard_of_the_scale);
RegisterSpellScript(spell_item_living_root_of_the_wildheart);
@@ -574,12 +574,16 @@ public:
void RespawnPet()
{
Creature* precious = Precious();
if (!precious)
return;
Position current = me->GetNearPosition(-5.0f, 0.0f);
Precious()->RemoveCorpse(false, false);
Precious()->SetPosition(current);
Precious()->SetHomePosition(current);
Precious()->setDeathState(DeathState::JustRespawned);
Precious()->UpdateObjectVisibility(true);
precious->RemoveCorpse(false, false);
precious->SetPosition(current);
precious->SetHomePosition(current);
precious->setDeathState(DeathState::JustRespawned);
precious->UpdateObjectVisibility(true);
}
void HandlePetRespawn()