Scripts/DK: correctly handle Blood Tap

This commit is contained in:
ccrs
2019-07-13 17:57:07 +02:00
parent 8c16f318fe
commit d1dc0e2dc1
7 changed files with 194 additions and 51 deletions
@@ -0,0 +1,4 @@
-- Blood Tap
DELETE FROM `spell_script_names` WHERE `ScriptName` = 'spell_dk_blood_tap';
INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES
(45529, 'spell_dk_blood_tap');
+54 -20
View File
@@ -22596,6 +22596,8 @@ void Player::SendInitialPacketsBeforeAddToMap()
// SMSG_UPDATE_WORLD_STATE // SMSG_UPDATE_WORLD_STATE
// SMSG_POWER_UPDATE // SMSG_POWER_UPDATE
ResyncRunes();
SetMovedUnit(this); SetMovedUnit(this);
} }
@@ -24514,46 +24516,73 @@ void Player::SetRuneCooldown(uint8 index, uint32 cooldown, bool casted /*= false
m_runes->runes[index].Cooldown = cooldown; m_runes->runes[index].Cooldown = cooldown;
m_runes->SetRuneState(index, (cooldown == 0) ? true : false); m_runes->SetRuneState(index, (cooldown == 0) ? true : false);
ResyncRunes();
} }
void Player::SetRuneConvertAura(uint8 index, AuraEffect const* aura) void Player::SetRuneConvertAura(uint8 index, AuraEffect const* aura)
{ {
m_runes->runes[index].ConvertAura = aura; m_runes->runes[index].ConvertAuras.insert(aura);
}
void Player::RemoveRuneConvertAura(uint8 index, AuraEffect const* aura)
{
m_runes->runes[index].ConvertAuras.erase(aura);
} }
void Player::AddRuneByAuraEffect(uint8 index, RuneType newType, AuraEffect const* aura) void Player::AddRuneByAuraEffect(uint8 index, RuneType newType, AuraEffect const* aura)
{ {
SetRuneConvertAura(index, aura); ConvertRune(index, newType); SetRuneConvertAura(index, aura);
ConvertRune(index, newType);
} }
void Player::RemoveRunesByAuraEffect(AuraEffect const* aura) void Player::RemoveRunesByAuraEffect(AuraEffect const* aura)
{ {
for (uint8 i = 0; i < MAX_RUNES; ++i) for (uint8 itr = 0; itr < MAX_RUNES; ++itr)
{ {
if (m_runes->runes[i].ConvertAura == aura) RemoveRuneConvertAura(itr, aura);
{
ConvertRune(i, GetBaseRune(i)); if (m_runes->runes[itr].ConvertAuras.empty())
SetRuneConvertAura(i, nullptr); ConvertRune(itr, GetBaseRune(itr));
}
} }
} }
void Player::RestoreBaseRune(uint8 index) void Player::RestoreBaseRune(uint8 index)
{ {
AuraEffect const* aura = m_runes->runes[index].ConvertAura; std::unordered_set<AuraEffect const*>& auras = m_runes->runes[index].ConvertAuras;
// If rune was converted by a non-passive aura that still active we should keep it converted
if (aura && !aura->GetSpellInfo()->HasAttribute(SPELL_ATTR0_PASSIVE)) AuraEffect const* aura = nullptr;
for (auto itr = auras.begin(); itr != auras.end() && !aura;)
{
if (AuraEffect const* temp = *itr)
{
if (temp->GetSpellInfo()->HasAttribute(SPELL_ATTR0_PASSIVE))
{
aura = temp;
auras.erase(itr);
}
else
++itr;
}
else
itr = auras.erase(itr);
}
if (!auras.empty())
return; return;
ConvertRune(index, GetBaseRune(index)); ConvertRune(index, GetBaseRune(index));
SetRuneConvertAura(index, nullptr);
// Don't drop passive talents providing rune convertion // Don't drop passive talents providing rune convertion
if (!aura || aura->GetAuraType() != SPELL_AURA_CONVERT_RUNE) if (!aura || aura->GetAuraType() != SPELL_AURA_CONVERT_RUNE)
return; return;
for (uint8 i = 0; i < MAX_RUNES; ++i)
for (uint8 itr = 0; itr < MAX_RUNES; ++itr)
{ {
if (aura == m_runes->runes[i].ConvertAura) if (m_runes->runes[itr].ConvertAuras.find(aura) != m_runes->runes[itr].ConvertAuras.end())
return; return;
} }
aura->GetBase()->Remove(); aura->GetBase()->Remove();
} }
@@ -24567,14 +24596,19 @@ void Player::ConvertRune(uint8 index, RuneType newType)
SendDirectMessage(&data); SendDirectMessage(&data);
} }
void Player::ResyncRunes(uint8 count) const void Player::ResyncRunes() const
{ {
WorldPacket data(SMSG_RESYNC_RUNES, 4 + count * 2); if (GetClass() != CLASS_DEATH_KNIGHT)
data << uint32(count); return;
for (uint32 i = 0; i < count; ++i)
WorldPacket data(SMSG_RESYNC_RUNES, 4 + MAX_RUNES * 2);
data << uint32(MAX_RUNES);
for (uint32 itr = 0; itr < MAX_RUNES; ++itr)
{ {
data << uint8(GetCurrentRune(i)); // rune type data << uint8(GetCurrentRune(itr)); // rune type
data << uint8(255 - (GetRuneCooldown(i) * 51)); // passed cooldown time (0-255)
uint32 value = uint32(255) - ((GetRuneCooldown(itr) * uint32(255)) / uint32(RUNE_BASE_COOLDOWN));
data << uint8(value); // passed cooldown time (0-255)
} }
SendDirectMessage(&data); SendDirectMessage(&data);
} }
+4 -2
View File
@@ -32,6 +32,7 @@
#include "QuestDef.h" #include "QuestDef.h"
#include <memory> #include <memory>
#include <queue> #include <queue>
#include <unordered_set>
struct AccessRequirement; struct AccessRequirement;
struct AchievementEntry; struct AchievementEntry;
@@ -276,7 +277,7 @@ struct RuneInfo
uint8 BaseRune; uint8 BaseRune;
uint8 CurrentRune; uint8 CurrentRune;
uint32 Cooldown; uint32 Cooldown;
AuraEffect const* ConvertAura; std::unordered_set<AuraEffect const*> ConvertAuras;
}; };
struct Runes struct Runes
@@ -2118,11 +2119,12 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void SetCurrentRune(uint8 index, RuneType currentRune) { m_runes->runes[index].CurrentRune = currentRune; } void SetCurrentRune(uint8 index, RuneType currentRune) { m_runes->runes[index].CurrentRune = currentRune; }
void SetRuneCooldown(uint8 index, uint32 cooldown, bool casted = false); void SetRuneCooldown(uint8 index, uint32 cooldown, bool casted = false);
void SetRuneConvertAura(uint8 index, AuraEffect const* aura); void SetRuneConvertAura(uint8 index, AuraEffect const* aura);
void RemoveRuneConvertAura(uint8 index, AuraEffect const* aura);
void AddRuneByAuraEffect(uint8 index, RuneType newType, AuraEffect const* aura); void AddRuneByAuraEffect(uint8 index, RuneType newType, AuraEffect const* aura);
void RemoveRunesByAuraEffect(AuraEffect const* aura); void RemoveRunesByAuraEffect(AuraEffect const* aura);
void RestoreBaseRune(uint8 index); void RestoreBaseRune(uint8 index);
void ConvertRune(uint8 index, RuneType newType); void ConvertRune(uint8 index, RuneType newType);
void ResyncRunes(uint8 count) const; void ResyncRunes() const;
void AddRunePower(uint8 index) const; void AddRunePower(uint8 index) const;
void InitRunes(); void InitRunes();
+6 -6
View File
@@ -4160,7 +4160,7 @@ void Spell::SendSpellGo()
castFlags |= CAST_FLAG_PENDING; castFlags |= CAST_FLAG_PENDING;
if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) || m_spellInfo->HasAttribute(SPELL_ATTR0_CU_NEEDS_AMMO_DATA)) if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) || m_spellInfo->HasAttribute(SPELL_ATTR0_CU_NEEDS_AMMO_DATA))
castFlags |= CAST_FLAG_AMMO; // arrows/bullets visual castFlags |= CAST_FLAG_AMMO; // arrows/bullets visual
if ((m_caster->GetTypeId() == TYPEID_PLAYER || if ((m_caster->GetTypeId() == TYPEID_PLAYER ||
(m_caster->GetTypeId() == TYPEID_UNIT && m_caster->ToCreature()->IsPet())) (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->ToCreature()->IsPet()))
@@ -4173,12 +4173,12 @@ void Spell::SendSpellGo()
&& m_spellInfo->PowerType == POWER_RUNE && m_spellInfo->PowerType == POWER_RUNE
&& !(_triggeredCastFlags & TRIGGERED_IGNORE_POWER_AND_REAGENT_COST)) && !(_triggeredCastFlags & TRIGGERED_IGNORE_POWER_AND_REAGENT_COST))
{ {
castFlags |= CAST_FLAG_NO_GCD; // not needed, but Blizzard sends it castFlags |= CAST_FLAG_NO_GCD; // not needed, but Blizzard sends it
castFlags |= CAST_FLAG_RUNE_LIST; // rune cooldowns list castFlags |= CAST_FLAG_RUNE_LIST; // rune cooldowns list
} }
if (m_spellInfo->HasEffect(SPELL_EFFECT_ACTIVATE_RUNE)) if (m_spellInfo->HasEffect(SPELL_EFFECT_ACTIVATE_RUNE))
castFlags |= CAST_FLAG_RUNE_LIST; // rune cooldowns list castFlags |= CAST_FLAG_RUNE_LIST; // rune cooldowns list
if (m_targets.HasTraj()) if (m_targets.HasTraj())
castFlags |= CAST_FLAG_ADJUST_MISSILE; castFlags |= CAST_FLAG_ADJUST_MISSILE;
@@ -4207,12 +4207,12 @@ void Spell::SendSpellGo()
if (castFlags & CAST_FLAG_POWER_LEFT_SELF) if (castFlags & CAST_FLAG_POWER_LEFT_SELF)
castData.RemainingPower = ASSERT_NOTNULL(m_caster->ToUnit())->GetPower(m_spellInfo->PowerType); castData.RemainingPower = ASSERT_NOTNULL(m_caster->ToUnit())->GetPower(m_spellInfo->PowerType);
if (castFlags & CAST_FLAG_RUNE_LIST) // rune cooldowns list if (castFlags & CAST_FLAG_RUNE_LIST && !m_spellInfo->HasAura(SPELL_AURA_CONVERT_RUNE)) // rune cooldowns list
{ {
castData.RemainingRunes = boost::in_place(); castData.RemainingRunes = boost::in_place();
/// @todo There is a crash caused by a spell with CAST_FLAG_RUNE_LIST cast by a creature /// @todo There is a crash caused by a spell with CAST_FLAG_RUNE_LIST cast by a creature
//The creature is the mover of a player, so HandleCastSpellOpcode uses it as the caster // The creature is the mover of a player, so HandleCastSpellOpcode uses it as the caster
if (Player* player = m_caster->ToPlayer()) if (Player* player = m_caster->ToPlayer())
{ {
uint8 runeMaskInitial = m_runesState; uint8 runeMaskInitial = m_runesState;
+3 -1
View File
@@ -540,6 +540,8 @@ class TC_GAME_API Spell
uint64 GetDelayMoment() const { return m_delayMoment; } uint64 GetDelayMoment() const { return m_delayMoment; }
uint64 CalculateDelayMomentForDst() const; uint64 CalculateDelayMomentForDst() const;
void RecalculateDelayMomentForDst(); void RecalculateDelayMomentForDst();
uint8 GetRuneState() const { return m_runesState; }
void SetRuneState(uint8 value) { m_runesState = value; }
bool IsNeedSendToClient() const; bool IsNeedSendToClient() const;
@@ -577,7 +579,7 @@ class TC_GAME_API Spell
// e.g. damage around area spell trigered by victim aura and damage enemies of aura caster // e.g. damage around area spell trigered by victim aura and damage enemies of aura caster
Unit* m_originalCaster; // cached pointer for m_originalCaster, updated at Spell::UpdatePointers() Unit* m_originalCaster; // cached pointer for m_originalCaster, updated at Spell::UpdatePointers()
//Spell data // Spell data
SpellSchoolMask m_spellSchoolMask; // Spell school (can be overwrite for some spells (wand shoot for example) SpellSchoolMask m_spellSchoolMask; // Spell school (can be overwrite for some spells (wand shoot for example)
WeaponAttackType m_attackType; // For weapon based attack WeaponAttackType m_attackType; // For weapon based attack
int32 m_powerCost; // Calculated spell cost initialized only in Spell::prepare int32 m_powerCost; // Calculated spell cost initialized only in Spell::prepare
-22
View File
@@ -5036,28 +5036,6 @@ void Spell::EffectActivateRune(SpellEffIndex effIndex)
if (count == 0) if (count == 0)
count = 1; count = 1;
// Blood Tap
if (m_spellInfo->Id == 45529 && count > 0)
{
for (uint32 l = 0; l + 1 < MAX_RUNES && count > 0; ++l)
{
// Check if both runes are on cd as that is the only time when this needs to come into effect
if ((player->GetRuneCooldown(l) && player->GetBaseRune(l) == RUNE_BLOOD) && (player->GetRuneCooldown(l + 1) && player->GetBaseRune(l + 1) == RUNE_BLOOD))
{
// Should always update the rune with the lowest cd
if (l + 1 < MAX_RUNES && player->GetRuneCooldown(l) >= player->GetRuneCooldown(l + 1))
++l;
player->SetRuneCooldown(l, 0);
--count;
// is needed to push through to the client that the rune is active
player->ResyncRunes(MAX_RUNES);
}
else
break;
}
}
for (uint32 j = 0; j < MAX_RUNES && count > 0; ++j) for (uint32 j = 0; j < MAX_RUNES && count > 0; ++j)
{ {
if (player->GetRuneCooldown(j) && player->GetCurrentRune(j) == RuneType(m_spellInfo->Effects[effIndex].MiscValue)) if (player->GetRuneCooldown(j) && player->GetCurrentRune(j) == RuneType(m_spellInfo->Effects[effIndex].MiscValue))
+123
View File
@@ -31,10 +31,12 @@
#include "PlayerAI.h" #include "PlayerAI.h"
#include "Spell.h" #include "Spell.h"
#include "SpellAuraEffects.h" #include "SpellAuraEffects.h"
#include "SpellAuras.h"
#include "SpellHistory.h" #include "SpellHistory.h"
#include "SpellMgr.h" #include "SpellMgr.h"
#include "SpellScript.h" #include "SpellScript.h"
#include "TemporarySummon.h" #include "TemporarySummon.h"
#include "Unit.h"
enum DeathKnightSpells enum DeathKnightSpells
{ {
@@ -3049,6 +3051,126 @@ public:
} }
}; };
#define DKBloodTapScriptName "spell_dk_blood_tap"
// 45529 - Blood Tap
class spell_dk_blood_tap : public SpellScriptLoader
{
public:
spell_dk_blood_tap() : SpellScriptLoader(DKBloodTapScriptName) { }
class spell_dk_blood_tap_AuraScript : public AuraScript
{
PrepareAuraScript(spell_dk_blood_tap_AuraScript);
public:
spell_dk_blood_tap_AuraScript()
{
_runeIndex = MAX_RUNES;
}
void SetRuneIndex(uint8 index)
{
_runeIndex = index;
}
void HandleApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
{
PreventDefaultAction();
Player* player = GetTarget()->ToPlayer();
if (!player)
return;
if (player->GetClass() != CLASS_DEATH_KNIGHT || _runeIndex == MAX_RUNES)
return;
player->AddRuneByAuraEffect(_runeIndex, RUNE_DEATH, aurEff);
}
void Register() override
{
OnEffectApply += AuraEffectApplyFn(spell_dk_blood_tap_AuraScript::HandleApply, EFFECT_1, SPELL_AURA_CONVERT_RUNE, AURA_EFFECT_HANDLE_REAL);
}
private:
uint8 _runeIndex;
};
AuraScript* GetAuraScript() const override
{
return new spell_dk_blood_tap_AuraScript();
}
class spell_dk_blood_tap_SpellScript : public SpellScript
{
PrepareSpellScript(spell_dk_blood_tap_SpellScript);
public:
spell_dk_blood_tap_SpellScript()
{
_runeIndex = MAX_RUNES;
}
void HandleEffect(SpellEffIndex effIndex)
{
PreventHitDefaultEffect(effIndex);
Unit* caster = GetCaster();
if (caster->GetTypeId() != TYPEID_PLAYER)
return;
Player* player = caster->ToPlayer();
if (player->GetClass() != CLASS_DEATH_KNIGHT)
return;
// needed later
if (Spell* spell = GetSpell())
spell->SetRuneState(caster->ToPlayer()->GetRunesState());
uint8 resetIndex;
// Rune reset:
// If both runes are on cooldown, reset the shorter one
// If only one rune is on cooldown, reset that rune
if (!player->GetRuneCooldown(1))
resetIndex = 0; // 1 is ready, so reset 0 (no matter if it's on cd)
else if (!player->GetRuneCooldown(0) || player->GetRuneCooldown(1) < player->GetRuneCooldown(0))
resetIndex = 1; // 0 is ready, or both are on cd and 1 is shorter, so reset 1
else
resetIndex = 0; // both are on cd and 0 is shorter, reset 0
// if both runes are the same type, transform the same one as above
if (player->GetCurrentRune(0) == player->GetCurrentRune(1))
_runeIndex = resetIndex;
else // otherwise transform the blood rune
_runeIndex = player->GetCurrentRune(0) == RUNE_BLOOD ? 0 : 1;
player->SetRuneCooldown(resetIndex, 0);
}
void SetRuneIndex(SpellEffIndex /*effIndex*/)
{
if (Aura* aura = GetHitAura())
if (spell_dk_blood_tap_AuraScript* script = aura->GetScript<spell_dk_blood_tap_AuraScript>(DKBloodTapScriptName))
script->SetRuneIndex(_runeIndex);
}
void Register() override
{
OnEffectLaunch += SpellEffectFn(spell_dk_blood_tap_SpellScript::HandleEffect, EFFECT_0, SPELL_EFFECT_ACTIVATE_RUNE);
OnEffectHitTarget += SpellEffectFn(spell_dk_blood_tap_SpellScript::SetRuneIndex, EFFECT_1, SPELL_EFFECT_APPLY_AURA);
}
private:
uint8 _runeIndex;
};
SpellScript* GetSpellScript() const override
{
return new spell_dk_blood_tap_SpellScript();
}
};
void AddSC_deathknight_spell_scripts() void AddSC_deathknight_spell_scripts()
{ {
new spell_dk_acclimation(); new spell_dk_acclimation();
@@ -3104,4 +3226,5 @@ void AddSC_deathknight_spell_scripts()
new spell_dk_raise_ally_initial(); new spell_dk_raise_ally_initial();
new spell_dk_raise_ally(); new spell_dk_raise_ally();
new spell_dk_ghoul_thrash(); new spell_dk_ghoul_thrash();
new spell_dk_blood_tap();
} }