mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-06 00:32:25 +00:00
Added component-based patch system (#5070)
This commit is contained in:
parent
0ada77f340
commit
743fd45b17
@ -71,7 +71,7 @@ if(UNIX)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(Boost REQUIRED COMPONENTS dynamic_bitset foreach tuple)
|
||||
find_package(Boost REQUIRED COMPONENTS dynamic_bitset foreach tuple CONFIG REQUIRED)
|
||||
find_package(cereal CONFIG REQUIRED)
|
||||
find_package(fmt CONFIG REQUIRED)
|
||||
find_package(glm CONFIG REQUIRED)
|
||||
|
||||
@ -86,6 +86,7 @@ set(common_sources
|
||||
packet_dump_file.cpp
|
||||
packet_dump.cpp
|
||||
packet_functions.cpp
|
||||
patches/client_version.cpp
|
||||
patches/patches.cpp
|
||||
patches/rof_limits.cpp
|
||||
patches/rof.cpp
|
||||
@ -135,6 +136,7 @@ set(common_sources
|
||||
util/directory.cpp
|
||||
util/uuid.cpp
|
||||
zone_store.cpp
|
||||
links.cpp
|
||||
)
|
||||
|
||||
set(repositories
|
||||
@ -654,6 +656,8 @@ set(common_headers
|
||||
packet_dump_file.h
|
||||
packet_dump.h
|
||||
packet_functions.h
|
||||
patches/IMessage.h
|
||||
patches/client_version.h
|
||||
patches/patches.h
|
||||
patches/rof_limits.h
|
||||
patches/rof_ops.h
|
||||
@ -733,9 +737,8 @@ set(common_headers
|
||||
util/memory_stream.h
|
||||
util/uuid.h
|
||||
version.h
|
||||
zone_store.h
|
||||
links.h
|
||||
links.cpp
|
||||
zone_store.h
|
||||
links.h
|
||||
)
|
||||
|
||||
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "Source Files" FILES ${common_sources})
|
||||
|
||||
@ -61,8 +61,8 @@ namespace EQ
|
||||
maskAllClients = 0xFFFFFFFF
|
||||
};
|
||||
|
||||
const ClientVersion LastClientVersion = ClientVersion::TOB;
|
||||
const size_t ClientVersionCount = (static_cast<size_t>(LastClientVersion) + 1);
|
||||
inline constexpr ClientVersion LastClientVersion = ClientVersion::TOB;
|
||||
inline constexpr size_t ClientVersionCount = (static_cast<size_t>(LastClientVersion) + 1);
|
||||
|
||||
bool IsValidClientVersion(ClientVersion client_version);
|
||||
ClientVersion ValidateClientVersion(ClientVersion client_version);
|
||||
|
||||
@ -4,7 +4,28 @@
|
||||
|
||||
#include "links.h"
|
||||
|
||||
std::string Links::FormatSpellLink(uint32_t SpellID, const std::string& SpellName)
|
||||
#include "spdat.h"
|
||||
|
||||
void Links::FormatItemLink(char* Buffer, size_t BufferSize, const EQ::ItemInstance* item)
|
||||
{
|
||||
return fmt::format("{}63^{}^0^'{}{}", ITEM_TAG_CHAR, SpellID, SpellName.c_str(), ITEM_TAG_CHAR);
|
||||
// TODO: Reverse 0x14064B220 to get definition of this function
|
||||
}
|
||||
|
||||
void Links::FormatSpellLink(char* Buffer, size_t BufferSize, uint32_t SpellID,
|
||||
const char* spellNameOverride)
|
||||
{
|
||||
snprintf(Buffer, BufferSize, "%c%d3^%d^0^'%s%c", ITEM_TAG_CHAR, ETAG_SPELL, SpellID,
|
||||
spellNameOverride && spellNameOverride[0] ? spellNameOverride : GetSpellName(SpellID), ITEM_TAG_CHAR);
|
||||
}
|
||||
|
||||
void Links::FormatDialogLink(char* Buffer, size_t BufferSize, std::string_view keyword, std::string_view text)
|
||||
{
|
||||
if (text.empty()) {
|
||||
snprintf(Buffer, BufferSize, "%c%d%.*s%c", ITEM_TAG_CHAR, ETAG_DIALOG_RESPONSE,
|
||||
static_cast<int>(keyword.length()), keyword.data(), ITEM_TAG_CHAR);
|
||||
} else {
|
||||
snprintf(Buffer, BufferSize, "%c%d%.*s:%.*s%c", ITEM_TAG_CHAR, ETAG_DIALOG_RESPONSE,
|
||||
static_cast<int>(keyword.length()), keyword.data(),
|
||||
static_cast<int>(text.length()), text.data(), ITEM_TAG_CHAR);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,9 +3,59 @@
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#include "item_instance.h"
|
||||
|
||||
namespace EQ { class ItemInstance; }
|
||||
|
||||
namespace Links
|
||||
{
|
||||
constexpr char ITEM_TAG_CHAR = '\x12';
|
||||
std::string FormatSpellLink(uint32_t SpellID, const std::string& SpellName);
|
||||
// Max Link Size in bytes
|
||||
constexpr size_t MAX_LINK_SIZE = 512;
|
||||
|
||||
// Universal link tag character
|
||||
constexpr char ITEM_TAG_CHAR = '\x12';
|
||||
|
||||
// Enumeration of different types of item tags
|
||||
enum ETagCodes
|
||||
{
|
||||
ETAG_ITEM = 0,
|
||||
ETAG_PLAYER,
|
||||
ETAG_SPAM,
|
||||
ETAG_ACHIEVEMENT,
|
||||
ETAG_DIALOG_RESPONSE,
|
||||
ETAG_COMMAND,
|
||||
ETAG_SPELL,
|
||||
ETAG_FACTION,
|
||||
ETAG_COMMAND2,
|
||||
ETAG_UNKNOWN9,
|
||||
|
||||
ETAG_COUNT,
|
||||
ETAG_FIRST = ETAG_ITEM,
|
||||
ETAG_LAST = ETAG_UNKNOWN9,
|
||||
|
||||
ETAG_INVALID = -1,
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Link Formatting -- Pulled from MQ code
|
||||
|
||||
// Create an achievement link for the given achievement.
|
||||
// TODO: implement this when achievements are added, leave the signature here for reference. Code in eqlib's ItemLinks.cpp
|
||||
// void FormatAchievementLink(char* Buffer, size_t BufferSize, const Achievement* achievement,
|
||||
// std::string_view playerName);
|
||||
|
||||
// Create an item link from the given item.
|
||||
void FormatItemLink(char* Buffer, size_t BufferSize, const EQ::ItemInstance* item);
|
||||
|
||||
// Create a spell link for the given spell, with optional spell name override. Spells on items often have
|
||||
// spell name overrides that changes the display name of the spell.
|
||||
void FormatSpellLink(char* Buffer, size_t BufferSize, uint32_t SpellID,
|
||||
const char* spellNameOverride = nullptr);
|
||||
|
||||
// Format text into a clickable dialog link. The keyword is the text that will be displayed in the chat window,
|
||||
// and the text is the text that will be sent to the server when the link is clicked. If no text is provided,
|
||||
// then the keyword will be used as the text.
|
||||
void FormatDialogLink(char* Buffer, size_t BufferSize, std::string_view keyword,
|
||||
std::string_view text = {});
|
||||
|
||||
}
|
||||
|
||||
51
common/patches/IMessage.h
Normal file
51
common/patches/IMessage.h
Normal file
@ -0,0 +1,51 @@
|
||||
/* 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 "client_version.h"
|
||||
|
||||
// Migration path: replace string_ids.h usage with ID enum values one call site at a time.
|
||||
|
||||
class Client;
|
||||
class Mob;
|
||||
class EQApplicationPacket;
|
||||
|
||||
namespace Message {
|
||||
|
||||
template<typename... Args>
|
||||
concept AllConstChar = (std::is_convertible_v<Args, const char*> && ...);
|
||||
|
||||
class IMessage
|
||||
{
|
||||
public:
|
||||
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;
|
||||
|
||||
// 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,
|
||||
const char* spell_link) const = 0;
|
||||
virtual std::unique_ptr<EQApplicationPacket> InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id,
|
||||
const char* name, const char* spell_link) const = 0;
|
||||
};
|
||||
|
||||
} // namespace Message
|
||||
86
common/patches/client_version.cpp
Normal file
86
common/patches/client_version.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
/* 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 "client_version.h"
|
||||
|
||||
#include "common/patches/titanium.h"
|
||||
#include "common/patches/sof.h"
|
||||
#include "common/patches/sod.h"
|
||||
#include "common/patches/uf.h"
|
||||
#include "common/patches/rof.h"
|
||||
#include "common/patches/rof2.h"
|
||||
#include "common/patches/tob.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
using Version = EQ::versions::ClientVersion;
|
||||
|
||||
struct ClientComponents
|
||||
{
|
||||
explicit ClientComponents(Version version) : version(version)
|
||||
{
|
||||
switch (version) {
|
||||
case Version::TOB:
|
||||
messageComponent = std::make_unique<Message::TOB>();
|
||||
break;
|
||||
case Version::RoF2:
|
||||
messageComponent = std::make_unique<Message::RoF2>();
|
||||
break;
|
||||
case Version::RoF:
|
||||
messageComponent = std::make_unique<Message::RoF>();
|
||||
break;
|
||||
case Version::UF:
|
||||
messageComponent = std::make_unique<Message::UF>();
|
||||
break;
|
||||
case Version::SoD:
|
||||
messageComponent = std::make_unique<Message::SoD>();
|
||||
break;
|
||||
case Version::SoF:
|
||||
messageComponent = std::make_unique<Message::SoF>();
|
||||
break;
|
||||
case Version::Titanium:
|
||||
messageComponent = std::make_unique<Message::Titanium>();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const Version version;
|
||||
std::unique_ptr<Message::IMessage> messageComponent;
|
||||
};
|
||||
|
||||
// this array must be in the same order as the Version enum because it converts Version to index directly
|
||||
static const std::array<ClientComponents, EQ::versions::ClientVersionCount> s_patches = {
|
||||
{
|
||||
ClientComponents(Version::Unknown), // empty
|
||||
ClientComponents(Version::Client62), // empty
|
||||
ClientComponents(Version::Titanium),
|
||||
ClientComponents(Version::SoF),
|
||||
ClientComponents(Version::SoD),
|
||||
ClientComponents(Version::UF),
|
||||
ClientComponents(Version::RoF),
|
||||
ClientComponents(Version::RoF2),
|
||||
ClientComponents(Version::TOB),
|
||||
}
|
||||
};
|
||||
|
||||
const std::unique_ptr<Message::IMessage>& GetMessageComponent(Version version)
|
||||
{
|
||||
return s_patches.at(static_cast<uint32_t>(version)).messageComponent;
|
||||
}
|
||||
13
common/patches/client_version.h
Normal file
13
common/patches/client_version.h
Normal file
@ -0,0 +1,13 @@
|
||||
//
|
||||
// Created by dannu on 4/21/2026.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/emu_versions.h"
|
||||
#include <memory>
|
||||
|
||||
namespace Message { class IMessage; }
|
||||
|
||||
// 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);
|
||||
@ -17,6 +17,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "uf.h"
|
||||
#include "common/struct_strategy.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
@ -48,3 +49,14 @@ namespace RoF
|
||||
};
|
||||
|
||||
} /*RoF*/
|
||||
|
||||
namespace Message {
|
||||
|
||||
class RoF : public UF
|
||||
{
|
||||
public:
|
||||
RoF() = default;
|
||||
~RoF() override = default;
|
||||
};
|
||||
|
||||
} // namespace Message
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "rof.h"
|
||||
#include "common/struct_strategy.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
@ -48,3 +49,14 @@ namespace RoF2
|
||||
};
|
||||
|
||||
}; /*RoF2*/
|
||||
|
||||
namespace Message {
|
||||
|
||||
class RoF2 : public RoF
|
||||
{
|
||||
public:
|
||||
RoF2() = default;
|
||||
~RoF2() override = default;
|
||||
};
|
||||
|
||||
} // namespace Message
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "sof.h"
|
||||
#include "common/struct_strategy.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
@ -48,3 +49,14 @@ namespace SoD
|
||||
};
|
||||
|
||||
} /*SoD*/
|
||||
|
||||
namespace Message {
|
||||
|
||||
class SoD : public SoF
|
||||
{
|
||||
public:
|
||||
SoD() = default;
|
||||
~SoD() override = default;
|
||||
};
|
||||
|
||||
} // namespace Message
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "titanium.h"
|
||||
#include "common/struct_strategy.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
@ -48,3 +49,14 @@ namespace SoF
|
||||
};
|
||||
|
||||
} /*SoF*/
|
||||
|
||||
namespace Message {
|
||||
|
||||
class SoF : public Titanium
|
||||
{
|
||||
public:
|
||||
SoF() = default;
|
||||
~SoF() override = default;
|
||||
};
|
||||
|
||||
} // namespace Message
|
||||
|
||||
@ -32,6 +32,7 @@
|
||||
#include "common/raid.h"
|
||||
#include "common/rulesys.h"
|
||||
#include "common/strings.h"
|
||||
#include "zone/string_ids.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
@ -3919,3 +3920,98 @@ namespace Titanium
|
||||
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
|
||||
{
|
||||
uint32_t string_id = ResolveID(id);
|
||||
if (string_id > 0) {
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_SimpleMessage, sizeof(SimpleMessage_Struct));
|
||||
auto* sms = reinterpret_cast<SimpleMessage_Struct*>(outapp->pBuffer);
|
||||
sms->string_id = string_id;
|
||||
sms->color = color;
|
||||
sms->unknown8 = 0;
|
||||
|
||||
return outapp;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> Titanium::Formatted(
|
||||
uint32_t color, uint32_t id, const std::array<const char*, 9>& args) const
|
||||
{
|
||||
uint32_t string_id = ResolveID(id);
|
||||
if (string_id > 0) {
|
||||
std::array<const char*, 9> resolved_args = args;
|
||||
ResolveArguments(id, resolved_args);
|
||||
if (!resolved_args[0])
|
||||
return Simple(color, id);
|
||||
|
||||
SerializeBuffer buf(20);
|
||||
buf.WriteUInt32(0);
|
||||
buf.WriteUInt32(string_id);
|
||||
buf.WriteUInt32(color);
|
||||
|
||||
for (const auto* a : resolved_args) {
|
||||
if (a != nullptr)
|
||||
buf.WriteString(a);
|
||||
}
|
||||
|
||||
buf.WriteUInt8(0);
|
||||
|
||||
return std::make_unique<EQApplicationPacket>(OP_FormattedMessage, std::move(buf));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> Titanium::InterruptSpell(uint32_t message, uint32_t spawn_id,
|
||||
const char* spell_link) const
|
||||
{
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_InterruptCast, sizeof(InterruptCast_Struct));
|
||||
auto ic = reinterpret_cast<InterruptCast_Struct*>(outapp->pBuffer);
|
||||
ic->messageid = ResolveID(message);
|
||||
ic->spawnid = spawn_id;
|
||||
outapp->priority = 5;
|
||||
|
||||
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
|
||||
{
|
||||
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);
|
||||
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
|
||||
{
|
||||
// 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
|
||||
{
|
||||
switch (id) {
|
||||
case SPELL_FIZZLE:
|
||||
case MISS_NOTE:
|
||||
args[0] = nullptr; // drop spell link
|
||||
break;
|
||||
case SPELL_FIZZLE_OTHER:
|
||||
case MISSED_NOTE_OTHER:
|
||||
args[1] = nullptr; // drop spell link
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Message
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "IMessage.h"
|
||||
#include "common/struct_strategy.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
@ -48,3 +49,30 @@ namespace Titanium
|
||||
};
|
||||
|
||||
} /*Titanium*/
|
||||
|
||||
// out-going message packets
|
||||
namespace Message {
|
||||
|
||||
class Titanium : public IMessage
|
||||
{
|
||||
public:
|
||||
Titanium() = default;
|
||||
~Titanium() 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;
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> InterruptSpell(uint32_t message, uint32_t spawn_id,
|
||||
const char* spell_link) const override;
|
||||
std::unique_ptr<EQApplicationPacket> InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id,
|
||||
const char* name,
|
||||
const char* spell_link) const override;
|
||||
|
||||
protected:
|
||||
[[nodiscard]] virtual uint32_t ResolveID(uint32_t id) const;
|
||||
virtual void ResolveArguments(uint32_t id, std::array<const char*, 9>& args) const;
|
||||
};
|
||||
|
||||
} // namespace Message
|
||||
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
|
||||
#include "common/packet_dump.h"
|
||||
#include "world/sof_char_create_data.h"
|
||||
#include "zone/string_ids.h"
|
||||
|
||||
namespace TOB
|
||||
{
|
||||
@ -37,8 +38,8 @@ namespace TOB
|
||||
void SerializeItem(SerializeBuffer &buffer, const EQ::ItemInstance* inst, int16 slot_id, uint8 depth, ItemPacketType packet_type);
|
||||
|
||||
// message link converters
|
||||
static inline void ServerToTOBConvertLinks(std::string& message_out, const std::string& message_in);
|
||||
static inline void TOBToServerConvertLinks(std::string& message_out, const std::string& message_in);
|
||||
static void ServerToTOBConvertLinks(std::string& message_out, const std::string& message_in);
|
||||
static void TOBToServerConvertLinks(std::string& message_out, const std::string& message_in);
|
||||
|
||||
// SpawnAppearance
|
||||
static inline uint32 ServerToTOBSpawnAppearanceType(uint32 server_type);
|
||||
@ -388,8 +389,7 @@ namespace TOB
|
||||
|
||||
VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sender);
|
||||
VARSTRUCT_ENCODE_STRING(OutBuffer, emu->targetname);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown
|
||||
VARSTRUCT_ENCODE_TYPE(uint64, OutBuffer, 0); // Unknown
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->language);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->chan_num);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown
|
||||
@ -397,11 +397,13 @@ namespace TOB
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->skill_in_language);
|
||||
VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str());
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown
|
||||
VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, 0); // Unknown
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0);// Unknown
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0);// Unknown
|
||||
|
||||
VARSTRUCT_ENCODE_STRING(OutBuffer, "");
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0);// Unknown
|
||||
|
||||
delete[] __emu_buffer;
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
@ -651,43 +653,6 @@ namespace TOB
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
ENCODE(OP_FormattedMessage)
|
||||
{
|
||||
EQApplicationPacket* in = *p;
|
||||
*p = nullptr;
|
||||
|
||||
FormattedMessage_Struct* emu = (FormattedMessage_Struct*)in->pBuffer;
|
||||
|
||||
char* old_message_ptr = (char*)in->pBuffer;
|
||||
old_message_ptr += sizeof(FormattedMessage_Struct);
|
||||
|
||||
std::string old_message_array[9];
|
||||
|
||||
for (int i = 0; i < 9; ++i) {
|
||||
if (*old_message_ptr == 0) { break; }
|
||||
old_message_array[i] = old_message_ptr;
|
||||
old_message_ptr += old_message_array[i].length() + 1;
|
||||
}
|
||||
|
||||
SerializeBuffer buffer;
|
||||
buffer.WriteUInt32(0); // This is a string written like the message arrays
|
||||
buffer.WriteUInt8(emu->unknown0);
|
||||
buffer.WriteUInt32(emu->string_id);
|
||||
buffer.WriteUInt32(emu->type);
|
||||
|
||||
for (int i = 0; i < 9; ++i) {
|
||||
std::string new_message;
|
||||
ServerToTOBConvertLinks(new_message, old_message_array[i]);
|
||||
buffer.WriteLengthString(new_message);
|
||||
}
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_FormattedMessage, buffer.size());
|
||||
outapp->WriteData(buffer.buffer(), buffer.size());
|
||||
dest->FastQueuePacket(&outapp, ack_req);
|
||||
|
||||
delete in;
|
||||
}
|
||||
|
||||
ENCODE(OP_GMTraining) {
|
||||
ENCODE_LENGTH_EXACT(GMTrainee_Struct);
|
||||
SETUP_DIRECT_ENCODE(GMTrainee_Struct, structs::GMTrainee_Struct);
|
||||
@ -3676,10 +3641,13 @@ namespace TOB
|
||||
|
||||
uint32 Skill = VARSTRUCT_DECODE_TYPE(uint32, InBuffer);
|
||||
|
||||
// this has a size limit of 11k in the client
|
||||
std::string old_message = InBuffer;
|
||||
std::string new_message;
|
||||
TOBToServerConvertLinks(new_message, old_message);
|
||||
|
||||
// there are 15 bytes after this, part of which is an unk string, check the ENCODE for the layout
|
||||
|
||||
__packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1;
|
||||
__packet->pBuffer = new unsigned char[__packet->size];
|
||||
ChannelMessage_Struct* emu = (ChannelMessage_Struct*)__packet->pBuffer;
|
||||
@ -4780,60 +4748,59 @@ namespace TOB
|
||||
buffer.WriteInt32(0); //unsupported atm
|
||||
}
|
||||
|
||||
static inline void ServerToTOBConvertLinks(std::string& message_out, const std::string& message_in)
|
||||
static void ServerToTOBConvertLinks(std::string& message_out, const std::string& message_in)
|
||||
{
|
||||
if (message_in.find('\x12') == std::string::npos) {
|
||||
message_out = message_in;
|
||||
return;
|
||||
}
|
||||
|
||||
auto segments = Strings::Split(message_in, '\x12');
|
||||
std::vector<std::string> segments = Strings::Split(message_in, '\x12');
|
||||
for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) {
|
||||
if (segment_iter & 1) {
|
||||
auto etag = std::stoi(segments[segment_iter].substr(0, 1));
|
||||
|
||||
switch (etag) {
|
||||
case 0:
|
||||
{
|
||||
case 0: {
|
||||
size_t index = 1;
|
||||
auto item_id = segments[segment_iter].substr(index, 5);
|
||||
std::string item_id = segments[segment_iter].substr(index, 5);
|
||||
index += 5;
|
||||
|
||||
auto aug1 = segments[segment_iter].substr(index, 5);
|
||||
|
||||
std::string aug1 = segments[segment_iter].substr(index, 5);
|
||||
index += 5;
|
||||
|
||||
auto aug2 = segments[segment_iter].substr(index, 5);
|
||||
|
||||
std::string aug2 = segments[segment_iter].substr(index, 5);
|
||||
index += 5;
|
||||
|
||||
auto aug3 = segments[segment_iter].substr(index, 5);
|
||||
|
||||
std::string aug3 = segments[segment_iter].substr(index, 5);
|
||||
index += 5;
|
||||
|
||||
auto aug4 = segments[segment_iter].substr(index, 5);
|
||||
|
||||
std::string aug4 = segments[segment_iter].substr(index, 5);
|
||||
index += 5;
|
||||
|
||||
auto aug5 = segments[segment_iter].substr(index, 5);
|
||||
|
||||
std::string aug5 = segments[segment_iter].substr(index, 5);
|
||||
index += 5;
|
||||
|
||||
auto aug6 = segments[segment_iter].substr(index, 5);
|
||||
|
||||
std::string aug6 = segments[segment_iter].substr(index, 5);
|
||||
index += 5;
|
||||
|
||||
auto is_evolving = segments[segment_iter].substr(index, 1);
|
||||
|
||||
std::string is_evolving = segments[segment_iter].substr(index, 1);
|
||||
index += 1;
|
||||
|
||||
auto evolutionGroup = segments[segment_iter].substr(index, 4);
|
||||
|
||||
std::string evolutionGroup = segments[segment_iter].substr(index, 4);
|
||||
index += 4;
|
||||
|
||||
auto evolutionLevel = segments[segment_iter].substr(index, 2);
|
||||
|
||||
std::string evolutionLevel = segments[segment_iter].substr(index, 2);
|
||||
index += 2;
|
||||
|
||||
auto ornamentationIconID = segments[segment_iter].substr(index, 5);
|
||||
|
||||
std::string ornamentationIconID = segments[segment_iter].substr(index, 5);
|
||||
index += 5;
|
||||
|
||||
auto itemHash = segments[segment_iter].substr(index, 8);
|
||||
|
||||
std::string itemHash = segments[segment_iter].substr(index, 8);
|
||||
index += 8;
|
||||
|
||||
auto text = segments[segment_iter].substr(index);
|
||||
|
||||
|
||||
std::string text = segments[segment_iter].substr(index);
|
||||
|
||||
message_out.push_back('\x12');
|
||||
message_out.push_back('0'); //etag item
|
||||
message_out.append(item_id);
|
||||
@ -4867,14 +4834,13 @@ namespace TOB
|
||||
message_out.push_back('\x12');
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
message_out.append(segments[segment_iter]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void TOBToServerConvertLinks(std::string& message_out, const std::string& message_in) {
|
||||
static void TOBToServerConvertLinks(std::string& message_out, const std::string& message_in) {
|
||||
message_out = message_in;
|
||||
}
|
||||
|
||||
@ -5598,3 +5564,135 @@ namespace TOB
|
||||
}
|
||||
} /*TOB*/
|
||||
|
||||
namespace Message {
|
||||
|
||||
struct TOBStringIDs
|
||||
{
|
||||
static constexpr uint32_t DisarmedTrap = 1458; // You successfully disarmed the trap
|
||||
};
|
||||
|
||||
uint32_t TOB::ResolveID(uint32_t id) const
|
||||
{
|
||||
switch (id) {
|
||||
case YOU_FLURRY:
|
||||
case BOW_DOUBLE_DAMAGE:
|
||||
case NO_INSTRUMENT_SKILL:
|
||||
case DISCIPLINE_CONLOST:
|
||||
case TGB_ON:
|
||||
case TGB_OFF:
|
||||
case DISCIPLINE_RDY:
|
||||
case SONG_NEEDS_DRUM:
|
||||
case SONG_NEEDS_WIND:
|
||||
case SONG_NEEDS_STRINGS:
|
||||
case SONG_NEEDS_BRASS:
|
||||
case YOU_CRIT_HEAL:
|
||||
case YOU_CRIT_BLAST:
|
||||
case SPELL_WORN_OFF:
|
||||
case PET_TAUNTING:
|
||||
case DISC_LEVEL_ERROR:
|
||||
case MALE_SLAYUNDEAD:
|
||||
case FEMALE_SLAYUNDEAD:
|
||||
case FINISHING_BLOW:
|
||||
case ASSASSINATES:
|
||||
case CRIPPLING_BLOW:
|
||||
case CRITICAL_HIT:
|
||||
case DEADLY_STRIKE:
|
||||
case OTHER_CRIT_HEAL:
|
||||
case OTHER_CRIT_BLAST:
|
||||
case NPC_RAMPAGE:
|
||||
case NPC_FLURRY:
|
||||
case DISCIPLINE_FEARLESS:
|
||||
case CORPSE_ITEM_LOST:
|
||||
case FATAL_BOW_SHOT:
|
||||
case CURRENT_SPELL_EFFECTS:
|
||||
case NOT_DELEGATED_MARKER:
|
||||
case STRIKETHROUGH_STRING:
|
||||
case AE_RAMPAGE:
|
||||
case DISC_LEVEL_USE_ERROR:
|
||||
case SPLIT_FAIL:
|
||||
// removed from the client
|
||||
return 0;
|
||||
case DISARMED_TRAP:
|
||||
return TOBStringIDs::DisarmedTrap;
|
||||
default:
|
||||
return RoF2::ResolveID(id);
|
||||
}
|
||||
}
|
||||
|
||||
void TOB::ResolveArguments(uint32_t id, std::array<const char*, 9>& args) const
|
||||
{
|
||||
switch (id) {
|
||||
case SPELL_FIZZLE:
|
||||
case MISS_NOTE:
|
||||
case SPELL_FIZZLE_OTHER:
|
||||
case MISSED_NOTE_OTHER:
|
||||
// take all arguments (spell link)
|
||||
break;
|
||||
default:
|
||||
RoF2::ResolveArguments(id, args);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> TOB::Formatted(uint32_t color, uint32_t id,
|
||||
const std::array<const char*, 9>& args) const
|
||||
{
|
||||
uint32_t string_id = ResolveID(id);
|
||||
if (string_id > 0) {
|
||||
std::array<const char*, 9> resolved_args = args;
|
||||
ResolveArguments(id, resolved_args);
|
||||
if (!resolved_args[0])
|
||||
return Simple(color, id);
|
||||
|
||||
SerializeBuffer buffer(49);
|
||||
// 49 is the minimum size needed for this packet since each arg writes at least 4 bytes
|
||||
buffer.WriteUInt32(0);
|
||||
// This is a string written like the message arrays, but it seems to be discarded by the client
|
||||
buffer.WriteUInt8(0); // 0 is a zone packet, 1 is a world packet -- these are always sent from zone from here
|
||||
buffer.WriteUInt32(string_id);
|
||||
buffer.WriteUInt32(color);
|
||||
|
||||
for (auto a : resolved_args) {
|
||||
if (a != nullptr) {
|
||||
std::string new_message;
|
||||
::TOB::ServerToTOBConvertLinks(new_message, a);
|
||||
buffer.WriteLengthString(new_message);
|
||||
} else
|
||||
buffer.WriteUInt32(0);
|
||||
}
|
||||
|
||||
return std::make_unique<EQApplicationPacket>(OP_FormattedMessage, std::move(buffer));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> TOB::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);
|
||||
outapp->priority = 5;
|
||||
|
||||
return outapp;
|
||||
}
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> TOB::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) + strlen(spell_link) + 2);
|
||||
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);
|
||||
|
||||
return outapp;
|
||||
}
|
||||
|
||||
} // namespace Message
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#ifndef COMMON_LAURION_H
|
||||
#define COMMON_LAURION_H
|
||||
#pragma once
|
||||
|
||||
#include "rof2.h"
|
||||
#include "../struct_strategy.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
@ -34,4 +34,25 @@ namespace TOB
|
||||
|
||||
}; /*TOB*/
|
||||
|
||||
#endif /*COMMON_LAURION_H*/
|
||||
namespace Message {
|
||||
|
||||
class TOB : public RoF2
|
||||
{
|
||||
public:
|
||||
TOB() {}
|
||||
~TOB() override {}
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> Formatted(uint32_t color, uint32_t id,
|
||||
const std::array<const char*, 9>& args) const override;
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> InterruptSpell(uint32_t message, uint32_t spawn_id,
|
||||
const char* spell_link) const override;
|
||||
std::unique_ptr<EQApplicationPacket> InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id,
|
||||
const char* name, const char* spell_link) const override;
|
||||
|
||||
protected:
|
||||
[[nodiscard]] uint32_t ResolveID(uint32_t id) const override;
|
||||
void ResolveArguments(uint32_t id, std::array<const char*, 9>& args) const override;
|
||||
};
|
||||
|
||||
} // namespace Message
|
||||
|
||||
@ -24,7 +24,6 @@ E(OP_DeleteSpawn)
|
||||
E(OP_DisciplineUpdate)
|
||||
E(OP_ExpansionInfo)
|
||||
E(OP_ExpUpdate)
|
||||
E(OP_FormattedMessage)
|
||||
E(OP_GMTraining)
|
||||
E(OP_GMTrainSkillConfirm)
|
||||
E(OP_GroundSpawn)
|
||||
|
||||
@ -741,8 +741,8 @@ namespace TOB {
|
||||
/*132*/ float y;
|
||||
/*136*/ float x;
|
||||
/*140*/ float z;
|
||||
/*144*/ uint8 level;
|
||||
/*145*/ uint8 type;
|
||||
/*144*/ uint8 type;
|
||||
/*145*/ uint8 level;
|
||||
/*146*/ uint8 charges; //no idea if these are right; eqlib doesn't seem to know either
|
||||
/*147*/ uint8 activatable;
|
||||
/*148*/ uint32 unknown1; //might be some timer, not sure though
|
||||
@ -754,7 +754,7 @@ namespace TOB {
|
||||
/*004*/ int32 unknown004;
|
||||
/*008*/ EQAffect_Struct affect;
|
||||
/*160*/ uint32 slot_id;
|
||||
/*164*/ uint32 buff_fade;
|
||||
/*164*/ uint32 buff_fade; // 1: remove, 2: modify, 3: add new
|
||||
/*168*/
|
||||
};
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "sod.h"
|
||||
#include "common/struct_strategy.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
@ -48,3 +49,14 @@ namespace UF
|
||||
};
|
||||
|
||||
}; /*UF*/
|
||||
|
||||
namespace Message {
|
||||
|
||||
class UF : public SoD
|
||||
{
|
||||
public:
|
||||
UF() = default;
|
||||
~UF() override = default;
|
||||
};
|
||||
|
||||
} // namespace Message
|
||||
|
||||
@ -81,7 +81,7 @@ Below is a status list for the 450 opcodes we currently use on the server for th
|
||||
| `OP_CashReward` | 🟡 Unverified | | |
|
||||
| `OP_CastSpell` | 🟢 Verified | | |
|
||||
| `OP_ChangeSize` | 🟢 Verified | | |
|
||||
| `OP_ChannelMessage` | 🟡 Unverified | | |
|
||||
| `OP_ChannelMessage` | 🟢 Verified | | |
|
||||
| `OP_ChangePetName` | 🔴 Not-Set | | |
|
||||
| `OP_CharacterCreate` | 🟢 Verified | Sends heroic type, can be used for something? | |
|
||||
| `OP_CharacterCreateRequest` | 🟢 Verified | | |
|
||||
|
||||
@ -28,6 +28,7 @@ set(zone_sources
|
||||
client_mods.cpp
|
||||
client_packet.cpp
|
||||
client_process.cpp
|
||||
client_version.cpp
|
||||
combat_record.cpp
|
||||
corpse.cpp
|
||||
dialogue_window.cpp
|
||||
@ -130,6 +131,7 @@ set(zone_headers
|
||||
cheat_manager.h
|
||||
client.h
|
||||
client_packet.h
|
||||
client_version.h
|
||||
combat_record.h
|
||||
command.h
|
||||
common.h
|
||||
@ -673,6 +675,8 @@ set_property(TARGET gm_commands_zone PROPERTY FOLDER libraries)
|
||||
|
||||
add_executable(zone ${zone_sources} ${zone_headers})
|
||||
|
||||
target_include_directories(zone PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
install(TARGETS zone RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
||||
|
||||
if(EQEMU_BUILD_PCH)
|
||||
|
||||
@ -5922,13 +5922,13 @@ bool Bot::CastSpell(
|
||||
|
||||
if (DivineAura()) {
|
||||
LogSpellsDetail("Spell casting canceled: cannot cast while Divine Aura is in effect");
|
||||
InterruptSpell(SPELL_FIZZLE, 0x121, false);
|
||||
InterruptSpell(SPELL_FIZZLE, Chat::SpellFailure, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (slot < EQ::spells::CastingSlot::MaxGems && !CheckFizzle(spell_id)) {
|
||||
int fizzle_msg = IsBardSong(spell_id) ? MISS_NOTE : SPELL_FIZZLE;
|
||||
InterruptSpell(fizzle_msg, 0x121, spell_id);
|
||||
InterruptSpell(fizzle_msg, Chat::SpellFailure, spell_id);
|
||||
|
||||
uint32 use_mana = ((spells[spell_id].mana) / 4);
|
||||
LogSpellsDetail("Spell casting canceled: fizzled. [{}] mana has been consumed", use_mana);
|
||||
|
||||
159
zone/client.cpp
159
zone/client.cpp
@ -46,6 +46,7 @@
|
||||
#include "common/zone_store.h"
|
||||
#include "zone/bot_command.h"
|
||||
#include "zone/cheat_manager.h"
|
||||
#include "zone/client_version.h"
|
||||
#include "zone/command.h"
|
||||
#include "zone/dialogue_window.h"
|
||||
#include "zone/dynamic_zone.h"
|
||||
@ -1792,7 +1793,7 @@ void Client::Message(uint32 type, const char* message, ...) {
|
||||
}
|
||||
|
||||
void Client::FilteredMessage(Mob *sender, uint32 type, eqFilterType filter, const char* message, ...) {
|
||||
if (!FilteredMessageCheck(sender, filter))
|
||||
if (!ShouldGetPacket(sender, filter))
|
||||
return;
|
||||
|
||||
va_list argptr;
|
||||
@ -3808,18 +3809,12 @@ void Client::MessageString(uint32 type, uint32 string_id, uint32 distance)
|
||||
return;
|
||||
if (GetFilter(FilterSpellCrits) == FilterHide && type == Chat::SpellCrit)
|
||||
return;
|
||||
auto outapp = new EQApplicationPacket(OP_SimpleMessage, 12);
|
||||
SimpleMessage_Struct* sms = (SimpleMessage_Struct*)outapp->pBuffer;
|
||||
sms->color=type;
|
||||
sms->string_id=string_id;
|
||||
|
||||
sms->unknown8=0;
|
||||
|
||||
if(distance>0)
|
||||
entity_list.QueueCloseClients(this,outapp,false,distance);
|
||||
if (distance > 0)
|
||||
Message::CloseMessageString(this, false, static_cast<float>(distance))(
|
||||
type, string_id);
|
||||
else
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
Message::MessageString(this, type, string_id);
|
||||
}
|
||||
|
||||
//
|
||||
@ -3829,9 +3824,9 @@ void Client::MessageString(uint32 type, uint32 string_id, uint32 distance)
|
||||
// This hack sucks but it's gonna work for now.
|
||||
//
|
||||
void Client::MessageString(uint32 type, uint32 string_id, const char* message1,
|
||||
const char* message2,const char* message3,const char* message4,
|
||||
const char* message5,const char* message6,const char* message7,
|
||||
const char* message8,const char* message9, uint32 distance)
|
||||
const char* message2, const char* message3, const char* message4,
|
||||
const char* message5, const char* message6, const char* message7,
|
||||
const char* message8, const char* message9, uint32 distance)
|
||||
{
|
||||
if (GetFilter(FilterSpellDamage) == FilterHide && type == Chat::NonMelee)
|
||||
return;
|
||||
@ -3847,34 +3842,12 @@ void Client::MessageString(uint32 type, uint32 string_id, const char* message1,
|
||||
if (type == Chat::Emote)
|
||||
type = 4;
|
||||
|
||||
if (!message1) {
|
||||
MessageString(type, string_id); // use the simple message instead
|
||||
return;
|
||||
}
|
||||
|
||||
const char *message_arg[] = {
|
||||
message1, message2, message3, message4, message5,
|
||||
message6, message7, message8, message9
|
||||
};
|
||||
|
||||
SerializeBuffer buf(20);
|
||||
buf.WriteInt32(0); // unknown
|
||||
buf.WriteInt32(string_id);
|
||||
buf.WriteInt32(type);
|
||||
for (auto &m : message_arg) {
|
||||
if (m == nullptr)
|
||||
break;
|
||||
buf.WriteString(m);
|
||||
}
|
||||
|
||||
buf.WriteInt8(0); // prevent oob in packet translation, maybe clean that up sometime
|
||||
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_FormattedMessage, std::move(buf));
|
||||
|
||||
if (distance > 0)
|
||||
entity_list.QueueCloseClients(this, outapp.get(), false, distance);
|
||||
Message::CloseMessageString(this, false, static_cast<float>(distance))(type, string_id, message1,
|
||||
message2, message3, message4, message5, message6, message7, message8, message9);
|
||||
else
|
||||
QueuePacket(outapp.get());
|
||||
Message::MessageString(this, type, string_id, message1, message2, message3, message4, message5,
|
||||
message6, message7, message8, message9);
|
||||
}
|
||||
|
||||
void Client::MessageString(const CZClientMessageString_Struct* msg)
|
||||
@ -3898,60 +3871,49 @@ void Client::MessageString(const CZClientMessageString_Struct* msg)
|
||||
}
|
||||
}
|
||||
|
||||
// helper function, returns true if we should see the message
|
||||
bool Client::FilteredMessageCheck(Mob *sender, eqFilterType filter)
|
||||
// helper function, returns true if the client should get the packet based on the filter and sender
|
||||
bool Client::ShouldGetPacket(Mob *sender, eqFilterType filter)
|
||||
{
|
||||
eqFilterMode mode = GetFilter(filter);
|
||||
// easy ones first
|
||||
if (mode == FilterShow) {
|
||||
return true;
|
||||
} else if (mode == FilterHide) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sender != this && mode == FilterShowSelfOnly) {
|
||||
// easy ones first
|
||||
if (mode == FilterShow)
|
||||
return true;
|
||||
|
||||
if (mode == FilterHide)
|
||||
return false;
|
||||
} else if (sender) {
|
||||
if (mode == FilterShowGroupOnly) {
|
||||
auto g = GetGroup();
|
||||
auto r = GetRaid();
|
||||
if (g) {
|
||||
if (g->IsGroupMember(sender)) {
|
||||
return true;
|
||||
}
|
||||
} else if (r && sender->IsClient()) {
|
||||
auto rgid1 = r->GetGroup(this);
|
||||
auto rgid2 = r->GetGroup(sender->CastToClient());
|
||||
if (rgid1 != RAID_GROUPLESS && rgid1 == rgid2) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sender != this && mode == FilterShowSelfOnly)
|
||||
return false;
|
||||
|
||||
if (sender != nullptr && mode == FilterShowGroupOnly) {
|
||||
if (sender == this)
|
||||
return true;
|
||||
|
||||
Group* g = GetGroup();
|
||||
if (g && g->IsGroupMember(sender))
|
||||
return true;
|
||||
|
||||
Raid* r = GetRaid();
|
||||
if (r && sender->IsClient()) {
|
||||
uint32 rgid1 = r->GetGroup(this);
|
||||
uint32 rgid2 = r->GetGroup(sender->CastToClient());
|
||||
if (rgid1 != RAID_GROUPLESS && rgid1 == rgid2)
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// we passed our checks
|
||||
// fallback case (send by default)
|
||||
return true;
|
||||
}
|
||||
|
||||
void Client::FilteredMessageString(Mob *sender, uint32 type,
|
||||
eqFilterType filter, uint32 string_id)
|
||||
{
|
||||
if (!FilteredMessageCheck(sender, filter))
|
||||
return;
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_SimpleMessage, 12);
|
||||
SimpleMessage_Struct *sms = (SimpleMessage_Struct *)outapp->pBuffer;
|
||||
sms->color = type;
|
||||
sms->string_id = string_id;
|
||||
|
||||
sms->unknown8 = 0;
|
||||
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
|
||||
return;
|
||||
if (ShouldGetPacket(sender, filter))
|
||||
MessageString(type, string_id);
|
||||
}
|
||||
|
||||
void Client::FilteredMessageString(Mob *sender, uint32 type, eqFilterType filter, uint32 string_id,
|
||||
@ -3959,37 +3921,16 @@ void Client::FilteredMessageString(Mob *sender, uint32 type, eqFilterType filter
|
||||
const char *message4, const char *message5, const char *message6,
|
||||
const char *message7, const char *message8, const char *message9)
|
||||
{
|
||||
if (!FilteredMessageCheck(sender, filter))
|
||||
return;
|
||||
|
||||
if (type == Chat::Emote)
|
||||
type = 4;
|
||||
|
||||
if (!message1) {
|
||||
FilteredMessageString(sender, type, filter, string_id); // use the simple message instead
|
||||
return;
|
||||
} else if (ShouldGetPacket(sender, filter)) {
|
||||
if (type == Chat::Emote)
|
||||
type = 4;
|
||||
|
||||
MessageString(
|
||||
type, string_id, message1, message2, message3, message4,
|
||||
message5, message6, message7, message8, message9);
|
||||
}
|
||||
|
||||
const char *message_arg[] = {
|
||||
message1, message2, message3, message4, message5,
|
||||
message6, message7, message8, message9
|
||||
};
|
||||
|
||||
SerializeBuffer buf(20);
|
||||
buf.WriteInt32(0); // unknown
|
||||
buf.WriteInt32(string_id);
|
||||
buf.WriteInt32(type);
|
||||
for (auto &m : message_arg) {
|
||||
if (m == nullptr)
|
||||
break;
|
||||
buf.WriteString(m);
|
||||
}
|
||||
|
||||
buf.WriteInt8(0); // prevent oob in packet translation, maybe clean that up sometime
|
||||
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_FormattedMessage, std::move(buf));
|
||||
|
||||
QueuePacket(outapp.get());
|
||||
}
|
||||
|
||||
void Client::Tell_StringID(uint32 string_id, const char *who, const char *message)
|
||||
|
||||
@ -343,10 +343,10 @@ public:
|
||||
void DyeArmor(EQ::TintProfile* dye);
|
||||
void DyeArmorBySlot(uint8 slot, uint8 red, uint8 green, uint8 blue, uint8 use_tint = 0x00);
|
||||
uint8 SlotConvert(uint8 slot,bool bracer=false);
|
||||
void MessageString(uint32 type, uint32 string_id, uint32 distance = 0);
|
||||
void MessageString(uint32 type, uint32 string_id, const char* message,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0, uint32 distance = 0);
|
||||
void MessageString(uint32 type, uint32 string_id, uint32 distance = 0) override;
|
||||
void MessageString(uint32 type, uint32 string_id, const char* message,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0, uint32 distance = 0) override;
|
||||
void MessageString(const CZClientMessageString_Struct* msg);
|
||||
bool FilteredMessageCheck(Mob *sender, eqFilterType filter);
|
||||
bool ShouldGetPacket(Mob *sender, eqFilterType filter);
|
||||
void FilteredMessageString(Mob *sender, uint32 type, eqFilterType filter, uint32 string_id);
|
||||
void FilteredMessageString(Mob *sender, uint32 type, eqFilterType filter,
|
||||
uint32 string_id, const char *message1, const char *message2 = nullptr,
|
||||
@ -1550,7 +1550,8 @@ public:
|
||||
|
||||
inline const EQ::versions::ClientVersion ClientVersion() const { return m_ClientVersion; }
|
||||
inline const uint32 ClientVersionBit() const { return m_ClientVersionBit; }
|
||||
inline void SetClientVersion(EQ::versions::ClientVersion client_version) { m_ClientVersion = client_version; }
|
||||
void SetClientVersion(EQ::versions::ClientVersion client_version);
|
||||
EQ::versions::ClientVersion GetClientVersion() const;
|
||||
|
||||
/** Adventure Stuff **/
|
||||
void SendAdventureError(const char *error);
|
||||
|
||||
@ -8912,31 +8912,20 @@ void Client::Handle_OP_Hide(const EQApplicationPacket *app)
|
||||
tmHidden = Timer::GetCurrentTime();
|
||||
}
|
||||
if (GetClass() == Class::Rogue) {
|
||||
auto outapp = new EQApplicationPacket(OP_SimpleMessage, sizeof(SimpleMessage_Struct));
|
||||
SimpleMessage_Struct *msg = (SimpleMessage_Struct *)outapp->pBuffer;
|
||||
msg->color = 0x010E;
|
||||
Mob *evadetar = GetTarget();
|
||||
if (!auto_attack && (evadetar && evadetar->CheckAggro(this)
|
||||
&& evadetar->IsNPC())) {
|
||||
uint32 string_id = HIDE_FAIL;
|
||||
Mob* evadetar = GetTarget();
|
||||
|
||||
if (!auto_attack && evadetar && evadetar->CheckAggro(this) && evadetar->IsNPC()) {
|
||||
if (zone->random.Int(0, 260) < (int)GetSkill(EQ::skills::SkillHide)) {
|
||||
msg->string_id = EVADE_SUCCESS;
|
||||
string_id = EVADE_SUCCESS;
|
||||
RogueEvade(evadetar);
|
||||
}
|
||||
else {
|
||||
msg->string_id = EVADE_FAIL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (hidden) {
|
||||
msg->string_id = HIDE_SUCCESS;
|
||||
}
|
||||
else {
|
||||
msg->string_id = HIDE_FAIL;
|
||||
}
|
||||
}
|
||||
FastQueuePacket(&outapp);
|
||||
} else
|
||||
string_id = EVADE_FAIL;
|
||||
} else if (hidden)
|
||||
string_id = HIDE_SUCCESS;
|
||||
|
||||
MessageString(Chat::Skills, string_id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void Client::Handle_OP_HideCorpse(const EQApplicationPacket *app)
|
||||
@ -10325,10 +10314,10 @@ void Client::Handle_OP_ManaChange(const EQApplicationPacket *app)
|
||||
if (app->size == 0) {
|
||||
// i think thats the sign to stop the songs
|
||||
if (IsBardSong(casting_spell_id) || HasActiveSong()) {
|
||||
InterruptSpell(SONG_ENDS, 0x121); //Live doesn't send song end message anymore (~Kayen 1/26/22)
|
||||
InterruptSpell(SONG_ENDS, Chat::SpellFailure); //Live doesn't send song end message anymore (~Kayen 1/26/22)
|
||||
}
|
||||
else {
|
||||
InterruptSpell(INTERRUPT_SPELL, 0x121);
|
||||
InterruptSpell(INTERRUPT_SPELL, Chat::SpellFailure);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -229,11 +229,11 @@ bool Client::Process() {
|
||||
}
|
||||
|
||||
if (song_target == nullptr) {
|
||||
InterruptSpell(SONG_ENDS_ABRUPTLY, 0x121, bardsong);
|
||||
InterruptSpell(SONG_ENDS_ABRUPTLY, Chat::SpellFailure, bardsong);
|
||||
}
|
||||
else {
|
||||
if (!ApplyBardPulse(bardsong, song_target, bardsong_slot)) {
|
||||
InterruptSpell(SONG_ENDS_ABRUPTLY, 0x121, bardsong);
|
||||
InterruptSpell(SONG_ENDS_ABRUPTLY, Chat::SpellFailure, bardsong);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
29
zone/client_version.cpp
Normal file
29
zone/client_version.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
/* 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 "client_version.h"
|
||||
|
||||
using Version = EQ::versions::ClientVersion;
|
||||
|
||||
void Client::SetClientVersion(Version client_version)
|
||||
{
|
||||
m_ClientVersion = client_version;
|
||||
m_ClientVersionBit = EQ::versions::ConvertClientVersionToClientVersionBit(client_version);
|
||||
}
|
||||
|
||||
Version Client::GetClientVersion() const { return m_ClientVersion; }
|
||||
150
zone/client_version.h
Normal file
150
zone/client_version.h
Normal file
@ -0,0 +1,150 @@
|
||||
//
|
||||
// Created by dannu on 4/21/2026.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "common/emu_versions.h"
|
||||
#include "common/patches/client_version.h"
|
||||
#include "common/patches/IMessage.h"
|
||||
|
||||
#include "zone/client.h"
|
||||
#include "zone/mob.h"
|
||||
|
||||
// store all _generic_ static functions for the different patches here
|
||||
namespace ClientPatch {
|
||||
|
||||
using ClientList = std::unordered_map<uint16, Client*>;
|
||||
template<typename Obj> using ComponentGetter = std::function<Obj*(const Client*)>;
|
||||
|
||||
template <typename Fun, typename Obj, typename... Args>
|
||||
requires std::is_member_function_pointer_v<Fun>
|
||||
static void QueuePacket(Client* c, Fun fun, Obj* obj, Args&&... args)
|
||||
{
|
||||
if (obj != nullptr) {
|
||||
std::unique_ptr<EQApplicationPacket> app = std::invoke(fun, obj, std::forward<Args>(args)...);
|
||||
if (app)
|
||||
c->QueuePacket(app.get());
|
||||
}
|
||||
}
|
||||
|
||||
// packet generator queue functions
|
||||
static auto QueueClients(Mob* sender, bool ignore_sender = false, bool ackreq = true)
|
||||
{
|
||||
return [=]<typename Fun, typename Obj, typename... Args>(Fun fun, const ComponentGetter<Obj>& component, Args&&... args)
|
||||
requires std::is_member_function_pointer_v<Fun>
|
||||
{
|
||||
std::array<std::unique_ptr<EQApplicationPacket>, EQ::versions::ClientVersionCount> build_packets;
|
||||
std::unordered_map<uint16, Client*> client_list = entity_list.GetClientList();
|
||||
|
||||
for (auto [_, ent] : client_list) {
|
||||
if (!ignore_sender || ent != sender) {
|
||||
auto& packet = build_packets.at(static_cast<uint32_t>(ent->ClientVersion()));
|
||||
if (!packet)
|
||||
if (auto comp = component(ent); comp != nullptr)
|
||||
packet = std::invoke(fun, comp, std::forward<Args>(args)...);
|
||||
|
||||
if (packet)
|
||||
ent->QueuePacket(packet.get(), ackreq, Client::CLIENT_CONNECTED);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static auto QueueCloseClients(
|
||||
Mob* sender, bool ignore_sender = false, float distance = 200,
|
||||
Mob* skipped_mob = nullptr, bool is_ack_required = true,
|
||||
eqFilterType filter = FilterNone)
|
||||
{
|
||||
if (distance <= 0) distance = static_cast<float>(zone->GetClientUpdateRange());
|
||||
|
||||
return [=]<typename Fun, typename Obj, typename... Args>(Fun fun, const ComponentGetter<Obj>& component, Args&&... args)
|
||||
requires std::is_member_function_pointer_v<Fun>
|
||||
{
|
||||
if (sender == nullptr) {
|
||||
QueueClients(sender, ignore_sender, is_ack_required)(fun, component, std::forward<Args>(args)...);
|
||||
} else {
|
||||
float distance_squared = distance * distance;
|
||||
std::array<std::unique_ptr<EQApplicationPacket>, EQ::versions::ClientVersionCount> build_packets;
|
||||
|
||||
for (auto& [_, mob] : sender->GetCloseMobList(distance)) {
|
||||
if (mob && mob->IsClient()) {
|
||||
Client* client = mob->CastToClient();
|
||||
if ((!ignore_sender || client != sender)
|
||||
&& client != skipped_mob
|
||||
&& DistanceSquared(client->GetPosition(), sender->GetPosition()) < distance_squared
|
||||
&& client->Connected()
|
||||
&& client->ShouldGetPacket(sender, filter))
|
||||
{
|
||||
auto& packet = build_packets.at(static_cast<uint32_t>(client->ClientVersion()));
|
||||
if (!packet)
|
||||
if (auto comp = component(client); comp != nullptr)
|
||||
packet = std::invoke(fun, comp, std::forward<Args>(args)...);
|
||||
|
||||
if (packet)
|
||||
client->QueuePacket(packet.get(), is_ack_required, Client::CLIENT_CONNECTED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace ClientPatch
|
||||
|
||||
// Helpers for the Message interface to send message packets
|
||||
namespace Message {
|
||||
|
||||
// this can return nullptr when the component doesn't exist for the version
|
||||
static std::function GetComponent = [](const Client* c) -> IMessage* {
|
||||
return GetMessageComponent(c->GetClientVersion()).get();
|
||||
};
|
||||
|
||||
// Helper functions to wrap the packet construction in sends
|
||||
template <AllConstChar... Args>
|
||||
requires (sizeof...(Args) <= 9)
|
||||
void MessageString(Client* c, uint32_t type, uint32_t id, Args&&... args)
|
||||
{
|
||||
if constexpr (sizeof...(Args) == 0) {
|
||||
ClientPatch::QueuePacket(c, &IMessage::Simple, GetComponent(c), type, id);
|
||||
} else {
|
||||
std::array<const char*, 9> a = {args...};
|
||||
ClientPatch::QueuePacket(c, &IMessage::Formatted, GetComponent(c), type, id, a);
|
||||
}
|
||||
}
|
||||
|
||||
static auto CloseMessageString(
|
||||
Mob* sender, bool ignore_sender = false, float distance = 200.f,
|
||||
Mob* skipped_mob = nullptr, bool is_ack_required = true,
|
||||
eqFilterType filter = FilterNone)
|
||||
{
|
||||
return [=]<AllConstChar... Args>(uint32_t type, uint32_t id, Args&&... args)
|
||||
requires (sizeof...(Args) <= 9)
|
||||
{
|
||||
auto queue_close_clients = ClientPatch::QueueCloseClients(sender, ignore_sender, distance, skipped_mob,
|
||||
is_ack_required, filter);
|
||||
|
||||
if constexpr (sizeof...(Args) == 0) {
|
||||
return queue_close_clients(&IMessage::Simple, GetComponent, type, id);
|
||||
} else {
|
||||
std::array<const char*, 9> a = {args...};
|
||||
return queue_close_clients(&IMessage::Formatted, GetComponent, type, id, a);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
inline void InterruptSpell(Client* c, uint32_t message, uint32_t spawn_id, const char* spell_link)
|
||||
{
|
||||
ClientPatch::QueuePacket(c, &IMessage::InterruptSpell, GetComponent(c), message, spawn_id, spell_link);
|
||||
}
|
||||
|
||||
inline void InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, const char* name,
|
||||
const char* spell_link)
|
||||
{
|
||||
ClientPatch::QueueCloseClients(sender, true, RuleI(Range, SongMessages), nullptr, true,
|
||||
sender->IsClient() ? FilterPCSpells : FilterNPCSpells)(
|
||||
&IMessage::InterruptSpellOther, GetComponent, sender, message, spawn_id, name, spell_link);
|
||||
}
|
||||
|
||||
} // namespace Message
|
||||
@ -82,6 +82,7 @@
|
||||
#include "common/strings.h"
|
||||
#include "zone/bot.h"
|
||||
#include "zone/client.h"
|
||||
#include "zone/client_version.h"
|
||||
#include "zone/fastmath.h"
|
||||
#include "zone/lua_parser.h"
|
||||
#include "zone/mob_movement_manager.h"
|
||||
@ -95,7 +96,6 @@
|
||||
#include <cassert>
|
||||
|
||||
#include "common/links.h"
|
||||
#include "common/packet_dump.h"
|
||||
|
||||
extern Zone *zone;
|
||||
extern volatile bool is_zone_loaded;
|
||||
@ -335,35 +335,21 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
|
||||
Mob::SetMana(GetMana() - use_mana); // We send StopCasting which will update mana
|
||||
StopCasting();
|
||||
|
||||
// TODO: can handle spell name overrides here
|
||||
std::string spell_name(GetSpellName(spell_id));
|
||||
std::string spell_link = Links::FormatSpellLink(spell_id, spell_name);
|
||||
char spell_link[Links::MAX_LINK_SIZE];
|
||||
Links::FormatSpellLink(spell_link, Links::MAX_LINK_SIZE, spell_id);
|
||||
|
||||
// pre-TOB clients will just discard the extra argument here, so don't worry about patching them out in patches
|
||||
MessageString(Chat::SpellFailure, fizzle_msg, spell_link.c_str());
|
||||
if (IsClient())
|
||||
Message::MessageString(CastToClient(), Chat::SpellFailure, fizzle_msg, spell_link);
|
||||
|
||||
/**
|
||||
* Song Failure message
|
||||
* pre-TOB clients will just discard the extra argument here, so don't worry about patching them out in patches
|
||||
*/
|
||||
entity_list.FilteredMessageCloseString(
|
||||
this,
|
||||
true,
|
||||
RuleI(Range, SpellMessages),
|
||||
Chat::SpellFailure,
|
||||
(IsClient() ? FilterPCSpells : FilterNPCSpells),
|
||||
(fizzle_msg == MISS_NOTE ? MISSED_NOTE_OTHER : SPELL_FIZZLE_OTHER),
|
||||
0,
|
||||
/*
|
||||
MessageFormat: A missed note brings %1's song to a close! (TOB: A missed note brings %1's %2 to a close!)
|
||||
MessageFormat: %1's spell fizzles! (TOB: %1's %2 spell fizzles!)
|
||||
*/
|
||||
GetName(),
|
||||
spell_link.c_str()
|
||||
);
|
||||
Message::CloseMessageString(this, true, RuleI(Range, SpellMessages),
|
||||
nullptr, true, IsClient() ? FilterPCSpells : FilterNPCSpells)(
|
||||
Chat::SpellFailure, fizzle_msg == MISS_NOTE ? MISSED_NOTE_OTHER : SPELL_FIZZLE_OTHER, GetName(), spell_link);
|
||||
|
||||
TryTriggerOnCastRequirement();
|
||||
return(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
SaveSpellLoc();
|
||||
@ -1254,7 +1240,6 @@ void Mob::InterruptSpell(uint16 spellid)
|
||||
// color not used right now
|
||||
void Mob::InterruptSpell(uint16 message, uint16 color, uint16 spellid)
|
||||
{
|
||||
EQApplicationPacket *outapp = nullptr;
|
||||
uint16 message_other;
|
||||
bool bard_song_mode = false; //has the bard song gone to auto repeat mode
|
||||
|
||||
@ -1312,24 +1297,13 @@ void Mob::InterruptSpell(uint16 message, uint16 color, uint16 spellid)
|
||||
if(!message)
|
||||
message = IsBardSong(spellid) ? SONG_ENDS_ABRUPTLY : INTERRUPT_SPELL;
|
||||
|
||||
// TODO: can handle spell name overrides here
|
||||
std::string spellname(GetSpellName(spellid));
|
||||
std::string spelllink = Links::FormatSpellLink(spellid, spellname);
|
||||
|
||||
// clients need some packets
|
||||
if (IsClient() && message != SONG_ENDS)
|
||||
{
|
||||
// the interrupt message
|
||||
outapp = new EQApplicationPacket(OP_InterruptCast, sizeof(InterruptCast_Struct) + spelllink.size() + 1);
|
||||
InterruptCast_Struct* ic = (InterruptCast_Struct*) outapp->pBuffer;
|
||||
ic->messageid = message;
|
||||
ic->spawnid = GetID();
|
||||
// pre-TOB clients will just discard the extra argument here, so don't worry about patching them out in patches
|
||||
fmt::format_to_n(ic->message, spelllink.size(), "{}", spelllink);
|
||||
outapp->priority = 5;
|
||||
CastToClient()->QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
|
||||
char spell_link[Links::MAX_LINK_SIZE];
|
||||
Links::FormatSpellLink(spell_link, Links::MAX_LINK_SIZE, spellid);
|
||||
Message::InterruptSpell(CastToClient(), message, GetID(), spell_link);
|
||||
SendSpellBarEnable(spellid);
|
||||
}
|
||||
|
||||
@ -1355,15 +1329,9 @@ void Mob::InterruptSpell(uint16 message, uint16 color, uint16 spellid)
|
||||
}
|
||||
|
||||
// this is the actual message, it works the same as a formatted message
|
||||
outapp = new EQApplicationPacket(OP_InterruptCast, sizeof(InterruptCast_Struct) + strlen(GetCleanName()) + spelllink.size() + 2);
|
||||
InterruptCast_Struct* ic = (InterruptCast_Struct*) outapp->pBuffer;
|
||||
ic->messageid = message_other;
|
||||
ic->spawnid = GetID();
|
||||
// pre-TOB clients will just discard the extra argument here, so don't worry about patching them out in patches
|
||||
fmt::format_to_n(ic->message, sizeof(GetCleanName()) + spelllink.size() + 1, "{}\x00{}", GetCleanName(), spelllink);
|
||||
entity_list.QueueCloseClients(this, outapp, true, RuleI(Range, SongMessages), 0, true, IsClient() ? FilterPCSpells : FilterNPCSpells);
|
||||
safe_delete(outapp);
|
||||
|
||||
char spell_link[Links::MAX_LINK_SIZE];
|
||||
Links::FormatSpellLink(spell_link, Links::MAX_LINK_SIZE, spellid);
|
||||
Message::InterruptSpellOther(this, message_other, GetID(), GetCleanName(), spell_link);
|
||||
}
|
||||
|
||||
// this is like interrupt, just it doesn't spam interrupt packets to everyone
|
||||
@ -7299,16 +7267,11 @@ void Mob::DoBardCastingFromItemClick(bool is_casting_bard_song, uint32 cast_time
|
||||
Known bug: When a bard uses an augment with a clicky that has a cast time, the cast won't display. This issue only affects bards.
|
||||
*/
|
||||
if (is_casting_bard_song) {
|
||||
//For spells with cast times. Cancel song cast, stop pusling and start item cast.
|
||||
//For spells with cast times. Cancel song cast, stop pulsing and start item cast.
|
||||
if (cast_time != 0) {
|
||||
EQApplicationPacket *outapp = nullptr;
|
||||
outapp = new EQApplicationPacket(OP_InterruptCast, sizeof(InterruptCast_Struct));
|
||||
InterruptCast_Struct* ic = (InterruptCast_Struct*)outapp->pBuffer;
|
||||
ic->messageid = SONG_ENDS;
|
||||
ic->spawnid = GetID();
|
||||
outapp->priority = 5;
|
||||
CastToClient()->QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
char spell_link[Links::MAX_LINK_SIZE];
|
||||
Links::FormatSpellLink(spell_link, Links::MAX_LINK_SIZE, spell_id);
|
||||
Message::InterruptSpell(CastToClient(), SONG_ENDS, GetID(), spell_link);
|
||||
|
||||
ZeroCastingVars();
|
||||
ZeroBardPulseVars();
|
||||
|
||||
@ -41,8 +41,8 @@
|
||||
#define DOORS_INSUFFICIENT_SKILL 132 //You are not sufficiently skilled to pick this lock.
|
||||
#define DOORS_GM 133 //You opened the locked door with your magic GM key.
|
||||
#define ITEMS_INSUFFICIENT_LEVEL 136 //You are not sufficient level to use this item.
|
||||
#define GAIN_XP 138 //You gain experience!!
|
||||
#define GAIN_GROUPXP 139 //You gain party experience!!
|
||||
#define GAIN_XP 138 //You gain experience!! // TODO: TOB added experience value - You gain experience!%1
|
||||
#define GAIN_GROUPXP 139 //You gain party experience!! // TODO: TOB added experience value - You gain party experience!%1
|
||||
#define BOW_DOUBLE_DAMAGE 143 //Your bow shot did double dmg.
|
||||
#define YOU_ARE_BEING_BANDAGED 147 //Someone is bandaging you.
|
||||
#define FORAGE_GRUBS 150 //You have scrounged up some fishing grubs.
|
||||
@ -69,7 +69,7 @@
|
||||
#define SPELL_FIZZLE 173 //Your spell fizzles!
|
||||
#define MUST_EQUIP_ITEM 179 //You cannot use this item unless it is equipped.
|
||||
#define MISS_NOTE 180 //You miss a note, bringing your song to a close!
|
||||
#define CANNOT_USE_ITEM 181 //Your race, class, or deity cannot use this item.
|
||||
#define CANNOT_USE_ITEM 181 //Your race, class, or deity cannot use this item. // TODO: TOB moved this: 6611 Your class, deity, and/or race may not equip %1.
|
||||
#define ITEM_OUT_OF_CHARGES 182 //Item is out of charges.
|
||||
#define ALREADY_ON_A_MOUNT 189 //You are already on a mount.
|
||||
#define TARGET_NO_MANA 191 //Your target has no mana to affect
|
||||
@ -106,7 +106,7 @@
|
||||
#define NO_PET 255 //You do not have a pet.
|
||||
#define GATE_FAIL 260 //Your gate is too unstable, and collapses.
|
||||
#define CORPSE_CANT_SENSE 262 //You cannot sense any corpses for this PC in this zone.
|
||||
#define SPELL_NO_HOLD 263 //Your spell did not take hold.
|
||||
#define SPELL_NO_HOLD 263 //Your spell did not take hold. // TODO: This one is complex. There are 4 replacement strings, all taking arguments 9164, 9209, 9210, 9211
|
||||
#define CANNOT_CHARM 267 //This NPC cannot be charmed.
|
||||
#define SPELL_NO_EFFECT 268 //Your target looks unaffected.
|
||||
#define NO_INSTRUMENT_SKILL 269 //Stick to singing until you learn to play this instrument.
|
||||
@ -123,9 +123,9 @@
|
||||
#define DISARMED_TRAP 305 //You have disarmed the trap.
|
||||
#define LDON_SENSE_TRAP1 306 //You do not Sense any traps.
|
||||
#define TRADESKILL_NOCOMBINE 334 //You cannot combine these items in this container type!
|
||||
#define TRADESKILL_FAILED 336 //You lacked the skills to fashion the items together.
|
||||
#define TRADESKILL_FAILED 336 //You lacked the skills to fashion the items together. // TODO: TOB - 336 You lacked the skills to fashion %1.
|
||||
#define TRADESKILL_TRIVIAL 338 //You can no longer advance your skill from making this item.
|
||||
#define TRADESKILL_SUCCEED 339 //You have fashioned the items together to create something new!
|
||||
#define TRADESKILL_SUCCEED 339 //You have fashioned the items together to create something new! // TODO: TOB - 339 You have %2fashioned the items together to create something new: %1.
|
||||
#define EVADE_SUCCESS 343 //You have momentarily ducked away from the main combat.
|
||||
#define EVADE_FAIL 344 //Your attempts at ducking clear of combat fail.
|
||||
#define HIDE_FAIL 345 //You failed to hide yourself.
|
||||
@ -136,11 +136,11 @@
|
||||
#define MEND_SUCCESS 350 //You mend your wounds and heal some damage.
|
||||
#define MEND_WORSEN 351 //You have worsened your wounds!
|
||||
#define MEND_FAIL 352 //You have failed to mend your wounds.
|
||||
#define LDON_SENSE_TRAP2 367 //You have not detected any traps.
|
||||
#define TRAP_TOO_FAR 368 //You are too far away from that trap to affect it.
|
||||
#define FAIL_DISARM_DETECTED_TRAP 370 //You fail to disarm the detected trap.
|
||||
#define LDON_SENSE_TRAP2 367 //You have not detected any traps. // TODO: TOB - unk
|
||||
#define TRAP_TOO_FAR 368 //You are too far away from that trap to affect it. // TODO: TOB - unk
|
||||
#define FAIL_DISARM_DETECTED_TRAP 370 //You fail to disarm the detected trap. // TODO: TOB - unk
|
||||
#define LOOT_LORE_ERROR 371 //You cannot loot this Lore Item. You already have one.
|
||||
#define PICK_LORE 379 //You cannot pick up a lore item you already possess.
|
||||
#define PICK_LORE 379 //You cannot pick up a lore item you already possess. // TODO: TOB - 379 You cannot pick up %1 because it is a lore item you already possess.
|
||||
#define POISON_TOO_HIGH 382 //This poison is too high level for you to apply.
|
||||
#define TAUNT_TOO_FAR 386 //You are too far away from your target to taunt.
|
||||
#define CORPSE_TOO_FAR 389 //The corpse is too far away to summon.
|
||||
@ -154,17 +154,17 @@
|
||||
#define SONG_NEEDS_WIND 406 //You need to play a wind instrument for this song
|
||||
#define SONG_NEEDS_STRINGS 407 //You need to play a stringed instrument for this song
|
||||
#define SONG_NEEDS_BRASS 408 //You need to play a brass instrument for this song
|
||||
#define AA_GAIN_ABILITY 410 //You have gained the ability "%T1" at a cost of %2 ability %T3.
|
||||
#define AA_IMPROVE 411 //You have improved %T1 %2 at a cost of %3 ability %T4.
|
||||
#define TAUNT_SUCCESS 412 //You taunt %1 to ignore others and attack you!
|
||||
#define AA_GAIN_ABILITY 410 //You have gained the ability "%T1" at a cost of %2 ability %T3. // TODO: TOB - You have gained the ability "%B1(1)" at a cost of %2 ability %T3.
|
||||
#define AA_IMPROVE 411 //You have improved %T1 %2 at a cost of %3 ability %T4. // TODO: TOB - You have improved %B1(1) %2 at a cost of %3 ability %T4.
|
||||
#define TAUNT_SUCCESS 412 //You taunt %1 to ignore others and attack you! // TODO: TOB - unk
|
||||
#define AA_REUSE_MSG 413 //You can use the ability %B1(1) again in %2 hour(s) %3 minute(s) %4 seconds.
|
||||
#define AA_REUSE_MSG2 414 //You can use the ability %B1(1) again in %2 minute(s) %3 seconds.
|
||||
#define YOU_HEALED 419 //%1 has healed you for %2 points of damage.
|
||||
#define YOU_HEALED 419 //%1 has healed you for %2 points of damage. // TODO: TOB - 12998 %1 healed you for %2 hit points by %3.
|
||||
#define BEGINS_TO_GLOW 422 //Your %1 begins to glow.
|
||||
#define ALREADY_INVIS 423 //%1 tries to cast an invisibility spell on you, but you are already invisible.
|
||||
#define YOU_ARE_PROTECTED 424 //%1 tries to cast a spell on you, but you are protected.
|
||||
#define TARGET_RESISTED 425 //Your target resisted the %1 spell.
|
||||
#define YOU_RESIST 426 //You resist the %1 spell!
|
||||
#define TARGET_RESISTED 425 //Your target resisted the %1 spell. // TODO: TOB - 425 %1 resisted your %2!
|
||||
#define YOU_RESIST 426 //You resist the %1 spell! // TODO: TOB - 426 You resist %1!
|
||||
#define YOU_CRIT_HEAL 427 //You perform an exceptional heal! (%1)
|
||||
#define YOU_CRIT_BLAST 428 //You deliver a critical blast! (%1)
|
||||
#define SUMMONING_CORPSE 429 //Summoning your corpse.
|
||||
@ -176,15 +176,15 @@
|
||||
#define PET_TAUNTING 438 //Taunting attacker, Master.
|
||||
#define INTERRUPT_SPELL 439 //Your spell is interrupted.
|
||||
#define LOSE_LEVEL 442 //You LOST a level! You are now level %1!
|
||||
#define GAIN_ABILITY_POINT 446 //You have gained an ability point! You now have %1 ability point%2.
|
||||
#define GAIN_ABILITY_POINT 446 //You have gained an ability point! You now have %1 ability point%2. // TODO: TOB - 446 You have gained an ability point! You now have %1 ability points. (Actual system moved to 8019-8021 and can handle multiples)
|
||||
#define GAIN_LEVEL 447 //You have gained a level! Welcome to level %1!
|
||||
#define LANG_SKILL_IMPROVED 449 //Your language skills have improved.
|
||||
#define OTHER_LOOTED_MESSAGE 466 //--%1 has looted a %2--
|
||||
#define LOOTED_MESSAGE 467 //--You have looted a %1--
|
||||
#define FACTION_WORST 469 //Your faction standing with %1 could not possibly get any worse.
|
||||
#define FACTION_WORSE 470 //Your faction standing with %1 got worse.
|
||||
#define FACTION_BEST 471 //Your faction standing with %1 could not possibly get any better.
|
||||
#define FACTION_BETTER 472 //Your faction standing with %1 got better.
|
||||
#define OTHER_LOOTED_MESSAGE 466 //--%1 has looted a %2-- // TODO: TOB - 466 --%1 has looted %2 %3 from %4.--
|
||||
#define LOOTED_MESSAGE 467 //--You have looted a %1-- // TODO: TOB - 467 --You have looted %1 %2 from %3.--
|
||||
#define FACTION_WORST 469 //Your faction standing with %1 could not possibly get any worse. // TODO: TOB - 469 Your faction standing with %B1(45) could not possibly get any worse.
|
||||
#define FACTION_WORSE 470 //Your faction standing with %1 got worse. // TODO: TOB - 14261 Your faction standing with %B1(45) has been adjusted by %2.
|
||||
#define FACTION_BEST 471 //Your faction standing with %1 could not possibly get any better. // TODO: TOB - 471 Your faction standing with %B1(45) could not possibly get any better.
|
||||
#define FACTION_BETTER 472 //Your faction standing with %1 got better. // TODO: TOB - 14261 Your faction standing with %B1(45) has been adjusted by %2.
|
||||
#define PET_REPORT_HP 488 //I have %1 percent of my hit points left.
|
||||
#define PET_NO_TAUNT 489 //No longer taunting attackers, Master.
|
||||
#define PET_DO_TAUNT 490 //Taunting attackers as normal, Master.
|
||||
@ -241,7 +241,7 @@
|
||||
#define NPC_RAMPAGE 1044 //%1 goes on a RAMPAGE!
|
||||
#define NPC_FLURRY 1045 //%1 executes a FLURRY of attacks on %2!
|
||||
#define DISCIPLINE_FEARLESS 1076 //%1 becomes fearless.
|
||||
#define DUEL_FINISHED 1088 //dont know text
|
||||
#define DUEL_FINISHED 1088 //%1 has defeated %2 in a duel to the death!
|
||||
#define EATING_MESSAGE 1091 //Chomp, chomp, chomp... %1 takes a bite from a %2.
|
||||
#define DRINKING_MESSAGE 1093 //Glug, glug, glug... %1 takes a drink from a %2.
|
||||
#define SUCCESSFUL_TAUNT 1095 //I'll teach you to interfere with me %3.
|
||||
@ -285,8 +285,8 @@
|
||||
#define MERCHANT_CLOSED_TWO 1200 //Can't you see I'm doing something here?
|
||||
#define MERCHANT_CLOSED_THREE 1201 //I am not open for business right now.
|
||||
#define AA_POINTS 1215 //points
|
||||
#define SPELL_FIZZLE_OTHER 1218 //%1's spell fizzles!
|
||||
#define MISSED_NOTE_OTHER 1219 //A missed note brings %1's song to a close!
|
||||
#define SPELL_FIZZLE_OTHER 1218 //%1's spell fizzles! // TODO: TOB - 1218 %1's %2 spell fizzles!
|
||||
#define MISSED_NOTE_OTHER 1219 //A missed note brings %1's song to a close! // TODO: TOB - 1219 A missed note brings %1's %2 to a close!
|
||||
#define SPELL_LEVEL_REQ 1226 //This spell only works on people who are level %1 and under.
|
||||
#define CORPSE_DECAY_NOW 1227 //This corpse is waiting to expire.
|
||||
#define CORPSE_ITEM_LOST 1228 //Your items will no longer stay with you when you respawn on death. You will now need to return to your corpse for your items.
|
||||
@ -327,27 +327,27 @@
|
||||
#define SENSE_CORPSE_DIRECTION 1563 //You sense a corpse in this direction.
|
||||
#define DUPE_LORE_MERCHANT 1573 //%1 tells you, 'You already have the lore item, %2, on your person, on your shroud, in the bank, in a real estate, or as an augment in another item. You cannot have more than one of a particular lore item at a time.'
|
||||
#define QUEUED_TELL 2458 //[queued]
|
||||
#define QUEUE_TELL_FULL 2459 //[zoing and queue is full]
|
||||
#define QUEUE_TELL_FULL 2459 //[zoning and queue is full]
|
||||
#define TRADER_BUSY_TWO 3192 //Sorry, that action cannot be performed while trading.
|
||||
#define SUSPEND_MINION_UNSUSPEND 3267 //%1 tells you, 'I live again...'
|
||||
#define SUSPEND_MINION_SUSPEND 3268 //%1 tells you, 'By your command, master.'
|
||||
#define ONLY_SUMMONED_PETS 3269 //3269 This effect only works with summoned pets.
|
||||
#define ONLY_SUMMONED_PETS 3269 //This effect only works with summoned pets.
|
||||
#define SUSPEND_MINION_FIGHTING 3270 //Your pet must be at peace, first.
|
||||
#define SHIELD_TARGET_NPC 3278 //You must first target a living Player Character.
|
||||
#define ALREADY_SHIELDED 3279 //Either you or your target is already being shielded.
|
||||
#define ALREADY_SHIELDING 3280 //Either you or your target is already shielding another.
|
||||
#define START_SHIELDING 3281 //%1 begins to use %2 as a living shield!
|
||||
#define END_SHIELDING 3282 //%1 ceases protecting %2.
|
||||
#define OVER_AA_CAP 3374 //Warning: You are currently over the earned Advancement point limit of %1. Please spend some of your stored AA points. The NEXT time you zone all of your AA points over %2 will be deleted. You MUST spend the extra points now.
|
||||
#define OVER_AA_CAP 3374 //Warning: You are currently over the earned Advancement point limit of %1. Please spend some of your stored AA points. The NEXT time you zone all of your AA points over %2 will be deleted. You MUST spend the extra points now. // TODO: TOB - 3374 You are currently over the earned Advancement point limit of %1. You must spend some of your ability points before you can earn more.
|
||||
#define TRADESKILL_MISSING_ITEM 3455 //You are missing a %1.
|
||||
#define TRADESKILL_MISSING_COMPONENTS 3456 //Sorry, but you don't have everything you need for this recipe in your general inventory.
|
||||
#define TRADESKILL_LEARN_RECIPE 3457 //You have learned the recipe %1!
|
||||
#define TASK_UPDATED 3471 //Your task '%1' has been updated.
|
||||
#define YOU_ASSIGNED_TASK 3472 //You have been assigned the task '%1'.
|
||||
#define DZ_YOU_BELONG 3500 //You cannot create this expedition since you already belong to another.
|
||||
#define DZ_REPLAY_YOU 3501 //You cannot create this expedition for another %1d:%2h:%3m since you have recently played here.
|
||||
#define DZ_PLAYER_COUNT 3503 //You do not meet the player count requirement. You have %1 players. You must have at least %2 and no more than %3.
|
||||
#define DZ_REPLAY_OTHER 3504 //%1 cannot be added to this expedition for another %2D:%3H:%4M since they have recently played in this area.
|
||||
#define DZ_REPLAY_YOU 3501 //You cannot create this expedition for another %1d:%2h:%3m since you have recently played here. // TODO: TOB - 3501 You cannot create this expedition for another %1d:%2h:%3m:%4s since you have recently played here.
|
||||
#define DZ_PLAYER_COUNT 3503 //You do not meet the player count requirement. You have %1 players. You must have at least %2 and no more than %3. // TODO: TOB - 3503 You do not meet the player count requirement. You have %1 players. You must have at least %2.
|
||||
#define DZ_REPLAY_OTHER 3504 //%1 cannot be added to this expedition for another %2D:%3H:%4M since they have recently played in this area. // TODO: TOB - 3504 %1 cannot be added to this expedition for another %2d:%3h:%4m:%5s since they have recently played in this area.
|
||||
#define DZ_AVAILABLE 3507 //%1 is now available to you.
|
||||
#define DZADD_INVITE 3508 //Sending an invitation to: %1.
|
||||
#define DZ_PREVENT_ENTERING 3510 //A strange magical presence prevents you from entering. It's too dangerous to enter at the moment.
|
||||
@ -358,7 +358,7 @@
|
||||
#define DZ_REMOVED 3516 //%1 has been removed from %2.
|
||||
#define DZSWAP_INVITE 3517 //Sending an invitation to: %1. They must accept in order to swap party members.
|
||||
#define DZ_LEADER_OFFLINE 3518 //%1 is not currently online. You can only transfer leadership to an online member of the expedition you are in.
|
||||
#define DZ_TIMER 3519 //You have %1d:%2h:%3m remaining until you may enter %4.
|
||||
#define DZ_TIMER 3519 //You have %1d:%2h:%3m remaining until you may enter %4. // TODO: TOB - 3519 You have %1d:%2h:%3m:%4s remaining until you may enter %5.
|
||||
#define DZ_LEADER_NAME 3520 //%1 has been made the leader for this expedition.
|
||||
#define DZ_LEADER_YOU 3521 //You have been made the leader of this expedition.
|
||||
#define DZ_INVITE_ACCEPTED 3522 //%1 has accepted your offer to join your expedition.
|
||||
@ -371,8 +371,8 @@
|
||||
#define DZ_MINUTES_REMAIN 3551 //You only have %1 minutes remaining before this expedition comes to an end.
|
||||
#define DZ_LEADER 3552 //Expedition Leader: %1
|
||||
#define DZ_MEMBERS 3553 //Expedition Members: %1
|
||||
#define DZ_EVENT_TIMER 3561 //%1 cannot be added to this expedition since they have recently experienced %2. They must wait another %3D:%4H:%5M until they can experience it again. They may be added to the expedition later, once %2 has been completed.
|
||||
#define LOOT_NOT_ALLOWED 3562 //You are not allowed to loot the item: %1.
|
||||
#define DZ_EVENT_TIMER 3561 //%1 cannot be added to this expedition since they have recently experienced %2. They must wait another %3D:%4H:%5M until they can experience it again. They may be added to the expedition later, once %2 has been completed. // TODO: TOB - 3561 %1 cannot be added to this expedition since they have recently experienced %2. They must wait another %3d:%4h:%5m:%6s until they can experience it again. They may be added to the expedition later, once %2 has been completed.
|
||||
#define LOOT_NOT_ALLOWED 3562 //You are not allowed to loot the item: %1. // TODO: TOB - 8337 & 8338 (has a reason now)
|
||||
#define DZ_UNABLE_RETRIEVE_LEADER 3583 //Unable to retrieve dynamic zone leader to check permissions.
|
||||
#define DZADD_NOT_ALLOWING 3585 //The expedition is not allowing players to be added.
|
||||
#define DZADD_NOT_ONLINE 3586 //%1 is not currently online. A player needs to be online to be added to a Dynamic Zone
|
||||
@ -380,8 +380,8 @@
|
||||
#define DZADD_ALREADY_PART 3588 //You can not add %1 since they are already part of this zone.
|
||||
#define DZADD_LEAVE_ZONE 3589 //You can not add %1 since they first need to leave the zone before being allowed back in.
|
||||
#define DZADD_ALREADY_OTHER 3590 //%1 can not be added to this dynamic zone since they are already assigned to another dynamic zone.
|
||||
#define DZADD_REPLAY_TIMER 3591 //%1 can not be added to this dynamic zone for another %2D:%3H:%4M since they have recently played this zone.
|
||||
#define DZADD_EVENT_TIMER 3592 //%1 can not be added to this dynamic zone since they have recently experienced %2. They must wait for another %3D:%4H:%5M, or until event %2 has occurred.
|
||||
#define DZADD_REPLAY_TIMER 3591 //%1 can not be added to this dynamic zone for another %2D:%3H:%4M since they have recently played this zone. // TODO: TOB - 3591 %1 can not be added to this dynamic zone for another %2d:%3h:%4m:%5s since they have recently played this zone.
|
||||
#define DZADD_EVENT_TIMER 3592 //%1 can not be added to this dynamic zone since they have recently experienced %2. They must wait for another %3D:%4H:%5M, or until event %2 has occurred. // TODO: TOB - 3592 %1 can not be added to this dynamic zone since they have recently experienced %2. They must wait for another %3d:%4h:%5m:%6s, or until event %2 has occurred.
|
||||
#define DZADD_PENDING 3593 //%1 currently has an outstanding invitation to join this Dynamic Zone.
|
||||
#define DZADD_PENDING_OTHER 3594 //%1 currently has an outstanding invitation to join another Dynamic Zone. Players may only have one invitation outstanding.
|
||||
#define DZSWAP_CANNOT_REMOVE 3595 //%1 can not be removed from this dynamic zone since they are not assigned to it.
|
||||
@ -405,9 +405,9 @@
|
||||
#define PETITION_NO_DELETE 5053 //You do not have a petition in the queue.
|
||||
#define PETITION_DELETED 5054 //Your petition was successfully deleted.
|
||||
#define ALREADY_IN_RAID 5060 //%1 is already in a raid.
|
||||
#define ALREADY_IN_YOUR_RAID 5077 //%1 is already in your raid.
|
||||
#define ALREADY_IN_YOUR_RAID 5077 //%1 is already in your raid. // TODO: TOB - 5077 That person is already in your raid.
|
||||
#define NOT_IN_YOUR_RAID 5082 //%1 is not in your raid.
|
||||
#define GAIN_RAIDEXP 5085 //You gained raid experience!
|
||||
#define GAIN_RAIDEXP 5085 //You gained raid experience! // TODO: TOB - 5085 You gained raid experience!%1
|
||||
#define ALREADY_IN_GRP_RAID 5088 //% 1 rejects your invite because they are in a raid and you are not in theirs, or they are a raid group leader
|
||||
#define DUNGEON_SEALED 5141 //The gateway to the dungeon is sealed off to you. Perhaps you would be able to enter if you needed to adventure there.
|
||||
#define ADVENTURE_COMPLETE 5147 //You received %1 points for successfully completing the adventure.
|
||||
@ -423,8 +423,8 @@
|
||||
#define MELEE_SILENCE 5806 //You *CANNOT* use this melee ability, you are suffering from amnesia!
|
||||
#define DISCIPLINE_REUSE_MSG 5807 //You can use the ability %1 again in %2 hour(s) %3 minute(s) %4 seconds.
|
||||
#define DISCIPLINE_REUSE_MSG2 5808 //You can use the ability %1 again in %2 minute(s) %3 seconds.
|
||||
#define FAILED_TAUNT 5811 //You have failed to taunt your target.
|
||||
#define PHYSICAL_RESIST_FAIL 5817 //Your target avoided your %1 ability.
|
||||
#define FAILED_TAUNT 5811 //You have failed to taunt your target. // TODO: TOB - 5811 You have failed to taunt %1.
|
||||
#define PHYSICAL_RESIST_FAIL 5817 //Your target avoided your %1 ability. // TODO: TOB - 5817 %1 avoided your %2!
|
||||
#define AA_NO_TARGET 5825 //You must first select a target for this ability!
|
||||
#define YOU_RECEIVE 5941 //You receive %1.
|
||||
#define NO_TASK_OFFERS 6009 //Sorry %3, I don't have anything for someone with your abilities.
|
||||
@ -447,7 +447,7 @@
|
||||
#define TRANSFORM_COMPLETE 6327 //You have successfully transformed your %1.
|
||||
#define DETRANSFORM_FAILED 6341 //%1 has no transformation that can be removed.
|
||||
#define GUILD_PERMISSION_FAILED 6418 //You do not have permission to change access options.
|
||||
#define PARCEL_DELIVERY_ARRIVED 6465 //You have received a new parcel delivery!
|
||||
#define PARCEL_DELIVERY_ARRIVED 6465 //You have received a new parcel delivery! // TODO: TOB - 6465 You have received a new parcel delivery %1!
|
||||
#define PARCEL_DELIVERY 6466 //%1 tells you, 'I will deliver the %2 to %3 as soon as possible!'
|
||||
#define PARCEL_UNKNOWN_NAME 6467 //%1 tells you, 'Unfortunately, I don't know anyone by the name of %2. Here is your %3 back.''
|
||||
#define PARCEL_DELIVERED 6471 //%1 hands you the %2 that was sent from %3.
|
||||
@ -474,7 +474,7 @@
|
||||
#define LDON_CERTAIN_TRAP 7557 //You are certain that %1 is trapped.
|
||||
#define LDON_CERTAIN_NOT_TRAP 7558 //You are certain that %1 is not trapped.
|
||||
#define LDON_CANT_DETERMINE_TRAP 7559 //You are unable to determine if %1 is trapped.
|
||||
#define LDON_PICKLOCK_SUCCESS 7560 //You have successfully picked %1!
|
||||
#define LDON_PICKLOCK_SUCCESS 7560 //You have successfully picked %1! // TODO: TOB - 7560 You have successfully picked %1%2!
|
||||
#define LDON_PICKLOCK_FAILURE 7561 //You have failed to pick %1.
|
||||
#define LDON_STILL_LOCKED 7562 //You cannot open %1, it is locked.
|
||||
#define LDON_BASH_CHEST 7563 //%1 try to %2 %3, but do no damage.
|
||||
@ -497,7 +497,7 @@
|
||||
#define NOT_DELEGATED_MARKER 8794 //You have not been delegated Raid Mark
|
||||
#define GAIN_GROUP_LEADERSHIP_EXP 8788 //
|
||||
#define GAIN_RAID_LEADERSHIP_EXP 8789 //
|
||||
#define BUFF_MINUTES_REMAINING 8799 //%1 (%2 minutes remaining)
|
||||
#define BUFF_MINUTES_REMAINING 8799 //%1 (%2 minutes remaining) // TODO: TOB - 8799 --You sense %1%2 on %3 has %4.--
|
||||
#define RAID_NO_LONGER_MARKED 8801 //%1 is no longer marked
|
||||
#define YOU_HAVE_BEEN_GIVEN 8994 //You have been given: %1
|
||||
#define NO_MORE_TRAPS 9002 //You have already placed your maximum number of traps.
|
||||
@ -508,13 +508,13 @@
|
||||
#define SPELL_OPPOSITE_EFFECT 9032 //Your spell may have had the opposite effect of what you desired.
|
||||
#define HAS_BEEN_AWAKENED 9037 //%1 has been awakened by %2.
|
||||
#define YOU_HEAL 9068 //You have healed %1 for %2 points of damage.
|
||||
#define YOUR_HIT_DOT 9072 //%1 has taken %2 damage from your %3.
|
||||
#define HIT_NON_MELEE 9073 //%1 hit %2 for %3 points of non-melee damage.
|
||||
#define YOUR_HIT_DOT 9072 //%1 has taken %2 damage from your %3. // TODO: TOB - 9072 %1 has taken %2 damage from your %3.%4
|
||||
#define HIT_NON_MELEE 9073 //%1 hit %2 for %3 points of non-melee damage. // TODO: TOB - 9073 %1 hit %2 for %3 points of %4 damage by %5.%6
|
||||
#define GLOWS_BLUE 9074 //Your %1 glows blue.
|
||||
#define GLOWS_RED 9075 //Your %1 glows red.
|
||||
#define SHAKE_OFF_STUN 9077 //You shake off the stun effect!
|
||||
#define STRIKETHROUGH_STRING 9078 //You strike through your opponent's defenses!
|
||||
#define SPELL_REFLECT 9082 //%1's spell has been reflected by %2.
|
||||
#define SPELL_REFLECT 9082 //%1's spell has been reflected by %2. // TODO: TOB - 9082 %1's %2 spell has been reflected by %3.
|
||||
#define NO_MORE_AURAS 9160 //You do not have sufficient focus to maintain that ability.
|
||||
#define NEW_SPELLS_AVAIL 9149 //You have new spells available to you. Check the merchants near your guild master.
|
||||
#define FD_CAST_ON_NO_BREAK 9174 //The strength of your will allows you to resume feigning death.
|
||||
@ -527,17 +527,17 @@
|
||||
#define NO_CAST_OUT_OF_COMBAT 9191 //You can not cast this spell while out of combat.
|
||||
#define NO_ABILITY_IN_COMBAT 9192 //You can not use this ability while in combat.
|
||||
#define NO_ABILITY_OUT_OF_COMBAT 9194 //You can not use this ability while out of combat.
|
||||
#define GAIN_GROUPXP_BONUS 9298 //You gain party experience (with a bonus)!
|
||||
#define GAIN_GROUPXP_PENALTY 9301 //You gain party experience (with a penalty)!
|
||||
#define GAIN_RAIDXP_BONUS 9302 //You gained raid experience (with a bonus)!
|
||||
#define GAIN_RAIDXP_PENALTY 9303 //You gained raid experience (with a penalty)!
|
||||
#define GAIN_GROUPXP_BONUS 9298 //You gain party experience (with a bonus)! // TODO: TOB - 9298 You gain party experience (with a bonus)!%1
|
||||
#define GAIN_GROUPXP_PENALTY 9301 //You gain party experience (with a penalty)! // TODO: TOB - 9301 You gain party experience (with a penalty)!%1
|
||||
#define GAIN_RAIDXP_BONUS 9302 //You gained raid experience (with a bonus)! // TODO: TOB - 9302 You gained raid experience (with a bonus)!%1
|
||||
#define GAIN_RAIDXP_PENALTY 9303 //You gained raid experience (with a penalty)! // TODO: TOB - 9303 You gained raid experience (with a penalty)!%1
|
||||
#define LESSER_SPELL_VERSION 11004 //%1 is a lesser version of %2 and cannot be scribed
|
||||
#define AE_RAMPAGE 11015 //%1 goes on a WILD RAMPAGE!
|
||||
#define GROUP_IS_FULL 12000 //You cannot join that group, it is full.
|
||||
#define FACE_ACCEPTED 12028 //Facial features accepted.
|
||||
#define TRACKING_BEGIN 12040 //You begin tracking %1.
|
||||
#define SPELL_LEVEL_TO_LOW 12048 //You will have to achieve level %1 before you can scribe the %2.
|
||||
#define YOU_RECEIVE_AS_SPLIT 12071 //You receive %1 as your split.
|
||||
#define YOU_RECEIVE_AS_SPLIT 12071 //You receive %1 as your split. // TODO: TOB - 12072 You receive %1 from the corpse%2.
|
||||
#define ATTACKFAILED 12158 //%1 try to %2 %3, but %4!
|
||||
#define HIT_STRING 12183 //hit
|
||||
#define CRUSH_STRING 12191 //crush
|
||||
@ -554,7 +554,7 @@
|
||||
#define TARGET_PLAYER_FOR_GUILD_STATUS 12260
|
||||
#define TARGET_ALREADY_IN_GROUP 12265 //% 1 is already in another group.
|
||||
#define GROUP_INVITEE_NOT_FOUND 12268 //You must target a player or use /invite <name> to invite someone to your group.
|
||||
#define GROUP_INVITEE_SELF 12270 //12270 You cannot invite yourself.
|
||||
#define GROUP_INVITEE_SELF 12270 //You cannot invite yourself.
|
||||
#define ALREADY_IN_PARTY 12272 //That person is already in your party.
|
||||
#define TALKING_TO_SELF 12323 //Talking to yourself again?
|
||||
#define SPLIT_NO_GROUP 12328 //You are not in a group! Keep it all.
|
||||
@ -593,7 +593,7 @@
|
||||
#define RANGED_TOO_CLOSE 12698 //Your target is too close to use a ranged weapon!
|
||||
#define BACKSTAB_WEAPON 12874 //You need a piercing weapon as your primary weapon in order to backstab
|
||||
#define DISARMED 12889 //You have been disarmed!
|
||||
#define DISARM_SUCCESS 12890 //You disarmed %1!
|
||||
#define DISARM_SUCCESS 12890 //You disarmed %1! // TODO: TOB - 12890 You %2disarmed %1!
|
||||
#define DISARM_FAILED 12891 //Your attempt to disarm failed.
|
||||
#define MORE_SKILLED_THAN_I 12931 //%1 tells you, 'You are more skilled than I! What could I possibly teach you?'
|
||||
#define SURNAME_EXISTS 12939 //You already have a surname. Operation failed.
|
||||
@ -602,7 +602,7 @@
|
||||
#define REPORT_ONCE 12945 //You may only submit a report once per time that you zone. Thank you.
|
||||
#define NOW_INVISIBLE 12950 //%1 is now Invisible.
|
||||
#define NOW_VISIBLE 12951 //%1 is now Visible.
|
||||
#define YOU_TAKE_DOT 12954 //You have taken %1 damage from %2 by %3
|
||||
#define YOU_TAKE_DOT 12954 //You have taken %1 damage from %2 by %3 // TODO: TOB - 12954 You have taken %1 damage from %2 by %3.%4
|
||||
#define GUILD_NOT_MEMBER2 12966 //You are not in a guild.
|
||||
#define HOT_HEAL_SELF 12976 //You have been healed for %1 hit points by your %2.
|
||||
#define HOT_HEAL_OTHER 12997 //You have healed %1 for %2 hit points with your %3.
|
||||
@ -612,7 +612,7 @@
|
||||
#define TOGGLE_ON 13172 //Asking server to turn ON your incoming tells.
|
||||
#define TOGGLE_OFF 13173 //Asking server to turn OFF all incoming tells for you.
|
||||
#define DUEL_INPROGRESS 13251 //You have already accepted a duel with someone else cowardly dog.
|
||||
#define OTHER_HIT_DOT 13327 //%1 has taken %2 damage from %3 by %4.
|
||||
#define GAIN_XP_BONUS 14541 //You gain experience (with a bonus)!
|
||||
#define GAIN_XP_PENALTY 14542 //You gain experience (with a penalty)!
|
||||
#define OTHER_HIT_DOT 13327 //%1 has taken %2 damage from %3 by %4. // TODO: TOB - 13327 %1 has taken %2 damage from %3 by %4.%5
|
||||
#define GAIN_XP_BONUS 14541 //You gain experience (with a bonus)! // TODO: TOB - 14541 You gain experience (with a bonus)!%1
|
||||
#define GAIN_XP_PENALTY 14542 //You gain experience (with a penalty)! // TODO: TOB - 14542 You gain experience (with a penalty)!%1
|
||||
#define GENERIC_MISS 15041 //%1 missed %2
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user