spell and solo lfg fix

This commit is contained in:
luis
2026-09-12 01:50:27 -03:00
parent 53d73abc74
commit b8e3f766bf
10 changed files with 376 additions and 4 deletions
+1 -1
View File
@@ -462,7 +462,7 @@ LfgCompatibility LFGQueue::CheckCompatibility(GuidList check)
return LFG_COMPATIBLES_WITH_LESS_PLAYERS;
}
if (check.size() == 1 && numPlayers != MAX_GROUP_SIZE && !hasFollowerDungeon)
if (!sLFGMgr->IsSoloLFG() && !forceMinPlayers && check.size() == 1 && numPlayers != MAX_GROUP_SIZE && !hasFollowerDungeon)
{
TC_LOG_DEBUG("lfg.queue.match.compatibility.check", "Guids: ({}) single group. Compatibles", GetDetailedMatchRoles(check));
LfgQueueDataContainer::iterator itQueue = QueueDataStore.find(check.front());
+153
View File
@@ -3623,12 +3623,46 @@ void Unit::_ApplyAuraEffect(Aura* aura, uint8 effIndex)
aurApp->_HandleEffect(effIndex, true);
}
//WowCommunity
namespace
{
// RAII helper: while a Fel Rush style dash bundle is being (un)applied we suppress the
// per-effect UpdateSpeed calls so the whole bundle lands in one movement transaction,
// then flush once on scope exit (see Unit::EndDeferDashMovementSpeedUpdates).
struct DashSpeedUpdateDeferGuard
{
Unit* UnitPtr;
bool Active;
DashSpeedUpdateDeferGuard(Unit* unit, bool active) : UnitPtr(unit), Active(active)
{
if (Active)
UnitPtr->BeginDeferDashMovementSpeedUpdates();
}
DashSpeedUpdateDeferGuard(DashSpeedUpdateDeferGuard const&) = delete;
DashSpeedUpdateDeferGuard& operator=(DashSpeedUpdateDeferGuard const&) = delete;
~DashSpeedUpdateDeferGuard()
{
if (Active)
UnitPtr->EndDeferDashMovementSpeedUpdates();
}
};
}
//WowCommunity
// handles effects of aura application
// should be done after registering aura in lists
void Unit::_ApplyAura(AuraApplication* aurApp, uint32 effMask)
{
Aura* aura = aurApp->GetBase();
//WowCommunity
DashSpeedUpdateDeferGuard deferGuard(this, GetTypeId() == TYPEID_PLAYER && aura->GetSpellInfo()->IsDashMovementBundle());
//WowCommunity
_RemoveNoStackAurasDueToAura(aura, false);
if (aurApp->GetRemoveMode())
@@ -3732,6 +3766,10 @@ void Unit::_UnapplyAura(AuraApplicationMap::iterator& i, AuraRemoveMode removeMo
aurApp->_Remove();
aura->_UnapplyForTarget(this, caster, aurApp);
//WowCommunity
DashSpeedUpdateDeferGuard deferGuard(this, GetTypeId() == TYPEID_PLAYER && aura->GetSpellInfo()->IsDashMovementBundle());
//WowCommunity
// remove effects of the spell - needs to be done after removing aura from lists
for (AuraEffect const* aurEff : aura->GetAuraEffects())
if (aurApp->HasEffect(aurEff->GetEffIndex()))
@@ -9068,6 +9106,33 @@ void Unit::UpdateSpeed(UnitMoveType mtype)
speed = min_speed;
}
//WowCommunity
// SPELL_AURA_MOD_SPEED_NO_CONTROL: dash abilities push an absolute yards/sec floor through
// SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED instead of a percentage speed mod.
// Threshold note (UNVERIFIED, inherited from the source branch): Fel Rush is reported to use a
// 66 yd/s floor while Monk Roll's aura 191 value (~35) is a cap only, hence only normalization
// values at or above 60 yd/s are treated as a forced floor. Needs an in-game/sniff re-check.
if (GetMaxPositiveAuraModifier(SPELL_AURA_MOD_SPEED_NO_CONTROL))
{
if (mtype == MOVE_RUN || mtype == MOVE_RUN_BACK || mtype == MOVE_WALK)
{
if (float normalization = GetMaxPositiveAuraModifier(SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED))
{
float constexpr felRushNormalizationYardsPerSec = 60.0f;
if (normalization >= felRushNormalizationYardsPerSec)
{
float baseSpeed = IsControlledByPlayer() ? playerBaseMoveSpeed[mtype] : baseMoveSpeed[mtype];
if (baseSpeed > 0.0f)
{
float forcedRate = normalization / baseSpeed;
if (speed < forcedRate)
speed = forcedRate;
}
}
}
}
}
//WowCommunity
SetSpeedRate(mtype, speed);
}
@@ -15582,3 +15647,91 @@ void Unit::GetAttackableUnitListInRange(std::list<Unit*>& list, float fMaxSearch
cell.Visit(p, world_unit_searcher, *GetMap(), *this, fMaxSearchRange);
cell.Visit(p, grid_unit_searcher, *GetMap(), *this, fMaxSearchRange);
}
void Unit::BeginDeferDashMovementSpeedUpdates()
{
++_deferDashMovementSpeedUpdates;
_dashMovementSpeedUpdatesFinalized = false;
}
void Unit::EndDeferDashMovementSpeedUpdates()
{
ASSERT(_deferDashMovementSpeedUpdates > 0);
--_deferDashMovementSpeedUpdates;
if (_deferDashMovementSpeedUpdates == 0)
{
if (!_dashMovementSpeedUpdatesFinalized)
{
UpdateSpeed(MOVE_RUN);
UpdateSpeed(MOVE_RUN_BACK);
UpdateSpeed(MOVE_WALK);
UpdateSpeed(MOVE_SWIM);
UpdateSpeed(MOVE_FLIGHT);
}
RestoreDeferredDashGravity();
_dashMovementSpeedUpdatesFinalized = false;
}
}
void Unit::PrepareDashMovementState()
{
// Matches the client's gravity-disable acknowledgement during a dash: moving Forward with
// gravity off and no horizontal fall carry (HasFallDirection false).
m_movementInfo.jump.sinAngle = 0.0f;
m_movementInfo.jump.cosAngle = 0.0f;
m_movementInfo.jump.xyspeed = 0.0f;
RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING | MOVEMENTFLAG_FALLING_FAR);
AddUnitMovementFlag(MOVEMENTFLAG_FORWARD);
}
void Unit::FinalizeDashMovementSpeedUpdates()
{
if (_dashMovementSpeedUpdatesFinalized)
return;
_dashMovementSpeedUpdatesFinalized = true;
PrepareDashMovementState();
UpdateSpeed(MOVE_RUN);
UpdateSpeed(MOVE_RUN_BACK);
UpdateSpeed(MOVE_WALK);
UpdateSpeed(MOVE_SWIM);
UpdateSpeed(MOVE_FLIGHT);
}
void Unit::CleanupDashMovementAfterAuraEnd()
{
m_movementInfo.jump.Reset();
RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING | MOVEMENTFLAG_FALLING_FAR);
if (Player* playerMover = GetPlayerMovingMe())
{
WorldPackets::Movement::MoveUpdate moveUpdate;
moveUpdate.Status = &m_movementInfo;
SendMessageToSet(moveUpdate.Write(), playerMover);
}
}
void Unit::DeferDashGravityRestore()
{
_deferDashGravityRestore = true;
}
void Unit::RestoreDeferredDashGravity()
{
if (!_deferDashGravityRestore)
return;
_deferDashGravityRestore = false;
if (HasAuraType(SPELL_AURA_MOD_ROOT_DISABLE_GRAVITY)
|| HasAuraType(SPELL_AURA_MOD_STUN_DISABLE_GRAVITY)
|| HasAuraType(SPELL_AURA_DISABLE_GRAVITY)
|| (IsCreature() && ToCreature()->IsFloating()))
return;
SetDisableGravity(false);
}
+16
View File
@@ -1187,6 +1187,16 @@ class TC_GAME_API Unit : public WorldObject
bool SetCanAdvFly(bool enable);
bool SetMoveCantSwim(bool cantSwim);
void SendSetVehicleRecId(uint32 vehicleId);
//WowCommunity
void BeginDeferDashMovementSpeedUpdates();
void EndDeferDashMovementSpeedUpdates();
bool IsDeferringDashMovementSpeedUpdates() const { return _deferDashMovementSpeedUpdates > 0; }
void FinalizeDashMovementSpeedUpdates();
void PrepareDashMovementState();
void CleanupDashMovementAfterAuraEnd();
void DeferDashGravityRestore();
void RestoreDeferredDashGravity();
//WowCommunity
MovementForces const* GetMovementForces() const { return _movementForces.get(); }
void ApplyMovementForce(ObjectGuid id, Position origin, float magnitude, MovementForceType type, Position direction = {}, ObjectGuid transportGuid = ObjectGuid::Empty);
@@ -1995,6 +2005,12 @@ class TC_GAME_API Unit : public WorldObject
std::array<float, MAX_MOVE_TYPE> m_speed_rate;
std::array<float, ADV_FLYING_MAX_SPEED_TYPE> m_advFlyingSpeed;
//WowCommunity
uint32 _deferDashMovementSpeedUpdates = 0;
bool _dashMovementSpeedUpdatesFinalized = false;
bool _deferDashGravityRestore = false;
//WowCommunity
Unit* m_unitMovedByMe; // only ever set for players, and only for direct client control
Player* m_playerMovingMe; // only set for direct client control (possess effects, vehicles and similar)
Unit* m_charmer; // Unit that is charming ME
@@ -457,7 +457,7 @@ enum AuraType : uint32
SPELL_AURA_SPELL_OVERRIDE_NAME_GROUP = 370, // picks a random SpellOverrideName id from a group (group id in miscValue)
SPELL_AURA_DISABLE_AUTOATTACK = 371,
SPELL_AURA_OVERRIDE_MOUNT_FROM_SET = 372, // NYI
SPELL_AURA_MOD_SPEED_NO_CONTROL = 373, // NYI
SPELL_AURA_MOD_SPEED_NO_CONTROL = 373,
SPELL_AURA_MODIFY_FALL_DAMAGE_PCT = 374,
SPELL_AURA_HIDE_MODEL_AND_EQUIPEMENT_SLOTS = 375,
SPELL_AURA_MOD_CURRENCY_GAIN_FROM_SOURCE = 376, // NYI
@@ -442,7 +442,7 @@ NonDefaultConstructible<pAuraEffectHandler> AuraEffectHandler[TOTAL_AURAS]=
&AuraEffect::HandleNULL, //370 SPELL_AURA_SPELL_OVERRIDE_NAME_GROUP
&AuraEffect::HandleNoImmediateEffect, //371 SPELL_AURA_DISABLE_AUTOATTACK implemented in Unit::_UpdateAutoRepeatSpell and Unit::AttackerStateUpdate
&AuraEffect::HandleNULL, //372 SPELL_AURA_OVERRIDE_MOUNT_FROM_SET
&AuraEffect::HandleNULL, //373 SPELL_AURA_MOD_SPEED_NO_CONTROL
&AuraEffect::HandleAuraModSpeedNoControl, //373 SPELL_AURA_MOD_SPEED_NO_CONTROL
&AuraEffect::HandleNoImmediateEffect, //374 SPELL_AURA_MODIFY_FALL_DAMAGE_PCT implemented in Player::HandleFall
&AuraEffect::HandleNULL, //375 SPELL_AURA_HIDE_MODEL_AND_EQUIPEMENT_SLOTS implemented clientside
&AuraEffect::HandleNULL, //376 SPELL_AURA_MOD_CURRENCY_GAIN_FROM_SOURCE
@@ -3286,6 +3286,20 @@ static void HandleAuraDisableGravity(Unit* target, bool apply)
|| (target->IsCreature() && target->ToCreature()->IsFloating()))
return;
if (target->IsDeferringDashMovementSpeedUpdates())
{
if (apply)
target->FinalizeDashMovementSpeedUpdates();
else
{
// Keep gravity off until the whole dash bundle has been unapplied, otherwise the client
// starts falling mid-transaction.
target->CleanupDashMovementAfterAuraEnd();
target->DeferDashGravityRestore();
return;
}
}
if (target->SetDisableGravity(apply))
if (!apply && !target->IsFlying())
target->GetMotionMaster()->MoveFall();
@@ -3488,6 +3502,9 @@ void AuraEffect::HandleAuraModIncreaseSpeed(AuraApplication const* aurApp, uint8
Unit* target = aurApp->GetTarget();
if (target->IsDeferringDashMovementSpeedUpdates())
return;
target->UpdateSpeed(MOVE_RUN);
}
@@ -3564,11 +3581,31 @@ void AuraEffect::HandleAuraModUseNormalSpeed(AuraApplication const* aurApp, uint
Unit* target = aurApp->GetTarget();
if (target->IsDeferringDashMovementSpeedUpdates())
return;
target->UpdateSpeed(MOVE_RUN);
target->UpdateSpeed(MOVE_SWIM);
target->UpdateSpeed(MOVE_FLIGHT);
}
void AuraEffect::HandleAuraModSpeedNoControl(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const
{
if (!(mode & AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK))
return;
Unit* target = aurApp->GetTarget();
if (target->IsDeferringDashMovementSpeedUpdates())
return;
target->UpdateSpeed(MOVE_RUN);
target->UpdateSpeed(MOVE_RUN_BACK);
target->UpdateSpeed(MOVE_WALK);
target->UpdateSpeed(MOVE_SWIM);
target->UpdateSpeed(MOVE_FLIGHT);
}
void AuraEffect::HandleAuraModMinimumSpeedRate(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const
{
if (!(mode & AURA_EFFECT_HANDLE_REAL))
@@ -3576,6 +3613,9 @@ void AuraEffect::HandleAuraModMinimumSpeedRate(AuraApplication const* aurApp, ui
Unit* target = aurApp->GetTarget();
if (target->IsDeferringDashMovementSpeedUpdates())
return;
target->UpdateSpeed(MOVE_RUN);
}
@@ -222,6 +222,7 @@ class TC_GAME_API AuraEffect
void HandleAuraModIncreaseSwimSpeed(AuraApplication const* aurApp, uint8 mode, bool apply) const;
void HandleAuraModDecreaseSpeed(AuraApplication const* aurApp, uint8 mode, bool apply) const;
void HandleAuraModUseNormalSpeed(AuraApplication const* aurApp, uint8 mode, bool apply) const;
void HandleAuraModSpeedNoControl(AuraApplication const* aurApp, uint8 mode, bool apply) const;
void HandleAuraModMinimumSpeedRate(AuraApplication const* aurApp, uint8 mode, bool apply) const;
void HandleModMovementForceMagnitude(AuraApplication const* aurApp, uint8 mode, bool apply) const;
void HandleAuraModAdvFlyingSpeed(AuraApplication const* aurApp, uint8 mode, bool apply) const;
+10
View File
@@ -5158,3 +5158,13 @@ bool SpellInfo::HasLabel(uint32 labelId) const
{
return Labels.contains(labelId);
}
bool SpellInfo::IsDashMovementBundle() const
{
// Many spells share HasAura(SPELL_AURA_MOD_SPEED_NO_CONTROL) && HasAura(SPELL_AURA_DISABLE_GRAVITY)
// (Evoker Hover, Void Dash, Crane Rush, ...), but FinalizeDashMovementSpeedUpdates and
// PrepareDashMovementState model the Fel Rush *air* dash specifically and must not fire on the
// others. Keep this an explicit allowlist rather than an aura-shape test.
return Id == 197923 // Fel Rush air bundle
|| Id == 389659; // Fel Rush air bundle (variant)
}
+3 -1
View File
@@ -454,7 +454,9 @@ public:
bool HasAreaAuraEffect() const;
bool HasOnlyDamageEffects() const;
bool HasTargetType(::Targets target) const;
//WowCommunity
bool IsDashMovementBundle() const;
//WowCommunity
bool HasAttribute(SpellAttr0 attribute) const { return !!(Attributes & attribute); }
bool HasAttribute(SpellAttr1 attribute) const { return !!(AttributesEx & attribute); }
bool HasAttribute(SpellAttr2 attribute) const { return !!(AttributesEx2 & attribute); }
+19
View File
@@ -5755,6 +5755,25 @@ void SpellMgr::LoadSpellInfoTargetCaps()
spellInfo->_LoadSqrtTargetLimit(5, 0, 1226033, EFFECT_0, {}, {});
});
// Fel Rush air dash (197923): the client-side bundle triggers the momentum/dash-end helpers from
// effects 8 and 10 while effect 6 points at a dead trigger. UNVERIFIED against a live sniff -
// ported from the source branch, re-check the trigger ids if air Fel Rush misbehaves.
ApplySpellFix({ 197923 }, [](SpellInfo* spellInfo)
{
ApplySpellEffectFix(spellInfo, EFFECT_6, [](SpellEffectInfo* spellEffectInfo)
{
spellEffectInfo->TriggerSpell = 0;
});
ApplySpellEffectFix(spellInfo, EFFECT_8, [](SpellEffectInfo* spellEffectInfo)
{
spellEffectInfo->TriggerSpell = 199737;
});
ApplySpellEffectFix(spellInfo, EFFECT_10, [](SpellEffectInfo* spellEffectInfo)
{
spellEffectInfo->TriggerSpell = 346123;
});
});
TC_LOG_INFO("server.loading", ">> Loaded SpellInfo target caps in {} ms", GetMSTimeDiffToNow(oldMSTime));
}
+131
View File
@@ -3182,6 +3182,135 @@ class spell_dh_collapsing_star_damage : public SpellScript
int32 _apocalypsePct = 0;
};
// 195072 - Fel Rush
class spell_dh_fel_rush : public SpellScript
{
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_DH_FEL_RUSH_GROUND, SPELL_DH_FEL_RUSH_WATER_AIR, SPELL_DH_FEL_RUSH_DMG,
SPELL_DH_GLIDE, SPELL_DH_GLIDE_DURATION });
}
SpellCastResult CheckCast()
{
if (GetCaster()->HasUnitState(UNIT_STATE_ROOT))
return SPELL_FAILED_ROOTED;
return SPELL_CAST_OK;
}
void CastPathDamage() const
{
Unit* caster = GetCaster();
float dashDistance = float(GetEffectInfo(EFFECT_0).CalcValue(caster));
if (dashDistance <= 0.0f)
return;
caster->CastSpell(caster->GetFirstCollisionPosition(dashDistance, 0.0f), SPELL_DH_FEL_RUSH_DMG, CastSpellExtraArgsInit{
.TriggerFlags = TRIGGERED_FULL_MASK,
.TriggeringSpell = GetSpell()
});
}
void PrepareDash(Unit* caster) const
{
caster->RemoveAurasDueToSpell(SPELL_DH_GLIDE);
caster->RemoveAurasDueToSpell(SPELL_DH_GLIDE_DURATION);
caster->m_movementInfo.inertia.reset();
caster->m_movementInfo.advFlying.reset();
if (Player* player = caster->ToPlayer())
{
// Mirror of the 250 ms Fel Rush lockout spell_dh_glide starts, in the other direction.
player->GetSpellHistory()->StartCooldown(sSpellMgr->AssertSpellInfo(SPELL_DH_GLIDE, GetCastDifficulty()), 0, nullptr, false, 250ms);
player->UpdateSpeed(MOVE_FLIGHT);
}
}
void HandleGroundDash(SpellEffIndex /*effIndex*/) const
{
Unit* caster = GetCaster();
if (caster->IsFalling() && !caster->IsInWater())
return;
CastPathDamage();
PrepareDash(caster);
caster->CastSpell(caster, SPELL_DH_FEL_RUSH_GROUND, CastSpellExtraArgsInit{
.TriggerFlags = TRIGGERED_FULL_MASK,
.TriggeringSpell = GetSpell()
});
}
void HandleAirDash(SpellEffIndex /*effIndex*/) const
{
Unit* caster = GetCaster();
if (!caster->IsFalling() || caster->IsInWater())
return;
CastPathDamage();
PrepareDash(caster);
caster->CastSpell(caster, SPELL_DH_FEL_RUSH_WATER_AIR, CastSpellExtraArgsInit{
.TriggerFlags = TRIGGERED_FULL_MASK,
.TriggeringSpell = GetSpell()
});
}
void Register() override
{
OnCheckCast += SpellCheckCastFn(spell_dh_fel_rush::CheckCast);
OnEffectHitTarget += SpellEffectFn(spell_dh_fel_rush::HandleGroundDash, EFFECT_0, SPELL_EFFECT_DUMMY);
OnEffectHitTarget += SpellEffectFn(spell_dh_fel_rush::HandleAirDash, EFFECT_1, SPELL_EFFECT_DUMMY);
}
};
// 197922 - Fel Rush (ground dash bundle)
// 197923 - Fel Rush (air / water dash bundle)
class spell_dh_fel_rush_aura : public AuraScript
{
// EFFECT_5 on 197922 is SPELL_AURA_MECHANIC_IMMUNITY with base points 0; the dash is supposed to
// shed the mechanic rather than grant immunity to it, so the amount is driven negative.
void CalcImmunityAmount(AuraEffect const* /*aurEff*/, SpellEffectValue& amount, bool& /*canBeRecalculated*/) const
{
amount -= 100.0f;
}
// While the dash owns movement the run-back speed must not be touched - the dash engine batches
// the speed updates itself and re-sends them once it is done.
void ChangeRunBackSpeed(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) const
{
Unit* target = GetTarget();
if (target->IsDeferringDashMovementSpeedUpdates())
return;
target->SetSpeed(MOVE_RUN_BACK, target->GetSpeed(MOVE_RUN));
}
void RestoreRunBackSpeed(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) const
{
Unit* target = GetTarget();
if (target->IsDeferringDashMovementSpeedUpdates())
return;
target->UpdateSpeed(MOVE_RUN_BACK);
}
void Register() override
{
// Aura 191 SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED sits on EFFECT_4 of both bundles, but
// SPELL_AURA_MECHANIC_IMMUNITY is EFFECT_5 on 197922 and EFFECT_7 on 197923, so the amount
// hook is only valid for the ground bundle. [DB2 12.0.7.68275 SpellEffect]
AfterEffectApply += AuraEffectApplyFn(spell_dh_fel_rush_aura::ChangeRunBackSpeed, EFFECT_4, SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED, AURA_EFFECT_HANDLE_REAL);
AfterEffectRemove += AuraEffectRemoveFn(spell_dh_fel_rush_aura::RestoreRunBackSpeed, EFFECT_4, SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED, AURA_EFFECT_HANDLE_REAL);
if (m_scriptSpellId == SPELL_DH_FEL_RUSH_GROUND)
DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_dh_fel_rush_aura::CalcImmunityAmount, EFFECT_5, SPELL_AURA_MECHANIC_IMMUNITY);
}
};
void AddSC_demon_hunter_spell_scripts()
{
RegisterSpellScript(spell_dh_army_unto_oneself);
@@ -3316,4 +3445,6 @@ void AddSC_demon_hunter_spell_scripts()
RegisterSpellScript(spell_dh_void_metamorphosis_devourer);
RegisterSpellScript(spell_dh_collapsing_star);
RegisterSpellScript(spell_dh_collapsing_star_damage);
RegisterSpellScript(spell_dh_fel_rush);
RegisterSpellScript(spell_dh_fel_rush_aura);
}