Merge branch 'azerothcore:master' into Playerbot
This commit is contained in:
@@ -134,14 +134,6 @@ void CreatureAI::DoZoneInCombat(Creature* creature /*= nullptr*/, float maxRange
|
||||
{
|
||||
creature->AddThreat(player, 0.0f);
|
||||
}
|
||||
|
||||
/* Causes certain things to never leave the threat list (Priest Lightwell, etc):
|
||||
for (Unit::ControlSet::const_iterator itr = player->m_Controlled.begin(); itr != player->m_Controlled.end(); ++itr)
|
||||
{
|
||||
creature->SetInCombatWith(*itr);
|
||||
(*itr)->SetInCombatWith(creature);
|
||||
creature->AddThreat(*itr, 0.0f);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,7 +153,7 @@ void CreatureAI::MoveInLineOfSight_Safe(Unit* who)
|
||||
|
||||
void CreatureAI::MoveInLineOfSight(Unit* who)
|
||||
{
|
||||
if (me->GetVictim())
|
||||
if (me->IsEngaged())
|
||||
return;
|
||||
|
||||
// pussywizard: civilian, non-combat pet or any other NOT HOSTILE TO ANYONE (!)
|
||||
@@ -182,7 +174,7 @@ void CreatureAI::TriggerAlert(Unit const* who) const
|
||||
if (!who || who->GetTypeId() != TYPEID_PLAYER)
|
||||
return;
|
||||
// If this unit isn't an NPC, is already distracted, is in combat, is confused, stunned or fleeing, do nothing
|
||||
if (me->GetTypeId() != TYPEID_UNIT || me->IsInCombat() || me->HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_STUNNED | UNIT_STATE_FLEEING | UNIT_STATE_DISTRACTED))
|
||||
if (me->GetTypeId() != TYPEID_UNIT || me->IsEngaged() || me->HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_STUNNED | UNIT_STATE_FLEEING | UNIT_STATE_DISTRACTED))
|
||||
return;
|
||||
// Only alert for hostiles!
|
||||
if (me->IsCivilian() || me->HasReactState(REACT_PASSIVE) || !me->IsHostileTo(who) || !me->_IsTargetAcceptable(who))
|
||||
@@ -253,7 +245,7 @@ void CreatureAI::SetGazeOn(Unit* target)
|
||||
|
||||
bool CreatureAI::UpdateVictimWithGaze()
|
||||
{
|
||||
if (!me->IsInCombat())
|
||||
if (!me->IsEngaged())
|
||||
return false;
|
||||
|
||||
if (me->HasReactState(REACT_PASSIVE))
|
||||
@@ -271,7 +263,7 @@ bool CreatureAI::UpdateVictimWithGaze()
|
||||
|
||||
bool CreatureAI::UpdateVictim()
|
||||
{
|
||||
if (!me->IsInCombat())
|
||||
if (!me->IsEngaged())
|
||||
return false;
|
||||
|
||||
if (!me->HasReactState(REACT_PASSIVE))
|
||||
|
||||
@@ -114,7 +114,7 @@ public:
|
||||
// Called for reaction at stopping attack at no attackers or targets
|
||||
virtual void EnterEvadeMode(EvadeReason why = EVADE_REASON_OTHER);
|
||||
|
||||
// Called for reaction at enter to combat if not in combat yet (enemy can be nullptr)
|
||||
// Called for reaction when initially engaged
|
||||
virtual void EnterCombat(Unit* /*victim*/) {}
|
||||
|
||||
// Called when the creature is killed
|
||||
@@ -196,6 +196,7 @@ public:
|
||||
virtual bool CanSeeAlways(WorldObject const* /*obj*/) { return false; }
|
||||
|
||||
virtual bool CanBeSeen(Player const* /*seer*/) { return true; }
|
||||
virtual bool CanAlwaysBeDetectable(WorldObject const* /*seer*/) { return false; }
|
||||
|
||||
virtual void PetStopAttack() { }
|
||||
|
||||
|
||||
@@ -574,7 +574,7 @@ void BossAI::TeleportCheaters()
|
||||
void BossAI::JustSummoned(Creature* summon)
|
||||
{
|
||||
summons.Summon(summon);
|
||||
if (me->IsInCombat())
|
||||
if (me->IsEngaged())
|
||||
DoZoneInCombat(summon);
|
||||
}
|
||||
|
||||
|
||||
@@ -4044,18 +4044,18 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
|
||||
break;
|
||||
case SMART_EVENT_UPDATE_OOC:
|
||||
if (me && me->IsInCombat())
|
||||
if (me && me->IsEngaged())
|
||||
return;
|
||||
ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
|
||||
break;
|
||||
case SMART_EVENT_UPDATE_IC:
|
||||
if (!me || !me->IsInCombat())
|
||||
if (!me || !me->IsEngaged())
|
||||
return;
|
||||
ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
|
||||
break;
|
||||
case SMART_EVENT_HEALTH_PCT:
|
||||
{
|
||||
if (!me || !me->IsInCombat() || !me->GetMaxHealth())
|
||||
if (!me || !me->IsEngaged() || !me->GetMaxHealth())
|
||||
return;
|
||||
uint32 perc = (uint32)me->GetHealthPct();
|
||||
if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min)
|
||||
@@ -4065,7 +4065,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
}
|
||||
case SMART_EVENT_TARGET_HEALTH_PCT:
|
||||
{
|
||||
if (!me || !me->IsInCombat() || !me->GetVictim() || !me->GetVictim()->GetMaxHealth())
|
||||
if (!me || !me->IsEngaged() || !me->GetVictim() || !me->GetVictim()->GetMaxHealth())
|
||||
return;
|
||||
uint32 perc = (uint32)me->GetVictim()->GetHealthPct();
|
||||
if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min)
|
||||
@@ -4075,7 +4075,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
}
|
||||
case SMART_EVENT_MANA_PCT:
|
||||
{
|
||||
if (!me || !me->IsInCombat() || !me->GetMaxPower(POWER_MANA))
|
||||
if (!me || !me->IsEngaged() || !me->GetMaxPower(POWER_MANA))
|
||||
return;
|
||||
uint32 perc = uint32(me->GetPowerPct(POWER_MANA));
|
||||
if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min)
|
||||
@@ -4085,7 +4085,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
}
|
||||
case SMART_EVENT_TARGET_MANA_PCT:
|
||||
{
|
||||
if (!me || !me->IsInCombat() || !me->GetVictim() || !me->GetVictim()->GetMaxPower(POWER_MANA))
|
||||
if (!me || !me->IsEngaged() || !me->GetVictim() || !me->GetVictim()->GetMaxPower(POWER_MANA))
|
||||
return;
|
||||
uint32 perc = uint32(me->GetVictim()->GetPowerPct(POWER_MANA));
|
||||
if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min)
|
||||
@@ -4095,7 +4095,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
}
|
||||
case SMART_EVENT_RANGE:
|
||||
{
|
||||
if (!me || !me->IsInCombat() || !me->GetVictim())
|
||||
if (!me || !me->IsEngaged() || !me->GetVictim())
|
||||
return;
|
||||
|
||||
if (me->IsInRange(me->GetVictim(), (float)e.event.minMaxRepeat.min, (float)e.event.minMaxRepeat.max))
|
||||
@@ -4106,7 +4106,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
}
|
||||
case SMART_EVENT_VICTIM_CASTING:
|
||||
{
|
||||
if (!me || !me->IsInCombat())
|
||||
if (!me || !me->IsEngaged())
|
||||
return;
|
||||
|
||||
Unit* victim = me->GetVictim();
|
||||
@@ -4124,7 +4124,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
}
|
||||
case SMART_EVENT_FRIENDLY_HEALTH:
|
||||
{
|
||||
if (!me || !me->IsInCombat())
|
||||
if (!me || !me->IsEngaged())
|
||||
return;
|
||||
|
||||
Unit* target = DoSelectLowestHpFriendly((float)e.event.friendlyHealth.radius, e.event.friendlyHealth.hpDeficit);
|
||||
@@ -4139,7 +4139,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
}
|
||||
case SMART_EVENT_FRIENDLY_IS_CC:
|
||||
{
|
||||
if (!me || !me->IsInCombat())
|
||||
if (!me || !me->IsEngaged())
|
||||
return;
|
||||
|
||||
std::list<Creature*> pList;
|
||||
@@ -4285,7 +4285,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
}
|
||||
case SMART_EVENT_OOC_LOS:
|
||||
{
|
||||
if (!me || me->IsInCombat())
|
||||
if (!me || me->IsEngaged())
|
||||
return;
|
||||
//can trigger if closer than fMaxAllowedRange
|
||||
float range = (float)e.event.los.maxDist;
|
||||
@@ -4309,7 +4309,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
}
|
||||
case SMART_EVENT_IC_LOS:
|
||||
{
|
||||
if (!me || !me->IsInCombat())
|
||||
if (!me || !me->IsEngaged())
|
||||
return;
|
||||
//can trigger if closer than fMaxAllowedRange
|
||||
float range = (float)e.event.los.maxDist;
|
||||
@@ -4507,7 +4507,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
}
|
||||
case SMART_EVENT_FRIENDLY_HEALTH_PCT:
|
||||
{
|
||||
if (!me || !me->IsInCombat())
|
||||
if (!me || !me->IsEngaged())
|
||||
return;
|
||||
|
||||
Unit* target = nullptr;
|
||||
@@ -4701,10 +4701,10 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff)
|
||||
if (e.event.event_phase_mask && !IsInPhase(e.event.event_phase_mask))
|
||||
return;
|
||||
|
||||
if (e.GetEventType() == SMART_EVENT_UPDATE_IC && (!me || !me->IsInCombat()))
|
||||
if (e.GetEventType() == SMART_EVENT_UPDATE_IC && (!me || !me->IsEngaged()))
|
||||
return;
|
||||
|
||||
if (e.GetEventType() == SMART_EVENT_UPDATE_OOC && (me && me->IsInCombat()))//can be used with me=nullptr (go script)
|
||||
if (e.GetEventType() == SMART_EVENT_UPDATE_OOC && (me && me->IsEngaged()))//can be used with me=nullptr (go script)
|
||||
return;
|
||||
|
||||
if (e.timer < diff)
|
||||
@@ -4982,7 +4982,7 @@ void SmartScript::OnMoveInLineOfSight(Unit* who)
|
||||
if (!me)
|
||||
return;
|
||||
|
||||
ProcessEventsFor(me->IsInCombat() ? SMART_EVENT_IC_LOS : SMART_EVENT_OOC_LOS, who);
|
||||
ProcessEventsFor(me->IsEngaged() ? SMART_EVENT_IC_LOS : SMART_EVENT_OOC_LOS, who);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -86,6 +86,8 @@ static FactionTeamMap sFactionTeamMap;
|
||||
DBCStorage <FactionEntry> sFactionStore(FactionEntryfmt);
|
||||
DBCStorage <FactionTemplateEntry> sFactionTemplateStore(FactionTemplateEntryfmt);
|
||||
|
||||
DBCStorage <GameObjectArtKitEntry> sGameObjectArtKitStore(GameObjectArtKitfmt);
|
||||
|
||||
DBCStorage <GameObjectDisplayInfoEntry> sGameObjectDisplayInfoStore(GameObjectDisplayInfofmt);
|
||||
DBCStorage <GemPropertiesEntry> sGemPropertiesStore(GemPropertiesEntryfmt);
|
||||
DBCStorage <GlyphPropertiesEntry> sGlyphPropertiesStore(GlyphPropertiesfmt);
|
||||
@@ -306,6 +308,7 @@ void LoadDBCStores(const std::string& dataPath)
|
||||
LOAD_DBC(sEmotesTextSoundStore, "EmotesTextSound.dbc", "emotetextsound_dbc");
|
||||
LOAD_DBC(sFactionStore, "Faction.dbc", "faction_dbc");
|
||||
LOAD_DBC(sFactionTemplateStore, "FactionTemplate.dbc", "factiontemplate_dbc");
|
||||
LOAD_DBC(sGameObjectArtKitStore, "GameObjectArtKit.dbc", "gameobjectartkit_dbc");
|
||||
LOAD_DBC(sGameObjectDisplayInfoStore, "GameObjectDisplayInfo.dbc", "gameobjectdisplayinfo_dbc");
|
||||
LOAD_DBC(sGemPropertiesStore, "GemProperties.dbc", "gemproperties_dbc");
|
||||
LOAD_DBC(sGlyphPropertiesStore, "GlyphProperties.dbc", "glyphproperties_dbc");
|
||||
|
||||
@@ -110,6 +110,7 @@ extern DBCStorage <EmotesTextEntry> sEmotesTextStore;
|
||||
extern DBCStorage <EmotesTextSoundEntry> sEmotesTextSoundStore;
|
||||
extern DBCStorage <FactionEntry> sFactionStore;
|
||||
extern DBCStorage <FactionTemplateEntry> sFactionTemplateStore;
|
||||
extern DBCStorage <GameObjectArtKitEntry> sGameObjectArtKitStore;
|
||||
extern DBCStorage <GameObjectDisplayInfoEntry> sGameObjectDisplayInfoStore;
|
||||
extern DBCStorage <GemPropertiesEntry> sGemPropertiesStore;
|
||||
extern DBCStorage <GlyphPropertiesEntry> sGlyphPropertiesStore;
|
||||
|
||||
@@ -704,7 +704,7 @@ void Creature::Update(uint32 diff)
|
||||
}
|
||||
|
||||
// periodic check to see if the creature has passed an evade boundary
|
||||
if (IsAIEnabled && !IsInEvadeMode() && IsInCombat())
|
||||
if (IsAIEnabled && !IsInEvadeMode() && IsEngaged())
|
||||
{
|
||||
if (diff >= m_boundaryCheckTime)
|
||||
{
|
||||
@@ -1810,6 +1810,21 @@ bool Creature::CanAlwaysSee(WorldObject const* obj) const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Creature::IsAlwaysDetectableFor(WorldObject const* seer) const
|
||||
{
|
||||
if (Unit::IsAlwaysDetectableFor(seer))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IsAIEnabled && AI()->CanAlwaysBeDetectable(seer))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Creature::CanStartAttack(Unit const* who) const
|
||||
{
|
||||
if (IsCivilian())
|
||||
@@ -1839,7 +1854,7 @@ bool Creature::CanStartAttack(Unit const* who) const
|
||||
// pussywizard: at this point we are either hostile to who or friendly to who->getAttackerForHelper()
|
||||
// pussywizard: if who is in combat and has an attacker, help him if the distance is right (help because who is hostile or help because attacker is friendly)
|
||||
bool assist = false;
|
||||
if (who->IsInCombat() && IsWithinDist(who, ATTACK_DISTANCE))
|
||||
if (who->IsEngaged() && IsWithinDist(who, ATTACK_DISTANCE))
|
||||
if (Unit* victim = who->getAttackerForHelper())
|
||||
if (IsWithinDistInMap(victim, sWorld->getFloatConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS)))
|
||||
assist = true;
|
||||
@@ -2358,7 +2373,7 @@ bool Creature::CanAssistTo(Unit const* u, Unit const* enemy, bool checkfaction /
|
||||
return false;
|
||||
|
||||
// skip fighting creature
|
||||
if (IsInCombat())
|
||||
if (IsEngaged())
|
||||
return false;
|
||||
|
||||
// only free creature
|
||||
@@ -2409,11 +2424,10 @@ bool Creature::_IsTargetAcceptable(Unit const* target) const
|
||||
return false;
|
||||
}
|
||||
|
||||
Unit const* myVictim = getAttackerForHelper();
|
||||
Unit const* targetVictim = target->getAttackerForHelper();
|
||||
|
||||
// if I'm already fighting target, or I'm hostile towards the target, the target is acceptable
|
||||
if (myVictim == target || targetVictim == this || IsHostileTo(target))
|
||||
if (IsEngagedBy(target) || IsHostileTo(target))
|
||||
return true;
|
||||
|
||||
// if the target's victim is friendly, and the target is neutral, the target is acceptable
|
||||
|
||||
@@ -443,6 +443,7 @@ protected:
|
||||
|
||||
[[nodiscard]] bool IsInvisibleDueToDespawn() const override;
|
||||
bool CanAlwaysSee(WorldObject const* obj) const override;
|
||||
bool IsAlwaysDetectableFor(WorldObject const* seer) const override;
|
||||
|
||||
private:
|
||||
void ForcedDespawn(uint32 timeMSToDespawn = 0, Seconds forcedRespawnTimer = 0s);
|
||||
|
||||
@@ -190,7 +190,7 @@ void CreatureGroup::RemoveMember(Creature* member)
|
||||
member->SetFormation(nullptr);
|
||||
}
|
||||
|
||||
void CreatureGroup::MemberAttackStart(Creature* member, Unit* target)
|
||||
void CreatureGroup::MemberEngagingTarget(Creature* member, Unit* target)
|
||||
{
|
||||
uint8 const groupAI = sFormationMgr->CreatureGroupMap[member->GetSpawnId()].groupAI;
|
||||
if (member == m_leader)
|
||||
|
||||
@@ -106,7 +106,7 @@ public:
|
||||
void FormationReset(bool dismiss, bool initMotionMaster);
|
||||
|
||||
void LeaderMoveTo(float x, float y, float z, bool run);
|
||||
void MemberAttackStart(Creature* member, Unit* target);
|
||||
void MemberEngagingTarget(Creature* member, Unit* target);
|
||||
void MemberEvaded(Creature* member);
|
||||
|
||||
private:
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "Object.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "Unit.h"
|
||||
#include <array>
|
||||
|
||||
class GameObjectAI;
|
||||
class Transport;
|
||||
@@ -676,6 +677,7 @@ struct GameObjectTemplateAddon
|
||||
uint32 flags;
|
||||
uint32 mingold;
|
||||
uint32 maxgold;
|
||||
std::array<uint32, 4> artKits = {};
|
||||
};
|
||||
|
||||
// Benchmarked: Faster than std::map (insert/find)
|
||||
@@ -736,6 +738,35 @@ enum GOState
|
||||
|
||||
#define MAX_GO_STATE 3
|
||||
|
||||
enum class GameObjectActions : uint32
|
||||
{
|
||||
// Name from client executable // Comments
|
||||
None, // -NONE-
|
||||
AnimateCustom0, // Animate Custom0
|
||||
AnimateCustom1, // Animate Custom1
|
||||
AnimateCustom2, // Animate Custom2
|
||||
AnimateCustom3, // Animate Custom3
|
||||
Disturb, // Disturb // Triggers trap
|
||||
Unlock, // Unlock // Resets GO_FLAG_LOCKED
|
||||
Lock, // Lock // Sets GO_FLAG_LOCKED
|
||||
Open, // Open // Sets GO_STATE_ACTIVE
|
||||
OpenAndUnlock, // Open + Unlock // Sets GO_STATE_ACTIVE and resets GO_FLAG_LOCKED
|
||||
Close, // Close // Sets GO_STATE_READY
|
||||
ToggleOpen, // Toggle Open
|
||||
Destroy, // Destroy // Sets GO_STATE_DESTROYED
|
||||
Rebuild, // Rebuild // Resets from GO_STATE_DESTROYED
|
||||
Creation, // Creation
|
||||
Despawn, // Despawn
|
||||
MakeInert, // Make Inert // Disables interactions
|
||||
MakeActive, // Make Active // Enables interactions
|
||||
CloseAndLock, // Close + Lock // Sets GO_STATE_READY and sets GO_FLAG_LOCKED
|
||||
UseArtKit0, // Use ArtKit0 // 46904: 121
|
||||
UseArtKit1, // Use ArtKit1 // 36639: 81, 46903: 122
|
||||
UseArtKit2, // Use ArtKit2
|
||||
UseArtKit3, // Use ArtKit3
|
||||
SetTapList, // Set Tap List
|
||||
};
|
||||
|
||||
// from `gameobject`
|
||||
struct GameObjectData
|
||||
{
|
||||
|
||||
@@ -85,6 +85,8 @@ void Totem::InitStats(uint32 duration)
|
||||
|
||||
void Totem::InitSummon()
|
||||
{
|
||||
Minion::InitSummon();
|
||||
|
||||
if (m_type == TOTEM_PASSIVE && GetSpell())
|
||||
CastSpell(this, GetSpell(), true);
|
||||
|
||||
|
||||
@@ -316,6 +316,8 @@ Unit::Unit(bool isWorldObject) : WorldObject(isWorldObject),
|
||||
|
||||
_oldFactionId = 0;
|
||||
|
||||
_isWalkingBeforeCharm = false;
|
||||
|
||||
_lastExtraAttackSpell = 0;
|
||||
}
|
||||
|
||||
@@ -10482,8 +10484,12 @@ void Unit::SetCharm(Unit* charm, bool apply)
|
||||
if (!charm->AddGuidValue(UNIT_FIELD_CHARMEDBY, GetGUID()))
|
||||
LOG_FATAL("entities.unit", "Unit {} is being charmed, but it already has a charmer {}", charm->GetEntry(), charm->GetCharmerGUID().ToString());
|
||||
|
||||
if (charm->HasUnitMovementFlag(MOVEMENTFLAG_WALKING))
|
||||
_isWalkingBeforeCharm = charm->IsWalking();
|
||||
if (_isWalkingBeforeCharm)
|
||||
{
|
||||
charm->SetWalk(false);
|
||||
charm->SendMovementFlagUpdate();
|
||||
}
|
||||
|
||||
m_Controlled.insert(charm);
|
||||
}
|
||||
@@ -10521,6 +10527,12 @@ void Unit::SetCharm(Unit* charm, bool apply)
|
||||
charm->SetByteValue(UNIT_FIELD_BYTES_2, 1, 0);
|
||||
}
|
||||
|
||||
if (charm->IsWalking() != _isWalkingBeforeCharm)
|
||||
{
|
||||
charm->SetWalk(_isWalkingBeforeCharm);
|
||||
charm->SendMovementFlagUpdate(true); // send packet to self, to update movement state on player.
|
||||
}
|
||||
|
||||
m_Controlled.erase(charm);
|
||||
}
|
||||
}
|
||||
@@ -13168,7 +13180,7 @@ void Unit::SetInCombatState(bool PvP, Unit* enemy, uint32 duration)
|
||||
creature->AI()->EnterCombat(enemy);
|
||||
|
||||
if (creature->GetFormation())
|
||||
creature->GetFormation()->MemberAttackStart(creature, enemy);
|
||||
creature->GetFormation()->MemberEngagingTarget(creature, enemy);
|
||||
}
|
||||
|
||||
creature->RefreshSwimmingFlag();
|
||||
|
||||
@@ -1326,6 +1326,7 @@ public:
|
||||
bool IsWithinCombatRange(Unit const* obj, float dist2compare) const;
|
||||
bool IsWithinMeleeRange(Unit const* obj, float dist = 0.f) const;
|
||||
float GetMeleeRange(Unit const* target) const;
|
||||
[[nodiscard]] virtual SpellSchoolMask GetMeleeDamageSchoolMask() const;
|
||||
bool GetRandomContactPoint(Unit const* target, float& x, float& y, float& z, bool force = false) const;
|
||||
uint32 m_extraAttacks;
|
||||
bool m_canDualWield;
|
||||
@@ -1343,6 +1344,9 @@ public:
|
||||
if (GetVictim() != nullptr)
|
||||
return GetVictim();
|
||||
|
||||
if (!IsEngaged())
|
||||
return nullptr;
|
||||
|
||||
if (!m_attackers.empty())
|
||||
return *(m_attackers.begin());
|
||||
|
||||
@@ -1644,6 +1648,9 @@ public:
|
||||
|
||||
[[nodiscard]] bool IsInFlight() const { return HasUnitState(UNIT_STATE_IN_FLIGHT); }
|
||||
|
||||
bool IsEngaged() const { return IsInCombat(); }
|
||||
bool IsEngagedBy(Unit const* who) const { return IsInCombatWith(who); }
|
||||
|
||||
[[nodiscard]] bool IsInCombat() const { return HasUnitFlag(UNIT_FLAG_IN_COMBAT); }
|
||||
bool IsInCombatWith(Unit const* who) const;
|
||||
|
||||
@@ -2105,6 +2112,7 @@ public:
|
||||
void TauntApply(Unit* victim);
|
||||
void TauntFadeOut(Unit* taunter);
|
||||
ThreatMgr& GetThreatMgr() { return m_ThreatMgr; }
|
||||
ThreatMgr const& GetThreatMgr() const { return m_ThreatMgr; }
|
||||
void addHatedBy(HostileReference* pHostileReference) { m_HostileRefMgr.insertFirst(pHostileReference); };
|
||||
void removeHatedBy(HostileReference* /*pHostileReference*/) { /* nothing to do yet */ }
|
||||
HostileRefMgr& getHostileRefMgr() { return m_HostileRefMgr; }
|
||||
@@ -2478,8 +2486,6 @@ protected:
|
||||
CharmInfo* m_charmInfo;
|
||||
SharedVisionList m_sharedVision;
|
||||
|
||||
[[nodiscard]] virtual SpellSchoolMask GetMeleeDamageSchoolMask() const;
|
||||
|
||||
MotionMaster* i_motionMaster;
|
||||
|
||||
uint32 m_reactiveTimer[MAX_REACTIVE];
|
||||
@@ -2549,6 +2555,7 @@ private:
|
||||
bool m_duringRemoveFromWorld; // lock made to not add stuff after begining removing from world
|
||||
|
||||
uint32 _oldFactionId; ///< faction before charm
|
||||
bool _isWalkingBeforeCharm; ///< Are we walking before we were charmed?
|
||||
|
||||
[[nodiscard]] float processDummyAuras(float TakenTotalMod) const;
|
||||
|
||||
|
||||
@@ -7183,8 +7183,8 @@ void ObjectMgr::LoadGameObjectTemplateAddons()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
// 0 1 2 3 4
|
||||
QueryResult result = WorldDatabase.Query("SELECT entry, faction, flags, mingold, maxgold FROM gameobject_template_addon");
|
||||
// 0 1 2 3 4 5 6 7 8
|
||||
QueryResult result = WorldDatabase.Query("SELECT entry, faction, flags, mingold, maxgold, artkit0, artkit1, artkit2, artkit3 FROM gameobject_template_addon");
|
||||
|
||||
if (!result)
|
||||
{
|
||||
@@ -7215,6 +7215,21 @@ void ObjectMgr::LoadGameObjectTemplateAddons()
|
||||
gameObjectAddon.mingold = fields[3].Get<uint32>();
|
||||
gameObjectAddon.maxgold = fields[4].Get<uint32>();
|
||||
|
||||
for (uint32 i = 0; i < gameObjectAddon.artKits.size(); i++)
|
||||
{
|
||||
uint32 artKitID = fields[5 + i].Get<uint32>();
|
||||
if (!artKitID)
|
||||
continue;
|
||||
|
||||
if (!sGameObjectArtKitStore.LookupEntry(artKitID))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "GameObject (Entry: {}) has invalid `artkit{}` {} defined, set to zero instead.", entry, i, artKitID);
|
||||
continue;
|
||||
}
|
||||
|
||||
gameObjectAddon.artKits[i] = artKitID;
|
||||
}
|
||||
|
||||
// checks
|
||||
if (gameObjectAddon.faction && !sFactionTemplateStore.LookupEntry(gameObjectAddon.faction))
|
||||
LOG_ERROR("sql.sql",
|
||||
|
||||
@@ -2677,7 +2677,7 @@ void Spell::EffectDistract(SpellEffIndex /*effIndex*/)
|
||||
return;
|
||||
|
||||
// Check for possible target
|
||||
if (!unitTarget || unitTarget->IsInCombat())
|
||||
if (!unitTarget || unitTarget->IsEngaged())
|
||||
return;
|
||||
|
||||
// target must be OK to do this
|
||||
@@ -4263,7 +4263,7 @@ void Spell::EffectSummonPlayer(SpellEffIndex /*effIndex*/)
|
||||
player->GetSession()->SendPacket(&data);
|
||||
}
|
||||
|
||||
void Spell::EffectActivateObject(SpellEffIndex /*effIndex*/)
|
||||
void Spell::EffectActivateObject(SpellEffIndex effIndex)
|
||||
{
|
||||
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
|
||||
return;
|
||||
@@ -4271,17 +4271,74 @@ void Spell::EffectActivateObject(SpellEffIndex /*effIndex*/)
|
||||
if (!gameObjTarget)
|
||||
return;
|
||||
|
||||
Player* player = m_caster->GetTypeId() == TYPEID_PLAYER ? m_caster->ToPlayer() : m_caster->GetCharmerOrOwnerPlayerOrPlayerItself();
|
||||
gameObjTarget->Use(player ? player : m_caster);
|
||||
GameObjectActions action = GameObjectActions(m_spellInfo->Effects[effIndex].MiscValue);
|
||||
switch (action)
|
||||
{
|
||||
case GameObjectActions::AnimateCustom0:
|
||||
case GameObjectActions::AnimateCustom1:
|
||||
case GameObjectActions::AnimateCustom2:
|
||||
case GameObjectActions::AnimateCustom3:
|
||||
gameObjTarget->SendCustomAnim(uint32(action) - uint32(GameObjectActions::AnimateCustom0));
|
||||
break;
|
||||
case GameObjectActions::Disturb: // What's the difference with Open?
|
||||
case GameObjectActions::Open:
|
||||
if (Unit* unitCaster = m_caster->ToUnit())
|
||||
gameObjTarget->Use(unitCaster);
|
||||
break;
|
||||
case GameObjectActions::OpenAndUnlock:
|
||||
if (Unit* unitCaster = m_caster->ToUnit())
|
||||
gameObjTarget->UseDoorOrButton(0, false, unitCaster);
|
||||
[[fallthrough]];
|
||||
case GameObjectActions::Unlock:
|
||||
case GameObjectActions::Lock:
|
||||
gameObjTarget->ApplyModFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED, action == GameObjectActions::Lock);
|
||||
break;
|
||||
case GameObjectActions::Close:
|
||||
case GameObjectActions::Rebuild:
|
||||
gameObjTarget->ResetDoorOrButton();
|
||||
break;
|
||||
case GameObjectActions::Despawn:
|
||||
gameObjTarget->DespawnOrUnsummon();
|
||||
break;
|
||||
case GameObjectActions::MakeInert:
|
||||
case GameObjectActions::MakeActive:
|
||||
gameObjTarget->ApplyModFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE, action == GameObjectActions::MakeInert);
|
||||
break;
|
||||
case GameObjectActions::CloseAndLock:
|
||||
gameObjTarget->ResetDoorOrButton();
|
||||
gameObjTarget->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED);
|
||||
break;
|
||||
case GameObjectActions::Destroy:
|
||||
if (Unit* unitCaster = m_caster->ToUnit())
|
||||
gameObjTarget->UseDoorOrButton(0, true, unitCaster);
|
||||
break;
|
||||
case GameObjectActions::UseArtKit0:
|
||||
case GameObjectActions::UseArtKit1:
|
||||
case GameObjectActions::UseArtKit2:
|
||||
case GameObjectActions::UseArtKit3:
|
||||
{
|
||||
GameObjectTemplateAddon const* templateAddon = gameObjTarget->GetTemplateAddon();
|
||||
|
||||
//ScriptInfo activateCommand;
|
||||
//activateCommand.command = SCRIPT_COMMAND_ACTIVATE_OBJECT;
|
||||
uint32 artKitIndex = uint32(action) - uint32(GameObjectActions::UseArtKit0);
|
||||
|
||||
// int32 unk = m_spellInfo->Effects[effIndex].MiscValue; // This is set for EffectActivateObject spells; needs research
|
||||
uint32 artKitValue = 0;
|
||||
if (templateAddon)
|
||||
artKitValue = templateAddon->artKits[artKitIndex];
|
||||
|
||||
// xinef: pass player to allow gossip scripts to work
|
||||
//
|
||||
//gameObjTarget->GetMap()->ScriptCommandStart(activateCommand, 0, player ? player : m_caster, gameObjTarget);
|
||||
if (artKitValue == 0)
|
||||
LOG_ERROR("sql.sql", "GameObject {} hit by spell {} needs `artkit{}` in `gameobject_template_addon`", gameObjTarget->GetEntry(), m_spellInfo->Id, artKitIndex);
|
||||
else
|
||||
gameObjTarget->SetGoArtKit(artKitValue);
|
||||
|
||||
break;
|
||||
}
|
||||
case GameObjectActions::None:
|
||||
LOG_FATAL("spell", "Spell {} has action type NONE in effect {}", m_spellInfo->Id, int32(effIndex));
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("spell", "Spell {} has unhandled action {} in effect {}", m_spellInfo->Id, int32(action), int32(effIndex));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Spell::EffectApplyGlyph(SpellEffIndex effIndex)
|
||||
|
||||
@@ -339,7 +339,7 @@ struct boss_priestess_lackey_commonAI : public ScriptedAI
|
||||
void EnterCombat(Unit* who) override
|
||||
{
|
||||
if (Creature* delrissa = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_DELRISSA)))
|
||||
if (delrissa->IsAlive() && !delrissa->IsInCombat())
|
||||
if (delrissa->IsAlive() && !delrissa->IsEngaged())
|
||||
delrissa->AI()->AttackStart(who);
|
||||
|
||||
events.ScheduleEvent(EVENT_SPELL_HELPER_HEALING_POTION, 1000);
|
||||
|
||||
@@ -783,37 +783,9 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class spell_banging_the_gong : public SpellScriptLoader
|
||||
{
|
||||
public:
|
||||
spell_banging_the_gong() : SpellScriptLoader("spell_banging_the_gong") { }
|
||||
|
||||
class spell_banging_the_gong_SpellScript : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_banging_the_gong_SpellScript);
|
||||
|
||||
void Activate(SpellEffIndex index)
|
||||
{
|
||||
PreventHitDefaultEffect(index);
|
||||
GetHitGObj()->SendCustomAnim(0);
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectHitTarget += SpellEffectFn(spell_banging_the_gong_SpellScript::Activate, EFFECT_1, SPELL_EFFECT_ACTIVATE_OBJECT);
|
||||
}
|
||||
};
|
||||
|
||||
SpellScript* GetSpellScript() const override
|
||||
{
|
||||
return new spell_banging_the_gong_SpellScript();
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_zulaman()
|
||||
{
|
||||
new npc_forest_frog();
|
||||
new npc_zulaman_hostage();
|
||||
new npc_harrison_jones();
|
||||
new spell_banging_the_gong();
|
||||
}
|
||||
|
||||
@@ -32,7 +32,8 @@ enum Spells
|
||||
SPELL_FROSTBREATH = 16099,
|
||||
SPELL_MASSIVEGEYSER = 22421,
|
||||
SPELL_SLAM = 24326,
|
||||
SPELL_THRASH = 3417 // Triggers 3391
|
||||
SPELL_THRASH = 3417, // Triggers 3391
|
||||
SPELL_SPLASH = 24593
|
||||
};
|
||||
|
||||
enum Events
|
||||
@@ -42,6 +43,11 @@ enum Events
|
||||
EVENT_SLAM = 3
|
||||
};
|
||||
|
||||
enum Misc
|
||||
{
|
||||
GAMEOBJECT_MUDSKUNK_LURE = 180346
|
||||
};
|
||||
|
||||
class boss_gahzranka : public CreatureScript
|
||||
{
|
||||
public:
|
||||
@@ -51,6 +57,11 @@ public:
|
||||
{
|
||||
boss_gahzrankaAI(Creature* creature) : BossAI(creature, DATA_GAHZRANKA) { }
|
||||
|
||||
void IsSummonedBy(Unit* /*summoner*/) override
|
||||
{
|
||||
me->GetMotionMaster()->MovePath(me->GetEntry() * 10, false);
|
||||
}
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
_Reset();
|
||||
@@ -147,8 +158,45 @@ private:
|
||||
bool _wipeThreat = false;
|
||||
};
|
||||
|
||||
class spell_pagles_point_cast : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_pagles_point_cast);
|
||||
|
||||
void OnEffect(SpellEffIndex /*effIndex*/)
|
||||
{
|
||||
if (Unit* caster = GetCaster())
|
||||
{
|
||||
if (InstanceScript* instanceScript = caster->GetInstanceScript())
|
||||
{
|
||||
if (!instanceScript->GetData(DATA_GAHZRANKA))
|
||||
{
|
||||
caster->m_Events.AddEventAtOffset([caster]()
|
||||
{
|
||||
if (GameObject* lure = caster->SummonGameObject(GAMEOBJECT_MUDSKUNK_LURE, -11688.5f, -1737.74f, 10.409842f, 1.f, 0.f, 0.f, 0.f, 0.f, 30 * IN_MILLISECONDS))
|
||||
{
|
||||
caster->m_Events.AddEventAtOffset([caster, lure]()
|
||||
{
|
||||
if (lure)
|
||||
lure->DespawnOrUnsummon();
|
||||
caster->CastSpell(caster, SPELL_SPLASH, true);
|
||||
caster->SummonCreature(NPC_GAHZRANKA, -11688.5f, -1723.74f, -5.78f, 0.f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5 * DAY * IN_MILLISECONDS);
|
||||
}, 5s);
|
||||
}
|
||||
}, 2s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectLaunch += SpellEffectFn(spell_pagles_point_cast::OnEffect, EFFECT_1, SPELL_EFFECT_SEND_EVENT);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_boss_gahzranka()
|
||||
{
|
||||
new boss_gahzranka();
|
||||
RegisterSpellScript(spell_gahzranka_slam);
|
||||
RegisterSpellScript(spell_pagles_point_cast);
|
||||
}
|
||||
|
||||
@@ -24,19 +24,25 @@ EndScriptData */
|
||||
|
||||
#include "ScriptMgr.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SpellScript.h"
|
||||
#include "zulgurub.h"
|
||||
|
||||
enum Spells
|
||||
{
|
||||
SPELL_MANABURN = 26046,
|
||||
SPELL_SLEEP = 24664
|
||||
SPELL_SLEEP = 24664,
|
||||
SPELL_EARTH_SHOCK = 24685,
|
||||
SPELL_CHAIN_BURN = 24684,
|
||||
SPELL_SUMMON_NIGHTMARE_ILLUSION_LEFT = 24681,
|
||||
SPELL_SUMMON_NIGHTMARE_ILLUSION_BACK = 24728,
|
||||
SPELL_SUMMON_NIGHTMARE_ILLUSION_RIGHT = 24729
|
||||
};
|
||||
|
||||
enum Events
|
||||
{
|
||||
EVENT_MANABURN = 1,
|
||||
EVENT_SLEEP = 2,
|
||||
EVENT_ILLUSIONS = 3
|
||||
EVENT_SLEEP = 1,
|
||||
EVENT_EARTH_SHOCK = 2,
|
||||
EVENT_CHAIN_BURN = 3,
|
||||
EVENT_ILLUSIONS = 4
|
||||
};
|
||||
|
||||
class boss_hazzarah : public CreatureScript
|
||||
@@ -48,22 +54,42 @@ public:
|
||||
{
|
||||
boss_hazzarahAI(Creature* creature) : BossAI(creature, DATA_EDGE_OF_MADNESS) { }
|
||||
|
||||
void Reset() override
|
||||
void JustSummoned(Creature* summon) override
|
||||
{
|
||||
_Reset();
|
||||
}
|
||||
summons.Summon(summon);
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
_JustDied();
|
||||
summon->SetCorpseDelay(10);
|
||||
summon->SetReactState(REACT_PASSIVE);
|
||||
summon->SetUnitFlag(UNIT_FLAG_DISABLE_MOVE);
|
||||
summon->SetVisible(false);
|
||||
summon->m_Events.AddEventAtOffset([summon]()
|
||||
{
|
||||
summon->SetVisible(true);
|
||||
}, 2s);
|
||||
|
||||
summon->m_Events.AddEventAtOffset([summon]()
|
||||
{
|
||||
summon->RemoveUnitFlag(UNIT_FLAG_DISABLE_MOVE);
|
||||
summon->SetReactState(REACT_AGGRESSIVE);
|
||||
summon->SetInCombatWithZone();
|
||||
}, 3500ms);
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* /*who*/) override
|
||||
{
|
||||
_EnterCombat();
|
||||
events.ScheduleEvent(EVENT_MANABURN, urand(4000, 10000));
|
||||
events.ScheduleEvent(EVENT_SLEEP, urand(10000, 18000));
|
||||
events.ScheduleEvent(EVENT_ILLUSIONS, urand(10000, 18000));
|
||||
events.ScheduleEvent(EVENT_SLEEP, 12s, 15s);
|
||||
events.ScheduleEvent(EVENT_EARTH_SHOCK, 8s, 18s);
|
||||
events.ScheduleEvent(EVENT_CHAIN_BURN, 12s, 28s);
|
||||
events.ScheduleEvent(EVENT_ILLUSIONS, 16s, 24s);
|
||||
}
|
||||
|
||||
bool CanAIAttack(Unit const* target) const override
|
||||
{
|
||||
if (me->GetThreatMgr().getThreatList().size() > 1 && me->GetThreatMgr().getOnlineContainer().getMostHated()->getTarget() == target)
|
||||
return !target->HasAura(SPELL_SLEEP);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
@@ -80,27 +106,26 @@ public:
|
||||
{
|
||||
switch (eventId)
|
||||
{
|
||||
case EVENT_MANABURN:
|
||||
DoCastVictim(SPELL_MANABURN, true);
|
||||
events.ScheduleEvent(EVENT_MANABURN, urand(8000, 16000));
|
||||
break;
|
||||
case EVENT_SLEEP:
|
||||
DoCastVictim(SPELL_SLEEP, true);
|
||||
events.ScheduleEvent(EVENT_SLEEP, urand(12000, 20000));
|
||||
events.ScheduleEvent(EVENT_SLEEP, 24s, 32s);
|
||||
return;
|
||||
case EVENT_EARTH_SHOCK:
|
||||
DoCastVictim(SPELL_EARTH_SHOCK);
|
||||
events.ScheduleEvent(EVENT_EARTH_SHOCK, 8s, 18s);
|
||||
break;
|
||||
case EVENT_CHAIN_BURN:
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, [&](Unit* u) { return u && !u->IsPet() && u->getPowerType() == POWER_MANA; }))
|
||||
{
|
||||
DoCast(target, SPELL_CHAIN_BURN, false);
|
||||
}
|
||||
events.ScheduleEvent(EVENT_CHAIN_BURN, 12s, 28s);
|
||||
break;
|
||||
case EVENT_ILLUSIONS:
|
||||
// We will summon 3 illusions that will spawn on a random gamer and attack this gamer
|
||||
// We will just use one model for the beginning
|
||||
for (uint8 i = 0; i < 3; ++i)
|
||||
{
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
|
||||
{
|
||||
Creature* Illusion = me->SummonCreature(NPC_NIGHTMARE_ILLUSION, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000);
|
||||
if (Illusion)
|
||||
Illusion->AI()->AttackStart(target);
|
||||
}
|
||||
}
|
||||
events.ScheduleEvent(EVENT_ILLUSIONS, urand(15000, 25000));
|
||||
DoCastSelf(SPELL_SUMMON_NIGHTMARE_ILLUSION_LEFT, true);
|
||||
DoCastSelf(SPELL_SUMMON_NIGHTMARE_ILLUSION_BACK, true);
|
||||
DoCastSelf(SPELL_SUMMON_NIGHTMARE_ILLUSION_RIGHT, true);
|
||||
events.ScheduleEvent(EVENT_ILLUSIONS, 16s, 24s);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -117,7 +142,28 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class spell_chain_burn : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_chain_burn);
|
||||
|
||||
void FilterTargets(std::list<WorldObject*>& targets)
|
||||
{
|
||||
Unit* caster = GetCaster();
|
||||
targets.remove_if([caster](WorldObject* target) -> bool
|
||||
{
|
||||
Unit* unit = target->ToUnit();
|
||||
return !unit || unit->getPowerType() != POWER_MANA || caster->GetVictim() == unit;
|
||||
});
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_chain_burn::FilterTargets, EFFECT_0, TARGET_UNIT_TARGET_ENEMY);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_boss_hazzarah()
|
||||
{
|
||||
new boss_hazzarah();
|
||||
RegisterSpellScript(spell_chain_burn);
|
||||
}
|
||||
|
||||
@@ -86,6 +86,23 @@ public:
|
||||
Talk(SAY_AGGRO);
|
||||
}
|
||||
|
||||
void JustSummoned(Creature* summon) override
|
||||
{
|
||||
BossAI::JustSummoned(summon);
|
||||
|
||||
switch (summon->GetEntry())
|
||||
{
|
||||
case NPC_BRAIN_WASH_TOTEM:
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1))
|
||||
{
|
||||
summon->CastSpell(target, summon->m_spells[0], true);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (!UpdateVictim())
|
||||
|
||||
@@ -15,45 +15,72 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* ScriptData
|
||||
SDName: Boss_Marli
|
||||
SD%Complete: 80
|
||||
SDComment: Charging healers and casters not working. Perhaps wrong Spell Timers.
|
||||
SDCategory: Zul'Gurub
|
||||
EndScriptData */
|
||||
|
||||
#include "GameObjectAI.h"
|
||||
#include "SpellScript.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "TaskScheduler.h"
|
||||
#include "zulgurub.h"
|
||||
|
||||
enum Says
|
||||
{
|
||||
// Mar'li
|
||||
SAY_AGGRO = 0,
|
||||
SAY_TRANSFORM = 1,
|
||||
SAY_SPIDER_SPAWN = 2,
|
||||
SAY_DEATH = 3
|
||||
SAY_DEATH = 3,
|
||||
SAY_TRANSFORM_BACK = 4,
|
||||
|
||||
// Spawn of Mar'li
|
||||
EMOTE_FULL_GROWN = 0
|
||||
};
|
||||
|
||||
enum Spells
|
||||
{
|
||||
// Spider form
|
||||
SPELL_CHARGE = 22911,
|
||||
SPELL_ASPECT_OF_MARLI = 24686, // A stun spell
|
||||
SPELL_ENVOLWINGWEB = 24110,
|
||||
SPELL_ENVELOPING_WEB = 24110,
|
||||
SPELL_CORROSIVE_POISON = 24111,
|
||||
SPELL_POISON_SHOCK = 24112,
|
||||
|
||||
//Troll form
|
||||
SPELL_POISON_VOLLEY = 24099,
|
||||
SPELL_DRAIN_LIFE = 24300,
|
||||
SPELL_ENLARGE = 24109,
|
||||
SPELL_SPIDER_EGGS = 24082,
|
||||
|
||||
// All
|
||||
SPELL_SPIDER_FORM = 24084,
|
||||
// The Spider Spell
|
||||
SPELL_LEVELUP = 24312 // Not right Spell.
|
||||
SPELL_TRANSFORM_BACK = 24085,
|
||||
SPELL_THRASH = 3391,
|
||||
SPELL_HATCH_SPIDER_EGG = 24082,
|
||||
SPELL_HATCH_EGGS = 24083,
|
||||
|
||||
// Spawn of Mar'li
|
||||
SPELL_GROWTH = 24086,
|
||||
SPELL_FULL_GROWN = 24088
|
||||
};
|
||||
|
||||
enum Events
|
||||
{
|
||||
EVENT_SPAWN_START_SPIDERS = 1, // Phase 1
|
||||
EVENT_POISON_VOLLEY = 2, // Phase All
|
||||
EVENT_SPAWN_SPIDER = 3, // Phase All
|
||||
EVENT_CHARGE_PLAYER = 4, // Phase 3
|
||||
EVENT_ASPECT_OF_MARLI = 5, // Phase 2
|
||||
EVENT_TRANSFORM = 6, // Phase 2
|
||||
EVENT_TRANSFORM_BACK = 7 // Phase 3
|
||||
// Spider form
|
||||
EVENT_CHARGE_PLAYER = 7,
|
||||
EVENT_ENVELOPING_WEB = 8,
|
||||
EVENT_CORROSIVE_POISON = 9,
|
||||
EVENT_POISON_SHOCK = 10,
|
||||
|
||||
// Troll form
|
||||
EVENT_POISON_VOLLEY = 11,
|
||||
EVENT_DRAIN_LIFE = 12,
|
||||
EVENT_ENLARGE = 13,
|
||||
|
||||
// All
|
||||
EVENT_SPAWN_START_SPIDERS = 1,
|
||||
EVENT_TRANSFORM = 2,
|
||||
EVENT_TRANSFORM_BACK = 3,
|
||||
EVENT_HATCH_SPIDER_EGG = 4,
|
||||
EVENT_THRASH = 5,
|
||||
EVENT_TALK_FIRST_SPIDERS = 6,
|
||||
};
|
||||
|
||||
enum Phases
|
||||
@@ -63,208 +90,269 @@ enum Phases
|
||||
PHASE_THREE = 3
|
||||
};
|
||||
|
||||
class boss_marli : public CreatureScript
|
||||
enum Misc
|
||||
{
|
||||
public:
|
||||
boss_marli() : CreatureScript("boss_marli") { }
|
||||
GO_SPIDER_EGGS = 179985,
|
||||
};
|
||||
|
||||
struct boss_marliAI : public BossAI
|
||||
struct boss_marli : public BossAI
|
||||
{
|
||||
boss_marli(Creature* creature) : BossAI(creature, DATA_MARLI) { }
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
boss_marliAI(Creature* creature) : BossAI(creature, DATA_MARLI) { }
|
||||
if (events.IsInPhase(PHASE_THREE))
|
||||
me->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 35.0f, false); // hack
|
||||
|
||||
void Reset() override
|
||||
std::list<GameObject*> eggs;
|
||||
me->GetGameObjectListWithEntryInGrid(eggs, GO_SPIDER_EGGS, DEFAULT_VISIBILITY_INSTANCE);
|
||||
|
||||
for (auto const& egg : eggs)
|
||||
{
|
||||
if (events.IsInPhase(PHASE_THREE))
|
||||
me->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 35.0f, false); // hack
|
||||
_Reset();
|
||||
egg->Respawn();
|
||||
egg->UpdateObjectVisibility();
|
||||
}
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
BossAI::Reset();
|
||||
}
|
||||
|
||||
void JustDied(Unit* killer) override
|
||||
{
|
||||
BossAI::JustDied(killer);
|
||||
Talk(SAY_DEATH);
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* who) override
|
||||
{
|
||||
BossAI::EnterCombat(who);
|
||||
events.ScheduleEvent(EVENT_SPAWN_START_SPIDERS, 1000, 0, PHASE_ONE);
|
||||
Talk(SAY_AGGRO);
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
events.Update(diff);
|
||||
|
||||
if (me->HasUnitState(UNIT_STATE_CASTING))
|
||||
return;
|
||||
|
||||
while (uint32 eventId = events.ExecuteEvent())
|
||||
{
|
||||
_JustDied();
|
||||
Talk(SAY_DEATH);
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* /*who*/) override
|
||||
{
|
||||
_EnterCombat();
|
||||
events.ScheduleEvent(EVENT_SPAWN_START_SPIDERS, 1000, 0, PHASE_ONE);
|
||||
Talk(SAY_AGGRO);
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
events.Update(diff);
|
||||
|
||||
if (me->HasUnitState(UNIT_STATE_CASTING))
|
||||
return;
|
||||
|
||||
while (uint32 eventId = events.ExecuteEvent())
|
||||
switch (eventId)
|
||||
{
|
||||
switch (eventId)
|
||||
case EVENT_SPAWN_START_SPIDERS:
|
||||
events.ScheduleEvent(EVENT_TALK_FIRST_SPIDERS, 500, 0, PHASE_TWO);
|
||||
DoCastAOE(SPELL_HATCH_EGGS);
|
||||
events.ScheduleEvent(EVENT_TRANSFORM, 60000, 0, PHASE_TWO);
|
||||
events.ScheduleEvent(EVENT_POISON_VOLLEY, 15000, 0, PHASE_TWO);
|
||||
events.ScheduleEvent(EVENT_HATCH_SPIDER_EGG, 30000, 0, PHASE_TWO);
|
||||
events.ScheduleEvent(EVENT_DRAIN_LIFE, 30000, 0, PHASE_TWO);
|
||||
events.ScheduleEvent(EVENT_THRASH, urand(4000, 6000)); // all phases
|
||||
events.ScheduleEvent(EVENT_ENLARGE, urand(10000, 20000), 0, PHASE_TWO);
|
||||
events.SetPhase(PHASE_TWO);
|
||||
break;
|
||||
case EVENT_TALK_FIRST_SPIDERS:
|
||||
Talk(SAY_SPIDER_SPAWN);
|
||||
break;
|
||||
case EVENT_POISON_VOLLEY:
|
||||
DoCastVictim(SPELL_POISON_VOLLEY, true);
|
||||
events.ScheduleEvent(EVENT_POISON_VOLLEY, urand(10000, 20000), 0, PHASE_TWO);
|
||||
break;
|
||||
case EVENT_HATCH_SPIDER_EGG:
|
||||
DoCastSelf(SPELL_HATCH_SPIDER_EGG, true);
|
||||
events.ScheduleEvent(EVENT_HATCH_SPIDER_EGG, 20000, 0, PHASE_TWO);
|
||||
break;
|
||||
case EVENT_DRAIN_LIFE:
|
||||
DoCastRandomTarget(SPELL_DRAIN_LIFE);
|
||||
events.ScheduleEvent(EVENT_DRAIN_LIFE, urand(20000, 50000), 0, PHASE_TWO);
|
||||
break;
|
||||
case EVENT_ENLARGE:
|
||||
{
|
||||
case EVENT_SPAWN_START_SPIDERS:
|
||||
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
|
||||
{
|
||||
Talk(SAY_SPIDER_SPAWN);
|
||||
Creature* Spider = me->SummonCreature(15041, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
if (Spider)
|
||||
Spider->AI()->AttackStart(target);
|
||||
Spider = me->SummonCreature(15041, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
if (Spider)
|
||||
Spider->AI()->AttackStart(target);
|
||||
Spider = me->SummonCreature(15041, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
if (Spider)
|
||||
Spider->AI()->AttackStart(target);
|
||||
Spider = me->SummonCreature(15041, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
if (Spider)
|
||||
Spider->AI()->AttackStart(target);
|
||||
}
|
||||
events.ScheduleEvent(EVENT_ASPECT_OF_MARLI, 12000, 0, PHASE_TWO);
|
||||
events.ScheduleEvent(EVENT_TRANSFORM, 45000, 0, PHASE_TWO);
|
||||
events.ScheduleEvent(EVENT_POISON_VOLLEY, 15000);
|
||||
events.ScheduleEvent(EVENT_SPAWN_SPIDER, 30000);
|
||||
events.ScheduleEvent(EVENT_TRANSFORM, 45000, 0, PHASE_TWO);
|
||||
events.SetPhase(PHASE_TWO);
|
||||
break;
|
||||
case EVENT_POISON_VOLLEY:
|
||||
DoCastVictim(SPELL_POISON_VOLLEY, true);
|
||||
events.ScheduleEvent(EVENT_POISON_VOLLEY, urand(10000, 20000));
|
||||
break;
|
||||
case EVENT_ASPECT_OF_MARLI:
|
||||
DoCastVictim(SPELL_ASPECT_OF_MARLI, true);
|
||||
events.ScheduleEvent(EVENT_ASPECT_OF_MARLI, urand(13000, 18000), 0, PHASE_TWO);
|
||||
break;
|
||||
case EVENT_SPAWN_SPIDER:
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
|
||||
{
|
||||
Creature* Spider = me->SummonCreature(15041, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
if (Spider)
|
||||
Spider->AI()->AttackStart(target);
|
||||
}
|
||||
events.ScheduleEvent(EVENT_SPAWN_SPIDER, urand(12000, 17000));
|
||||
break;
|
||||
case EVENT_TRANSFORM:
|
||||
{
|
||||
Talk(SAY_TRANSFORM);
|
||||
DoCast(me, SPELL_SPIDER_FORM); // SPELL_AURA_TRANSFORM
|
||||
/*
|
||||
CreatureTemplate const* cinfo = me->GetCreatureTemplate();
|
||||
me->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg +((cinfo->mindmg/100) * 35)));
|
||||
me->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg +((cinfo->maxdmg/100) * 35)));
|
||||
me->UpdateDamagePhysical(BASE_ATTACK);
|
||||
*/
|
||||
me->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 35.0f, true); // hack
|
||||
DoCastVictim(SPELL_ENVOLWINGWEB);
|
||||
if (DoGetThreat(me->GetVictim()))
|
||||
DoModifyThreatPercent(me->GetVictim(), -100);
|
||||
events.ScheduleEvent(EVENT_CHARGE_PLAYER, 1500, 0, PHASE_THREE);
|
||||
events.ScheduleEvent(EVENT_TRANSFORM_BACK, 25000, 0, PHASE_THREE);
|
||||
events.SetPhase(PHASE_THREE);
|
||||
break;
|
||||
}
|
||||
case EVENT_CHARGE_PLAYER:
|
||||
{
|
||||
Unit* target = nullptr;
|
||||
int i = 0;
|
||||
while (i++ < 3) // max 3 tries to get a random target with power_mana
|
||||
{
|
||||
target = SelectTarget(SelectTargetMethod::Random, 1, 100, true); // not aggro leader
|
||||
if (target && target->getPowerType() == POWER_MANA)
|
||||
break;
|
||||
}
|
||||
if (target)
|
||||
{
|
||||
DoCast(target, SPELL_CHARGE);
|
||||
AttackStart(target);
|
||||
}
|
||||
events.ScheduleEvent(EVENT_CHARGE_PLAYER, 8000, 0, PHASE_THREE);
|
||||
break;
|
||||
}
|
||||
case EVENT_TRANSFORM_BACK:
|
||||
{
|
||||
me->RemoveAura(SPELL_SPIDER_FORM);
|
||||
/*
|
||||
CreatureTemplate const* cinfo = me->GetCreatureTemplate();
|
||||
me->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg +((cinfo->mindmg/100) * 1)));
|
||||
me->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg +((cinfo->maxdmg/100) * 1)));
|
||||
me->UpdateDamagePhysical(BASE_ATTACK);
|
||||
*/
|
||||
me->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 35.0f, false); // hack
|
||||
events.ScheduleEvent(EVENT_ASPECT_OF_MARLI, 12000, 0, PHASE_TWO);
|
||||
events.ScheduleEvent(EVENT_TRANSFORM, 45000, 0, PHASE_TWO);
|
||||
events.ScheduleEvent(EVENT_POISON_VOLLEY, 15000);
|
||||
events.ScheduleEvent(EVENT_SPAWN_SPIDER, 30000);
|
||||
events.ScheduleEvent(EVENT_TRANSFORM, urand(35000, 60000), 0, PHASE_TWO);
|
||||
events.SetPhase(PHASE_TWO);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
std::list<Creature*> targets = DoFindFriendlyMissingBuff(100.f, SPELL_ENLARGE);
|
||||
if (targets.size() > 0)
|
||||
DoCast(*(targets.begin()), SPELL_ENLARGE);
|
||||
events.ScheduleEvent(EVENT_ENLARGE, urand(20000, 40000), 0, PHASE_TWO);
|
||||
break;
|
||||
}
|
||||
case EVENT_TRANSFORM:
|
||||
Talk(SAY_TRANSFORM);
|
||||
DoCastSelf(SPELL_SPIDER_FORM, true);
|
||||
me->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 35.0f, true); // hack
|
||||
events.ScheduleEvent(EVENT_ENVELOPING_WEB, 5000, 0, PHASE_THREE);
|
||||
events.ScheduleEvent(EVENT_CHARGE_PLAYER, 6000, 0, PHASE_THREE);
|
||||
events.ScheduleEvent(EVENT_CORROSIVE_POISON, 1000, 0, PHASE_THREE);
|
||||
events.ScheduleEvent(EVENT_POISON_SHOCK, urand(5000, 10000), 0, PHASE_THREE);
|
||||
events.ScheduleEvent(EVENT_TRANSFORM_BACK, 60000, 0, PHASE_THREE);
|
||||
events.SetPhase(PHASE_THREE);
|
||||
break;
|
||||
case EVENT_ENVELOPING_WEB:
|
||||
DoCastAOE(SPELL_ENVELOPING_WEB);
|
||||
events.ScheduleEvent(EVENT_CHARGE_PLAYER, 500, 0, PHASE_THREE);
|
||||
events.ScheduleEvent(EVENT_ENVELOPING_WEB, urand(15000, 20000), 0, PHASE_THREE);
|
||||
break;
|
||||
case EVENT_CHARGE_PLAYER:
|
||||
{
|
||||
Unit* target = SelectTarget(SelectTargetMethod::Random, 0, [this](Unit* target) -> bool
|
||||
{
|
||||
if (target->GetTypeId() != TYPEID_PLAYER || target->getPowerType() != Powers::POWER_MANA)
|
||||
return false;
|
||||
if (me->IsWithinMeleeRange(target) || me->GetVictim() == target)
|
||||
return false;
|
||||
return true;
|
||||
});
|
||||
if (target)
|
||||
{
|
||||
DoCast(target, SPELL_CHARGE);
|
||||
AttackStart(target);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EVENT_CORROSIVE_POISON:
|
||||
DoCastVictim(SPELL_CORROSIVE_POISON);
|
||||
events.ScheduleEvent(EVENT_CORROSIVE_POISON, urand(25000, 35000), 0, PHASE_THREE);
|
||||
break;
|
||||
case EVENT_POISON_SHOCK:
|
||||
DoCastRandomTarget(SPELL_POISON_SHOCK);
|
||||
events.ScheduleEvent(EVENT_POISON_SHOCK, 10000, 0, PHASE_THREE);
|
||||
break;
|
||||
case EVENT_TRANSFORM_BACK:
|
||||
me->RemoveAura(SPELL_SPIDER_FORM);
|
||||
DoCastSelf(SPELL_TRANSFORM_BACK, true);
|
||||
Talk(SAY_TRANSFORM_BACK);
|
||||
me->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 35.0f, false); // hack
|
||||
events.ScheduleEvent(EVENT_TRANSFORM, 60000, 0, PHASE_TWO);
|
||||
events.ScheduleEvent(EVENT_POISON_VOLLEY, 15000, 0, PHASE_TWO);
|
||||
events.ScheduleEvent(EVENT_HATCH_SPIDER_EGG, 30000, 0, PHASE_TWO);
|
||||
events.ScheduleEvent(EVENT_DRAIN_LIFE, 30000, 0, PHASE_TWO);
|
||||
events.ScheduleEvent(EVENT_ENLARGE, urand(10000, 20000), 0, PHASE_TWO);
|
||||
events.SetPhase(PHASE_TWO);
|
||||
break;
|
||||
case EVENT_THRASH:
|
||||
DoCastVictim(SPELL_THRASH);
|
||||
events.ScheduleEvent(EVENT_THRASH, urand(10000, 20000));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
};
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
{
|
||||
return GetZulGurubAI<boss_marliAI>(creature);
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
};
|
||||
|
||||
// Spawn of Marli
|
||||
class npc_spawn_of_marli : public CreatureScript
|
||||
// Spawn of Mar'li
|
||||
struct npc_spawn_of_marli : public ScriptedAI
|
||||
{
|
||||
public:
|
||||
npc_spawn_of_marli() : CreatureScript("npc_spawn_of_marli") { }
|
||||
npc_spawn_of_marli(Creature* creature) : ScriptedAI(creature) { }
|
||||
|
||||
struct npc_spawn_of_marliAI : public ScriptedAI
|
||||
void Reset() override
|
||||
{
|
||||
npc_spawn_of_marliAI(Creature* creature) : ScriptedAI(creature) { }
|
||||
_scheduler.CancelAll();
|
||||
}
|
||||
|
||||
uint32 LevelUp_Timer;
|
||||
|
||||
void Reset() override
|
||||
void EnterCombat(Unit* /*who*/) override
|
||||
{
|
||||
_scheduler.Schedule(4s, [this](TaskContext context)
|
||||
{
|
||||
LevelUp_Timer = 3000;
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* /*who*/) override
|
||||
{
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
//Return since we have no target
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
//LevelUp_Timer
|
||||
if (LevelUp_Timer <= diff)
|
||||
if (context.GetRepeatCounter() < 5)
|
||||
{
|
||||
DoCast(me, SPELL_LEVELUP);
|
||||
LevelUp_Timer = 3000;
|
||||
DoCastSelf(SPELL_GROWTH);
|
||||
context.Repeat(4s);
|
||||
}
|
||||
else LevelUp_Timer -= diff;
|
||||
else
|
||||
{
|
||||
Talk(EMOTE_FULL_GROWN);
|
||||
DoCastSelf(SPELL_FULL_GROWN);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
};
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
return GetZulGurubAI<npc_spawn_of_marliAI>(creature);
|
||||
//Return since we have no target
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
_scheduler.Update(diff, [this]
|
||||
{
|
||||
DoMeleeAttackIfReady();
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
TaskScheduler _scheduler;
|
||||
};
|
||||
|
||||
// 24083 - Hatch Eggs
|
||||
class spell_hatch_eggs : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_hatch_eggs);
|
||||
|
||||
void HandleObjectAreaTargetSelect(std::list<WorldObject*>& targets)
|
||||
{
|
||||
targets.sort(Acore::ObjectDistanceOrderPred(GetCaster()));
|
||||
targets.resize(GetSpellInfo()->MaxAffectedTargets);
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_hatch_eggs::HandleObjectAreaTargetSelect, EFFECT_0, TARGET_GAMEOBJECT_DEST_AREA);
|
||||
}
|
||||
};
|
||||
|
||||
// 24110 - Enveloping Webs
|
||||
class spell_enveloping_webs : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_enveloping_webs);
|
||||
|
||||
void HandleOnHit()
|
||||
{
|
||||
Unit* caster = GetCaster();
|
||||
Unit* hitUnit = GetHitUnit();
|
||||
if (caster && hitUnit && hitUnit->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
caster->GetThreatMgr().modifyThreatPercent(hitUnit, -100);
|
||||
}
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnHit += SpellHitFn(spell_enveloping_webs::HandleOnHit);
|
||||
}
|
||||
};
|
||||
|
||||
// 24084 - Mar'li Transform
|
||||
class spell_marli_transform : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_marli_transform);
|
||||
|
||||
void HandleApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
|
||||
{
|
||||
if (GetCaster() && GetCaster()->ToCreature())
|
||||
GetCaster()->ToCreature()->LoadEquipment(0, true);
|
||||
}
|
||||
|
||||
void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
|
||||
{
|
||||
if (GetCaster() && GetCaster()->ToCreature())
|
||||
GetCaster()->ToCreature()->LoadEquipment(1, true);
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectApply += AuraEffectApplyFn(spell_marli_transform::HandleApply, EFFECT_0, SPELL_AURA_TRANSFORM, AURA_EFFECT_HANDLE_REAL);
|
||||
OnEffectRemove += AuraEffectRemoveFn(spell_marli_transform::HandleRemove, EFFECT_0, SPELL_AURA_TRANSFORM, AURA_EFFECT_HANDLE_REAL);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_boss_marli()
|
||||
{
|
||||
new boss_marli();
|
||||
new npc_spawn_of_marli();
|
||||
RegisterCreatureAI(boss_marli);
|
||||
RegisterCreatureAI(npc_spawn_of_marli);
|
||||
RegisterSpellScript(spell_hatch_eggs);
|
||||
RegisterSpellScript(spell_enveloping_webs);
|
||||
RegisterSpellScript(spell_marli_transform);
|
||||
}
|
||||
|
||||
@@ -28,13 +28,20 @@ EndScriptData */
|
||||
|
||||
enum Spells
|
||||
{
|
||||
SPELL_AMBUSH = 34794,
|
||||
SPELL_THOUSANDBLADES = 34799
|
||||
SPELL_VANISH = 24699,
|
||||
SPELL_AMBUSH = 24337,
|
||||
SPELL_GOUGE = 24698,
|
||||
SPELL_THOUSAND_BLADES = 24649,
|
||||
SPELL_THRASH = 3417,
|
||||
SPELL_ENRAGE = 8269
|
||||
};
|
||||
|
||||
enum Misc
|
||||
enum Events
|
||||
{
|
||||
EQUIP_ID_MAIN_HAND = 0 //was item display id 31818, but this id does not exist
|
||||
EVENT_VANISH = 1,
|
||||
EVENT_AMBUSH = 2,
|
||||
EVENT_GOUGE = 3,
|
||||
EVENT_THOUSAND_BLADES = 4
|
||||
};
|
||||
|
||||
class boss_renataki : public CreatureScript
|
||||
@@ -46,36 +53,67 @@ public:
|
||||
{
|
||||
boss_renatakiAI(Creature* creature) : BossAI(creature, DATA_EDGE_OF_MADNESS) { }
|
||||
|
||||
uint32 Invisible_Timer;
|
||||
uint32 Ambush_Timer;
|
||||
uint32 Visible_Timer;
|
||||
uint32 Aggro_Timer;
|
||||
uint32 ThousandBlades_Timer;
|
||||
|
||||
bool Invisible;
|
||||
bool Ambushed;
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
_Reset();
|
||||
Invisible_Timer = urand(8000, 18000);
|
||||
Ambush_Timer = 3000;
|
||||
Visible_Timer = 4000;
|
||||
Aggro_Timer = urand(15000, 25000);
|
||||
ThousandBlades_Timer = urand(4000, 8000);
|
||||
|
||||
Invisible = false;
|
||||
Ambushed = false;
|
||||
}
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
_JustDied();
|
||||
me->SetReactState(REACT_AGGRESSIVE);
|
||||
_enraged = false;
|
||||
_thousandBladesCount = urand(2, 5);
|
||||
_thousandBladesTargets.clear();
|
||||
_dynamicFlags = me->GetDynamicFlags();
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* /*who*/) override
|
||||
{
|
||||
_EnterCombat();
|
||||
events.ScheduleEvent(EVENT_VANISH, 23s, 25s);
|
||||
events.ScheduleEvent(EVENT_GOUGE, 5s, 10s);
|
||||
events.ScheduleEvent(EVENT_THOUSAND_BLADES, 15s, 20s);
|
||||
|
||||
DoCastSelf(SPELL_THRASH, true);
|
||||
}
|
||||
|
||||
void DamageTaken(Unit*, uint32& /*damage*/, DamageEffectType, SpellSchoolMask) override
|
||||
{
|
||||
if (!_enraged && HealthBelowPct(30))
|
||||
{
|
||||
me->TextEmote("%s becomes enraged", me, false);
|
||||
DoCast(me, SPELL_ENRAGE);
|
||||
_enraged = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool CanAIAttack(Unit const* target) const override
|
||||
{
|
||||
if (me->GetThreatMgr().getThreatList().size() > 1 && me->GetThreatMgr().getOnlineContainer().getMostHated()->getTarget() == target)
|
||||
return !target->HasAura(SPELL_GOUGE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CanBeSeen(Player const* /*player*/) override
|
||||
{
|
||||
return me->GetReactState() == REACT_AGGRESSIVE;
|
||||
}
|
||||
|
||||
bool CanSeeAlways(WorldObject const* obj) override
|
||||
{
|
||||
if (me->GetReactState() == REACT_PASSIVE)
|
||||
{
|
||||
return obj->ToCreature() && obj->ToCreature()->IsPet();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CanAlwaysBeDetectable(WorldObject const* seer) override
|
||||
{
|
||||
if (me->GetReactState() == REACT_PASSIVE)
|
||||
{
|
||||
return seer->ToCreature() && seer->ToCreature()->IsPet();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
@@ -83,86 +121,122 @@ public:
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
//Invisible_Timer
|
||||
if (Invisible_Timer <= diff)
|
||||
events.Update(diff);
|
||||
|
||||
if (me->HasUnitState(UNIT_STATE_CASTING))
|
||||
return;
|
||||
|
||||
while (uint32 eventId = events.ExecuteEvent())
|
||||
{
|
||||
me->InterruptSpell(CURRENT_GENERIC_SPELL);
|
||||
|
||||
SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_NO_CHANGE, EQUIP_NO_CHANGE);
|
||||
me->SetDisplayId(11686);
|
||||
|
||||
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
|
||||
Invisible = true;
|
||||
|
||||
Invisible_Timer = urand(15000, 30000);
|
||||
}
|
||||
else Invisible_Timer -= diff;
|
||||
|
||||
if (Invisible)
|
||||
{
|
||||
if (Ambush_Timer <= diff)
|
||||
switch (eventId)
|
||||
{
|
||||
Unit* target = SelectTarget(SelectTargetMethod::Random, 0);
|
||||
if (target)
|
||||
case EVENT_VANISH:
|
||||
me->SetReactState(REACT_PASSIVE);
|
||||
_dynamicFlags = me->GetDynamicFlags();
|
||||
me->RemoveDynamicFlag(UNIT_DYNFLAG_TRACK_UNIT);
|
||||
DoCastSelf(SPELL_VANISH);
|
||||
events.DelayEvents(5s);
|
||||
events.ScheduleEvent(EVENT_AMBUSH, 5s);
|
||||
events.ScheduleEvent(EVENT_VANISH, 38s, 45s);
|
||||
return;
|
||||
case EVENT_AMBUSH:
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1))
|
||||
{
|
||||
me->NearTeleportTo(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), me->GetOrientation());
|
||||
DoCast(target, SPELL_AMBUSH, true);
|
||||
}
|
||||
me->SetDynamicFlag(_dynamicFlags);
|
||||
me->RemoveAurasDueToSpell(SPELL_VANISH);
|
||||
me->SetReactState(REACT_AGGRESSIVE);
|
||||
break;
|
||||
case EVENT_GOUGE:
|
||||
DoCastAOE(SPELL_GOUGE);
|
||||
events.ScheduleEvent(EVENT_GOUGE, 10s, 15s);
|
||||
return;
|
||||
case EVENT_THOUSAND_BLADES:
|
||||
{
|
||||
me->NearTeleportTo(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), me->GetOrientation());
|
||||
DoCast(target, SPELL_AMBUSH);
|
||||
}
|
||||
if (_thousandBladesTargets.empty())
|
||||
{
|
||||
std::vector<Unit*> targetList;
|
||||
ThreatContainer::StorageType const& threatlist = me->GetThreatMgr().getThreatList();
|
||||
for (ThreatContainer::StorageType::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr)
|
||||
{
|
||||
if (Unit* target = (*itr)->getTarget())
|
||||
{
|
||||
if (target->IsAlive() && target->IsWithinDist2d(me, 100.f))
|
||||
{
|
||||
targetList.push_back(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ambushed = true;
|
||||
Ambush_Timer = 3000;
|
||||
if (!targetList.empty())
|
||||
{
|
||||
Acore::Containers::RandomShuffle(targetList);
|
||||
|
||||
// First get ranged targets
|
||||
for (Unit* target : targetList)
|
||||
{
|
||||
if (!target->IsWithinMeleeRange(me))
|
||||
{
|
||||
_thousandBladesTargets.push_back(target);
|
||||
}
|
||||
}
|
||||
|
||||
if (_thousandBladesTargets.size() < _thousandBladesCount)
|
||||
{
|
||||
// if still not enough, get melee targets
|
||||
for (Unit* target : targetList)
|
||||
{
|
||||
if (target->IsWithinMeleeRange(me))
|
||||
{
|
||||
_thousandBladesTargets.push_back(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!_thousandBladesTargets.empty())
|
||||
{
|
||||
Acore::Containers::RandomResize(_thousandBladesTargets, _thousandBladesCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!_thousandBladesTargets.empty())
|
||||
{
|
||||
std::vector<Unit*>::iterator itr = _thousandBladesTargets.begin();
|
||||
std::advance(itr, urand(0, _thousandBladesTargets.size() - 1));
|
||||
|
||||
if (Unit* target = *itr)
|
||||
{
|
||||
DoCast(target, SPELL_THOUSAND_BLADES, false);
|
||||
}
|
||||
|
||||
_thousandBladesTargets.erase(itr);
|
||||
|
||||
events.ScheduleEvent(EVENT_THOUSAND_BLADES, 500ms);
|
||||
}
|
||||
else
|
||||
{
|
||||
_thousandBladesCount = urand(2, 5);
|
||||
events.ScheduleEvent(EVENT_THOUSAND_BLADES, 15s, 22s);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
else Ambush_Timer -= diff;
|
||||
}
|
||||
|
||||
if (Ambushed)
|
||||
{
|
||||
if (Visible_Timer <= diff)
|
||||
{
|
||||
me->InterruptSpell(CURRENT_GENERIC_SPELL);
|
||||
|
||||
me->SetDisplayId(15268);
|
||||
SetEquipmentSlots(false, EQUIP_ID_MAIN_HAND, EQUIP_NO_CHANGE, EQUIP_NO_CHANGE);
|
||||
|
||||
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
|
||||
Invisible = false;
|
||||
|
||||
Visible_Timer = 4000;
|
||||
}
|
||||
else Visible_Timer -= diff;
|
||||
}
|
||||
|
||||
//Resetting some aggro so he attacks other gamers
|
||||
if (!Invisible)
|
||||
{
|
||||
if (Aggro_Timer <= diff)
|
||||
{
|
||||
Unit* target = SelectTarget(SelectTargetMethod::Random, 1);
|
||||
|
||||
if (DoGetThreat(me->GetVictim()))
|
||||
{
|
||||
DoModifyThreatPercent(me->GetVictim(), -50);
|
||||
}
|
||||
|
||||
if (target)
|
||||
{
|
||||
AttackStart(target);
|
||||
}
|
||||
|
||||
Aggro_Timer = urand(7000, 20000);
|
||||
}
|
||||
else Aggro_Timer -= diff;
|
||||
|
||||
if (ThousandBlades_Timer <= diff)
|
||||
{
|
||||
DoCastVictim(SPELL_THOUSANDBLADES);
|
||||
ThousandBlades_Timer = urand(7000, 12000);
|
||||
}
|
||||
else ThousandBlades_Timer -= diff;
|
||||
}
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
if (me->GetReactState() == REACT_AGGRESSIVE)
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
|
||||
private:
|
||||
bool _enraged;
|
||||
uint32 _dynamicFlags;
|
||||
uint8 _thousandBladesCount;
|
||||
std::vector<Unit*> _thousandBladesTargets;
|
||||
};
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
|
||||
@@ -22,6 +22,7 @@ SDComment: Missing reset function after killing a boss for Ohgan, Thekal.
|
||||
SDCategory: Zul'Gurub
|
||||
EndScriptData */
|
||||
|
||||
#include "GameObjectAI.h"
|
||||
#include "InstanceScript.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "zulgurub.h"
|
||||
@@ -36,7 +37,8 @@ ObjectData const creatureData[] =
|
||||
{
|
||||
{ NPC_HIGH_PRIEST_THEKAL, DATA_THEKAL },
|
||||
{ NPC_ZEALOT_LORKHAN, DATA_LORKHAN },
|
||||
{ NPC_ZEALOT_ZATH, DATA_ZATH }
|
||||
{ NPC_ZEALOT_ZATH, DATA_ZATH },
|
||||
{ NPC_PRIESTESS_MARLI, DATA_MARLI }
|
||||
};
|
||||
|
||||
class instance_zulgurub : public InstanceMapScript
|
||||
@@ -49,6 +51,7 @@ public:
|
||||
instance_zulgurub_InstanceMapScript(Map* map) : InstanceScript(map)
|
||||
{
|
||||
SetBossNumber(EncounterCount);
|
||||
LoadObjectData(creatureData, nullptr);
|
||||
LoadDoorData(doorData);
|
||||
LoadObjectData(creatureData, nullptr);
|
||||
}
|
||||
@@ -71,7 +74,20 @@ public:
|
||||
case NPC_HAKKAR:
|
||||
_hakkarGUID = creature->GetGUID();
|
||||
break;
|
||||
case NPC_SPAWN_OF_MARLI:
|
||||
if (Creature* marli = GetCreature(DATA_MARLI))
|
||||
{
|
||||
marli->AI()->JustSummoned(creature);
|
||||
}
|
||||
break;
|
||||
case NPC_GAHZRANKA:
|
||||
_gahzrankaGUID = creature->GetGUID();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
InstanceScript::OnCreatureCreate(creature);
|
||||
}
|
||||
|
||||
void OnGameObjectCreate(GameObject* go) override
|
||||
@@ -109,6 +125,16 @@ public:
|
||||
return ObjectGuid::Empty;
|
||||
}
|
||||
|
||||
uint32 GetData(uint32 type) const override
|
||||
{
|
||||
if (type == DATA_GAHZRANKA)
|
||||
{
|
||||
return _gahzrankaGUID || GetBossState(DATA_GAHZRANKA) == DONE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string GetSaveData() override
|
||||
{
|
||||
OUT_SAVE_INST_DATA;
|
||||
@@ -160,6 +186,7 @@ public:
|
||||
ObjectGuid _arlokkGUID;
|
||||
ObjectGuid _goGongOfBethekkGUID;
|
||||
ObjectGuid _hakkarGUID;
|
||||
ObjectGuid _gahzrankaGUID;
|
||||
};
|
||||
|
||||
InstanceScript* GetInstanceScript(InstanceMap* map) const override
|
||||
@@ -168,7 +195,56 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
enum EdgeOfMadnessEnum
|
||||
{
|
||||
EVENT_EDGE_OF_MADNESS_GRILEK = 27,
|
||||
EVENT_EDGE_OF_MADNESS_HAZZARAH = 28,
|
||||
EVENT_EDGE_OF_MADNESS_RENATAKI = 29,
|
||||
EVENT_EDGE_OF_MADNESS_WUSHOOLAY = 30
|
||||
};
|
||||
|
||||
std::vector<std::pair<uint32, uint32>> BrazierOfMadnessContainer =
|
||||
{
|
||||
{ EVENT_EDGE_OF_MADNESS_GRILEK, NPC_GRILEK },
|
||||
{ EVENT_EDGE_OF_MADNESS_HAZZARAH, NPC_HAZZARAH },
|
||||
{ EVENT_EDGE_OF_MADNESS_RENATAKI, NPC_RENATAKI },
|
||||
{ EVENT_EDGE_OF_MADNESS_WUSHOOLAY, NPC_WUSHOOLAY }
|
||||
};
|
||||
|
||||
Position const edgeOfMagnessSummonPos = { -11901.229f, -1906.366f, 65.358f, 0.942f };
|
||||
|
||||
struct go_brazier_of_madness : public GameObjectAI
|
||||
{
|
||||
go_brazier_of_madness(GameObject* go) : GameObjectAI(go) { }
|
||||
|
||||
bool GossipHello(Player* /*player*/, bool reportUse) override
|
||||
{
|
||||
if (reportUse)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 bossEntry = 0;
|
||||
for (uint8 i = 0; i < 4; ++i)
|
||||
{
|
||||
if (sGameEventMgr->IsActiveEvent(BrazierOfMadnessContainer[i].first))
|
||||
{
|
||||
bossEntry = BrazierOfMadnessContainer[i].second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bossEntry)
|
||||
{
|
||||
me->SummonCreature(bossEntry, edgeOfMagnessSummonPos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2 * HOUR * IN_MILLISECONDS);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_instance_zulgurub()
|
||||
{
|
||||
new instance_zulgurub();
|
||||
RegisterGameObjectAI(go_brazier_of_madness);
|
||||
}
|
||||
|
||||
@@ -49,6 +49,8 @@ enum CreatureIds
|
||||
NPC_ZULIAN_PROWLER = 15101, // Arlokk Event
|
||||
NPC_ZEALOT_LORKHAN = 11347,
|
||||
NPC_ZEALOT_ZATH = 11348,
|
||||
NPC_PRIESTESS_MARLI = 14510,
|
||||
NPC_SPAWN_OF_MARLI = 15041,
|
||||
NPC_HIGH_PRIEST_THEKAL = 14509,
|
||||
NPC_JINDO_THE_HEXXER = 11380,
|
||||
NPC_NIGHTMARE_ILLUSION = 15163,
|
||||
@@ -59,7 +61,13 @@ enum CreatureIds
|
||||
NPC_VILEBRANCH_SPEAKER = 11391, // Mandokir Event
|
||||
NPC_CHAINED_SPIRIT = 15117, // Mandokir Event
|
||||
NPC_HAKKAR = 14834,
|
||||
NPC_ZULGURUB_TIGER = 11361
|
||||
NPC_ZULGURUB_TIGER = 11361,
|
||||
NPC_BRAIN_WASH_TOTEM = 15112,
|
||||
NPC_GAHZRANKA = 15114,
|
||||
NPC_GRILEK = 15082,
|
||||
NPC_HAZZARAH = 15083,
|
||||
NPC_RENATAKI = 15084,
|
||||
NPC_WUSHOOLAY = 15085
|
||||
};
|
||||
|
||||
enum GameobjectIds
|
||||
|
||||
@@ -25,38 +25,9 @@ EndScriptData */
|
||||
#include "Player.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "TaskScheduler.h"
|
||||
#include "temple_of_ahnqiraj.h"
|
||||
|
||||
/*
|
||||
* This is a 2 phases events. Here follows an explanation of the main events and transition between phases and sub-phases.
|
||||
*
|
||||
* The first phase is the EYE phase: the Eye of C'Thun is active and C'thun is not active.
|
||||
* During this phase, the "Eye of C'Thun" alternates between 2 sub-phases:
|
||||
* - PHASE_EYE_GREEN_BEAM:
|
||||
* 50 sec phase during which the Eye mainly casts its Green Beam every 3 sec.
|
||||
* - PHASE_EYE_RED_BEAM:
|
||||
* 35 sec phase during which the Eye casts its red beam every sec.
|
||||
* This EYE phase ends when the "Eye of C'Thun" is killed. Then starts the CTHUN phase.
|
||||
*
|
||||
* The second phase is the CTHUN phase. The Eye of C'Thun is not active and C'Thun is active.
|
||||
* This phase starts with the transformation of the Eye into C'Thun (PHASE_CTHUN_TRANSITION).
|
||||
* After the transformation, C'Thun alternates between 2 sub-phases:
|
||||
* - PHASE_CTHUN_STOMACH:
|
||||
* - C'Thun is almost insensible to all damage (99% damage reduction).
|
||||
* - It spawns 2 tentacles in its stomach.
|
||||
* - C'Thun swallows players.
|
||||
* - This sub-phase ends when the 2 tentacles are killed. Swallowed players are regurgitate.
|
||||
*
|
||||
* - PHASE_CTHUN_WEAK:
|
||||
* - weakened C'Thun takes normal damage.
|
||||
* - This sub-phase ends after 45 secs.
|
||||
*
|
||||
* This CTHUN phase ends when C'Thun is killed
|
||||
*
|
||||
* Note:
|
||||
* - the current phase is stored in the instance data to be easily shared between the eye and cthun.
|
||||
*/
|
||||
|
||||
enum Phases
|
||||
{
|
||||
PHASE_NOT_STARTED = 0,
|
||||
@@ -99,7 +70,7 @@ enum Spells
|
||||
//SAME AS PHASE1
|
||||
|
||||
//Giant Claw Tentacles
|
||||
SPELL_MASSIVE_GROUND_RUPTURE = 26100,
|
||||
SPELL_MASSIVE_GROUND_RUPTURE = 26478,
|
||||
|
||||
//Also casts Hamstring
|
||||
SPELL_THRASH = 3391,
|
||||
@@ -111,6 +82,10 @@ enum Spells
|
||||
SPELL_MOUTH_TENTACLE = 26332,
|
||||
SPELL_EXIT_STOMACH_KNOCKBACK = 25383,
|
||||
SPELL_DIGESTIVE_ACID = 26476,
|
||||
|
||||
// Tentacles
|
||||
SPELL_SUBMERGE_VISUAL = 26234,
|
||||
SPELL_BIRTH = 26262
|
||||
};
|
||||
|
||||
enum Actions
|
||||
@@ -142,6 +117,20 @@ const Position FleshTentaclePos[2] =
|
||||
{ -8525.0f, 1994.0f, -98.0f, 2.12f},
|
||||
};
|
||||
|
||||
class NotInStomachSelector
|
||||
{
|
||||
public:
|
||||
NotInStomachSelector() { }
|
||||
|
||||
bool operator()(Unit* unit) const
|
||||
{
|
||||
if (unit->GetTypeId() != TYPEID_PLAYER || unit->HasAura(SPELL_DIGESTIVE_ACID))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
//Kick out position
|
||||
const Position KickPos = { -8545.0f, 1984.0f, -96.0f, 0.0f};
|
||||
|
||||
@@ -917,32 +906,40 @@ public:
|
||||
{
|
||||
eye_tentacleAI(Creature* creature) : ScriptedAI(creature)
|
||||
{
|
||||
if (Creature* pPortal = me->SummonCreature(NPC_SMALL_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN))
|
||||
if (Creature* portal = me->SummonCreature(NPC_SMALL_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN))
|
||||
{
|
||||
pPortal->SetReactState(REACT_PASSIVE);
|
||||
Portal = pPortal->GetGUID();
|
||||
portal->SetReactState(REACT_PASSIVE);
|
||||
_portalGUID = portal->GetGUID();
|
||||
}
|
||||
|
||||
SetCombatMovement(false);
|
||||
}
|
||||
|
||||
uint32 MindflayTimer;
|
||||
uint32 KillSelfTimer;
|
||||
ObjectGuid Portal;
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
if (Unit* p = ObjectAccessor::GetUnit(*me, Portal))
|
||||
if (Unit* p = ObjectAccessor::GetUnit(*me, _portalGUID))
|
||||
{
|
||||
Unit::Kill(p, p);
|
||||
}
|
||||
}
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
//Mind flay half a second after we spawn
|
||||
MindflayTimer = 500;
|
||||
_scheduler.Schedule(500ms, [this](TaskContext /*task*/)
|
||||
{
|
||||
DoCastAOE(SPELL_GROUND_RUPTURE);
|
||||
}).Schedule(5min, [this](TaskContext /*task*/)
|
||||
{
|
||||
me->DespawnOrUnsummon();
|
||||
}).Schedule(1s, 5s, [this](TaskContext context)
|
||||
{
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, [&](Unit* u) { return u && u->GetTypeId() == TYPEID_PLAYER && !u->HasAura(SPELL_DIGESTIVE_ACID) && !u->HasAura(SPELL_MIND_FLAY); }))
|
||||
{
|
||||
DoCast(target, SPELL_MIND_FLAY);
|
||||
}
|
||||
|
||||
//This prevents eyes from overlapping
|
||||
KillSelfTimer = 35000;
|
||||
context.Repeat(10s, 15s);
|
||||
});
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* /*who*/) override
|
||||
@@ -956,26 +953,12 @@ public:
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
//KillSelfTimer
|
||||
if (KillSelfTimer <= diff)
|
||||
{
|
||||
Unit::Kill(me, me);
|
||||
return;
|
||||
}
|
||||
else KillSelfTimer -= diff;
|
||||
|
||||
//MindflayTimer
|
||||
if (MindflayTimer <= diff)
|
||||
{
|
||||
Unit* target = SelectTarget(SelectTargetMethod::Random, 0);
|
||||
if (target && !target->HasAura(SPELL_DIGESTIVE_ACID))
|
||||
DoCast(target, SPELL_MIND_FLAY);
|
||||
|
||||
//Mindflay every 10 seconds
|
||||
MindflayTimer = 10000;
|
||||
}
|
||||
else MindflayTimer -= diff;
|
||||
_scheduler.Update(diff);
|
||||
}
|
||||
|
||||
private:
|
||||
TaskScheduler _scheduler;
|
||||
ObjectGuid _portalGUID;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -995,35 +978,41 @@ public:
|
||||
{
|
||||
SetCombatMovement(false);
|
||||
|
||||
if (Creature* pPortal = me->SummonCreature(NPC_SMALL_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN))
|
||||
if (Creature* portal = me->SummonCreature(NPC_SMALL_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN))
|
||||
{
|
||||
pPortal->SetReactState(REACT_PASSIVE);
|
||||
Portal = pPortal->GetGUID();
|
||||
portal->SetReactState(REACT_PASSIVE);
|
||||
_portalGUID = portal->GetGUID();
|
||||
}
|
||||
}
|
||||
|
||||
uint32 GroundRuptureTimer;
|
||||
uint32 HamstringTimer;
|
||||
uint32 EvadeTimer;
|
||||
ObjectGuid Portal;
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
if (Unit* p = ObjectAccessor::GetUnit(*me, Portal))
|
||||
if (Unit* p = ObjectAccessor::GetUnit(*me, _portalGUID))
|
||||
{
|
||||
Unit::Kill(p, p);
|
||||
}
|
||||
}
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
//First rupture should happen half a second after we spawn
|
||||
GroundRuptureTimer = 500;
|
||||
HamstringTimer = 2000;
|
||||
EvadeTimer = 5000;
|
||||
_scheduler.Schedule(Milliseconds(500), [this](TaskContext /*task*/)
|
||||
{
|
||||
DoCastAOE(SPELL_GROUND_RUPTURE);
|
||||
}).Schedule(Minutes(5), [this](TaskContext /*task*/)
|
||||
{
|
||||
me->DespawnOrUnsummon();
|
||||
});
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* /*who*/) override
|
||||
{
|
||||
DoZoneInCombat();
|
||||
|
||||
_scheduler.Schedule(2s, [this](TaskContext context)
|
||||
{
|
||||
DoCastVictim(SPELL_HAMSTRING);
|
||||
context.Repeat(5s);
|
||||
});
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
@@ -1032,62 +1021,14 @@ public:
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
//EvadeTimer
|
||||
if (!me->IsWithinMeleeRange(me->GetVictim()))
|
||||
{
|
||||
if (EvadeTimer <= diff)
|
||||
{
|
||||
if (Unit* p = ObjectAccessor::GetUnit(*me, Portal))
|
||||
Unit::Kill(p, p);
|
||||
|
||||
//Dissapear and reappear at new position
|
||||
me->SetVisible(false);
|
||||
|
||||
Unit* target = SelectTarget(SelectTargetMethod::Random, 0);
|
||||
if (!target)
|
||||
{
|
||||
Unit::Kill(me, me);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!target->HasAura(SPELL_DIGESTIVE_ACID))
|
||||
{
|
||||
me->SetPosition(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0);
|
||||
if (Creature* pPortal = me->SummonCreature(NPC_SMALL_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN))
|
||||
{
|
||||
pPortal->SetReactState(REACT_PASSIVE);
|
||||
Portal = pPortal->GetGUID();
|
||||
}
|
||||
|
||||
GroundRuptureTimer = 500;
|
||||
HamstringTimer = 2000;
|
||||
EvadeTimer = 5000;
|
||||
AttackStart(target);
|
||||
}
|
||||
|
||||
me->SetVisible(true);
|
||||
}
|
||||
else EvadeTimer -= diff;
|
||||
}
|
||||
|
||||
//GroundRuptureTimer
|
||||
if (GroundRuptureTimer <= diff)
|
||||
{
|
||||
DoCastVictim(SPELL_GROUND_RUPTURE);
|
||||
GroundRuptureTimer = 30000;
|
||||
}
|
||||
else GroundRuptureTimer -= diff;
|
||||
|
||||
//HamstringTimer
|
||||
if (HamstringTimer <= diff)
|
||||
{
|
||||
DoCastVictim(SPELL_HAMSTRING);
|
||||
HamstringTimer = 5000;
|
||||
}
|
||||
else HamstringTimer -= diff;
|
||||
_scheduler.Update(diff);
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
|
||||
private:
|
||||
TaskScheduler _scheduler;
|
||||
ObjectGuid _portalGUID;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1107,37 +1048,112 @@ public:
|
||||
{
|
||||
SetCombatMovement(false);
|
||||
|
||||
if (Creature* pPortal = me->SummonCreature(NPC_GIANT_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN))
|
||||
if (Creature* portal = me->SummonCreature(NPC_GIANT_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN))
|
||||
{
|
||||
pPortal->SetReactState(REACT_PASSIVE);
|
||||
Portal = pPortal->GetGUID();
|
||||
portal->SetReactState(REACT_PASSIVE);
|
||||
_portalGUID = portal->GetGUID();
|
||||
}
|
||||
}
|
||||
|
||||
uint32 GroundRuptureTimer;
|
||||
uint32 ThrashTimer;
|
||||
uint32 HamstringTimer;
|
||||
uint32 EvadeTimer;
|
||||
ObjectGuid Portal;
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
if (Unit* p = ObjectAccessor::GetUnit(*me, Portal))
|
||||
if (Unit* p = ObjectAccessor::GetUnit(*me, _portalGUID))
|
||||
{
|
||||
Unit::Kill(p, p);
|
||||
}
|
||||
}
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
//First rupture should happen half a second after we spawn
|
||||
GroundRuptureTimer = 500;
|
||||
HamstringTimer = 2000;
|
||||
ThrashTimer = 5000;
|
||||
EvadeTimer = 5000;
|
||||
_scheduler.Schedule(500ms, [this](TaskContext /*task*/)
|
||||
{
|
||||
DoCastAOE(SPELL_MASSIVE_GROUND_RUPTURE);
|
||||
});
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* /*who*/) override
|
||||
{
|
||||
DoZoneInCombat();
|
||||
|
||||
_scheduler.Schedule(2s, [this](TaskContext context)
|
||||
{
|
||||
DoCastVictim(SPELL_HAMSTRING);
|
||||
context.Repeat(10s);
|
||||
}).Schedule(5s, [this](TaskContext context) {
|
||||
DoCastSelf(SPELL_THRASH);
|
||||
context.Repeat(10s);
|
||||
});
|
||||
}
|
||||
|
||||
void ScheduleMeleeCheck()
|
||||
{
|
||||
// Check if a target is in melee range
|
||||
_scheduler.Schedule(10s, [this](TaskContext task)
|
||||
{
|
||||
if (Unit* target = me->GetVictim())
|
||||
{
|
||||
if (!target->IsWithinMeleeRange(me))
|
||||
{
|
||||
// Main target not found within melee range, try to select a new one
|
||||
if (Player* newTarget = me->SelectNearestPlayer(5.0f))
|
||||
{
|
||||
AttackStart(newTarget);
|
||||
}
|
||||
else // Main target not found, and failed to acquire a new target... Submerge
|
||||
{
|
||||
Submerge();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task.Repeat();
|
||||
});
|
||||
}
|
||||
|
||||
void Submerge()
|
||||
{
|
||||
if (me->SelectNearestPlayer(5.0f))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Despawn portal
|
||||
if (Creature* p = ObjectAccessor::GetCreature(*me, _portalGUID))
|
||||
{
|
||||
p->DespawnOrUnsummon();
|
||||
}
|
||||
|
||||
DoCastSelf(SPELL_SUBMERGE_VISUAL);
|
||||
me->SetHealth(me->GetMaxHealth());
|
||||
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE);
|
||||
|
||||
_scheduler.CancelAll();
|
||||
|
||||
_scheduler.Schedule(5s, [this](TaskContext /*task*/)
|
||||
{
|
||||
Emerge();
|
||||
});
|
||||
}
|
||||
|
||||
void Emerge()
|
||||
{
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, NotInStomachSelector()))
|
||||
{
|
||||
Position pos = target->GetPosition();
|
||||
me->NearTeleportTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), 0);
|
||||
if (Creature* portal = me->SummonCreature(NPC_GIANT_PORTAL, pos, TEMPSUMMON_CORPSE_DESPAWN))
|
||||
{
|
||||
portal->SetReactState(REACT_PASSIVE);
|
||||
_portalGUID = portal->GetGUID();
|
||||
}
|
||||
|
||||
me->RemoveAurasDueToSpell(SPELL_SUBMERGE_VISUAL);
|
||||
DoCastSelf(SPELL_BIRTH);
|
||||
DoCastAOE(SPELL_MASSIVE_GROUND_RUPTURE, true);
|
||||
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE);
|
||||
|
||||
ScheduleMeleeCheck();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
@@ -1146,70 +1162,14 @@ public:
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
//EvadeTimer
|
||||
if (!me->IsWithinMeleeRange(me->GetVictim()))
|
||||
{
|
||||
if (EvadeTimer <= diff)
|
||||
{
|
||||
if (Unit* p = ObjectAccessor::GetUnit(*me, Portal))
|
||||
Unit::Kill(p, p);
|
||||
|
||||
//Dissapear and reappear at new position
|
||||
me->SetVisible(false);
|
||||
|
||||
Unit* target = SelectTarget(SelectTargetMethod::Random, 0);
|
||||
if (!target)
|
||||
{
|
||||
Unit::Kill(me, me);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!target->HasAura(SPELL_DIGESTIVE_ACID))
|
||||
{
|
||||
me->SetPosition(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0);
|
||||
if (Creature* pPortal = me->SummonCreature(NPC_GIANT_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN))
|
||||
{
|
||||
pPortal->SetReactState(REACT_PASSIVE);
|
||||
Portal = pPortal->GetGUID();
|
||||
}
|
||||
|
||||
GroundRuptureTimer = 500;
|
||||
HamstringTimer = 2000;
|
||||
ThrashTimer = 5000;
|
||||
EvadeTimer = 5000;
|
||||
AttackStart(target);
|
||||
}
|
||||
me->SetVisible(true);
|
||||
}
|
||||
else EvadeTimer -= diff;
|
||||
}
|
||||
|
||||
//GroundRuptureTimer
|
||||
if (GroundRuptureTimer <= diff)
|
||||
{
|
||||
DoCastVictim(SPELL_GROUND_RUPTURE);
|
||||
GroundRuptureTimer = 30000;
|
||||
}
|
||||
else GroundRuptureTimer -= diff;
|
||||
|
||||
//ThrashTimer
|
||||
if (ThrashTimer <= diff)
|
||||
{
|
||||
DoCastVictim(SPELL_THRASH);
|
||||
ThrashTimer = 10000;
|
||||
}
|
||||
else ThrashTimer -= diff;
|
||||
|
||||
//HamstringTimer
|
||||
if (HamstringTimer <= diff)
|
||||
{
|
||||
DoCastVictim(SPELL_HAMSTRING);
|
||||
HamstringTimer = 10000;
|
||||
}
|
||||
else HamstringTimer -= diff;
|
||||
_scheduler.Update(diff);
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
|
||||
private:
|
||||
TaskScheduler _scheduler;
|
||||
ObjectGuid _portalGUID;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1229,26 +1189,34 @@ public:
|
||||
{
|
||||
SetCombatMovement(false);
|
||||
|
||||
if (Creature* pPortal = me->SummonCreature(NPC_GIANT_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN))
|
||||
if (Creature* portal = me->SummonCreature(NPC_GIANT_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN))
|
||||
{
|
||||
pPortal->SetReactState(REACT_PASSIVE);
|
||||
Portal = pPortal->GetGUID();
|
||||
portal->SetReactState(REACT_PASSIVE);
|
||||
_portalGUID = portal->GetGUID();
|
||||
}
|
||||
}
|
||||
|
||||
uint32 BeamTimer;
|
||||
ObjectGuid Portal;
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
if (Unit* p = ObjectAccessor::GetUnit(*me, Portal))
|
||||
if (Unit* p = ObjectAccessor::GetUnit(*me, _portalGUID))
|
||||
{
|
||||
Unit::Kill(p, p);
|
||||
}
|
||||
}
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
//Green Beam half a second after we spawn
|
||||
BeamTimer = 500;
|
||||
_scheduler.Schedule(500ms, [this](TaskContext /*task*/)
|
||||
{
|
||||
DoCastAOE(SPELL_MASSIVE_GROUND_RUPTURE);
|
||||
}).Schedule(1s, 5s, [this](TaskContext context) {
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, -SPELL_DIGESTIVE_ACID))
|
||||
{
|
||||
DoCast(target, SPELL_GREEN_BEAM);
|
||||
}
|
||||
|
||||
context.Repeat(2100ms);
|
||||
});
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* /*who*/) override
|
||||
@@ -1262,18 +1230,12 @@ public:
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
//BeamTimer
|
||||
if (BeamTimer <= diff)
|
||||
{
|
||||
Unit* target = SelectTarget(SelectTargetMethod::Random, 0);
|
||||
if (target && !target->HasAura(SPELL_DIGESTIVE_ACID))
|
||||
DoCast(target, SPELL_GREEN_BEAM);
|
||||
|
||||
//Beam every 2 seconds
|
||||
BeamTimer = 2100;
|
||||
}
|
||||
else BeamTimer -= diff;
|
||||
_scheduler.Update(diff);
|
||||
}
|
||||
|
||||
private:
|
||||
TaskScheduler _scheduler;
|
||||
ObjectGuid _portalGUID;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -69,8 +69,6 @@ public:
|
||||
{
|
||||
if (p->GetZoneId() == me->GetZoneId())
|
||||
{
|
||||
|
||||
p->RemoveAurasDueToSpell(SPELL_AURA_OF_FROST);
|
||||
p->RemoveAurasDueToSpell(SPELL_CHILL);
|
||||
p->RemoveAurasDueToSpell(SPELL_FROST_BREATH);
|
||||
}
|
||||
|
||||
@@ -738,48 +738,6 @@ class spell_gen_no_offhand_proc : public AuraScript
|
||||
}
|
||||
};
|
||||
|
||||
/* 71602 - Item - Icecrown 25 Normal Caster Trinket 1 Base
|
||||
71645 - Item - Icecrown 25 Heroic Caster Trinket 1 Base
|
||||
71845 - Item - Icecrown 25 Normal Caster Weapon Proc
|
||||
71846 - Item - Icecrown 25 Heroic Caster Weapon Proc
|
||||
72419 - Item - Icecrown Reputation Ring Healer Trigger
|
||||
75465 - Item - Chamber of Aspects 25 Nuker Trinket
|
||||
75474 - Item - Chamber of Aspects 25 Heroic Nuker Trinket */
|
||||
class spell_gen_proc_once_per_cast : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_gen_proc_once_per_cast);
|
||||
|
||||
bool Load() override
|
||||
{
|
||||
_spellPointer = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CheckProc(ProcEventInfo& eventInfo)
|
||||
{
|
||||
if (eventInfo.GetActor())
|
||||
{
|
||||
if (Player* player = eventInfo.GetActor()->ToPlayer())
|
||||
{
|
||||
if (player->m_spellModTakingSpell == _spellPointer)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_spellPointer = player->m_spellModTakingSpell;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
DoCheckProc += AuraCheckProcFn(spell_gen_proc_once_per_cast::CheckProc);
|
||||
}
|
||||
|
||||
private:
|
||||
Spell* _spellPointer;
|
||||
};
|
||||
|
||||
// 70805 - Item - Rogue T10 2P Bonus
|
||||
class spell_gen_proc_on_self : public AuraScript
|
||||
{
|
||||
@@ -4545,7 +4503,6 @@ void AddSC_generic_spell_scripts()
|
||||
RegisterSpellScript(spell_gen_use_spell_base_level_check);
|
||||
RegisterSpellScript(spell_gen_proc_from_direct_damage);
|
||||
RegisterSpellScript(spell_gen_no_offhand_proc);
|
||||
RegisterSpellScript(spell_gen_proc_once_per_cast);
|
||||
RegisterSpellScript(spell_gen_proc_on_self);
|
||||
RegisterSpellScript(spell_gen_proc_not_self);
|
||||
RegisterSpellScript(spell_gen_baby_murloc_passive);
|
||||
|
||||
@@ -1208,6 +1208,27 @@ class spell_warl_drain_soul : public AuraScript
|
||||
}
|
||||
};
|
||||
|
||||
// 29341 - Shadowburn
|
||||
class spell_warl_shadowburn : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_warl_shadowburn);
|
||||
|
||||
void RemoveEffect(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
|
||||
{
|
||||
Unit* caster = GetCaster();
|
||||
Unit* target = GetTarget();
|
||||
if (!(GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_DEATH && caster && target && caster->IsPlayer() && caster->ToPlayer()->isHonorOrXPTarget(target)))
|
||||
{
|
||||
PreventDefaultAction();
|
||||
}
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectRemove += AuraEffectRemoveFn(spell_warl_shadowburn::RemoveEffect, EFFECT_0, SPELL_AURA_CHANNEL_DEATH_ITEM, AURA_EFFECT_HANDLE_REAL);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_warlock_spell_scripts()
|
||||
{
|
||||
RegisterSpellScript(spell_warl_eye_of_kilrogg);
|
||||
@@ -1238,4 +1259,5 @@ void AddSC_warlock_spell_scripts()
|
||||
RegisterSpellScript(spell_warl_soulshatter);
|
||||
RegisterSpellScript(spell_warl_unstable_affliction);
|
||||
RegisterSpellScript(spell_warl_drain_soul);
|
||||
RegisterSpellScript(spell_warl_shadowburn);
|
||||
}
|
||||
|
||||
@@ -302,9 +302,6 @@ public:
|
||||
* ---
|
||||
* --- Dragonspecific scripts and handling: LETHON
|
||||
* ---
|
||||
*
|
||||
* @todo
|
||||
* - Spell: Shadow bolt whirl casts needs custom handling (spellscript)
|
||||
*/
|
||||
|
||||
enum LethonTexts
|
||||
@@ -318,6 +315,14 @@ enum LethonSpells
|
||||
SPELL_DRAW_SPIRIT = 24811,
|
||||
SPELL_SHADOW_BOLT_WHIRL = 24834,
|
||||
SPELL_DARK_OFFERING = 24804,
|
||||
SPELL_SHADOW_BOLT_WHIRL1 = 24820,
|
||||
SPELL_SHADOW_BOLT_WHIRL2 = 24821,
|
||||
SPELL_SHADOW_BOLT_WHIRL3 = 24822,
|
||||
SPELL_SHADOW_BOLT_WHIRL4 = 24823,
|
||||
SPELL_SHADOW_BOLT_WHIRL5 = 24835,
|
||||
SPELL_SHADOW_BOLT_WHIRL6 = 24836,
|
||||
SPELL_SHADOW_BOLT_WHIRL7 = 24837,
|
||||
SPELL_SHADOW_BOLT_WHIRL8 = 24838,
|
||||
};
|
||||
|
||||
enum LethonCreatures
|
||||
@@ -340,13 +345,14 @@ public:
|
||||
{
|
||||
_stage = 1;
|
||||
emerald_dragonAI::Reset();
|
||||
events.ScheduleEvent(EVENT_SHADOW_BOLT_WHIRL, 10000);
|
||||
me->RemoveAurasDueToSpell(SPELL_SHADOW_BOLT_WHIRL);
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* who) override
|
||||
{
|
||||
Talk(SAY_LETHON_AGGRO);
|
||||
WorldBossAI::EnterCombat(who);
|
||||
DoCastSelf(SPELL_SHADOW_BOLT_WHIRL, true);
|
||||
}
|
||||
|
||||
void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override
|
||||
@@ -368,20 +374,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void ExecuteEvent(uint32 eventId) override
|
||||
{
|
||||
switch (eventId)
|
||||
{
|
||||
case EVENT_SHADOW_BOLT_WHIRL:
|
||||
me->CastSpell((Unit*)nullptr, SPELL_SHADOW_BOLT_WHIRL, false);
|
||||
events.ScheduleEvent(EVENT_SHADOW_BOLT_WHIRL, urand(15000, 30000));
|
||||
break;
|
||||
default:
|
||||
emerald_dragonAI::ExecuteEvent(eventId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
uint8 _stage;
|
||||
};
|
||||
@@ -712,6 +704,41 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class spell_shadow_bolt_whirl : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_shadow_bolt_whirl);
|
||||
|
||||
bool Validate(SpellInfo const* /*spellInfo*/) override
|
||||
{
|
||||
return ValidateSpellInfo({ SPELL_SHADOW_BOLT_WHIRL1, SPELL_SHADOW_BOLT_WHIRL2, SPELL_SHADOW_BOLT_WHIRL3, SPELL_SHADOW_BOLT_WHIRL4, SPELL_SHADOW_BOLT_WHIRL5, SPELL_SHADOW_BOLT_WHIRL6, SPELL_SHADOW_BOLT_WHIRL7, SPELL_SHADOW_BOLT_WHIRL8 });
|
||||
}
|
||||
|
||||
void HandlePeriodic(AuraEffect const* aurEff)
|
||||
{
|
||||
Unit* caster = GetCaster();
|
||||
Unit* target = GetTarget();
|
||||
|
||||
if (!caster || !target)
|
||||
return;
|
||||
std::array<uint32, 8> spellForTick = { SPELL_SHADOW_BOLT_WHIRL1, SPELL_SHADOW_BOLT_WHIRL2, SPELL_SHADOW_BOLT_WHIRL3, SPELL_SHADOW_BOLT_WHIRL4, SPELL_SHADOW_BOLT_WHIRL5, SPELL_SHADOW_BOLT_WHIRL6, SPELL_SHADOW_BOLT_WHIRL7, SPELL_SHADOW_BOLT_WHIRL8 };
|
||||
uint32 tick = (aurEff->GetTickNumber() + 7/*-1*/) % 8;
|
||||
|
||||
// casted in left/right (but triggered spell have wide forward cone)
|
||||
float forward = target->GetOrientation();
|
||||
if (tick <= 3)
|
||||
target->SetOrientation(forward + 0.75f * M_PI - tick * M_PI / 8); // Left
|
||||
else
|
||||
target->SetOrientation(forward - 0.75f * M_PI + (8 - tick) * M_PI / 8); // Right
|
||||
|
||||
target->CastSpell(target, spellForTick[tick], true);
|
||||
target->SetOrientation(forward);
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectPeriodic += AuraEffectPeriodicFn(spell_shadow_bolt_whirl::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
|
||||
}
|
||||
};
|
||||
class spell_mark_of_nature : public SpellScriptLoader
|
||||
{
|
||||
public:
|
||||
@@ -765,4 +792,5 @@ void AddSC_emerald_dragons()
|
||||
// dragon spellscripts
|
||||
new spell_dream_fog_sleep();
|
||||
new spell_mark_of_nature();
|
||||
RegisterSpellScript(spell_shadow_bolt_whirl);
|
||||
};
|
||||
|
||||
@@ -1015,6 +1015,13 @@ struct FactionTemplateEntry
|
||||
[[nodiscard]] bool IsContestedGuardFaction() const { return (factionFlags & FACTION_TEMPLATE_FLAG_ATTACK_PVP_ACTIVE_PLAYERS) != 0; }
|
||||
};
|
||||
|
||||
struct GameObjectArtKitEntry
|
||||
{
|
||||
uint32 ID; // 0
|
||||
//char* TextureVariation[3] // 1-3 m_textureVariations[3]
|
||||
//char* AttachModel[4] // 4-8 m_attachModels[4]
|
||||
};
|
||||
|
||||
struct GameObjectDisplayInfoEntry
|
||||
{
|
||||
uint32 Displayid; // 0 m_ID
|
||||
|
||||
@@ -52,6 +52,7 @@ char constexpr EmotesTextEntryfmt[] = "nxixxxxxxxxxxxxxxxx";
|
||||
char constexpr EmotesTextSoundEntryfmt[] = "niiii";
|
||||
char constexpr FactionEntryfmt[] = "niiiiiiiiiiiiiiiiiiffixssssssssssssssssxxxxxxxxxxxxxxxxxx";
|
||||
char constexpr FactionTemplateEntryfmt[] = "niiiiiiiiiiiii";
|
||||
char constexpr GameObjectArtKitfmt[] = "nxxxxxxx";
|
||||
char constexpr GameObjectDisplayInfofmt[] = "nsxxxxxxxxxxffffffx";
|
||||
char constexpr GemPropertiesEntryfmt[] = "nixxi";
|
||||
char constexpr GlyphPropertiesfmt[] = "niii";
|
||||
|
||||
Reference in New Issue
Block a user