mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-26 11:52:32 +01:00
Core/Spell: fix spell visual for other players
- Ported SMSG_SPELL_GO and SMSG_SPELL_START to new packet system
This commit is contained in:
@@ -20,5 +20,6 @@
|
||||
|
||||
#include "QueryPackets.h"
|
||||
#include "QuestPackets.h"
|
||||
#include "SpellPackets.h"
|
||||
|
||||
#endif // AllPackets_h__
|
||||
|
||||
160
src/server/game/Server/Packets/SpellPackets.cpp
Normal file
160
src/server/game/Server/Packets/SpellPackets.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
|
||||
*
|
||||
* 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 "SpellPackets.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "Spell.h"
|
||||
#include "SpellInfo.h"
|
||||
|
||||
ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::SpellMissStatus const& spellMissStatus)
|
||||
{
|
||||
data << uint64(spellMissStatus.TargetGUID);
|
||||
data << uint8(spellMissStatus.Reason);
|
||||
if (spellMissStatus.Reason == SPELL_MISS_REFLECT)
|
||||
data << uint8(spellMissStatus.ReflectStatus);
|
||||
return data;
|
||||
}
|
||||
|
||||
ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::TargetLocation const& targetLocation)
|
||||
{
|
||||
data << targetLocation.Transport.WriteAsPacked(); // relative position guid here - transport for example
|
||||
data << targetLocation.Location.PositionXYZStream();
|
||||
return data;
|
||||
}
|
||||
|
||||
ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::SpellTargetData const& spellTargetData)
|
||||
{
|
||||
data << uint32(spellTargetData.Flags);
|
||||
|
||||
if (spellTargetData.Unit)
|
||||
data << spellTargetData.Unit->WriteAsPacked();
|
||||
|
||||
if (spellTargetData.Item)
|
||||
data << spellTargetData.Item->WriteAsPacked();
|
||||
|
||||
if (spellTargetData.SrcLocation)
|
||||
data << *spellTargetData.SrcLocation;
|
||||
|
||||
if (spellTargetData.DstLocation)
|
||||
data << *spellTargetData.DstLocation;
|
||||
|
||||
if (spellTargetData.Name)
|
||||
data << *spellTargetData.Name;
|
||||
return data;
|
||||
}
|
||||
|
||||
ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::RuneData const& runeData)
|
||||
{
|
||||
data << uint8(runeData.Start);
|
||||
data << uint8(runeData.Count);
|
||||
for (uint8 cooldown : runeData.Cooldowns)
|
||||
data << uint8(cooldown);
|
||||
return data;
|
||||
}
|
||||
|
||||
ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::MissileTrajectoryResult const& traj)
|
||||
{
|
||||
data << float(traj.Pitch);
|
||||
data << uint32(traj.TravelTime);
|
||||
return data;
|
||||
}
|
||||
|
||||
ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::SpellAmmo const& spellAmmo)
|
||||
{
|
||||
data << uint32(spellAmmo.DisplayID);
|
||||
data << uint32(spellAmmo.InventoryType);
|
||||
return data;
|
||||
}
|
||||
|
||||
ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::CreatureImmunities const& immunities)
|
||||
{
|
||||
data << uint32(immunities.School);
|
||||
data << uint32(immunities.Value);
|
||||
return data;
|
||||
}
|
||||
|
||||
ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::SpellCastData const& spellCastData)
|
||||
{
|
||||
data << spellCastData.CasterGUID.WriteAsPacked();
|
||||
data << spellCastData.CasterUnit.WriteAsPacked();
|
||||
data << uint8(spellCastData.CastID); // pending spell cast?
|
||||
data << uint32(spellCastData.SpellID); // spellId
|
||||
data << uint32(spellCastData.CastFlags); // cast flags
|
||||
data << uint32(spellCastData.CastTime); // timestamp
|
||||
|
||||
if (spellCastData.HitTargets && spellCastData.MissStatus)
|
||||
{
|
||||
// Hit and miss target counts are both uint8, that limits us to 255 targets for each
|
||||
// sending more than 255 targets crashes the client (since count sent would be wrong)
|
||||
// Spells like 40647 (with a huge radius) can easily reach this limit (spell might need
|
||||
// target conditions but we still need to limit the number of targets sent and keeping
|
||||
// correct count for both hit and miss).
|
||||
static std::size_t const PACKET_TARGET_LIMIT = std::numeric_limits<uint8>::max();
|
||||
if (spellCastData.HitTargets->size() > PACKET_TARGET_LIMIT)
|
||||
spellCastData.HitTargets->resize(PACKET_TARGET_LIMIT);
|
||||
|
||||
data << uint8(spellCastData.HitTargets->size());
|
||||
for (ObjectGuid const& target : *spellCastData.HitTargets)
|
||||
data << uint64(target);
|
||||
|
||||
if (spellCastData.MissStatus->size() > PACKET_TARGET_LIMIT)
|
||||
spellCastData.MissStatus->resize(PACKET_TARGET_LIMIT);
|
||||
|
||||
data << uint8(spellCastData.MissStatus->size());
|
||||
for (WorldPackets::Spells::SpellMissStatus const& status : *spellCastData.MissStatus)
|
||||
data << status;
|
||||
}
|
||||
|
||||
data << spellCastData.Target;
|
||||
|
||||
if (spellCastData.RemainingPower)
|
||||
data << uint32(*spellCastData.RemainingPower);
|
||||
|
||||
if (spellCastData.RemainingRunes)
|
||||
data << *spellCastData.RemainingRunes;
|
||||
|
||||
if (spellCastData.MissileTrajectory)
|
||||
data << *spellCastData.MissileTrajectory;
|
||||
|
||||
if (spellCastData.Ammo)
|
||||
data << *spellCastData.Ammo;
|
||||
|
||||
if (spellCastData.Immunities)
|
||||
data << *spellCastData.Immunities;
|
||||
|
||||
if (spellCastData.CastFlags & CAST_FLAG_VISUAL_CHAIN)
|
||||
{
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
}
|
||||
|
||||
if (spellCastData.Target.Flags & TARGET_FLAG_DEST_LOCATION)
|
||||
data << uint8(0);
|
||||
return data;
|
||||
}
|
||||
|
||||
WorldPacket const* WorldPackets::Spells::SpellGo::Write()
|
||||
{
|
||||
_worldPacket << Cast;
|
||||
return &_worldPacket;
|
||||
}
|
||||
|
||||
WorldPacket const* WorldPackets::Spells::SpellStart::Write()
|
||||
{
|
||||
_worldPacket << Cast;
|
||||
return &_worldPacket;
|
||||
}
|
||||
124
src/server/game/Server/Packets/SpellPackets.h
Normal file
124
src/server/game/Server/Packets/SpellPackets.h
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Scripts for spells with SPELLFAMILY_GENERIC which cannot be included in AI script file
|
||||
* of creature using it or can't be bound to any player class.
|
||||
* Ordered alphabetically using scriptname.
|
||||
* Scriptnames of files in this file should be prefixed with "spell_gen_"
|
||||
*/
|
||||
|
||||
#ifndef SpellPackets_h__
|
||||
#define SpellPackets_h__
|
||||
|
||||
#include "Packet.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Optional.h"
|
||||
|
||||
namespace WorldPackets
|
||||
{
|
||||
namespace Spells
|
||||
{
|
||||
struct SpellMissStatus
|
||||
{
|
||||
ObjectGuid TargetGUID;
|
||||
uint8 Reason = 0;
|
||||
uint8 ReflectStatus = 0;
|
||||
};
|
||||
|
||||
struct RuneData
|
||||
{
|
||||
uint8 Start = 0;
|
||||
uint8 Count = 0;
|
||||
std::vector<uint8> Cooldowns;
|
||||
};
|
||||
|
||||
struct MissileTrajectoryResult
|
||||
{
|
||||
uint32 TravelTime = 0;
|
||||
float Pitch = 0.0f;
|
||||
};
|
||||
|
||||
struct SpellAmmo
|
||||
{
|
||||
uint32 DisplayID = 0;
|
||||
uint32 InventoryType = 0;
|
||||
};
|
||||
|
||||
struct CreatureImmunities
|
||||
{
|
||||
uint32 School = 0;
|
||||
uint32 Value = 0;
|
||||
};
|
||||
|
||||
struct TargetLocation
|
||||
{
|
||||
ObjectGuid Transport;
|
||||
Position Location;
|
||||
};
|
||||
|
||||
struct SpellTargetData
|
||||
{
|
||||
uint32 Flags = 0;
|
||||
Optional<ObjectGuid> Unit;
|
||||
Optional<ObjectGuid> Item;
|
||||
Optional<TargetLocation> SrcLocation;
|
||||
Optional<TargetLocation> DstLocation;
|
||||
Optional<std::string> Name;
|
||||
};
|
||||
|
||||
struct SpellCastData
|
||||
{
|
||||
ObjectGuid CasterGUID;
|
||||
ObjectGuid CasterUnit;
|
||||
uint8 CastID = 0;
|
||||
uint32 SpellID = 0;
|
||||
uint32 CastFlags = 0;
|
||||
uint32 CastTime = 0;
|
||||
mutable Optional<std::vector<ObjectGuid>> HitTargets;
|
||||
mutable Optional<std::vector<SpellMissStatus>> MissStatus;
|
||||
SpellTargetData Target;
|
||||
Optional<uint32> RemainingPower;
|
||||
Optional<RuneData> RemainingRunes;
|
||||
Optional<MissileTrajectoryResult> MissileTrajectory;
|
||||
Optional<SpellAmmo> Ammo;
|
||||
Optional<CreatureImmunities> Immunities;
|
||||
};
|
||||
|
||||
class SpellGo final : public ServerPacket
|
||||
{
|
||||
public:
|
||||
SpellGo() : ServerPacket(SMSG_SPELL_GO) { }
|
||||
|
||||
WorldPacket const* Write() override;
|
||||
|
||||
SpellCastData Cast;
|
||||
};
|
||||
|
||||
class SpellStart final : public ServerPacket
|
||||
{
|
||||
public:
|
||||
SpellStart() : ServerPacket(SMSG_SPELL_START) { }
|
||||
|
||||
WorldPacket const* Write() override;
|
||||
|
||||
SpellCastData Cast;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SpellPackets_h__
|
||||
@@ -47,6 +47,7 @@
|
||||
#include "SpellHistory.h"
|
||||
#include "SpellInfo.h"
|
||||
#include "SpellMgr.h"
|
||||
#include "SpellPackets.h"
|
||||
#include "SpellScript.h"
|
||||
#include "TemporarySummon.h"
|
||||
#include "TradeData.h"
|
||||
@@ -175,41 +176,42 @@ void SpellCastTargets::Read(ByteBuffer& data, Unit* caster)
|
||||
Update(caster);
|
||||
}
|
||||
|
||||
void SpellCastTargets::Write(ByteBuffer& data)
|
||||
void SpellCastTargets::Write(WorldPackets::Spells::SpellTargetData& data)
|
||||
{
|
||||
data << uint32(m_targetMask);
|
||||
data.Flags = m_targetMask;
|
||||
|
||||
if (m_targetMask & (TARGET_FLAG_UNIT | TARGET_FLAG_CORPSE_ALLY | TARGET_FLAG_GAMEOBJECT | TARGET_FLAG_CORPSE_ENEMY | TARGET_FLAG_UNIT_MINIPET))
|
||||
data << m_objectTargetGUID.WriteAsPacked();
|
||||
data.Unit = m_objectTargetGUID;
|
||||
|
||||
if (m_targetMask & (TARGET_FLAG_ITEM | TARGET_FLAG_TRADE_ITEM))
|
||||
{
|
||||
data.Item = boost::in_place();
|
||||
if (m_itemTarget)
|
||||
data << m_itemTarget->GetPackGUID();
|
||||
else
|
||||
data << uint8(0);
|
||||
data.Item = m_itemTarget->GetGUID();
|
||||
}
|
||||
|
||||
if (m_targetMask & TARGET_FLAG_SOURCE_LOCATION)
|
||||
{
|
||||
data << m_src._transportGUID.WriteAsPacked(); // relative position guid here - transport for example
|
||||
if (m_src._transportGUID)
|
||||
data << m_src._transportOffset.PositionXYZStream();
|
||||
data.SrcLocation = boost::in_place();
|
||||
data.SrcLocation->Transport = m_src._transportGUID;
|
||||
if (!m_src._transportGUID.IsEmpty())
|
||||
data.SrcLocation->Location = m_src._transportOffset;
|
||||
else
|
||||
data << m_src._position.PositionXYZStream();
|
||||
data.SrcLocation->Location = m_src._position;
|
||||
}
|
||||
|
||||
if (m_targetMask & TARGET_FLAG_DEST_LOCATION)
|
||||
{
|
||||
data << m_dst._transportGUID.WriteAsPacked(); // relative position guid here - transport for example
|
||||
data.DstLocation = boost::in_place();
|
||||
data.DstLocation->Transport = m_dst._transportGUID;
|
||||
if (m_dst._transportGUID)
|
||||
data << m_dst._transportOffset.PositionXYZStream();
|
||||
data.DstLocation->Location = m_dst._transportOffset;
|
||||
else
|
||||
data << m_dst._position.PositionXYZStream();
|
||||
data.DstLocation->Location = m_dst._position;
|
||||
}
|
||||
|
||||
if (m_targetMask & TARGET_FLAG_STRING)
|
||||
data << m_strTarget;
|
||||
data.Name = m_strTarget;
|
||||
}
|
||||
|
||||
ObjectGuid SpellCastTargets::GetOrigUnitTargetGUID() const
|
||||
@@ -4082,33 +4084,39 @@ void Spell::SendSpellStart()
|
||||
if (m_spellInfo->RuneCostID && m_spellInfo->PowerType == POWER_RUNE)
|
||||
castFlags |= CAST_FLAG_NO_GCD; // not needed, but Blizzard sends it
|
||||
|
||||
WorldPacket data(SMSG_SPELL_START, (8+8+4+4+2));
|
||||
WorldPackets::Spells::SpellStart packet;
|
||||
WorldPackets::Spells::SpellCastData& castData = packet.Cast;
|
||||
|
||||
if (m_CastItem)
|
||||
data << m_CastItem->GetPackGUID();
|
||||
castData.CasterGUID = m_CastItem->GetGUID();
|
||||
else
|
||||
data << m_caster->GetPackGUID();
|
||||
castData.CasterGUID = m_caster->GetGUID();
|
||||
|
||||
data << m_caster->GetPackGUID();
|
||||
data << uint8(m_cast_count); // pending spell cast?
|
||||
data << uint32(m_spellInfo->Id); // spellId
|
||||
data << uint32(castFlags); // cast flags
|
||||
data << int32(m_timer); // delay?
|
||||
castData.CasterUnit = m_caster->GetGUID();
|
||||
castData.CastID = m_cast_count;
|
||||
castData.SpellID = m_spellInfo->Id;
|
||||
castData.CastFlags = castFlags;
|
||||
castData.CastTime = m_timer;
|
||||
|
||||
m_targets.Write(data);
|
||||
m_targets.Write(castData.Target);
|
||||
|
||||
if (castFlags & CAST_FLAG_POWER_LEFT_SELF)
|
||||
data << uint32(ASSERT_NOTNULL(m_caster->ToUnit())->GetPower((Powers)m_spellInfo->PowerType));
|
||||
castData.RemainingPower = ASSERT_NOTNULL(m_caster->ToUnit())->GetPower(static_cast<Powers>(m_spellInfo->PowerType));
|
||||
|
||||
if (castFlags & CAST_FLAG_AMMO)
|
||||
WriteAmmoToPacket(&data);
|
||||
{
|
||||
castData.Ammo = boost::in_place();
|
||||
UpdateSpellCastDataAmmo(*castData.Ammo);
|
||||
}
|
||||
|
||||
if (castFlags & CAST_FLAG_IMMUNITY)
|
||||
{
|
||||
data << uint32(schoolImmunityMask);
|
||||
data << uint32(mechanicImmunityMask);
|
||||
castData.Immunities = boost::in_place();
|
||||
castData.Immunities->School = schoolImmunityMask;
|
||||
castData.Immunities->Value = mechanicImmunityMask;
|
||||
}
|
||||
|
||||
m_caster->SendMessageToSet(&data, true);
|
||||
m_caster->SendMessageToSet(packet.Write(), true);
|
||||
}
|
||||
|
||||
void Spell::SendSpellGo()
|
||||
@@ -4117,8 +4125,6 @@ void Spell::SendSpellGo()
|
||||
if (!IsNeedSendToClient())
|
||||
return;
|
||||
|
||||
//TC_LOG_DEBUG("spells", "Sending SMSG_SPELL_GO id=%u", m_spellInfo->Id);
|
||||
|
||||
uint32 castFlags = CAST_FLAG_UNKNOWN_9;
|
||||
|
||||
// triggered spells with spell visual != 0
|
||||
@@ -4152,78 +4158,82 @@ void Spell::SendSpellGo()
|
||||
if (!m_spellInfo->StartRecoveryTime)
|
||||
castFlags |= CAST_FLAG_NO_GCD;
|
||||
|
||||
WorldPacket data(SMSG_SPELL_GO, 50); // guess size
|
||||
WorldPackets::Spells::SpellGo packet;
|
||||
WorldPackets::Spells::SpellCastData& castData = packet.Cast;
|
||||
|
||||
if (m_CastItem)
|
||||
data << m_CastItem->GetPackGUID();
|
||||
castData.CasterGUID = m_CastItem->GetGUID();
|
||||
else
|
||||
data << m_caster->GetPackGUID();
|
||||
castData.CasterGUID = m_caster->GetGUID();
|
||||
|
||||
data << m_caster->GetPackGUID();
|
||||
data << uint8(m_cast_count); // pending spell cast?
|
||||
data << uint32(m_spellInfo->Id); // spellId
|
||||
data << uint32(castFlags); // cast flags
|
||||
data << uint32(GameTime::GetGameTimeMS()); // timestamp
|
||||
castData.CasterUnit = m_caster->GetGUID();
|
||||
castData.CastID = m_cast_count;
|
||||
castData.SpellID = m_spellInfo->Id;
|
||||
castData.CastFlags = castFlags;
|
||||
castData.CastTime = GameTime::GetGameTimeMS();
|
||||
|
||||
WriteSpellGoTargets(&data);
|
||||
UpdateSpellCastDataTargets(castData);
|
||||
|
||||
m_targets.Write(data);
|
||||
m_targets.Write(castData.Target);
|
||||
|
||||
if (castFlags & CAST_FLAG_POWER_LEFT_SELF)
|
||||
data << uint32(ASSERT_NOTNULL(m_caster->ToUnit())->GetPower((Powers)m_spellInfo->PowerType));
|
||||
castData.RemainingPower = ASSERT_NOTNULL(m_caster->ToUnit())->GetPower(static_cast<Powers>(m_spellInfo->PowerType));
|
||||
|
||||
if (castFlags & CAST_FLAG_RUNE_LIST) // rune cooldowns list
|
||||
{
|
||||
castData.RemainingRunes = boost::in_place();
|
||||
|
||||
/// @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
|
||||
if (Player* player = m_caster->ToPlayer())
|
||||
{
|
||||
uint8 runeMaskInitial = m_runesState;
|
||||
uint8 runeMaskAfterCast = player->GetRunesState();
|
||||
data << uint8(runeMaskInitial); // runes state before
|
||||
data << uint8(runeMaskAfterCast); // runes state after
|
||||
castData.RemainingRunes->Start = runeMaskInitial; // runes state before
|
||||
castData.RemainingRunes->Count = runeMaskAfterCast; // runes state after
|
||||
|
||||
for (uint8 i = 0; i < MAX_RUNES; ++i)
|
||||
{
|
||||
uint8 mask = (1 << i);
|
||||
if (mask & runeMaskInitial && !(mask & runeMaskAfterCast)) // usable before andon cooldown now...
|
||||
if ((mask & runeMaskInitial) && !(mask & runeMaskAfterCast)) // usable before and on cooldown now...
|
||||
{
|
||||
// float casts ensure the division is performed on floats as we need float result
|
||||
float baseCd = float(player->GetRuneBaseCooldown(i));
|
||||
data << uint8((baseCd - float(player->GetRuneCooldown(i))) / baseCd * 255); // rune cooldown passed
|
||||
castData.RemainingRunes->Cooldowns.push_back(uint8((baseCd - float(player->GetRuneCooldown(i))) / baseCd * 255));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (castFlags & CAST_FLAG_ADJUST_MISSILE)
|
||||
{
|
||||
data << m_targets.GetElevation();
|
||||
data << uint32(m_delayMoment);
|
||||
castData.MissileTrajectory = boost::in_place();
|
||||
castData.MissileTrajectory->Pitch = m_targets.GetElevation();
|
||||
castData.MissileTrajectory->TravelTime = m_delayMoment;
|
||||
}
|
||||
|
||||
if (castFlags & CAST_FLAG_AMMO)
|
||||
WriteAmmoToPacket(&data);
|
||||
|
||||
if (castFlags & CAST_FLAG_VISUAL_CHAIN)
|
||||
{
|
||||
data << uint32(0);
|
||||
data << uint32(0);
|
||||
}
|
||||
|
||||
if (m_targets.GetTargetMask() & TARGET_FLAG_DEST_LOCATION)
|
||||
{
|
||||
data << uint8(0);
|
||||
castData.Ammo = boost::in_place();
|
||||
UpdateSpellCastDataAmmo(*castData.Ammo);
|
||||
}
|
||||
|
||||
// should be sent to self only
|
||||
if (castFlags & CAST_FLAG_POWER_LEFT_SELF)
|
||||
{
|
||||
if (Player* player = m_caster->GetAffectingPlayer())
|
||||
player->SendDirectMessage(&data);
|
||||
player->SendDirectMessage(packet.Write());
|
||||
|
||||
// update nearby players (remove flag)
|
||||
castData.CastFlags &= ~CAST_FLAG_POWER_LEFT_SELF;
|
||||
castData.RemainingPower = boost::none;
|
||||
m_caster->SendMessageToSet(packet.Write(), false);
|
||||
}
|
||||
else
|
||||
m_caster->SendMessageToSet(&data, true);
|
||||
m_caster->SendMessageToSet(packet.Write(), true);
|
||||
}
|
||||
|
||||
void Spell::WriteAmmoToPacket(WorldPacket* data)
|
||||
void Spell::UpdateSpellCastDataAmmo(WorldPackets::Spells::SpellAmmo& ammo)
|
||||
{
|
||||
uint32 ammoInventoryType = 0;
|
||||
uint32 ammoDisplayID = 0;
|
||||
@@ -4291,13 +4301,16 @@ void Spell::WriteAmmoToPacket(WorldPacket* data)
|
||||
}
|
||||
}
|
||||
|
||||
*data << uint32(ammoDisplayID);
|
||||
*data << uint32(ammoInventoryType);
|
||||
ammo.DisplayID = ammoDisplayID;
|
||||
ammo.InventoryType = ammoInventoryType;
|
||||
}
|
||||
|
||||
/// Writes miss and hit targets for a SMSG_SPELL_GO packet
|
||||
void Spell::WriteSpellGoTargets(WorldPacket* data)
|
||||
void Spell::UpdateSpellCastDataTargets(WorldPackets::Spells::SpellCastData& data)
|
||||
{
|
||||
data.HitTargets = boost::in_place();
|
||||
data.MissStatus = boost::in_place();
|
||||
|
||||
// This function also fill data for channeled spells:
|
||||
// m_needAliveTargetMask req for stop channelig if one target die
|
||||
for (TargetInfo& targetInfo : m_UniqueTargetInfo)
|
||||
@@ -4305,53 +4318,31 @@ void Spell::WriteSpellGoTargets(WorldPacket* data)
|
||||
if (targetInfo.EffectMask == 0) // No effect apply - all immuned add state
|
||||
// possibly SPELL_MISS_IMMUNE2 for this??
|
||||
targetInfo.MissCondition = SPELL_MISS_IMMUNE2;
|
||||
}
|
||||
|
||||
// Hit and miss target counts are both uint8, that limits us to 255 targets for each
|
||||
// sending more than 255 targets crashes the client (since count sent would be wrong)
|
||||
// Spells like 40647 (with a huge radius) can easily reach this limit (spell might need
|
||||
// target conditions but we still need to limit the number of targets sent and keeping
|
||||
// correct count for both hit and miss).
|
||||
|
||||
uint32 hit = 0;
|
||||
size_t hitPos = data->wpos();
|
||||
*data << (uint8)0; // placeholder
|
||||
for (auto ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end() && hit < 255; ++ihit)
|
||||
{
|
||||
if (ihit->MissCondition == SPELL_MISS_NONE) // Add only hits
|
||||
if (targetInfo.MissCondition == SPELL_MISS_NONE) // Add only hits
|
||||
{
|
||||
*data << uint64(ihit->TargetGUID);
|
||||
m_channelTargetEffectMask |= ihit->EffectMask;
|
||||
++hit;
|
||||
data.HitTargets->push_back(targetInfo.TargetGUID);
|
||||
|
||||
m_channelTargetEffectMask |= targetInfo.EffectMask;
|
||||
}
|
||||
else // misses
|
||||
{
|
||||
WorldPackets::Spells::SpellMissStatus missStatus;
|
||||
missStatus.TargetGUID = targetInfo.TargetGUID;
|
||||
missStatus.Reason = targetInfo.MissCondition;
|
||||
if (targetInfo.MissCondition == SPELL_MISS_REFLECT)
|
||||
missStatus.ReflectStatus = targetInfo.ReflectResult;
|
||||
|
||||
data.MissStatus->push_back(missStatus);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto ighit = m_UniqueGOTargetInfo.begin(); ighit != m_UniqueGOTargetInfo.end() && hit < 255; ++ighit)
|
||||
{
|
||||
*data << uint64(ighit->TargetGUID); // Always hits
|
||||
++hit;
|
||||
}
|
||||
for (GOTargetInfo const& targetInfo : m_UniqueGOTargetInfo)
|
||||
data.HitTargets->push_back(targetInfo.TargetGUID); // Always hits
|
||||
|
||||
uint32 miss = 0;
|
||||
size_t missPos = data->wpos();
|
||||
*data << (uint8)0; // placeholder
|
||||
for (auto ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end() && miss < 255; ++ihit)
|
||||
{
|
||||
if (ihit->MissCondition != SPELL_MISS_NONE) // Add only miss
|
||||
{
|
||||
*data << uint64(ihit->TargetGUID);
|
||||
*data << uint8(ihit->MissCondition);
|
||||
if (ihit->MissCondition == SPELL_MISS_REFLECT)
|
||||
*data << uint8(ihit->ReflectResult);
|
||||
++miss;
|
||||
}
|
||||
}
|
||||
// Reset m_needAliveTargetMask for non channeled spell
|
||||
if (!m_spellInfo->IsChanneled())
|
||||
m_channelTargetEffectMask = 0;
|
||||
|
||||
data->put<uint8>(hitPos, (uint8)hit);
|
||||
data->put<uint8>(missPos, (uint8)miss);
|
||||
}
|
||||
|
||||
void Spell::SendLogExecute()
|
||||
|
||||
@@ -26,6 +26,16 @@
|
||||
#include "SharedDefines.h"
|
||||
#include <memory>
|
||||
|
||||
namespace WorldPackets
|
||||
{
|
||||
namespace Spells
|
||||
{
|
||||
struct SpellTargetData;
|
||||
struct SpellAmmo;
|
||||
struct SpellCastData;
|
||||
}
|
||||
}
|
||||
|
||||
class Aura;
|
||||
class AuraEffect;
|
||||
class Corpse;
|
||||
@@ -125,7 +135,7 @@ class TC_GAME_API SpellCastTargets
|
||||
~SpellCastTargets();
|
||||
|
||||
void Read(ByteBuffer& data, Unit* caster);
|
||||
void Write(ByteBuffer& data);
|
||||
void Write(WorldPackets::Spells::SpellTargetData& data);
|
||||
|
||||
uint32 GetTargetMask() const { return m_targetMask; }
|
||||
void SetTargetMask(uint32 newMask) { m_targetMask = newMask; }
|
||||
@@ -449,8 +459,8 @@ class TC_GAME_API Spell
|
||||
void setState(uint32 state) { m_spellState = state; }
|
||||
|
||||
void DoCreateItem(uint32 i, uint32 itemtype);
|
||||
void WriteSpellGoTargets(WorldPacket* data);
|
||||
void WriteAmmoToPacket(WorldPacket* data);
|
||||
void UpdateSpellCastDataTargets(WorldPackets::Spells::SpellCastData& data);
|
||||
void UpdateSpellCastDataAmmo(WorldPackets::Spells::SpellAmmo& data);
|
||||
|
||||
bool CheckEffectTarget(Unit const* target, uint32 eff, Position const* losPosition) const;
|
||||
bool CanAutoCast(Unit* target);
|
||||
|
||||
Reference in New Issue
Block a user