Core/Spells: Implemented evoker empower spell mechanic
Port From (https://github.com/TrinityCore/TrinityCore/commit/a39d0db9ec64f6bf38716abaade5b7835f2db338)
This commit is contained in:
@@ -34,7 +34,9 @@ namespace Framework.Constants
|
|||||||
CalcDamage,
|
CalcDamage,
|
||||||
CalcHealing,
|
CalcHealing,
|
||||||
OnPrecast,
|
OnPrecast,
|
||||||
CalcCastTime
|
CalcCastTime,
|
||||||
|
EmpowerStageCompleted,
|
||||||
|
EmpowerCompleted
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuraScript interface - enum used for runtime checks of script function calls
|
// AuraScript interface - enum used for runtime checks of script function calls
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ namespace Framework.Constants
|
|||||||
|
|
||||||
public const uint VisualKitFood = 406;
|
public const uint VisualKitFood = 406;
|
||||||
public const uint VisualKitDrink = 438;
|
public const uint VisualKitDrink = 438;
|
||||||
|
|
||||||
|
public const uint EmpowerHoldTimeAtMax = 1 * Time.InMilliseconds;
|
||||||
|
public const uint EmpowerHardcodedGCD = 359115;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1217,6 +1217,13 @@ namespace Framework.Database
|
|||||||
"EffectRadiusIndex2, EffectSpellClassMask1, EffectSpellClassMask2, EffectSpellClassMask3, EffectSpellClassMask4, ImplicitTarget1, " +
|
"EffectRadiusIndex2, EffectSpellClassMask1, EffectSpellClassMask2, EffectSpellClassMask3, EffectSpellClassMask4, ImplicitTarget1, " +
|
||||||
"ImplicitTarget2, SpellID FROM spell_effect WHERE (`VerifiedBuild` > 0) = ?");
|
"ImplicitTarget2, SpellID FROM spell_effect WHERE (`VerifiedBuild` > 0) = ?");
|
||||||
|
|
||||||
|
// SpellEmpower.db2
|
||||||
|
PrepareStatement(HotfixStatements.SEL_SPELL_EMPOWER, "SELECT ID, SpellID, Unused1000 FROM spell_empower WHERE (`VerifiedBuild` > 0) = ?");
|
||||||
|
|
||||||
|
// SpellEmpowerStage.db2
|
||||||
|
PrepareStatement(HotfixStatements.SEL_SPELL_EMPOWER_STAGE, "SELECT ID, Stage, DurationMs, SpellEmpowerID FROM spell_empower_stage" +
|
||||||
|
" WHERE (`VerifiedBuild` > 0) = ?");
|
||||||
|
|
||||||
// SpellEquippedItems.db2
|
// SpellEquippedItems.db2
|
||||||
PrepareStatement(HotfixStatements.SEL_SPELL_EQUIPPED_ITEMS, "SELECT ID, SpellID, EquippedItemClass, EquippedItemInvTypes, EquippedItemSubclass" +
|
PrepareStatement(HotfixStatements.SEL_SPELL_EQUIPPED_ITEMS, "SELECT ID, SpellID, EquippedItemClass, EquippedItemInvTypes, EquippedItemSubclass" +
|
||||||
" FROM spell_equipped_items WHERE (`VerifiedBuild` > 0) = ?");
|
" FROM spell_equipped_items WHERE (`VerifiedBuild` > 0) = ?");
|
||||||
@@ -2183,6 +2190,10 @@ namespace Framework.Database
|
|||||||
|
|
||||||
SEL_SPELL_EFFECT,
|
SEL_SPELL_EFFECT,
|
||||||
|
|
||||||
|
SEL_SPELL_EMPOWER,
|
||||||
|
|
||||||
|
SEL_SPELL_EMPOWER_STAGE,
|
||||||
|
|
||||||
SEL_SPELL_EQUIPPED_ITEMS,
|
SEL_SPELL_EQUIPPED_ITEMS,
|
||||||
|
|
||||||
SEL_SPELL_FOCUS_OBJECT,
|
SEL_SPELL_FOCUS_OBJECT,
|
||||||
|
|||||||
@@ -303,6 +303,8 @@ namespace Game.DataStorage
|
|||||||
SpellCooldownsStorage = ReadDB2<SpellCooldownsRecord>("SpellCooldowns.db2", HotfixStatements.SEL_SPELL_COOLDOWNS);
|
SpellCooldownsStorage = ReadDB2<SpellCooldownsRecord>("SpellCooldowns.db2", HotfixStatements.SEL_SPELL_COOLDOWNS);
|
||||||
SpellDurationStorage = ReadDB2<SpellDurationRecord>("SpellDuration.db2", HotfixStatements.SEL_SPELL_DURATION);
|
SpellDurationStorage = ReadDB2<SpellDurationRecord>("SpellDuration.db2", HotfixStatements.SEL_SPELL_DURATION);
|
||||||
SpellEffectStorage = ReadDB2<SpellEffectRecord>("SpellEffect.db2", HotfixStatements.SEL_SPELL_EFFECT);
|
SpellEffectStorage = ReadDB2<SpellEffectRecord>("SpellEffect.db2", HotfixStatements.SEL_SPELL_EFFECT);
|
||||||
|
SpellEmpowerStorage = ReadDB2<SpellEmpowerRecord>("SpellEmpower.db2", HotfixStatements.SEL_SPELL_EMPOWER);
|
||||||
|
SpellEmpowerStageStorage = ReadDB2<SpellEmpowerStageRecord>("SpellEmpowerStage.db2", HotfixStatements.SEL_SPELL_EMPOWER_STAGE);
|
||||||
SpellEquippedItemsStorage = ReadDB2<SpellEquippedItemsRecord>("SpellEquippedItems.db2", HotfixStatements.SEL_SPELL_EQUIPPED_ITEMS);
|
SpellEquippedItemsStorage = ReadDB2<SpellEquippedItemsRecord>("SpellEquippedItems.db2", HotfixStatements.SEL_SPELL_EQUIPPED_ITEMS);
|
||||||
SpellFocusObjectStorage = ReadDB2<SpellFocusObjectRecord>("SpellFocusObject.db2", HotfixStatements.SEL_SPELL_FOCUS_OBJECT, HotfixStatements.SEL_SPELL_FOCUS_OBJECT_LOCALE);
|
SpellFocusObjectStorage = ReadDB2<SpellFocusObjectRecord>("SpellFocusObject.db2", HotfixStatements.SEL_SPELL_FOCUS_OBJECT, HotfixStatements.SEL_SPELL_FOCUS_OBJECT_LOCALE);
|
||||||
SpellInterruptsStorage = ReadDB2<SpellInterruptsRecord>("SpellInterrupts.db2", HotfixStatements.SEL_SPELL_INTERRUPTS);
|
SpellInterruptsStorage = ReadDB2<SpellInterruptsRecord>("SpellInterrupts.db2", HotfixStatements.SEL_SPELL_INTERRUPTS);
|
||||||
@@ -378,7 +380,7 @@ namespace Game.DataStorage
|
|||||||
UnitPowerBarStorage = ReadDB2<UnitPowerBarRecord>("UnitPowerBar.db2", HotfixStatements.SEL_UNIT_POWER_BAR, HotfixStatements.SEL_UNIT_POWER_BAR_LOCALE);
|
UnitPowerBarStorage = ReadDB2<UnitPowerBarRecord>("UnitPowerBar.db2", HotfixStatements.SEL_UNIT_POWER_BAR, HotfixStatements.SEL_UNIT_POWER_BAR_LOCALE);
|
||||||
VehicleStorage = ReadDB2<VehicleRecord>("Vehicle.db2", HotfixStatements.SEL_VEHICLE);
|
VehicleStorage = ReadDB2<VehicleRecord>("Vehicle.db2", HotfixStatements.SEL_VEHICLE);
|
||||||
VehicleSeatStorage = ReadDB2<VehicleSeatRecord>("VehicleSeat.db2", HotfixStatements.SEL_VEHICLE_SEAT);
|
VehicleSeatStorage = ReadDB2<VehicleSeatRecord>("VehicleSeat.db2", HotfixStatements.SEL_VEHICLE_SEAT);
|
||||||
VignetteStorage = ReadDB2<VignetteRecord>("Vignette.db2", HotfixStatements.SEL_VIGNETTE, HotfixStatements.SEL_VIGNETTE_LOCALE);
|
VignetteStorage = ReadDB2<VignetteRecord>("Vignette.db2", HotfixStatements.SEL_VIGNETTE, HotfixStatements.SEL_VIGNETTE_LOCALE);
|
||||||
WMOAreaTableStorage = ReadDB2<WMOAreaTableRecord>("WMOAreaTable.db2", HotfixStatements.SEL_WMO_AREA_TABLE, HotfixStatements.SEL_WMO_AREA_TABLE_LOCALE);
|
WMOAreaTableStorage = ReadDB2<WMOAreaTableRecord>("WMOAreaTable.db2", HotfixStatements.SEL_WMO_AREA_TABLE, HotfixStatements.SEL_WMO_AREA_TABLE_LOCALE);
|
||||||
WorldEffectStorage = ReadDB2<WorldEffectRecord>("WorldEffect.db2", HotfixStatements.SEL_WORLD_EFFECT);
|
WorldEffectStorage = ReadDB2<WorldEffectRecord>("WorldEffect.db2", HotfixStatements.SEL_WORLD_EFFECT);
|
||||||
WorldMapOverlayStorage = ReadDB2<WorldMapOverlayRecord>("WorldMapOverlay.db2", HotfixStatements.SEL_WORLD_MAP_OVERLAY);
|
WorldMapOverlayStorage = ReadDB2<WorldMapOverlayRecord>("WorldMapOverlay.db2", HotfixStatements.SEL_WORLD_MAP_OVERLAY);
|
||||||
@@ -740,6 +742,8 @@ namespace Game.DataStorage
|
|||||||
public static DB6Storage<SpellCooldownsRecord> SpellCooldownsStorage;
|
public static DB6Storage<SpellCooldownsRecord> SpellCooldownsStorage;
|
||||||
public static DB6Storage<SpellDurationRecord> SpellDurationStorage;
|
public static DB6Storage<SpellDurationRecord> SpellDurationStorage;
|
||||||
public static DB6Storage<SpellEffectRecord> SpellEffectStorage;
|
public static DB6Storage<SpellEffectRecord> SpellEffectStorage;
|
||||||
|
public static DB6Storage<SpellEmpowerRecord> SpellEmpowerStorage;
|
||||||
|
public static DB6Storage<SpellEmpowerStageRecord> SpellEmpowerStageStorage;
|
||||||
public static DB6Storage<SpellEquippedItemsRecord> SpellEquippedItemsStorage;
|
public static DB6Storage<SpellEquippedItemsRecord> SpellEquippedItemsStorage;
|
||||||
public static DB6Storage<SpellFocusObjectRecord> SpellFocusObjectStorage;
|
public static DB6Storage<SpellFocusObjectRecord> SpellFocusObjectStorage;
|
||||||
public static DB6Storage<SpellInterruptsRecord> SpellInterruptsStorage;
|
public static DB6Storage<SpellInterruptsRecord> SpellInterruptsStorage;
|
||||||
|
|||||||
@@ -327,6 +327,21 @@ namespace Game.DataStorage
|
|||||||
public uint SpellID;
|
public uint SpellID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class SpellEmpowerRecord
|
||||||
|
{
|
||||||
|
public uint Id;
|
||||||
|
public int SpellID;
|
||||||
|
public int Unused1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class SpellEmpowerStageRecord
|
||||||
|
{
|
||||||
|
public uint Id;
|
||||||
|
public int Stage;
|
||||||
|
public int DurationMs;
|
||||||
|
public uint SpellEmpowerID;
|
||||||
|
}
|
||||||
|
|
||||||
public sealed class SpellEquippedItemsRecord
|
public sealed class SpellEquippedItemsRecord
|
||||||
{
|
{
|
||||||
public uint Id;
|
public uint Id;
|
||||||
|
|||||||
@@ -262,6 +262,7 @@ namespace Game.Entities
|
|||||||
SetModRangedHaste(1.0f);
|
SetModRangedHaste(1.0f);
|
||||||
SetModHasteRegen(1.0f);
|
SetModHasteRegen(1.0f);
|
||||||
SetModTimeRate(1.0f);
|
SetModTimeRate(1.0f);
|
||||||
|
SetSpellEmpowerStage(-1);
|
||||||
|
|
||||||
SetSpeedRate(UnitMoveType.Walk, creatureInfo.SpeedWalk);
|
SetSpeedRate(UnitMoveType.Walk, creatureInfo.SpeedWalk);
|
||||||
SetSpeedRate(UnitMoveType.Run, creatureInfo.SpeedRun);
|
SetSpeedRate(UnitMoveType.Run, creatureInfo.SpeedRun);
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ namespace Game.Entities
|
|||||||
ObjectGuid m_temporaryUnsummonedBattlePet;
|
ObjectGuid m_temporaryUnsummonedBattlePet;
|
||||||
Dictionary<uint, StoredAuraTeleportLocation> m_storedAuraTeleportLocations = new();
|
Dictionary<uint, StoredAuraTeleportLocation> m_storedAuraTeleportLocations = new();
|
||||||
SpellCastRequest _pendingSpellCastRequest;
|
SpellCastRequest _pendingSpellCastRequest;
|
||||||
|
float m_empowerMinHoldStagePercent;
|
||||||
|
|
||||||
//Mail
|
//Mail
|
||||||
List<Mail> m_mail = new();
|
List<Mail> m_mail = new();
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ namespace Game.Entities
|
|||||||
m_legacyRaidDifficulty = Difficulty.Raid10N;
|
m_legacyRaidDifficulty = Difficulty.Raid10N;
|
||||||
m_InstanceValid = true;
|
m_InstanceValid = true;
|
||||||
|
|
||||||
|
m_empowerMinHoldStagePercent = 1.0f;
|
||||||
|
|
||||||
_specializationInfo = new SpecializationInfo();
|
_specializationInfo = new SpecializationInfo();
|
||||||
|
|
||||||
for (byte i = 0; i < (byte)BaseModGroup.End; ++i)
|
for (byte i = 0; i < (byte)BaseModGroup.End; ++i)
|
||||||
@@ -3319,6 +3321,10 @@ namespace Game.Entities
|
|||||||
SetFaction(rEntry != null ? (uint)rEntry.FactionID : 0);
|
SetFaction(rEntry != null ? (uint)rEntry.FactionID : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float GetEmpowerMinHoldStagePercent() { return m_empowerMinHoldStagePercent; }
|
||||||
|
|
||||||
|
public void SetEmpowerMinHoldStagePercent(float empowerMinHoldStagePercent) { m_empowerMinHoldStagePercent = empowerMinHoldStagePercent; }
|
||||||
|
|
||||||
public void SetResurrectRequestData(WorldObject caster, uint health, uint mana, uint appliedAura)
|
public void SetResurrectRequestData(WorldObject caster, uint health, uint mana, uint appliedAura)
|
||||||
{
|
{
|
||||||
Cypher.Assert(!IsResurrectRequested());
|
Cypher.Assert(!IsResurrectRequested());
|
||||||
@@ -3329,6 +3335,7 @@ namespace Game.Entities
|
|||||||
_resurrectionData.Mana = mana;
|
_resurrectionData.Mana = mana;
|
||||||
_resurrectionData.Aura = appliedAura;
|
_resurrectionData.Aura = appliedAura;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearResurrectRequestData()
|
public void ClearResurrectRequestData()
|
||||||
{
|
{
|
||||||
_resurrectionData = null;
|
_resurrectionData = null;
|
||||||
@@ -5810,6 +5817,7 @@ namespace Game.Entities
|
|||||||
SetModRangedHaste(1.0f);
|
SetModRangedHaste(1.0f);
|
||||||
SetModHasteRegen(1.0f);
|
SetModHasteRegen(1.0f);
|
||||||
SetModTimeRate(1.0f);
|
SetModTimeRate(1.0f);
|
||||||
|
SetSpellEmpowerStage(-1);
|
||||||
|
|
||||||
// reset size before reapply auras
|
// reset size before reapply auras
|
||||||
SetObjectScale(1.0f);
|
SetObjectScale(1.0f);
|
||||||
|
|||||||
@@ -898,7 +898,7 @@ namespace Game.Entities
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (spellType == CurrentSpellTypes.Channeled)
|
if (spellType == CurrentSpellTypes.Channeled)
|
||||||
spell.SendChannelUpdate(0);
|
spell.SendChannelUpdate(0, result);
|
||||||
|
|
||||||
spell.Finish(result);
|
spell.Finish(result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2428,6 +2428,10 @@ namespace Game.Entities
|
|||||||
RemoveDynamicUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ChannelObjects), index);
|
RemoveDynamicUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ChannelObjects), index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sbyte GetSpellEmpowerStage() { return m_unitData.SpellEmpowerStage; }
|
||||||
|
|
||||||
|
public void SetSpellEmpowerStage(sbyte stage) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.SpellEmpowerStage), stage); }
|
||||||
|
|
||||||
public static bool IsDamageReducedByArmor(SpellSchoolMask schoolMask, SpellInfo spellInfo = null)
|
public static bool IsDamageReducedByArmor(SpellSchoolMask schoolMask, SpellInfo spellInfo = null)
|
||||||
{
|
{
|
||||||
// only physical spells damage gets reduced by armor
|
// only physical spells damage gets reduced by armor
|
||||||
|
|||||||
@@ -379,6 +379,42 @@ namespace Game
|
|||||||
mover.InterruptSpell(CurrentSpellTypes.Channeled);
|
mover.InterruptSpell(CurrentSpellTypes.Channeled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[WorldPacketHandler(ClientOpcodes.SetEmpowerMinHoldStagePercent, Processing = PacketProcessing.Inplace)]
|
||||||
|
void HandleSetEmpowerMinHoldStagePercent(SetEmpowerMinHoldStagePercent setEmpowerMinHoldStagePercent)
|
||||||
|
{
|
||||||
|
_player.SetEmpowerMinHoldStagePercent(setEmpowerMinHoldStagePercent.MinHoldStagePercent);
|
||||||
|
}
|
||||||
|
|
||||||
|
[WorldPacketHandler(ClientOpcodes.SpellEmpowerRelease, Processing = PacketProcessing.Inplace)]
|
||||||
|
void HandleSpellEmpowerRelease(SpellEmpowerRelease spellEmpowerRelease)
|
||||||
|
{
|
||||||
|
// ignore for remote control state (for player case)
|
||||||
|
Unit mover = _player.GetUnitBeingMoved();
|
||||||
|
if (mover != _player && mover.IsPlayer())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Spell spell = mover.GetCurrentSpell(CurrentSpellTypes.Channeled);
|
||||||
|
if (spell == null || spell.GetSpellInfo().Id != spellEmpowerRelease.SpellID || !spell.IsEmpowerSpell())
|
||||||
|
return;
|
||||||
|
|
||||||
|
spell.SetEmpowerReleasedByClient(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[WorldPacketHandler(ClientOpcodes.SpellEmpowerRestart, Processing = PacketProcessing.Inplace)]
|
||||||
|
void HandleSpellEmpowerRestart(SpellEmpowerRestart spellEmpowerRestart)
|
||||||
|
{
|
||||||
|
// ignore for remote control state (for player case)
|
||||||
|
Unit mover = _player.GetUnitBeingMoved();
|
||||||
|
if (mover != _player && mover.IsPlayer())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Spell spell = mover.GetCurrentSpell(CurrentSpellTypes.Channeled);
|
||||||
|
if (spell == null || spell.GetSpellInfo().Id != spellEmpowerRestart.SpellID || !spell.IsEmpowerSpell())
|
||||||
|
return;
|
||||||
|
|
||||||
|
spell.SetEmpowerReleasedByClient(false);
|
||||||
|
}
|
||||||
|
|
||||||
[WorldPacketHandler(ClientOpcodes.TotemDestroyed, Processing = PacketProcessing.Inplace)]
|
[WorldPacketHandler(ClientOpcodes.TotemDestroyed, Processing = PacketProcessing.Inplace)]
|
||||||
void HandleTotemDestroyed(TotemDestroyed totemDestroyed)
|
void HandleTotemDestroyed(TotemDestroyed totemDestroyed)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
|
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
using Framework.Constants;
|
using Framework.Constants;
|
||||||
using Framework.Dynamic;
|
|
||||||
using Game.Entities;
|
using Game.Entities;
|
||||||
using Game.Spells;
|
using Game.Spells;
|
||||||
using System;
|
using System;
|
||||||
@@ -834,6 +833,128 @@ namespace Game.Networking.Packets
|
|||||||
public int TimeRemaining;
|
public int TimeRemaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SpellEmpowerStart : ServerPacket
|
||||||
|
{
|
||||||
|
public ObjectGuid CastID;
|
||||||
|
public ObjectGuid CasterGUID;
|
||||||
|
public int SpellID;
|
||||||
|
public SpellCastVisual Visual;
|
||||||
|
public TimeSpan EmpowerDuration;
|
||||||
|
public TimeSpan MinHoldTime;
|
||||||
|
public TimeSpan HoldAtMaxTime;
|
||||||
|
public List<ObjectGuid> Targets = new();
|
||||||
|
public List<TimeSpan> StageDurations = new();
|
||||||
|
public SpellChannelStartInterruptImmunities? InterruptImmunities;
|
||||||
|
public SpellTargetedHealPrediction? HealPrediction;
|
||||||
|
|
||||||
|
public SpellEmpowerStart() : base(ServerOpcodes.SpellEmpowerStart) { }
|
||||||
|
|
||||||
|
public override void Write()
|
||||||
|
{
|
||||||
|
_worldPacket.WritePackedGuid(CastID);
|
||||||
|
_worldPacket.WritePackedGuid(CasterGUID);
|
||||||
|
_worldPacket.WriteInt32(Targets.Count);
|
||||||
|
_worldPacket.WriteInt32(SpellID);
|
||||||
|
Visual.Write(_worldPacket);
|
||||||
|
_worldPacket.WriteUInt32((uint)EmpowerDuration.TotalMilliseconds);
|
||||||
|
_worldPacket.WriteUInt32((uint)MinHoldTime.TotalMilliseconds);
|
||||||
|
_worldPacket.WriteUInt32((uint)HoldAtMaxTime.TotalMilliseconds);
|
||||||
|
_worldPacket.WriteInt32(StageDurations.Count);
|
||||||
|
|
||||||
|
foreach (var target in Targets)
|
||||||
|
_worldPacket.WritePackedGuid(target);
|
||||||
|
|
||||||
|
foreach (var stageDuration in StageDurations)
|
||||||
|
_worldPacket.WriteUInt32((uint)stageDuration.TotalMilliseconds);
|
||||||
|
|
||||||
|
_worldPacket.WriteBit(InterruptImmunities.HasValue);
|
||||||
|
_worldPacket.WriteBit(HealPrediction.HasValue);
|
||||||
|
_worldPacket.FlushBits();
|
||||||
|
|
||||||
|
if (InterruptImmunities.HasValue)
|
||||||
|
InterruptImmunities.Value.Write(_worldPacket);
|
||||||
|
|
||||||
|
if (HealPrediction.HasValue)
|
||||||
|
HealPrediction.Value.Write(_worldPacket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SpellEmpowerUpdate : ServerPacket
|
||||||
|
{
|
||||||
|
public ObjectGuid CastID;
|
||||||
|
public ObjectGuid CasterGUID;
|
||||||
|
public TimeSpan TimeRemaining;
|
||||||
|
public List<TimeSpan> StageDurations = new();
|
||||||
|
public byte Status;
|
||||||
|
|
||||||
|
public SpellEmpowerUpdate() : base(ServerOpcodes.SpellEmpowerUpdate) { }
|
||||||
|
|
||||||
|
public override void Write()
|
||||||
|
{
|
||||||
|
_worldPacket.WritePackedGuid(CastID);
|
||||||
|
_worldPacket.WritePackedGuid(CasterGUID);
|
||||||
|
_worldPacket.WriteUInt32((uint)TimeRemaining.TotalMilliseconds);
|
||||||
|
_worldPacket.WriteInt32(StageDurations.Count);
|
||||||
|
_worldPacket.WriteUInt8(Status);
|
||||||
|
_worldPacket.FlushBits();
|
||||||
|
|
||||||
|
foreach (var stageDuration in StageDurations)
|
||||||
|
_worldPacket.WriteUInt32((uint)stageDuration.TotalMilliseconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SetEmpowerMinHoldStagePercent : ClientPacket
|
||||||
|
{
|
||||||
|
public float MinHoldStagePercent = 1.0f;
|
||||||
|
|
||||||
|
public SetEmpowerMinHoldStagePercent(WorldPacket packet) : base(packet) { }
|
||||||
|
|
||||||
|
public override void Read()
|
||||||
|
{
|
||||||
|
MinHoldStagePercent = _worldPacket.ReadFloat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SpellEmpowerRelease : ClientPacket
|
||||||
|
{
|
||||||
|
public int SpellID;
|
||||||
|
|
||||||
|
public SpellEmpowerRelease(WorldPacket packet) : base(packet) { }
|
||||||
|
|
||||||
|
public override void Read()
|
||||||
|
{
|
||||||
|
SpellID = _worldPacket.ReadInt32();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SpellEmpowerRestart : ClientPacket
|
||||||
|
{
|
||||||
|
public int SpellID;
|
||||||
|
|
||||||
|
public SpellEmpowerRestart(WorldPacket packet) : base(packet) { }
|
||||||
|
|
||||||
|
public override void Read()
|
||||||
|
{
|
||||||
|
SpellID = _worldPacket.ReadInt32();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SpellEmpowerSetStage : ServerPacket
|
||||||
|
{
|
||||||
|
public ObjectGuid CastID;
|
||||||
|
public ObjectGuid CasterGUID;
|
||||||
|
public int Stage;
|
||||||
|
|
||||||
|
public SpellEmpowerSetStage() : base(ServerOpcodes.SpellEmpowerSetStage) { }
|
||||||
|
|
||||||
|
public override void Write()
|
||||||
|
{
|
||||||
|
_worldPacket.WritePackedGuid(CastID);
|
||||||
|
_worldPacket.WritePackedGuid(CasterGUID);
|
||||||
|
_worldPacket.WriteInt32(Stage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ResurrectRequest : ServerPacket
|
class ResurrectRequest : ServerPacket
|
||||||
{
|
{
|
||||||
public ResurrectRequest() : base(ServerOpcodes.ResurrectRequest) { }
|
public ResurrectRequest() : base(ServerOpcodes.ResurrectRequest) { }
|
||||||
|
|||||||
@@ -174,6 +174,7 @@ namespace Game.Scripting
|
|||||||
public delegate void SpellObjectAreaTargetSelectFnType(List<WorldObject> targets);
|
public delegate void SpellObjectAreaTargetSelectFnType(List<WorldObject> targets);
|
||||||
public delegate void SpellObjectTargetSelectFnType(ref WorldObject targets);
|
public delegate void SpellObjectTargetSelectFnType(ref WorldObject targets);
|
||||||
public delegate void SpellDestinationTargetSelectFnType(ref SpellDestination dest);
|
public delegate void SpellDestinationTargetSelectFnType(ref SpellDestination dest);
|
||||||
|
public delegate void SpellEmpowerStageFnType(int completedStagesCount);
|
||||||
|
|
||||||
public class CastHandler
|
public class CastHandler
|
||||||
{
|
{
|
||||||
@@ -232,6 +233,21 @@ namespace Game.Scripting
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class EmpowerStageCompletedHandler
|
||||||
|
{
|
||||||
|
SpellEmpowerStageFnType _callImpl;
|
||||||
|
|
||||||
|
public EmpowerStageCompletedHandler(SpellEmpowerStageFnType handler)
|
||||||
|
{
|
||||||
|
_callImpl = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Call(int completedStagesCount)
|
||||||
|
{
|
||||||
|
_callImpl(completedStagesCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class EffectHandler : EffectHook
|
public class EffectHandler : EffectHook
|
||||||
{
|
{
|
||||||
SpellEffectName _effName;
|
SpellEffectName _effName;
|
||||||
@@ -572,6 +588,14 @@ namespace Game.Scripting
|
|||||||
// where function is void function(DamageInfo damageInfo, ref uint resistAmount, ref int absorbAmount)
|
// where function is void function(DamageInfo damageInfo, ref uint resistAmount, ref int absorbAmount)
|
||||||
public List<OnCalculateResistAbsorbHandler> OnCalculateResistAbsorb = new();
|
public List<OnCalculateResistAbsorbHandler> OnCalculateResistAbsorb = new();
|
||||||
|
|
||||||
|
// example: OnEmpowerStageCompleted += SpellOnEmpowerStageCompletedFn(class::function);
|
||||||
|
// where function is void function(int32 completedStages)
|
||||||
|
public List<EmpowerStageCompletedHandler> OnEmpowerStageCompleted = new();
|
||||||
|
|
||||||
|
// example: OnEmpowerCompleted += SpellOnEmpowerCompletedFn(class::function);
|
||||||
|
// where function is void function(int32 completedStages)
|
||||||
|
public List<EmpowerStageCompletedHandler> OnEmpowerCompleted = new();
|
||||||
|
|
||||||
// where function is void function(uint effIndex)
|
// where function is void function(uint effIndex)
|
||||||
public List<EffectHandler> OnEffectLaunch = new();
|
public List<EffectHandler> OnEffectLaunch = new();
|
||||||
public List<EffectHandler> OnEffectLaunchTarget = new();
|
public List<EffectHandler> OnEffectLaunchTarget = new();
|
||||||
@@ -621,6 +645,8 @@ namespace Game.Scripting
|
|||||||
// 14. OnEffectHitTarget - executed just before specified effect handler call - called for each target from spell target map
|
// 14. OnEffectHitTarget - executed just before specified effect handler call - called for each target from spell target map
|
||||||
// 15. OnHit - executed just before spell deals damage and procs auras - when spell hits target - called for each target from spell target map
|
// 15. OnHit - executed just before spell deals damage and procs auras - when spell hits target - called for each target from spell target map
|
||||||
// 16. AfterHit - executed just after spell finishes all it's jobs for target - called for each target from spell target map
|
// 16. AfterHit - executed just after spell finishes all it's jobs for target - called for each target from spell target map
|
||||||
|
// 17. OnEmpowerStageCompleted - executed when empowered spell completes each stage
|
||||||
|
// 18. OnEmpowerCompleted - executed when empowered spell is released
|
||||||
|
|
||||||
//
|
//
|
||||||
// methods allowing interaction with Spell object
|
// methods allowing interaction with Spell object
|
||||||
|
|||||||
@@ -764,9 +764,15 @@ namespace Game.Spells
|
|||||||
maxDuration = -1;
|
maxDuration = -1;
|
||||||
|
|
||||||
// IsPermanent() checks max duration (which we are supposed to calculate here)
|
// IsPermanent() checks max duration (which we are supposed to calculate here)
|
||||||
if (maxDuration != -1 && modOwner != null)
|
if (maxDuration != -1)
|
||||||
modOwner.ApplySpellMod(spellInfo, SpellModOp.Duration, ref maxDuration);
|
{
|
||||||
|
|
||||||
|
if (modOwner != null)
|
||||||
|
modOwner.ApplySpellMod(spellInfo, SpellModOp.Duration, ref maxDuration);
|
||||||
|
|
||||||
|
if (spellInfo.IsEmpowerSpell())
|
||||||
|
maxDuration += (int)SpellConst.EmpowerHoldTimeAtMax;
|
||||||
|
}
|
||||||
return maxDuration;
|
return maxDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+207
-35
@@ -91,14 +91,15 @@ namespace Game.Spells
|
|||||||
//Auto Shot & Shoot (wand)
|
//Auto Shot & Shoot (wand)
|
||||||
m_autoRepeat = m_spellInfo.IsAutoRepeatRangedSpell();
|
m_autoRepeat = m_spellInfo.IsAutoRepeatRangedSpell();
|
||||||
|
|
||||||
|
if (m_spellInfo.IsEmpowerSpell())
|
||||||
|
m_empower = new EmpowerData();
|
||||||
|
|
||||||
// Determine if spell can be reflected back to the caster
|
// Determine if spell can be reflected back to the caster
|
||||||
// Patch 1.2 notes: Spell Reflection no longer reflects abilities
|
// Patch 1.2 notes: Spell Reflection no longer reflects abilities
|
||||||
m_canReflect = caster.IsUnit() && ((m_spellInfo.DmgClass == SpellDmgClass.Magic && !m_spellInfo.HasAttribute(SpellAttr0.IsAbility)) || m_spellInfo.HasAttribute(SpellAttr7.AllowSpellReflection))
|
m_canReflect = caster.IsUnit() && ((m_spellInfo.DmgClass == SpellDmgClass.Magic && !m_spellInfo.HasAttribute(SpellAttr0.IsAbility)) || m_spellInfo.HasAttribute(SpellAttr7.AllowSpellReflection))
|
||||||
&& !m_spellInfo.HasAttribute(SpellAttr1.NoReflection) && !m_spellInfo.HasAttribute(SpellAttr0.NoImmunities)
|
&& !m_spellInfo.HasAttribute(SpellAttr1.NoReflection) && !m_spellInfo.HasAttribute(SpellAttr0.NoImmunities)
|
||||||
&& !m_spellInfo.IsPassive();
|
&& !m_spellInfo.IsPassive();
|
||||||
|
|
||||||
CleanupTargetList();
|
|
||||||
|
|
||||||
for (var i = 0; i < SpellConst.MaxEffects; ++i)
|
for (var i = 0; i < SpellConst.MaxEffects; ++i)
|
||||||
m_destTargets[i] = new SpellDestination(m_caster);
|
m_destTargets[i] = new SpellDestination(m_caster);
|
||||||
}
|
}
|
||||||
@@ -1046,7 +1047,7 @@ namespace Game.Spells
|
|||||||
if (liquidLevel <= ground) // When there is no liquid Map.GetWaterOrGroundLevel returns ground level
|
if (liquidLevel <= ground) // When there is no liquid Map.GetWaterOrGroundLevel returns ground level
|
||||||
{
|
{
|
||||||
SendCastResult(SpellCastResult.NotHere);
|
SendCastResult(SpellCastResult.NotHere);
|
||||||
SendChannelUpdate(0);
|
SendChannelUpdate(0, SpellCastResult.NotHere);
|
||||||
Finish(SpellCastResult.NotHere);
|
Finish(SpellCastResult.NotHere);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1054,7 +1055,7 @@ namespace Game.Spells
|
|||||||
if (ground + 0.75 > liquidLevel)
|
if (ground + 0.75 > liquidLevel)
|
||||||
{
|
{
|
||||||
SendCastResult(SpellCastResult.TooShallow);
|
SendCastResult(SpellCastResult.TooShallow);
|
||||||
SendChannelUpdate(0);
|
SendChannelUpdate(0, SpellCastResult.TooShallow);
|
||||||
Finish(SpellCastResult.TooShallow);
|
Finish(SpellCastResult.TooShallow);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2609,7 +2610,7 @@ namespace Game.Spells
|
|||||||
// a possible alternative sollution for those would be validating aura target on unit state change
|
// a possible alternative sollution for those would be validating aura target on unit state change
|
||||||
if (triggeredByAura != null && triggeredByAura.IsPeriodic() && !triggeredByAura.GetBase().IsPassive())
|
if (triggeredByAura != null && triggeredByAura.IsPeriodic() && !triggeredByAura.GetBase().IsPassive())
|
||||||
{
|
{
|
||||||
SendChannelUpdate(0);
|
SendChannelUpdate(0, result);
|
||||||
triggeredByAura.GetBase().SetDuration(0);
|
triggeredByAura.GetBase().SetDuration(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2741,7 +2742,7 @@ namespace Game.Spells
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SendChannelUpdate(0);
|
SendChannelUpdate(0, SpellCastResult.Interrupted);
|
||||||
SendInterrupted(0);
|
SendInterrupted(0);
|
||||||
SendCastResult(SpellCastResult.Interrupted);
|
SendCastResult(SpellCastResult.Interrupted);
|
||||||
|
|
||||||
@@ -3141,10 +3142,12 @@ namespace Game.Spells
|
|||||||
if (m_spellInfo.IsChanneled())
|
if (m_spellInfo.IsChanneled())
|
||||||
{
|
{
|
||||||
int duration = m_spellInfo.GetDuration();
|
int duration = m_spellInfo.GetDuration();
|
||||||
if (duration > 0 || m_spellValue.Duration.HasValue)
|
if (duration > 0 || m_spellValue.Duration > 0)
|
||||||
{
|
{
|
||||||
if (!m_spellValue.Duration.HasValue)
|
if (!m_spellValue.Duration.HasValue)
|
||||||
{
|
{
|
||||||
|
int originalDuration = duration;
|
||||||
|
|
||||||
// First mod_duration then haste - see Missile Barrage
|
// First mod_duration then haste - see Missile Barrage
|
||||||
// Apply duration mod
|
// Apply duration mod
|
||||||
Player modOwner = m_caster.GetSpellModOwner();
|
Player modOwner = m_caster.GetSpellModOwner();
|
||||||
@@ -3155,6 +3158,27 @@ namespace Game.Spells
|
|||||||
|
|
||||||
// Apply haste mods
|
// Apply haste mods
|
||||||
m_caster.ModSpellDurationTime(m_spellInfo, ref duration, this);
|
m_caster.ModSpellDurationTime(m_spellInfo, ref duration, this);
|
||||||
|
|
||||||
|
if (IsEmpowerSpell())
|
||||||
|
{
|
||||||
|
float ratio = (float)duration / (float)originalDuration;
|
||||||
|
TimeSpan totalExceptLastStage = TimeSpan.Zero;
|
||||||
|
for (int i = 0; i < m_spellInfo.EmpowerStageThresholds.Count - 1; ++i)
|
||||||
|
{
|
||||||
|
m_empower.StageDurations[i] = TimeSpan.FromMilliseconds((long)(m_spellInfo.EmpowerStageThresholds[i].TotalMilliseconds * ratio));
|
||||||
|
totalExceptLastStage += m_empower.StageDurations[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
m_empower.StageDurations[^1] = TimeSpan.FromMilliseconds(duration) - totalExceptLastStage;
|
||||||
|
|
||||||
|
Player playerCaster = m_caster.ToPlayer();
|
||||||
|
if (playerCaster != null)
|
||||||
|
m_empower.MinHoldTime = TimeSpan.FromMilliseconds((long)(m_empower.StageDurations[0].TotalMilliseconds * playerCaster.GetEmpowerMinHoldStagePercent()));
|
||||||
|
else
|
||||||
|
m_empower.MinHoldTime = m_empower.StageDurations[0];
|
||||||
|
|
||||||
|
duration += (int)SpellConst.EmpowerHoldTimeAtMax;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
duration = m_spellValue.Duration.Value;
|
duration = m_spellValue.Duration.Value;
|
||||||
@@ -3452,9 +3476,47 @@ namespace Game.Spells
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsEmpowerSpell())
|
||||||
|
{
|
||||||
|
int getCompletedEmpowerStages()
|
||||||
|
{
|
||||||
|
TimeSpan passed = TimeSpan.FromMilliseconds(m_channeledDuration - m_timer);
|
||||||
|
for (int i = 0; i < m_empower.StageDurations.Count; ++i)
|
||||||
|
{
|
||||||
|
passed -= m_empower.StageDurations[i];
|
||||||
|
if (passed < TimeSpan.Zero)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_empower.StageDurations.Count;
|
||||||
|
};
|
||||||
|
|
||||||
|
int completedStages = getCompletedEmpowerStages();
|
||||||
|
if (completedStages != m_empower.CompletedStages)
|
||||||
|
{
|
||||||
|
SpellEmpowerSetStage empowerSetStage = new();
|
||||||
|
empowerSetStage.CastID = m_castId;
|
||||||
|
empowerSetStage.CasterGUID = m_caster.GetGUID();
|
||||||
|
empowerSetStage.Stage = m_empower.CompletedStages;
|
||||||
|
m_caster.SendMessageToSet(empowerSetStage, true);
|
||||||
|
|
||||||
|
m_empower.CompletedStages = completedStages;
|
||||||
|
m_caster.ToUnit().SetSpellEmpowerStage((sbyte)completedStages);
|
||||||
|
|
||||||
|
CallScriptEmpowerStageCompletedHandlers(completedStages);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CanReleaseEmpowerSpell())
|
||||||
|
{
|
||||||
|
m_empower.IsReleased = true;
|
||||||
|
m_timer = 0;
|
||||||
|
CallScriptEmpowerCompletedHandlers(m_empower.CompletedStages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (m_timer == 0)
|
if (m_timer == 0)
|
||||||
{
|
{
|
||||||
SendChannelUpdate(0);
|
SendChannelUpdate(0, SpellCastResult.SpellCastOk);
|
||||||
Finish();
|
Finish();
|
||||||
|
|
||||||
// We call the hook here instead of in Spell::finish because we only want to call it for completed channeling. Everything else is handled by interrupts
|
// We call the hook here instead of in Spell::finish because we only want to call it for completed channeling. Everything else is handled by interrupts
|
||||||
@@ -3520,6 +3582,9 @@ namespace Game.Spells
|
|||||||
if (m_customArg is TraitConfig)
|
if (m_customArg is TraitConfig)
|
||||||
m_caster.ToPlayer().SendPacket(new TraitConfigCommitFailed((m_customArg as TraitConfig).ID));
|
m_caster.ToPlayer().SendPacket(new TraitConfigCommitFailed((m_customArg as TraitConfig).ID));
|
||||||
|
|
||||||
|
if (IsEmpowerSpell())
|
||||||
|
unitCaster.GetSpellHistory().ResetCooldown(m_spellInfo.Id, true);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3549,6 +3614,14 @@ namespace Game.Spells
|
|||||||
// Stop Attack for some spells
|
// Stop Attack for some spells
|
||||||
if (m_spellInfo.HasAttribute(SpellAttr0.CancelsAutoAttackCombat))
|
if (m_spellInfo.HasAttribute(SpellAttr0.CancelsAutoAttackCombat))
|
||||||
unitCaster.AttackStop();
|
unitCaster.AttackStop();
|
||||||
|
|
||||||
|
if (IsEmpowerSpell())
|
||||||
|
{
|
||||||
|
// Empower spells trigger gcd at the end of cast instead of at start
|
||||||
|
SpellInfo gcd = Global.SpellMgr.GetSpellInfo(SpellConst.EmpowerHardcodedGCD, Difficulty.None);
|
||||||
|
if (gcd != null)
|
||||||
|
unitCaster.GetSpellHistory().AddGlobalCooldown(gcd, TimeSpan.FromMilliseconds(gcd.StartRecoveryTime));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FillSpellCastFailedArgs<T>(T packet, ObjectGuid castId, SpellInfo spellInfo, SpellCastResult result, SpellCustomErrors customError, int? param1, int? param2, Player caster) where T : CastFailedBase
|
static void FillSpellCastFailedArgs<T>(T packet, ObjectGuid castId, SpellInfo spellInfo, SpellCastResult result, SpellCustomErrors customError, int? param1, int? param2, Player caster) where T : CastFailedBase
|
||||||
@@ -4354,7 +4427,7 @@ namespace Game.Spells
|
|||||||
m_caster.SendMessageToSet(failedPacket, true);
|
m_caster.SendMessageToSet(failedPacket, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendChannelUpdate(uint time)
|
public void SendChannelUpdate(uint time, SpellCastResult? result = null)
|
||||||
{
|
{
|
||||||
// GameObjects don't channel
|
// GameObjects don't channel
|
||||||
Unit unitCaster = m_caster.ToUnit();
|
Unit unitCaster = m_caster.ToUnit();
|
||||||
@@ -4366,12 +4439,31 @@ namespace Game.Spells
|
|||||||
unitCaster.ClearChannelObjects();
|
unitCaster.ClearChannelObjects();
|
||||||
unitCaster.SetChannelSpellId(0);
|
unitCaster.SetChannelSpellId(0);
|
||||||
unitCaster.SetChannelVisual(new SpellCastVisualField());
|
unitCaster.SetChannelVisual(new SpellCastVisualField());
|
||||||
|
unitCaster.SetSpellEmpowerStage(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
SpellChannelUpdate spellChannelUpdate = new();
|
if (IsEmpowerSpell())
|
||||||
spellChannelUpdate.CasterGUID = unitCaster.GetGUID();
|
{
|
||||||
spellChannelUpdate.TimeRemaining = (int)time;
|
SpellEmpowerUpdate spellEmpowerUpdate = new();
|
||||||
unitCaster.SendMessageToSet(spellChannelUpdate, true);
|
spellEmpowerUpdate.CastID = m_castId;
|
||||||
|
spellEmpowerUpdate.CasterGUID = unitCaster.GetGUID();
|
||||||
|
spellEmpowerUpdate.TimeRemaining = TimeSpan.FromMilliseconds(time);
|
||||||
|
if (time > 0)
|
||||||
|
spellEmpowerUpdate.StageDurations.AddRange(m_empower.StageDurations);
|
||||||
|
else if (result.HasValue && result != SpellCastResult.SpellCastOk)
|
||||||
|
spellEmpowerUpdate.Status = 1;
|
||||||
|
else
|
||||||
|
spellEmpowerUpdate.Status = 4;
|
||||||
|
|
||||||
|
unitCaster.SendMessageToSet(spellEmpowerUpdate, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SpellChannelUpdate spellChannelUpdate = new();
|
||||||
|
spellChannelUpdate.CasterGUID = unitCaster.GetGUID();
|
||||||
|
spellChannelUpdate.TimeRemaining = (int)time;
|
||||||
|
unitCaster.SendMessageToSet(spellChannelUpdate, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendChannelStart(uint duration)
|
void SendChannelStart(uint duration)
|
||||||
@@ -4427,36 +4519,62 @@ namespace Game.Spells
|
|||||||
unitCaster.SetChannelSpellId(m_spellInfo.Id);
|
unitCaster.SetChannelSpellId(m_spellInfo.Id);
|
||||||
unitCaster.SetChannelVisual(m_SpellVisual);
|
unitCaster.SetChannelVisual(m_SpellVisual);
|
||||||
|
|
||||||
SpellChannelStart spellChannelStart = new();
|
void setImmunitiesAndHealPrediction(ref SpellChannelStartInterruptImmunities? interruptImmunities, ref SpellTargetedHealPrediction? healPrediction)
|
||||||
spellChannelStart.CasterGUID = unitCaster.GetGUID();
|
|
||||||
spellChannelStart.SpellID = (int)m_spellInfo.Id;
|
|
||||||
spellChannelStart.Visual = m_SpellVisual;
|
|
||||||
spellChannelStart.ChannelDuration = duration;
|
|
||||||
|
|
||||||
uint schoolImmunityMask = unitCaster.GetSchoolImmunityMask();
|
|
||||||
ulong mechanicImmunityMask = unitCaster.GetMechanicImmunityMask();
|
|
||||||
|
|
||||||
if (schoolImmunityMask != 0 || mechanicImmunityMask != 0)
|
|
||||||
{
|
{
|
||||||
SpellChannelStartInterruptImmunities interruptImmunities = new();
|
uint schoolImmunityMask = unitCaster.GetSchoolImmunityMask();
|
||||||
interruptImmunities.SchoolImmunities = (int)schoolImmunityMask;
|
ulong mechanicImmunityMask = unitCaster.GetMechanicImmunityMask();
|
||||||
interruptImmunities.Immunities = (int)mechanicImmunityMask;
|
|
||||||
|
|
||||||
spellChannelStart.InterruptImmunities = interruptImmunities;
|
if (schoolImmunityMask != 0 || mechanicImmunityMask != 0)
|
||||||
}
|
{
|
||||||
|
SpellChannelStartInterruptImmunities immunities = new();
|
||||||
|
immunities.SchoolImmunities = (int)schoolImmunityMask;
|
||||||
|
immunities.Immunities = (int)mechanicImmunityMask;
|
||||||
|
|
||||||
if (m_spellInfo.HasAttribute(SpellAttr8.HealPrediction) && m_caster.IsUnit())
|
interruptImmunities = immunities;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_spellInfo.HasAttribute(SpellAttr8.HealPrediction) && m_caster.IsUnit())
|
||||||
|
{
|
||||||
|
|
||||||
|
SpellTargetedHealPrediction prediction = new();
|
||||||
|
if (unitCaster.m_unitData.ChannelObjects.Size() == 1 && unitCaster.m_unitData.ChannelObjects[0].IsUnit())
|
||||||
|
prediction.TargetGUID = unitCaster.m_unitData.ChannelObjects[0];
|
||||||
|
|
||||||
|
UpdateSpellHealPrediction(prediction.Predict, true);
|
||||||
|
|
||||||
|
healPrediction = prediction;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (IsEmpowerSpell())
|
||||||
{
|
{
|
||||||
SpellTargetedHealPrediction healPrediction = new();
|
unitCaster.SetSpellEmpowerStage(0);
|
||||||
if (unitCaster.m_unitData.ChannelObjects.Size() == 1 && unitCaster.m_unitData.ChannelObjects[0].IsUnit())
|
|
||||||
healPrediction.TargetGUID = unitCaster.m_unitData.ChannelObjects[0];
|
|
||||||
|
|
||||||
UpdateSpellHealPrediction(healPrediction.Predict, true);
|
SpellEmpowerStart spellEmpowerStart = new();
|
||||||
|
spellEmpowerStart.CastID = m_castId;
|
||||||
|
spellEmpowerStart.CasterGUID = unitCaster.GetGUID();
|
||||||
|
spellEmpowerStart.SpellID = (int)m_spellInfo.Id;
|
||||||
|
spellEmpowerStart.Visual = m_SpellVisual;
|
||||||
|
spellEmpowerStart.EmpowerDuration = new TimeSpan(m_empower.StageDurations.Sum(r => r.Ticks));
|
||||||
|
spellEmpowerStart.MinHoldTime = m_empower.MinHoldTime;
|
||||||
|
spellEmpowerStart.HoldAtMaxTime = TimeSpan.FromMilliseconds(SpellConst.EmpowerHoldTimeAtMax);
|
||||||
|
spellEmpowerStart.Targets.AddRange(unitCaster.m_unitData.ChannelObjects._values);
|
||||||
|
spellEmpowerStart.StageDurations.AddRange(m_empower.StageDurations);
|
||||||
|
setImmunitiesAndHealPrediction(ref spellEmpowerStart.InterruptImmunities, ref spellEmpowerStart.HealPrediction);
|
||||||
|
|
||||||
spellChannelStart.HealPrediction = healPrediction;
|
unitCaster.SendMessageToSet(spellEmpowerStart, true);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SpellChannelStart spellChannelStart = new();
|
||||||
|
spellChannelStart.CasterGUID = unitCaster.GetGUID();
|
||||||
|
spellChannelStart.SpellID = (int)m_spellInfo.Id;
|
||||||
|
spellChannelStart.Visual = m_SpellVisual;
|
||||||
|
spellChannelStart.ChannelDuration = duration;
|
||||||
|
setImmunitiesAndHealPrediction(ref spellChannelStart.InterruptImmunities, ref spellChannelStart.HealPrediction);
|
||||||
|
|
||||||
unitCaster.SendMessageToSet(spellChannelStart, true);
|
unitCaster.SendMessageToSet(spellChannelStart, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendResurrectRequest(Player target)
|
void SendResurrectRequest(Player target)
|
||||||
@@ -7332,6 +7450,25 @@ namespace Game.Spells
|
|||||||
return m_spellInfo.IsPositive() && (m_triggeredByAuraSpell == null || m_triggeredByAuraSpell.IsPositive());
|
return m_spellInfo.IsPositive() && (m_triggeredByAuraSpell == null || m_triggeredByAuraSpell.IsPositive());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsEmpowerSpell() { return m_empower != null; }
|
||||||
|
|
||||||
|
public void SetEmpowerReleasedByClient(bool release)
|
||||||
|
{
|
||||||
|
m_empower.IsReleasedByClient = release;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanReleaseEmpowerSpell()
|
||||||
|
{
|
||||||
|
if (m_empower.IsReleased)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!m_empower.IsReleasedByClient && m_timer != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
TimeSpan passedTime = TimeSpan.FromMilliseconds(m_channeledDuration - m_timer);
|
||||||
|
return passedTime >= m_empower.MinHoldTime;
|
||||||
|
}
|
||||||
|
|
||||||
bool IsNeedSendToClient()
|
bool IsNeedSendToClient()
|
||||||
{
|
{
|
||||||
return m_SpellVisual.SpellXSpellVisualID != 0 || m_SpellVisual.ScriptVisualID != 0 || m_spellInfo.IsChanneled() ||
|
return m_SpellVisual.SpellXSpellVisualID != 0 || m_SpellVisual.ScriptVisualID != 0 || m_spellInfo.IsChanneled() ||
|
||||||
@@ -7936,6 +8073,30 @@ namespace Game.Spells
|
|||||||
return source.IsWithinLOS(target.GetPositionX(), target.GetPositionY(), target.GetPositionZ(), LineOfSightChecks.All, ignoreFlags);
|
return source.IsWithinLOS(target.GetPositionX(), target.GetPositionY(), target.GetPositionZ(), LineOfSightChecks.All, ignoreFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CallScriptEmpowerStageCompletedHandlers(int completedStagesCount)
|
||||||
|
{
|
||||||
|
foreach (SpellScript script in m_loadedScripts)
|
||||||
|
{
|
||||||
|
script._PrepareScriptCall(SpellScriptHookType.EmpowerStageCompleted);
|
||||||
|
foreach (var empowerStageCompleted in script.OnEmpowerStageCompleted)
|
||||||
|
empowerStageCompleted.Call(completedStagesCount);
|
||||||
|
|
||||||
|
script._FinishScriptCall();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallScriptEmpowerCompletedHandlers(int completedStagesCount)
|
||||||
|
{
|
||||||
|
foreach (SpellScript script in m_loadedScripts)
|
||||||
|
{
|
||||||
|
script._PrepareScriptCall(SpellScriptHookType.EmpowerCompleted);
|
||||||
|
foreach (var empowerStageCompleted in script.OnEmpowerCompleted)
|
||||||
|
empowerStageCompleted.Call(completedStagesCount);
|
||||||
|
|
||||||
|
script._FinishScriptCall();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool CheckScriptEffectImplicitTargets(uint effIndex, uint effIndexToCheck)
|
bool CheckScriptEffectImplicitTargets(uint effIndex, uint effIndexToCheck)
|
||||||
{
|
{
|
||||||
// Skip if there are not any script
|
// Skip if there are not any script
|
||||||
@@ -8288,6 +8449,8 @@ namespace Game.Spells
|
|||||||
byte m_runesState;
|
byte m_runesState;
|
||||||
byte m_delayAtDamageCount;
|
byte m_delayAtDamageCount;
|
||||||
|
|
||||||
|
EmpowerData m_empower;
|
||||||
|
|
||||||
// Delayed spells system
|
// Delayed spells system
|
||||||
ulong m_delayStart; // time of spell delay start, filled by event handler, zero = just started
|
ulong m_delayStart; // time of spell delay start, filled by event handler, zero = just started
|
||||||
ulong m_delayMoment; // moment of next delay call, used internally
|
ulong m_delayMoment; // moment of next delay call, used internally
|
||||||
@@ -9724,4 +9887,13 @@ namespace Game.Spells
|
|||||||
return (_storage[1] & (int)procFlags) != 0;
|
return (_storage[1] & (int)procFlags) != 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class EmpowerData
|
||||||
|
{
|
||||||
|
public TimeSpan MinHoldTime;
|
||||||
|
public List<TimeSpan> StageDurations = new();
|
||||||
|
public int CompletedStages = 0;
|
||||||
|
public bool IsReleasedByClient;
|
||||||
|
public bool IsReleased;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -158,6 +158,11 @@ namespace Game.Spells
|
|||||||
EquippedItemClass = ItemClass.None;
|
EquippedItemClass = ItemClass.None;
|
||||||
EquippedItemSubClassMask = 0;
|
EquippedItemSubClassMask = 0;
|
||||||
EquippedItemInventoryTypeMask = 0;
|
EquippedItemInventoryTypeMask = 0;
|
||||||
|
|
||||||
|
// SpellEmpowerStageEntry
|
||||||
|
foreach (var stage in data.EmpowerStages)
|
||||||
|
EmpowerStageThresholds.Add(TimeSpan.FromMilliseconds(stage.DurationMs));
|
||||||
|
|
||||||
// SpellEquippedItemsEntry
|
// SpellEquippedItemsEntry
|
||||||
SpellEquippedItemsRecord _equipped = data.EquippedItems;
|
SpellEquippedItemsRecord _equipped = data.EquippedItems;
|
||||||
if (_equipped != null)
|
if (_equipped != null)
|
||||||
@@ -566,6 +571,11 @@ namespace Game.Spells
|
|||||||
return HasAttribute(SpellAttr2.AutoRepeat);
|
return HasAttribute(SpellAttr2.AutoRepeat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsEmpowerSpell()
|
||||||
|
{
|
||||||
|
return !EmpowerStageThresholds.Empty();
|
||||||
|
}
|
||||||
|
|
||||||
public bool HasInitialAggro()
|
public bool HasInitialAggro()
|
||||||
{
|
{
|
||||||
return !(HasAttribute(SpellAttr1.NoThreat) || HasAttribute(SpellAttr2.NoInitialThreat) || HasAttribute(SpellAttr4.NoHarmfulThreat));
|
return !(HasAttribute(SpellAttr1.NoThreat) || HasAttribute(SpellAttr2.NoInitialThreat) || HasAttribute(SpellAttr4.NoHarmfulThreat));
|
||||||
@@ -3940,6 +3950,7 @@ namespace Game.Spells
|
|||||||
public SpellSchoolMask SchoolMask { get; set; }
|
public SpellSchoolMask SchoolMask { get; set; }
|
||||||
public uint ChargeCategoryId;
|
public uint ChargeCategoryId;
|
||||||
public List<uint> Labels = new();
|
public List<uint> Labels = new();
|
||||||
|
public List<TimeSpan> EmpowerStageThresholds = new();
|
||||||
|
|
||||||
// SpellScalingEntry
|
// SpellScalingEntry
|
||||||
public ScalingInfo Scaling;
|
public ScalingInfo Scaling;
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
|
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
|
||||||
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
|
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
using Framework.Collections;
|
|
||||||
using Framework.Constants;
|
using Framework.Constants;
|
||||||
using Framework.Database;
|
using Framework.Database;
|
||||||
using Framework.Dynamic;
|
using Framework.Dynamic;
|
||||||
using Game.BattleFields;
|
using Game.BattleFields;
|
||||||
using Game.BattleGrounds;
|
|
||||||
using Game.BattlePets;
|
using Game.BattlePets;
|
||||||
using Game.DataStorage;
|
using Game.DataStorage;
|
||||||
using Game.Miscellaneous;
|
using Game.Miscellaneous;
|
||||||
@@ -2248,6 +2246,17 @@ namespace Game.Entities
|
|||||||
foreach (SpellCooldownsRecord cooldowns in CliDB.SpellCooldownsStorage.Values)
|
foreach (SpellCooldownsRecord cooldowns in CliDB.SpellCooldownsStorage.Values)
|
||||||
GetLoadHelper(cooldowns.SpellID, cooldowns.DifficultyID).Cooldowns = cooldowns;
|
GetLoadHelper(cooldowns.SpellID, cooldowns.DifficultyID).Cooldowns = cooldowns;
|
||||||
|
|
||||||
|
foreach (var (_, empowerStage) in CliDB.SpellEmpowerStageStorage)
|
||||||
|
{
|
||||||
|
SpellEmpowerRecord empower = CliDB.SpellEmpowerStorage.LookupByKey(empowerStage.SpellEmpowerID);
|
||||||
|
if (empower != null)
|
||||||
|
GetLoadHelper((uint)empower.SpellID, 0).EmpowerStages.Add(empowerStage);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var data in loadData)
|
||||||
|
data.Value.EmpowerStages.OrderBy(stageRecord => stageRecord.Stage);
|
||||||
|
|
||||||
foreach (SpellEquippedItemsRecord equippedItems in CliDB.SpellEquippedItemsStorage.Values)
|
foreach (SpellEquippedItemsRecord equippedItems in CliDB.SpellEquippedItemsStorage.Values)
|
||||||
GetLoadHelper(equippedItems.SpellID, 0).EquippedItems = equippedItems;
|
GetLoadHelper(equippedItems.SpellID, 0).EquippedItems = equippedItems;
|
||||||
|
|
||||||
@@ -2307,84 +2316,87 @@ namespace Game.Entities
|
|||||||
foreach (var data in loadData)
|
foreach (var data in loadData)
|
||||||
data.Value.Visuals.Sort((left, right) => { return right.CasterPlayerConditionID.CompareTo(left.CasterPlayerConditionID); });
|
data.Value.Visuals.Sort((left, right) => { return right.CasterPlayerConditionID.CompareTo(left.CasterPlayerConditionID); });
|
||||||
|
|
||||||
foreach (var data in loadData)
|
foreach (var (key, data) in loadData)
|
||||||
{
|
{
|
||||||
SpellNameRecord spellNameEntry = CliDB.SpellNameStorage.LookupByKey(data.Key.Id);
|
SpellNameRecord spellNameEntry = CliDB.SpellNameStorage.LookupByKey(key.Id);
|
||||||
if (spellNameEntry == null)
|
if (spellNameEntry == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// fill blanks
|
// fill blanks
|
||||||
DifficultyRecord difficultyEntry = CliDB.DifficultyStorage.LookupByKey(data.Key.difficulty);
|
DifficultyRecord difficultyEntry = CliDB.DifficultyStorage.LookupByKey(key.difficulty);
|
||||||
if (difficultyEntry != null)
|
if (difficultyEntry != null)
|
||||||
{
|
{
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
SpellInfoLoadHelper fallbackData = loadData.LookupByKey((data.Key.Id, (Difficulty)difficultyEntry.FallbackDifficultyID));
|
SpellInfoLoadHelper fallbackData = loadData.LookupByKey((key.Id, (Difficulty)difficultyEntry.FallbackDifficultyID));
|
||||||
if (fallbackData != null)
|
if (fallbackData != null)
|
||||||
{
|
{
|
||||||
if (data.Value.AuraOptions == null)
|
if (data.AuraOptions == null)
|
||||||
data.Value.AuraOptions = fallbackData.AuraOptions;
|
data.AuraOptions = fallbackData.AuraOptions;
|
||||||
|
|
||||||
if (data.Value.AuraRestrictions == null)
|
if (data.AuraRestrictions == null)
|
||||||
data.Value.AuraRestrictions = fallbackData.AuraRestrictions;
|
data.AuraRestrictions = fallbackData.AuraRestrictions;
|
||||||
|
|
||||||
if (data.Value.CastingRequirements == null)
|
if (data.CastingRequirements == null)
|
||||||
data.Value.CastingRequirements = fallbackData.CastingRequirements;
|
data.CastingRequirements = fallbackData.CastingRequirements;
|
||||||
|
|
||||||
if (data.Value.Categories == null)
|
if (data.Categories == null)
|
||||||
data.Value.Categories = fallbackData.Categories;
|
data.Categories = fallbackData.Categories;
|
||||||
|
|
||||||
if (data.Value.ClassOptions == null)
|
if (data.ClassOptions == null)
|
||||||
data.Value.ClassOptions = fallbackData.ClassOptions;
|
data.ClassOptions = fallbackData.ClassOptions;
|
||||||
|
|
||||||
if (data.Value.Cooldowns == null)
|
if (data.Cooldowns == null)
|
||||||
data.Value.Cooldowns = fallbackData.Cooldowns;
|
data.Cooldowns = fallbackData.Cooldowns;
|
||||||
|
|
||||||
for (var i = 0; i < data.Value.Effects.Length; ++i)
|
for (var i = 0; i < data.Effects.Length; ++i)
|
||||||
if (data.Value.Effects[i] == null)
|
if (data.Effects[i] == null)
|
||||||
data.Value.Effects[i] = fallbackData.Effects[i];
|
data.Effects[i] = fallbackData.Effects[i];
|
||||||
|
|
||||||
if (data.Value.EquippedItems == null)
|
if (data.EmpowerStages.Empty())
|
||||||
data.Value.EquippedItems = fallbackData.EquippedItems;
|
data.EmpowerStages = fallbackData.EmpowerStages;
|
||||||
|
|
||||||
if (data.Value.Interrupts == null)
|
if (data.EquippedItems == null)
|
||||||
data.Value.Interrupts = fallbackData.Interrupts;
|
data.EquippedItems = fallbackData.EquippedItems;
|
||||||
|
|
||||||
if (data.Value.Labels.Empty())
|
if (data.Interrupts == null)
|
||||||
data.Value.Labels = fallbackData.Labels;
|
data.Interrupts = fallbackData.Interrupts;
|
||||||
|
|
||||||
if (data.Value.Levels == null)
|
if (data.Labels.Empty())
|
||||||
data.Value.Levels = fallbackData.Levels;
|
data.Labels = fallbackData.Labels;
|
||||||
|
|
||||||
if (data.Value.Misc == null)
|
if (data.Levels == null)
|
||||||
data.Value.Misc = fallbackData.Misc;
|
data.Levels = fallbackData.Levels;
|
||||||
|
|
||||||
|
if (data.Misc == null)
|
||||||
|
data.Misc = fallbackData.Misc;
|
||||||
|
|
||||||
for (var i = 0; i < fallbackData.Powers.Length; ++i)
|
for (var i = 0; i < fallbackData.Powers.Length; ++i)
|
||||||
if (data.Value.Powers[i] == null)
|
if (data.Powers[i] == null)
|
||||||
data.Value.Powers[i] = fallbackData.Powers[i];
|
data.Powers[i] = fallbackData.Powers[i];
|
||||||
|
|
||||||
if (data.Value.Reagents == null)
|
if (data.Reagents == null)
|
||||||
data.Value.Reagents = fallbackData.Reagents;
|
data.Reagents = fallbackData.Reagents;
|
||||||
|
|
||||||
if (data.Value.ReagentsCurrency.Empty())
|
if (data.ReagentsCurrency.Empty())
|
||||||
data.Value.ReagentsCurrency = fallbackData.ReagentsCurrency;
|
data.ReagentsCurrency = fallbackData.ReagentsCurrency;
|
||||||
|
|
||||||
if (data.Value.Scaling == null)
|
if (data.Scaling == null)
|
||||||
data.Value.Scaling = fallbackData.Scaling;
|
data.Scaling = fallbackData.Scaling;
|
||||||
|
|
||||||
if (data.Value.Shapeshift == null)
|
if (data.Shapeshift == null)
|
||||||
data.Value.Shapeshift = fallbackData.Shapeshift;
|
data.Shapeshift = fallbackData.Shapeshift;
|
||||||
|
|
||||||
if (data.Value.TargetRestrictions == null)
|
if (data.TargetRestrictions == null)
|
||||||
data.Value.TargetRestrictions = fallbackData.TargetRestrictions;
|
data.TargetRestrictions = fallbackData.TargetRestrictions;
|
||||||
|
|
||||||
if (data.Value.Totems == null)
|
if (data.Totems == null)
|
||||||
data.Value.Totems = fallbackData.Totems;
|
data.Totems = fallbackData.Totems;
|
||||||
|
|
||||||
// visuals fall back only to first difficulty that defines any visual
|
// visuals fall back only to first difficulty that defines any visual
|
||||||
// they do not stack all difficulties in fallback chain
|
// they do not stack all difficulties in fallback chain
|
||||||
if (data.Value.Visuals.Empty())
|
if (data.Visuals.Empty())
|
||||||
data.Value.Visuals = fallbackData.Visuals;
|
data.Visuals = fallbackData.Visuals;
|
||||||
}
|
}
|
||||||
|
|
||||||
difficultyEntry = CliDB.DifficultyStorage.LookupByKey(difficultyEntry.FallbackDifficultyID);
|
difficultyEntry = CliDB.DifficultyStorage.LookupByKey(difficultyEntry.FallbackDifficultyID);
|
||||||
@@ -2395,7 +2407,7 @@ namespace Game.Entities
|
|||||||
//second key = id
|
//second key = id
|
||||||
|
|
||||||
|
|
||||||
mSpellInfoMap.Add(spellNameEntry.Id, new SpellInfo(spellNameEntry, data.Key.difficulty, data.Value));
|
mSpellInfoMap.Add(spellNameEntry.Id, new SpellInfo(spellNameEntry, key.difficulty, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.outInfo(LogFilter.ServerLoading, "Loaded SpellInfo store in {0} ms", Time.GetMSTimeDiffToNow(oldMSTime));
|
Log.outInfo(LogFilter.ServerLoading, "Loaded SpellInfo store in {0} ms", Time.GetMSTimeDiffToNow(oldMSTime));
|
||||||
@@ -4921,6 +4933,7 @@ namespace Game.Entities
|
|||||||
public SpellClassOptionsRecord ClassOptions;
|
public SpellClassOptionsRecord ClassOptions;
|
||||||
public SpellCooldownsRecord Cooldowns;
|
public SpellCooldownsRecord Cooldowns;
|
||||||
public SpellEffectRecord[] Effects = new SpellEffectRecord[SpellConst.MaxEffects];
|
public SpellEffectRecord[] Effects = new SpellEffectRecord[SpellConst.MaxEffects];
|
||||||
|
public List<SpellEmpowerStageRecord> EmpowerStages = new();
|
||||||
public SpellEquippedItemsRecord EquippedItems;
|
public SpellEquippedItemsRecord EquippedItems;
|
||||||
public SpellInterruptsRecord Interrupts;
|
public SpellInterruptsRecord Interrupts;
|
||||||
public List<SpellLabelRecord> Labels = new();
|
public List<SpellLabelRecord> Labels = new();
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ namespace Scripts.Spells.Evoker
|
|||||||
{
|
{
|
||||||
struct SpellIds
|
struct SpellIds
|
||||||
{
|
{
|
||||||
|
public const uint BlastFurnace = 375510;
|
||||||
public const uint EnergizingFlame = 400006;
|
public const uint EnergizingFlame = 400006;
|
||||||
|
public const uint FireBreathDamage = 357209;
|
||||||
public const uint GlideKnockback = 358736;
|
public const uint GlideKnockback = 358736;
|
||||||
public const uint Hover = 358267;
|
public const uint Hover = 358267;
|
||||||
public const uint LivingFlame = 361469;
|
public const uint LivingFlame = 361469;
|
||||||
@@ -22,6 +24,7 @@ namespace Scripts.Spells.Evoker
|
|||||||
public const uint LivingFlameHeal = 361509;
|
public const uint LivingFlameHeal = 361509;
|
||||||
public const uint PermeatingChillTalent = 370897;
|
public const uint PermeatingChillTalent = 370897;
|
||||||
public const uint PyreDamage = 357212;
|
public const uint PyreDamage = 357212;
|
||||||
|
public const uint ScouringFlame = 378438;
|
||||||
public const uint SoarRacial = 369536;
|
public const uint SoarRacial = 369536;
|
||||||
|
|
||||||
public const uint LabelEvokerBlue = 1465;
|
public const uint LabelEvokerBlue = 1465;
|
||||||
@@ -57,6 +60,68 @@ namespace Scripts.Spells.Evoker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 357208 Fire Breath (Red)
|
||||||
|
[Script] // 382266 Fire Breath (Red)
|
||||||
|
class spell_evo_fire_breath : SpellScript
|
||||||
|
{
|
||||||
|
public override bool Validate(SpellInfo spellInfo)
|
||||||
|
{
|
||||||
|
return ValidateSpellInfo(SpellIds.FireBreathDamage, SpellIds.BlastFurnace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnComplete(int completedStageCount)
|
||||||
|
{
|
||||||
|
int dotTicks = 10 - (completedStageCount - 1) * 3;
|
||||||
|
AuraEffect blastFurnace = GetCaster().GetAuraEffect(SpellIds.BlastFurnace, 0);
|
||||||
|
if (blastFurnace != null)
|
||||||
|
dotTicks += blastFurnace.GetAmount() / 2;
|
||||||
|
|
||||||
|
GetCaster().CastSpell(GetCaster(), SpellIds.FireBreathDamage, new CastSpellExtraArgs()
|
||||||
|
.SetTriggeringSpell(GetSpell())
|
||||||
|
.SetTriggerFlags(TriggerCastFlags.IgnoreCastInProgress | TriggerCastFlags.DontReportCastError)
|
||||||
|
.AddSpellMod(SpellValueMod.DurationPct, 100 * dotTicks)
|
||||||
|
.SetCustomArg(completedStageCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Register()
|
||||||
|
{
|
||||||
|
OnEmpowerCompleted.Add(new(OnComplete));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Script] // 357209 Fire Breath (Red)
|
||||||
|
class spell_evo_fire_breath_damage : SpellScript
|
||||||
|
{
|
||||||
|
public override bool Validate(SpellInfo spellInfo)
|
||||||
|
{
|
||||||
|
return ValidateSpellEffect((spellInfo.Id, 2))
|
||||||
|
&& spellInfo.GetEffect(2).IsAura(AuraType.ModSilence); // validate we are removing the correct effect
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddBonusUpfrontDamage(Unit victim, ref int damage, ref int flatMod, ref float pctMod)
|
||||||
|
{
|
||||||
|
int empowerLevel = (int)GetSpell().m_customArg;
|
||||||
|
if (empowerLevel == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// damage is done after aura is applied, grab periodic amount
|
||||||
|
AuraEffect fireBreath = victim.GetAuraEffect(GetSpellInfo().Id, 1, GetCaster().GetGUID());
|
||||||
|
if (fireBreath != null)
|
||||||
|
flatMod += (int)(fireBreath.GetEstimatedAmount().GetValueOrDefault(fireBreath.GetAmount()) * (empowerLevel - 1) * 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveUnusedEffect(List<WorldObject> targets)
|
||||||
|
{
|
||||||
|
targets.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Register()
|
||||||
|
{
|
||||||
|
CalcDamage.Add(new(AddBonusUpfrontDamage));
|
||||||
|
OnObjectAreaTargetSelect.Add(new(RemoveUnusedEffect, 2, Targets.UnitConeCasterToDestEnemy));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Script] // 358733 - Glide (Racial)
|
[Script] // 358733 - Glide (Racial)
|
||||||
class spell_evo_glide : SpellScript
|
class spell_evo_glide : SpellScript
|
||||||
{
|
{
|
||||||
@@ -182,4 +247,32 @@ namespace Scripts.Spells.Evoker
|
|||||||
OnEffectHitTarget.Add(new(HandleDamage, 0, SpellEffectName.Dummy));
|
OnEffectHitTarget.Add(new(HandleDamage, 0, SpellEffectName.Dummy));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Script] // 357209 Fire Breath (Red)
|
||||||
|
class spell_evo_scouring_flame : SpellScript
|
||||||
|
{
|
||||||
|
public override bool Validate(SpellInfo spellInfo)
|
||||||
|
{
|
||||||
|
return ValidateSpellInfo(SpellIds.ScouringFlame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleScouringFlame(List<WorldObject> targets)
|
||||||
|
{
|
||||||
|
if (!GetCaster().HasAura(SpellIds.ScouringFlame))
|
||||||
|
targets.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CalcDispelCount(uint effIndex)
|
||||||
|
{
|
||||||
|
int empowerLevel = (int)GetSpell().m_customArg;
|
||||||
|
if (empowerLevel != 0)
|
||||||
|
SetEffectValue(empowerLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Register()
|
||||||
|
{
|
||||||
|
OnObjectAreaTargetSelect.Add(new(HandleScouringFlame, 3, Targets.UnitConeCasterToDestEnemy));
|
||||||
|
OnEffectHitTarget.Add(new(CalcDispelCount, 3, SpellEffectName.Dispel));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user