mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-03-15 21:45:12 +00:00
950 lines
34 KiB
C++
950 lines
34 KiB
C++
/*
|
|
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "ProcEventInfoHelper.h"
|
|
#include "SpellInfoTestHelper.h"
|
|
#include "SpellMgr.h"
|
|
#include "WorldMock.h"
|
|
#include "gtest/gtest.h"
|
|
#include "gmock/gmock.h"
|
|
|
|
using namespace testing;
|
|
|
|
/**
|
|
* @brief Test fixture for SpellMgr proc tests
|
|
*
|
|
* Tests the CanSpellTriggerProcOnEvent function and related proc logic.
|
|
*/
|
|
class SpellProcTest : public ::testing::Test
|
|
{
|
|
protected:
|
|
void SetUp() override
|
|
{
|
|
_originalWorld = sWorld.release();
|
|
_worldMock = new NiceMock<WorldMock>();
|
|
sWorld.reset(_worldMock);
|
|
|
|
static std::string emptyString;
|
|
ON_CALL(*_worldMock, GetDataPath()).WillByDefault(ReturnRef(emptyString));
|
|
}
|
|
|
|
void TearDown() override
|
|
{
|
|
IWorld* currentWorld = sWorld.release();
|
|
delete currentWorld;
|
|
_worldMock = nullptr;
|
|
|
|
sWorld.reset(_originalWorld);
|
|
_originalWorld = nullptr;
|
|
|
|
// Clean up any SpellInfo objects we created
|
|
for (auto* spellInfo : _spellInfos)
|
|
delete spellInfo;
|
|
_spellInfos.clear();
|
|
}
|
|
|
|
// Helper to create and track SpellInfo objects for cleanup
|
|
SpellInfo* CreateSpellInfo(uint32 id = 1, uint32 familyName = 0,
|
|
uint32 familyFlag0 = 0, uint32 familyFlag1 = 0, uint32 familyFlag2 = 0)
|
|
{
|
|
auto* spellInfo = SpellInfoBuilder()
|
|
.WithId(id)
|
|
.WithSpellFamilyName(familyName)
|
|
.WithSpellFamilyFlags(familyFlag0, familyFlag1, familyFlag2)
|
|
.Build();
|
|
_spellInfos.push_back(spellInfo);
|
|
return spellInfo;
|
|
}
|
|
|
|
IWorld* _originalWorld = nullptr;
|
|
NiceMock<WorldMock>* _worldMock = nullptr;
|
|
std::vector<SpellInfo*> _spellInfos;
|
|
};
|
|
|
|
// =============================================================================
|
|
// ProcFlags Tests - Basic proc flag matching
|
|
// =============================================================================
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_ProcFlagsMatch)
|
|
{
|
|
// Setup: Create a proc entry that triggers on melee auto attacks
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_MELEE_AUTO_ATTACK)
|
|
.Build();
|
|
|
|
// Create ProcEventInfo with matching type mask
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_MELEE_AUTO_ATTACK)
|
|
.WithHitMask(PROC_HIT_NORMAL)
|
|
.Build();
|
|
|
|
// Should match
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_ProcFlagsNoMatch)
|
|
{
|
|
// Setup: Create a proc entry that triggers on melee auto attacks
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_MELEE_AUTO_ATTACK)
|
|
.Build();
|
|
|
|
// Create ProcEventInfo with different type mask (ranged instead of melee)
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_RANGED_AUTO_ATTACK)
|
|
.WithHitMask(PROC_HIT_NORMAL)
|
|
.Build();
|
|
|
|
// Should not match
|
|
EXPECT_FALSE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_MultipleProcFlagsPartialMatch)
|
|
{
|
|
// Setup: Create a proc entry that triggers on melee OR ranged
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_MELEE_AUTO_ATTACK | PROC_FLAG_DONE_RANGED_AUTO_ATTACK)
|
|
.Build();
|
|
|
|
// Create ProcEventInfo with only melee
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_MELEE_AUTO_ATTACK)
|
|
.WithHitMask(PROC_HIT_NORMAL)
|
|
.Build();
|
|
|
|
// Should match (partial match is OK - it's an OR relationship)
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
// =============================================================================
|
|
// Kill/Death Event Tests - These always trigger regardless of other conditions
|
|
// =============================================================================
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_KillEventAlwaysProcs)
|
|
{
|
|
// Setup: Create a proc entry for kill events
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_KILL)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_KILL)
|
|
.Build();
|
|
|
|
// Kill events should always trigger
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_KilledEventAlwaysProcs)
|
|
{
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_KILLED)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_KILLED)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_DeathEventAlwaysProcs)
|
|
{
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DEATH)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DEATH)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
// =============================================================================
|
|
// HitMask Tests - Test hit type filtering
|
|
// =============================================================================
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_HitMaskCriticalMatch)
|
|
{
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_MELEE_AUTO_ATTACK)
|
|
.WithHitMask(PROC_HIT_CRITICAL)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_MELEE_AUTO_ATTACK)
|
|
.WithHitMask(PROC_HIT_CRITICAL)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_HitMaskCriticalNoMatch)
|
|
{
|
|
// Proc entry requires critical hit
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_MELEE_AUTO_ATTACK)
|
|
.WithHitMask(PROC_HIT_CRITICAL)
|
|
.Build();
|
|
|
|
// Event is a normal hit
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_MELEE_AUTO_ATTACK)
|
|
.WithHitMask(PROC_HIT_NORMAL)
|
|
.Build();
|
|
|
|
EXPECT_FALSE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_HitMaskDefaultForDone)
|
|
{
|
|
// When HitMask is 0, default for DONE procs is NORMAL | CRITICAL | ABSORB
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_MELEE_AUTO_ATTACK)
|
|
.WithHitMask(0) // Default
|
|
.Build();
|
|
|
|
// Normal hit should work with default mask
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_MELEE_AUTO_ATTACK)
|
|
.WithHitMask(PROC_HIT_NORMAL)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_HitMaskDefaultForTaken)
|
|
{
|
|
// When HitMask is 0, default for TAKEN procs is NORMAL | CRITICAL
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_TAKEN_MELEE_AUTO_ATTACK)
|
|
.WithHitMask(0) // Default
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_TAKEN_MELEE_AUTO_ATTACK)
|
|
.WithHitMask(PROC_HIT_CRITICAL)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_HitMaskMissNoMatch)
|
|
{
|
|
// Miss should not trigger default hit mask
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_MELEE_AUTO_ATTACK)
|
|
.WithHitMask(0) // Default allows NORMAL | CRITICAL | ABSORB
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_MELEE_AUTO_ATTACK)
|
|
.WithHitMask(PROC_HIT_MISS)
|
|
.Build();
|
|
|
|
EXPECT_FALSE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_HitMaskDodge)
|
|
{
|
|
// Explicitly require dodge
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_TAKEN_MELEE_AUTO_ATTACK)
|
|
.WithHitMask(PROC_HIT_DODGE)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_TAKEN_MELEE_AUTO_ATTACK)
|
|
.WithHitMask(PROC_HIT_DODGE)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_HitMaskParry)
|
|
{
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_TAKEN_MELEE_AUTO_ATTACK)
|
|
.WithHitMask(PROC_HIT_PARRY)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_TAKEN_MELEE_AUTO_ATTACK)
|
|
.WithHitMask(PROC_HIT_PARRY)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_HitMaskBlock)
|
|
{
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_TAKEN_MELEE_AUTO_ATTACK)
|
|
.WithHitMask(PROC_HIT_BLOCK)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_TAKEN_MELEE_AUTO_ATTACK)
|
|
.WithHitMask(PROC_HIT_BLOCK)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
// =============================================================================
|
|
// SpellTypeMask Tests - Damage vs Heal vs Other
|
|
// =============================================================================
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_SpellTypeMaskDamage)
|
|
{
|
|
auto* spellInfo = CreateSpellInfo(1);
|
|
|
|
// Create DamageInfo for the test
|
|
DamageInfo damageInfo(nullptr, nullptr, 100, spellInfo, SPELL_SCHOOL_MASK_FIRE, SPELL_DIRECT_DAMAGE);
|
|
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellTypeMask(PROC_SPELL_TYPE_DAMAGE)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellTypeMask(PROC_SPELL_TYPE_DAMAGE)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.WithHitMask(PROC_HIT_NORMAL)
|
|
.WithDamageInfo(&damageInfo)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_SpellTypeMaskHeal)
|
|
{
|
|
auto* spellInfo = CreateSpellInfo(1);
|
|
|
|
// Create HealInfo with the spell info so GetSpellInfo() works
|
|
HealInfo healInfo(nullptr, nullptr, 100, spellInfo, SPELL_SCHOOL_MASK_HOLY);
|
|
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS)
|
|
.WithSpellTypeMask(PROC_SPELL_TYPE_HEAL)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS)
|
|
.WithSpellTypeMask(PROC_SPELL_TYPE_HEAL)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.WithHitMask(PROC_HIT_NORMAL)
|
|
.WithHealInfo(&healInfo)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_SpellTypeMaskNoMatch)
|
|
{
|
|
// Proc requires heal but event is damage
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS)
|
|
.WithSpellTypeMask(PROC_SPELL_TYPE_HEAL)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS)
|
|
.WithSpellTypeMask(PROC_SPELL_TYPE_DAMAGE) // Mismatch
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.WithHitMask(PROC_HIT_NORMAL)
|
|
.Build();
|
|
|
|
EXPECT_FALSE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
// =============================================================================
|
|
// SpellPhaseMask Tests - Cast vs Hit vs Finish
|
|
// =============================================================================
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_SpellPhaseMaskCast)
|
|
{
|
|
auto* spellInfo = CreateSpellInfo(1);
|
|
DamageInfo damageInfo(nullptr, nullptr, 100, spellInfo, SPELL_SCHOOL_MASK_FIRE, SPELL_DIRECT_DAMAGE);
|
|
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_CAST)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_CAST)
|
|
.WithDamageInfo(&damageInfo)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_SpellPhaseMaskHit)
|
|
{
|
|
auto* spellInfo = CreateSpellInfo(1);
|
|
DamageInfo damageInfo(nullptr, nullptr, 100, spellInfo, SPELL_SCHOOL_MASK_FIRE, SPELL_DIRECT_DAMAGE);
|
|
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.WithHitMask(PROC_HIT_NORMAL)
|
|
.WithDamageInfo(&damageInfo)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_SpellPhaseMaskNoMatch)
|
|
{
|
|
auto* spellInfo = CreateSpellInfo(1);
|
|
DamageInfo damageInfo(nullptr, nullptr, 100, spellInfo, SPELL_SCHOOL_MASK_FIRE, SPELL_DIRECT_DAMAGE);
|
|
|
|
// Proc requires cast phase
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_CAST)
|
|
.Build();
|
|
|
|
// Event is hit phase
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.WithHitMask(PROC_HIT_NORMAL)
|
|
.WithDamageInfo(&damageInfo)
|
|
.Build();
|
|
|
|
EXPECT_FALSE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_CastPhaseWithExplicitHitMaskCrit)
|
|
{
|
|
// Nature's Grace scenario: CAST phase + explicit HitMask for crit
|
|
// Crit is pre-calculated for travel-time spells
|
|
auto* spellInfo = CreateSpellInfo(1);
|
|
DamageInfo damageInfo(nullptr, nullptr, 100, spellInfo, SPELL_SCHOOL_MASK_NATURE, SPELL_DIRECT_DAMAGE);
|
|
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_CAST)
|
|
.WithHitMask(PROC_HIT_CRITICAL)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_CAST)
|
|
.WithHitMask(PROC_HIT_CRITICAL)
|
|
.WithDamageInfo(&damageInfo)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_CastPhaseWithExplicitHitMaskNoCrit)
|
|
{
|
|
// CAST phase + explicit HitMask requires crit, but spell didn't crit
|
|
auto* spellInfo = CreateSpellInfo(1);
|
|
DamageInfo damageInfo(nullptr, nullptr, 100, spellInfo, SPELL_SCHOOL_MASK_NATURE, SPELL_DIRECT_DAMAGE);
|
|
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_CAST)
|
|
.WithHitMask(PROC_HIT_CRITICAL)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_CAST)
|
|
.WithHitMask(PROC_HIT_NORMAL) // No crit
|
|
.WithDamageInfo(&damageInfo)
|
|
.Build();
|
|
|
|
EXPECT_FALSE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_CastPhaseWithDefaultHitMask)
|
|
{
|
|
// CAST phase + HitMask=0 should skip HitMask check (old behavior)
|
|
auto* spellInfo = CreateSpellInfo(1);
|
|
DamageInfo damageInfo(nullptr, nullptr, 100, spellInfo, SPELL_SCHOOL_MASK_NATURE, SPELL_DIRECT_DAMAGE);
|
|
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_CAST)
|
|
.WithHitMask(0) // Default - no explicit HitMask
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_CAST)
|
|
.WithHitMask(PROC_HIT_NORMAL) // Doesn't matter - HitMask check skipped
|
|
.WithDamageInfo(&damageInfo)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
// =============================================================================
|
|
// Combined Condition Tests
|
|
// =============================================================================
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_AllConditionsMatch)
|
|
{
|
|
auto* spellInfo = CreateSpellInfo(1);
|
|
DamageInfo damageInfo(nullptr, nullptr, 100, spellInfo, SPELL_SCHOOL_MASK_FIRE, SPELL_DIRECT_DAMAGE);
|
|
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellTypeMask(PROC_SPELL_TYPE_DAMAGE)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.WithHitMask(PROC_HIT_CRITICAL)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellTypeMask(PROC_SPELL_TYPE_DAMAGE)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.WithHitMask(PROC_HIT_CRITICAL)
|
|
.WithDamageInfo(&damageInfo)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_OneConditionFails)
|
|
{
|
|
auto* spellInfo = CreateSpellInfo(1);
|
|
DamageInfo damageInfo(nullptr, nullptr, 100, spellInfo, SPELL_SCHOOL_MASK_FIRE, SPELL_DIRECT_DAMAGE);
|
|
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellTypeMask(PROC_SPELL_TYPE_DAMAGE)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.WithHitMask(PROC_HIT_CRITICAL) // Requires crit
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellTypeMask(PROC_SPELL_TYPE_DAMAGE)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.WithHitMask(PROC_HIT_NORMAL) // But we got normal hit
|
|
.WithDamageInfo(&damageInfo)
|
|
.Build();
|
|
|
|
EXPECT_FALSE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
// =============================================================================
|
|
// Edge Cases
|
|
// =============================================================================
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_ZeroProcFlags)
|
|
{
|
|
// Zero proc flags should never match anything
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(0)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_MELEE_AUTO_ATTACK)
|
|
.WithHitMask(PROC_HIT_NORMAL)
|
|
.Build();
|
|
|
|
EXPECT_FALSE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_PeriodicDamage)
|
|
{
|
|
auto* spellInfo = CreateSpellInfo(1);
|
|
DamageInfo damageInfo(nullptr, nullptr, 50, spellInfo, SPELL_SCHOOL_MASK_SHADOW, SPELL_DIRECT_DAMAGE);
|
|
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_PERIODIC)
|
|
.WithSpellTypeMask(PROC_SPELL_TYPE_DAMAGE)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_PERIODIC)
|
|
.WithSpellTypeMask(PROC_SPELL_TYPE_DAMAGE)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.WithHitMask(PROC_HIT_NORMAL)
|
|
.WithDamageInfo(&damageInfo)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_TakenDamage)
|
|
{
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_TAKEN_DAMAGE)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_TAKEN_DAMAGE)
|
|
.WithHitMask(PROC_HIT_NORMAL)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
// =============================================================================
|
|
// SpellFamilyName/SpellFamilyFlags Tests - Class-specific proc matching
|
|
// =============================================================================
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_SpellFamilyNameMatch)
|
|
{
|
|
// Create a Mage spell (SpellFamilyName = SPELLFAMILY_MAGE = 3)
|
|
auto* spellInfo = SpellInfoBuilder()
|
|
.WithId(133) // Fireball
|
|
.WithSpellFamilyName(SPELLFAMILY_MAGE)
|
|
.Build();
|
|
_spellInfos.push_back(spellInfo);
|
|
|
|
DamageInfo damageInfo(nullptr, nullptr, 100, spellInfo, SPELL_SCHOOL_MASK_FIRE, SPELL_DIRECT_DAMAGE);
|
|
|
|
// Proc entry requires Mage spells
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellFamilyName(SPELLFAMILY_MAGE)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.WithHitMask(PROC_HIT_NORMAL)
|
|
.WithDamageInfo(&damageInfo)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_SpellFamilyNameNoMatch)
|
|
{
|
|
// Create a Warlock spell but proc requires Mage
|
|
auto* spellInfo = SpellInfoBuilder()
|
|
.WithId(686) // Shadow Bolt
|
|
.WithSpellFamilyName(SPELLFAMILY_WARLOCK)
|
|
.Build();
|
|
_spellInfos.push_back(spellInfo);
|
|
|
|
DamageInfo damageInfo(nullptr, nullptr, 100, spellInfo, SPELL_SCHOOL_MASK_SHADOW, SPELL_DIRECT_DAMAGE);
|
|
|
|
// Proc entry requires Mage spells
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellFamilyName(SPELLFAMILY_MAGE)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.WithHitMask(PROC_HIT_NORMAL)
|
|
.WithDamageInfo(&damageInfo)
|
|
.Build();
|
|
|
|
EXPECT_FALSE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_SpellFamilyFlagsMatch)
|
|
{
|
|
// Create a Paladin Holy Light spell with specific family flags
|
|
auto* spellInfo = SpellInfoBuilder()
|
|
.WithId(635) // Holy Light
|
|
.WithSpellFamilyName(SPELLFAMILY_PALADIN)
|
|
.WithSpellFamilyFlags(0x80000000, 0, 0) // Example flag for Holy Light
|
|
.Build();
|
|
_spellInfos.push_back(spellInfo);
|
|
|
|
HealInfo healInfo(nullptr, nullptr, 500, spellInfo, SPELL_SCHOOL_MASK_HOLY);
|
|
|
|
// Proc entry requires specific Paladin family flag
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS)
|
|
.WithSpellFamilyName(SPELLFAMILY_PALADIN)
|
|
.WithSpellFamilyMask(flag96(0x80000000, 0, 0))
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.WithHitMask(PROC_HIT_NORMAL)
|
|
.WithHealInfo(&healInfo)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_SpellFamilyFlagsNoMatch)
|
|
{
|
|
// Create a Paladin spell with different family flags
|
|
auto* spellInfo = SpellInfoBuilder()
|
|
.WithId(19750) // Flash of Light
|
|
.WithSpellFamilyName(SPELLFAMILY_PALADIN)
|
|
.WithSpellFamilyFlags(0x40000000, 0, 0) // Different flag
|
|
.Build();
|
|
_spellInfos.push_back(spellInfo);
|
|
|
|
HealInfo healInfo(nullptr, nullptr, 300, spellInfo, SPELL_SCHOOL_MASK_HOLY);
|
|
|
|
// Proc entry requires different family flag
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS)
|
|
.WithSpellFamilyName(SPELLFAMILY_PALADIN)
|
|
.WithSpellFamilyMask(flag96(0x80000000, 0, 0)) // Wants Holy Light flag
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.WithHitMask(PROC_HIT_NORMAL)
|
|
.WithHealInfo(&healInfo)
|
|
.Build();
|
|
|
|
EXPECT_FALSE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_SpellFamilyNameZeroAcceptsAll)
|
|
{
|
|
// When SpellFamilyName is 0, it should accept any spell family
|
|
auto* spellInfo = SpellInfoBuilder()
|
|
.WithId(100)
|
|
.WithSpellFamilyName(SPELLFAMILY_DRUID)
|
|
.Build();
|
|
_spellInfos.push_back(spellInfo);
|
|
|
|
DamageInfo damageInfo(nullptr, nullptr, 100, spellInfo, SPELL_SCHOOL_MASK_NATURE, SPELL_DIRECT_DAMAGE);
|
|
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellFamilyName(0) // Accept any family
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.WithHitMask(PROC_HIT_NORMAL)
|
|
.WithDamageInfo(&damageInfo)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_SpellFamilyFlagsZeroAcceptsAll)
|
|
{
|
|
// When SpellFamilyMask is 0, it should accept any flags within the family
|
|
auto* spellInfo = SpellInfoBuilder()
|
|
.WithId(100)
|
|
.WithSpellFamilyName(SPELLFAMILY_PRIEST)
|
|
.WithSpellFamilyFlags(0x12345678, 0xABCDEF01, 0x87654321) // Any flags
|
|
.Build();
|
|
_spellInfos.push_back(spellInfo);
|
|
|
|
HealInfo healInfo(nullptr, nullptr, 200, spellInfo, SPELL_SCHOOL_MASK_HOLY);
|
|
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS)
|
|
.WithSpellFamilyName(SPELLFAMILY_PRIEST)
|
|
.WithSpellFamilyMask(flag96(0, 0, 0)) // Accept any flags
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.WithHitMask(PROC_HIT_NORMAL)
|
|
.WithHealInfo(&healInfo)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_GenericFamilyIgnoresMask)
|
|
{
|
|
// Generic family (0) should bypass family mask checks entirely
|
|
auto* spellInfo = SpellInfoBuilder()
|
|
.WithId(101)
|
|
.WithSpellFamilyName(SPELLFAMILY_MAGE)
|
|
.WithSpellFamilyFlags(0x1, 0, 0) // some mage flag
|
|
.Build();
|
|
_spellInfos.push_back(spellInfo);
|
|
|
|
DamageInfo damageInfo(nullptr, nullptr, 100, spellInfo, SPELL_SCHOOL_MASK_FIRE, SPELL_DIRECT_DAMAGE);
|
|
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellFamilyName(0) // generic family
|
|
.WithSpellFamilyMask(flag96(0x2, 0, 0)) // does NOT match the spell's flags
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.WithHitMask(PROC_HIT_NORMAL)
|
|
.WithDamageInfo(&damageInfo)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo))
|
|
<< "Generic family entries should ignore mask restrictions";
|
|
}
|
|
|
|
TEST_F(SpellProcTest, SpellInfo_IsAffected_GenericBehavior)
|
|
{
|
|
auto* spellInfo = SpellInfoBuilder()
|
|
.WithId(102)
|
|
.WithSpellFamilyName(SPELLFAMILY_WARLOCK)
|
|
.WithSpellFamilyFlags(0x4, 0, 0)
|
|
.Build();
|
|
_spellInfos.push_back(spellInfo);
|
|
|
|
// generic family should return true regardless of mask
|
|
EXPECT_TRUE(spellInfo->IsAffected(0, flag96(0x4, 0, 0)));
|
|
EXPECT_TRUE(spellInfo->IsAffected(0, flag96(0x8, 0, 0)));
|
|
// a non-generic family still respects mask
|
|
EXPECT_FALSE(spellInfo->IsAffected(SPELLFAMILY_PALADIN, flag96(0x4, 0, 0)));
|
|
}
|
|
|
|
// =============================================================================
|
|
// Real-world Spell Proc Examples
|
|
// =============================================================================
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_HotStreakScenario)
|
|
{
|
|
// Hot Streak: Proc on critical damage spell from Mage
|
|
auto* fireballSpell = SpellInfoBuilder()
|
|
.WithId(133)
|
|
.WithSpellFamilyName(SPELLFAMILY_MAGE)
|
|
.WithSpellFamilyFlags(0x00000001, 0, 0) // Fireball flag
|
|
.Build();
|
|
_spellInfos.push_back(fireballSpell);
|
|
|
|
DamageInfo damageInfo(nullptr, nullptr, 1000, fireballSpell, SPELL_SCHOOL_MASK_FIRE, SPELL_DIRECT_DAMAGE);
|
|
|
|
// Hot Streak proc entry - triggers on fire spell crits
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellFamilyName(SPELLFAMILY_MAGE)
|
|
.WithSpellTypeMask(PROC_SPELL_TYPE_DAMAGE)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.WithHitMask(PROC_HIT_CRITICAL)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG)
|
|
.WithSpellTypeMask(PROC_SPELL_TYPE_DAMAGE)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.WithHitMask(PROC_HIT_CRITICAL)
|
|
.WithDamageInfo(&damageInfo)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_IlluminationScenario)
|
|
{
|
|
// Illumination: Proc on critical heals from Paladin
|
|
auto* holyLightSpell = SpellInfoBuilder()
|
|
.WithId(635)
|
|
.WithSpellFamilyName(SPELLFAMILY_PALADIN)
|
|
.WithSpellFamilyFlags(0x80000000, 0, 0)
|
|
.Build();
|
|
_spellInfos.push_back(holyLightSpell);
|
|
|
|
HealInfo healInfo(nullptr, nullptr, 2000, holyLightSpell, SPELL_SCHOOL_MASK_HOLY);
|
|
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS)
|
|
.WithSpellFamilyName(SPELLFAMILY_PALADIN)
|
|
.WithSpellTypeMask(PROC_SPELL_TYPE_HEAL)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.WithHitMask(PROC_HIT_CRITICAL)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS)
|
|
.WithSpellTypeMask(PROC_SPELL_TYPE_HEAL)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.WithHitMask(PROC_HIT_CRITICAL)
|
|
.WithHealInfo(&healInfo)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_SecondWindScenario)
|
|
{
|
|
// Second Wind: Proc when stunned/immobilized (taken hit with dodge/parry)
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_TAKEN_MELEE_AUTO_ATTACK | PROC_FLAG_TAKEN_SPELL_MELEE_DMG_CLASS)
|
|
.WithHitMask(PROC_HIT_DODGE | PROC_HIT_PARRY)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_TAKEN_MELEE_AUTO_ATTACK)
|
|
.WithHitMask(PROC_HIT_DODGE)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|
|
|
|
TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_SwordAndBoardScenario)
|
|
{
|
|
// Sword and Board: Proc on Devastate/Revenge (block effects)
|
|
auto* devastateSpell = SpellInfoBuilder()
|
|
.WithId(20243) // Devastate
|
|
.WithSpellFamilyName(SPELLFAMILY_WARRIOR)
|
|
.WithSpellFamilyFlags(0x00000000, 0x00000000, 0x00000100) // Devastate flag
|
|
.Build();
|
|
_spellInfos.push_back(devastateSpell);
|
|
|
|
DamageInfo damageInfo(nullptr, nullptr, 500, devastateSpell, SPELL_SCHOOL_MASK_NORMAL, SPELL_DIRECT_DAMAGE);
|
|
|
|
auto procEntry = SpellProcEntryBuilder()
|
|
.WithProcFlags(PROC_FLAG_DONE_SPELL_MELEE_DMG_CLASS)
|
|
.WithSpellFamilyName(SPELLFAMILY_WARRIOR)
|
|
.WithSpellFamilyMask(flag96(0, 0, 0x100)) // Devastate flag
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.Build();
|
|
|
|
auto eventInfo = ProcEventInfoBuilder()
|
|
.WithTypeMask(PROC_FLAG_DONE_SPELL_MELEE_DMG_CLASS)
|
|
.WithSpellPhaseMask(PROC_SPELL_PHASE_HIT)
|
|
.WithHitMask(PROC_HIT_NORMAL)
|
|
.WithDamageInfo(&damageInfo)
|
|
.Build();
|
|
|
|
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo));
|
|
}
|