Core/Vehicles: expand vehicle seat addon table implementation to support passenger exit options

This commit is contained in:
Ovahlord committed 2020-01-28 11:09:05 +01:00
1 parent f336d6032b
commit dfaa49c87b
8 files changed
+455 -412

No files matched your search

@@ -0,0 +1,26 @@
DROP TABLE IF EXISTS `vehicle_seat_addon`;
CREATE TABLE `vehicle_seat_addon` (
`SeatEntry` INT(4) UNSIGNED NOT NULL COMMENT 'VehicleSeatEntry.dbc identifier',
`SeatOrientation` FLOAT(6) DEFAULT 0 COMMENT 'Seat Orientation override value',
`ExitParamX` FLOAT(10) DEFAULT 0,
`ExitParamY` FLOAT(10) DEFAULT 0,
`ExitParamZ` FLOAT(10) DEFAULT 0,
`ExitParamO` FLOAT(10) DEFAULT 0,
`ExitParamValue` TINYINT(1) DEFAULT 0,
PRIMARY KEY (`SeatEntry`)
);
INSERT INTO `vehicle_seat_addon` (`SeatEntry`, `SeatOrientation`, `ExitParamX`, `ExitParamY`, `ExitParamZ`, `ExitParamO`, `ExitParamValue`) VALUES
(8372, 3.141592, 0, 0, 0, 0, 0), -- Ozumat
(8426, 3.141592, 0, 0, 0, 0, 0), -- Ozumat
(8450, 3.141592, 0, 0, 0, 0, 0), -- Ozumat
(8451, 3.141592, 0, 0, 0, 0, 0), -- Ozumat
(8453, 3.141592, 0, 0, 0, 0, 0), -- Ozumat
(8030, 0, -9618.6875, -1030.1104, 41.684166, 4.847490787, 2), -- Stormwind Charger Seat 1
(7936, 0, -311.4653, -48.59722, 212.8065, 1.064651, 2), -- Magmaw Seat 3
(8104, 0, -311.4653, -48.59722, 212.8065, 1.064651, 2), -- Magmaw' Pincer Seat 1
(7909, 0, -311.4653, -48.59722, 212.8065, 1.064651, 2), -- Magmaw' Pincer Seat 1
(2764, 0, -2, 2, 0, 0, 1), -- Traveler's Tundra Mammoth Seat 2
(2765, 0, -2, -2, 0, 0, 1), -- Traveler's Tundra Mammoth Seat 3
(2767, 0, -2, 2, 0, 0, 1), -- Traveler's Tundra Mammoth Seat 2
(2768, 0, -2, -2, 0, 0, 1); -- Traveler's Tundra Mammoth Seat 3
+7 -2
View File
@@ -13465,8 +13465,8 @@ void Unit::_ExitVehicle(Position const* exitPosition)
return;
// This should be done before dismiss, because there may be some aura removal
VehicleSeatAddon const seatAddon = m_vehicle->GetSeatAddonForSeatOfPassenger(this);
Vehicle* vehicle = m_vehicle->RemovePassenger(this);
Player* player = ToPlayer();
// If the player is on mounted duel and exits the mount, he should immediatly lose the duel
@@ -13489,7 +13489,12 @@ void Unit::_ExitVehicle(Position const* exitPosition)
// Set exit position to vehicle position and use the current orientation
pos = vehicle->GetBase()->GetPosition();
pos.SetOrientation(GetOrientation());
sScriptMgr->ModifyVehiclePassengerExitPos(this, vehicle, pos);
// Change exit position based on seat entry
if (seatAddon.ExitParameter == VehicleExitParameters::VehicleExitParamOffset)
pos.RelocateOffset({ seatAddon.ExitParameterX, seatAddon.ExitParameterY, seatAddon.ExitParameterZ, seatAddon.ExitParameterO });
else if (seatAddon.ExitParameter == VehicleExitParameters::VehicleExitParamDest)
pos.Relocate({ seatAddon.ExitParameterX, seatAddon.ExitParameterY, seatAddon.ExitParameterZ, seatAddon.ExitParameterO });
}
float height = pos.GetPositionZ() + vehicle->GetBase()->GetCollisionHeight();
+15 -3
View File
@@ -41,7 +41,8 @@ UsableSeatNum(0), _me(unit), _vehicleInfo(vehInfo), _creatureEntry(creatureEntry
if (uint32 seatId = _vehicleInfo->SeatID[i])
if (VehicleSeatEntry const* veSeat = sVehicleSeatStore.LookupEntry(seatId))
{
Seats.insert(std::make_pair(i, VehicleSeat(veSeat)));
VehicleSeatAddon addon = sObjectMgr->GetVehicleSeatAddon(seatId);
Seats.insert(std::make_pair(i, VehicleSeat(veSeat, addon)));
if (veSeat->CanEnterOrExit())
++UsableSeatNum;
}
@@ -395,6 +396,17 @@ SeatMap::const_iterator Vehicle::GetNextEmptySeat(int8 seatId, bool next) const
return seat;
}
VehicleSeatAddon const Vehicle::GetSeatAddonForSeatOfPassenger(Unit const* passenger) const
{
for (SeatMap::const_iterator itr = Seats.begin(); itr != Seats.end(); itr++)
{
if (!itr->second.IsEmpty() && itr->second.Passenger.Guid == passenger->GetGUID())
return itr->second.SeatAddon;
}
return VehicleSeatAddon();
}
/**
* @fn void Vehicle::InstallAccessory(uint32 entry, int8 seatId, bool minion, uint8 type,
* uint32 summonTime)
@@ -861,6 +873,7 @@ bool VehicleJoinEvent::Execute(uint64, uint32)
Passenger->RemoveAurasByType(SPELL_AURA_MOUNTED);
VehicleSeatEntry const* veSeat = Seat->second.SeatInfo;
VehicleSeatAddon const veSeatAddon = Seat->second.SeatAddon;
Player* player = Passenger->ToPlayer();
if (player)
@@ -882,8 +895,7 @@ bool VehicleJoinEvent::Execute(uint64, uint32)
if (veSeat->HasFlag(VEHICLE_SEAT_FLAG_PASSENGER_NOT_SELECTABLE))
Passenger->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
float o = sObjectMgr->GetVehicleSeatOrientationOffset(veSeat->ID);
float o = veSeatAddon.SeatOrientationOffset;
float x = veSeat->AttachmentOffset.X;
float y = veSeat->AttachmentOffset.Y;
float z = veSeat->AttachmentOffset.Z;
+1 -1
View File
@@ -52,10 +52,10 @@ class TC_GAME_API Vehicle : public TransportBase
bool HasEmptySeat(int8 seatId) const;
Unit* GetPassenger(int8 seatId) const;
SeatMap::const_iterator GetNextEmptySeat(int8 seatId, bool next) const;
VehicleSeatAddon const GetSeatAddonForSeatOfPassenger(Unit const* passenger) const;
uint8 GetAvailableSeatCount() const;
bool AddPassenger(Unit* passenger, int8 seatId = -1);
void EjectPassenger(Unit* passenger, Unit* controller);
Vehicle* RemovePassenger(Unit* passenger);
void RelocatePassengers();
void RemoveAllPassengers();
@@ -75,6 +75,14 @@ enum VehicleSpells
VEHICLE_SPELL_PARACHUTE = 45472
};
enum class VehicleExitParameters
{
VehicleExitParamNone, // provided parameters will be ignored
VehicleExitParamOffset, // provided parameters will be used as offset values
VehicleExitParamDest, // provided parameters will be used as absolute destination
VehicleExitParamMax
};
struct PassengerInfo
{
PassengerInfo() : IsUnselectable(false) { }
@@ -89,9 +97,24 @@ struct PassengerInfo
}
};
struct VehicleSeatAddon
{
VehicleSeatAddon() { }
VehicleSeatAddon(float orientatonOffset, float exitX, float exitY, float exitZ, float exitO, uint8 param) :
SeatOrientationOffset(orientatonOffset), ExitParameterX(exitX), ExitParameterY(exitY), ExitParameterZ(exitZ),
ExitParameterO(exitO), ExitParameter(VehicleExitParameters(param)) { }
float SeatOrientationOffset = 0.f;
float ExitParameterX = 0.f;
float ExitParameterY = 0.f;
float ExitParameterZ = 0.f;
float ExitParameterO = 0.f;
VehicleExitParameters ExitParameter = VehicleExitParameters::VehicleExitParamNone;
};
struct VehicleSeat
{
explicit VehicleSeat(VehicleSeatEntry const* seatInfo) : SeatInfo(seatInfo)
explicit VehicleSeat(VehicleSeatEntry const* seatInfo, VehicleSeatAddon const seatAddon) : SeatInfo(seatInfo), SeatAddon(seatAddon)
{
Passenger.Reset();
}
@@ -99,6 +122,7 @@ struct VehicleSeat
bool IsEmpty() const { return Passenger.Guid.IsEmpty(); }
VehicleSeatEntry const* SeatInfo;
VehicleSeatAddon const SeatAddon;
PassengerInfo Passenger;
};
+16 -5
View File
@@ -3347,8 +3347,8 @@ void ObjectMgr::LoadVehicleSeatAddon()
uint32 count = 0;
// 0 1
QueryResult result = WorldDatabase.Query("SELECT `SeatID`, `Angle` FROM `vehicle_seat_addon`");
// 0 1
QueryResult result = WorldDatabase.Query("SELECT `SeatEntry`, `SeatOrientation`, `ExitParamX`, `ExitParamY`, `ExitParamZ`, `ExitParamO`, `ExitParamValue` FROM `vehicle_seat_addon`");
if (!result)
{
@@ -3361,7 +3361,12 @@ void ObjectMgr::LoadVehicleSeatAddon()
Field* fields = result->Fetch();
uint32 seatID = fields[0].GetUInt32();
float orientation = fields[1].GetFloat() * float(M_PI) / 180;
float orientation = fields[1].GetFloat();
float exitX = fields[2].GetFloat();
float exitY = fields[3].GetFloat();
float exitZ = fields[4].GetFloat();
float exitO = fields[5].GetFloat();
uint8 exitParam = fields[6].GetUInt8();
if (!sVehicleSeatStore.LookupEntry(seatID))
{
@@ -3372,12 +3377,18 @@ void ObjectMgr::LoadVehicleSeatAddon()
// Sanitizing values
if (orientation > float(M_PI * 2))
{
TC_LOG_ERROR("sql.sql", "Table `vehicle_seat_addon`: SeatID: %u is using invalid angle offset value (%f). Setting to 0.", seatID, fields[1].GetFloat());
TC_LOG_ERROR("sql.sql", "Table `vehicle_seat_addon`: SeatID: %u is using invalid angle offset value (%f). Setting to 0.", seatID, orientation);
orientation = 0.0f;
continue;
}
_vehicleSeatAddonStore.emplace(seatID, orientation);
if (exitParam >= AsUnderlyingType(VehicleExitParameters::VehicleExitParamMax))
{
TC_LOG_ERROR("sql.sql", "Table `vehicle_seat_addon`: SeatID: %u is using invalid exit parameter value (%u). Setting to 0 (none).", seatID, exitParam);
continue;
}
_vehicleSeatAddonStore[seatID] = VehicleSeatAddon(orientation, exitX, exitY, exitZ, exitO, exitParam);
++count;
}
+5 -5
View File
@@ -560,7 +560,7 @@ typedef std::unordered_map<uint32, QuestLocale> QuestLocaleContainer;
typedef std::unordered_map<uint32, NpcTextLocale> NpcTextLocaleContainer;
typedef std::unordered_map<uint32, PageTextLocale> PageTextLocaleContainer;
typedef std::unordered_map<uint32, uint8> TaxiNodeLevelDataContainer;
typedef std::unordered_map<uint32, float> VehicleSeatAddonContainer;
typedef std::unordered_map<uint32, VehicleSeatAddon> VehicleSeatAddonContainer;
struct GossipMenuItemsLocale
{
@@ -1557,13 +1557,13 @@ class TC_GAME_API ObjectMgr
uint32 GetGameObjectTypeByEntry(uint32 entry) const;
float GetVehicleSeatOrientationOffset(uint32 seatId) const
VehicleSeatAddon const GetVehicleSeatAddon(uint32 seatId) const
{
VehicleSeatAddonContainer::const_iterator itr = _vehicleSeatAddonStore.find(seatId);
if (itr != _vehicleSeatAddonStore.end())
return itr->second;
if (itr == _vehicleSeatAddonStore.end())
return VehicleSeatAddon();
return 0.0f;
return itr->second;
}
private:
@@ -174,421 +174,387 @@ Position const NefarianIntroSummonPos = { -390.1042f, 40.88411f, 207.8586f, 0
#define SPELL_PARASITIC_INFECTION_PERIODIC_DAMAGE RAID_MODE<uint32>(78941, 91913, 94678, 94679)
class boss_magmaw : public CreatureScript, public UnitScript
struct boss_magmaw : public BossAI
{
public:
boss_magmaw() : CreatureScript("boss_magmaw"), UnitScript("boss_magmaw_vehicle") { }
void ModifyVehiclePassengerExitPos(Unit* /*passenger*/, Vehicle* vehicle, Position& pos) override
boss_magmaw(Creature* creature) : BossAI(creature, DATA_MAGMAW)
{
if (vehicle->GetBase()->GetEntry() != BOSS_MAGMAW)
return;
pos.Relocate(MagmawVehicleExitPos);
Initialize();
}
struct boss_magmawAI : public BossAI
void Initialize()
{
boss_magmawAI(Creature* creature) : BossAI(creature, DATA_MAGMAW)
_magmaProjectileCount = 0;
_exposedHead1 = nullptr;
_exposedHead2 = nullptr;
_pincer1 = nullptr;
_pincer2 = nullptr;
_hasExposedHead = false;
_headEngaged = false;
_achievementEnligible = true;
_heroicPhaseTwoActive = !IsHeroic();
me->SetReactState(REACT_PASSIVE);
}
void Reset() override
{
_Reset();
Initialize();
}
void JustAppeared() override
{
SetupBody();
}
void JustEngagedWith(Unit* /*who*/) override
{
_JustEngagedWith();
instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me);
me->SetReactState(REACT_AGGRESSIVE);
events.ScheduleEvent(EVENT_MAGMA_PROJECTILE, 6s);
events.ScheduleEvent(EVENT_LAVA_SPEW, 19s);
events.ScheduleEvent(EVENT_MANGLE, 1min + 30s);
_exposedHead1->SetInCombatWithZone();
_exposedHead2->SetInCombatWithZone();
if (IsHeroic())
DoSummon(NPC_NEFARIAN_MAGMAW, NefarianIntroSummonPos, 0, TEMPSUMMON_MANUAL_DESPAWN);
}
void EnterEvadeMode(EvadeReason /*why*/) override
{
_EnterEvadeMode();
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
if (_headEngaged)
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, _exposedHead1);
DoCastSelf(SPELL_EJECT_PASSENGER_3, true);
_pincer1->CastSpell(_pincer1, SPELL_EJECT_PASSENGER_1, true);
_pincer2->CastSpell(_pincer2, SPELL_EJECT_PASSENGER_1, true);
instance->SetBossState(DATA_MAGMAW, FAIL);
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_PARASITIC_INFECTION_VOMIT);
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_PARASITIC_INFECTION_PERIODIC_DAMAGE);
_exposedHead1->DespawnOrUnsummon();
summons.DespawnAll();
if (Creature* nefarian = instance->GetCreature(DATA_NEFARIAN_MAGMAW))
nefarian->DespawnOrUnsummon();
_DespawnAtEvade();
}
void JustDied(Unit* /*killer*/) override
{
if (_headEngaged)
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, _exposedHead1);
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_PARASITIC_INFECTION_VOMIT);
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_PARASITIC_INFECTION_PERIODIC_DAMAGE);
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
if (Creature* nefarian = instance->GetCreature(DATA_NEFARIAN_MAGMAW))
nefarian->AI()->DoAction(ACTION_MAGMAW_DEAD);
me->StopMoving(); // Tempfix to prevent Magmaw from falling into the lava
_JustDied();
}
void JustSummoned(Creature* summon) override
{
switch (summon->GetEntry())
{
Initialize();
case NPC_PILLAR_OF_FLAME:
summon->CastSpell(summon, SPELL_PILLAR_OF_FLAME_DUMMY);
summon->SetDisplayId(summon->GetCreatureTemplate()->Modelid1);
summon->DespawnOrUnsummon(7s);
Talk(SAY_ANNOUNCE_LAVA_PARASITES);
summons.Summon(summon);
break;
case NPC_NEFARIAN_MAGMAW:
case NPC_EXPOSED_HEAD_OF_MAGMAW:
break;
default:
summons.Summon(summon);
break;
}
}
void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override
{
switch (spell->Id)
{
case SPELL_MASSIVE_CRASH:
{
me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK);
me->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_ALLOW_ENEMY_INTERACT);
_exposedHead1->CastSpell(_exposedHead1, SPELL_RIDE_VEHICLE_HEAD, true);
_pincer1->CastSpell(_pincer1, SPELL_EJECT_PASSENGER_1, true);
_pincer2->CastSpell(_pincer2, SPELL_EJECT_PASSENGER_1, true);
events.ScheduleEvent(EVENT_ANNOUNCE_PINCERS_EXPOSED, 1s);
events.ScheduleEvent(EVENT_FINISH_MASSIVE_CRASH, 7s);
Unit* head = _exposedHead1;
head->m_Events.AddEventAtOffset([head]()
{
head->CastSpell(head, SPELL_RIDE_VEHICLE_EXPOSED_HEAD, true);
}, 6s);
break;
}
case SPELL_IMPALE_SELF:
DoCastSelf(SPELL_EJECT_PASSENGER_3, true);
Talk(SAY_ANNOUNCE_EXPOSED_HEAD);
me->RemoveAurasDueToSpell(SPELL_CHAIN_VISUAL_1);
me->RemoveAurasDueToSpell(SPELL_CHAIN_VISUAL_2);
events.ScheduleEvent(EVENT_HIDE_HEAD, 29s);
events.ScheduleEvent(EVENT_FINISH_IMPALE_SELF, 33s);
break;
default:
break;
}
}
uint32 GetData(uint32 type) const override
{
if (type == DATA_ACHIEVEMENT_STATE)
return _achievementEnligible;
return 0;
}
ObjectGuid GetGUID(int32 type) const override
{
switch (type)
{
case DATA_FREE_PINCER:
if (_pincer1->GetVehicleKit() && _pincer1->GetVehicleKit()->GetAvailableSeatCount())
return _pincer1->GetGUID();
else if (_pincer2->GetVehicleKit() && _pincer2->GetVehicleKit()->GetAvailableSeatCount())
return _pincer2->GetGUID();
default:
return ObjectGuid::Empty;
}
void Initialize()
return ObjectGuid::Empty;
}
void DamageTaken(Unit* /*attacker*/, uint32& damage) override
{
if (me->HealthBelowPctDamaged(30, damage) && !_heroicPhaseTwoActive)
{
_magmaProjectileCount = 0;
_exposedHead1 = nullptr;
_exposedHead2 = nullptr;
_pincer1 = nullptr;
_pincer2 = nullptr;
_hasExposedHead = false;
_headEngaged = false;
_achievementEnligible = true;
_heroicPhaseTwoActive = !IsHeroic();
me->SetReactState(REACT_PASSIVE);
if (Creature* nefarian = instance->GetCreature(DATA_NEFARIAN_MAGMAW))
nefarian->AI()->DoAction(ACTION_SCHEDULE_SHADOW_BREATH);
_heroicPhaseTwoActive = true;
}
void Reset() override
if (damage >= me->GetHealth())
{
_Reset();
Initialize();
}
void JustAppeared() override
{
SetupBody();
}
void JustEngagedWith(Unit* /*who*/) override
{
_JustEngagedWith();
instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me);
me->SetReactState(REACT_AGGRESSIVE);
events.ScheduleEvent(EVENT_MAGMA_PROJECTILE, 6s);
events.ScheduleEvent(EVENT_LAVA_SPEW, 19s);
events.ScheduleEvent(EVENT_MANGLE, 1min + 30s);
_exposedHead1->SetInCombatWithZone();
_exposedHead2->SetInCombatWithZone();
if (IsHeroic())
DoSummon(NPC_NEFARIAN_MAGMAW, NefarianIntroSummonPos, 0, TEMPSUMMON_MANUAL_DESPAWN);
}
void EnterEvadeMode(EvadeReason /*why*/) override
{
_EnterEvadeMode();
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
if (_headEngaged)
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, _exposedHead1);
// Make sure we eject all passengers nicely before we die so they wont end up in the lava
DoCastSelf(SPELL_EJECT_PASSENGER_3, true);
_pincer1->CastSpell(_pincer1, SPELL_EJECT_PASSENGER_1, true);
_pincer2->CastSpell(_pincer2, SPELL_EJECT_PASSENGER_1, true);
instance->SetBossState(DATA_MAGMAW, FAIL);
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_PARASITIC_INFECTION_VOMIT);
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_PARASITIC_INFECTION_PERIODIC_DAMAGE);
_exposedHead1->DespawnOrUnsummon();
summons.DespawnAll();
if (Creature* nefarian = instance->GetCreature(DATA_NEFARIAN_MAGMAW))
nefarian->DespawnOrUnsummon();
_DespawnAtEvade();
}
void JustDied(Unit* /*killer*/) override
{
if (_headEngaged)
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, _exposedHead1);
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_PARASITIC_INFECTION_VOMIT);
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_PARASITIC_INFECTION_PERIODIC_DAMAGE);
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
if (Creature* nefarian = instance->GetCreature(DATA_NEFARIAN_MAGMAW))
nefarian->AI()->DoAction(ACTION_MAGMAW_DEAD);
me->StopMoving(); // Tempfix to prevent Magmaw from falling into the lava
_JustDied();
}
void JustSummoned(Creature* summon) override
{
switch (summon->GetEntry())
{
case NPC_PILLAR_OF_FLAME:
summon->CastSpell(summon, SPELL_PILLAR_OF_FLAME_DUMMY);
summon->SetDisplayId(summon->GetCreatureTemplate()->Modelid1);
summon->DespawnOrUnsummon(7s);
Talk(SAY_ANNOUNCE_LAVA_PARASITES);
summons.Summon(summon);
break;
case NPC_NEFARIAN_MAGMAW:
case NPC_EXPOSED_HEAD_OF_MAGMAW:
break;
default:
summons.Summon(summon);
break;
}
}
void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override
{
switch (spell->Id)
{
case SPELL_MASSIVE_CRASH:
{
me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK);
me->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_ALLOW_ENEMY_INTERACT);
_exposedHead1->CastSpell(_exposedHead1, SPELL_RIDE_VEHICLE_HEAD, true);
_pincer1->CastSpell(_pincer1, SPELL_EJECT_PASSENGER_1, true);
_pincer2->CastSpell(_pincer2, SPELL_EJECT_PASSENGER_1, true);
events.ScheduleEvent(EVENT_ANNOUNCE_PINCERS_EXPOSED, 1s);
events.ScheduleEvent(EVENT_FINISH_MASSIVE_CRASH, 7s);
Unit* head = _exposedHead1;
head->m_Events.AddEventAtOffset([head]()
{
head->CastSpell(head, SPELL_RIDE_VEHICLE_EXPOSED_HEAD, true);
}, 6s);
break;
}
case SPELL_IMPALE_SELF:
DoCastSelf(SPELL_EJECT_PASSENGER_3, true);
Talk(SAY_ANNOUNCE_EXPOSED_HEAD);
me->RemoveAurasDueToSpell(SPELL_CHAIN_VISUAL_1);
me->RemoveAurasDueToSpell(SPELL_CHAIN_VISUAL_2);
events.ScheduleEvent(EVENT_HIDE_HEAD, 29s);
events.ScheduleEvent(EVENT_FINISH_IMPALE_SELF, 33s);
break;
default:
break;
}
}
uint32 GetData(uint32 type) const override
{
if (type == DATA_ACHIEVEMENT_STATE)
return _achievementEnligible;
return 0;
}
ObjectGuid GetGUID(int32 type) const override
{
switch (type)
{
case DATA_FREE_PINCER:
if (_pincer1->GetVehicleKit() && _pincer1->GetVehicleKit()->GetAvailableSeatCount())
return _pincer1->GetGUID();
else if (_pincer2->GetVehicleKit() && _pincer2->GetVehicleKit()->GetAvailableSeatCount())
return _pincer2->GetGUID();
default:
return ObjectGuid::Empty;
}
return ObjectGuid::Empty;
}
void DamageTaken(Unit* /*attacker*/, uint32& damage) override
{
if (me->HealthBelowPctDamaged(30, damage) && !_heroicPhaseTwoActive)
{
if (Creature* nefarian = instance->GetCreature(DATA_NEFARIAN_MAGMAW))
nefarian->AI()->DoAction(ACTION_SCHEDULE_SHADOW_BREATH);
_heroicPhaseTwoActive = true;
}
if (damage >= me->GetHealth())
{
// Make sure we eject all passengers nicely before we die so they wont end up in the lava
DoCastSelf(SPELL_EJECT_PASSENGER_3, true);
_pincer1->CastSpell(_pincer1, SPELL_EJECT_PASSENGER_1, true);
_pincer2->CastSpell(_pincer2, SPELL_EJECT_PASSENGER_1, true);
}
}
void DoAction(int32 action) override
{
switch (action)
{
case ACTION_IMPALE_MAGMAW:
events.CancelEvent(EVENT_MAGMA_PROJECTILE);
events.CancelEvent(EVENT_LAVA_SPEW);
me->AttackStop();
me->SetReactState(REACT_PASSIVE);
me->CastStop();
me->RemoveAurasDueToSpell(SPELL_MASSIVE_CRASH);
me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK);
me->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_ALLOW_ENEMY_INTERACT);
me->RemoveAurasDueToSpell(SPELL_PILLAR_OF_FLAME_MISSILE_PERIODIC);
if (Creature* spikeStalker = me->FindNearestCreature(NPC_MAGMAW_SPIKE_STALKER, 60.0f))
me->SetFacingToObject(spikeStalker);
events.ScheduleEvent(EVENT_IMPALE_SELF, 1s);
events.ScheduleEvent(EVENT_EXPOSE_HEAD, 4s + 700ms);
_hasExposedHead = true;
break;
case ACTION_FAIL_ACHIEVEMT:
_achievementEnligible = false;
break;
default:
break;
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING) && !_hasExposedHead)
return;
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_MAGMA_PROJECTILE:
if (_magmaProjectileCount < 4)
{
DoCastAOE(SPELL_MAGMA_SPIT_TARGETING);
_magmaProjectileCount++;
events.Repeat(6s);
}
else
{
DoCastAOE(SPELL_PILLAR_OF_FLAME);
DoCastAOE(SPELL_PILLAR_OF_FLAME_SET_VEHICLE_ID);
_magmaProjectileCount = 0;
events.Repeat(8s);
}
break;
case EVENT_LAVA_SPEW:
DoCastAOE(SPELL_LAVA_SPEW);
events.Repeat(27s, 28s);
break;
case EVENT_MANGLE:
if (SelectTarget(SELECT_TARGET_RANDOM, 0, NonTankTargetSelector(me)))
DoCastAOE(SPELL_MANGLE_TARGETING);
events.CancelEvent(EVENT_MAGMA_PROJECTILE);
events.CancelEvent(EVENT_LAVA_SPEW);
events.ScheduleEvent(EVENT_PREPARE_MASSIVE_CRASH, 3s + 500ms);
events.Repeat(1min + 35s);
break;
case EVENT_PREPARE_MASSIVE_CRASH:
if (ObjectGuid guid = instance->GetGuidData(DATA_PREPARE_MASSIVE_CRASH_AND_GET_TARGET_GUID))
{
if (Creature* stalker = ObjectAccessor::GetCreature(*me, guid))
{
me->AttackStop();
me->SetReactState(REACT_PASSIVE);
me->ReleaseSpellFocus(nullptr, false);
me->SetFacingToObject(stalker, true);
events.ScheduleEvent(EVENT_MASSIVE_CRASH, 5s);
}
}
break;
case EVENT_MASSIVE_CRASH:
DoCast(SPELL_MASSIVE_CRASH);
_pincer1->CastSpell(_pincer1, SPELL_EJECT_PASSENGER_1, true);
_pincer2->CastSpell(_pincer2, SPELL_EJECT_PASSENGER_1, true);
break;
case EVENT_ANNOUNCE_PINCERS_EXPOSED:
Talk(SAY_ANNOUNCE_EXPOSE_PINCERS);
break;
case EVENT_FINISH_MASSIVE_CRASH:
me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK);
me->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_ALLOW_ENEMY_INTERACT);
me->SetReactState(REACT_AGGRESSIVE);
events.ScheduleEvent(EVENT_LAVA_SPEW, 1ms);
events.ScheduleEvent(EVENT_MAGMA_PROJECTILE, 8s);
break;
case EVENT_IMPALE_SELF:
DoCastSelf(SPELL_IMPALE_SELF);
break;
case EVENT_EXPOSE_HEAD:
if (!_headEngaged)
{
instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, _exposedHead1, 2);
instance->SendEncounterUnit(ENCOUNTER_FRAME_UPDATE_PRIORITY, me);
_headEngaged = true;
}
_exposedHead1->CastSpell(_exposedHead1, SPELL_RIDE_VEHICLE_HEAD, true);
_exposedHead1->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
break;
case EVENT_HIDE_HEAD:
_exposedHead1->CastSpell(_exposedHead1, SPELL_RIDE_VEHICLE_EXPOSED_HEAD, true);
_exposedHead1->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
break;
case EVENT_FINISH_IMPALE_SELF:
_hasExposedHead = false;
me->SetReactState(REACT_AGGRESSIVE);
events.ScheduleEvent(EVENT_LAVA_SPEW, 1ms);
events.ScheduleEvent(EVENT_MAGMA_PROJECTILE, 4s);
break;
default:
break;
}
}
if (Unit* victim = me->GetVictim())
{
if (me->IsWithinMeleeRange(victim))
DoMeleeAttackIfReady();
else
DoSpellAttackIfReady(SPELL_MAGMA_SPIT_MOLTEN_TANTRUM);
}
}
private:
void SetupBody()
{
_pincer1 = DoSummon(NPC_MAGMAWS_PINCER_1, me->GetPosition());
if (_pincer1)
{
_pincer1->EnterVehicle(me, SEAT_MAGMAWS_PINCER_1);
_pincer1->SetDisplayId(_pincer1->GetCreatureTemplate()->Modelid3);
}
_pincer2 = DoSummon(NPC_MAGMAWS_PINCER_2, me->GetPosition());
if (_pincer2)
{
_pincer2->EnterVehicle(me, SEAT_MAGMAWS_PINCER_2);
_pincer2->SetDisplayId(_pincer2->GetCreatureTemplate()->Modelid3);
}
_exposedHead1 = DoSummon(NPC_EXPOSED_HEAD_OF_MAGMAW, ExposedHeadOfMagmawPos);
_exposedHead2 = DoSummon(NPC_EXPOSED_HEAD_OF_MAGMAW_2, me->GetPosition());
if (!_exposedHead1 || !_exposedHead2)
return;
_exposedHead1->SetReactState(REACT_PASSIVE);
_exposedHead2->SetReactState(REACT_PASSIVE);
_exposedHead2->EnterVehicle(me, SEAT_EXPOSED_HEAD_OF_MAGMAW_2);
DoCastSelf(SPELL_BIRTH);
// First we link the real exposed head
_exposedHead1->CastSpell(me, SPELL_POINT_OF_VULNERABILITY_SHARE_DAMAGE);
_exposedHead1->CastSpell(_exposedHead1, SPELL_POINT_OF_VULNERABILITY);
_exposedHead1->CastSpell(_exposedHead2, SPELL_POINT_OF_VULNERABILITY_SHARE_DAMAGE);
// ... now the dummy exposed head
_exposedHead2->CastSpell(me, SPELL_POINT_OF_VULNERABILITY_SHARE_DAMAGE);
_exposedHead2->CastSpell(_exposedHead2, SPELL_POINT_OF_VULNERABILITY);
// ... and now Magmaw
DoCast(_exposedHead2, SPELL_POINT_OF_VULNERABILITY_SHARE_DAMAGE);
DoCast(_exposedHead1, SPELL_POINT_OF_VULNERABILITY_SHARE_DAMAGE);
_exposedHead2->CastSpell(_exposedHead2, SPELL_QUEST_INVIS_5);
ObjectGuid guid = me->GetGUID();
Unit* head = _exposedHead1;
head->m_Events.AddEventAtOffset([head, guid]()
{
if (Unit* target = ObjectAccessor::GetUnit(*head, guid))
head->CastSpell(target, SPELL_RIDE_VEHICLE_EXPOSED_HEAD, true);
}, 1s + 200ms);
}
Creature* _exposedHead1;
Creature* _exposedHead2;
Creature* _pincer1;
Creature* _pincer2;
uint8 _magmaProjectileCount;
bool _achievementEnligible;
bool _hasExposedHead;
bool _headEngaged;
bool _heroicPhaseTwoActive;
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetBlackwingDescentAI<boss_magmawAI>(creature);
}
};
class npc_magmaw_magmaws_pincer : public UnitScript
{
public:
npc_magmaw_magmaws_pincer() : UnitScript("npc_magmaw_magmaws_pincer") { }
void ModifyVehiclePassengerExitPos(Unit* /*passenger*/, Vehicle* vehicle, Position& pos)
void DoAction(int32 action) override
{
Unit* base = vehicle->GetBase();
if (base->GetEntry() != NPC_MAGMAWS_PINCER_1 && base->GetEntry() != NPC_MAGMAWS_PINCER_2)
switch (action)
{
case ACTION_IMPALE_MAGMAW:
events.CancelEvent(EVENT_MAGMA_PROJECTILE);
events.CancelEvent(EVENT_LAVA_SPEW);
me->AttackStop();
me->SetReactState(REACT_PASSIVE);
me->CastStop();
me->RemoveAurasDueToSpell(SPELL_MASSIVE_CRASH);
me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK);
me->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_ALLOW_ENEMY_INTERACT);
me->RemoveAurasDueToSpell(SPELL_PILLAR_OF_FLAME_MISSILE_PERIODIC);
if (Creature* spikeStalker = me->FindNearestCreature(NPC_MAGMAW_SPIKE_STALKER, 60.0f))
me->SetFacingToObject(spikeStalker);
events.ScheduleEvent(EVENT_IMPALE_SELF, 1s);
events.ScheduleEvent(EVENT_EXPOSE_HEAD, 4s + 700ms);
_hasExposedHead = true;
break;
case ACTION_FAIL_ACHIEVEMT:
_achievementEnligible = false;
break;
default:
break;
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
pos.Relocate(MagmawVehicleExitPos);
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING) && !_hasExposedHead)
return;
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_MAGMA_PROJECTILE:
if (_magmaProjectileCount < 4)
{
DoCastAOE(SPELL_MAGMA_SPIT_TARGETING);
_magmaProjectileCount++;
events.Repeat(6s);
}
else
{
DoCastAOE(SPELL_PILLAR_OF_FLAME);
DoCastAOE(SPELL_PILLAR_OF_FLAME_SET_VEHICLE_ID);
_magmaProjectileCount = 0;
events.Repeat(8s);
}
break;
case EVENT_LAVA_SPEW:
DoCastAOE(SPELL_LAVA_SPEW);
events.Repeat(27s, 28s);
break;
case EVENT_MANGLE:
if (SelectTarget(SELECT_TARGET_RANDOM, 0, NonTankTargetSelector(me)))
DoCastAOE(SPELL_MANGLE_TARGETING);
events.CancelEvent(EVENT_MAGMA_PROJECTILE);
events.CancelEvent(EVENT_LAVA_SPEW);
events.ScheduleEvent(EVENT_PREPARE_MASSIVE_CRASH, 3s + 500ms);
events.Repeat(1min + 35s);
break;
case EVENT_PREPARE_MASSIVE_CRASH:
if (ObjectGuid guid = instance->GetGuidData(DATA_PREPARE_MASSIVE_CRASH_AND_GET_TARGET_GUID))
{
if (Creature* stalker = ObjectAccessor::GetCreature(*me, guid))
{
me->AttackStop();
me->SetReactState(REACT_PASSIVE);
me->ReleaseSpellFocus(nullptr, false);
me->SetFacingToObject(stalker, true);
events.ScheduleEvent(EVENT_MASSIVE_CRASH, 5s);
}
}
break;
case EVENT_MASSIVE_CRASH:
DoCast(SPELL_MASSIVE_CRASH);
_pincer1->CastSpell(_pincer1, SPELL_EJECT_PASSENGER_1, true);
_pincer2->CastSpell(_pincer2, SPELL_EJECT_PASSENGER_1, true);
break;
case EVENT_ANNOUNCE_PINCERS_EXPOSED:
Talk(SAY_ANNOUNCE_EXPOSE_PINCERS);
break;
case EVENT_FINISH_MASSIVE_CRASH:
me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK);
me->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_ALLOW_ENEMY_INTERACT);
me->SetReactState(REACT_AGGRESSIVE);
events.ScheduleEvent(EVENT_LAVA_SPEW, 1ms);
events.ScheduleEvent(EVENT_MAGMA_PROJECTILE, 8s);
break;
case EVENT_IMPALE_SELF:
DoCastSelf(SPELL_IMPALE_SELF);
break;
case EVENT_EXPOSE_HEAD:
if (!_headEngaged)
{
instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, _exposedHead1, 2);
instance->SendEncounterUnit(ENCOUNTER_FRAME_UPDATE_PRIORITY, me);
_headEngaged = true;
}
_exposedHead1->CastSpell(_exposedHead1, SPELL_RIDE_VEHICLE_HEAD, true);
_exposedHead1->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
break;
case EVENT_HIDE_HEAD:
_exposedHead1->CastSpell(_exposedHead1, SPELL_RIDE_VEHICLE_EXPOSED_HEAD, true);
_exposedHead1->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
break;
case EVENT_FINISH_IMPALE_SELF:
_hasExposedHead = false;
me->SetReactState(REACT_AGGRESSIVE);
events.ScheduleEvent(EVENT_LAVA_SPEW, 1ms);
events.ScheduleEvent(EVENT_MAGMA_PROJECTILE, 4s);
break;
default:
break;
}
}
if (Unit* victim = me->GetVictim())
{
if (me->IsWithinMeleeRange(victim))
DoMeleeAttackIfReady();
else
DoSpellAttackIfReady(SPELL_MAGMA_SPIT_MOLTEN_TANTRUM);
}
}
private:
void SetupBody()
{
_pincer1 = DoSummon(NPC_MAGMAWS_PINCER_1, me->GetPosition());
if (_pincer1)
{
_pincer1->EnterVehicle(me, SEAT_MAGMAWS_PINCER_1);
_pincer1->SetDisplayId(_pincer1->GetCreatureTemplate()->Modelid3);
}
_pincer2 = DoSummon(NPC_MAGMAWS_PINCER_2, me->GetPosition());
if (_pincer2)
{
_pincer2->EnterVehicle(me, SEAT_MAGMAWS_PINCER_2);
_pincer2->SetDisplayId(_pincer2->GetCreatureTemplate()->Modelid3);
}
_exposedHead1 = DoSummon(NPC_EXPOSED_HEAD_OF_MAGMAW, ExposedHeadOfMagmawPos);
_exposedHead2 = DoSummon(NPC_EXPOSED_HEAD_OF_MAGMAW_2, me->GetPosition());
if (!_exposedHead1 || !_exposedHead2)
return;
_exposedHead1->SetReactState(REACT_PASSIVE);
_exposedHead2->SetReactState(REACT_PASSIVE);
_exposedHead2->EnterVehicle(me, SEAT_EXPOSED_HEAD_OF_MAGMAW_2);
DoCastSelf(SPELL_BIRTH);
// First we link the real exposed head
_exposedHead1->CastSpell(me, SPELL_POINT_OF_VULNERABILITY_SHARE_DAMAGE);
_exposedHead1->CastSpell(_exposedHead1, SPELL_POINT_OF_VULNERABILITY);
_exposedHead1->CastSpell(_exposedHead2, SPELL_POINT_OF_VULNERABILITY_SHARE_DAMAGE);
// ... now the dummy exposed head
_exposedHead2->CastSpell(me, SPELL_POINT_OF_VULNERABILITY_SHARE_DAMAGE);
_exposedHead2->CastSpell(_exposedHead2, SPELL_POINT_OF_VULNERABILITY);
// ... and now Magmaw
DoCast(_exposedHead2, SPELL_POINT_OF_VULNERABILITY_SHARE_DAMAGE);
DoCast(_exposedHead1, SPELL_POINT_OF_VULNERABILITY_SHARE_DAMAGE);
_exposedHead2->CastSpell(_exposedHead2, SPELL_QUEST_INVIS_5);
ObjectGuid guid = me->GetGUID();
Unit* head = _exposedHead1;
head->m_Events.AddEventAtOffset([head, guid]()
{
if (Unit* target = ObjectAccessor::GetUnit(*head, guid))
head->CastSpell(target, SPELL_RIDE_VEHICLE_EXPOSED_HEAD, true);
}, 1s + 200ms);
}
Creature* _exposedHead1;
Creature* _exposedHead2;
Creature* _pincer1;
Creature* _pincer2;
uint8 _magmaProjectileCount;
bool _achievementEnligible;
bool _hasExposedHead;
bool _headEngaged;
bool _heroicPhaseTwoActive;
};
struct npc_magmaw_nefarian : public ScriptedAI
@@ -1233,8 +1199,7 @@ class achievement_parasite_evening : public AchievementCriteriaScript
void AddSC_boss_magmaw()
{
new boss_magmaw();
new npc_magmaw_magmaws_pincer();
RegisterBlackwingDescentCreatureAI(boss_magmaw);
RegisterBlackwingDescentCreatureAI(npc_magmaw_nefarian);
RegisterBlackwingDescentCreatureAI(npc_magmaw_lava_parasite);
RegisterBlackwingDescentCreatureAI(npc_magmaw_blazing_bone_construct);