mirror of
https://github.com/EQEmu/Server.git
synced 2026-06-20 09:38:21 +00:00
Add Buff infrastructure to go through different logic paths from different patches (#5073)
This commit is contained in:
+172
-136
@@ -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*/
|
||||
Reference in New Issue
Block a user