Add Buff infrastructure to go through different logic paths from different patches (#5073)
Build / Linux (push) Waiting to run
Build / Windows (push) Waiting to run

This commit is contained in:
dannuic
2026-05-02 22:53:26 -06:00
committed by GitHub
parent c253734c57
commit 18df055f16
201 changed files with 2856 additions and 3105 deletions
+60
View File
@@ -0,0 +1,60 @@
/* EQEmu: EQEmulator
Copyright (C) 2001-2026 EQEmu Development Team
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 3 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/>.
*/
#pragma once
#include "common/emu_opcodes.h"
#include <vector>
#include <functional>
class Client;
class Mob;
class EQApplicationPacket;
class Buffs_Struct;
namespace ClientPatch {
class IBuff
{
public:
using BuffSequenceFunc = std::function<std::unique_ptr<EQApplicationPacket>(const Client*)>;
IBuff(uint32_t maxLongBuffs, uint32_t maxShortBuffs)
: m_maxLongBuffs(maxLongBuffs)
, m_maxShortBuffs(maxShortBuffs)
{}
IBuff() = delete;
virtual ~IBuff() = default;
virtual std::unique_ptr<EQApplicationPacket> BuffDefinition(Mob* mob, const Buffs_Struct& buff, uint32_t slot,
bool fade) const = 0;
virtual std::unique_ptr<EQApplicationPacket> RefreshBuffs(EmuOpcode opcode, Mob* mob, bool remove,
bool buff_timers_suspended, const std::vector<uint32_t>& slots) const = 0;
virtual void SetRefreshType(std::unique_ptr<EQApplicationPacket>& packet, uint8_t refresh_type) const = 0;
virtual bool NeedsWearMessage() const = 0;
uint32_t ServerToPatchBuffSlot(uint32_t slot) const;
protected:
uint32_t m_maxLongBuffs;
uint32_t m_maxShortBuffs;
};
} // namespace Buff
+4 -4
View File
@@ -15,9 +15,8 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "client_version.h"
#pragma once
// Migration path: replace string_ids.h usage with ID enum values one call site at a time.
@@ -25,7 +24,7 @@ class Client;
class Mob;
class EQApplicationPacket;
namespace Message {
namespace ClientPatch {
template<typename... Args>
concept AllConstChar = (std::is_convertible_v<Args, const char*> && ...);
@@ -33,13 +32,14 @@ concept AllConstChar = (std::is_convertible_v<Args, const char*> && ...);
class IMessage
{
public:
using FormattedArgs = std::array<const char*, 9>;
IMessage() = default;
virtual ~IMessage() = default;
// these two are the basic string message packets
virtual std::unique_ptr<EQApplicationPacket> Simple(uint32_t color, uint32_t id) const = 0;
virtual std::unique_ptr<EQApplicationPacket> Formatted(uint32_t color, uint32_t id,
const std::array<const char*, 9>& args) const = 0;
const FormattedArgs& args) const = 0;
// These aren't technically messages, but they use the same format and are similar enough to include here
virtual std::unique_ptr<EQApplicationPacket> InterruptSpell(uint32_t message, uint32_t spawn_id,
+46 -9
View File
@@ -18,13 +18,22 @@
#include "client_version.h"
#include "common/emu_constants.h"
#include "common/patches/titanium.h"
#include "common/patches/titanium_limits.h"
#include "common/patches/sof.h"
#include "common/patches/sof_limits.h"
#include "common/patches/sod.h"
#include "common/patches/sod_limits.h"
#include "common/patches/uf.h"
#include "common/patches/uf_limits.h"
#include "common/patches/rof.h"
#include "common/patches/rof_limits.h"
#include "common/patches/rof2.h"
#include "common/patches/rof2_limits.h"
#include "common/patches/tob.h"
#include "common/patches/tob_limits.h"
#include <array>
@@ -36,25 +45,32 @@ struct ClientComponents
{
switch (version) {
case Version::TOB:
messageComponent = std::make_unique<Message::TOB>();
buffComponent = std::make_unique<TOB::BuffComponent>(TOB::spells::LONG_BUFFS, TOB::spells::SHORT_BUFFS);
messageComponent = std::make_unique<TOB::MessageComponent>();
break;
case Version::RoF2:
messageComponent = std::make_unique<Message::RoF2>();
buffComponent = std::make_unique<UF::BuffComponent>(RoF2::spells::LONG_BUFFS, RoF2::spells::SHORT_BUFFS);
messageComponent = std::make_unique<Titanium::MessageComponent>();
break;
case Version::RoF:
messageComponent = std::make_unique<Message::RoF>();
buffComponent = std::make_unique<UF::BuffComponent>(RoF::spells::LONG_BUFFS, RoF::spells::SHORT_BUFFS);
messageComponent = std::make_unique<Titanium::MessageComponent>();
break;
case Version::UF:
messageComponent = std::make_unique<Message::UF>();
buffComponent = std::make_unique<UF::BuffComponent>(UF::spells::LONG_BUFFS, UF::spells::SHORT_BUFFS);
messageComponent = std::make_unique<Titanium::MessageComponent>();
break;
case Version::SoD:
messageComponent = std::make_unique<Message::SoD>();
buffComponent = std::make_unique<SoD::BuffComponent>(SoD::spells::LONG_BUFFS, SoD::spells::SHORT_BUFFS);
messageComponent = std::make_unique<Titanium::MessageComponent>();
break;
case Version::SoF:
messageComponent = std::make_unique<Message::SoF>();
buffComponent = std::make_unique<Titanium::BuffComponent>(SoF::spells::LONG_BUFFS, SoF::spells::SHORT_BUFFS);
messageComponent = std::make_unique<Titanium::MessageComponent>();
break;
case Version::Titanium:
messageComponent = std::make_unique<Message::Titanium>();
buffComponent = std::make_unique<Titanium::BuffComponent>(Titanium::spells::LONG_BUFFS, Titanium::spells::SHORT_BUFFS);
messageComponent = std::make_unique<Titanium::MessageComponent>();
break;
default:
break;
@@ -62,7 +78,8 @@ struct ClientComponents
}
const Version version;
std::unique_ptr<Message::IMessage> messageComponent;
std::unique_ptr<ClientPatch::IBuff> buffComponent;
std::unique_ptr<ClientPatch::IMessage> messageComponent;
};
// this array must be in the same order as the Version enum because it converts Version to index directly
@@ -80,7 +97,27 @@ static const std::array<ClientComponents, EQ::versions::ClientVersionCount> s_pa
}
};
const std::unique_ptr<Message::IMessage>& GetMessageComponent(Version version)
template<>
const std::unique_ptr<ClientPatch::IBuff>& GetComponent(Version version)
{
return s_patches.at(static_cast<uint32_t>(version)).buffComponent;
}
template<>
const std::unique_ptr<ClientPatch::IMessage>& GetComponent(Version version)
{
return s_patches.at(static_cast<uint32_t>(version)).messageComponent;
}
uint32_t ClientPatch::IBuff::ServerToPatchBuffSlot(uint32_t slot) const
{
// we're a disc
if (slot >= EQ::spells::LONG_BUFFS + EQ::spells::SHORT_BUFFS)
return slot - EQ::spells::LONG_BUFFS - EQ::spells::SHORT_BUFFS +
m_maxLongBuffs + m_maxShortBuffs;
// we're a song
if (slot >= EQ::spells::LONG_BUFFS)
return slot - EQ::spells::LONG_BUFFS + m_maxLongBuffs;
// we're a normal buff
return slot; // as long as we guard against bad slots server side, we should be fine
}
+30 -5
View File
@@ -1,13 +1,38 @@
//
// Created by dannu on 4/21/2026.
//
/* EQEmu: EQEmulator
Copyright (C) 2001-2026 EQEmu Development Team
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 3 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/>.
*/
#pragma once
#include "common/emu_versions.h"
#include <memory>
namespace Message { class IMessage; }
namespace ClientPatch {
class IBuff;
class IMessage;
}
// store all static functions for the different patches here
// store all static functions for the different patches here, this can return nullptr for unsupported patches
const std::unique_ptr<Message::IMessage>& GetMessageComponent(EQ::versions::ClientVersion version);
template <typename Component>
const std::unique_ptr<Component>& GetComponent(EQ::versions::ClientVersion version);
template <>
const std::unique_ptr<ClientPatch::IBuff>& GetComponent(EQ::versions::ClientVersion version);
template <>
const std::unique_ptr<ClientPatch::IMessage>& GetComponent(EQ::versions::ClientVersion version);
+1 -1
View File
@@ -353,7 +353,7 @@
0x05ea,
0x1b6f,
0x198e,
0x7bd6, OP_Buff
0x7bd6, OP_BuffDefinition
0x3501,
0x47ab,
0x7a9e, OP_World_Client_CRC1
+5 -102
View File
@@ -67,7 +67,6 @@ namespace RoF
static inline spells::CastingSlot ServerToRoFCastingSlot(EQ::spells::CastingSlot slot);
static inline EQ::spells::CastingSlot RoFToServerCastingSlot(spells::CastingSlot slot);
static inline int ServerToRoFBuffSlot(int index);
static inline int RoFToServerBuffSlot(int index);
void Register(EQStreamIdentifier &into)
@@ -402,7 +401,7 @@ namespace RoF
FINISH_ENCODE();
}
ENCODE(OP_Buff)
ENCODE(OP_BuffDefinition)
{
ENCODE_LENGTH_EXACT(SpellBuffPacket_Struct);
SETUP_DIRECT_ENCODE(SpellBuffPacket_Struct, structs::SpellBuffPacket_Struct);
@@ -410,8 +409,7 @@ namespace RoF
OUT(entityid);
OUT(buff.effect_type);
OUT(buff.level);
// just so we're 100% sure we get a 1.0f ...
eq->buff.bard_modifier = emu->buff.bard_modifier == 10 ? 1.0f : emu->buff.bard_modifier / 10.0f;
OUT(buff.bard_modifier);
OUT(buff.spellid);
OUT(buff.duration);
OUT(buff.player_id);
@@ -420,59 +418,13 @@ namespace RoF
OUT(buff.x);
OUT(buff.z);
// TODO: implement slot_data stuff
eq->slotid = ServerToRoFBuffSlot(emu->slotid);
OUT(slotid);
if (emu->bufffade == 1)
eq->bufffade = 1;
else
eq->bufffade = 2;
// Bit of a hack. OP_Buff appears to add/remove the buff while OP_BuffCreate adds/removes the actual buff icon
EQApplicationPacket *outapp = nullptr;
if (eq->bufffade == 1)
{
outapp = new EQApplicationPacket(OP_BuffCreate, 29);
outapp->WriteUInt32(emu->entityid);
outapp->WriteUInt32(0); // tic timer
outapp->WriteUInt8(0); // Type of OP_BuffCreate packet ?
outapp->WriteUInt16(1); // 1 buff in this packet
outapp->WriteUInt32(eq->slotid);
outapp->WriteUInt32(0xffffffff); // SpellID (0xffff to remove)
outapp->WriteUInt32(0); // Duration
outapp->WriteUInt32(0); // numhits
outapp->WriteUInt8(0); // Caster name
outapp->WriteUInt8(0); // Type
}
FINISH_ENCODE();
if (outapp)
dest->FastQueuePacket(&outapp); // Send the OP_BuffCreate to remove the buff
}
ENCODE(OP_BuffCreate)
{
SETUP_VAR_ENCODE(BuffIcon_Struct);
uint32 sz = 12 + (17 * emu->count) + emu->name_lengths; // 17 includes nullterm
__packet->size = sz;
__packet->pBuffer = new unsigned char[sz];
memset(__packet->pBuffer, 0, sz);
__packet->WriteUInt32(emu->entity_id);
__packet->WriteUInt32(emu->tic_timer);
__packet->WriteUInt8(emu->all_buffs); // 1 indicates all buffs on the player (0 to add or remove a single buff)
__packet->WriteUInt16(emu->count);
for (int i = 0; i < emu->count; ++i)
{
__packet->WriteUInt32(emu->type == 0 ? ServerToRoFBuffSlot(emu->entries[i].buff_slot) : emu->entries[i].buff_slot);
__packet->WriteUInt32(emu->entries[i].spell_id);
__packet->WriteUInt32(emu->entries[i].tics_remaining);
__packet->WriteUInt32(emu->entries[i].num_hits); // Unknown
__packet->WriteString(emu->entries[i].caster);
}
__packet->WriteUInt8(emu->type); // Unknown
FINISH_ENCODE();
}
@@ -1858,38 +1810,6 @@ namespace RoF
FINISH_ENCODE();
}
ENCODE(OP_PetBuffWindow)
{
// The format of the RoF packet is identical to the OP_BuffCreate packet.
SETUP_VAR_ENCODE(PetBuff_Struct);
uint32 sz = 12 + (17 * emu->buffcount);
__packet->size = sz;
__packet->pBuffer = new unsigned char[sz];
memset(__packet->pBuffer, 0, sz);
__packet->WriteUInt32(emu->petid);
__packet->WriteUInt32(0); // PlayerID ?
__packet->WriteUInt8(1); // 1 indicates all buffs on the pet (0 to add or remove a single buff)
__packet->WriteUInt16(emu->buffcount);
for (uint16 i = 0; i < PET_BUFF_COUNT; ++i)
{
if (emu->spellid[i])
{
__packet->WriteUInt32(i);
__packet->WriteUInt32(emu->spellid[i]);
__packet->WriteUInt32(emu->ticsremaining[i]);
__packet->WriteUInt32(0); // numhits
__packet->WriteString("");
}
}
__packet->WriteUInt8(0); // some sort of type
FINISH_ENCODE();
}
ENCODE(OP_PlayerProfile)
{
EQApplicationPacket *in = *p;
@@ -2179,7 +2099,7 @@ namespace RoF
outapp->WriteUInt32(emu->buffs[r].counters);
outapp->WriteUInt32(emu->buffs[r].duration);
outapp->WriteUInt8(emu->buffs[r].level);
outapp->WriteUInt32(emu->buffs[r].spellid);
outapp->WriteSInt32 (emu->buffs[r].spellid);
outapp->WriteUInt8(effect_type); // Only ever seen 2
outapp->WriteUInt32(emu->buffs[r].num_hits);
outapp->WriteUInt32(0);
@@ -3239,8 +3159,6 @@ namespace RoF
FINISH_ENCODE();
}
ENCODE(OP_TargetBuffs) { ENCODE_FORWARD(OP_BuffCreate); }
ENCODE(OP_TaskDescription)
{
EQApplicationPacket *in = *p;
@@ -4196,7 +4114,7 @@ namespace RoF
FINISH_DIRECT_DECODE();
}
DECODE(OP_Buff)
DECODE(OP_BuffDefinition)
{
DECODE_LENGTH_EXACT(structs::SpellBuffPacket_Struct);
SETUP_DIRECT_DECODE(SpellBuffPacket_Struct, structs::SpellBuffPacket_Struct);
@@ -6263,21 +6181,6 @@ namespace RoF
}
}
// these should be optimized out for RoF since they should all boil down to return index :P
// but lets leave it here for future proofing
static inline int ServerToRoFBuffSlot(int index)
{
// we're a disc
if (index >= EQ::spells::LONG_BUFFS + EQ::spells::SHORT_BUFFS)
return index - EQ::spells::LONG_BUFFS - EQ::spells::SHORT_BUFFS +
spells::LONG_BUFFS + spells::SHORT_BUFFS;
// we're a song
if (index >= EQ::spells::LONG_BUFFS)
return index - EQ::spells::LONG_BUFFS + spells::LONG_BUFFS;
// we're a normal buff
return index; // as long as we guard against bad slots server side, we should be fine
}
static inline int RoFToServerBuffSlot(int index)
{
// we're a disc
+15 -33
View File
@@ -17,46 +17,28 @@
*/
#pragma once
#include "uf.h"
#include "common/struct_strategy.h"
#include "common/patches/uf.h"
class EQStreamIdentifier;
namespace RoF
{
namespace RoF {
//these are the only public member of this namespace.
extern void Register(EQStreamIdentifier &into);
extern void Reload();
extern void Register(EQStreamIdentifier& into);
extern void Reload();
//you should not directly access anything below..
//I just dont feel like making a seperate header for it.
class Strategy : public StructStrategy {
public:
Strategy();
protected:
virtual std::string Describe() const;
virtual const EQ::versions::ClientVersion ClientVersion() const;
//magic macro to declare our opcode processors
#include "ss_declare.h"
#include "rof_ops.h"
};
} /*RoF*/
namespace Message {
class RoF : public UF
class Strategy : public StructStrategy
{
public:
RoF() = default;
~RoF() override = default;
Strategy();
protected:
virtual std::string Describe() const;
virtual const EQ::versions::ClientVersion ClientVersion() const;
//magic macro to declare our opcode processors
#include "ss_declare.h"
#include "rof_ops.h"
};
} // namespace Message
} /*RoF*/
+4 -100
View File
@@ -69,7 +69,6 @@ namespace RoF2
static inline spells::CastingSlot ServerToRoF2CastingSlot(EQ::spells::CastingSlot slot);
static inline EQ::spells::CastingSlot RoF2ToServerCastingSlot(spells::CastingSlot slot);
static inline int ServerToRoF2BuffSlot(int index);
static inline int RoF2ToServerBuffSlot(int index);
void Register(EQStreamIdentifier &into)
@@ -661,7 +660,7 @@ namespace RoF2
FINISH_ENCODE();
}
ENCODE(OP_Buff)
ENCODE(OP_BuffDefinition)
{
ENCODE_LENGTH_EXACT(SpellBuffPacket_Struct);
SETUP_DIRECT_ENCODE(SpellBuffPacket_Struct, structs::SpellBuffPacket_Struct);
@@ -678,59 +677,13 @@ namespace RoF2
OUT(buff.y);
OUT(buff.x);
OUT(buff.z);
eq->slotid = ServerToRoF2BuffSlot(emu->slotid);
OUT(slotid);
// TODO: implement slot_data stuff
if (emu->bufffade == 1)
eq->bufffade = 1;
else
eq->bufffade = 2;
// Bit of a hack. OP_Buff appears to add/remove the buff while OP_BuffCreate adds/removes the actual buff icon
EQApplicationPacket *outapp = nullptr;
if (eq->bufffade == 1)
{
outapp = new EQApplicationPacket(OP_BuffCreate, 29u);
outapp->WriteUInt32(emu->entityid);
outapp->WriteUInt32(0); // tic timer
outapp->WriteUInt8(0); // Type of OP_BuffCreate packet ?
outapp->WriteUInt16(1); // 1 buff in this packet
outapp->WriteUInt32(eq->slotid);
outapp->WriteUInt32(0xffffffff); // SpellID (0xffff to remove)
outapp->WriteUInt32(0); // Duration
outapp->WriteUInt32(0); // numhits
outapp->WriteUInt8(0); // Caster name
outapp->WriteUInt8(0); // Type
}
FINISH_ENCODE();
if (outapp)
dest->FastQueuePacket(&outapp); // Send the OP_BuffCreate to remove the buff
}
ENCODE(OP_BuffCreate)
{
SETUP_VAR_ENCODE(BuffIcon_Struct);
uint32 sz = 12 + (17 * emu->count) + emu->name_lengths; // 17 includes nullterm
__packet->size = sz;
__packet->pBuffer = new unsigned char[sz];
memset(__packet->pBuffer, 0, sz);
__packet->WriteUInt32(emu->entity_id);
__packet->WriteUInt32(emu->tic_timer);
__packet->WriteUInt8(emu->all_buffs); // 1 indicates all buffs on the player (0 to add or remove a single buff)
__packet->WriteUInt16(emu->count);
for (int i = 0; i < emu->count; ++i)
{
__packet->WriteUInt32(emu->type == 0 ? ServerToRoF2BuffSlot(emu->entries[i].buff_slot) : emu->entries[i].buff_slot);
__packet->WriteUInt32(emu->entries[i].spell_id);
__packet->WriteUInt32(emu->entries[i].tics_remaining);
__packet->WriteUInt32(emu->entries[i].num_hits); // Unknown
__packet->WriteString(emu->entries[i].caster);
}
__packet->WriteUInt8(emu->type); // Unknown
FINISH_ENCODE();
}
@@ -2464,38 +2417,6 @@ namespace RoF2
FINISH_ENCODE();
}
ENCODE(OP_PetBuffWindow)
{
// The format of the RoF2 packet is identical to the OP_BuffCreate packet.
SETUP_VAR_ENCODE(PetBuff_Struct);
uint32 sz = 12 + (17 * emu->buffcount);
__packet->size = sz;
__packet->pBuffer = new unsigned char[sz];
memset(__packet->pBuffer, 0, sz);
__packet->WriteUInt32(emu->petid);
__packet->WriteUInt32(0); // PlayerID ?
__packet->WriteUInt8(1); // 1 indicates all buffs on the pet (0 to add or remove a single buff)
__packet->WriteUInt16(emu->buffcount);
for (uint16 i = 0; i < PET_BUFF_COUNT; ++i)
{
if (emu->spellid[i])
{
__packet->WriteUInt32(i);
__packet->WriteUInt32(emu->spellid[i]);
__packet->WriteUInt32(emu->ticsremaining[i]);
__packet->WriteUInt32(0); // num hits
__packet->WriteString("");
}
}
__packet->WriteUInt8(0); // some sort of type
FINISH_ENCODE();
}
ENCODE(OP_PlayerProfile)
{
EQApplicationPacket *in = *p;
@@ -2786,7 +2707,7 @@ namespace RoF2
outapp->WriteUInt32(emu->buffs[r].counters);
outapp->WriteUInt32(emu->buffs[r].duration);
outapp->WriteUInt8(emu->buffs[r].level);
outapp->WriteUInt32(emu->buffs[r].spellid);
outapp->WriteSInt32 (emu->buffs[r].spellid);
outapp->WriteUInt8(effect_type); // Only ever seen 2
outapp->WriteUInt32(emu->buffs[r].num_hits);
outapp->WriteUInt32(0);
@@ -3841,8 +3762,6 @@ namespace RoF2
FINISH_ENCODE();
}
ENCODE(OP_TargetBuffs) { ENCODE_FORWARD(OP_BuffCreate); }
ENCODE(OP_TaskDescription)
{
EQApplicationPacket *in = *p;
@@ -5145,7 +5064,7 @@ namespace RoF2
FINISH_DIRECT_DECODE();
}
DECODE(OP_Buff)
DECODE(OP_BuffDefinition)
{
DECODE_LENGTH_EXACT(structs::SpellBuffPacket_Struct);
SETUP_DIRECT_DECODE(SpellBuffPacket_Struct, structs::SpellBuffPacket_Struct);
@@ -7495,21 +7414,6 @@ namespace RoF2
}
}
// these should be optimized out for RoF2 since they should all boil down to return index :P
// but lets leave it here for future proofing
static inline int ServerToRoF2BuffSlot(int index)
{
// we're a disc
if (index >= EQ::spells::LONG_BUFFS + EQ::spells::SHORT_BUFFS)
return index - EQ::spells::LONG_BUFFS - EQ::spells::SHORT_BUFFS +
spells::LONG_BUFFS + spells::SHORT_BUFFS;
// we're a song
if (index >= EQ::spells::LONG_BUFFS)
return index - EQ::spells::LONG_BUFFS + spells::LONG_BUFFS;
// we're a normal buff
return index; // as long as we guard against bad slots server side, we should be fine
}
static inline int RoF2ToServerBuffSlot(int index)
{
// we're a disc
+15 -33
View File
@@ -17,46 +17,28 @@
*/
#pragma once
#include "rof.h"
#include "common/struct_strategy.h"
#include "common/patches/rof.h"
class EQStreamIdentifier;
namespace RoF2
{
namespace RoF2 {
//these are the only public member of this namespace.
extern void Register(EQStreamIdentifier &into);
extern void Reload();
extern void Register(EQStreamIdentifier& into);
extern void Reload();
//you should not directly access anything below..
//I just dont feel like making a seperate header for it.
class Strategy : public StructStrategy {
public:
Strategy();
protected:
virtual std::string Describe() const;
virtual const EQ::versions::ClientVersion ClientVersion() const;
//magic macro to declare our opcode processors
#include "ss_declare.h"
#include "rof2_ops.h"
};
}; /*RoF2*/
namespace Message {
class RoF2 : public RoF
class Strategy : public StructStrategy
{
public:
RoF2() = default;
~RoF2() override = default;
Strategy();
protected:
virtual std::string Describe() const;
virtual const EQ::versions::ClientVersion ClientVersion() const;
//magic macro to declare our opcode processors
#include "ss_declare.h"
#include "rof2_ops.h"
};
} // namespace Message
}; /*RoF2*/
+2 -5
View File
@@ -43,8 +43,7 @@ E(OP_BazaarSearch)
E(OP_BecomeTrader)
E(OP_BeginCast)
E(OP_BlockedBuffs)
E(OP_Buff)
E(OP_BuffCreate)
E(OP_BuffDefinition)
E(OP_BuyerItems)
E(OP_CancelTrade)
E(OP_CastSpell)
@@ -100,7 +99,6 @@ E(OP_MoveItem)
E(OP_NewSpawn)
E(OP_NewZone)
E(OP_OnLevelMessage)
E(OP_PetBuffWindow)
E(OP_PlayerProfile)
E(OP_RaidJoin)
E(OP_RaidUpdate)
@@ -123,7 +121,6 @@ E(OP_SpawnAppearance)
E(OP_SpawnDoor)
E(OP_SpecialMesg)
E(OP_Stun)
E(OP_TargetBuffs)
E(OP_TaskDescription)
E(OP_TaskHistoryReply)
E(OP_Track)
@@ -153,7 +150,7 @@ D(OP_Barter)
D(OP_BazaarSearch)
D(OP_BlockedBuffs)
D(OP_BookButton)
D(OP_Buff)
D(OP_BuffDefinition)
D(OP_BuffRemoveRequest)
D(OP_BuyerItems)
D(OP_CastSpell)
+11 -11
View File
@@ -668,7 +668,7 @@ struct NewZone_Struct {
*/
struct MemorizeSpell_Struct {
uint32 slot; // Spot in the spell book/memorized slot
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc)
int32 spell_id; // Spell id (200 or c8 is minor healing, etc)
uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming
uint32 reduction; // lowers reuse
};
@@ -705,7 +705,7 @@ struct ManaChange_Struct
{
/*00*/ uint32 new_mana; // New Mana AMount
/*04*/ uint32 stamina;
/*08*/ uint32 spell_id;
/*08*/ int32 spell_id;
/*12*/ uint8 keepcasting; // won't stop the cast. Change mana while casting?
/*13*/ uint8 padding[3]; // client doesn't read it, garbage data seems like
/*16*/ int32 slot; // -1 for normal usage slot for when we want silent interrupt? I think it does timer stuff or something. Linked Spell Reuse interrupt uses it
@@ -719,7 +719,7 @@ struct SwapSpell_Struct
struct BeginCast_Struct
{
/*000*/ uint32 spell_id;
/*000*/ int32 spell_id;
/*004*/ uint16 caster_id;
/*006*/ uint32 cast_time; // in miliseconds
/*010*/
@@ -728,7 +728,7 @@ struct BeginCast_Struct
struct CastSpell_Struct
{
/*00*/ uint32 slot;
/*04*/ uint32 spell_id;
/*04*/ int32 spell_id;
/*08*/ InventorySlot_Struct inventory_slot; // slot for clicky item, Seen unknown of 131 = normal cast
/*20*/ uint32 target_id;
/*24*/ uint32 cs_unknown[2];
@@ -760,7 +760,7 @@ struct SpellBuff_Struct
/*002*/ uint8 unknown002; //pretty sure padding now
/*003*/ uint8 unknown003; // MQ2 used to call this "damage shield" -- don't see client referencing it, so maybe server side DS type tracking?
/*004*/ float bard_modifier;
/*008*/ uint32 spellid;
/*008*/ int32 spellid;
/*012*/ uint32 duration;
/*016*/ uint32 player_id; // caster ID, pretty sure just zone ID
/*020*/ uint32 num_hits;
@@ -791,7 +791,7 @@ struct BuffRemoveRequest_Struct
// not in use
struct BuffIconEntry_Struct {
/*000*/ uint32 buff_slot;
/*004*/ uint32 spell_id;
/*004*/ int32 spell_id;
/*008*/ uint32 tics_remaining;
/*012*/ uint32 num_hits;
// char name[0]; caster name is also here sometimes
@@ -1513,7 +1513,7 @@ struct CombatDamage_Struct
/* 00 */ uint16 target;
/* 02 */ uint16 source;
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells
/* 05 */ uint32 spellid;
/* 05 */ int32 spellid;
/* 09 */ int32 damage;
/* 13 */ float force; // cd cc cc 3d
/* 17 */ float hit_heading; // see above notes in Action_Struct
@@ -1549,7 +1549,7 @@ struct Death_Struct
/*004*/ uint32 killer_id;
/*008*/ uint32 corpseid; // was corpseid
/*012*/ uint32 attack_skill; // was type
/*016*/ uint32 spell_id;
/*016*/ int32 spell_id;
/*020*/ uint32 bindzoneid; //bindzoneid?
/*024*/ uint32 damage;
/*028*/ uint32 unknown028;
@@ -2658,7 +2658,7 @@ struct GroupFollow_Struct { // Live Follow Struct
};
struct InspectBuffs_Struct {
/*000*/ uint32 spell_id[BUFF_COUNT];
/*000*/ int32 spell_id[BUFF_COUNT];
/*168*/ int32 tics_remaining[BUFF_COUNT];
};
@@ -3077,7 +3077,7 @@ struct Resurrect_Struct
/*024*/ char your_name[64];
/*088*/ uint32 unknown088;
/*092*/ char rezzer_name[64];
/*156*/ uint32 spellid;
/*156*/ int32 spellid;
/*160*/ char corpse_name[64];
/*224*/ uint32 action;
/*228*/ uint32 unknown228;
@@ -4581,7 +4581,7 @@ struct SendAA_Struct {
/*0045*/ uint32 prereq_minpoints_count; // mutliple prereqs at least 1, even no prereqs
/*0049*/ uint32 prereq_minpoints; //min points in the prereq
/*0053*/ uint32 type;
/*0057*/ uint32 spellid;
/*0057*/ int32 spellid;
/*0061*/ uint32 unknown057; // Introduced during HoT - Seen 1 - Maybe account status or enable/disable AA?
/*0065*/ uint32 spell_type;
/*0069*/ uint32 spell_refresh;
+2 -5
View File
@@ -27,8 +27,7 @@ E(OP_Barter)
E(OP_BazaarSearch)
E(OP_BeginCast)
E(OP_BlockedBuffs)
E(OP_Buff)
E(OP_BuffCreate)
E(OP_BuffDefinition)
E(OP_CancelTrade)
E(OP_CastSpell)
E(OP_ChannelMessage)
@@ -81,7 +80,6 @@ E(OP_MoveItem)
E(OP_NewSpawn)
E(OP_NewZone)
E(OP_OnLevelMessage)
E(OP_PetBuffWindow)
E(OP_PlayerProfile)
E(OP_RaidJoin)
E(OP_RaidUpdate)
@@ -104,7 +102,6 @@ E(OP_SpawnAppearance)
E(OP_SpawnDoor)
E(OP_SpecialMesg)
E(OP_Stun)
E(OP_TargetBuffs)
E(OP_TaskDescription)
E(OP_TaskHistoryReply)
E(OP_Track)
@@ -131,7 +128,7 @@ D(OP_AugmentInfo)
D(OP_AugmentItem)
D(OP_BazaarSearch)
D(OP_BlockedBuffs)
D(OP_Buff)
D(OP_BuffDefinition)
D(OP_BuffRemoveRequest)
D(OP_CastSpell)
D(OP_ChannelMessage)
+11 -11
View File
@@ -614,7 +614,7 @@ struct NewZone_Struct {
*/
struct MemorizeSpell_Struct {
uint32 slot; // Spot in the spell book/memorized slot
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc)
int32 spell_id; // Spell id (200 or c8 is minor healing, etc)
uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming
uint32 reduction; // lowers reuse
};
@@ -651,7 +651,7 @@ struct ManaChange_Struct
{
/*00*/ uint32 new_mana; // New Mana AMount
/*04*/ uint32 stamina;
/*08*/ uint32 spell_id;
/*08*/ int32 spell_id;
/*12*/ uint8 keepcasting; // won't stop the cast. Change mana while casting?
/*13*/ uint8 padding[3]; // client doesn't read it, garbage data seems like
/*16*/ int32 slot; // -1 for normal usage slot for when we want silent interrupt? I think it does timer stuff or something. Linked Spell Reuse interrupt uses it
@@ -665,7 +665,7 @@ struct SwapSpell_Struct
struct BeginCast_Struct
{
/*000*/ uint32 spell_id;
/*000*/ int32 spell_id;
/*004*/ uint16 caster_id;
/*006*/ uint32 cast_time; // in miliseconds
/*010*/
@@ -674,7 +674,7 @@ struct BeginCast_Struct
struct CastSpell_Struct
{
/*00*/ uint32 slot;
/*04*/ uint32 spell_id;
/*04*/ int32 spell_id;
/*08*/ InventorySlot_Struct inventory_slot; // slot for clicky item, Seen unknown of 131 = normal cast
/*20*/ uint32 target_id;
/*24*/ uint32 cs_unknown[2];
@@ -706,7 +706,7 @@ struct SpellBuff_Struct
/*002*/ uint8 unknown002; //pretty sure padding now
/*003*/ uint8 unknown003; // MQ2 used to call this "damage shield" -- don't see client referencing it, so maybe server side DS type tracking?
/*004*/ float bard_modifier;
/*008*/ uint32 spellid;
/*008*/ int32 spellid;
/*012*/ uint32 duration;
/*016*/ uint32 player_id; // caster ID, pretty sure just zone ID
/*020*/ uint32 num_hits;
@@ -737,7 +737,7 @@ struct BuffRemoveRequest_Struct
// not in use
struct BuffIconEntry_Struct {
/*000*/ uint32 buff_slot;
/*004*/ uint32 spell_id;
/*004*/ int32 spell_id;
/*008*/ uint32 tics_remaining;
/*012*/ uint32 num_hits;
// char name[0]; caster name is also here sometimes
@@ -1500,7 +1500,7 @@ struct CombatDamage_Struct
/* 00 */ uint16 target;
/* 02 */ uint16 source;
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells
/* 05 */ uint32 spellid;
/* 05 */ int32 spellid;
/* 09 */ int32 damage;
/* 13 */ float force; // cd cc cc 3d
/* 17 */ float hit_heading; // see above notes in Action_Struct
@@ -1536,7 +1536,7 @@ struct Death_Struct
/*004*/ uint32 killer_id;
/*008*/ uint32 corpseid; // was corpseid
/*012*/ uint32 attack_skill; // was type
/*016*/ uint32 spell_id;
/*016*/ int32 spell_id;
/*020*/ uint32 bindzoneid; //bindzoneid?
/*024*/ uint32 damage;
/*028*/ uint32 unknown028;
@@ -2603,7 +2603,7 @@ struct GroupFollow_Struct { // Live Follow Struct
};
struct InspectBuffs_Struct {
/*000*/ uint32 spell_id[BUFF_COUNT];
/*000*/ int32 spell_id[BUFF_COUNT];
/*168*/ int32 tics_remaining[BUFF_COUNT];
};
@@ -3013,7 +3013,7 @@ struct Resurrect_Struct
/*024*/ char your_name[64];
/*088*/ uint32 unknown088;
/*092*/ char rezzer_name[64];
/*156*/ uint32 spellid;
/*156*/ int32 spellid;
/*160*/ char corpse_name[64];
/*224*/ uint32 action;
/*228*/ uint32 unknown228;
@@ -4333,7 +4333,7 @@ struct SendAA_Struct {
/*0045*/ uint32 prereq_minpoints_count; // mutliple prereqs at least 1, even no prereqs
/*0049*/ uint32 prereq_minpoints; //min points in the prereq
/*0053*/ uint32 type;
/*0057*/ uint32 spellid;
/*0057*/ int32 spellid;
/*0061*/ uint32 unknown057; // Introduced during HoT - Seen 1 - Maybe account status or enable/disable AA?
/*0065*/ uint32 spell_type;
/*0069*/ uint32 spell_refresh;
+56 -78
View File
@@ -31,6 +31,8 @@
#include "common/raid.h"
#include "common/rulesys.h"
#include "common/strings.h"
#include "zone/client.h"
#include "zone/mob.h"
#include <iostream>
#include <sstream>
@@ -61,7 +63,6 @@ namespace SoD
static inline spells::CastingSlot ServerToSoDCastingSlot(EQ::spells::CastingSlot slot);
static inline EQ::spells::CastingSlot SoDToServerCastingSlot(spells::CastingSlot slot);
static inline int ServerToSoDBuffSlot(int index);
static inline int SoDToServerBuffSlot(int index);
void Register(EQStreamIdentifier &into)
@@ -293,7 +294,7 @@ namespace SoD
dest->FastQueuePacket(&in, ack_req);
}
ENCODE(OP_Buff)
ENCODE(OP_BuffDefinition)
{
ENCODE_LENGTH_EXACT(SpellBuffPacket_Struct);
SETUP_DIRECT_ENCODE(SpellBuffPacket_Struct, structs::SpellBuffPacket_Struct);
@@ -306,7 +307,7 @@ namespace SoD
OUT(buff.duration);
OUT(buff.counters);
OUT(buff.player_id);
eq->slotid = ServerToSoDBuffSlot(emu->slotid);
OUT(slotid);
OUT(bufffade);
FINISH_ENCODE();
@@ -1375,38 +1376,6 @@ namespace SoD
FINISH_ENCODE();
}
ENCODE(OP_PetBuffWindow)
{
EQApplicationPacket *in = *p;
*p = nullptr;
unsigned char *__emu_buffer = in->pBuffer;
PetBuff_Struct *emu = (PetBuff_Struct *)__emu_buffer;
int PacketSize = 7 + (emu->buffcount * 13);
in->size = PacketSize;
in->pBuffer = new unsigned char[in->size];
char *Buffer = (char *)in->pBuffer;
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petid);
VARSTRUCT_ENCODE_TYPE(uint16, Buffer, emu->buffcount);
for (unsigned int i = 0; i < PET_BUFF_COUNT; ++i)
{
if (emu->spellid[i])
{
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, i);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spellid[i]);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->ticsremaining[i]);
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string. Name of the caster of the buff.
}
}
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->buffcount); // I think this is actually some sort of type
delete[] __emu_buffer;
dest->FastQueuePacket(&in, ack_req);
}
ENCODE(OP_PlayerProfile)
{
SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct);
@@ -2145,35 +2114,6 @@ namespace SoD
FINISH_ENCODE();
}
ENCODE(OP_TargetBuffs)
{
SETUP_VAR_ENCODE(BuffIcon_Struct);
uint32 sz = 7 + (13 * emu->count);
__packet->size = sz;
__packet->pBuffer = new unsigned char[sz];
memset(__packet->pBuffer, 0, sz);
uchar *ptr = __packet->pBuffer;
*((uint32*)ptr) = emu->entity_id;
ptr += sizeof(uint32);
*((uint16*)ptr) = emu->count;
ptr += sizeof(uint16);
for (uint16 i = 0; i < emu->count; ++i)
{
*((uint32*)ptr) = emu->entries[i].buff_slot;
ptr += sizeof(uint32);
*((uint32*)ptr) = emu->entries[i].spell_id;
ptr += sizeof(uint32);
*((uint32*)ptr) = emu->entries[i].tics_remaining;
ptr += sizeof(uint32);
ptr += 1;
}
FINISH_ENCODE();
}
ENCODE(OP_TaskDescription)
{
EQApplicationPacket *in = *p;
@@ -2877,7 +2817,7 @@ namespace SoD
FINISH_DIRECT_DECODE();
}
DECODE(OP_Buff)
DECODE(OP_BuffDefinition)
{
DECODE_LENGTH_EXACT(structs::SpellBuffPacket_Struct);
SETUP_DIRECT_DECODE(SpellBuffPacket_Struct, structs::SpellBuffPacket_Struct);
@@ -4259,19 +4199,6 @@ namespace SoD
}
}
static inline int ServerToSoDBuffSlot(int index)
{
// we're a disc
if (index >= EQ::spells::LONG_BUFFS + EQ::spells::SHORT_BUFFS)
return index - EQ::spells::LONG_BUFFS - EQ::spells::SHORT_BUFFS +
spells::LONG_BUFFS + spells::SHORT_BUFFS;
// we're a song
if (index >= EQ::spells::LONG_BUFFS)
return index - EQ::spells::LONG_BUFFS + spells::LONG_BUFFS;
// we're a normal buff
return index; // as long as we guard against bad slots server side, we should be fine
}
static inline int SoDToServerBuffSlot(int index)
{
// we're a disc
@@ -4284,4 +4211,55 @@ namespace SoD
// we're a normal buff
return index; // as long as we guard against bad slots server side, we should be fine
}
std::unique_ptr<EQApplicationPacket> BuffComponent::RefreshBuffs(EmuOpcode opcode, Mob* mob, bool remove,
bool buff_timers_suspended, const std::vector<uint32_t>& slots) const
{
if (opcode == OP_RefreshPetBuffs || opcode == OP_RefreshTargetBuffs) {
Buffs_Struct* buffs = mob->GetBuffs();
size_t buffer_size = 7; // 7 bytes outside the list
std::vector<uint32_t> send_slots;
if (slots.empty()) {
for (uint32_t slot = 0; slot < mob->GetMaxTotalSlots(); ++slot)
if (buffs[slot].spellid > 1) {
buffer_size += 13 + strlen(buffs[slot].caster_name); // 13 includes the null terminator
send_slots.push_back(slot);
}
} else {
for (uint32_t slot : slots)
if (slot < mob->GetMaxTotalSlots() && buffs[slot].spellid > 1) {
buffer_size += 13 + strlen(buffs[slot].caster_name);
send_slots.push_back(slot);
}
}
// SoD only supports target and pet refresh, not self refresh packets
SerializeBuffer buffer(buffer_size);
buffer.WriteUInt32(mob->GetID());
buffer.WriteUInt16(send_slots.size());
for (uint32_t slot : send_slots) {
buffer.WriteUInt32(ServerToPatchBuffSlot(slot));
buffer.WriteInt32(remove ? -1 : buffs[slot].spellid);
buffer.WriteInt32(buffs[slot].ticsremaining);
buffer.WriteString(buffs[slot].caster_name);
}
buffer.WriteUInt8(opcode == OP_RefreshPetBuffs ? 2 : 0);
return std::make_unique<EQApplicationPacket>(opcode, std::move(buffer));
}
return nullptr;
}
// 0 = self buff window, 1 = self target window, 2 = pet buff or target window, 4 = group, 5 = PC, 7 = NPC
void BuffComponent::SetRefreshType(std::unique_ptr<EQApplicationPacket>& packet, uint8_t refresh_type) const
{
if (packet)
packet->pBuffer[packet->size - 1] = refresh_type;
}
} /*SoD*/
+27 -33
View File
@@ -17,46 +17,40 @@
*/
#pragma once
#include "sof.h"
#include "common/struct_strategy.h"
#include "common/patches/sof.h"
class EQStreamIdentifier;
namespace SoD
{
namespace SoD {
//these are the only public member of this namespace.
extern void Register(EQStreamIdentifier &into);
extern void Reload();
extern void Register(EQStreamIdentifier& into);
extern void Reload();
//you should not directly access anything below..
//I just dont feel like making a seperate header for it.
class Strategy : public StructStrategy {
public:
Strategy();
protected:
virtual std::string Describe() const;
virtual const EQ::versions::ClientVersion ClientVersion() const;
//magic macro to declare our opcode processors
#include "ss_declare.h"
#include "sod_ops.h"
};
} /*SoD*/
namespace Message {
class SoD : public SoF
class Strategy : public StructStrategy
{
public:
SoD() = default;
~SoD() override = default;
Strategy();
protected:
virtual std::string Describe() const;
virtual const EQ::versions::ClientVersion ClientVersion() const;
//magic macro to declare our opcode processors
#include "ss_declare.h"
#include "sod_ops.h"
};
} // namespace Message
class BuffComponent : public Titanium::BuffComponent
{
public:
BuffComponent(uint32_t maxLongBuffs, uint32_t maxShortBuffs) : Titanium::BuffComponent(maxLongBuffs, maxShortBuffs) {}
BuffComponent() = delete;
~BuffComponent() override = default;
std::unique_ptr<EQApplicationPacket> RefreshBuffs(EmuOpcode opcode, Mob* mob, bool remove,
bool buff_timers_suspended, const std::vector<uint32_t>& slots) const override;
void SetRefreshType(std::unique_ptr<EQApplicationPacket>& packet, uint8_t refresh_type) const override;
};
} /*SoD*/
+2 -4
View File
@@ -23,7 +23,7 @@ E(OP_ApplyPoison)
E(OP_AugmentInfo)
E(OP_Barter)
E(OP_BazaarSearch)
E(OP_Buff)
E(OP_BuffDefinition)
E(OP_CancelTrade)
E(OP_ChannelMessage)
E(OP_CharInventory)
@@ -65,7 +65,6 @@ E(OP_MoveItem)
E(OP_NewSpawn)
E(OP_NewZone)
E(OP_OnLevelMessage)
E(OP_PetBuffWindow)
E(OP_PlayerProfile)
E(OP_RaidJoin)
E(OP_RaidUpdate)
@@ -80,7 +79,6 @@ E(OP_SomeItemPacketMaybe)
E(OP_SpawnDoor)
E(OP_SpecialMesg)
E(OP_Stun)
E(OP_TargetBuffs)
E(OP_TaskDescription)
E(OP_Track)
E(OP_Trader)
@@ -102,7 +100,7 @@ D(OP_AugmentInfo)
D(OP_AugmentItem)
D(OP_BazaarSearch)
D(OP_BookButton)
D(OP_Buff)
D(OP_BuffDefinition)
D(OP_CastSpell)
D(OP_ChannelMessage)
D(OP_CharacterCreate)
+9 -9
View File
@@ -480,7 +480,7 @@ struct NewZone_Struct {
*/
struct MemorizeSpell_Struct {
uint32 slot; // Spot in the spell book/memorized slot
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc)
int32 spell_id; // Spell id (200 or c8 is minor healing, etc)
uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming
uint32 reduction; // lowers reuse
};
@@ -517,7 +517,7 @@ struct ManaChange_Struct
{
/*00*/ uint32 new_mana; // New Mana AMount
/*04*/ uint32 stamina;
/*08*/ uint32 spell_id;
/*08*/ int32 spell_id;
/*12*/ uint8 keepcasting; // won't stop the cast. Change mana while casting?
/*13*/ uint8 padding[3]; // client doesn't read it, garbage data seems like
/*16*/ int32 slot; // -1 for normal usage slot for when we want silent interrupt? I think it does timer stuff or something. Linked Spell Reuse interrupt uses it
@@ -535,14 +535,14 @@ struct BeginCast_Struct
{
// len = 8
/*004*/ uint16 caster_id;
/*006*/ uint16 spell_id;
/*006*/ int16 spell_id;
/*016*/ uint32 cast_time; // in miliseconds
};
struct CastSpell_Struct
{
uint32 slot;
uint32 spell_id;
int32 spell_id;
uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast
uint32 target_id;
uint8 cs_unknown[4];
@@ -571,7 +571,7 @@ struct SpellBuff_Struct
/*001*/ uint8 level; // Seen 1 for no buff
/*002*/ uint8 bard_modifier;
/*003*/ uint8 unknown003; // MQ2 used to call this "damage shield" -- don't see client referencing it, so maybe server side DS type tracking?
/*004*/ uint32 spellid;
/*004*/ int32 spellid;
/*008*/ uint32 duration;
/*012*/ uint32 counters;
/*016*/ uint32 unknown016;
@@ -1266,7 +1266,7 @@ struct CombatDamage_Struct
/* 00 */ uint16 target;
/* 02 */ uint16 source;
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells
/* 05 */ uint16 spellid;
/* 05 */ int16 spellid;
/* 07 */ int32 damage;
/* 11 */ float force; // cd cc cc 3d
/* 15 */ float hit_heading; // see above notes in Action_Struct
@@ -1301,7 +1301,7 @@ struct Death_Struct
/*004*/ uint32 killer_id;
/*008*/ uint32 corpseid; // was corpseid
/*012*/ uint32 attack_skill; // was type
/*016*/ uint32 spell_id;
/*016*/ int32 spell_id;
/*020*/ uint32 bindzoneid; //bindzoneid?
/*024*/ uint32 damage;
/*028*/ uint32 unknown028;
@@ -2534,7 +2534,7 @@ struct Resurrect_Struct {
char your_name[64];
uint32 unknown88;
char rezzer_name[64];
uint32 spellid;
int32 spellid;
char corpse_name[64];
uint32 action;
/* 228 */
@@ -3787,7 +3787,7 @@ struct SendAA_Struct {
/*0037*/ uint32 prereq_skill; //is < 0, abs() is category #
/*0041*/ uint32 prereq_minpoints; //min points in the prereq
/*0045*/ uint32 type;
/*0049*/ uint32 spellid;
/*0049*/ int32 spellid;
/*0053*/ uint32 spell_type;
/*0057*/ uint32 spell_refresh;
/*0061*/ uint32 classes;
+4 -39
View File
@@ -60,7 +60,6 @@ namespace SoF
static inline spells::CastingSlot ServerToSoFCastingSlot(EQ::spells::CastingSlot slot);
static inline EQ::spells::CastingSlot SoFToServerCastingSlot(spells::CastingSlot slot, uint32 item_location);
static inline int ServerToSoFBuffSlot(int index);
static inline int SoFToServerBuffSlot(int index);
void Register(EQStreamIdentifier &into)
@@ -272,7 +271,7 @@ namespace SoF
FINISH_ENCODE();
}
ENCODE(OP_Buff)
ENCODE(OP_BuffDefinition)
{
ENCODE_LENGTH_EXACT(SpellBuffPacket_Struct);
SETUP_DIRECT_ENCODE(SpellBuffPacket_Struct, structs::SpellBuffPacket_Struct);
@@ -285,7 +284,7 @@ namespace SoF
OUT(buff.duration);
OUT(buff.counters);
OUT(buff.player_id);
eq->slotid = ServerToSoFBuffSlot(emu->slotid);
eq->slotid = SoFToServerBuffSlot(emu->slotid);
OUT(bufffade);
FINISH_ENCODE();
@@ -1049,28 +1048,6 @@ namespace SoF
FINISH_ENCODE();
}
ENCODE(OP_PetBuffWindow)
{
ENCODE_LENGTH_EXACT(PetBuff_Struct);
SETUP_DIRECT_ENCODE(PetBuff_Struct, PetBuff_Struct);
OUT(petid);
OUT(buffcount);
int EQBuffSlot = 0; // do we really want to shuffle them around like this?
for (uint32 EmuBuffSlot = 0; EmuBuffSlot < PET_BUFF_COUNT; ++EmuBuffSlot)
{
if (emu->spellid[EmuBuffSlot])
{
eq->spellid[EQBuffSlot] = emu->spellid[EmuBuffSlot];
eq->ticsremaining[EQBuffSlot++] = emu->ticsremaining[EmuBuffSlot];
}
}
FINISH_ENCODE();
}
ENCODE(OP_PlayerProfile)
{
SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct);
@@ -2321,7 +2298,7 @@ namespace SoF
FINISH_DIRECT_DECODE();
}
DECODE(OP_Buff)
DECODE(OP_BuffDefinition)
{
DECODE_LENGTH_EXACT(structs::SpellBuffPacket_Struct);
SETUP_DIRECT_DECODE(SpellBuffPacket_Struct, structs::SpellBuffPacket_Struct);
@@ -2334,7 +2311,7 @@ namespace SoF
IN(buff.duration);
IN(buff.counters);
IN(buff.player_id);
emu->slotid = SoFToServerBuffSlot(eq->slotid);
IN(slotid);
IN(bufffade);
FINISH_DIRECT_DECODE();
@@ -3657,18 +3634,6 @@ namespace SoF
}
}
static inline int ServerToSoFBuffSlot(int index) {
// we're a disc
if (index >= EQ::spells::LONG_BUFFS + EQ::spells::SHORT_BUFFS)
return index - EQ::spells::LONG_BUFFS - EQ::spells::SHORT_BUFFS +
spells::LONG_BUFFS + spells::SHORT_BUFFS;
// we're a song
if (index >= EQ::spells::LONG_BUFFS)
return index - EQ::spells::LONG_BUFFS + spells::LONG_BUFFS;
// we're a normal buff
return index; // as long as we guard against bad slots server side, we should be fine
}
static inline int SoFToServerBuffSlot(int index)
{
// we're a disc
+15 -33
View File
@@ -17,46 +17,28 @@
*/
#pragma once
#include "titanium.h"
#include "common/struct_strategy.h"
#include "common/patches/titanium.h"
class EQStreamIdentifier;
namespace SoF
{
namespace SoF {
//these are the only public member of this namespace.
extern void Register(EQStreamIdentifier &into);
extern void Reload();
extern void Register(EQStreamIdentifier& into);
extern void Reload();
//you should not directly access anything below..
//I just dont feel like making a seperate header for it.
class Strategy : public StructStrategy {
public:
Strategy();
protected:
virtual std::string Describe() const;
virtual const EQ::versions::ClientVersion ClientVersion() const;
//magic macro to declare our opcode processors
#include "ss_declare.h"
#include "sof_ops.h"
};
} /*SoF*/
namespace Message {
class SoF : public Titanium
class Strategy : public StructStrategy
{
public:
SoF() = default;
~SoF() override = default;
Strategy();
protected:
virtual std::string Describe() const;
virtual const EQ::versions::ClientVersion ClientVersion() const;
//magic macro to declare our opcode processors
#include "ss_declare.h"
#include "sof_ops.h"
};
} // namespace Message
} /*SoF*/
+2 -3
View File
@@ -23,7 +23,7 @@ E(OP_ApplyPoison)
E(OP_AugmentInfo)
E(OP_BazaarSearch)
E(OP_BecomeTrader)
E(OP_Buff)
E(OP_BuffDefinition)
E(OP_CancelTrade)
E(OP_ChannelMessage)
E(OP_CharInventory)
@@ -60,7 +60,6 @@ E(OP_MoveItem)
E(OP_NewSpawn)
E(OP_NewZone)
E(OP_OnLevelMessage)
E(OP_PetBuffWindow)
E(OP_PlayerProfile)
E(OP_RaidJoin)
E(OP_RaidUpdate)
@@ -93,7 +92,7 @@ D(OP_ApplyPoison)
D(OP_AugmentInfo)
D(OP_AugmentItem)
D(OP_BookButton)
D(OP_Buff)
D(OP_BuffDefinition)
D(OP_Bug)
D(OP_CastSpell)
D(OP_ChannelMessage)
+9 -9
View File
@@ -480,7 +480,7 @@ struct NewZone_Struct {
*/
struct MemorizeSpell_Struct {
uint32 slot; // Spot in the spell book/memorized slot
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc)
int32 spell_id; // Spell id (200 or c8 is minor healing, etc)
uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming
uint32 reduction; // lowers reuse
};
@@ -517,7 +517,7 @@ struct ManaChange_Struct
{
/*00*/ uint32 new_mana; // New Mana AMount
/*04*/ uint32 stamina;
/*08*/ uint32 spell_id;
/*08*/ int32 spell_id;
/*12*/ uint8 keepcasting; // won't stop the cast. Change mana while casting?
/*13*/ uint8 padding[3]; // client doesn't read it, garbage data seems like
/*16*/ int32 slot; // -1 for normal usage slot for when we want silent interrupt? I think it does timer stuff or something. Linked Spell Reuse interrupt uses it
@@ -535,14 +535,14 @@ struct BeginCast_Struct
{
// len = 8
/*004*/ uint16 caster_id;
/*006*/ uint16 spell_id;
/*006*/ int16 spell_id;
/*016*/ uint32 cast_time; // in miliseconds
};
struct CastSpell_Struct
{
uint32 slot;
uint32 spell_id;
int32 spell_id;
uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast
uint32 target_id;
uint8 cs_unknown[4];
@@ -571,7 +571,7 @@ struct SpellBuff_Struct
/*001*/ uint8 level; // Seen 1 for no buff
/*002*/ uint8 bard_modifier;
/*003*/ uint8 unknown003; // MQ2 used to call this "damage shield" -- don't see client referencing it, so maybe server side DS type tracking?
/*004*/ uint32 spellid;
/*004*/ int32 spellid;
/*008*/ uint32 duration;
/*012*/ uint32 counters;
/*016*/ uint32 unknown016;
@@ -1266,7 +1266,7 @@ struct CombatDamage_Struct
/* 00 */ uint16 target;
/* 02 */ uint16 source;
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells
/* 05 */ uint16 spellid;
/* 05 */ int16 spellid;
/* 07 */ int32 damage;
/* 11 */ float force; // cd cc cc 3d
/* 15 */ float hit_heading; // see above notes in Action_Struct
@@ -1301,7 +1301,7 @@ struct Death_Struct
/*004*/ uint32 killer_id;
/*008*/ uint32 corpseid; // was corpseid
/*012*/ uint32 attack_skill; // was type
/*016*/ uint32 spell_id;
/*016*/ int32 spell_id;
/*020*/ uint32 bindzoneid; //bindzoneid?
/*024*/ uint32 damage;
/*028*/ uint32 unknown028;
@@ -2504,7 +2504,7 @@ struct Resurrect_Struct {
char your_name[64];
uint32 unknown88;
char rezzer_name[64];
uint32 spellid;
int32 spellid;
char corpse_name[64];
uint32 action;
/* 228 */
@@ -3711,7 +3711,7 @@ struct SendAA_Struct {
/*0037*/ uint32 prereq_skill; //is < 0, abs() is category #
/*0041*/ uint32 prereq_minpoints; //min points in the prereq
/*0045*/ uint32 type;
/*0049*/ uint32 spellid;
/*0049*/ int32 spellid;
/*0053*/ uint32 spell_type;
/*0057*/ uint32 spell_refresh;
/*0061*/ uint32 classes;
+13 -20
View File
@@ -23,27 +23,20 @@ class EQStreamIdentifier;
namespace TEMPLATE {
//these are the only public member of this namespace.
extern void Register(EQStreamIdentifier &into);
extern void Reload();
extern void Register(EQStreamIdentifier& into);
extern void Reload();
class Strategy : public StructStrategy
{
public:
Strategy();
//you should not directly access anything below..
//I just dont feel like making a seperate header for it.
class Strategy : public StructStrategy {
public:
Strategy();
protected:
virtual std::string Describe() const;
virtual const EQClientVersion ClientVersion() const;
//magic macro to declare our opcodes
#include "ss_declare.h"
#include "TEMPLATE_ops.h"
};
protected:
virtual std::string Describe() const;
virtual const EQClientVersion ClientVersion() const;
//magic macro to declare our opcodes
#include "ss_declare.h"
#include "TEMPLATE_ops.h"
};
};
+98 -54
View File
@@ -32,6 +32,7 @@
#include "common/raid.h"
#include "common/rulesys.h"
#include "common/strings.h"
#include "zone/mob.h"
#include "zone/string_ids.h"
#include <sstream>
@@ -62,7 +63,6 @@ namespace Titanium
static inline spells::CastingSlot ServerToTitaniumCastingSlot(EQ::spells::CastingSlot slot);
static inline EQ::spells::CastingSlot TitaniumToServerCastingSlot(spells::CastingSlot slot, uint32 item_location);
static inline int ServerToTitaniumBuffSlot(int index);
static inline int TitaniumToServerBuffSlot(int index);
void Register(EQStreamIdentifier &into)
@@ -326,7 +326,7 @@ namespace Titanium
}
}
ENCODE(OP_Buff)
ENCODE(OP_BuffDefinition)
{
ENCODE_LENGTH_EXACT(SpellBuffPacket_Struct);
SETUP_DIRECT_ENCODE(SpellBuffPacket_Struct, structs::SpellBuffPacket_Struct);
@@ -339,7 +339,7 @@ namespace Titanium
OUT(buff.duration);
OUT(buff.counters);
OUT(buff.player_id);
eq->slotid = ServerToTitaniumBuffSlot(emu->slotid);
OUT(slotid);
OUT(bufffade);
FINISH_ENCODE();
@@ -1307,28 +1307,6 @@ namespace Titanium
FINISH_ENCODE();
}
ENCODE(OP_PetBuffWindow)
{
ENCODE_LENGTH_EXACT(PetBuff_Struct);
SETUP_DIRECT_ENCODE(PetBuff_Struct, PetBuff_Struct);
OUT(petid);
OUT(buffcount);
int EQBuffSlot = 0; // do we really want to shuffle them around like this?
for (uint32 EmuBuffSlot = 0; EmuBuffSlot < PET_BUFF_COUNT; ++EmuBuffSlot)
{
if (emu->spellid[EmuBuffSlot])
{
eq->spellid[EQBuffSlot] = emu->spellid[EmuBuffSlot];
eq->ticsremaining[EQBuffSlot++] = emu->ticsremaining[EmuBuffSlot];
}
}
FINISH_ENCODE();
}
ENCODE(OP_PlayerProfile)
{
SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct);
@@ -2540,7 +2518,7 @@ namespace Titanium
}
}
DECODE(OP_Buff)
DECODE(OP_BuffDefinition)
{
DECODE_LENGTH_EXACT(structs::SpellBuffPacket_Struct);
SETUP_DIRECT_DECODE(SpellBuffPacket_Struct, structs::SpellBuffPacket_Struct);
@@ -3894,19 +3872,6 @@ namespace Titanium
}
}
static inline int ServerToTitaniumBuffSlot(int index)
{
// we're a disc
if (index >= EQ::spells::LONG_BUFFS + EQ::spells::SHORT_BUFFS)
return index - EQ::spells::LONG_BUFFS - EQ::spells::SHORT_BUFFS +
spells::LONG_BUFFS + spells::SHORT_BUFFS;
// we're a song
if (index >= EQ::spells::LONG_BUFFS)
return index - EQ::spells::LONG_BUFFS + spells::LONG_BUFFS;
// we're a normal buff
return index; // as long as we guard against bad slots server side, we should be fine
}
static inline int TitaniumToServerBuffSlot(int index)
{
// we're a disc
@@ -3919,10 +3884,8 @@ namespace Titanium
// we're a normal buff
return index; // as long as we guard against bad slots server side, we should be fine
}
} /*Titanium*/
namespace Message {
std::unique_ptr<EQApplicationPacket> Titanium::Simple(uint32_t color, uint32_t id) const
std::unique_ptr<EQApplicationPacket> MessageComponent::Simple(uint32_t color, uint32_t id) const
{
uint32_t string_id = ResolveID(id);
if (string_id > 0) {
@@ -3938,8 +3901,8 @@ std::unique_ptr<EQApplicationPacket> Titanium::Simple(uint32_t color, uint32_t i
return nullptr;
}
std::unique_ptr<EQApplicationPacket> Titanium::Formatted(
uint32_t color, uint32_t id, const std::array<const char*, 9>& args) const
std::unique_ptr<EQApplicationPacket> MessageComponent::Formatted(uint32_t color, uint32_t id,
const FormattedArgs& args) const
{
uint32_t string_id = ResolveID(id);
if (string_id > 0) {
@@ -3966,7 +3929,7 @@ std::unique_ptr<EQApplicationPacket> Titanium::Formatted(
return nullptr;
}
std::unique_ptr<EQApplicationPacket> Titanium::InterruptSpell(uint32_t message, uint32_t spawn_id,
std::unique_ptr<EQApplicationPacket> MessageComponent::InterruptSpell(uint32_t message, uint32_t spawn_id,
const char* spell_link) const
{
auto outapp = std::make_unique<EQApplicationPacket>(OP_InterruptCast, sizeof(InterruptCast_Struct));
@@ -3978,40 +3941,121 @@ std::unique_ptr<EQApplicationPacket> Titanium::InterruptSpell(uint32_t message,
return outapp;
}
std::unique_ptr<EQApplicationPacket> Titanium::InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id,
const char* name,
const char* spell_link) const
std::unique_ptr<EQApplicationPacket> MessageComponent::InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id,
const char* name, const char* spell_link) const
{
auto outapp = std::make_unique<EQApplicationPacket>(OP_InterruptCast, sizeof(InterruptCast_Struct) + strlen(name) + 1);
auto ic = reinterpret_cast<InterruptCast_Struct*>(outapp->pBuffer);
ic->messageid = ResolveID(message);
ic->spawnid = spawn_id;
fmt::format_to_n(ic->message, strlen(name) + 1, "{}\0", name);
strcpy(ic->message, spell_link);
return outapp;
}
// A value of 0 means that the string isn't mapped in this client, valid string ids start at 1
uint32_t Titanium::ResolveID(uint32_t id) const
uint32_t MessageComponent::ResolveID(uint32_t id) const
{
// passthrough — string IDs are defined at the base client level;
// override in patches where IDs need remapping
return id;
}
void Titanium::ResolveArguments(uint32_t id, std::array<const char*, 9>& args) const
void MessageComponent::ResolveArguments(uint32_t id, std::array<const char*, 9>& args) const
{
switch (id) {
case SPELL_FIZZLE:
case MISS_NOTE:
args[0] = nullptr; // drop spell link
args[0] = nullptr; // the 0th (and only) argument here is the spell link, not supported before TOB
break;
case SPELL_FIZZLE_OTHER:
case MISSED_NOTE_OTHER:
args[1] = nullptr; // drop spell link
args[1] = nullptr; // the 1st argument here is the spell link, not supported before TOB
break;
default:
break;
}
}
} // namespace Message
std::unique_ptr<EQApplicationPacket> BuffComponent::BuffDefinition(Mob* mob, const Buffs_Struct& buff, uint32_t slot,
bool fade) const
{
auto outapp = std::make_unique<EQApplicationPacket>(OP_BuffDefinition, sizeof(SpellBuffPacket_Struct));
auto sbf = reinterpret_cast<SpellBuffPacket_Struct*>(outapp->pBuffer);
sbf->entityid = mob->GetID();
sbf->buff.effect_type = 2;
sbf->buff.level = buff.casterlevel > 0 ? buff.casterlevel : mob->GetLevel();
sbf->buff.bard_modifier = buff.instrument_mod;
sbf->buff.spellid = buff.spellid;
sbf->buff.duration = buff.ticsremaining;
if (buff.dot_rune)
sbf->buff.counters = buff.dot_rune;
else if (buff.magic_rune)
sbf->buff.counters = buff.magic_rune;
else if (buff.melee_rune)
sbf->buff.counters = buff.melee_rune;
else if (buff.counters)
sbf->buff.counters = buff.counters;
sbf->buff.player_id = buff.casterid;
sbf->buff.num_hits = buff.hit_number;
sbf->buff.y = buff.caston_y;
sbf->buff.x = buff.caston_x;
sbf->buff.z = buff.caston_z;
sbf->slotid = ServerToPatchBuffSlot(slot);
sbf->bufffade = fade;
return outapp;
}
std::unique_ptr<EQApplicationPacket> BuffComponent::RefreshBuffs(EmuOpcode opcode, Mob* mob, bool remove,
bool buff_timers_suspended, const std::vector<uint32_t>& slots) const
{
// titanium only sends refresh for pet buffs
if (opcode == OP_RefreshPetBuffs) {
Buffs_Struct* buffs = mob->GetBuffs();
std::vector<uint32_t> send_slots;
if (slots.empty()) {
for (uint32_t slot = 0; slot < mob->GetMaxTotalSlots(); ++slot)
if (buffs[slot].spellid > 1)
send_slots.push_back(slot);
} else {
for (uint32_t slot : slots)
if (slot < mob->GetMaxTotalSlots() && buffs[slot].spellid > 1)
send_slots.push_back(slot);
}
auto outapp = std::make_unique<EQApplicationPacket>(OP_RefreshPetBuffs, sizeof(PetBuff_Struct));
auto pbs = reinterpret_cast<PetBuff_Struct*>(outapp->pBuffer);
memset(outapp->pBuffer, 0, outapp->size);
pbs->petid = mob->GetID();
int MaxSlots = mob->GetMaxTotalSlots();
if (MaxSlots > PET_BUFF_COUNT)
MaxSlots = PET_BUFF_COUNT;
int count = 0;
for (uint32_t slot : send_slots) {
if (slot < MaxSlots) {
pbs->spellid[slot] = buffs[slot].spellid;
pbs->ticsremaining[slot] = buffs[slot].ticsremaining;
++count;
}
}
pbs->buffcount = count;
return outapp;
}
return nullptr;
}
bool BuffComponent::NeedsWearMessage() const { return true; }
void BuffComponent::SetRefreshType(std::unique_ptr<EQApplicationPacket>& packet, uint8_t refresh_type) const {}
} /*Titanium*/
+39 -35
View File
@@ -17,51 +17,40 @@
*/
#pragma once
#include "IMessage.h"
#include "common/struct_strategy.h"
#include "common/patches/IBuff.h"
#include "common/patches/IMessage.h"
class EQStreamIdentifier;
namespace Titanium
{
namespace Titanium {
//these are the only public member of this namespace.
extern void Register(EQStreamIdentifier &into);
extern void Reload();
extern void Register(EQStreamIdentifier& into);
extern void Reload();
//you should not directly access anything below..
//I just dont feel like making a seperate header for it.
class Strategy : public StructStrategy {
public:
Strategy();
protected:
virtual std::string Describe() const;
virtual const EQ::versions::ClientVersion ClientVersion() const;
//magic macro to declare our opcode processors
#include "ss_declare.h"
#include "titanium_ops.h"
};
} /*Titanium*/
// out-going message packets
namespace Message {
class Titanium : public IMessage
class Strategy : public StructStrategy
{
public:
Titanium() = default;
~Titanium() override = default;
Strategy();
protected:
virtual std::string Describe() const;
virtual const EQ::versions::ClientVersion ClientVersion() const;
//magic macro to declare our opcode processors
#include "ss_declare.h"
#include "titanium_ops.h"
};
class MessageComponent : public ClientPatch::IMessage
{
public:
MessageComponent() = default;
~MessageComponent() override = default;
std::unique_ptr<EQApplicationPacket> Simple(uint32_t color, uint32_t id) const override;
std::unique_ptr<EQApplicationPacket> Formatted(uint32_t color, uint32_t id,
const std::array<const char*, 9>& args) const override;
const FormattedArgs& args) const override;
std::unique_ptr<EQApplicationPacket> InterruptSpell(uint32_t message, uint32_t spawn_id,
const char* spell_link) const override;
@@ -74,5 +63,20 @@ protected:
virtual void ResolveArguments(uint32_t id, std::array<const char*, 9>& args) const;
};
} // namespace Message
class BuffComponent : public ClientPatch::IBuff
{
public:
BuffComponent(uint32_t maxLongBuffs, uint32_t maxShortBuffs) : IBuff(maxLongBuffs, maxShortBuffs) {}
BuffComponent() = delete;
~BuffComponent() override = default;
std::unique_ptr<EQApplicationPacket> BuffDefinition(Mob* mob, const Buffs_Struct& buff, uint32_t slot,
bool fade) const override;
std::unique_ptr<EQApplicationPacket> RefreshBuffs(EmuOpcode opcode, Mob* mob, bool remove,
bool buff_timers_suspended, const std::vector<uint32_t>& slots) const override;
bool NeedsWearMessage() const override;
void SetRefreshType(std::unique_ptr<EQApplicationPacket>& packet, uint8_t refresh_type) const override;
};
} /*Titanium*/
+2 -3
View File
@@ -21,7 +21,7 @@ E(OP_AdventureMerchantSell)
E(OP_ApplyPoison)
E(OP_BazaarSearch)
E(OP_BecomeTrader)
E(OP_Buff)
E(OP_BuffDefinition)
E(OP_ChannelMessage)
E(OP_CharInventory)
E(OP_ClientUpdate)
@@ -61,7 +61,6 @@ E(OP_ManaChange)
E(OP_MemorizeSpell)
E(OP_MoveItem)
E(OP_OnLevelMessage)
E(OP_PetBuffWindow)
E(OP_PlayerProfile)
E(OP_NewSpawn)
E(OP_MarkRaidNPC)
@@ -91,7 +90,7 @@ D(OP_AdventureMerchantSell)
D(OP_ApplyPoison)
D(OP_AugmentItem)
D(OP_BazaarSearch)
D(OP_Buff)
D(OP_BuffDefinition)
D(OP_Bug)
D(OP_CastSpell)
D(OP_ChannelMessage)
+9 -9
View File
@@ -406,7 +406,7 @@ struct NewZone_Struct {
*/
struct MemorizeSpell_Struct {
uint32 slot; // Spot in the spell book/memorized slot
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc)
int32 spell_id; // Spell id (200 or c8 is minor healing, etc)
uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming
uint32 reduction; // lowers reuse
};
@@ -442,7 +442,7 @@ struct ManaChange_Struct
{
/*00*/ uint32 new_mana; // New Mana AMount
/*04*/ uint32 stamina;
/*08*/ uint32 spell_id;
/*08*/ int32 spell_id;
/*12*/ uint8 keepcasting; // won't stop the cast. Change mana while casting?
/*13*/ uint8 padding[3]; // client doesn't read it, garbage data seems like
};
@@ -459,14 +459,14 @@ struct BeginCast_Struct
{
// len = 8
/*000*/ uint16 caster_id;
/*002*/ uint16 spell_id;
/*002*/ int16 spell_id;
/*004*/ uint32 cast_time; // in miliseconds
};
struct CastSpell_Struct
{
uint32 slot;
uint32 spell_id;
int32 spell_id;
uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast
uint32 target_id;
uint8 cs_unknown[4];
@@ -495,7 +495,7 @@ struct SpellBuff_Struct
/*001*/ uint8 level;
/*002*/ uint8 bard_modifier;
/*003*/ uint8 unknown003; // MQ2 used to call this "damage shield" -- don't see client referencing it, so maybe server side DS type tracking?
/*004*/ uint32 spellid;
/*004*/ int32 spellid;
/*008*/ int32 duration;
/*012*/ uint32 counters; // single book keeping value (counters, rune/vie)
/*016*/ uint32 player_id; // caster ID, pretty sure just zone ID
@@ -1142,7 +1142,7 @@ struct CombatDamage_Struct
/* 00 */ uint16 target;
/* 02 */ uint16 source;
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells, skill
/* 05 */ uint16 spellid;
/* 05 */ int16 spellid;
/* 07 */ uint32 damage;
/* 11 */ float force;
/* 15 */ float hit_heading; // see above notes in Action_Struct
@@ -1177,7 +1177,7 @@ struct Death_Struct
/*004*/ uint32 killer_id;
/*008*/ uint32 corpseid; // was corpseid
/*012*/ uint32 attack_skill; // was type
/*016*/ uint32 spell_id;
/*016*/ int32 spell_id;
/*020*/ uint32 bindzoneid; //bindzoneid?
/*024*/ uint32 damage;
/*028*/ uint32 unknown028;
@@ -2233,7 +2233,7 @@ struct Resurrect_Struct {
char your_name[64];
uint32 unknown88;
char rezzer_name[64];
uint32 spellid;
int32 spellid;
char corpse_name[64];
uint32 action;
/* 228 */
@@ -3247,7 +3247,7 @@ struct SendAA_Struct {
/*0040*/ uint32 prereq_skill; //is < 0, abs() is category #
/*0044*/ uint32 prereq_minpoints; //min points in the prereq
/*0048*/ uint32 type;
/*0052*/ uint32 spellid;
/*0052*/ int32 spellid;
/*0056*/ uint32 spell_type;
/*0060*/ uint32 spell_refresh;
/*0064*/ uint32 classes;
+172 -136
View File
@@ -1,22 +1,46 @@
#include "../global_define.h"
#include "../eqemu_config.h"
#include "../eqemu_logsys.h"
/* EQEmu: EQEmulator
Copyright (C) 2001-2026 EQEmu Development Team
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 3 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 "tob.h"
#include "../opcodemgr.h"
#include "../eq_stream_ident.h"
#include "../crc32.h"
#include "../eq_packet_structs.h"
#include "../misc_functions.h"
#include "../strings.h"
#include "../inventory_profile.h"
#include "tob_structs.h"
#include "../rulesys.h"
#include "../path_manager.h"
#include "../classes.h"
#include "../races.h"
#include "../raid.h"
#include "common/global_define.h"
#include "common/eqemu_config.h"
#include "common/eqemu_logsys.h"
#include "common/opcodemgr.h"
#include "common/eq_stream_ident.h"
#include "common/crc32.h"
#include "common/eq_packet_structs.h"
#include "common/misc_functions.h"
#include "common/strings.h"
#include "common/inventory_profile.h"
#include "common/rulesys.h"
#include "common/path_manager.h"
#include "common/classes.h"
#include "common/packet_dump.h"
#include "common/races.h"
#include "common/raid.h"
#include "world/sof_char_create_data.h"
#include "zone/client.h"
#include "zone/mob.h"
#include "zone/string_ids.h"
#include <iostream>
#include <sstream>
@@ -25,10 +49,6 @@
#include <cinttypes>
#include <set>
#include "common/packet_dump.h"
#include "world/sof_char_create_data.h"
#include "zone/string_ids.h"
namespace TOB
{
static const char* name = "TOB";
@@ -67,7 +87,6 @@ namespace TOB
static inline EQ::spells::CastingSlot TOBToServerCastingSlot(spells::CastingSlot slot);
// buff slots
static inline int ServerToTOBBuffSlot(int index);
static inline int TOBToServerBuffSlot(int index);
void Register(EQStreamIdentifier& into)
@@ -254,92 +273,6 @@ namespace TOB
FINISH_ENCODE();
}
ENCODE(OP_Buff)
{
ENCODE_LENGTH_EXACT(SpellBuffPacket_Struct);
SETUP_DIRECT_ENCODE(SpellBuffPacket_Struct, structs::EQAffectPacket_Struct);
eq->entity_id = emu->entityid;
eq->unknown004 = 0;
//fill in affect info
eq->affect.caster_id.Id = emu->buff.player_id;
eq->affect.flags = 0;
eq->affect.spell_id = emu->buff.spellid;
eq->affect.duration = emu->buff.duration;
eq->affect.initial_duration = emu->buff.duration;
eq->affect.hit_count = emu->buff.num_hits;
eq->affect.viral_timer = 0;
eq->affect.modifier = emu->buff.bard_modifier == 10 ? 1.0f : emu->buff.bard_modifier / 10.0f;
eq->affect.y = emu->buff.y;
eq->affect.x = emu->buff.x;
eq->affect.z = emu->buff.z;
eq->affect.level = emu->buff.level;
eq->slot_id = ServerToTOBBuffSlot(emu->slotid);
if (emu->bufffade == 1)
{
eq->buff_fade = 1;
}
else
{
eq->buff_fade = 2;
}
EQApplicationPacket* outapp = nullptr;
if (emu->bufffade == 1)
{
// Bit of a hack. OP_Buff appears to add/remove the buff while OP_BuffCreate adds/removes the actual buff icon
outapp = new EQApplicationPacket(OP_BuffCreate, 30);
outapp->WriteUInt32(emu->entityid);
outapp->WriteUInt32(0); // tic timer
outapp->WriteUInt8(0); // Type of OP_BuffCreate packet ?
outapp->WriteUInt16(1); // 1 buff in this packet
outapp->WriteUInt32(ServerToTOBBuffSlot(emu->slotid));
outapp->WriteUInt32(0xffffffff); // SpellID (0xffff to remove)
outapp->WriteUInt32(0); // Duration
outapp->WriteUInt32(0); // numhits
outapp->WriteUInt8(0); // Caster name
outapp->WriteUInt8(0); // Type
outapp->WriteUInt8(0); // Type
}
FINISH_ENCODE();
if (outapp) {
dest->FastQueuePacket(&outapp);
}
}
ENCODE(OP_BuffCreate)
{
SETUP_VAR_ENCODE(BuffIcon_Struct);
//TOB has one extra 0x00 byte before the end byte
uint32 sz = 13 + (17 * emu->count) + emu->name_lengths; // 17 includes nullterm
__packet->size = sz;
__packet->pBuffer = new unsigned char[sz];
memset(__packet->pBuffer, 0, sz);
__packet->WriteUInt32(emu->entity_id);
__packet->WriteUInt32(emu->tic_timer);
__packet->WriteUInt8(emu->all_buffs); // 1 indicates all buffs on the player (0 to add or remove a single buff)
__packet->WriteUInt16(emu->count);
for (int i = 0; i < emu->count; ++i)
{
__packet->WriteUInt32(emu->type == 0 ? ServerToTOBBuffSlot(emu->entries[i].buff_slot) : emu->entries[i].buff_slot);
__packet->WriteUInt32(emu->entries[i].spell_id);
__packet->WriteUInt32(emu->entries[i].tics_remaining);
__packet->WriteUInt32(emu->entries[i].num_hits); // Unknown
__packet->WriteString(emu->entries[i].caster);
}
__packet->WriteUInt8(0); // Unknown1
__packet->WriteUInt8(emu->type); // Unknown2
FINISH_ENCODE();
}
ENCODE(OP_CancelTrade)
{
ENCODE_LENGTH_EXACT(CancelTrade_Struct);
@@ -3603,6 +3536,20 @@ namespace TOB
emu->Initialise = init;
}
DECODE(OP_BuffRemoveRequest)
{
// This is to cater for the fact that short buff box buffs start at 30 as opposed to 25 in prior clients.
//
DECODE_LENGTH_EXACT(BuffRemoveRequest_Struct);
SETUP_DIRECT_DECODE(BuffRemoveRequest_Struct, BuffRemoveRequest_Struct);
emu->SlotID = TOBToServerBuffSlot(eq->SlotID);
IN(EntityID);
FINISH_DIRECT_DECODE();
}
DECODE(OP_CastSpell)
{
DECODE_LENGTH_EXACT(structs::CastSpell_Struct);
@@ -5536,20 +5483,6 @@ namespace TOB
}
}
//TOB has the same # of long buffs as rof2, but 10 more short buffs
static inline int ServerToTOBBuffSlot(int index)
{
// we're a disc
if (index >= EQ::spells::LONG_BUFFS + EQ::spells::SHORT_BUFFS)
return index - EQ::spells::LONG_BUFFS - EQ::spells::SHORT_BUFFS +
spells::LONG_BUFFS + spells::SHORT_BUFFS;
// we're a song
if (index >= EQ::spells::LONG_BUFFS)
return index - EQ::spells::LONG_BUFFS + spells::LONG_BUFFS;
// we're a normal buff
return index; // as long as we guard against bad slots server side, we should be fine
}
static inline int TOBToServerBuffSlot(int index)
{
// we're a disc
@@ -5562,16 +5495,13 @@ namespace TOB
// we're a normal buff
return index; // as long as we guard against bad slots server side, we should be fine
}
} /*TOB*/
namespace Message {
struct TOBStringIDs
{
static constexpr uint32_t DisarmedTrap = 1458; // You successfully disarmed the trap
};
uint32_t TOB::ResolveID(uint32_t id) const
uint32_t MessageComponent::ResolveID(uint32_t id) const
{
switch (id) {
case YOU_FLURRY:
@@ -5615,11 +5545,11 @@ uint32_t TOB::ResolveID(uint32_t id) const
case DISARMED_TRAP:
return TOBStringIDs::DisarmedTrap;
default:
return RoF2::ResolveID(id);
return Titanium::MessageComponent::ResolveID(id);
}
}
void TOB::ResolveArguments(uint32_t id, std::array<const char*, 9>& args) const
void MessageComponent::ResolveArguments(uint32_t id, std::array<const char*, 9>& args) const
{
switch (id) {
case SPELL_FIZZLE:
@@ -5629,13 +5559,13 @@ void TOB::ResolveArguments(uint32_t id, std::array<const char*, 9>& args) const
// take all arguments (spell link)
break;
default:
RoF2::ResolveArguments(id, args);
Titanium::MessageComponent::ResolveArguments(id, args);
break;
}
}
std::unique_ptr<EQApplicationPacket> TOB::Formatted(uint32_t color, uint32_t id,
const std::array<const char*, 9>& args) const
std::unique_ptr<EQApplicationPacket> MessageComponent::Formatted(uint32_t color, uint32_t id,
const FormattedArgs& args) const
{
uint32_t string_id = ResolveID(id);
if (string_id > 0) {
@@ -5667,20 +5597,20 @@ std::unique_ptr<EQApplicationPacket> TOB::Formatted(uint32_t color, uint32_t id,
return nullptr;
}
std::unique_ptr<EQApplicationPacket> TOB::InterruptSpell(uint32_t message, uint32_t spawn_id,
std::unique_ptr<EQApplicationPacket> MessageComponent::InterruptSpell(uint32_t message, uint32_t spawn_id,
const char* spell_link) const
{
auto outapp = std::make_unique<EQApplicationPacket>(OP_InterruptCast, sizeof(InterruptCast_Struct) + strlen(spell_link) + 1);
auto ic = reinterpret_cast<InterruptCast_Struct*>(outapp->pBuffer);
ic->messageid = ResolveID(message);
ic->spawnid = spawn_id;
fmt::format_to_n(ic->message, strlen(spell_link) + 1, "{}\0", spell_link);
strcpy(ic->message, spell_link);
outapp->priority = 5;
return outapp;
}
std::unique_ptr<EQApplicationPacket> TOB::InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id,
std::unique_ptr<EQApplicationPacket> MessageComponent::InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id,
const char* name,
const char* spell_link) const
{
@@ -5689,10 +5619,116 @@ std::unique_ptr<EQApplicationPacket> TOB::InterruptSpellOther(Mob* sender, uint3
auto ic = reinterpret_cast<InterruptCast_Struct*>(outapp->pBuffer);
ic->messageid = ResolveID(message);
ic->spawnid = spawn_id;
fmt::format_to_n(ic->message, strlen(name) + strlen(spell_link) + 2, "{}\0{}\0", name, spell_link);
strcpy(ic->message, name);
strcpy(&ic->message[strlen(name) + 1], spell_link);
return outapp;
}
} // namespace Message
std::unique_ptr<EQApplicationPacket> BuffComponent::BuffDefinition(Mob* mob, const Buffs_Struct& buff, uint32_t slot, bool fade) const
{
auto packet = std::make_unique<EQApplicationPacket>(OP_BuffDefinition, sizeof(structs::EQAffectPacket_Struct));
auto affect = reinterpret_cast<structs::EQAffectPacket_Struct*>(packet->pBuffer);
// base packet
affect->entity_id = mob->GetID();
affect->unknown004 = 0;
affect->slot_id = ServerToPatchBuffSlot(slot);
affect->buff_fade = fade ? 1 : 2; // 1 is remove, 2 is modify, 3 is add (only seen 1 and 2 sent)
memset(&affect->affect, 0, sizeof(affect->affect));
// affect slots
for (int affect_slot = 0; affect_slot < 6; ++affect_slot) {
// all of this is unknown, just what we've seen
affect->affect.slots[affect_slot].slot = -1; // this is always -1
affect->affect.slots[affect_slot].padding = 0; // this is never 0, but the values aren't clear
affect->affect.slots[affect_slot].value = 0; // this is always 0
}
// affect info
affect->affect.caster_id.Id = buff.casterid;
affect->affect.caster_id.WorldId = RuleI(World, Id);
affect->affect.caster_id.Reserved = 0;
affect->affect.flags = 0;
affect->affect.spell_id = buff.spellid;
affect->affect.duration = buff.ticsremaining;
affect->affect.initial_duration = buff.initialduration;
affect->affect.hit_count = buff.hit_number;
affect->affect.viral_timer = 0;
affect->affect.modifier = static_cast<float>(buff.instrument_mod) / 10.f;
affect->affect.y = static_cast<float>(buff.caston_y);
affect->affect.x = static_cast<float>(buff.caston_x);
affect->affect.z = static_cast<float>(buff.caston_z);
affect->affect.type = 2;
affect->affect.level = buff.casterlevel > 0 ? buff.casterlevel : mob->GetLevel();
//no idea if these are right; eqlib doesn't seem to know either
if (buff.dot_rune > 0)
affect->affect.charges = buff.dot_rune;
else if (buff.magic_rune > 0)
affect->affect.charges = buff.magic_rune;
else if (buff.melee_rune > 0)
affect->affect.charges = buff.melee_rune;
else if (buff.counters > 0)
affect->affect.charges = buff.counters;
affect->affect.activatable = 0;
affect->affect.unknown1 = 0; //might be some timer, not sure though
return packet;
}
std::unique_ptr<EQApplicationPacket> BuffComponent::RefreshBuffs(EmuOpcode opcode, Mob* mob, bool remove,
bool buff_timers_suspended, const std::vector<uint32_t>& slots) const
{
Buffs_Struct* buffs = mob->GetBuffs();
// pre-calculate the buffer size to avoid too many grow calls
size_t buffer_size = 13; // 13 bytes outside the list
std::vector<uint32_t> send_slots;
if (slots.empty()) {
for (uint32_t slot = 0; slot < mob->GetMaxTotalSlots(); ++slot)
if (buffs[slot].spellid > 1) {
buffer_size += 17 + strlen(buffs[slot].caster_name); // 17 includes the null terminator
send_slots.push_back(slot);
}
} else {
for (uint32_t slot : slots)
if (slot < mob->GetMaxTotalSlots() && buffs[slot].spellid > 1) {
buffer_size += 17 + strlen(buffs[slot].caster_name);
send_slots.push_back(slot);
}
}
SerializeBuffer buffer(buffer_size);
buffer.WriteUInt32(mob->GetID());
buffer.WriteInt32(mob->GetRemainingTicTime());
buffer.WriteUInt8(slots.empty() ? 1 : 0); // 1 indicates all buffs on the mob (0 to add or remove a single buff)
buffer.WriteUInt16(send_slots.size());
for (uint32_t slot : send_slots) {
buffer.WriteUInt32(ServerToPatchBuffSlot(slot)); // the server stores fewer buffs
buffer.WriteInt32(remove ? -1 : buffs[slot].spellid);
buffer.WriteUInt32(buffs[slot].ticsremaining);
buffer.WriteUInt32(buffs[slot].hit_number);
buffer.WriteString(buffs[slot].caster_name);
}
buffer.WriteUInt8(opcode == OP_RefreshPetBuffs ? 2 : 0);
buffer.WriteUInt8(buff_timers_suspended ? 1 : 0); // bBuffTimersOnHold
return std::make_unique<EQApplicationPacket>(opcode, std::move(buffer));
}
bool BuffComponent::NeedsWearMessage() const { return false; }
// 0 = self buff window, 1 = self target window, 2 = pet buff or target window, 4 = group, 5 = PC, 7 = NPC
void BuffComponent::SetRefreshType(std::unique_ptr<EQApplicationPacket>& packet, uint8_t refresh_type) const
{
if (packet)
packet->pBuffer[packet->size - 2] = refresh_type;
}
} /*TOB*/
+57 -36
View File
@@ -1,49 +1,55 @@
/* EQEmu: EQEmulator
Copyright (C) 2001-2026 EQEmu Development Team
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 3 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/>.
*/
#pragma once
#include "rof2.h"
#include "../struct_strategy.h"
#include "common/struct_strategy.h"
#include "common/patches/rof2.h"
class EQStreamIdentifier;
namespace TOB
{
namespace TOB {
//these are the only public member of this namespace.
extern void Register(EQStreamIdentifier& into);
extern void Reload();
extern void Register(EQStreamIdentifier& into);
extern void Reload();
//you should not directly access anything below..
//I just dont feel like making a seperate header for it.
class Strategy : public StructStrategy {
public:
Strategy();
protected:
virtual std::string Describe() const;
virtual const EQ::versions::ClientVersion ClientVersion() const;
//magic macro to declare our opcode processors
#include "ss_declare.h"
#include "tob_ops.h"
};
}; /*TOB*/
namespace Message {
class TOB : public RoF2
class Strategy : public StructStrategy
{
public:
TOB() {}
~TOB() override {}
Strategy();
protected:
virtual std::string Describe() const;
virtual const EQ::versions::ClientVersion ClientVersion() const;
//magic macro to declare our opcode processors
#include "ss_declare.h"
#include "tob_ops.h"
};
class MessageComponent : public Titanium::MessageComponent
{
public:
MessageComponent() = default;
~MessageComponent() override = default;
std::unique_ptr<EQApplicationPacket> Formatted(uint32_t color, uint32_t id,
const std::array<const char*, 9>& args) const override;
const FormattedArgs& args) const override;
std::unique_ptr<EQApplicationPacket> InterruptSpell(uint32_t message, uint32_t spawn_id,
const char* spell_link) const override;
@@ -55,4 +61,19 @@ protected:
void ResolveArguments(uint32_t id, std::array<const char*, 9>& args) const override;
};
} // namespace Message
class BuffComponent : public UF::BuffComponent
{
public:
BuffComponent(uint32_t maxLongBuffs, uint32_t maxShortBuffs) : UF::BuffComponent(maxLongBuffs, maxShortBuffs) {}
BuffComponent() = delete;
~BuffComponent() override = default;
std::unique_ptr<EQApplicationPacket>
BuffDefinition(Mob* mob, const Buffs_Struct& buff, uint32_t slot, bool fade) const override;
std::unique_ptr<EQApplicationPacket> RefreshBuffs(EmuOpcode opcode, Mob* mob, bool remove,
bool buff_timers_suspended, const std::vector<uint32_t>& slots) const override;
bool NeedsWearMessage() const override;
void SetRefreshType(std::unique_ptr<EQApplicationPacket>& packet, uint8_t refresh_type) const override;
};
}; /*TOB*/
+3 -3
View File
@@ -1,9 +1,9 @@
#ifndef COMMON_LAURION_LIMITS_H
#define COMMON_LAURION_LIMITS_H
#include "../types.h"
#include "../emu_versions.h"
#include "../skills.h"
#include "common/types.h"
#include "common/emu_versions.h"
#include "common/skills.h"
namespace TOB
{
+1 -2
View File
@@ -6,8 +6,6 @@ E(OP_ApplyPoison)
E(OP_AugmentInfo)
E(OP_BeginCast)
E(OP_BlockedBuffs)
E(OP_Buff)
E(OP_BuffCreate)
E(OP_CancelTrade)
E(OP_CastSpell)
E(OP_ChannelMessage)
@@ -71,6 +69,7 @@ D(OP_ApproveName)
D(OP_AugmentInfo)
D(OP_AugmentItem)
D(OP_BlockedBuffs)
D(OP_BuffRemoveRequest)
D(OP_CastSpell)
D(OP_ChannelMessage)
D(OP_CharacterCreate)
+29 -11
View File
@@ -1,5 +1,25 @@
#ifndef STEAM_LATEST_STRUCTS_H_
#define STEAM_LATEST_STRUCTS_H_
/* EQEmu: EQEmulator
Copyright (C) 2001-2026 EQEmu Development Team
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 3 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/>.
*/
#pragma once
#include "common/eq_packet_structs.h"
#include "common/skills.h"
namespace TOB {
namespace structs {
@@ -627,7 +647,7 @@ namespace TOB {
/*004*/ uint32 killer_id;
/*008*/ uint32 corpseid; //not read by client
/*012*/ uint32 unknown1; //not read by client
/*016*/ uint32 spell_id;
/*016*/ int32 spell_id;
/*020*/ uint32 attack_skill;
/*024*/ uint64 damage;
/*032*/ uint32 unknown2; //not read by client
@@ -672,7 +692,7 @@ namespace TOB {
struct BeginCast_Struct
{
/*000*/ uint32 spell_id;
/*000*/ int32 spell_id;
/*004*/ uint16 caster_id;
/*006*/ uint32 cast_time; // in miliseconds
/*010*/ uint32 unknown0a; // I think this is caster effective level but im not sure. live always sends 0. The client uses this for the spell link
@@ -682,7 +702,7 @@ namespace TOB {
struct MemorizeSpell_Struct {
uint32 slot; // Spot in the spell book/memorized slot
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc)
int32 spell_id; // Spell id (200 or c8 is minor healing, etc)
uint32 scribing; // -1 refreshes book, 0 scribe to book, 2 end mem, 1 start mem, 3 unmem, 4 set activated item keyring -- client will send back 2 if a 0 operation updated a memorized spell of the same group + subgroup
uint32 reduction; // lower reuse (only used if scribing is 4)
};
@@ -702,7 +722,7 @@ namespace TOB {
struct CastSpell_Struct
{
/*00*/ uint32 slot;
/*04*/ uint32 spell_id;
/*04*/ int32 spell_id;
/*08*/ CastSpellInventorySlot_Struct inventory_slot;
/*18*/ uint32 target_id;
/*22*/ uint32 spell_crc;
@@ -732,7 +752,7 @@ namespace TOB {
/*000*/ EQAffectSlot_Struct slots[6];
/*096*/ EqGuid caster_id;
/*104*/ uint32 flags;
/*108*/ uint32 spell_id;
/*108*/ int32 spell_id;
/*112*/ uint32 duration;
/*116*/ uint32 initial_duration;
/*120*/ uint32 hit_count;
@@ -762,7 +782,7 @@ namespace TOB {
{
uint32 new_mana;
uint32 stamina; // endurance
uint32 spell_id;
int32 spell_id;
uint32 keepcasting;
int32 slot; // gem slot
};
@@ -775,7 +795,7 @@ namespace TOB {
{
uint16 target;
uint16 source;
uint32 spell_id;
int32 spell_id;
//4 leaves a buff
uint32 effect_type;
uint32 effective_casting_level;
@@ -1086,5 +1106,3 @@ namespace TOB {
}; //end namespace structs
}; //end namespace tob
#endif /*LAURION_STRUCTS_H_*/
+49 -113
View File
@@ -33,6 +33,7 @@
#include "common/raid.h"
#include "common/rulesys.h"
#include "common/strings.h"
#include "zone/mob.h"
#include "cereal/types/vector.hpp"
#include <iostream>
@@ -64,7 +65,6 @@ namespace UF
static inline spells::CastingSlot ServerToUFCastingSlot(EQ::spells::CastingSlot slot);
static inline EQ::spells::CastingSlot UFToServerCastingSlot(spells::CastingSlot slot);
static inline int ServerToUFBuffSlot(int index);
static inline int UFToServerBuffSlot(int index);
void Register(EQStreamIdentifier &into)
@@ -434,7 +434,7 @@ namespace UF
}
}
ENCODE(OP_Buff)
ENCODE(OP_BuffDefinition)
{
ENCODE_LENGTH_EXACT(SpellBuffPacket_Struct);
SETUP_DIRECT_ENCODE(SpellBuffPacket_Struct, structs::SpellBuffPacket_Struct);
@@ -442,72 +442,17 @@ namespace UF
OUT(entityid);
OUT(buff.effect_type);
OUT(buff.level);
// just so we're 100% sure we get a 1.0f ...
eq->buff.bard_modifier = emu->buff.bard_modifier == 10 ? 1.0f : emu->buff.bard_modifier / 10.0f;
OUT(buff.bard_modifier);
OUT(buff.spellid);
OUT(buff.duration);
OUT(buff.num_hits);
// TODO: implement slot_data stuff
eq->slotid = ServerToUFBuffSlot(emu->slotid);
OUT(slotid);
OUT(bufffade); // Live (October 2011) sends a 2 rather than 0 when a buff is created, but it doesn't seem to matter.
FINISH_ENCODE();
}
ENCODE(OP_BuffCreate)
{
SETUP_VAR_ENCODE(BuffIcon_Struct);
uint32 sz = 12 + (17 * emu->count) + emu->name_lengths; // 17 includes nullterm
__packet->size = sz;
__packet->pBuffer = new unsigned char[sz];
memset(__packet->pBuffer, 0, sz);
__packet->WriteUInt32(emu->entity_id);
__packet->WriteUInt32(emu->tic_timer);
__packet->WriteUInt8(emu->all_buffs); // 1 = all buffs, 0 = 1 buff
__packet->WriteUInt16(emu->count);
for (int i = 0; i < emu->count; ++i)
{
__packet->WriteUInt32(emu->type == 0 ? ServerToUFBuffSlot(emu->entries[i].buff_slot) : emu->entries[i].buff_slot);
__packet->WriteUInt32(emu->entries[i].spell_id);
__packet->WriteUInt32(emu->entries[i].tics_remaining);
__packet->WriteUInt32(emu->entries[i].num_hits);
__packet->WriteString(emu->entries[i].caster);
}
__packet->WriteUInt8(emu->type);
FINISH_ENCODE();
/*
uint32 write_var32 = 60;
uint8 write_var8 = 1;
ss.write((const char*)&emu->entity_id, sizeof(uint32));
ss.write((const char*)&write_var32, sizeof(uint32));
ss.write((const char*)&write_var8, sizeof(uint8));
ss.write((const char*)&emu->count, sizeof(uint16));
write_var32 = 0;
write_var8 = 0;
for(uint16 i = 0; i < emu->count; ++i)
{
if(emu->entries[i].buff_slot >= 25 && emu->entries[i].buff_slot < 37)
{
emu->entries[i].buff_slot += 5;
}
else if(emu->entries[i].buff_slot >= 37)
{
emu->entries[i].buff_slot += 14;
}
ss.write((const char*)&emu->entries[i].buff_slot, sizeof(uint32));
ss.write((const char*)&emu->entries[i].spell_id, sizeof(uint32));
ss.write((const char*)&emu->entries[i].tics_remaining, sizeof(uint32));
ss.write((const char*)&write_var32, sizeof(uint32));
ss.write((const char*)&write_var8, sizeof(uint8));
}
ss.write((const char*)&write_var8, sizeof(uint8));
*/
}
ENCODE(OP_CancelTrade)
{
ENCODE_LENGTH_EXACT(CancelTrade_Struct);
@@ -1800,44 +1745,6 @@ namespace UF
FINISH_ENCODE();
}
ENCODE(OP_PetBuffWindow)
{
EQApplicationPacket *in = *p;
*p = nullptr;
unsigned char *__emu_buffer = in->pBuffer;
PetBuff_Struct *emu = (PetBuff_Struct *)__emu_buffer;
int PacketSize = 12 + (emu->buffcount * 17);
in->size = PacketSize;
in->pBuffer = new unsigned char[in->size];
char *Buffer = (char *)in->pBuffer;
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petid);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1);
VARSTRUCT_ENCODE_TYPE(uint16, Buffer, emu->buffcount);
for (unsigned int i = 0; i < PET_BUFF_COUNT; ++i)
{
if (emu->spellid[i])
{
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, i);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spellid[i]);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->ticsremaining[i]);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // numhits
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string. Name of the caster of the buff.
}
}
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->buffcount); /// I think this is actually some sort of type
delete[] __emu_buffer;
dest->FastQueuePacket(&in, ack_req);
}
ENCODE(OP_PlayerProfile)
{
SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct);
@@ -2729,8 +2636,6 @@ namespace UF
FINISH_ENCODE();
}
ENCODE(OP_TargetBuffs) { ENCODE_FORWARD(OP_BuffCreate); }
ENCODE(OP_TaskDescription)
{
EQApplicationPacket *in = *p;
@@ -3638,7 +3543,7 @@ namespace UF
FINISH_DIRECT_DECODE();
}
DECODE(OP_Buff)
DECODE(OP_BuffDefinition)
{
DECODE_LENGTH_EXACT(structs::SpellBuffPacket_Struct);
SETUP_DIRECT_DECODE(SpellBuffPacket_Struct, structs::SpellBuffPacket_Struct);
@@ -5208,19 +5113,6 @@ namespace UF
}
}
static inline int ServerToUFBuffSlot(int index)
{
// we're a disc
if (index >= EQ::spells::LONG_BUFFS + EQ::spells::SHORT_BUFFS)
return index - EQ::spells::LONG_BUFFS - EQ::spells::SHORT_BUFFS +
spells::LONG_BUFFS + spells::SHORT_BUFFS;
// we're a song
if (index >= EQ::spells::LONG_BUFFS)
return index - EQ::spells::LONG_BUFFS + spells::LONG_BUFFS;
// we're a normal buff
return index; // as long as we guard against bad slots server side, we should be fine
}
static inline int UFToServerBuffSlot(int index)
{
// we're a disc
@@ -5233,4 +5125,48 @@ namespace UF
// we're a normal buff
return index; // as long as we guard against bad slots server side, we should be fine
}
std::unique_ptr<EQApplicationPacket> BuffComponent::RefreshBuffs(EmuOpcode opcode, Mob* mob,
bool remove,
bool buff_timers_suspended, const std::vector<uint32_t>& slots) const
{
// UF introduced the self refresh buff packet
Buffs_Struct* buffs = mob->GetBuffs();
size_t buffer_size = 12; // 12 bytes outside the list
std::vector<uint32_t> send_slots;
if (slots.empty()) {
for (uint32_t slot = 0; slot < mob->GetMaxTotalSlots(); ++slot)
if (buffs[slot].spellid > 1) {
buffer_size += 17 + strlen(buffs[slot].caster_name); // 17 includes the null terminator
send_slots.push_back(slot);
}
} else {
for (uint32_t slot : slots)
if (slot < mob->GetMaxTotalSlots() && buffs[slot].spellid > 1) {
buffer_size += 17 + strlen(buffs[slot].caster_name);
send_slots.push_back(slot);
}
}
SerializeBuffer buffer(buffer_size);
buffer.WriteUInt32(mob->GetID());
buffer.WriteUInt32(mob->GetRemainingTicTime());
buffer.WriteUInt8(slots.empty() ? 1 : 0);
buffer.WriteUInt16(send_slots.size());
for (uint32_t slot : send_slots) {
buffer.WriteUInt32(ServerToPatchBuffSlot(slot));
buffer.WriteInt32(remove ? -1 : buffs[slot].spellid);
buffer.WriteInt32(buffs[slot].ticsremaining);
buffer.WriteUInt32(buffs[slot].hit_number);
buffer.WriteString(buffs[slot].caster_name);
}
buffer.WriteUInt8(opcode == OP_RefreshPetBuffs ? 2 : 0);
return std::make_unique<EQApplicationPacket>(opcode, std::move(buffer));
}
} /*UF*/
+26 -33
View File
@@ -17,46 +17,39 @@
*/
#pragma once
#include "sod.h"
#include "common/struct_strategy.h"
#include "common/patches/sod.h"
class EQStreamIdentifier;
namespace UF
{
namespace UF {
//these are the only public member of this namespace.
extern void Register(EQStreamIdentifier &into);
extern void Reload();
extern void Register(EQStreamIdentifier& into);
extern void Reload();
//you should not directly access anything below..
//I just dont feel like making a seperate header for it.
class Strategy : public StructStrategy {
public:
Strategy();
protected:
virtual std::string Describe() const;
virtual const EQ::versions::ClientVersion ClientVersion() const;
//magic macro to declare our opcode processors
#include "ss_declare.h"
#include "uf_ops.h"
};
}; /*UF*/
namespace Message {
class UF : public SoD
class Strategy : public StructStrategy
{
public:
UF() = default;
~UF() override = default;
Strategy();
protected:
virtual std::string Describe() const;
virtual const EQ::versions::ClientVersion ClientVersion() const;
//magic macro to declare our opcode processors
#include "ss_declare.h"
#include "uf_ops.h"
};
} // namespace Message
class BuffComponent : public SoD::BuffComponent
{
public:
BuffComponent(uint32_t maxLongBuffs, uint32_t maxShortBuffs) : SoD::BuffComponent(maxLongBuffs, maxShortBuffs) {}
BuffComponent() = delete;
~BuffComponent() override = default;
std::unique_ptr<EQApplicationPacket> RefreshBuffs(EmuOpcode opcode, Mob* mob, bool remove,
bool buff_timers_suspended, const std::vector<uint32_t>& slots) const override;
};
}; /*UF*/
+2 -5
View File
@@ -25,8 +25,7 @@ E(OP_AugmentInfo)
E(OP_Barter)
E(OP_BazaarSearch)
E(OP_BecomeTrader)
E(OP_Buff)
E(OP_BuffCreate)
E(OP_BuffDefinition)
E(OP_CancelTrade)
E(OP_ChannelMessage)
E(OP_CharInventory)
@@ -75,7 +74,6 @@ E(OP_MoveItem)
E(OP_NewSpawn)
E(OP_NewZone)
E(OP_OnLevelMessage)
E(OP_PetBuffWindow)
E(OP_PlayerProfile)
E(OP_RaidJoin)
E(OP_RaidUpdate)
@@ -93,7 +91,6 @@ E(OP_SpawnAppearance)
E(OP_SpawnDoor)
E(OP_SpecialMesg)
E(OP_Stun)
E(OP_TargetBuffs)
E(OP_TaskDescription)
E(OP_Track)
E(OP_Trader)
@@ -116,7 +113,7 @@ D(OP_AugmentInfo)
D(OP_AugmentItem)
D(OP_BazaarSearch)
D(OP_BookButton)
D(OP_Buff)
D(OP_BuffDefinition)
D(OP_BuffRemoveRequest)
D(OP_CastSpell)
D(OP_ChannelMessage)
+11 -11
View File
@@ -480,7 +480,7 @@ struct NewZone_Struct {
*/
struct MemorizeSpell_Struct {
uint32 slot; // Spot in the spell book/memorized slot
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc)
int32 spell_id; // Spell id (200 or c8 is minor healing, etc)
uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming
uint32 reduction; // lowers reuse
};
@@ -517,7 +517,7 @@ struct ManaChange_Struct
{
/*00*/ uint32 new_mana; // New Mana AMount
/*04*/ uint32 stamina;
/*08*/ uint32 spell_id;
/*08*/ int32 spell_id;
/*12*/ uint8 keepcasting; // won't stop the cast. Change mana while casting?
/*13*/ uint8 padding[3]; // client doesn't read it, garbage data seems like
/*16*/ int32 slot; // -1 for normal usage slot for when we want silent interrupt? I think it does timer stuff or something. Linked Spell Reuse interrupt uses it
@@ -535,14 +535,14 @@ struct BeginCast_Struct
{
// len = 8
/*004*/ uint16 caster_id;
/*006*/ uint16 spell_id;
/*006*/ int16 spell_id;
/*016*/ uint32 cast_time; // in miliseconds
};
struct CastSpell_Struct
{
uint32 slot;
uint32 spell_id;
int32 spell_id;
uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast
uint32 target_id;
uint32 cs_unknown1;
@@ -575,7 +575,7 @@ struct SpellBuff_Struct
/*002*/ uint8 unknown002; //pretty sure padding now
/*003*/ uint8 unknown003; // MQ2 used to call this "damage shield" -- don't see client referencing it, so maybe server side DS type tracking?
/*004*/ float bard_modifier;
/*008*/ uint32 spellid;
/*008*/ int32 spellid;
/*012*/ uint32 duration;
/*016*/ uint32 num_hits;
/*020*/ uint32 player_id; // caster ID, pretty sure just zone ID
@@ -595,7 +595,7 @@ struct SpellBuffPacket_Struct {
#if 0
struct BuffIconEntry_Struct {
/*000*/ uint32 buff_slot;
/*004*/ uint32 spell_id;
/*004*/ int32 spell_id;
/*008*/ uint32 tics_remaining;
/*012*/ uint32 num_hits;
// char name[0]; caster name is also here sometimes
@@ -1306,7 +1306,7 @@ struct CombatDamage_Struct
/* 00 */ uint16 target;
/* 02 */ uint16 source;
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells
/* 05 */ uint16 spellid;
/* 05 */ int16 spellid;
/* 07 */ int32 damage;
/* 11 */ float force; // cd cc cc 3d
/* 15 */ float hit_heading; // see above notes in Action_Struct
@@ -1342,7 +1342,7 @@ struct Death_Struct
/*004*/ uint32 killer_id;
/*008*/ uint32 corpseid; // was corpseid
/*012*/ uint32 attack_skill; // was type
/*016*/ uint32 spell_id;
/*016*/ int32 spell_id;
/*020*/ uint32 bindzoneid; //bindzoneid?
/*024*/ uint32 damage;
/*028*/ uint32 unknown028;
@@ -2194,7 +2194,7 @@ struct GroupFollow_Struct { // Underfoot Follow Struct
};
struct InspectBuffs_Struct {
/*000*/ uint32 spell_id[BUFF_COUNT];
/*000*/ int32 spell_id[BUFF_COUNT];
/*120*/ int32 tics_remaining[BUFF_COUNT];
};
@@ -2610,7 +2610,7 @@ struct Resurrect_Struct {
char your_name[64];
uint32 unknown88;
char rezzer_name[64];
uint32 spellid;
int32 spellid;
char corpse_name[64];
uint32 action;
/* 228 */
@@ -3896,7 +3896,7 @@ struct SendAA_Struct {
/*0037*/ uint32 prereq_skill; //is < 0, abs() is category #
/*0041*/ uint32 prereq_minpoints; //min points in the prereq
/*0045*/ uint32 type;
/*0049*/ uint32 spellid;
/*0049*/ int32 spellid;
/*0053*/ uint32 spell_type;
/*0057*/ uint32 spell_refresh;
/*0061*/ uint32 classes;