diff --git a/data/sql/updates/pending_db_world/rev_1782492883671113125.sql b/data/sql/updates/pending_db_world/rev_1782492883671113125.sql new file mode 100644 index 0000000000..0e3d3bc44b --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1782492883671113125.sql @@ -0,0 +1,29 @@ +-- +-- Flame Leviathan: Remove spell_linked_spell entries for SPELL_SYSTEMS_SHUTDOWN (62475). +-- These effects are now handled explicitly via script +DELETE FROM `spell_linked_spell` WHERE `spell_trigger` = -62475; + +-- 62399 Overload Circuit: Entry 33139 (cannon), Entry 33113 (boss) +UPDATE `conditions` SET `SourceGroup` = 1, `Comment` = 'target must be Flame Leviathan' WHERE (`SourceTypeOrReferenceId` = 13) AND (`SourceGroup` = 3) AND (`SourceEntry` = 62399) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 31) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 3) AND (`ConditionValue2` = 33113) AND (`ConditionValue3` = 0); + +-- change turret (33142) summontype from CORPSE_TIMED_DESPAWN (6) to CORPSE_DESPAWN (5) so InstallAccessory skips SetDeathState +UPDATE `vehicle_template_accessory` SET `summontype` = 5 WHERE `entry` = 33114 AND `accessory_entry` = 33142; +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 13) AND (`SourceGroup` = 2) AND (`SourceEntry` = 62399) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 31) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 3) AND (`ConditionValue2` = 33139) AND (`ConditionValue3` = 0); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(13, 2, 62399, 0, 0, 31, 0, 3, 33139, 0, 0, 0, 0, '', 'target must be Flame Leviathan'); + +-- ID - 62323 Hookshot +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 13) AND (`SourceGroup` = 1) AND (`SourceEntry` = 62323) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 31) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 3) AND (`ConditionValue2` = 33114) AND (`ConditionValue3` = 0); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(13, 1, 62323, 0, 0, 31, 0, 3, 33114, 0, 0, 0, 0, '', 'must be Flame Leviathan Seat'); + +-- Add missing text +DELETE FROM `creature_text` WHERE (`CreatureID` = 33113) AND (`GroupID` IN (20)); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(33113, 20, 0, 'The Flame Leviathan begins to overload!', 41, 0, 100, 0, 0, 0, 33276, 0, 'Flame Leviathan EMOTE_OVERLOAD_START'); + +DELETE FROM `spell_script_names` WHERE `spell_id` = 62336; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(62336, 'spell_hookshot_aura'); + +DELETE FROM `spell_script_names` WHERE `ScriptName` = 'spell_vehicle_circuit_overload_aura'; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 66f57773fd..73219a924f 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -15849,7 +15849,6 @@ void Unit::_ExitVehicle(Position const* exitPosition) init.Launch(); DisableSpline(); KnockbackFrom(pos.GetPositionX(), pos.GetPositionY(), 10.0f, 20.0f); - CastSpell(this, VEHICLE_SPELL_PARACHUTE, true); } // xinef: move fall, should we support all creatures that exited vehicle in air? Currently Quest Drag and Drop only, Air Assault quest diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp index 7193e43e1b..6ed9052672 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp @@ -56,6 +56,7 @@ enum LeviathanSpells // Shutdown spells SPELL_SYSTEMS_SHUTDOWN = 62475, SPELL_OVERLOAD_CIRCUIT = 62399, + SPELL_START_THE_ENGINE = 62472, // hard mode SPELL_TOWER_OF_STORMS = 65076, @@ -84,6 +85,10 @@ enum LeviathanSpells SPELL_LIQUID_PYRITE = 62494, SPELL_DUSTY_EXPLOSION = 63360, SPELL_DUST_CLOUD_IMPACT = 54740, + + // Hookshot + SPELL_HOOKSHOT_AURA = 62336, + SPELL_HOOKSHOT = 62323, }; enum GosNpcs @@ -115,12 +120,12 @@ enum Events EVENT_MISSILE = 2, EVENT_VENT = 3, EVENT_SPEED = 4, - EVENT_REINSTALL = 5, EVENT_HODIRS_FURY = 6, EVENT_FREYA = 7, EVENT_MIMIRONS_INFERNO = 8, EVENT_THORIMS_HAMMER = 9, EVENT_SOUND_BEGINNING = 10, + EVENT_EJECT_PLAYERS = 11, }; enum Texts @@ -145,6 +150,7 @@ enum Texts FLAME_LEVIATHAN_EMOTE_NATURE = 17, FLAME_LEVIATHAN_EMOTE_STORM = 18, FLAME_LEVIATHAN_EMOTE_REACTIVATE = 19, + FLAME_LEVIATHAN_EMOTE_OVERLOAD_START = 20, // NPC_BRANN_RADIO BRANN_RADIO_SAY_FL_START_0 = 0, @@ -179,7 +185,6 @@ enum Misc ACTION_START_BRANN_EVENT = 3, ACTION_DESPAWN_ADDS = 4, ACTION_DELAY_CANNON = 5, - ACTION_DESTROYED_TURRET = 6, }; const Position homePos = {322.39f, -14.5f, 409.8f, 3.14f}; @@ -197,7 +202,7 @@ struct boss_flame_leviathan : public BossAI uint32 _speakTimer; uint8 _towersCount; bool _shutdown; - uint32 _destroyedTurretCount; + uint8 _overloadCircuitCount; // Custom void BindPlayers(); @@ -284,7 +289,7 @@ struct boss_flame_leviathan : public BossAI _startTimer = 1; _speakTimer = 0; _towersCount = 0; - _destroyedTurretCount = 0; + _overloadCircuitCount = 0; if (instance->GetBossState(BOSS_LEVIATHAN) != SPECIAL) { @@ -402,13 +407,6 @@ struct boss_flame_leviathan : public BossAI else Talk(FLAME_LEVIATHAN_SAY_TOWER_NONE); return; - case EVENT_REINSTALL: - for (uint8 i = RAID_MODE(0, 2); i < 4; ++i) - if (Unit* seat = vehicle->GetPassenger(i)) - if (seat->IsCreature()) - seat->ToCreature()->AI()->EnterEvadeMode(); - Talk(FLAME_LEVIATHAN_EMOTE_REACTIVATE); - return; case EVENT_THORIMS_HAMMER: SummonTowerHelpers(TOWER_OF_STORMS); events.Repeat(1min, 2min); @@ -430,6 +428,13 @@ struct boss_flame_leviathan : public BossAI Talk(FLAME_LEVIATHAN_EMOTE_FROST); Talk(FLAME_LEVIATHAN_SAY_TOWER_FROST); return; + case EVENT_EJECT_PLAYERS: + for (int8 i = 0; i < 4; ++i) + if (Unit* seat = vehicle->GetPassenger(i)) + if (Vehicle* seatVehicle = seat->GetVehicleKit()) + if (Unit* player = seatVehicle->GetPassenger(SEAT_PLAYER)) + player->ExitVehicle(); + return; } if (me->isAttackReady() && !me->HasUnitState(UNIT_STATE_STUNNED)) @@ -441,20 +446,6 @@ struct boss_flame_leviathan : public BossAI } } } - - void DoAction(int32 action) override - { - if (action == ACTION_DESTROYED_TURRET) - { - ++_destroyedTurretCount; - - if (_destroyedTurretCount == RAID_MODE(2, 4)) - { - _destroyedTurretCount = 0; - me->CastSpell(me, SPELL_SYSTEMS_SHUTDOWN, true); - } - } - } }; void boss_flame_leviathan::BindPlayers() @@ -574,16 +565,37 @@ void boss_flame_leviathan::ScheduleEvents() void boss_flame_leviathan::SpellHit(Unit* /*caster*/, SpellInfo const* spellInfo) { - if (spellInfo->Id == SPELL_SYSTEMS_SHUTDOWN) + if (spellInfo->Id == SPELL_OVERLOAD_CIRCUIT) + { + ++_overloadCircuitCount; + if (_overloadCircuitCount == 1) + Talk(FLAME_LEVIATHAN_EMOTE_OVERLOAD_START); + + uint8 const threshold = me->GetMap()->Is25ManRaid() ? 4 : 2; + if (_overloadCircuitCount >= threshold) + { + _overloadCircuitCount = 0; + me->CastSpell(me, SPELL_SYSTEMS_SHUTDOWN, true); + } + } + else if (spellInfo->Id == SPELL_SYSTEMS_SHUTDOWN) { _shutdown = true; // ACHIEVEMENT - Talk(FLAME_LEVIATHAN_EMOTE_OVERLOAD); - Talk(FLAME_LEVIATHAN_EMOTE_REPAIR); Talk(FLAME_LEVIATHAN_SAY_OVERLOAD); + Talk(FLAME_LEVIATHAN_EMOTE_OVERLOAD); - events.DelayEvents(21ms); - events.ScheduleEvent(EVENT_REINSTALL, 20ms); + events.ScheduleEvent(EVENT_EJECT_PLAYERS, 3s); + } + else if (spellInfo->Id == SPELL_START_THE_ENGINE) + { + // Respawn turrets + for (uint8 i = 0; i < 4; ++i) + if (Unit* seat = vehicle->GetPassenger(i)) + if (seat->IsCreature()) + seat->ToCreature()->AI()->EnterEvadeMode(); + + Talk(FLAME_LEVIATHAN_EMOTE_REACTIVATE); } else if (spellInfo->Id == 62522 /*SPELL_ELECTROSHOCK*/) me->InterruptNonMeleeSpells(false); @@ -667,7 +679,11 @@ struct boss_flame_leviathan_seat : public VehicleAI } Vehicle* vehicle; - uint32 _despawnTimer; + + // Despawn 2 seats in 10-man. + static constexpr uint32 DESPAWN_DELAY_10MAN = 2000; + bool _pending10ManDespawn; + uint32 _despawnCheckTimer; void EnterEvadeMode(EvadeReason /*why*/) override { @@ -676,19 +692,20 @@ struct boss_flame_leviathan_seat : public VehicleAI void Reset() override { - _despawnTimer = !me->GetMap()->Is25ManRaid(); + _pending10ManDespawn = !me->GetMap()->Is25ManRaid(); + _despawnCheckTimer = 0; } void UpdateAI(uint32 diff) override { - if (_despawnTimer) + if (_pending10ManDespawn) { - _despawnTimer += diff; - if (_despawnTimer >= 2000) + _despawnCheckTimer += diff; + if (_despawnCheckTimer >= DESPAWN_DELAY_10MAN) { - _despawnTimer = 0; - if (Vehicle* veh = me->GetVehicle()) - if (veh->GetPassenger(0) == me || veh->GetPassenger(1) == me) + _pending10ManDespawn = false; + if (Vehicle* parentVehicle = me->GetVehicle()) + if (parentVehicle->GetPassenger(0) == me || parentVehicle->GetPassenger(1) == me) me->DespawnOrUnsummon(1ms); } } @@ -711,6 +728,14 @@ struct boss_flame_leviathan_seat : public VehicleAI { if (Unit* turret = me->GetVehicleKit()->GetPassenger(SEAT_TURRET)) { + if (apply) + { + who->RemoveAurasDueToSpell(SPELL_HOOKSHOT); + who->RemoveAurasDueToSpell(SPELL_HOOKSHOT_AURA); + } + else + who->CastSpell(who, SPELL_SMOKE_TRAIL, true); + if (apply) { turret->ReplaceAllUnitFlags(UNIT_FLAG_NONE); @@ -726,6 +751,14 @@ struct boss_flame_leviathan_seat : public VehicleAI turret->ToCreature()->AI()->EnterEvadeMode(); } } + if (Unit* device = me->GetVehicleKit()->GetPassenger(SEAT_DEVICE)) + { + if (apply) + device->SetNpcFlag(UNIT_NPC_FLAG_SPELLCLICK); + else + device->RemoveNpcFlag(UNIT_NPC_FLAG_SPELLCLICK); + } + } } }; @@ -755,12 +788,9 @@ struct boss_flame_leviathan_defense_turret : public TurretAI if (Player* player = killer->ToPlayer()) player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS, 1, 0, me); - if (Vehicle* vehicle = me->GetVehicle()) - if (Unit* device = vehicle->GetPassenger(SEAT_DEVICE)) - device->ReplaceAllUnitFlags(UNIT_FLAG_NONE); // unselectable - - if (Creature* leviathan = _instance->GetCreature(BOSS_LEVIATHAN)) - leviathan->AI()->DoAction(ACTION_DESTROYED_TURRET); + if (Vehicle* seatVehicle = me->GetVehicle()) + if (Unit* device = seatVehicle->GetPassenger(SEAT_DEVICE)) + device->CastSpell(device, SPELL_OVERLOAD_CIRCUIT, true); } bool CanAIAttack(Unit const* who) const override @@ -806,10 +836,7 @@ struct boss_flame_leviathan_overload_device : public NullCreatureAI me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); if (Unit* player = me->GetVehicle()->GetPassenger(SEAT_PLAYER)) - { - me->GetVehicleBase()->CastSpell(player, SPELL_SMOKE_TRAIL, true); player->ExitVehicle(); - } } } }; @@ -1245,7 +1272,7 @@ class spell_systems_shutdown_aura : public AuraScript bool Validate(SpellInfo const* /*spellInfo*/) override { - return ValidateSpellInfo({ SPELL_GATHERING_SPEED }); + return ValidateSpellInfo({ SPELL_GATHERING_SPEED, SPELL_OVERLOAD_CIRCUIT, SPELL_START_THE_ENGINE }); } void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) @@ -1256,6 +1283,7 @@ class spell_systems_shutdown_aura : public AuraScript owner->SetControlled(true, UNIT_STATE_STUNNED); owner->RemoveAurasDueToSpell(SPELL_GATHERING_SPEED); + owner->RemoveAurasDueToSpell(SPELL_OVERLOAD_CIRCUIT); if (Vehicle* vehicle = owner->GetVehicleKit()) if (Unit* cannon = vehicle->GetPassenger(SEAT_CANNON)) cannon->GetAI()->DoAction(ACTION_DELAY_CANNON); @@ -1268,6 +1296,7 @@ class spell_systems_shutdown_aura : public AuraScript return; owner->SetControlled(false, UNIT_STATE_STUNNED); + owner->CastSpell(owner, SPELL_START_THE_ENGINE, true); } void Register() override @@ -1352,50 +1381,58 @@ class spell_vehicle_throw_passenger : public SpellScript { PrepareSpellScript(spell_vehicle_throw_passenger); + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_HOOKSHOT_AURA }); + } + void HandleScript() { - Spell* baseSpell = GetSpell(); - SpellCastTargets targets = baseSpell->m_targets; - if (Vehicle* vehicle = GetCaster()->GetVehicleKit()) - if (Unit* passenger = vehicle->GetPassenger(3)) + Vehicle* vehicle = GetCaster()->GetVehicleKit(); + Unit* passenger = vehicle ? vehicle->GetPassenger(3) : nullptr; + if (!vehicle || !passenger) + return; + + // Find nearest unoccupied Flame Leviathan seat near trajectory destination + Position const* dst = GetExplTargetDest(); + if (!dst) + return; + + constexpr float SEARCH_RADIUS = 99.0f; + + std::list targetList; + Acore::WorldObjectSpellAreaTargetCheck check(SEARCH_RADIUS, dst, GetCaster(), GetCaster(), GetSpellInfo(), TARGET_CHECK_DEFAULT, nullptr); + Acore::WorldObjectListSearcher searcher(GetCaster(), targetList, check); + Cell::VisitObjects(GetCaster(), searcher, SEARCH_RADIUS); + + Unit* seatTarget = nullptr; + float minDist = SEARCH_RADIUS * SEARCH_RADIUS; + + for (WorldObject* obj : targetList) + { + Unit* unit = obj->ToUnit(); + if (!unit || unit->GetEntry() != NPC_SEAT) continue; + + Vehicle* seat = unit->GetVehicleKit(); + Unit* device = seat ? seat->GetPassenger(SEAT_DEVICE) : nullptr; + if (!seat || seat->GetPassenger(0) || !device || device->GetCurrentSpell(CURRENT_CHANNELED_SPELL)) continue; + + float dist = unit->GetExactDistSq(dst); + if (dist < minDist) { - // use 99 because it is 3d search - std::list targetList; - Acore::WorldObjectSpellAreaTargetCheck check(99, GetExplTargetDest(), GetCaster(), GetCaster(), GetSpellInfo(), TARGET_CHECK_DEFAULT, nullptr); - Acore::WorldObjectListSearcher searcher(GetCaster(), targetList, check); - Cell::VisitObjects(GetCaster(), searcher, 99.0f); - float minDist = 99 * 99; - Unit* target = nullptr; - for (std::list::iterator itr = targetList.begin(); itr != targetList.end(); ++itr) - { - if (Unit* unit = (*itr)->ToUnit()) - if (unit->GetEntry() == NPC_SEAT) - if (Vehicle* seat = unit->GetVehicleKit()) - if (!seat->GetPassenger(0)) - if (Unit* device = seat->GetPassenger(2)) - if (!device->GetCurrentSpell(CURRENT_CHANNELED_SPELL)) - { - float dist = unit->GetExactDistSq(targets.GetDstPos()); - if (dist < minDist) - { - minDist = dist; - target = unit; - } - } - } - if (target && target->IsWithinDist2d(targets.GetDstPos(), GetSpellInfo()->Effects[EFFECT_0].CalcRadius() * 2)) // now we use *2 because the location of the seat is not correct - { - passenger->ExitVehicle(); - passenger->EnterVehicle(target, 0); - } - else - { - passenger->ExitVehicle(); - float x, y, z; - targets.GetDstPos()->GetPosition(x, y, z); - passenger->GetMotionMaster()->MoveJump(x, y, z, targets.GetSpeedXY(), targets.GetSpeedZ()); - } + minDist = dist; + seatTarget = unit; } + } + + // Launch passenger toward destination + float x, y, z; + dst->GetPosition(x, y, z); + passenger->ExitVehicle(); + passenger->GetMotionMaster()->MoveJump(x, y, z, GetSpell()->m_targets.GetSpeedXY(), GetSpell()->m_targets.GetSpeedZ()); + + if (seatTarget && seatTarget->IsWithinDist2d(dst, GetSpellInfo()->Effects[EFFECT_0].CalcRadius() * 2)) // now we use *2 because the location of the seat is not correct + passenger->CastCustomSpell(SPELL_HOOKSHOT_AURA, SPELLVALUE_AURA_DURATION, 5000, passenger, true); } void Register() override @@ -1404,6 +1441,28 @@ class spell_vehicle_throw_passenger : public SpellScript } }; +// 62336 Hookshot Aura +class spell_hookshot_aura : public AuraScript +{ + PrepareAuraScript(spell_hookshot_aura); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_HOOKSHOT }); + } + + void OnPeriodic(AuraEffect const* aurEff) + { + PreventDefaultAction(); + GetUnitOwner()->CastSpell(GetUnitOwner(), GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell, true); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_hookshot_aura::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } +}; + class spell_tar_blaze_aura : public AuraScript { PrepareAuraScript(spell_tar_blaze_aura); @@ -1476,31 +1535,6 @@ class spell_vehicle_grab_pyrite : public SpellScript } }; -class spell_vehicle_circuit_overload_aura : public AuraScript -{ - PrepareAuraScript(spell_vehicle_circuit_overload_aura); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_SYSTEMS_SHUTDOWN }); - } - - void OnPeriodic(AuraEffect const* /*aurEff*/) - { - if (Unit* target = GetTarget()) - if (int(target->GetAppliedAuras().count(SPELL_OVERLOAD_CIRCUIT)) >= (target->GetMap()->Is25ManRaid() ? 4 : 2)) - { - target->CastSpell(target, SPELL_SYSTEMS_SHUTDOWN, true); - target->RemoveAurasDueToSpell(SPELL_OVERLOAD_CIRCUIT); - } - } - - void Register() override - { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_vehicle_circuit_overload_aura::OnPeriodic, EFFECT_1, SPELL_AURA_PERIODIC_DUMMY); - } -}; - class spell_orbital_supports_aura : public AuraScript { PrepareAuraScript(spell_orbital_supports_aura); @@ -1757,9 +1791,9 @@ void AddSC_boss_flame_leviathan() RegisterSpellScript(spell_systems_shutdown_aura); RegisterSpellScript(spell_pursue); RegisterSpellScript(spell_vehicle_throw_passenger); + RegisterSpellScript(spell_hookshot_aura); RegisterSpellScript(spell_tar_blaze_aura); RegisterSpellScript(spell_vehicle_grab_pyrite); - RegisterSpellScript(spell_vehicle_circuit_overload_aura); RegisterSpellScript(spell_orbital_supports_aura); RegisterSpellScript(spell_thorims_hammer); RegisterSpellScript(spell_transitus_shield_beam_aura);