diff --git a/data/sql/updates/db_world/2025_11_29_00.sql b/data/sql/updates/db_world/2025_11_29_00.sql new file mode 100644 index 0000000000..bc7f243229 --- /dev/null +++ b/data/sql/updates/db_world/2025_11_29_00.sql @@ -0,0 +1,5 @@ +-- DB update 2025_11_28_08 -> 2025_11_29_00 +-- Add creature_text for Eck the Ferocious "grows increasingly crazed" emote +DELETE FROM `creature_text` WHERE `CreatureID` = 29932 AND `GroupID` = 1; +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) +VALUES (29932, 1, 0, '%s grows increasingly crazed!', 16, 0, 100, 0, 0, 0, 30727, 0, 'Eck the Ferocious - Crazed Warning'); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index b5e385d3e5..817bc25b77 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -14085,7 +14085,22 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo if (repThisToTarget > REP_NEUTRAL || (repTargetToThis = target->GetReactionTo(this)) > REP_NEUTRAL) - return false; + { + // contested guards can attack contested PvP players even though players may be friendly + if (!target->IsControlledByPlayer()) + return false; + + bool isContestedGuard = false; + if (FactionTemplateEntry const* entry = GetFactionTemplateEntry()) + isContestedGuard = entry->factionFlags & FACTION_TEMPLATE_FLAG_ATTACK_PVP_ACTIVE_PLAYERS; + + bool isContestedPvp = false; + if (Player const* player = target->GetCharmerOrOwnerPlayerOrPlayerItself()) + isContestedPvp = player->HasPlayerFlag(PLAYER_FLAGS_CONTESTED_PVP); + + if (!isContestedGuard && !isContestedPvp) + return false; + } // Not all neutral creatures can be attacked (even some unfriendly faction does not react aggresive to you, like Sporaggar) if (repThisToTarget == REP_NEUTRAL && diff --git a/src/server/game/Spells/SpellInfoCorrections.cpp b/src/server/game/Spells/SpellInfoCorrections.cpp index 1dacdbd852..ca45356219 100644 --- a/src/server/game/Spells/SpellInfoCorrections.cpp +++ b/src/server/game/Spells/SpellInfoCorrections.cpp @@ -5283,14 +5283,6 @@ void SpellMgr::LoadSpellInfoCorrections() factionTemplateEntry = const_cast(sFactionTemplateStore.LookupEntry(1921)); // The Taunka factionTemplateEntry->hostileMask |= 8; - // Remove 1 from guards friendly mask, making able to attack players - factionTemplateEntry = const_cast(sFactionTemplateStore.LookupEntry(1857)); // Area 52 Bruiser - factionTemplateEntry->friendlyMask &= ~1; - factionTemplateEntry = const_cast(sFactionTemplateStore.LookupEntry(1806)); // Netherstorm Agent - factionTemplateEntry->friendlyMask &= ~1; - factionTemplateEntry = const_cast(sFactionTemplateStore.LookupEntry(1812)); // K3 Bruiser - factionTemplateEntry->friendlyMask &= ~1; - // Remove vehicles attr, making accessories selectable VehicleSeatEntry* vse = const_cast(sVehicleSeatStore.LookupEntry(4689)); // Siege Engine, Accessory vse->m_flags &= ~VEHICLE_SEAT_FLAG_PASSENGER_NOT_SELECTABLE; diff --git a/src/server/scripts/Northrend/Gundrak/boss_eck.cpp b/src/server/scripts/Northrend/Gundrak/boss_eck.cpp index 9e7a6bbc97..12f84b71f1 100644 --- a/src/server/scripts/Northrend/Gundrak/boss_eck.cpp +++ b/src/server/scripts/Northrend/Gundrak/boss_eck.cpp @@ -36,7 +36,8 @@ enum Misc EVENT_ECK_BITE = 2, EVENT_ECK_SPIT = 3, EVENT_ECK_SPRING = 4, - EVENT_ECK_HEALTH = 5 + EVENT_ECK_CRAZED_EMOTE = 5, + EMOTE_CRAZED = 1 }; class boss_eck : public CreatureScript @@ -89,7 +90,8 @@ public: void JustEngagedWith(Unit* who) override { BossAI::JustEngagedWith(who); - events.ScheduleEvent(EVENT_ECK_BERSERK, 60s, 90s); + events.ScheduleEvent(EVENT_ECK_CRAZED_EMOTE, 76s, 78s); + events.ScheduleEvent(EVENT_ECK_BERSERK, 90s); events.ScheduleEvent(EVENT_ECK_BITE, 5s); events.ScheduleEvent(EVENT_ECK_SPIT, 10s, 37s); events.ScheduleEvent(EVENT_ECK_SPRING, 10s, 24s); @@ -111,18 +113,11 @@ public: switch (events.ExecuteEvent()) { - case EVENT_ECK_HEALTH: - if (me->HealthBelowPct(21)) - { - events.CancelEvent(EVENT_ECK_BERSERK); - me->CastSpell(me, SPELL_ECK_BERSERK, false); - break; - } - events.ScheduleEvent(EVENT_ECK_HEALTH, 1s); + case EVENT_ECK_CRAZED_EMOTE: + Talk(EMOTE_CRAZED); break; case EVENT_ECK_BERSERK: me->CastSpell(me, SPELL_ECK_BERSERK, false); - events.CancelEvent(EVENT_ECK_HEALTH); break; case EVENT_ECK_BITE: me->CastSpell(me->GetVictim(), SPELL_ECK_BITE, false); diff --git a/src/server/scripts/Spells/spell_paladin.cpp b/src/server/scripts/Spells/spell_paladin.cpp index 62389db5b4..0acf87cb21 100644 --- a/src/server/scripts/Spells/spell_paladin.cpp +++ b/src/server/scripts/Spells/spell_paladin.cpp @@ -961,6 +961,24 @@ class spell_pal_lay_on_hands : public SpellScript return true; } + void HandleMaxHealthHeal(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + Unit* target = GetExplTargetUnit(); + + if (!target || !caster) + return; + + uint32 baseHeal = caster->GetMaxHealth(); + uint32 modifiedHeal = target->SpellHealingBonusTaken(caster, GetSpellInfo(), baseHeal, HEAL); + + // EffectHealMaxHealth() ignores healing modifiers, so we pre-apply the + // difference here; this delta will be added on top of the raw heal. + int64 healAdjustment = int64(modifiedHeal) - int64(baseHeal); + + SetHitHeal(healAdjustment); + } + SpellCastResult CheckCast() { Unit* caster = GetCaster(); @@ -1000,6 +1018,7 @@ class spell_pal_lay_on_hands : public SpellScript { OnCheckCast += SpellCheckCastFn(spell_pal_lay_on_hands::CheckCast); AfterHit += SpellHitFn(spell_pal_lay_on_hands::HandleScript); + OnEffectHitTarget += SpellEffectFn(spell_pal_lay_on_hands::HandleMaxHealthHeal, EFFECT_0, SPELL_EFFECT_HEAL_MAX_HEALTH); } int32 _manaAmount; diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp index 23b46b039d..cb669c3f0e 100644 --- a/src/server/scripts/World/npcs_special.cpp +++ b/src/server/scripts/World/npcs_special.cpp @@ -563,10 +563,6 @@ public: if (!SpawnAssoc) return; - // check if they're hostile - if (!(me->IsHostileTo(who) || who->IsHostileTo(me))) - return; - if (me->IsValidAttackTarget(who)) { Player* playerTarget = who->ToPlayer();