From 27e4ad8725de0037a664bfe55afc49b95418df90 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Fri, 26 Jun 2026 02:20:36 -0300 Subject: [PATCH] feat(Core/SAI): Add health pct check mode to SMART_EVENT_DAMAGED (#24693) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Gultask <100873791+Gultask@users.noreply.github.com> Co-authored-by: Ludwig Co-authored-by: Claude Opus 4.8 (1M context) --- .../game/AI/SmartScripts/SmartScript.cpp | 20 +++++++++++++- .../game/AI/SmartScripts/SmartScriptMgr.cpp | 26 ++++++++++++++++++- .../game/AI/SmartScripts/SmartScriptMgr.h | 2 +- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index ee060c635..a5cf7b90c 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -4528,7 +4528,6 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui break; } case SMART_EVENT_RECEIVE_HEAL: - case SMART_EVENT_DAMAGED: case SMART_EVENT_DAMAGED_TARGET: { if (var0 > e.event.minMaxRepeat.max || var0 < e.event.minMaxRepeat.min) @@ -4537,6 +4536,25 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui ProcessAction(e, unit); break; } + case SMART_EVENT_DAMAGED: + { + if (e.event.minMaxRepeat.rangeMin) // health check mode + { + if (!me || !me->IsEngaged() || !me->GetMaxHealth()) + return; + if (!me->HealthBelowPctDamaged(e.event.minMaxRepeat.rangeMin, var0)) + return; + ProcessAction(e, unit); + } + else + { + if (var0 > e.event.minMaxRepeat.max || var0 < e.event.minMaxRepeat.min) + return; + RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax); + ProcessAction(e, unit); + } + break; + } case SMART_EVENT_MOVEMENTINFORM: { if ((e.event.movementInform.type && var0 != e.event.movementInform.type) || (e.event.movementInform.id && var1 != e.event.movementInform.id)) diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp index acc23295b..ac0216405 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp @@ -282,6 +282,12 @@ void SmartAIMgr::LoadSmartAIFromDB() if (temp.event.minMaxRepeat.repeatMin == 0 && temp.event.minMaxRepeat.repeatMax == 0) temp.event.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE; break; + case SMART_EVENT_DAMAGED: + // health check mode stays below the threshold on every following hit, so force one-shot; + // normal mode keeps repeatMin/repeatMax as a cooldown (0 = fire on every hit), so leave it repeatable. + if (temp.event.minMaxRepeat.rangeMin) + temp.event.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE; + break; case SMART_EVENT_VICTIM_CASTING: case SMART_EVENT_FRIENDLY_IS_CC: if (temp.event.friendlyCC.repeatMin == 0 && temp.event.friendlyCC.repeatMax == 0) @@ -1075,7 +1081,6 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) case SMART_EVENT_MANA_PCT: case SMART_EVENT_TARGET_HEALTH_PCT: case SMART_EVENT_TARGET_MANA_PCT: - case SMART_EVENT_DAMAGED: case SMART_EVENT_DAMAGED_TARGET: case SMART_EVENT_RECEIVE_HEAL: if (!IsMinMaxValid(e, e.event.minMaxRepeat.min, e.event.minMaxRepeat.max)) @@ -1084,6 +1089,25 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) if (!IsMinMaxValid(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax)) return false; break; + case SMART_EVENT_DAMAGED: + if (e.event.minMaxRepeat.rangeMin) // health check mode + { + if (e.event.minMaxRepeat.rangeMin > 100) + { + LOG_ERROR("sql.sql", "SmartAIMgr: Entry {} SourceType {} Event {} Action {} has invalid health pct value ({}), must be between 1 and 100, skipped.", + e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.minMaxRepeat.rangeMin); + return false; + } + } + else // normal mode + { + if (!IsMinMaxValid(e, e.event.minMaxRepeat.min, e.event.minMaxRepeat.max)) + return false; + + if (!IsMinMaxValid(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax)) + return false; + } + break; case SMART_EVENT_AREA_RANGE: case SMART_EVENT_AREA_CASTING: case SMART_EVENT_IS_BEHIND_TARGET: diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index 1105fe559..a8ae1cc9f 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -128,7 +128,7 @@ enum SMART_EVENT SMART_EVENT_CHARMED = 29, // onRemove (0 - on apply, 1 - on remove) SMART_EVENT_CHARMED_TARGET = 30, // NONE SMART_EVENT_SPELLHIT_TARGET = 31, // SpellID, School, CooldownMin, CooldownMax - SMART_EVENT_DAMAGED = 32, // MinDmg, MaxDmg, CooldownMin, CooldownMax + SMART_EVENT_DAMAGED = 32, // MinDmg, MaxDmg, CooldownMin, CooldownMax, rangeMin (hpPct, if set: health check mode, one-shot) SMART_EVENT_DAMAGED_TARGET = 33, // MinDmg, MaxDmg, CooldownMin, CooldownMax SMART_EVENT_MOVEMENTINFORM = 34, // MovementType(any), PointID, PathId(0 - any) SMART_EVENT_SUMMON_DESPAWNED = 35, // Entry, CooldownMin, CooldownMax