diff --git a/sql/updates/world/custom/custom_2020_01_28_01_world.sql b/sql/updates/world/custom/custom_2020_01_28_01_world.sql new file mode 100644 index 00000000000..8651f2586bb --- /dev/null +++ b/sql/updates/world/custom/custom_2020_01_28_01_world.sql @@ -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 diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 9e1cb35bfb2..df0efee9cc2 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -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(); diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp index 50a51b7ac93..33daaebbb40 100644 --- a/src/server/game/Entities/Vehicle/Vehicle.cpp +++ b/src/server/game/Entities/Vehicle/Vehicle.cpp @@ -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; diff --git a/src/server/game/Entities/Vehicle/Vehicle.h b/src/server/game/Entities/Vehicle/Vehicle.h index b13444b4a8f..6c815067bc4 100644 --- a/src/server/game/Entities/Vehicle/Vehicle.h +++ b/src/server/game/Entities/Vehicle/Vehicle.h @@ -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(); diff --git a/src/server/game/Entities/Vehicle/VehicleDefines.h b/src/server/game/Entities/Vehicle/VehicleDefines.h index 8f36e98a473..13e56c940b1 100644 --- a/src/server/game/Entities/Vehicle/VehicleDefines.h +++ b/src/server/game/Entities/Vehicle/VehicleDefines.h @@ -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; }; diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index cfaa11a36d8..1b1b2f17873 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -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; } diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index b665b890000..0fb22c061c5 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -560,7 +560,7 @@ typedef std::unordered_map QuestLocaleContainer; typedef std::unordered_map NpcTextLocaleContainer; typedef std::unordered_map PageTextLocaleContainer; typedef std::unordered_map TaxiNodeLevelDataContainer; -typedef std::unordered_map VehicleSeatAddonContainer; +typedef std::unordered_map 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: diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingDescent/boss_magmaw.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingDescent/boss_magmaw.cpp index c87ebc6adfe..0e4c1d05021 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingDescent/boss_magmaw.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingDescent/boss_magmaw.cpp @@ -174,421 +174,387 @@ Position const NefarianIntroSummonPos = { -390.1042f, 40.88411f, 207.8586f, 0 #define SPELL_PARASITIC_INFECTION_PERIODIC_DAMAGE RAID_MODE(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(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);