Merge remote-tracking branch 'upstream/master' into core/update_2025_09_18

This commit is contained in:
bash
2025-09-18 20:09:00 +02:00
55 changed files with 1248 additions and 51 deletions

View File

@@ -4395,6 +4395,19 @@ Event.Announce = 0
Sunsreach.CounterMax = 10000
#
# ScourgeInvasion.CounterFirst
# ScourgeInvasion.CounterSecond
# ScourgeInvasion.CounterThird
# Description: Counter thresholds to be reached to transition phases
# Default: 50 - (ScourgeInvasion.CounterFirst)
# 100 - (ScourgeInvasion.CounterSecond)
# 150 - (ScourgeInvasion.CounterThird)
ScourgeInvasion.CounterFirst = 50
ScourgeInvasion.CounterSecond = 100
ScourgeInvasion.CounterThird = 150
#
###################################################################################################

View File

@@ -53,15 +53,19 @@ void CreatureAI::Talk(uint8 id, WorldObject const* target /*= nullptr*/, Millise
{
if (delay > Seconds::zero())
{
me->m_Events.AddEventAtOffset([this, id, target]()
ObjectGuid targetGuid;
if (target)
targetGuid = target->GetGUID();
me->m_Events.AddEventAtOffset([this, id, targetGuid]()
{
sCreatureTextMgr->SendChat(me, id, target);
// Target can be nullptr here, it will be handled inside the function.
sCreatureTextMgr->SendChat(me, id, ObjectAccessor::GetUnit(*me, targetGuid));
}, delay);
}
else
{
sCreatureTextMgr->SendChat(me, id, target);
}
}
/**

View File

@@ -751,6 +751,10 @@ void BossAI::DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damage
ScriptedAI::DamageTaken(attacker, damage, damagetype, damageSchoolMask);
if (_nextHealthCheck._valid)
{
if (!_nextHealthCheck._allowedWhileCasting && me->HasUnitState(UNIT_STATE_CASTING))
return;
if (me->HealthBelowPctDamaged(_nextHealthCheck._healthPct, damage))
{
_nextHealthCheck._exec();
@@ -764,6 +768,7 @@ void BossAI::DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damage
if (!_healthCheckEvents.empty())
_nextHealthCheck = _healthCheckEvents.front();
}
}
}
/**
@@ -771,19 +776,18 @@ void BossAI::DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damage
*
* @param healthPct The health percent at which the code will be executed.
* @param exec The fuction to be executed.
* @param allowedWhileCasting If false, the event will not be checked while the creature is casting.
*/
void BossAI::ScheduleHealthCheckEvent(uint32 healthPct, std::function<void()> exec)
void BossAI::ScheduleHealthCheckEvent(uint32 healthPct, std::function<void()> exec, bool allowedWhileCasting /*=true*/)
{
_healthCheckEvents.push_back(HealthCheckEventData(healthPct, exec));
_healthCheckEvents.push_back(HealthCheckEventData(healthPct, exec, true, allowedWhileCasting));
_nextHealthCheck = _healthCheckEvents.front();
};
void BossAI::ScheduleHealthCheckEvent(std::initializer_list<uint8> healthPct, std::function<void()> exec)
void BossAI::ScheduleHealthCheckEvent(std::initializer_list<uint8> healthPct, std::function<void()> exec, bool allowedWhileCasting /*=true*/)
{
for (auto const& checks : healthPct)
{
_healthCheckEvents.push_back(HealthCheckEventData(checks, exec));
}
_healthCheckEvents.push_back(HealthCheckEventData(checks, exec, true, allowedWhileCasting));
_nextHealthCheck = _healthCheckEvents.front();
}

View File

@@ -456,11 +456,12 @@ private:
struct HealthCheckEventData
{
HealthCheckEventData(uint8 healthPct, std::function<void()> exec, bool valid = true) : _healthPct(healthPct), _exec(exec), _valid(valid) { };
HealthCheckEventData(uint8 healthPct, std::function<void()> exec, bool valid = true, bool allowedWhileCasting = true) : _healthPct(healthPct), _exec(exec), _valid(valid), _allowedWhileCasting(allowedWhileCasting) { };
uint8 _healthPct;
std::function<void()> _exec;
bool _valid;
bool _allowedWhileCasting;
};
class BossAI : public ScriptedAI
@@ -482,8 +483,8 @@ public:
void UpdateAI(uint32 diff) override;
void ScheduleHealthCheckEvent(uint32 healthPct, std::function<void()> exec);
void ScheduleHealthCheckEvent(std::initializer_list<uint8> healthPct, std::function<void()> exec);
void ScheduleHealthCheckEvent(uint32 healthPct, std::function<void()> exec, bool allowedWhileCasting = true);
void ScheduleHealthCheckEvent(std::initializer_list<uint8> healthPct, std::function<void()> exec, bool allowedWhileCasting = true);
// @brief Casts the spell after the fixed time and says the text id if provided. Timer will run even if the creature is casting or out of combat.
// @param spellId The spell to cast.

View File

@@ -74,6 +74,8 @@ SmartAI::SmartAI(Creature* c) : CreatureAI(c)
_chaseOnInterrupt = false;
aiDataSet.clear();
// Xinef: Vehicle conditions
m_ConditionsTimer = 0;
if (me->GetVehicleKit())
@@ -778,6 +780,7 @@ void SmartAI::JustRespawned()
mFollowArrivedEntry = 0;
mFollowCreditType = 0;
mFollowArrivedAlive = true;
aiDataSet.clear();
}
void SmartAI::JustReachedHome()
@@ -963,8 +966,12 @@ void SmartAI::DoAction(int32 param)
GetScript()->ProcessEventsFor(SMART_EVENT_ACTION_DONE, nullptr, param);
}
uint32 SmartAI::GetData(uint32 /*id*/) const
uint32 SmartAI::GetData(uint32 id) const
{
auto const& itr = aiDataSet.find(id);
if (itr != aiDataSet.end())
return itr->second;
return 0;
}
@@ -980,6 +987,7 @@ void SmartAI::SetData(uint32 id, uint32 value, WorldObject* invoker)
gob = invoker->ToGameObject();
}
aiDataSet[id] = value;
GetScript()->ProcessEventsFor(SMART_EVENT_DATA_SET, unit, id, value, false, nullptr, gob);
}

View File

@@ -262,6 +262,7 @@ private:
uint32 m_ConditionsTimer;
bool _chaseOnInterrupt;
std::unordered_map<uint32, uint32> aiDataSet;
};
class SmartGameObjectAI : public GameObjectAI

View File

@@ -238,7 +238,8 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
mLastTextID = e.action.talk.textGroupID;
mTextTimer = e.action.talk.duration;
mUseTextTimer = true;
sCreatureTextMgr->SendChat(talker, uint8(e.action.talk.textGroupID), talkTarget);
talker->AI()->Talk(e.action.talk.textGroupID, talkTarget, Milliseconds(e.action.talk.delay));
LOG_DEBUG("sql.sql", "SmartScript::ProcessAction: SMART_ACTION_TALK: talker: {} ({}), textId: {}", talker->GetName(), talker->GetGUID().ToString(), mLastTextID);
break;
}

View File

@@ -765,6 +765,7 @@ struct SmartAction
uint32 textGroupID;
uint32 duration;
SAIBool useTalkTarget;
uint32 delay;
} talk;
struct

View File

@@ -18,6 +18,8 @@
#include "ConditionMgr.h"
#include "AchievementMgr.h"
#include "GameEventMgr.h"
#include "GameObject.h"
#include "GameObjectAI.h"
#include "InstanceScript.h"
#include "ObjectMgr.h"
#include "Pet.h"
@@ -576,6 +578,14 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo)
condMeets = sWorldState->IsConditionFulfilled(ConditionValue1, ConditionValue2);
break;
}
case CONDITION_AI_DATA:
{
if (Creature* creature = object->ToCreature())
condMeets = creature->AI() && creature->AI()->GetData(ConditionValue1) == ConditionValue2;
else if (GameObject* go = object->ToGameObject())
condMeets = go->AI() && go->AI()->GetData(ConditionValue1) == ConditionValue2;
break;
}
default:
condMeets = false;
break;
@@ -779,6 +789,9 @@ uint32 Condition::GetSearcherTypeMaskForCondition()
case CONDITION_WORLD_SCRIPT:
mask |= GRID_MAP_TYPE_MASK_ALL;
break;
case CONDITION_AI_DATA:
mask |= GRID_MAP_TYPE_MASK_CREATURE | GRID_MAP_TYPE_MASK_GAMEOBJECT;
break;
default:
ASSERT(false && "Condition::GetSearcherTypeMaskForCondition - missing condition handling!");
break;

View File

@@ -87,8 +87,9 @@ enum ConditionTypes
CONDITION_QUEST_SATISFY_EXCLUSIVE = 101, // quest_id 0 0 true if satisfied exclusive group
CONDITION_HAS_AURA_TYPE = 102, // aura_type 0 0 true if has aura type
CONDITION_WORLD_SCRIPT = 103, // conditionId state 0 true if WorldState::IsConditionFulfilled returns true
CONDITION_AI_DATA = 104, // dataId value 0 true if AI::GetData returns value
CONDITION_AC_END = 104 // placeholder
CONDITION_AC_END = 105 // placeholder
};
/*! Documentation on implementing a new ConditionSourceType:

View File

@@ -3914,5 +3914,8 @@ bool Creature::IsUpdateNeeded()
if (HasUnitState(UNIT_STATE_EVADE))
return true;
if (m_formation && m_formation->GetLeader() != this)
return true;
return false;
}

View File

@@ -49,14 +49,14 @@ namespace PlayerSettingsStore
return "";
std::ostringstream data;
data << settings[0].value;
data << settings[0].value << ' ';
for (size_t i = 1; i < settings.size(); ++i)
data << ' ' << settings[i].value;
data << settings[i].value << ' ';
return data.str();
}
// helper: load a single source row for a player and parse to vector
static PlayerSettingVector LoadPlayerSettings(uint32 playerLowGuid, std::string const& source)
static PlayerSettingVector LoadPlayerSettings(ObjectGuid::LowType playerLowGuid, std::string const& source)
{
PlayerSettingVector result;
@@ -80,7 +80,7 @@ namespace PlayerSettingsStore
return result;
}
void UpdateSetting(uint32 playerLowGuid, std::string const& source, uint32 index, uint32 value)
void UpdateSetting(ObjectGuid::LowType playerLowGuid, std::string const& source, uint32 index, uint32 value)
{
if (!sWorld->getBoolConfig(CONFIG_PLAYER_SETTINGS_ENABLED))
return;
@@ -98,7 +98,7 @@ namespace PlayerSettingsStore
}
// Implementation of PrepareReplaceStatement
CharacterDatabasePreparedStatement* PlayerSettingsStore::PrepareReplaceStatement(uint32 playerLowGuid, std::string const& source, PlayerSettingVector const& settings)
CharacterDatabasePreparedStatement* PlayerSettingsStore::PrepareReplaceStatement(ObjectGuid::LowType playerLowGuid, std::string const& source, PlayerSettingVector const& settings)
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CHAR_SETTINGS);
stmt->SetData(0, playerLowGuid);

View File

@@ -57,13 +57,13 @@ namespace PlayerSettingsStore
{
// Update a single setting value for any player by GUID (works for online or offline players).
// This reads the existing "source" row from character_settings, adjusts the index, and REPLACE's it back.
void UpdateSetting(uint32 playerLowGuid, std::string const& source, uint32 index, uint32 value);
void UpdateSetting(ObjectGuid::LowType playerLowGuid, std::string const& source, uint32 index, uint32 value);
// Common helpers for parsing and serializing settings data
PlayerSettingVector ParseSettingsData(std::string const& data);
std::string SerializeSettingsData(PlayerSettingVector const& settings);
// Prepare a REPLACE statement populated with given settings data. Caller may execute or append to a transaction.
CharacterDatabasePreparedStatement* PrepareReplaceStatement(uint32 playerLowGuid, std::string const& source, PlayerSettingVector const& settings);
CharacterDatabasePreparedStatement* PrepareReplaceStatement(ObjectGuid::LowType playerLowGuid, std::string const& source, PlayerSettingVector const& settings);
}
#endif

View File

@@ -2905,6 +2905,7 @@ void Player::RemoveItem(uint8 bag, uint8 slot, bool update, bool swap)
RemoveEnchantmentDurations(pItem);
RemoveItemDurations(pItem);
RemoveTradeableItem(pItem);
ApplyItemObtainSpells(pItem, false);
if (bag == INVENTORY_SLOT_BAG_0)
{

View File

@@ -656,7 +656,11 @@ void WorldConfig::BuildConfigCache()
SetConfigValue<bool>(CONFIG_SPELL_QUEUE_ENABLED, "SpellQueue.Enabled", true);
SetConfigValue<uint32>(CONFIG_SPELL_QUEUE_WINDOW, "SpellQueue.Window", 400);
// World State
SetConfigValue<uint32>(CONFIG_SUNSREACH_COUNTER_MAX, "Sunsreach.CounterMax", 10000);
SetConfigValue<uint32>(CONFIG_SCOURGEINVASION_COUNTER_FIRST, "ScourgeInvasion.CounterFirst", 50);
SetConfigValue<uint32>(CONFIG_SCOURGEINVASION_COUNTER_SECOND, "ScourgeInvasion.CounterSecond", 100);
SetConfigValue<uint32>(CONFIG_SCOURGEINVASION_COUNTER_THIRD, "ScourgeInvasion.CounterThird", 150);
SetConfigValue<std::string>(CONFIG_NEW_CHAR_STRING, "PlayerStart.String", "");
}

View File

@@ -378,6 +378,9 @@ enum ServerConfigs
CONFIG_AUCTIONHOUSE_WORKERTHREADS,
CONFIG_SPELL_QUEUE_WINDOW,
CONFIG_SUNSREACH_COUNTER_MAX,
CONFIG_SCOURGEINVASION_COUNTER_FIRST,
CONFIG_SCOURGEINVASION_COUNTER_SECOND,
CONFIG_SCOURGEINVASION_COUNTER_THIRD,
CONFIG_RESPAWN_DYNAMICMINIMUM_GAMEOBJECT,
CONFIG_RESPAWN_DYNAMICMINIMUM_CREATURE,
RATE_HEALTH,

View File

@@ -25,6 +25,7 @@
#include "UnitAI.h"
#include "Weather.h"
#include "WorldState.h"
#include "WorldConfig.h"
#include "WorldStateDefines.h"
#include <chrono>
@@ -1442,6 +1443,8 @@ void WorldState::StopScourgeInvasion()
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION_BLASTED_LANDS);
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION_EASTERN_PLAGUELANDS);
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION_BURNING_STEPPES);
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION_INVASIONS_DONE);
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION_BOSSES);
BroadcastSIWorldstates();
m_siData.Reset();
@@ -1564,22 +1567,25 @@ void WorldState::BroadcastSIWorldstates()
void WorldState::HandleDefendedZones()
{
if (m_siData.m_battlesWon < 50)
if (m_siData.m_battlesWon < sWorld->getIntConfig(CONFIG_SCOURGEINVASION_COUNTER_FIRST))
{
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION_50_INVASIONS);
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION_100_INVASIONS);
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION_150_INVASIONS);
}
else if (m_siData.m_battlesWon >= 50 && m_siData.m_battlesWon < 100)
else if (m_siData.m_battlesWon >= sWorld->getIntConfig(CONFIG_SCOURGEINVASION_COUNTER_FIRST) &&
m_siData.m_battlesWon < sWorld->getIntConfig(CONFIG_SCOURGEINVASION_COUNTER_SECOND))
sGameEventMgr->StartEvent(GAME_EVENT_SCOURGE_INVASION_50_INVASIONS);
else if (m_siData.m_battlesWon >= 100 && m_siData.m_battlesWon < 150)
else if (m_siData.m_battlesWon >= sWorld->getIntConfig(CONFIG_SCOURGEINVASION_COUNTER_SECOND) &&
m_siData.m_battlesWon < sWorld->getIntConfig(CONFIG_SCOURGEINVASION_COUNTER_THIRD))
{
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION_50_INVASIONS);
sGameEventMgr->StartEvent(GAME_EVENT_SCOURGE_INVASION_100_INVASIONS);
}
else if (m_siData.m_battlesWon >= 150)
else if (m_siData.m_battlesWon >= sWorld->getIntConfig(CONFIG_SCOURGEINVASION_COUNTER_THIRD))
{
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION);
// The event is enabled via command, so we expect it to be disabled via command as well.
// sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION);
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION_50_INVASIONS);
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION_100_INVASIONS);
sGameEventMgr->StartEvent(GAME_EVENT_SCOURGE_INVASION_INVASIONS_DONE);

View File

@@ -126,9 +126,36 @@ class spell_azjol_nerub_web_wrap_aura : public AuraScript
}
};
enum DrainPowerSpells
{
SPELL_DRAIN_POWER_AURA = 54315
};
// 54314, 59354 - Drain Power
class spell_azjol_drain_power : public SpellScript
{
PrepareSpellScript(spell_azjol_drain_power);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_DRAIN_POWER_AURA });
}
void HandleScriptEffect(SpellEffIndex /*effIndex*/)
{
GetCaster()->CastSpell(GetCaster(), SPELL_DRAIN_POWER_AURA, true);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_azjol_drain_power::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_APPLY_AURA);
}
};
void AddSC_instance_azjol_nerub()
{
new instance_azjol_nerub();
RegisterSpellScript(spell_azjol_nerub_fixate);
RegisterSpellScript(spell_azjol_nerub_web_wrap_aura);
RegisterSpellScript(spell_azjol_drain_power);
}

View File

@@ -18,6 +18,7 @@
#include "InstanceMapScript.h"
#include "ScriptedCreature.h"
#include "gundrak.h"
#include "GameObjectAI.h"
DoorData const doorData[] =
{
@@ -211,6 +212,9 @@ public:
if (GameObject* go = instance->GetGameObject(_bridgeGUIDs[i]))
go->SetGoState(GO_STATE_ACTIVE_ALTERNATIVE);
}
if (GameObject* collision = instance->GetGameObject(_bridgeGUIDs[4]))
if (collision->AI())
collision->AI()->SetData(0, 1);
}
};
};

View File

@@ -2143,6 +2143,34 @@ class spell_necropolis_beam: public SpellScript
}
};
enum SoulDeflectionSpells
{
SPELL_SOUL_DEFLECTION_DAMAGE = 51011
};
class spell_soul_deflection : public AuraScript
{
PrepareAuraScript(spell_soul_deflection);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_SOUL_DEFLECTION_DAMAGE });
}
void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo)
{
if (!eventInfo.GetDamageInfo() || !eventInfo.GetDamageInfo()->GetDamage() || !GetTarget())
return;
GetCaster()->CastCustomSpell(SPELL_SOUL_DEFLECTION_DAMAGE, SPELLVALUE_BASE_POINT0, eventInfo.GetDamageInfo()->GetDamage(), GetTarget(), true);
}
void Register() override
{
OnEffectProc += AuraEffectProcFn(spell_soul_deflection::HandleProc, EFFECT_0, SPELL_AURA_DUMMY);
}
};
void AddSC_borean_tundra()
{
RegisterSpellScript(spell_q11919_q11940_drake_hunt_aura);
@@ -2168,4 +2196,5 @@ void AddSC_borean_tundra()
new npc_bloodmage_laurith();
RegisterCreatureAI(npc_jenny);
RegisterSpellScript(spell_necropolis_beam);
RegisterSpellScript(spell_soul_deflection);
}

View File

@@ -2261,6 +2261,41 @@ class spell_handover_reins : public SpellScript
}
};
enum FlameFurySpells
{
SPELL_FLAME_FURY_1 = 50351,
SPELL_FLAME_FURY_2 = 50353,
SPELL_FLAME_FURY_3 = 50354,
SPELL_FLAME_FURY_4 = 50355,
SPELL_FLAME_FURY_5 = 50357
};
// 50348 - Flame Fury
class spell_dragonblight_flame_fury : public AuraScript
{
PrepareAuraScript(spell_dragonblight_flame_fury);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo(spellIds);
}
void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
if (Unit* owner = GetUnitOwner())
if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE && !owner->IsAlive())
owner->CastSpell(owner, Acore::Containers::SelectRandomContainerElement(spellIds), true);
}
private:
std::array<uint32, 5> const spellIds = { SPELL_FLAME_FURY_1, SPELL_FLAME_FURY_2, SPELL_FLAME_FURY_3, SPELL_FLAME_FURY_4, SPELL_FLAME_FURY_5 };
void Register() override
{
OnEffectRemove += AuraEffectRemoveFn(spell_dragonblight_flame_fury::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL);
}
};
void AddSC_dragonblight()
{
new npc_conversing_with_the_depths_trigger();
@@ -2289,4 +2324,5 @@ void AddSC_dragonblight()
new npc_torturer_lecraft();
RegisterSpellScript(spell_dragonblight_corrosive_spit);
RegisterSpellScript(spell_handover_reins);
RegisterSpellScript(spell_dragonblight_flame_fury);
}

View File

@@ -394,6 +394,38 @@ public:
}
};
enum RodinLightningSpells
{
SPELL_RODIN_LIGHTNING_START = 44787,
SPELL_RODIN_LIGHTNING_END = 44791,
NPC_RODIN = 24876
};
struct npc_rodin_lightning_enabler : public ScriptedAI
{
npc_rodin_lightning_enabler(Creature* creature) : ScriptedAI(creature) {}
void Reset() override
{
_scheduler.Schedule(1s, [this](TaskContext context)
{
if (Creature* rodin = me->FindNearestCreature(NPC_RODIN, 10.0f))
DoCast(rodin, urand(SPELL_RODIN_LIGHTNING_START, SPELL_RODIN_LIGHTNING_END));
context.Repeat(2s, 8s);
});
}
void UpdateAI(uint32 /*diff*/) override
{
_scheduler.Update();
}
private:
TaskScheduler _scheduler;
};
enum HawkHunting
{
SPELL_HAWK_HUNTING_ITEM = 44408
@@ -431,5 +463,6 @@ void AddSC_howling_fjord()
new npc_apothecary_hanes();
new npc_plaguehound_tracker();
new npc_razael_and_lyana();
RegisterCreatureAI(npc_rodin_lightning_enabler);
RegisterSpellScript(spell_hawk_hunting);
}

View File

@@ -923,6 +923,21 @@ class spell_dru_starfall_dummy : public SpellScript
void FilterTargets(std::list<WorldObject*>& targets)
{
// Get caster object
Unit* caster = GetCaster();
// Remove targets if they are outside line of sight with respect to caster
targets.remove_if([caster](WorldObject const* target)
{
if (target)
{
if (!caster->IsWithinLOS(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ()))
return true;
}
return false;
});
// Take 2 random targets from remaining within line of sight targets
Acore::Containers::RandomResize(targets, 2);
}

View File

@@ -5492,6 +5492,42 @@ class spell_gen_cooldown_all : public SpellScript
}
};
// 29007 - Drink (Freshly-Squeezed Lemonade)
// 29008 - Food (Friendship Bread)
enum HeartFood
{
SPELL_VISUAL_KIT_HEART_EMOTE = 6552
};
class spell_gen_food_heart_emote : public AuraScript
{
PrepareAuraScript(spell_gen_food_heart_emote);
void CalcPeriodic(AuraEffect const* /*effect*/, bool& isPeriodic, int32& amplitude)
{
isPeriodic = true;
amplitude = 5 * IN_MILLISECONDS;
}
void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
GetUnitOwner()->SendPlaySpellVisual(SPELL_VISUAL_KIT_HEART_EMOTE);
}
void HandleUpdatePeriodic(AuraEffect* /*aurEff*/)
{
GetUnitOwner()->SendPlaySpellVisual(SPELL_VISUAL_KIT_HEART_EMOTE);
}
void Register() override
{
AuraType effName = (m_scriptSpellId == 29007) ? SPELL_AURA_MOD_POWER_REGEN : SPELL_AURA_MOD_REGEN;
OnEffectApply += AuraEffectApplyFn(spell_gen_food_heart_emote::OnApply, EFFECT_0, effName, AURA_EFFECT_HANDLE_REAL);
DoEffectCalcPeriodic += AuraEffectCalcPeriodicFn(spell_gen_food_heart_emote::CalcPeriodic, EFFECT_0, effName);
OnEffectUpdatePeriodic += AuraEffectUpdatePeriodicFn(spell_gen_food_heart_emote::HandleUpdatePeriodic, EFFECT_0, effName);
}
};
void AddSC_generic_spell_scripts()
{
RegisterSpellScript(spell_silithyst);
@@ -5656,4 +5692,5 @@ void AddSC_generic_spell_scripts()
RegisterSpellScriptWithArgs(spell_gen_translocate, "spell_gen_translocate_down", SPELL_TRANSLOCATION_DOWN);
RegisterSpellScriptWithArgs(spell_gen_translocate, "spell_gen_translocate_up", SPELL_TRANSLOCATION_UP);
RegisterSpellScript(spell_gen_cooldown_all);
RegisterSpellScript(spell_gen_food_heart_emote);
}

View File

@@ -60,7 +60,8 @@ enum WarriorSpells
SPELL_WARRIOR_VIGILANCE_PROC = 50725,
SPELL_WARRIOR_VIGILANCE_REDIRECT_THREAT = 59665,
SPELL_WARRIOR_WHIRLWIND_MAIN = 50622,
SPELL_WARRIOR_WHIRLWIND_OFF = 44949
SPELL_WARRIOR_WHIRLWIND_OFF = 44949,
SPELL_WARRIOR_EXECUTE_R1 = 5308,
};
enum WarriorSpellIcons
@@ -959,6 +960,26 @@ class spell_warr_heroic_strike : public SpellScript
}
};
class spell_war_sudden_death_aura : public AuraScript
{ PrepareAuraScript(spell_war_sudden_death_aura);
bool AfterCheckProc(ProcEventInfo& eventInfo, bool isTriggeredAtSpellProcEvent)
{
// Check PROC_SPELL_PHASE_FINISH only for Execute
if (eventInfo.GetSpellPhaseMask() != PROC_SPELL_PHASE_FINISH)
return isTriggeredAtSpellProcEvent;
if (Spell const* procSpell = eventInfo.GetProcSpell())
if (procSpell->GetSpellInfo()->GetFirstRankSpell()->Id == SPELL_WARRIOR_EXECUTE_R1)
return isTriggeredAtSpellProcEvent;
return false;
}
void Register() override
{
DoAfterCheckProc += AuraAfterCheckProcFn(spell_war_sudden_death_aura::AfterCheckProc);
}
};
void AddSC_warrior_spell_scripts()
{
RegisterSpellScript(spell_warr_mocking_blow);
@@ -986,4 +1007,5 @@ void AddSC_warrior_spell_scripts()
RegisterSpellScript(spell_warr_vigilance_trigger);
RegisterSpellScript(spell_warr_t3_prot_8p_bonus);
RegisterSpellScript(spell_warr_heroic_strike);
RegisterSpellScript(spell_war_sudden_death_aura);
}

View File

@@ -20,27 +20,6 @@
#include "Player.h"
#include "ScriptedCreature.h"
class AreaTrigger_at_voltarus_middle : public AreaTriggerScript
{
public:
AreaTrigger_at_voltarus_middle()
: AreaTriggerScript("at_voltarus_middle")
{
}
bool OnTrigger(Player* player, AreaTrigger const* /*trigger*/) override
{
if (player->IsAlive() && !player->IsInCombat())
if (player->HasItemCount(39319)) // Scepter of Domination
{
player->TeleportTo(MAP_NORTHREND, 6242.67f, -1972.10f, 484.783f, 0.6f);
return true;
}
return false;
}
};
/*######
## at_coilfang_waterfall
######*/
@@ -416,7 +395,6 @@ private:
void AddSC_areatrigger_scripts()
{
new AreaTrigger_at_voltarus_middle();
new AreaTrigger_at_coilfang_waterfall();
new AreaTrigger_at_legion_teleporter();
new AreaTrigger_at_stormwright_shelf();