diff --git a/data/sql/updates/pending_db_world/rev_1687966694240866600.sql b/data/sql/updates/pending_db_world/rev_1687966694240866600.sql new file mode 100644 index 000000000..5f211415f --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1687966694240866600.sql @@ -0,0 +1,3 @@ +-- +UPDATE `smart_scripts` SET `event_param5`=5000 WHERE `entryorguid` = 16420 AND `source_type` = 0 AND `id`=0; +UPDATE `smart_scripts` SET `event_param5`=1000 WHERE `entryorguid` = 30085 AND `source_type` = 0 AND `id` = 1; diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index a6dfcfdfe..53f46cfcd 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -4208,34 +4208,105 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui break; case SMART_EVENT_NEAR_PLAYERS: { - ObjectVector units; - GetWorldObjectsInDist(units, static_cast(e.event.nearPlayer.radius)); + uint32 playerCount = 0; + ObjectVector targets; + GetWorldObjectsInDist(targets, static_cast(e.event.nearPlayer.radius)); - if (!units.empty()) + if (!targets.empty()) { - if (!unit || unit->GetTypeId() != TYPEID_PLAYER) - return; + for (WorldObject* target : targets) + { + if (IsPlayer(target)) + playerCount++; - if (units.size() >= e.event.nearPlayer.minCount) - ProcessAction(e, unit); + if (playerCount >= e.event.nearPlayer.minCount) + ProcessAction(e, target->ToUnit()); + } } - RecalcTimer(e, e.event.nearPlayer.checkTimer, e.event.nearPlayer.checkTimer); + RecalcTimer(e, e.event.nearPlayer.repeatMin, e.event.nearPlayer.repeatMax); break; } case SMART_EVENT_NEAR_PLAYERS_NEGATION: { - ObjectVector units; - GetWorldObjectsInDist(units, static_cast(e.event.nearPlayerNegation.radius)); + uint32 playerCount = 0; + ObjectVector targets; + GetWorldObjectsInDist(targets, static_cast(e.event.nearPlayerNegation.radius)); - if (!units.empty()) + if (!targets.empty()) { - if (!unit || unit->GetTypeId() != TYPEID_PLAYER) - return; + for (WorldObject* target : targets) + { + if (IsPlayer(target)) + playerCount++; + } - if (units.size() < e.event.nearPlayerNegation.minCount) + if (playerCount <= e.event.nearPlayerNegation.maxCount) ProcessAction(e, unit); } - RecalcTimer(e, e.event.nearPlayerNegation.checkTimer, e.event.nearPlayerNegation.checkTimer); + RecalcTimer(e, e.event.nearPlayerNegation.repeatMin, e.event.nearPlayerNegation.repeatMax); + break; + } + case SMART_EVENT_NEAR_UNIT: + { + uint32 unitCount = 0; + ObjectVector targets; + GetWorldObjectsInDist(targets, static_cast(e.event.nearUnit.range)); + + if (!targets.empty()) + { + if (e.event.nearUnit.type) + { + for (WorldObject* target : targets) + { + if (IsGameObject(target) && target->GetEntry() == e.event.nearUnit.entry) + unitCount++; + } + } + else + { + for (WorldObject* target : targets) + { + if (IsCreature(target) && target->GetEntry() == e.event.nearUnit.entry) + unitCount++; + } + } + + if (unitCount >= e.event.nearUnit.count) + ProcessAction(e, unit); + } + RecalcTimer(e, e.event.nearUnit.timer, e.event.nearUnit.timer); + break; + } + case SMART_EVENT_AREA_CASTING: + { + if (!me || !me->IsEngaged()) + return; + + float range = static_cast(e.event.areaCasting.range); + ThreatContainer::StorageType threatList = me->GetThreatMgr().GetThreatList(); + for (ThreatContainer::StorageType::const_iterator i = threatList.begin(); i != threatList.end(); ++i) + { + if (Unit* target = ObjectAccessor::GetUnit(*me, (*i)->getUnitGuid())) + { + if (e.event.areaCasting.range && !me->IsWithinDistInMap(target, range)) + continue; + + if (!target || !target->IsNonMeleeSpellCast(false, false, true)) + continue; + + if (e.event.areaCasting.spellId > 0) + if (Spell* currSpell = target->GetCurrentSpell(CURRENT_GENERIC_SPELL)) + if (currSpell->m_spellInfo->Id != e.event.areaCasting.spellId) + continue; + + ProcessAction(e, target); + RecalcTimer(e, e.event.areaCasting.repeatMin, e.event.areaCasting.repeatMin); + return; + } + } + + // If no targets are found and it's off cooldown, check again + RecalcTimer(e, e.event.areaCasting.checkTimer, e.event.areaCasting.checkTimer); break; } default: @@ -4274,6 +4345,12 @@ void SmartScript::InitTimer(SmartScriptHolder& e) case SMART_EVENT_DISTANCE_GAMEOBJECT: RecalcTimer(e, e.event.distance.repeat, e.event.distance.repeat); break; + case SMART_EVENT_NEAR_UNIT: + RecalcTimer(e, e.event.nearUnit.timer, e.event.nearUnit.timer); + break; + case SMART_EVENT_AREA_CASTING: + RecalcTimer(e, e.event.areaCasting.repeatMin, e.event.areaCasting.repeatMax); + break; default: e.active = true; break; @@ -4327,6 +4404,7 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff) { case SMART_EVENT_NEAR_PLAYERS: case SMART_EVENT_NEAR_PLAYERS_NEGATION: + case SMART_EVENT_NEAR_UNIT: case SMART_EVENT_UPDATE: case SMART_EVENT_UPDATE_OOC: case SMART_EVENT_UPDATE_IC: @@ -4336,6 +4414,7 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff) case SMART_EVENT_TARGET_MANA_PCT: case SMART_EVENT_RANGE: case SMART_EVENT_VICTIM_CASTING: + case SMART_EVENT_AREA_CASTING: case SMART_EVENT_FRIENDLY_HEALTH: case SMART_EVENT_FRIENDLY_IS_CC: case SMART_EVENT_FRIENDLY_MISSING_BUFF: diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp index df4481507..d82056b87 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp @@ -280,6 +280,10 @@ void SmartAIMgr::LoadSmartAIFromDB() if (temp.event.minMaxRepeat.min == 0 && temp.event.minMaxRepeat.max == 0) temp.event.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE; break; + case SMART_EVENT_AREA_CASTING: + if (temp.event.areaCasting.repeatMin == 0 && temp.event.areaCasting.repeatMax == 0) + temp.event.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE; + break; case SMART_EVENT_FRIENDLY_IS_CC: if (temp.event.friendlyCC.repeatMin == 0 && temp.event.friendlyCC.repeatMax == 0) temp.event.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE; @@ -346,10 +350,12 @@ void SmartAIMgr::LoadSmartAIFromDB() case SMART_EVENT_TARGET_MANA_PCT: case SMART_EVENT_RANGE: case SMART_EVENT_VICTIM_CASTING: + case SMART_EVENT_AREA_CASTING: case SMART_EVENT_TARGET_BUFFED: case SMART_EVENT_IS_BEHIND_TARGET: case SMART_EVENT_INSTANCE_PLAYER_ENTER: case SMART_EVENT_TRANSPORT_ADDCREATURE: + case SMART_EVENT_NEAR_PLAYERS: return true; default: return false; @@ -572,6 +578,8 @@ bool SmartAIMgr::CheckUnusedEventParams(SmartScriptHolder const& e) case SMART_EVENT_SUMMONED_UNIT_DIES: return sizeof(SmartEvent::summoned); case SMART_EVENT_NEAR_PLAYERS: return sizeof(SmartEvent::nearPlayer); case SMART_EVENT_NEAR_PLAYERS_NEGATION: return sizeof(SmartEvent::nearPlayerNegation); + case SMART_EVENT_NEAR_UNIT: return sizeof(SmartEvent::nearUnit); + case SMART_EVENT_AREA_CASTING: return sizeof(SmartEvent::areaCasting); default: LOG_WARN("sql.sql", "SmartAIMgr: entryorguid {} source_type {} id {} action_type {} is using an event {} with no unused params specified in SmartAIMgr::CheckUnusedEventParams(), please report this.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.GetEventType()); @@ -1043,6 +1051,16 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) if (!IsMinMaxValid(e, e.event.targetCasting.repeatMin, e.event.targetCasting.repeatMax)) return false; break; + case SMART_EVENT_AREA_CASTING: + if (e.event.areaCasting.spellId > 0 && !sSpellMgr->GetSpellInfo(e.event.areaCasting.spellId)) + { + LOG_ERROR("scripts.ai.sai", "SmartAIMgr: Entry {} SourceType {} Event {} Action {} uses non-existent Spell entry {}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.spellHit.spell); + return false; + } + + if (!IsMinMaxValid(e, e.event.areaCasting.repeatMin, e.event.areaCasting.repeatMax)) + return false; + break; case SMART_EVENT_PASSENGER_BOARDED: case SMART_EVENT_PASSENGER_REMOVED: if (!IsMinMaxValid(e, e.event.minMax.repeatMin, e.event.minMax.repeatMax)) @@ -1272,6 +1290,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) case SMART_EVENT_GO_EVENT_INFORM: case SMART_EVENT_NEAR_PLAYERS: case SMART_EVENT_NEAR_PLAYERS_NEGATION: + case SMART_EVENT_NEAR_UNIT: case SMART_EVENT_TIMED_EVENT_TRIGGERED: case SMART_EVENT_INSTANCE_PLAYER_ENTER: case SMART_EVENT_TRANSPORT_RELOCATE: diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index fd841d539..6e45ee232 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -206,10 +206,12 @@ enum SMART_EVENT /* AC Custom Events */ SMART_EVENT_AC_START = 100, - SMART_EVENT_NEAR_PLAYERS = 101, // min, radius, first timer, check timer - SMART_EVENT_NEAR_PLAYERS_NEGATION = 102, // min, radius, first timer, check timer + SMART_EVENT_NEAR_PLAYERS = 101, // min, radius, first timer, repeatMin, repeatMax + SMART_EVENT_NEAR_PLAYERS_NEGATION = 102, // max, radius, first timer, repeatMin, repeatMax + SMART_EVENT_NEAR_UNIT = 103, // type (0: creature 1: gob), entry, count, range, timer + SMART_EVENT_AREA_CASTING = 104, // spellId (0: any), range (0: any), repeatMin, repeatMax, checkTimer - SMART_EVENT_AC_END = 103 + SMART_EVENT_AC_END = 105 }; struct SmartEvent @@ -482,17 +484,37 @@ struct SmartEvent uint32 minCount; uint32 radius; uint32 firstTimer; - uint32 checkTimer; + uint32 repeatMin; + uint32 repeatMax; } nearPlayer; struct { - uint32 minCount; + uint32 maxCount; uint32 radius; uint32 firstTimer; - uint32 checkTimer; + uint32 repeatMin; + uint32 repeatMax; } nearPlayerNegation; + struct + { + uint32 type; + uint32 entry; + uint32 count; + uint32 range; + uint32 timer; + } nearUnit; + + struct + { + uint32 spellId; + uint32 range; + uint32 repeatMin; + uint32 repeatMax; + uint32 checkTimer; + } areaCasting; + struct { uint32 param1; @@ -1795,8 +1817,10 @@ const uint32 SmartAIEventMask[SMART_EVENT_AC_END][2] = { 0, 0 }, // 98 { 0, 0 }, // 99 { 0, 0 }, // 100 - {SMART_EVENT_NEAR_PLAYERS, SMART_SCRIPT_TYPE_MASK_CREATURE }, - {SMART_EVENT_NEAR_PLAYERS_NEGATION, SMART_SCRIPT_TYPE_MASK_CREATURE } + {SMART_EVENT_NEAR_PLAYERS, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT }, + {SMART_EVENT_NEAR_PLAYERS_NEGATION, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT }, + {SMART_EVENT_NEAR_UNIT, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT }, + {SMART_EVENT_AREA_CASTING, SMART_SCRIPT_TYPE_MASK_CREATURE } }; enum SmartEventFlags