Merge branch 'master' into Playerbot

This commit is contained in:
Yunfan Li
2024-06-03 23:16:28 +08:00
40 changed files with 555 additions and 243 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE
*
* This file was based on
* https://embeddedartistry.com/blog/2017/05/17/creating-a-circular-buffer-in-c-and-c/

View File

@@ -1,5 +1,5 @@
/*
* Originally written by Rochet2 - Copyright (C) 2018+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: http://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Originally written by Rochet2 - Copyright (C) 2018+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: http://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE
*/
#ifndef _DATA_MAP_H_

View File

@@ -2176,6 +2176,9 @@ void Player::SetInWater(bool apply)
RemoveAurasWithInterruptFlags(apply ? AURA_INTERRUPT_FLAG_NOT_ABOVEWATER : AURA_INTERRUPT_FLAG_NOT_UNDERWATER);
getHostileRefMgr().updateThreatTables();
if (InstanceScript* instance = GetInstanceScript())
instance->OnPlayerInWaterStateUpdate(this, apply);
}
bool Player::IsInAreaTriggerRadius(AreaTrigger const* trigger, float delta) const

View File

@@ -436,31 +436,27 @@ void Player::UpdateNextMailTimeAndUnreads()
{
// Update the next delivery time and unread mails
time_t cTime = GameTime::GetGameTime().count();
// Get the next delivery time
CharacterDatabasePreparedStatement* stmtNextDeliveryTime =
CharacterDatabase.GetPreparedStatement(CHAR_SEL_NEXT_MAIL_DELIVERYTIME);
stmtNextDeliveryTime->SetData(0, GetGUID().GetCounter());
stmtNextDeliveryTime->SetData(1, uint32(cTime));
PreparedQueryResult resultNextDeliveryTime =
CharacterDatabase.Query(stmtNextDeliveryTime);
if (resultNextDeliveryTime)
{
Field* fields = resultNextDeliveryTime->Fetch();
m_nextMailDelivereTime = time_t(fields[0].Get<uint32>());
}
// Get unread mails count
CharacterDatabasePreparedStatement* stmtUnreadAmount =
CharacterDatabase.GetPreparedStatement(
CHAR_SEL_CHARACTER_MAILCOUNT_UNREAD_SYNCH);
stmtUnreadAmount->SetData(0, GetGUID().GetCounter());
stmtUnreadAmount->SetData(1, uint32(cTime));
PreparedQueryResult resultUnreadAmount =
CharacterDatabase.Query(stmtUnreadAmount);
if (resultUnreadAmount)
m_nextMailDelivereTime = 0;
unReadMails = 0;
for (Mail const* mail : GetMails())
{
Field* fields = resultUnreadAmount->Fetch();
unReadMails = uint8(fields[0].Get<uint64>());
if (mail->deliver_time > cTime)
{
if (!m_nextMailDelivereTime || m_nextMailDelivereTime > mail->deliver_time)
m_nextMailDelivereTime = mail->deliver_time;
}
// must be not checked yet
if (mail->checked & MAIL_CHECK_MASK_READ)
continue;
// and already delivered or expired
if (cTime < mail->deliver_time || cTime > mail->expire_time)
continue;
unReadMails++;
}
}

View File

@@ -4223,7 +4223,7 @@ void Unit::ProcessTerrainStatusUpdate()
// remove appropriate auras if we are swimming/not swimming respectively
if (liquidData.Status & MAP_LIQUID_STATUS_SWIMMING)
RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_ABOVEWATER);
else
else if (!isSwimming())
RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_UNDERWATER);
// liquid aura handling

View File

@@ -287,10 +287,7 @@ void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spe
case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet)
if (pet->GetCharmerGUID() == GetPlayer()->GetGUID())
{
if (pet->IsSummon())
pet->ToTempSummon()->UnSummon();
else
_player->StopCastingCharm();
_player->StopCastingCharm();
}
else if (pet->GetOwnerGUID() == GetPlayer()->GetGUID())
{

View File

@@ -186,6 +186,9 @@ public:
virtual void OnPlayerAreaUpdate(Player* /*player*/, uint32 /*oldArea*/, uint32 /*newArea*/) {}
//Called when a player enters/leaves water bodies.
virtual void OnPlayerInWaterStateUpdate(Player* /*player*/, bool /*inWater*/) {}
//Handle open / close objects
//use HandleGameObject(ObjectGuid::Empty, boolen, GO); in OnObjectCreate in instance scripts
//use HandleGameObject(GUID, boolen, nullptr); in any other script

View File

@@ -110,3 +110,21 @@ bool BoundaryUnionBoundary::IsWithinBoundaryArea(Position const* pos) const
{
return (_b1->IsWithinBoundary(pos) || _b2->IsWithinBoundary(pos));
}
// ---== INTERSECT OF 2 BOUNDARIES ==---
BoundaryIntersectBoundary::BoundaryIntersectBoundary(AreaBoundary const* b1, AreaBoundary const* b2, bool isInverted) :
AreaBoundary(isInverted), _b1(b1), _b2(b2)
{
ASSERT(b1 && b2);
}
BoundaryIntersectBoundary::~BoundaryIntersectBoundary()
{
delete _b1;
delete _b2;
}
bool BoundaryIntersectBoundary::IsWithinBoundaryArea(Position const* pos) const
{
return (_b1->IsWithinBoundary(pos) && _b2->IsWithinBoundary(pos));
}

View File

@@ -165,4 +165,18 @@ class AC_GAME_API BoundaryUnionBoundary : public AreaBoundary
AreaBoundary const* const _b2;
};
class AC_GAME_API BoundaryIntersectBoundary : public AreaBoundary
{
public:
BoundaryIntersectBoundary(AreaBoundary const* b1, AreaBoundary const* b2, bool isInverted = false);
protected:
virtual ~BoundaryIntersectBoundary();
bool IsWithinBoundaryArea(Position const* pos) const override;
private:
AreaBoundary const* const _b1;
AreaBoundary const* const _b2;
};
#endif //ACORE_AREA_BOUNDARY_H

View File

@@ -65,9 +65,8 @@ public:
context.Repeat(10s, 15s);
}).Schedule(25s, 32s, [this](TaskContext context)
{
if (DoCastRandomTarget(SPELL_SLEEP) == SPELL_CAST_OK)
Talk(SAY_SLEEP);
Talk(SAY_SLEEP);
DoCastRandomTarget(SPELL_SLEEP, 1, 0.0f, true, false, false);
context.Repeat(35s, 48s);
}).Schedule(30s, 48s, [this](TaskContext context)
{
@@ -137,7 +136,6 @@ public:
private:
bool _recentlySpoken;
};
class spell_anetheron_sleep : public SpellScript

View File

@@ -97,8 +97,9 @@ uint32 const availableChargeAurasAndSpells[3][2] = {
Position const nordrassilPosition = { 5503.713f, -3523.436f, 1608.781f, 0.0f };
float const DOOMFIRE_OFFSET = 15.0f;
float const DOOMFIRE_OFFSET = 25.0f;
uint8 const WISP_OFFSET = 40;
uint8 NEAR_POINT = 0;
struct npc_ancient_wisp : public ScriptedAI
{
@@ -148,6 +149,39 @@ private:
InstanceScript* _instance;
};
struct npc_doomfire_spirit : public ScriptedAI
{
npc_doomfire_spirit(Creature* creature) : ScriptedAI(creature)
{
_instance = creature->GetInstanceScript();
}
void Reset() override
{
scheduler.CancelAll();
ScheduleTimedEvent(0s, [&]{
if (Creature* archimonde = _instance->GetCreature(DATA_ARCHIMONDE))
{
Position randomNearPosition = archimonde->GetRandomNearPosition(200.0f);
me->GetMotionMaster()->MovePoint(NEAR_POINT, randomNearPosition);
}
}, 6s);
}
void UpdateAI(uint32 diff) override
{
scheduler.Update(diff);
if (!UpdateVictim())
return;
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
private:
InstanceScript* _instance;
};
struct boss_archimonde : public BossAI
{
boss_archimonde(Creature* creature) : BossAI(creature, DATA_ARCHIMONDE)
@@ -262,10 +296,7 @@ struct boss_archimonde : public BossAI
}, 25s, 40s);
ScheduleTimedEvent(25s, 35s, [&]
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, false))
{
DoCastDoomFire(target);
}
DoCastDoomFire();
}, 20s);
ScheduleTimedEvent(25s, 35s, [&]
{
@@ -391,12 +422,12 @@ struct boss_archimonde : public BossAI
}
}
void DoCastDoomFire(Unit* target)
void DoCastDoomFire()
{
// hack because spell doesn't work?
Talk(SAY_DOOMFIRE);
Position spiritPosition = { target->GetPositionX() + DOOMFIRE_OFFSET, target->GetPositionY() + DOOMFIRE_OFFSET, target->GetPositionZ(), 0.0f };
Position doomfirePosition = { target->GetPositionX() - DOOMFIRE_OFFSET, target->GetPositionY() - DOOMFIRE_OFFSET, target->GetPositionZ(), 0.0f };
Position spiritPosition = me->GetRandomNearPosition(DOOMFIRE_OFFSET);
Position doomfirePosition = me->GetRandomNearPosition(DOOMFIRE_OFFSET);
if (Creature* doomfireSpirit = me->SummonCreature(CREATURE_DOOMFIRE_SPIRIT, spiritPosition, TEMPSUMMON_TIMED_DESPAWN, 27000))
{
if (Creature* doomfire = me->SummonCreature(CREATURE_DOOMFIRE, doomfirePosition, TEMPSUMMON_TIMED_DESPAWN, 27000))
@@ -508,5 +539,6 @@ void AddSC_boss_archimonde()
RegisterSpellScript(spell_finger_of_death);
RegisterHyjalAI(boss_archimonde);
RegisterHyjalAI(npc_ancient_wisp);
RegisterHyjalAI(npc_doomfire_spirit);
}

View File

@@ -17,6 +17,8 @@
#include "CreatureScript.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "SpellScriptLoader.h"
#include "hyjal.h"
enum Spells
@@ -60,7 +62,7 @@ public:
context.Repeat(8s, 16s);
}).Schedule(25s, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_RAIN_OF_FIRE, 0, 40.f);
DoCastRandomTarget(SPELL_RAIN_OF_FIRE, 0, 40.f, false);
context.Repeat(15s);
}).Schedule(30s, [this](TaskContext context)
{
@@ -68,7 +70,7 @@ public:
context.Repeat(18s, 20s);
}).Schedule(45s, 55s, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_DOOM, 0, 100.f, true, false, false);
DoCastRandomTarget(SPELL_DOOM, 1, 100.f, true, false, false);
Talk(SAY_DOOM);
context.Repeat();
}).Schedule(10min, [this](TaskContext context)
@@ -114,10 +116,29 @@ public:
private:
bool _recentlySpoken;
};
class spell_azgalor_doom : public AuraScript
{
PrepareAuraScript(spell_azgalor_doom);
void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
Unit* target = GetTarget();
if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_DEATH && !IsExpired())
{
target->CastSpell(target, GetSpellInfo()->Effects[EFFECT_0].TriggerSpell, true);
}
}
void Register() override
{
OnEffectRemove += AuraEffectRemoveFn(spell_azgalor_doom::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL);
}
};
void AddSC_boss_azgalor()
{
RegisterHyjalAI(boss_azgalor);
RegisterSpellScript(spell_azgalor_doom);
}

View File

@@ -133,67 +133,51 @@ private:
uint8 _markCounter;
};
class spell_mark_of_kazrogal : public SpellScriptLoader
class spell_mark_of_kazrogal : public SpellScript
{
public:
spell_mark_of_kazrogal() : SpellScriptLoader("spell_mark_of_kazrogal") { }
PrepareSpellScript(spell_mark_of_kazrogal);
class spell_mark_of_kazrogal_SpellScript : public SpellScript
void FilterTargets(std::list<WorldObject*>& targets)
{
PrepareSpellScript(spell_mark_of_kazrogal_SpellScript);
void FilterTargets(std::list<WorldObject*>& targets)
{
targets.remove_if(Acore::PowerCheck(POWER_MANA, false));
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mark_of_kazrogal_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
}
};
class spell_mark_of_kazrogal_AuraScript : public AuraScript
{
PrepareAuraScript(spell_mark_of_kazrogal_AuraScript);
bool Validate(SpellInfo const* /*spell*/) override
{
return ValidateSpellInfo({ SPELL_MARK_DAMAGE });
}
void OnPeriodic(AuraEffect const* aurEff)
{
Unit* target = GetTarget();
if ((int32)target->GetPower(POWER_MANA) < aurEff->GetBaseAmount())
{
target->CastSpell(target, SPELL_MARK_DAMAGE, true, nullptr, aurEff);
// Remove aura
SetDuration(0);
}
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_mark_of_kazrogal_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_MANA_LEECH);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_mark_of_kazrogal_SpellScript();
targets.remove_if(Acore::PowerCheck(POWER_MANA, false));
}
AuraScript* GetAuraScript() const override
void Register() override
{
return new spell_mark_of_kazrogal_AuraScript();
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mark_of_kazrogal::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
}
};
class spell_mark_of_kazrogal_aura : public AuraScript
{
PrepareAuraScript(spell_mark_of_kazrogal_aura);
bool Validate(SpellInfo const* /*spell*/) override
{
return ValidateSpellInfo({ SPELL_MARK_DAMAGE });
}
void OnPeriodic(AuraEffect const* aurEff)
{
Unit* target = GetTarget();
if ((int32)target->GetPower(POWER_MANA) < aurEff->GetBaseAmount())
{
target->CastSpell(target, SPELL_MARK_DAMAGE, true, nullptr, aurEff);
// Remove aura
SetDuration(0);
}
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_mark_of_kazrogal_aura::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_MANA_LEECH);
}
};
void AddSC_boss_kazrogal()
{
RegisterHyjalAI(boss_kazrogal);
new spell_mark_of_kazrogal();
RegisterSpellAndAuraScriptPair(spell_mark_of_kazrogal, spell_mark_of_kazrogal_aura);
}

View File

@@ -120,7 +120,11 @@ enum HyjalCreaturesIds
NPC_KAZROGAL = 17888,
NPC_AZGALOR = 17842,
NPC_ARCHIMONDE = 17968,
NPC_WORLD_TRIGGER_TINY = 21987
NPC_WORLD_TRIGGER_TINY = 21987,
// Boss summons
NPC_TOWERING_INFERNAL = 17818,
NPC_LESSER_DOOMGUARD = 17864
};
enum HyjalGameobjectIds
@@ -144,7 +148,11 @@ enum HyjalMisc
START_WAVE_HORDE_RETREAT = 39,
START_WAVE_NIGHT_ELF = 42,
CONTEXT_GROUP_WAVES = 1
CONTEXT_GROUP_WAVES = 1,
AREA_NORDRASSIL = 3710,
SPELL_ETERNAL_SILENCE = 42201
};
enum HyjalPaths

View File

@@ -19,6 +19,7 @@
#include "InstanceMapScript.h"
#include "InstanceScript.h"
#include "Opcodes.h"
#include "Player.h"
#include "WorldPacket.h"
#include "hyjal.h"
@@ -101,6 +102,7 @@ public:
trash = 0;
_currentWave = 0;
_encounterNPCs.clear();
_summonedNPCs.clear();
_baseAlliance.clear();
_baseHorde.clear();
_infernalTargets.clear();
@@ -199,6 +201,13 @@ public:
_encounterNPCs.insert(creature->GetGUID()); // Used for despawning on wipe
}
break;
case NPC_TOWERING_INFERNAL:
case NPC_LESSER_DOOMGUARD:
if (creature->IsSummon())
{
_summonedNPCs.insert(creature->GetGUID());
}
break;
}
InstanceScript::OnCreatureCreate(creature);
}
@@ -232,6 +241,10 @@ public:
}
}
break;
case NPC_TOWERING_INFERNAL:
case NPC_LESSER_DOOMGUARD:
_summonedNPCs.erase(unit->ToCreature()->GetGUID());
break;
case NPC_WINTERCHILL:
case NPC_ANETHERON:
case NPC_KAZROGAL:
@@ -401,6 +414,14 @@ public:
if (Creature* creature = instance->GetCreature(guid))
creature->DespawnOrUnsummon();
// also force despawn boss summons
for (ObjectGuid const& guid : _summonedNPCs)
if (Creature* creature = instance->GetCreature(guid))
creature->DespawnOrUnsummon();
if (_bossWave && (GetBossState(_bossWave) != DONE))
SetBossState(_bossWave, NOT_STARTED);
_scheduler.Schedule(300s, [this](TaskContext)
{
for (ObjectGuid const& guid : _baseAlliance)
@@ -419,6 +440,14 @@ public:
if (Creature* creature = instance->GetCreature(guid))
creature->DespawnOrUnsummon();
// also force despawn boss summons
for (ObjectGuid const& guid : _summonedNPCs)
if (Creature* creature = instance->GetCreature(guid))
creature->DespawnOrUnsummon();
if (_bossWave && (GetBossState(_bossWave) != DONE))
SetBossState(_bossWave, NOT_STARTED);
_scheduler.Schedule(300s, [this](TaskContext)
{
for (ObjectGuid const& guid : _baseHorde)
@@ -454,6 +483,7 @@ public:
case DATA_RESET_WAVES:
_scheduler.CancelGroup(CONTEXT_GROUP_WAVES);
_encounterNPCs.clear();
_summonedNPCs.clear();
_currentWave = 0;
trash = 0;
_bossWave = 0;
@@ -530,6 +560,14 @@ public:
_scheduler.Update(diff);
}
void OnPlayerInWaterStateUpdate(Player* player, bool inWater) override
{
if (inWater && player->GetAreaId() == AREA_NORDRASSIL)
{
player->CastSpell(player, SPELL_ETERNAL_SILENCE, true);
}
}
protected:
int32 trash;
uint8 _currentWave;
@@ -537,6 +575,7 @@ public:
uint8 _retreat;
TaskScheduler _scheduler;
GuidSet _encounterNPCs;
GuidSet _summonedNPCs;
GuidSet _baseAlliance;
GuidSet _baseHorde;
GuidVector _infernalTargets;

View File

@@ -2383,20 +2383,45 @@ public:
return GetCaster()->GetTypeId() == TYPEID_UNIT;
}
void CheckEnergy()
void CalculatePower()
{
if (GetCaster()->GetPower(POWER_ENERGY) >= 100)
Unit* caster = GetCaster();
if (!caster)
return;
SpellInfo const* spellInfo = GetSpellInfo();
if (!spellInfo)
return;
// Check if the effect is energize
if (spellInfo->Effects[EFFECT_1].Effect == SPELL_EFFECT_ENERGIZE)
{
GetCaster()->CastSpell(GetCaster(), SPELL_OVERHEAT, true);
if (Vehicle* vehicle = GetCaster()->GetVehicleKit())
if (Unit* passenger = vehicle->GetPassenger(0))
sCreatureTextMgr->SendChat(GetCaster()->ToCreature(), SAY_OVERHEAT, passenger);
int32 energizeAmount = spellInfo->Effects[EFFECT_1].CalcValue(caster);
// Apply the power gain directly to the caster
caster->ModifyPower(POWER_ENERGY, energizeAmount);
}
if (caster->GetPower(POWER_ENERGY) >= 100)
{
caster->CastSpell(caster, SPELL_OVERHEAT, true);
if (Vehicle* vehicle = caster->GetVehicleKit())
if (Unit* passenger = vehicle->GetPassenger(0))
sCreatureTextMgr->SendChat(caster->ToCreature(), SAY_OVERHEAT, passenger);
}
}
void PreventPowerGainOnHit(SpellEffIndex effIndex)
{
PreventHitDefaultEffect(effIndex);
}
void Register() override
{
AfterHit += SpellHitFn(spell_igb_cannon_blast_SpellScript::CheckEnergy);
OnCast += SpellCastFn(spell_igb_cannon_blast_SpellScript::CalculatePower);
OnEffectHitTarget += SpellEffectFn(spell_igb_cannon_blast_SpellScript::PreventPowerGainOnHit, EFFECT_1, SPELL_EFFECT_ENERGIZE);
}
};

View File

@@ -869,85 +869,29 @@ private:
bool _removeHealers;
};
class spell_sindragosa_unchained_magic : public SpellScriptLoader
class spell_sindragosa_unchained_magic : public SpellScript
{
public:
spell_sindragosa_unchained_magic() : SpellScriptLoader("spell_sindragosa_unchained_magic") { }
PrepareSpellScript(spell_sindragosa_unchained_magic);
class spell_sindragosa_unchained_magic_SpellScript : public SpellScript
void FilterTargets(std::list<WorldObject*>& unitList)
{
PrepareSpellScript(spell_sindragosa_unchained_magic_SpellScript);
void FilterTargets(std::list<WorldObject*>& unitList)
{
std::list<WorldObject*> healList = unitList;
std::list<WorldObject*> dpsList = unitList;
unitList.clear();
uint32 maxSize = uint32(GetCaster()->GetMap()->GetSpawnMode() & 1 ? 3 : 1);
healList.remove_if(UnchainedMagicTargetSelector(false));
if (healList.size() > maxSize)
Acore::Containers::RandomResize(healList, maxSize);
dpsList.remove_if(UnchainedMagicTargetSelector(true));
if (dpsList.size() > maxSize)
Acore::Containers::RandomResize(dpsList, maxSize);
unitList.splice(unitList.begin(), healList);
unitList.splice(unitList.begin(), dpsList);
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sindragosa_unchained_magic_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_sindragosa_unchained_magic_SpellScript();
std::list<WorldObject*> healList = unitList;
std::list<WorldObject*> dpsList = unitList;
unitList.clear();
uint32 maxSize = uint32(GetCaster()->GetMap()->GetSpawnMode() & 1 ? 3 : 1);
healList.remove_if(UnchainedMagicTargetSelector(false));
if (healList.size() > maxSize)
Acore::Containers::RandomResize(healList, maxSize);
dpsList.remove_if(UnchainedMagicTargetSelector(true));
if (dpsList.size() > maxSize)
Acore::Containers::RandomResize(dpsList, maxSize);
unitList.splice(unitList.begin(), healList);
unitList.splice(unitList.begin(), dpsList);
}
class spell_sindragosa_unchained_magic_AuraScript : public AuraScript
void Register() override
{
PrepareAuraScript(spell_sindragosa_unchained_magic_AuraScript);
std::map<uint32, uint32> _lastMSTimeForSpell;
bool Validate(SpellInfo const* /*spellInfo*/) override
{
_lastMSTimeForSpell.clear();
return true;
}
bool CheckProc(ProcEventInfo& eventInfo)
{
SpellInfo const* spellInfo = eventInfo.GetSpellInfo();
if (!spellInfo)
return false;
uint32 currMSTime = GameTime::GetGameTimeMS().count();
std::map<uint32, uint32>::iterator itr = _lastMSTimeForSpell.find(spellInfo->Id);
if (itr != _lastMSTimeForSpell.end())
{
uint32 lastMSTime = itr->second;
itr->second = currMSTime;
if (getMSTimeDiff(lastMSTime, currMSTime) < 600)
return false;
return true;
}
_lastMSTimeForSpell[spellInfo->Id] = currMSTime;
return true;
}
void Register() override
{
DoCheckProc += AuraCheckProcFn(spell_sindragosa_unchained_magic_AuraScript::CheckProc);
}
};
AuraScript* GetAuraScript() const override
{
return new spell_sindragosa_unchained_magic_AuraScript();
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sindragosa_unchained_magic::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
}
};
@@ -2012,7 +1956,7 @@ void AddSC_boss_sindragosa()
new boss_sindragosa();
new npc_ice_tomb();
new spell_sindragosa_s_fury();
new spell_sindragosa_unchained_magic();
RegisterSpellScript(spell_sindragosa_unchained_magic);
new spell_sindragosa_permeating_chill();
new spell_sindragosa_instability();
new spell_sindragosa_icy_grip();

View File

@@ -665,6 +665,37 @@ public:
};
};
struct boss_kologarn_pit_kill_bunny : public NullCreatureAI
{
boss_kologarn_pit_kill_bunny(Creature* creature) : NullCreatureAI(creature) { }
void Reset() override
{
RectangleBoundary* _boundaryXY = new RectangleBoundary(1782.0f, 1832.0f, -56.0f, 8.0f);
ZRangeBoundary* _boundaryZ = new ZRangeBoundary(400.0f, 439.0f);
_boundaryIntersect = new BoundaryIntersectBoundary(_boundaryXY, _boundaryZ);
scheduler.Schedule(0s, [this](TaskContext context)
{
me->GetMap()->DoForAllPlayers([&](Player* player)
{
if (_boundaryIntersect->IsWithinBoundary(player->GetPosition()) && !player->IsGameMaster())
{
player->KillSelf(false);
}
});
context.Repeat(1s);
});
}
void UpdateAI(uint32 diff) override
{
scheduler.Update(diff);
}
private:
BoundaryIntersectBoundary const* _boundaryIntersect;
};
// predicate function to select non main tank target
class StoneGripTargetSelector
{
@@ -894,6 +925,7 @@ void AddSC_boss_kologarn()
new boss_kologarn();
new boss_kologarn_arms();
new boss_kologarn_eyebeam();
RegisterUlduarCreatureAI(boss_kologarn_pit_kill_bunny);
// Spells
new spell_ulduar_stone_grip_cast_target();

View File

@@ -86,7 +86,8 @@ enum SpellData
SPELL_HAND_PULSE_25_L = 64536,
SPELL_SELF_REPAIR = 64383,
SPELL_SLEEP = 64394,
SPELL_SLEEP_VISUAL_1 = 64393,
SPELL_SLEEP_VISUAL_2 = 64394,
};
enum NPCs
@@ -172,10 +173,11 @@ enum EVENTS
EVENT_JOIN_ACU = 35,
EVENT_START_PHASE4 = 36,
EVENT_FINISH = 50,
EVENT_SAY_VOLTRON_DEAD = 51,
EVENT_DISAPPEAR = 52,
EVENT_BERSERK = 53,
EVENT_BERSERK_2 = 54,
EVENT_STAND_UP_FRIENDLY = 51,
EVENT_SAY_VOLTRON_DEAD = 52,
EVENT_DISAPPEAR = 53,
EVENT_BERSERK = 54,
EVENT_BERSERK_2 = 55,
// Leviathan:
EVENT_SPELL_NAPALM_SHELL = 3,
@@ -754,17 +756,20 @@ public:
Position exitPos = me->GetPosition();
me->_ExitVehicle(&exitPos);
me->AttackStop();
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_TALK);
me->GetMotionMaster()->Clear();
summons.DoAction(1337); // despawn summons of summons
summons.DespawnEntry(NPC_FLAMES_INITIAL);
summons.DespawnEntry(33576);
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
float angle = VX001->GetOrientation();
float v_x = me->GetPositionX() + cos(angle) * 10.0f;
float v_y = me->GetPositionY() + std::sin(angle) * 10.0f;
me->GetMotionMaster()->MoveJump(v_x, v_y, 364.32f, 7.0f, 7.0f);
DoCastSelf(SPELL_SLEEP_VISUAL_1);
if( pInstance )
for( uint16 i = 0; i < 3; ++i )
if( ObjectGuid guid = pInstance->GetGuidData(DATA_GO_MIMIRON_DOOR_1 + i) )
@@ -783,11 +788,20 @@ public:
computer->AI()->Talk(TALK_COMPUTER_TERMINATED);
events.Reset();
events.ScheduleEvent(EVENT_SAY_VOLTRON_DEAD, 6s);
events.ScheduleEvent(EVENT_STAND_UP_FRIENDLY, 6s);
}
break;
case EVENT_STAND_UP_FRIENDLY:
me->RemoveAurasDueToSpell(SPELL_SLEEP_VISUAL_1);
DoCastSelf(SPELL_SLEEP_VISUAL_2);
me->SetFaction(FACTION_FRIENDLY);
events.ScheduleEvent(EVENT_SAY_VOLTRON_DEAD, 4s);
break;
case EVENT_SAY_VOLTRON_DEAD:
Talk(SAY_V07TRON_DEATH);
me->HandleEmoteCommand(EMOTE_ONESHOT_TALK);
if (pInstance)
pInstance->SetData(TYPE_MIMIRON, DONE);
// spawn chest
if (uint32 chestId = (hardmode ? RAID_MODE(GO_MIMIRON_CHEST_HARD, GO_MIMIRON_CHEST_HERO_HARD) : RAID_MODE(GO_MIMIRON_CHEST, GO_MIMIRON_CHEST_HERO)))
{
@@ -797,11 +811,9 @@ public:
go->SetLootRecipient(me->GetMap());
}
}
events.ScheduleEvent(EVENT_DISAPPEAR, 15s);
events.ScheduleEvent(EVENT_DISAPPEAR, 9s);
break;
case EVENT_DISAPPEAR:
if( pInstance )
pInstance->SetData(TYPE_MIMIRON, DONE);
DoCastSelf(SPELL_TELEPORT);
summons.DespawnAll();
break;

View File

@@ -100,6 +100,15 @@ public:
Position normalChestPosition = { 1967.152588f, -204.188461f, 432.686951f, 5.50957f };
Position hardChestPosition = { 2035.94600f, -202.084885f, 432.686859f, 3.164077f };
// Mimiron Tram
ObjectGuid m_mimironTramGUID;
ObjectGuid m_mimironActivateTramGUID;
ObjectGuid m_mimironTramRocketBoosterGUID;
ObjectGuid m_mimironTramTurnaround1GUID;
ObjectGuid m_mimironTramTurnaround2GUID;
ObjectGuid m_mimironCallTramCenterGUID;
ObjectGuid m_mimironCallTramMimironGUID;
// Mimiron
ObjectGuid m_MimironDoor[3];
ObjectGuid m_MimironLeviathanMKIIguid;
@@ -124,10 +133,12 @@ public:
ObjectGuid m_brannBronzebeardBaseCamp;
uint32 m_algalonTimer;
// Ancient Gate
const Position triggerAncientGatePosition = { 1883.65f, 269.272f, 418.406f };
// Shared
EventMap _events;
bool m_mimironTramUsed;
ObjectGuid m_mimironTramGUID;
ObjectGuid m_keepersgateGUID;
ObjectGuid m_keepersGossipGUID[4];
@@ -170,7 +181,18 @@ public:
// mimiron tram:
instance->LoadGrid(2307.0f, 284.632f);
if (GameObject* MimironTram = instance->GetGameObject(m_mimironTramGUID))
{
player->UpdateVisibilityOf(MimironTram);
if (StaticTransport* t = MimironTram->ToStaticTransport())
{
if (GameObject* go = instance->GetGameObject(m_mimironTramRocketBoosterGUID))
if (!go->GetTransport())
t->AddPassenger(go, true);
if (GameObject* go = instance->GetGameObject(m_mimironActivateTramGUID))
if (!go->GetTransport())
t->AddPassenger(go, true);
}
}
if (!m_uiAlgalonGUID && m_algalonTimer && (m_algalonTimer <= 60 || m_algalonTimer == TIMER_ALGALON_TO_SUMMON))
{
@@ -553,11 +575,30 @@ public:
case GO_SNOW_MOUND:
gameObject->EnableCollision(false);
break;
// Mimiron Tram
case GO_MIMIRON_TRAM:
if (GetData(TYPE_MIMIRON) == DONE)
m_mimironTramUsed = true;
m_mimironTramGUID = gameObject->GetGUID();
break;
case GO_MIMIRON_TRAM_ROCKET_BOOSTER:
m_mimironTramRocketBoosterGUID = gameObject->GetGUID();
break;
case GO_MIMIRON_ACTIVATE_TRAM:
m_mimironActivateTramGUID = gameObject->GetGUID();
break;
case GO_MIMIRON_CALL_TRAM_CENTER:
m_mimironCallTramCenterGUID = gameObject->GetGUID();
break;
case GO_MIMIRON_CALL_TRAM_MIMIRON:
m_mimironCallTramMimironGUID = gameObject->GetGUID();
break;
case GO_DOODAD_UL_TRAIN_TURNAROUND01:
m_mimironTramTurnaround1GUID = gameObject->GetGUID();
break;
case GO_DOODAD_UL_TRAIN_TURNAROUND02:
m_mimironTramTurnaround2GUID = gameObject->GetGUID();
break;
// Algalon the Observer
case GO_CELESTIAL_PLANETARIUM_ACCESS_10:
case GO_CELESTIAL_PLANETARIUM_ACCESS_25:
@@ -668,8 +709,17 @@ public:
m_auiEncounter[type] = data;
if (GetData(TYPE_MIMIRON) == DONE && GetData(TYPE_FREYA) == DONE && GetData(TYPE_HODIR) == DONE && GetData(TYPE_THORIM) == DONE)
{
if (GameObject* go = instance->GetGameObject(m_keepersgateGUID))
go->RemoveGameObjectFlag(GO_FLAG_LOCKED);
scheduler.Schedule(45s, [this](TaskContext /*context*/)
{
if (GameObject* go = instance->GetGameObject(m_keepersgateGUID))
{
go->RemoveGameObjectFlag(GO_FLAG_LOCKED);
if (Creature* trigger = instance->SummonCreature(NPC_ANCIENT_GATE_WORLD_TRIGGER, triggerAncientGatePosition, nullptr, 10*IN_MILLISECONDS))
{
trigger->AI()->Talk(EMOTE_ANCIENT_GATE_UNLOCKED);
}
}
});
}
if (type == TYPE_MIMIRON && data == IN_PROGRESS) // after reaching him without tram and starting the fight
m_mimironTramUsed = true;
@@ -785,9 +835,51 @@ public:
if (StaticTransport* t = MimironTram->ToStaticTransport())
{
if (data == 0 && t->GetGoState() == GO_STATE_ACTIVE && t->GetPathProgress() == t->GetPauseTime())
{
MimironTram->SetGoState(GO_STATE_READY);
if (GameObject* rocketBooster = instance->GetGameObject(m_mimironTramRocketBoosterGUID))
rocketBooster->SetGoState(GO_STATE_ACTIVE);
if (GameObject* activateTramButton = instance->GetGameObject(m_mimironActivateTramGUID))
activateTramButton->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
if (GameObject* callTramCenterButton = instance->GetGameObject(m_mimironCallTramCenterGUID))
callTramCenterButton->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
scheduler.Schedule(30s, [this](TaskContext /*context*/)
{
if (GameObject* turnaround1 = instance->GetGameObject(m_mimironTramTurnaround1GUID))
turnaround1->UseDoorOrButton();
if (GameObject* rocketBooster = instance->GetGameObject(m_mimironTramRocketBoosterGUID))
rocketBooster->SetGoState(GO_STATE_READY);
}).Schedule(60s, [this](TaskContext /*context*/)
{
if (GameObject* activateTramButton = instance->GetGameObject(m_mimironActivateTramGUID))
activateTramButton->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
if (GameObject* callTramMimironButton = instance->GetGameObject(m_mimironCallTramMimironGUID))
callTramMimironButton->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
});
}
if (data == 1 && t->GetGoState() == GO_STATE_READY && t->GetPathProgress() == 0)
{
MimironTram->SetGoState(GO_STATE_ACTIVE);
if (GameObject* rocketBooster = instance->GetGameObject(m_mimironTramRocketBoosterGUID))
rocketBooster->SetGoState(GO_STATE_ACTIVE);
if (GameObject* activateTramButton = instance->GetGameObject(m_mimironActivateTramGUID))
activateTramButton->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
if (GameObject* callTramMimironButton = instance->GetGameObject(m_mimironCallTramMimironGUID))
callTramMimironButton->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
scheduler.Schedule(33s, [this](TaskContext /*context*/)
{
if (GameObject* turnaround2 = instance->GetGameObject(m_mimironTramTurnaround2GUID))
turnaround2->UseDoorOrButton();
if (GameObject* rocketBooster = instance->GetGameObject(m_mimironTramRocketBoosterGUID))
rocketBooster->SetGoState(GO_STATE_READY);
}).Schedule(63s, [this](TaskContext /*context*/)
{
if (GameObject* activateTramButton = instance->GetGameObject(m_mimironActivateTramGUID))
activateTramButton->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
if (GameObject* callTramCenterButton = instance->GetGameObject(m_mimironCallTramCenterGUID))
callTramCenterButton->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
});
}
}
break;
case DATA_BRANN_MEMOTESAY:

View File

@@ -545,33 +545,6 @@ public:
}
};
class go_call_tram : public GameObjectScript
{
public:
go_call_tram() : GameObjectScript("go_call_tram") { }
bool OnGossipHello(Player* /*player*/, GameObject* go) override
{
InstanceScript* pInstance = go->GetInstanceScript();
if (!pInstance)
return false;
switch(go->GetEntry())
{
case 194914:
case 194438:
pInstance->SetData(DATA_CALL_TRAM, 0);
break;
case 194912:
case 194437:
pInstance->SetData(DATA_CALL_TRAM, 1);
break;
}
return true;
}
};
struct npc_salvaged_siege_engine : public VehicleAI
{
npc_salvaged_siege_engine(Creature* creature) : VehicleAI(creature) { }
@@ -609,7 +582,5 @@ void AddSC_ulduar()
new npc_ulduar_arachnopod_destroyer();
new spell_ulduar_arachnopod_damaged();
new AreaTrigger_at_celestial_planetarium_enterance();
new go_call_tram();
RegisterCreatureAI(npc_salvaged_siege_engine);
}

View File

@@ -215,7 +215,15 @@ enum UlduarGameObjects
GO_KOLOGARN_DOORS = 194553,
GO_KEEPERS_GATE = 194255,
GO_XT002_DOORS = 194631,
// Tram
GO_MIMIRON_TRAM = 194675,
GO_MIMIRON_ACTIVATE_TRAM = 194437,
GO_MIMIRON_CALL_TRAM_CENTER = 194914,
GO_MIMIRON_CALL_TRAM_MIMIRON = 194912,
GO_MIMIRON_TRAM_ROCKET_BOOSTER = 194904,
GO_DOODAD_UL_TRAIN_TURNAROUND01 = 194915, // center
GO_DOODAD_UL_TRAIN_TURNAROUND02 = 194913, // mimiron
// Mimiron, Hodir, Vezax
GO_MIMIRON_ELEVATOR = 194749,
@@ -290,6 +298,10 @@ enum UlduarMisc
// Freya, Hodir, Mimiron, Thorim
EVENT_KEEPER_TELEPORTED = 62941,
// Ancient Gate
NPC_ANCIENT_GATE_WORLD_TRIGGER = 22515,
EMOTE_ANCIENT_GATE_UNLOCKED = 19,
// Yogg-Saron
ACTION_SARA_UPDATE_SUMMON_KEEPERS = 4,
KEEPER_FREYA = 0,