diff --git a/changelog.txt b/changelog.txt index 5aa90cfc9..1b5483e73 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,13 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 01/06/2015 == +Trevius: Changed the pet command #defines to be based on RoF2 list of pet commands and added decodes to Titanium, SoF and SoD. +Trevius: (RoF+) The /pet focus on/off and /pet hold on/off commands are now functional. +Trevius: Added defines for all remaining pet commands and some support for them as well. + +== 01/05/2015 == +Uleat: Fixed (added translators for) item/text links. Only 'OP_ChannelMessage' and 'OP_SpecialMesg' are currently handled..more text channels will be added as the need arises. + == 01/03/2015 == Trevius: (RoF2) /shield (shielding) and /key (key ring) are both now functional after opcode updates. diff --git a/common/eq_dictionary.h b/common/eq_dictionary.h index 43511072f..3438f133b 100644 --- a/common/eq_dictionary.h +++ b/common/eq_dictionary.h @@ -149,6 +149,8 @@ public: static const uint32 BANDOLIER_SIZE = Titanium::consts::BANDOLIER_SIZE; // size = number of equipment slots in bandolier instance static const uint32 POTION_BELT_SIZE = Titanium::consts::POTION_BELT_SIZE; + static const size_t TEXT_LINK_BODY_LENGTH = 56; + // legacy-related functions //static int ServerToPerlSlot(int slot); // encode //static int PerlToServerSlot(int slot); // decode diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 07daa97fd..0c91d96f8 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -5269,6 +5269,23 @@ struct ClientMarqueeMessage_Struct { typedef std::list ItemList; +struct TextLinkBody_Struct { + // Current server mask: EQClientRoF2 + uint8 unknown_1; /* %1X */ + uint32 item_id; /* %05X */ + uint32 augment_1; /* %05X */ + uint32 augment_2; /* %05X */ + uint32 augment_3; /* %05X */ + uint32 augment_4; /* %05X */ + uint32 augment_5; /* %05X */ + uint32 augment_6; /* %05X */ + uint8 is_evolving; /* %1X */ + uint32 evolve_group; /* %05X */ + uint8 evolve_level; /* %02X */ + uint32 ornament_icon; /* %05X */ + int hash; /* %08X */ +}; + // Restore structure packing to default #pragma pack() diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 36d487545..f65302c36 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -33,6 +33,12 @@ namespace RoF static inline uint32 RoFToServerMainInvSlot(structs::MainInvItemSlotStruct RoFSlot); static inline uint32 RoFToServerCorpseSlot(uint32 RoFCorpse); + // server to client text link converter + static inline void ServerToRoFTextLink(std::string& rofTextLink, const std::string& serverTextLink); + + // client to server text link converter + static inline void RoFToServerTextLink(std::string& serverTextLink, const std::string& rofTextLink); + void Register(EQStreamIdentifier &into) { //create our opcode manager if we havent already @@ -488,7 +494,13 @@ namespace RoF unsigned char *__emu_buffer = in->pBuffer; - in->size = strlen(emu->sender) + 1 + strlen(emu->targetname) + 1 + strlen(emu->message) + 1 + 36; + std::string old_message = emu->message; + std::string new_message; + ServerToRoFTextLink(new_message, old_message); + + //in->size = strlen(emu->sender) + 1 + strlen(emu->targetname) + 1 + strlen(emu->message) + 1 + 36; + in->size = strlen(emu->sender) + strlen(emu->targetname) + new_message.length() + 39; + in->pBuffer = new unsigned char[in->size]; char *OutBuffer = (char *)in->pBuffer; @@ -501,7 +513,7 @@ namespace RoF VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->skill_in_language); - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->message); + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown @@ -3096,6 +3108,44 @@ namespace RoF FINISH_ENCODE(); } + ENCODE(OP_SpecialMesg) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + SpecialMesg_Struct *emu = (SpecialMesg_Struct *)in->pBuffer; + + unsigned char *__emu_buffer = in->pBuffer; + + std::string old_message = &emu->message[strlen(emu->sayer)]; + std::string new_message; + ServerToRoFTextLink(new_message, old_message); + + //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; + in->size = 25 + strlen(emu->sayer) + new_message.length(); + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[0]); + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[1]); + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[2]); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->msg_type); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->target_spawn_id); + + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sayer); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + ENCODE(OP_Stun) { ENCODE_LENGTH_EXACT(Stun_Struct); @@ -4041,7 +4091,13 @@ namespace RoF uint32 Skill = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - __packet->size = sizeof(ChannelMessage_Struct)+strlen(InBuffer) + 1; + std::string old_message = InBuffer; + std::string new_message; + RoFToServerTextLink(new_message, old_message); + + //__packet->size = sizeof(ChannelMessage_Struct)+strlen(InBuffer) + 1; + __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; + __packet->pBuffer = new unsigned char[__packet->size]; ChannelMessage_Struct *emu = (ChannelMessage_Struct *)__packet->pBuffer; @@ -4050,7 +4106,7 @@ namespace RoF emu->language = Language; emu->chan_num = Channel; emu->skill_in_language = Skill; - strcpy(emu->message, InBuffer); + strcpy(emu->message, new_message.c_str()); delete[] __eq_buffer; } @@ -4456,47 +4512,8 @@ namespace RoF DECODE_LENGTH_EXACT(structs::PetCommand_Struct); SETUP_DIRECT_DECODE(PetCommand_Struct, structs::PetCommand_Struct); - switch (eq->command) - { - case 0x00: - emu->command = 0x04; // Health - break; - case 0x01: - emu->command = 0x10; // Leader - break; - case 0x02: - emu->command = 0x07; // Attack - break; - case 0x04: - emu->command = 0x08; // Follow - break; - case 0x05: - emu->command = 0x05; // Guard - break; - case 0x06: - emu->command = 0x09; // Sit. Needs work. This appears to be a toggle between Sit/Stand now. - break; - case 0x0c: - emu->command = 0x0b; // Taunt - break; - case 0x0f: - emu->command = 0x0c; // Hold - break; - case 0x10: - emu->command = 0x1b; // Hold on - break; - case 0x11: - emu->command = 0x1c; // Hold off - break; - case 0x1c: - emu->command = 0x01; // Back - break; - case 0x1d: - emu->command = 0x02; // Leave/Go Away - break; - default: - emu->command = eq->command; - } + IN(command); + emu->unknown = eq->unknown04; FINISH_DIRECT_DECODE(); } @@ -5627,5 +5644,78 @@ namespace RoF { return (RoFCorpse - 1); } + + static inline void ServerToRoFTextLink(std::string& rofTextLink, const std::string& serverTextLink) + { + const char delimiter = 0x12; + + if ((consts::TEXT_LINK_BODY_LENGTH == EmuConstants::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find(delimiter) == std::string::npos)) { + rofTextLink = serverTextLink; + return; + } + + auto segments = SplitString(serverTextLink, delimiter); + + for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { + if (segment_iter & 1) { + std::string new_segment; + + // Idx: 0 1 6 11 16 21 26 31 36 37 41 43 48 (Source) + // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) + // RoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (55) + // Diff: ^ + + new_segment.append(segments[segment_iter].substr(0, 41).c_str()); + + if (segments[segment_iter].substr(41, 1) == "0") + new_segment.append(segments[segment_iter].substr(42, 1).c_str()); + else + new_segment.append("F"); + + new_segment.append(segments[segment_iter].substr(43).c_str()); + + rofTextLink.push_back(delimiter); + rofTextLink.append(new_segment.c_str()); + rofTextLink.push_back(delimiter); + } + else { + rofTextLink.append(segments[segment_iter].c_str()); + } + } + } + + static inline void RoFToServerTextLink(std::string& serverTextLink, const std::string& rofTextLink) + { + const char delimiter = 0x12; + + if ((EmuConstants::TEXT_LINK_BODY_LENGTH == consts::TEXT_LINK_BODY_LENGTH) || (rofTextLink.find(delimiter) == std::string::npos)) { + serverTextLink = rofTextLink; + return; + } + + auto segments = SplitString(rofTextLink, delimiter); + + for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { + if (segment_iter & 1) { + std::string new_segment; + + // Idx: 0 1 6 11 16 21 26 31 36 37 41 42 47 (Source) + // RoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (55) + // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) + // Diff: ^ + + new_segment.append(segments[segment_iter].substr(0, 41).c_str()); + new_segment.append("0"); + new_segment.append(segments[segment_iter].substr(41).c_str()); + + serverTextLink.push_back(delimiter); + serverTextLink.append(new_segment.c_str()); + serverTextLink.push_back(delimiter); + } + else { + serverTextLink.append(segments[segment_iter].c_str()); + } + } + } } // end namespace RoF diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 02ac289a9..03d919dd8 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -33,6 +33,12 @@ namespace RoF2 static inline uint32 RoF2ToServerMainInvSlot(structs::MainInvItemSlotStruct RoF2Slot); static inline uint32 RoF2ToServerCorpseSlot(uint32 RoF2Corpse); + // server to client text link converter + static inline void ServerToRoF2TextLink(std::string& rof2TextLink, const std::string& serverTextLink); + + // client to server text link converter + static inline void RoF2ToServerTextLink(std::string& serverTextLink, const std::string& rof2TextLink); + void Register(EQStreamIdentifier &into) { //create our opcode manager if we havent already @@ -554,7 +560,13 @@ namespace RoF2 unsigned char *__emu_buffer = in->pBuffer; - in->size = strlen(emu->sender) + 1 + strlen(emu->targetname) + 1 + strlen(emu->message) + 1 + 36; + std::string old_message = emu->message; + std::string new_message; + ServerToRoF2TextLink(new_message, old_message); + + //in->size = strlen(emu->sender) + 1 + strlen(emu->targetname) + 1 + strlen(emu->message) + 1 + 36; + in->size = strlen(emu->sender) + strlen(emu->targetname) + new_message.length() + 39; + in->pBuffer = new unsigned char[in->size]; char *OutBuffer = (char *)in->pBuffer; @@ -567,7 +579,7 @@ namespace RoF2 VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->skill_in_language); - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->message); + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown @@ -3162,6 +3174,44 @@ namespace RoF2 FINISH_ENCODE(); } + ENCODE(OP_SpecialMesg) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + SpecialMesg_Struct *emu = (SpecialMesg_Struct *)in->pBuffer; + + unsigned char *__emu_buffer = in->pBuffer; + + std::string old_message = &emu->message[strlen(emu->sayer)]; + std::string new_message; + ServerToRoF2TextLink(new_message, old_message); + + //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; + in->size = 25 + strlen(emu->sayer) + new_message.length(); + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[0]); + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[1]); + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[2]); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->msg_type); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->target_spawn_id); + + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sayer); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + ENCODE(OP_Stun) { ENCODE_LENGTH_EXACT(Stun_Struct); @@ -4113,7 +4163,13 @@ namespace RoF2 uint32 Skill = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - __packet->size = sizeof(ChannelMessage_Struct)+strlen(InBuffer) + 1; + std::string old_message = InBuffer; + std::string new_message; + RoF2ToServerTextLink(new_message, old_message); + + //__packet->size = sizeof(ChannelMessage_Struct)+strlen(InBuffer) + 1; + __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; + __packet->pBuffer = new unsigned char[__packet->size]; ChannelMessage_Struct *emu = (ChannelMessage_Struct *)__packet->pBuffer; @@ -4122,7 +4178,7 @@ namespace RoF2 emu->language = Language; emu->chan_num = Channel; emu->skill_in_language = Skill; - strcpy(emu->message, InBuffer); + strcpy(emu->message, new_message.c_str()); delete[] __eq_buffer; } @@ -4527,47 +4583,8 @@ namespace RoF2 DECODE_LENGTH_EXACT(structs::PetCommand_Struct); SETUP_DIRECT_DECODE(PetCommand_Struct, structs::PetCommand_Struct); - switch (eq->command) - { - case 0x00: - emu->command = 0x04; // Health - break; - case 0x01: - emu->command = 0x10; // Leader - break; - case 0x02: - emu->command = 0x07; // Attack - break; - case 0x04: - emu->command = 0x08; // Follow - break; - case 0x05: - emu->command = 0x05; // Guard - break; - case 0x06: - emu->command = 0x09; // Sit. Needs work. This appears to be a toggle between Sit/Stand now. - break; - case 0x0c: - emu->command = 0x0b; // Taunt - break; - case 0x0f: - emu->command = 0x0c; // Hold - break; - case 0x10: - emu->command = 0x1b; // Hold on - break; - case 0x11: - emu->command = 0x1c; // Hold off - break; - case 0x1c: - emu->command = 0x01; // Back - break; - case 0x1d: - emu->command = 0x02; // Leave/Go Away - break; - default: - emu->command = eq->command; - } + IN(command); + emu->unknown = eq->unknown04; FINISH_DIRECT_DECODE(); } @@ -5722,5 +5739,69 @@ namespace RoF2 { return (RoF2Corpse + EmuConstants::CORPSE_BEGIN - 1); } + + static inline void ServerToRoF2TextLink(std::string& rof2TextLink, const std::string& serverTextLink) + { + const char delimiter = 0x12; + + if ((consts::TEXT_LINK_BODY_LENGTH == EmuConstants::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find(delimiter) == std::string::npos)) { + rof2TextLink = serverTextLink; + return; + } + + auto segments = SplitString(serverTextLink, delimiter); + + for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { + if (segment_iter & 1) { + std::string new_segment; + + // Idx: 0 1 6 11 16 21 26 31 36 37 41 43 48 (Source) + // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) + // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) + // Diff: + + new_segment.append(segments[segment_iter]); + + rof2TextLink.push_back(delimiter); + rof2TextLink.append(new_segment.c_str()); + rof2TextLink.push_back(delimiter); + } + else { + rof2TextLink.append(segments[segment_iter].c_str()); + } + } + } + + static inline void RoF2ToServerTextLink(std::string& serverTextLink, const std::string& rof2TextLink) + { + const char delimiter = 0x12; + + if ((EmuConstants::TEXT_LINK_BODY_LENGTH == consts::TEXT_LINK_BODY_LENGTH) || (rof2TextLink.find(delimiter) == std::string::npos)) { + serverTextLink = rof2TextLink; + return; + } + + auto segments = SplitString(rof2TextLink, delimiter); + + for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { + if (segment_iter & 1) { + std::string new_segment; + + // Idx: 0 1 6 11 16 21 26 31 36 37 41 43 48 (Source) + // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) + // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) + // Diff: + + new_segment.append(segments[segment_iter]); + + serverTextLink.push_back(delimiter); + serverTextLink.append(new_segment.c_str()); + serverTextLink.push_back(delimiter); + } + else { + serverTextLink.append(segments[segment_iter].c_str()); + } + } + } } // end namespace RoF2 diff --git a/common/patches/rof2_constants.h b/common/patches/rof2_constants.h index 596ffa7e7..b4cbb15e0 100644 --- a/common/patches/rof2_constants.h +++ b/common/patches/rof2_constants.h @@ -181,6 +181,8 @@ namespace RoF2 { static const uint32 BANDOLIERS_COUNT = 20; // count = number of bandolier instances static const uint32 BANDOLIER_SIZE = 4; // size = number of equipment slots in bandolier instance static const uint32 POTION_BELT_SIZE = 5; + + static const size_t TEXT_LINK_BODY_LENGTH = 56; } namespace limits { diff --git a/common/patches/rof2_ops.h b/common/patches/rof2_ops.h index 79614504d..66b86d756 100644 --- a/common/patches/rof2_ops.h +++ b/common/patches/rof2_ops.h @@ -95,6 +95,7 @@ E(OP_SkillUpdate) E(OP_SomeItemPacketMaybe) E(OP_SpawnAppearance) E(OP_SpawnDoor) +E(OP_SpecialMesg) E(OP_Stun) E(OP_TargetBuffs) E(OP_TaskDescription) diff --git a/common/patches/rof_constants.h b/common/patches/rof_constants.h index 97cb25aa5..233a1b36c 100644 --- a/common/patches/rof_constants.h +++ b/common/patches/rof_constants.h @@ -180,6 +180,8 @@ namespace RoF { static const uint32 BANDOLIERS_COUNT = 20; // count = number of bandolier instances static const uint32 BANDOLIER_SIZE = 4; // size = number of equipment slots in bandolier instance static const uint32 POTION_BELT_SIZE = 5; + + static const size_t TEXT_LINK_BODY_LENGTH = 55; } namespace limits { diff --git a/common/patches/rof_ops.h b/common/patches/rof_ops.h index 878870a81..ef97dda99 100644 --- a/common/patches/rof_ops.h +++ b/common/patches/rof_ops.h @@ -84,6 +84,7 @@ E(OP_SkillUpdate) E(OP_SomeItemPacketMaybe) E(OP_SpawnAppearance) E(OP_SpawnDoor) +E(OP_SpecialMesg) E(OP_Stun) E(OP_TargetBuffs) E(OP_TaskDescription) diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index 7355526c4..6982290c8 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -31,6 +31,12 @@ namespace SoD static inline uint32 SoDToServerSlot(uint32 SoDSlot); static inline uint32 SoDToServerCorpseSlot(uint32 SoDCorpse); + // server to client text link converter + static inline void ServerToSoDTextLink(std::string& sodTextLink, const std::string& serverTextLink); + + // client to server text link converter + static inline void SoDToServerTextLink(std::string& serverTextLink, const std::string& sodTextLink); + void Register(EQStreamIdentifier &into) { //create our opcode manager if we havent already @@ -296,6 +302,35 @@ namespace SoD FINISH_ENCODE(); } + ENCODE(OP_ChannelMessage) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + ChannelMessage_Struct *emu = (ChannelMessage_Struct *)in->pBuffer; + + unsigned char *__emu_buffer = in->pBuffer; + + std::string old_message = emu->message; + std::string new_message; + ServerToSoDTextLink(new_message, old_message); + + in->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; + + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + memcpy(OutBuffer, __emu_buffer, sizeof(ChannelMessage_Struct)); + + OutBuffer += sizeof(ChannelMessage_Struct); + + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + ENCODE(OP_CharInventory) { //consume the packet @@ -1963,6 +1998,44 @@ namespace SoD FINISH_ENCODE(); } + ENCODE(OP_SpecialMesg) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + SpecialMesg_Struct *emu = (SpecialMesg_Struct *)in->pBuffer; + + unsigned char *__emu_buffer = in->pBuffer; + + std::string old_message = &emu->message[strlen(emu->sayer)]; + std::string new_message; + ServerToSoDTextLink(new_message, old_message); + + //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; + in->size = 25 + strlen(emu->sayer) + new_message.length(); + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[0]); + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[1]); + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[2]); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->msg_type); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->target_spawn_id); + + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sayer); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + ENCODE(OP_Stun) { ENCODE_LENGTH_EXACT(Stun_Struct); @@ -2729,6 +2802,25 @@ namespace SoD FINISH_DIRECT_DECODE(); } + DECODE(OP_ChannelMessage) + { + unsigned char *__eq_buffer = __packet->pBuffer; + + std::string old_message = (char *)&__eq_buffer[sizeof(ChannelMessage_Struct)]; + std::string new_message; + SoDToServerTextLink(new_message, old_message); + + __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; + __packet->pBuffer = new unsigned char[__packet->size]; + + ChannelMessage_Struct *emu = (ChannelMessage_Struct *)__packet->pBuffer; + + memcpy(emu, __eq_buffer, sizeof(ChannelMessage_Struct)); + strcpy(emu->message, new_message.c_str()); + + delete[] __eq_buffer; + } + DECODE(OP_CharacterCreate) { DECODE_LENGTH_EXACT(structs::CharCreate_Struct); @@ -3007,6 +3099,89 @@ namespace SoD FINISH_DIRECT_DECODE(); } + DECODE(OP_PetCommands) + { + DECODE_LENGTH_EXACT(structs::PetCommand_Struct); + SETUP_DIRECT_DECODE(PetCommand_Struct, structs::PetCommand_Struct); + + switch (eq->command) + { + case 0x04: + emu->command = 0x00; // /pet health + break; + case 0x10: + emu->command = 0x01; // /pet leader + break; + case 0x07: + emu->command = 0x02; // /pet attack or Pet Window + break; + case 0x03: // Case Guessed + emu->command = 0x03; // /pet qattack + case 0x08: + emu->command = 0x04; // /pet follow or Pet Window + break; + case 0x05: + emu->command = 0x05; // /pet guard or Pet Window + break; + case 0x09: + emu->command = 0x07; // /pet sit or Pet Window + break; + case 0x0a: + emu->command = 0x08; // /pet stand or Pet Window + break; + case 0x06: + emu->command = 0x1e; // /pet guard me + break; + case 0x0f: // Case Made Up + emu->command = 0x09; // /pet stop + break; + case 0x0b: + emu->command = 0x0d; // /pet taunt or Pet Window + break; + case 0x0e: + emu->command = 0x0e; // /pet notaunt or Pet Window + break; + case 0x0c: + emu->command = 0x0f; // /pet hold + break; + case 0x1b: + emu->command = 0x10; // /pet hold on + break; + case 0x1c: + emu->command = 0x11; // /pet hold off + break; + case 0x11: + emu->command = 0x12; // Slumber? + break; + case 0x12: + emu->command = 0x15; // /pet no cast + break; + case 0x0d: // Case Made Up + emu->command = 0x16; // Pet Window No Cast + break; + case 0x13: + emu->command = 0x18; // /pet focus + break; + case 0x19: + emu->command = 0x19; // /pet focus on + break; + case 0x1a: + emu->command = 0x1a; // /pet focus off + break; + case 0x01: + emu->command = 0x1c; // /pet back off + break; + case 0x02: + emu->command = 0x1d; // /pet get lost + break; + default: + emu->command = eq->command; + } + OUT(unknown); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_RaidInvite) { DECODE_LENGTH_ATLEAST(structs::RaidGeneral_Struct); @@ -3683,5 +3858,81 @@ namespace SoD //uint32 ServerCorpse; return (SoDCorpse - 1); } + + static inline void ServerToSoDTextLink(std::string& sodTextLink, const std::string& serverTextLink) + { + const char delimiter = 0x12; + + if ((consts::TEXT_LINK_BODY_LENGTH == EmuConstants::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find(delimiter) == std::string::npos)) { + sodTextLink = serverTextLink; + return; + } + + auto segments = SplitString(serverTextLink, delimiter); + + for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { + if (segment_iter & 1) { + std::string new_segment; + + // Idx: 0 1 6 11 16 21 26 31 36 37 41 43 48 (Source) + // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) + // SoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (50) + // Diff: ^^^^^ ^ + + new_segment.append(segments[segment_iter].substr(0, 31).c_str()); + new_segment.append(segments[segment_iter].substr(36, 5).c_str()); + + if (segments[segment_iter].substr(41, 1) == "0") + new_segment.append(segments[segment_iter].substr(42, 1).c_str()); + else + new_segment.append("F"); + + new_segment.append(segments[segment_iter].substr(43).c_str()); + + sodTextLink.push_back(delimiter); + sodTextLink.append(new_segment.c_str()); + sodTextLink.push_back(delimiter); + } + else { + sodTextLink.append(segments[segment_iter].c_str()); + } + } + } + + static inline void SoDToServerTextLink(std::string& serverTextLink, const std::string& sodTextLink) + { + const char delimiter = 0x12; + + if ((EmuConstants::TEXT_LINK_BODY_LENGTH == consts::TEXT_LINK_BODY_LENGTH) || (sodTextLink.find(delimiter) == std::string::npos)) { + serverTextLink = sodTextLink; + return; + } + + auto segments = SplitString(sodTextLink, delimiter); + + for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { + if (segment_iter & 1) { + std::string new_segment; + + // Idx: 0 1 6 11 16 21 26 31 32 36 37 42 (Source) + // SoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (50) + // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) + // Diff: ^^^^^ ^ + + new_segment.append(segments[segment_iter].substr(0, 31).c_str()); + new_segment.append("00000"); + new_segment.append(segments[segment_iter].substr(31, 5).c_str()); + new_segment.append("0"); + new_segment.append(segments[segment_iter].substr(36).c_str()); + + serverTextLink.push_back(delimiter); + serverTextLink.append(new_segment.c_str()); + serverTextLink.push_back(delimiter); + } + else { + serverTextLink.append(segments[segment_iter].c_str()); + } + } + } } // end namespace SoD diff --git a/common/patches/sod_constants.h b/common/patches/sod_constants.h index 8bdf45532..89749fd56 100644 --- a/common/patches/sod_constants.h +++ b/common/patches/sod_constants.h @@ -177,6 +177,8 @@ namespace SoD { static const uint32 BANDOLIERS_COUNT = 20; // count = number of bandolier instances static const uint32 BANDOLIER_SIZE = 4; // size = number of equipment slots in bandolier instance static const uint32 POTION_BELT_SIZE = 5; + + static const size_t TEXT_LINK_BODY_LENGTH = 50; } namespace limits { diff --git a/common/patches/sod_ops.h b/common/patches/sod_ops.h index 038b42e08..0a0a8a4d5 100644 --- a/common/patches/sod_ops.h +++ b/common/patches/sod_ops.h @@ -8,6 +8,7 @@ E(OP_Barter) E(OP_BazaarSearch) E(OP_Buff) E(OP_CancelTrade) +E(OP_ChannelMessage) E(OP_CharInventory) E(OP_ClientUpdate) E(OP_Consider) @@ -57,6 +58,7 @@ E(OP_ShopPlayerBuy) E(OP_ShopPlayerSell) E(OP_SomeItemPacketMaybe) E(OP_SpawnDoor) +E(OP_SpecialMesg) E(OP_Stun) E(OP_TargetBuffs) E(OP_Track) @@ -81,6 +83,7 @@ D(OP_BazaarSearch) D(OP_Buff) D(OP_Bug) D(OP_CastSpell) +D(OP_ChannelMessage) D(OP_CharacterCreate) D(OP_ClientUpdate) D(OP_Consider) @@ -101,6 +104,7 @@ D(OP_ItemVerifyRequest) D(OP_LoadSpellSet) D(OP_LootItem) D(OP_MoveItem) +D(OP_PetCommands) D(OP_RaidInvite) D(OP_ReadBook) D(OP_Save) diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 7ec101fd3..24ed9c7cc 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -4409,7 +4409,6 @@ struct MercenaryAssign_Struct { /*0012*/ }; - }; //end namespace structs }; //end namespace SoD diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index fc46c53cb..8405f9def 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -31,6 +31,12 @@ namespace SoF static inline uint32 SoFToServerSlot(uint32 SoFSlot); static inline uint32 SoFToServerCorpseSlot(uint32 SoFCorpse); + // server to client text link converter + static inline void ServerToSoFTextLink(std::string& sofTextLink, const std::string& serverTextLink); + + // client to server text link converter + static inline void SoFToServerTextLink(std::string& serverTextLink, const std::string& sofTextLink); + void Register(EQStreamIdentifier &into) { //create our opcode manager if we havent already @@ -278,6 +284,35 @@ namespace SoF FINISH_ENCODE(); } + ENCODE(OP_ChannelMessage) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + ChannelMessage_Struct *emu = (ChannelMessage_Struct *)in->pBuffer; + + unsigned char *__emu_buffer = in->pBuffer; + + std::string old_message = emu->message; + std::string new_message; + ServerToSoFTextLink(new_message, old_message); + + in->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; + + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + memcpy(OutBuffer, __emu_buffer, sizeof(ChannelMessage_Struct)); + + OutBuffer += sizeof(ChannelMessage_Struct); + + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + ENCODE(OP_CharInventory) { //consume the packet @@ -1609,6 +1644,44 @@ namespace SoF FINISH_ENCODE(); } + ENCODE(OP_SpecialMesg) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + SpecialMesg_Struct *emu = (SpecialMesg_Struct *)in->pBuffer; + + unsigned char *__emu_buffer = in->pBuffer; + + std::string old_message = &emu->message[strlen(emu->sayer)]; + std::string new_message; + ServerToSoFTextLink(new_message, old_message); + + //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; + in->size = 25 + strlen(emu->sayer) + new_message.length(); + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[0]); + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[1]); + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[2]); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->msg_type); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->target_spawn_id); + + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sayer); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + ENCODE(OP_Stun) { ENCODE_LENGTH_EXACT(Stun_Struct); @@ -2129,6 +2202,25 @@ namespace SoF FINISH_DIRECT_DECODE(); } + DECODE(OP_ChannelMessage) + { + unsigned char *__eq_buffer = __packet->pBuffer; + + std::string old_message = (char *)&__eq_buffer[sizeof(ChannelMessage_Struct)]; + std::string new_message; + SoFToServerTextLink(new_message, old_message); + + __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; + __packet->pBuffer = new unsigned char[__packet->size]; + + ChannelMessage_Struct *emu = (ChannelMessage_Struct *)__packet->pBuffer; + + memcpy(emu, __eq_buffer, sizeof(ChannelMessage_Struct)); + strcpy(emu->message, new_message.c_str()); + + delete[] __eq_buffer; + } + DECODE(OP_CharacterCreate) { DECODE_LENGTH_EXACT(structs::CharCreate_Struct); @@ -2345,6 +2437,89 @@ namespace SoF FINISH_DIRECT_DECODE(); } + DECODE(OP_PetCommands) + { + DECODE_LENGTH_EXACT(structs::PetCommand_Struct); + SETUP_DIRECT_DECODE(PetCommand_Struct, structs::PetCommand_Struct); + + switch (eq->command) + { + case 0x04: + emu->command = 0x00; // /pet health + break; + case 0x10: + emu->command = 0x01; // /pet leader + break; + case 0x07: + emu->command = 0x02; // /pet attack or Pet Window + break; + case 0x03: // Case Guessed + emu->command = 0x03; // /pet qattack + case 0x08: + emu->command = 0x04; // /pet follow or Pet Window + break; + case 0x05: + emu->command = 0x05; // /pet guard or Pet Window + break; + case 0x09: + emu->command = 0x07; // /pet sit or Pet Window + break; + case 0x0a: + emu->command = 0x08; // /pet stand or Pet Window + break; + case 0x06: + emu->command = 0x1e; // /pet guard me + break; + case 0x0f: // Case Made Up + emu->command = 0x09; // Stop? + break; + case 0x0b: + emu->command = 0x0d; // /pet taunt or Pet Window + break; + case 0x0e: + emu->command = 0x0e; // /pet notaunt or Pet Window + break; + case 0x0c: + emu->command = 0x0f; // /pet hold + break; + case 0x1b: + emu->command = 0x10; // /pet hold on + break; + case 0x1c: + emu->command = 0x11; // /pet hold off + break; + case 0x11: + emu->command = 0x12; // Slumber? + break; + case 0x12: + emu->command = 0x15; // /pet no cast + break; + case 0x0d: // Case Made Up + emu->command = 0x16; // Pet Window No Cast + break; + case 0x13: + emu->command = 0x18; // /pet focus + break; + case 0x19: + emu->command = 0x19; // /pet focus on + break; + case 0x1a: + emu->command = 0x1a; // /pet focus off + break; + case 0x01: + emu->command = 0x1c; // /pet back off + break; + case 0x02: + emu->command = 0x1d; // /pet get lost + break; + default: + emu->command = eq->command; + } + OUT(unknown); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_RaidInvite) { DECODE_LENGTH_ATLEAST(structs::RaidGeneral_Struct); @@ -3005,5 +3180,81 @@ namespace SoF //uint32 ServerCorpse; return (SoFCorpse - 1); } + + static inline void ServerToSoFTextLink(std::string& sofTextLink, const std::string& serverTextLink) + { + const char delimiter = 0x12; + + if ((consts::TEXT_LINK_BODY_LENGTH == EmuConstants::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find(delimiter) == std::string::npos)) { + sofTextLink = serverTextLink; + return; + } + + auto segments = SplitString(serverTextLink, delimiter); + + for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { + if (segment_iter & 1) { + std::string new_segment; + + // Idx: 0 1 6 11 16 21 26 31 36 37 41 43 48 (Source) + // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) + // SoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (50) + // Diff: ^^^^^ ^ + + new_segment.append(segments[segment_iter].substr(0, 31).c_str()); + new_segment.append(segments[segment_iter].substr(36, 5).c_str()); + + if (segments[segment_iter].substr(41, 1) == "0") + new_segment.append(segments[segment_iter].substr(42, 1).c_str()); + else + new_segment.append("F"); + + new_segment.append(segments[segment_iter].substr(43).c_str()); + + sofTextLink.push_back(delimiter); + sofTextLink.append(new_segment.c_str()); + sofTextLink.push_back(delimiter); + } + else { + sofTextLink.append(segments[segment_iter].c_str()); + } + } + } + + static inline void SoFToServerTextLink(std::string& serverTextLink, const std::string& sofTextLink) + { + const char delimiter = 0x12; + + if ((EmuConstants::TEXT_LINK_BODY_LENGTH == consts::TEXT_LINK_BODY_LENGTH) || (sofTextLink.find(delimiter) == std::string::npos)) { + serverTextLink = sofTextLink; + return; + } + + auto segments = SplitString(sofTextLink, delimiter); + + for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { + if (segment_iter & 1) { + std::string new_segment; + + // Idx: 0 1 6 11 16 21 26 31 32 36 37 42 (Source) + // SoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (50) + // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) + // Diff: ^^^^^ ^ + + new_segment.append(segments[segment_iter].substr(0, 31).c_str()); + new_segment.append("00000"); + new_segment.append(segments[segment_iter].substr(31, 5).c_str()); + new_segment.append("0"); + new_segment.append(segments[segment_iter].substr(36).c_str()); + + serverTextLink.push_back(delimiter); + serverTextLink.append(new_segment.c_str()); + serverTextLink.push_back(delimiter); + } + else { + serverTextLink.append(segments[segment_iter].c_str()); + } + } + } } // end namespace SoF diff --git a/common/patches/sof_constants.h b/common/patches/sof_constants.h index 0b959ef3a..5e0f4b18a 100644 --- a/common/patches/sof_constants.h +++ b/common/patches/sof_constants.h @@ -177,6 +177,8 @@ namespace SoF { static const uint32 BANDOLIERS_COUNT = 20; // count = number of bandolier instances static const uint32 BANDOLIER_SIZE = 4; // size = number of equipment slots in bandolier instance static const uint32 POTION_BELT_SIZE = 5; + + static const size_t TEXT_LINK_BODY_LENGTH = 50; } namespace limits { diff --git a/common/patches/sof_ops.h b/common/patches/sof_ops.h index 132e3dc77..9cfd400e3 100644 --- a/common/patches/sof_ops.h +++ b/common/patches/sof_ops.h @@ -8,6 +8,7 @@ E(OP_BazaarSearch) E(OP_BecomeTrader) E(OP_Buff) E(OP_CancelTrade) +E(OP_ChannelMessage) E(OP_CharInventory) E(OP_ClientUpdate) E(OP_Consider) @@ -50,6 +51,7 @@ E(OP_SendZonepoints) E(OP_ShopPlayerSell) E(OP_SomeItemPacketMaybe) E(OP_SpawnDoor) +E(OP_SpecialMesg) E(OP_Stun) E(OP_Track) E(OP_Trader) @@ -70,6 +72,7 @@ D(OP_AugmentInfo) D(OP_AugmentItem) D(OP_Buff) D(OP_CastSpell) +D(OP_ChannelMessage) D(OP_CharacterCreate) D(OP_ClientUpdate) D(OP_Consider) @@ -85,6 +88,7 @@ D(OP_ItemLinkClick) D(OP_ItemVerifyRequest) D(OP_LootItem) D(OP_MoveItem) +D(OP_PetCommands) D(OP_RaidInvite) D(OP_ReadBook) D(OP_Save) diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index 51f41a590..4e84998b7 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -4115,18 +4115,7 @@ struct AltCurrencySellItem_Struct { /*010*/ uint32 cost; }; - }; //end namespace structs }; //end namespace SoF #endif /*SoF_STRUCTS_H_*/ - - - - - - - - - - diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index f9ad6a4ff..f40c0266c 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -7,6 +7,7 @@ #include "../races.h" #include "../eq_packet_structs.h" +#include "../misc_functions.h" #include "../string_util.h" #include "../item.h" #include "titanium_structs.h" @@ -28,6 +29,12 @@ namespace Titanium static inline uint32 TitaniumToServerSlot(int16 TitaniumSlot); static inline uint32 TitaniumToServerCorpseSlot(int16 TitaniumCorpse); + // server to client text link converter + static inline void ServerToTitaniumTextLink(std::string& titaniumTextLink, const std::string& serverTextLink); + + // client to server text link converter + static inline void TitaniumToServerTextLink(std::string& serverTextLink, const std::string& titaniumTextLink); + void Register(EQStreamIdentifier &into) { //create our opcode manager if we havent already @@ -220,6 +227,35 @@ namespace Titanium FINISH_ENCODE(); } + ENCODE(OP_ChannelMessage) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + ChannelMessage_Struct *emu = (ChannelMessage_Struct *)in->pBuffer; + + unsigned char *__emu_buffer = in->pBuffer; + + std::string old_message = emu->message; + std::string new_message; + ServerToTitaniumTextLink(new_message, old_message); + + in->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; + + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + memcpy(OutBuffer, __emu_buffer, sizeof(ChannelMessage_Struct)); + + OutBuffer += sizeof(ChannelMessage_Struct); + + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + ENCODE(OP_CharInventory) { //consume the packet @@ -1070,6 +1106,44 @@ namespace Titanium FINISH_ENCODE(); } + ENCODE(OP_SpecialMesg) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + SpecialMesg_Struct *emu = (SpecialMesg_Struct *)in->pBuffer; + + unsigned char *__emu_buffer = in->pBuffer; + + std::string old_message = &emu->message[strlen(emu->sayer)]; + std::string new_message; + ServerToTitaniumTextLink(new_message, old_message); + + //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; + in->size = 25 + strlen(emu->sayer) + new_message.length(); + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[0]); + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[1]); + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[2]); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->msg_type); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->target_spawn_id); + + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sayer); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + ENCODE(OP_Track) { EQApplicationPacket *in = *p; @@ -1371,6 +1445,25 @@ namespace Titanium FINISH_DIRECT_DECODE(); } + DECODE(OP_ChannelMessage) + { + unsigned char *__eq_buffer = __packet->pBuffer; + + std::string old_message = (char *)&__eq_buffer[sizeof(ChannelMessage_Struct)]; + std::string new_message; + TitaniumToServerTextLink(new_message, old_message); + + __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; + __packet->pBuffer = new unsigned char[__packet->size]; + + ChannelMessage_Struct *emu = (ChannelMessage_Struct *)__packet->pBuffer; + + memcpy(emu, __eq_buffer, sizeof(ChannelMessage_Struct)); + strcpy(emu->message, new_message.c_str()); + + delete[] __eq_buffer; + } + DECODE(OP_CharacterCreate) { DECODE_LENGTH_EXACT(structs::CharCreate_Struct); @@ -1538,6 +1631,89 @@ namespace Titanium FINISH_DIRECT_DECODE(); } + DECODE(OP_PetCommands) + { + DECODE_LENGTH_EXACT(structs::PetCommand_Struct); + SETUP_DIRECT_DECODE(PetCommand_Struct, structs::PetCommand_Struct); + + switch (eq->command) + { + case 0x04: + emu->command = 0x00; // /pet health + break; + case 0x10: + emu->command = 0x01; // /pet leader + break; + case 0x07: + emu->command = 0x02; // /pet attack or Pet Window + break; + case 0x03: // Case Guessed + emu->command = 0x03; // /pet qattack + case 0x08: + emu->command = 0x04; // /pet follow or Pet Window + break; + case 0x05: + emu->command = 0x05; // /pet guard or Pet Window + break; + case 0x09: + emu->command = 0x07; // /pet sit or Pet Window + break; + case 0x0a: + emu->command = 0x08; // /pet stand or Pet Window + break; + case 0x06: + emu->command = 0x1e; // /pet guard me + break; + case 0x0f: // Case Made Up + emu->command = 0x09; // Stop? + break; + case 0x0b: + emu->command = 0x0d; // /pet taunt or Pet Window + break; + case 0x0e: + emu->command = 0x0e; // /pet notaunt or Pet Window + break; + case 0x0c: + emu->command = 0x0f; // /pet hold + break; + case 0x1b: + emu->command = 0x10; // /pet hold on + break; + case 0x1c: + emu->command = 0x11; // /pet hold off + break; + case 0x11: + emu->command = 0x12; // Slumber? + break; + case 0x12: + emu->command = 0x15; // /pet no cast + break; + case 0x0d: // Case Made Up + emu->command = 0x16; // Pet Window No Cast + break; + case 0x13: + emu->command = 0x18; // /pet focus + break; + case 0x19: + emu->command = 0x19; // /pet focus on + break; + case 0x1a: + emu->command = 0x1a; // /pet focus off + break; + case 0x01: + emu->command = 0x1c; // /pet back off + break; + case 0x02: + emu->command = 0x1d; // /pet get lost + break; + default: + emu->command = eq->command; + } + OUT(unknown); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_ReadBook) { // no apparent slot translation needed -U @@ -1763,5 +1939,83 @@ namespace Titanium //uint32 ServerCorpse; return TitaniumCorpse; } + + static inline void ServerToTitaniumTextLink(std::string& titaniumTextLink, const std::string& serverTextLink) + { + const char delimiter = 0x12; + + if ((consts::TEXT_LINK_BODY_LENGTH == EmuConstants::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find(delimiter) == std::string::npos)) { + titaniumTextLink = serverTextLink; + return; + } + + auto segments = SplitString(serverTextLink, delimiter); + + for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { + if (segment_iter & 1) { + std::string new_segment; + + // Idx: 0 1 6 11 16 21 26 31 36 37 41 43 48 (Source) + // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) + // 6.2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXXXXX (45) + // Diff: ^^^^^ ^ ^^^^^ + + new_segment.append(segments[segment_iter].substr(0, 31).c_str()); + new_segment.append(segments[segment_iter].substr(36, 5).c_str()); + + if (segments[segment_iter].substr(41, 1) == "0") + new_segment.append(segments[segment_iter].substr(42, 1).c_str()); + else + new_segment.append("F"); + + new_segment.append(segments[segment_iter].substr(48).c_str()); + + titaniumTextLink.push_back(delimiter); + titaniumTextLink.append(new_segment.c_str()); + titaniumTextLink.push_back(delimiter); + } + else { + titaniumTextLink.append(segments[segment_iter].c_str()); + } + } + } + + static inline void TitaniumToServerTextLink(std::string& serverTextLink, const std::string& titaniumTextLink) + { + const char delimiter = 0x12; + + if ((EmuConstants::TEXT_LINK_BODY_LENGTH == consts::TEXT_LINK_BODY_LENGTH) || (titaniumTextLink.find(delimiter) == std::string::npos)) { + serverTextLink = titaniumTextLink; + return; + } + + auto segments = SplitString(titaniumTextLink, delimiter); + + for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { + if (segment_iter & 1) { + std::string new_segment; + + // Idx: 0 1 6 11 16 21 26 31 32 36 37 (Source) + // 6.2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXXXXX (45) + // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) + // Diff: ^^^^^ ^ ^^^^^ + + new_segment.append(segments[segment_iter].substr(0, 31).c_str()); + new_segment.append("00000"); + new_segment.append(segments[segment_iter].substr(31, 5).c_str()); + new_segment.append("0"); + new_segment.append(segments[segment_iter].substr(36, 1).c_str()); + new_segment.append("00000"); + new_segment.append(segments[segment_iter].substr(37).c_str()); + + serverTextLink.push_back(delimiter); + serverTextLink.append(new_segment.c_str()); + serverTextLink.push_back(delimiter); + } + else { + serverTextLink.append(segments[segment_iter].c_str()); + } + } + } } // end namespace Titanium diff --git a/common/patches/titanium_constants.h b/common/patches/titanium_constants.h index 85e525fe8..ff44513c2 100644 --- a/common/patches/titanium_constants.h +++ b/common/patches/titanium_constants.h @@ -176,6 +176,8 @@ namespace Titanium { static const uint32 BANDOLIERS_COUNT = 4; // count = number of bandolier instances static const uint32 BANDOLIER_SIZE = 4; // size = number of equipment slots in bandolier instance static const uint32 POTION_BELT_SIZE = 4; + + static const size_t TEXT_LINK_BODY_LENGTH = 45; } namespace limits { diff --git a/common/patches/titanium_ops.h b/common/patches/titanium_ops.h index 338558602..4ddcae630 100644 --- a/common/patches/titanium_ops.h +++ b/common/patches/titanium_ops.h @@ -4,6 +4,7 @@ E(OP_AdventureMerchantSell) E(OP_ApplyPoison) E(OP_BazaarSearch) E(OP_BecomeTrader) +E(OP_ChannelMessage) E(OP_CharInventory) E(OP_DeleteCharge) E(OP_DeleteItem) @@ -35,6 +36,7 @@ E(OP_RespondAA) E(OP_SendCharInfo) E(OP_SendAATable) E(OP_ShopPlayerSell) +E(OP_SpecialMesg) E(OP_Track) E(OP_Trader) E(OP_TraderBuy) @@ -49,6 +51,7 @@ D(OP_AdventureMerchantSell) D(OP_ApplyPoison) D(OP_AugmentItem) D(OP_CastSpell) +D(OP_ChannelMessage) D(OP_CharacterCreate) D(OP_Consume) D(OP_DeleteItem) @@ -59,6 +62,7 @@ D(OP_ItemLinkClick) D(OP_LFGuild) D(OP_LootItem) D(OP_MoveItem) +D(OP_PetCommands) D(OP_ReadBook) D(OP_SetServerFilter) D(OP_ShopPlayerSell) diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index 0b5b1ec25..7d21ba998 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -3332,16 +3332,4 @@ struct LFGuild_GuildToggle_Struct }; //end namespace structs }; //end namespace Titanium - - #endif /*Titanium_STRUCTS_H_*/ - - - - - - - - - - diff --git a/common/patches/underfoot.cpp b/common/patches/underfoot.cpp index 5ee38f586..1ebd8dc8d 100644 --- a/common/patches/underfoot.cpp +++ b/common/patches/underfoot.cpp @@ -31,6 +31,12 @@ namespace Underfoot static inline uint32 UnderfootToServerSlot(uint32 UnderfootSlot); static inline uint32 UnderfootToServerCorpseSlot(uint32 UnderfootCorpse); + // server to client text link converter + static inline void ServerToUnderfootTextLink(std::string& underfootTextLink, const std::string& serverTextLink); + + // client to server text link converter + static inline void UnderfootToServerTextLink(std::string& serverTextLink, const std::string& underfootTextLink); + void Register(EQStreamIdentifier &into) { //create our opcode manager if we havent already @@ -432,7 +438,12 @@ namespace Underfoot unsigned char *__emu_buffer = in->pBuffer; - in->size = strlen(emu->sender) + 1 + strlen(emu->targetname) + 1 + strlen(emu->message) + 1 + 36; + std::string old_message = emu->message; + std::string new_message; + ServerToUnderfootTextLink(new_message, old_message); + + //in->size = strlen(emu->sender) + 1 + strlen(emu->targetname) + 1 + strlen(emu->message) + 1 + 36; + in->size = strlen(emu->sender) + strlen(emu->targetname) + new_message.length() + 39; in->pBuffer = new unsigned char[in->size]; @@ -446,7 +457,7 @@ namespace Underfoot VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->skill_in_language); - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->message); + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown @@ -2298,6 +2309,44 @@ namespace Underfoot FINISH_ENCODE(); } + ENCODE(OP_SpecialMesg) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + SpecialMesg_Struct *emu = (SpecialMesg_Struct *)in->pBuffer; + + unsigned char *__emu_buffer = in->pBuffer; + + std::string old_message = &emu->message[strlen(emu->sayer)]; + std::string new_message; + ServerToUnderfootTextLink(new_message, old_message); + + //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; + in->size = 25 + strlen(emu->sayer) + new_message.length(); + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[0]); + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[1]); + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[2]); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->msg_type); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->target_spawn_id); + + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sayer); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + ENCODE(OP_Stun) { ENCODE_LENGTH_EXACT(Stun_Struct); @@ -3036,7 +3085,13 @@ namespace Underfoot uint32 Skill = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - __packet->size = sizeof(ChannelMessage_Struct)+strlen(InBuffer) + 1; + std::string old_message = InBuffer; + std::string new_message; + UnderfootToServerTextLink(new_message, old_message); + + //__packet->size = sizeof(ChannelMessage_Struct)+strlen(InBuffer) + 1; + __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; + __packet->pBuffer = new unsigned char[__packet->size]; ChannelMessage_Struct *emu = (ChannelMessage_Struct *)__packet->pBuffer; @@ -3045,7 +3100,7 @@ namespace Underfoot emu->language = Language; emu->chan_num = Channel; emu->skill_in_language = Skill; - strcpy(emu->message, InBuffer); + strcpy(emu->message, new_message.c_str()); delete[] __eq_buffer; } @@ -3364,57 +3419,8 @@ namespace Underfoot DECODE_LENGTH_EXACT(structs::PetCommand_Struct); SETUP_DIRECT_DECODE(PetCommand_Struct, structs::PetCommand_Struct); - switch (eq->command) - { - case 0x00: - emu->command = 0x04; // Health - break; - case 0x01: - emu->command = 0x10; // Leader - break; - case 0x02: - emu->command = 0x07; // Attack - break; - case 0x04: - emu->command = 0x08; // Follow - break; - case 0x05: - emu->command = 0x05; // Guard - break; - case 0x06: - emu->command = 0x09; // Sit. Needs work. This appears to be a toggle between Sit/Stand now. - break; - case 0x0c: - emu->command = 0x0b; // Taunt - break; - case 0x0f: - emu->command = 0x0c; // Hold - break; - case 0x10: - emu->command = 0x1b; // Hold on - break; - case 0x11: - emu->command = 0x1c; // Hold off - break; - case 0x1c: - emu->command = 0x01; // Back - break; - case 0x1d: - emu->command = 0x02; // Leave/Go Away - break; - case 0x15: - emu->command = 0x12; // No Cast - /command - break; - case 0x16: - emu->command = 0x12; // No Cast - Pet Window - break; - case 0x18: - emu->command = 0x13; // Focus - Pet Window - break; - default: - emu->command = eq->command; - } - OUT(unknown); + IN(command); + IN(unknown); FINISH_DIRECT_DECODE(); } @@ -4155,5 +4161,81 @@ namespace Underfoot //uint32 ServerCorpse; return (UnderfootCorpse - 1); } + + static inline void ServerToUnderfootTextLink(std::string& underfootTextLink, const std::string& serverTextLink) + { + const char delimiter = 0x12; + + if ((consts::TEXT_LINK_BODY_LENGTH == EmuConstants::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find(delimiter) == std::string::npos)) { + underfootTextLink = serverTextLink; + return; + } + + auto segments = SplitString(serverTextLink, delimiter); + + for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { + if (segment_iter & 1) { + std::string new_segment; + + // Idx: 0 1 6 11 16 21 26 31 36 37 41 43 48 (Source) + // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) + // SoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (50) + // Diff: ^^^^^ ^ + + new_segment.append(segments[segment_iter].substr(0, 31).c_str()); + new_segment.append(segments[segment_iter].substr(36, 5).c_str()); + + if (segments[segment_iter].substr(41, 1) == "0") + new_segment.append(segments[segment_iter].substr(42, 1).c_str()); + else + new_segment.append("F"); + + new_segment.append(segments[segment_iter].substr(43).c_str()); + + underfootTextLink.push_back(delimiter); + underfootTextLink.append(new_segment.c_str()); + underfootTextLink.push_back(delimiter); + } + else { + underfootTextLink.append(segments[segment_iter].c_str()); + } + } + } + + static inline void UnderfootToServerTextLink(std::string& serverTextLink, const std::string& underfootTextLink) + { + const char delimiter = 0x12; + + if ((EmuConstants::TEXT_LINK_BODY_LENGTH == consts::TEXT_LINK_BODY_LENGTH) || (underfootTextLink.find(delimiter) == std::string::npos)) { + serverTextLink = underfootTextLink; + return; + } + + auto segments = SplitString(underfootTextLink, delimiter); + + for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { + if (segment_iter & 1) { + std::string new_segment; + + // Idx: 0 1 6 11 16 21 26 31 32 36 37 42 (Source) + // SoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (50) + // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) + // Diff: ^^^^^ ^ + + new_segment.append(segments[segment_iter].substr(0, 31).c_str()); + new_segment.append("00000"); + new_segment.append(segments[segment_iter].substr(31, 5).c_str()); + new_segment.append("0"); + new_segment.append(segments[segment_iter].substr(36).c_str()); + + serverTextLink.push_back(delimiter); + serverTextLink.append(new_segment.c_str()); + serverTextLink.push_back(delimiter); + } + else { + serverTextLink.append(segments[segment_iter].c_str()); + } + } + } } // end namespace Underfoot diff --git a/common/patches/underfoot_constants.h b/common/patches/underfoot_constants.h index b89a4f255..a656c56eb 100644 --- a/common/patches/underfoot_constants.h +++ b/common/patches/underfoot_constants.h @@ -177,6 +177,8 @@ namespace Underfoot { static const uint32 BANDOLIERS_COUNT = 20; // count = number of bandolier instances static const uint32 BANDOLIER_SIZE = 4; // size = number of equipment slots in bandolier instance static const uint32 POTION_BELT_SIZE = 5; + + static const size_t TEXT_LINK_BODY_LENGTH = 50; } namespace limits { diff --git a/common/patches/underfoot_ops.h b/common/patches/underfoot_ops.h index cfb4d4fe9..61f4f1c0f 100644 --- a/common/patches/underfoot_ops.h +++ b/common/patches/underfoot_ops.h @@ -65,6 +65,7 @@ E(OP_ShopPlayerSell) E(OP_SomeItemPacketMaybe) E(OP_SpawnAppearance) E(OP_SpawnDoor) +E(OP_SpecialMesg) E(OP_Stun) E(OP_TargetBuffs) E(OP_Track) diff --git a/zone/bot.cpp b/zone/bot.cpp index d41b6db11..bd7c9f946 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -11707,7 +11707,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { std::string item_link; Client::TextLink linker; linker.SetLinkType(linker.linkItemInst); - linker.SetClientVersion(c->GetClientVersion()); for(int i = EmuConstants::EQUIPMENT_BEGIN; i <= EmuConstants::EQUIPMENT_END; ++i) { if((i == MainSecondary) && is2Hweapon) { diff --git a/zone/client.cpp b/zone/client.cpp index 0ad34d752..8b82f77cf 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -6255,6 +6255,7 @@ void Client::Doppelganger(uint16 spell_id, Mob *target, const char *name_overrid made_npc->DR = GetDR(); made_npc->PR = GetPR(); made_npc->Corrup = GetCorrup(); + made_npc->PhR = GetPhR(); // looks made_npc->texture = GetEquipmentMaterial(MaterialChest); made_npc->helmtexture = GetEquipmentMaterial(MaterialHead); @@ -6352,7 +6353,7 @@ void Client::SendStatsWindow(Client* client, bool use_window) // Set Class std::string class_Name = itoa(GetClass()); - std::string class_List[] = { "WAR", "CLR", "PAL", "RNG", "SK", "DRU", "MNK", "BRD", "ROG", "SHM", "NEC", "WIZ", "MAG", "ENC", "BST", "BER" }; + std::string class_List[] = { "WAR", "CLR", "PAL", "RNG", "SHD", "DRU", "MNK", "BRD", "ROG", "SHM", "NEC", "WIZ", "MAG", "ENC", "BST", "BER" }; if(GetClass() < 17 && GetClass() > 0) { class_Name = class_List[GetClass()-1]; } @@ -6440,7 +6441,7 @@ void Client::SendStatsWindow(Client* client, bool use_window) /*===========================*/ std::string regen_row_header = ""; std::string regen_row_color = ""; - std::string base_regen_field = ""; + std::string base_regen_field = ""; std::string base_regen_spacing = ""; std::string item_regen_field = ""; std::string item_regen_spacing = ""; @@ -6601,8 +6602,11 @@ void Client::SendStatsWindow(Client* client, bool use_window) } case 6: { a_stat_name = " CHA: "; + a_resist_name = "PhR: "; // Not implemented for clients yet a_stat = itoa(GetCHA()); h_stat = itoa(GetHeroicCHA()); + a_resist = itoa(GetPhR()); + h_resist_field = itoa(GetHeroicPhR()); break; } default: { break; } @@ -6617,8 +6621,9 @@ void Client::SendStatsWindow(Client* client, bool use_window) for(int h = a_resist.size(); h < max_stat_value_len; h++) { a_resist_spacing += " . "; } stat_field += indP + a_stat_name + a_stat_spacing + a_stat + heroic_color + h_stat + ""; + stat_field += h_stat_spacing + a_resist_name + a_resist_spacing + a_resist + heroic_color + h_resist_field + ""; if(stat_row_counter < 6) { - stat_field += h_stat_spacing + a_resist_name + a_resist_spacing + a_resist + heroic_color + h_resist_field + "
"; + stat_field += "
"; } } /*########################################################## @@ -6827,7 +6832,7 @@ void Client::SendStatsWindow(Client* client, bool use_window) client->Message(0, " Haste: %i / %i (Item: %i + Spell: %i + Over: %i)", GetHaste(), RuleI(Character, HasteCap), itembonuses.haste, spellbonuses.haste + spellbonuses.hastetype2, spellbonuses.hastetype3 + ExtraHaste); client->Message(0, " STR: %i STA: %i DEX: %i AGI: %i INT: %i WIS: %i CHA: %i", GetSTR(), GetSTA(), GetDEX(), GetAGI(), GetINT(), GetWIS(), GetCHA()); client->Message(0, " hSTR: %i hSTA: %i hDEX: %i hAGI: %i hINT: %i hWIS: %i hCHA: %i", GetHeroicSTR(), GetHeroicSTA(), GetHeroicDEX(), GetHeroicAGI(), GetHeroicINT(), GetHeroicWIS(), GetHeroicCHA()); - client->Message(0, " MR: %i PR: %i FR: %i CR: %i DR: %i Corruption: %i", GetMR(), GetPR(), GetFR(), GetCR(), GetDR(), GetCorrup()); + client->Message(0, " MR: %i PR: %i FR: %i CR: %i DR: %i Corruption: %i PhR: %i", GetMR(), GetPR(), GetFR(), GetCR(), GetDR(), GetCorrup(), GetPhR()); client->Message(0, " hMR: %i hPR: %i hFR: %i hCR: %i hDR: %i hCorruption: %i", GetHeroicMR(), GetHeroicPR(), GetHeroicFR(), GetHeroicCR(), GetHeroicDR(), GetHeroicCorrup()); client->Message(0, " Shielding: %i Spell Shield: %i DoT Shielding: %i Stun Resist: %i Strikethrough: %i Avoidance: %i Accuracy: %i Combat Effects: %i", GetShielding(), GetSpellShield(), GetDoTShield(), GetStunResist(), GetStrikeThrough(), GetAvoidance(), GetAccuracy(), GetCombatEffects()); client->Message(0, " Heal Amt.: %i Spell Dmg.: %i Clairvoyance: %i DS Mitigation: %i", GetHealAmt(), GetSpellDmg(), GetClair(), GetDSMit()); @@ -8271,71 +8276,27 @@ std::string Client::TextLink::GenerateLink() generate_body(); generate_text(); - if (m_LinkBody.length() && m_LinkText.length()) { - m_Link.append(StringFormat("%c", 0x12)); + if ((m_LinkBody.length() == EmuConstants::TEXT_LINK_BODY_LENGTH) && (m_LinkText.length() > 0)) { + m_Link.push_back(0x12); m_Link.append(m_LinkBody); m_Link.append(m_LinkText); - m_Link.append(StringFormat("%c", 0x12)); + m_Link.push_back(0x12); } if ((m_Link.length() == 0) || (m_Link.length() > 250)) { m_Error = true; m_Link = ""; - _log(CHANNELS__ERROR, "TextLink::GenerateLink() failed to generate a useable text link (LinkType: %i, Lengths: {l: %u, b: %u, t: %u})", + _log(CHANNELS__ERROR, "TextLink::GenerateLink() failed to generate a useable text link (LinkType: %i, Lengths: {link: %u, body: %u, text: %u})", m_LinkType, m_Link.length(), m_LinkBody.length(), m_LinkText.length()); +#if EQDEBUG >= 5 + _log(CHANNELS__ERROR, ">> LinkBody: %s", m_LinkBody.c_str()); + _log(CHANNELS__ERROR, ">> LinkText: %s", m_LinkText.c_str()); +#endif } return m_Link; } -const char* Client::TextLink::GetLink() -{ - if (m_Link.length() == 0) - return nullptr; - - return m_Link.c_str(); -} - -const char* Client::TextLink::GetLinkBody() -{ - if (m_LinkBody.length() == 0) - return nullptr; - - return m_LinkBody.c_str(); -} - -const char* Client::TextLink::GetLinkText() -{ - if (m_LinkText.length() == 0) - return nullptr; - - return m_LinkText.c_str(); -} - -std::string Client::TextLink::GetLinkString() -{ - if (m_Link.length() == 0) - return ""; - - return m_Link; -} - -std::string Client::TextLink::GetLinkBodyString() -{ - if (m_LinkBody.length() == 0) - return ""; - - return m_LinkBody; -} - -std::string Client::TextLink::GetLinkTextString() -{ - if (m_LinkText.length() == 0) - return ""; - - return m_LinkText; -} - void Client::TextLink::Reset() { m_LinkType = linkBlank; @@ -8348,108 +8309,93 @@ void Client::TextLink::Reset() m_Link.clear(); m_LinkBody.clear(); m_LinkText.clear(); - m_ClientVersion = EQClientUnknown; m_Error = false; } void Client::TextLink::generate_body() { - enum { field_0 = 0, field_1, field_2, field_3, field_4, field_5, field_6, field_7, field_8, field_9, field_10, field_11, field_12, field_13 }; - static const int field_count = 14; - static const bool field_use[_EQClientCount][field_count] = { - // 6.2: MakeAnyLenString(&ret_link, "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%08X" - // SoF: MakeAnyLenString(&ret_link, "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X" - // RoF: MakeAnyLenString(&ret_link, "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X" - // RoF2: MakeAnyLenString(&ret_link, "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%1X" "%04X" "%1X" "%05X" "%08X" + /* + Current server mask: EQClientRoF2 + + RoF2: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%02X" "%05X" "%08X" (56) + RoF: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X" (55) + SoF: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X" (50) + 6.2: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%08X" (45) + */ -//(RoF2) %01x %05x %05x %05x %05x %05x %05x %05x %01x %01x %04x %01x %05x %08x - { true, true, true, true, true, true, true, true, true, true, true, true, true, true }, // EQClientUnknown - { true, true, true, true, true, true, true, false, false, true, true, true, false, true }, // EQClient6.2 - { true, true, true, true, true, true, true, false, false, true, true, true, false, true }, // EQClientTitanium - { true, true, true, true, true, true, true, false, false, true, true, true, true, true }, // EQClientSoF - { true, true, true, true, true, true, true, false, false, true, true, true, true, true }, // EQClientSoD - { true, true, true, true, true, true, true, false, false, true, true, true, true, true }, // EQClientUnderfoot - { true, true, true, true, true, true, true, true, false, true, true, true, true, true }, // EQClientRoF - { true, true, true, true, true, true, true, true, true, true, true, true, true, true } // EQClientRoF2 - }; - -/*%01X*/ uint8 unknown_0 = NOT_USED; -/*%05X*/ uint32 item_id = NOT_USED; -/*%05X*/ uint32 augment_0 = NOT_USED; -/*%05X*/ uint32 augment_1 = NOT_USED; -/*%05X*/ uint32 augment_2 = NOT_USED; -/*%05X*/ uint32 augment_3 = NOT_USED; -/*%05X*/ uint32 augment_4 = NOT_USED; -/*%05X*/ uint32 augment_5 = NOT_USED; -/*%01X*/ uint8 unknown_8 = NOT_USED; -/*%01X*/ uint8 unknown_9 = NOT_USED; -/*%04X*/ uint32 unknown_10 = NOT_USED; -/*%01X*/ uint8 unknown_11 = NOT_USED; -/*%05X*/ uint32 unknown_12 = NOT_USED; -/*%08X*/ int hash = NOT_USED; + memset(&m_LinkBodyStruct, 0, sizeof(TextLinkBody_Struct)); + const Item_Struct* item_data = nullptr; + switch (m_LinkType) { case linkBlank: break; case linkItemData: - if (m_ItemData != nullptr) { - item_id = m_ItemData->ID; - // TODO: add hash call - } + if (m_ItemData == nullptr) { break; } + m_LinkBodyStruct.item_id = m_ItemData->ID; + m_LinkBodyStruct.evolve_group = m_ItemData->LoreGroup; // this probably won't work for all items + //m_LinkBodyStruct.evolve_level = m_ItemData->EvolvingLevel; + // TODO: add hash call break; case linkLootItem: - if (m_LootData != nullptr) { - const Item_Struct* item_data = database.GetItem(m_LootData->item_id); - if (item_data == nullptr) { break; } - item_id = item_data->ID; - augment_0 = m_LootData->aug_1; - augment_1 = m_LootData->aug_2; - augment_2 = m_LootData->aug_3; - augment_3 = m_LootData->aug_4; - augment_4 = m_LootData->aug_5; - augment_5 = m_LootData->aug_6; - // TODO: add hash call - } + if (m_LootData == nullptr) { break; } + item_data = database.GetItem(m_LootData->item_id); + if (item_data == nullptr) { break; } + m_LinkBodyStruct.item_id = item_data->ID; + m_LinkBodyStruct.augment_1 = m_LootData->aug_1; + m_LinkBodyStruct.augment_2 = m_LootData->aug_2; + m_LinkBodyStruct.augment_3 = m_LootData->aug_3; + m_LinkBodyStruct.augment_4 = m_LootData->aug_4; + m_LinkBodyStruct.augment_5 = m_LootData->aug_5; + m_LinkBodyStruct.augment_6 = m_LootData->aug_6; + m_LinkBodyStruct.evolve_group = item_data->LoreGroup; // see note above + //m_LinkBodyStruct.evolve_level = item_data->EvolvingLevel; + // TODO: add hash call break; case linkItemInst: - if (m_ItemInst != nullptr) { - if (m_ItemInst->GetItem() == nullptr) { break; } - item_id = m_ItemInst->GetItem()->ID; - augment_0 = m_ItemInst->GetAugmentItemID(0); - augment_1 = m_ItemInst->GetAugmentItemID(1); - augment_2 = m_ItemInst->GetAugmentItemID(2); - augment_3 = m_ItemInst->GetAugmentItemID(3); - augment_4 = m_ItemInst->GetAugmentItemID(4); - augment_5 = m_ItemInst->GetAugmentItemID(5); - // TODO: add hash call - } + if (m_ItemInst == nullptr) { break; } + if (m_ItemInst->GetItem() == nullptr) { break; } + m_LinkBodyStruct.item_id = m_ItemInst->GetItem()->ID; + m_LinkBodyStruct.augment_1 = m_ItemInst->GetAugmentItemID(0); + m_LinkBodyStruct.augment_2 = m_ItemInst->GetAugmentItemID(1); + m_LinkBodyStruct.augment_3 = m_ItemInst->GetAugmentItemID(2); + m_LinkBodyStruct.augment_4 = m_ItemInst->GetAugmentItemID(3); + m_LinkBodyStruct.augment_5 = m_ItemInst->GetAugmentItemID(4); + m_LinkBodyStruct.augment_6 = m_ItemInst->GetAugmentItemID(5); + m_LinkBodyStruct.is_evolving = (m_ItemInst->IsEvolving() ? 1 : 0); + m_LinkBodyStruct.evolve_group = m_ItemInst->GetItem()->LoreGroup; // see note above + m_LinkBodyStruct.evolve_level = m_ItemInst->GetEvolveLvl(); + m_LinkBodyStruct.ornament_icon = m_ItemInst->GetOrnamentationIcon(); + // TODO: add hash call break; default: break; } if (m_ProxyItemID != NOT_USED) { - item_id = m_ProxyItemID; + m_LinkBodyStruct.item_id = m_ProxyItemID; } if (m_TaskUse) { - hash = 0x0000000014505DC2; + m_LinkBodyStruct.hash = 0x14505DC2; } - if (field_use[m_ClientVersion][field_0]) { m_LinkBody.append(StringFormat("%01x", unknown_0)); } - if (field_use[m_ClientVersion][field_1]) { m_LinkBody.append(StringFormat("%05x", item_id)); } - if (field_use[m_ClientVersion][field_2]) { m_LinkBody.append(StringFormat("%05x", augment_0)); } - if (field_use[m_ClientVersion][field_3]) { m_LinkBody.append(StringFormat("%05x", augment_1)); } - if (field_use[m_ClientVersion][field_4]) { m_LinkBody.append(StringFormat("%05x", augment_2)); } - if (field_use[m_ClientVersion][field_5]) { m_LinkBody.append(StringFormat("%05x", augment_3)); } - if (field_use[m_ClientVersion][field_6]) { m_LinkBody.append(StringFormat("%05x", augment_4)); } - if (field_use[m_ClientVersion][field_7]) { m_LinkBody.append(StringFormat("%05x", augment_5)); } - if (field_use[m_ClientVersion][field_8]) { m_LinkBody.append(StringFormat("%01x", unknown_8)); } - if (field_use[m_ClientVersion][field_9]) { m_LinkBody.append(StringFormat("%01x", unknown_9)); } - if (field_use[m_ClientVersion][field_10]) { m_LinkBody.append(StringFormat("%04x", unknown_10)); } - if (field_use[m_ClientVersion][field_11]) { m_LinkBody.append(StringFormat("%01x", unknown_11)); } - if (field_use[m_ClientVersion][field_12]) { m_LinkBody.append(StringFormat("%05x", unknown_12)); } - if (field_use[m_ClientVersion][field_13]) { m_LinkBody.append(StringFormat("%08x", hash)); } + m_LinkBody = StringFormat( + "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%02X" "%05X" "%08X", + (0x0F & m_LinkBodyStruct.unknown_1), + (0x000FFFFF & m_LinkBodyStruct.item_id), + (0x000FFFFF & m_LinkBodyStruct.augment_1), + (0x000FFFFF & m_LinkBodyStruct.augment_2), + (0x000FFFFF & m_LinkBodyStruct.augment_3), + (0x000FFFFF & m_LinkBodyStruct.augment_4), + (0x000FFFFF & m_LinkBodyStruct.augment_5), + (0x000FFFFF & m_LinkBodyStruct.augment_6), + (0x0F & m_LinkBodyStruct.is_evolving), + (0x0000FFFF & m_LinkBodyStruct.evolve_group), + (0xFF & m_LinkBodyStruct.evolve_level), + (0x000FFFFF & m_LinkBodyStruct.ornament_icon), + (0xFFFFFFFF & m_LinkBodyStruct.hash) + ); } void Client::TextLink::generate_text() @@ -8459,35 +8405,74 @@ void Client::TextLink::generate_text() return; } + const Item_Struct* item_data = nullptr; + switch (m_LinkType) { case linkBlank: break; case linkItemData: - if (m_ItemData != nullptr) { - m_LinkText = m_ItemData->Name; - return; - } - break; + if (m_ItemData == nullptr) { break; } + m_LinkText = m_ItemData->Name; + return; case linkLootItem: - if (m_LootData != nullptr) { - const Item_Struct* item_data = database.GetItem(m_LootData->item_id); - if (item_data != nullptr) { - m_LinkText = item_data->Name; - return; - } - } - break; + if (m_LootData == nullptr) { break; } + item_data = database.GetItem(m_LootData->item_id); + if (item_data == nullptr) { break; } + m_LinkText = item_data->Name; + return; case linkItemInst: - if (m_ItemInst != nullptr) { - if (m_ItemInst->GetItem() != nullptr) { - m_LinkText = m_ItemInst->GetItem()->Name; - return; - } - } - break; + if (m_ItemInst == nullptr) { break; } + if (m_ItemInst->GetItem() == nullptr) { break; } + m_LinkText = m_ItemInst->GetItem()->Name; + return; default: break; } m_LinkText = "null"; } + +bool Client::TextLink::DegenerateLinkBody(TextLinkBody_Struct& textLinkBodyStruct, const std::string& textLinkBody) +{ + memset(&textLinkBodyStruct, 0, sizeof(TextLinkBody_Struct)); + if (textLinkBody.length() != EmuConstants::TEXT_LINK_BODY_LENGTH) { return false; } + + textLinkBodyStruct.unknown_1 = (uint8)strtol(textLinkBody.substr(0, 1).c_str(), nullptr, 16); + textLinkBodyStruct.item_id = (uint32)strtol(textLinkBody.substr(1, 5).c_str(), nullptr, 16); + textLinkBodyStruct.augment_1 = (uint32)strtol(textLinkBody.substr(6, 5).c_str(), nullptr, 16); + textLinkBodyStruct.augment_2 = (uint32)strtol(textLinkBody.substr(11, 5).c_str(), nullptr, 16); + textLinkBodyStruct.augment_3 = (uint32)strtol(textLinkBody.substr(16, 5).c_str(), nullptr, 16); + textLinkBodyStruct.augment_4 = (uint32)strtol(textLinkBody.substr(21, 5).c_str(), nullptr, 16); + textLinkBodyStruct.augment_5 = (uint32)strtol(textLinkBody.substr(26, 5).c_str(), nullptr, 16); + textLinkBodyStruct.augment_6 = (uint32)strtol(textLinkBody.substr(31, 5).c_str(), nullptr, 16); + textLinkBodyStruct.is_evolving = (uint8)strtol(textLinkBody.substr(36, 1).c_str(), nullptr, 16); + textLinkBodyStruct.evolve_group = (uint32)strtol(textLinkBody.substr(37, 4).c_str(), nullptr, 16); + textLinkBodyStruct.evolve_level = (uint8)strtol(textLinkBody.substr(41, 2).c_str(), nullptr, 16); + textLinkBodyStruct.ornament_icon = (uint32)strtol(textLinkBody.substr(43, 5).c_str(), nullptr, 16); + textLinkBodyStruct.hash = (int)strtol(textLinkBody.substr(48, 8).c_str(), nullptr, 16); + + return true; +} + +bool Client::TextLink::GenerateLinkBody(std::string& textLinkBody, const TextLinkBody_Struct& textLinkBodyStruct) +{ + textLinkBody = StringFormat( + "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%02X" "%05X" "%08X", + (0x0F & textLinkBodyStruct.unknown_1), + (0x000FFFFF & textLinkBodyStruct.item_id), + (0x000FFFFF & textLinkBodyStruct.augment_1), + (0x000FFFFF & textLinkBodyStruct.augment_2), + (0x000FFFFF & textLinkBodyStruct.augment_3), + (0x000FFFFF & textLinkBodyStruct.augment_4), + (0x000FFFFF & textLinkBodyStruct.augment_5), + (0x000FFFFF & textLinkBodyStruct.augment_6), + (0x0F & textLinkBodyStruct.is_evolving), + (0x0000FFFF & textLinkBodyStruct.evolve_group), + (0xFF & textLinkBodyStruct.evolve_level), + (0x000FFFFF & textLinkBodyStruct.ornament_icon), + (0xFFFFFFFF & textLinkBodyStruct.hash) + ); + + if (textLinkBody.length() != EmuConstants::TEXT_LINK_BODY_LENGTH) { return false; } + return true; +} diff --git a/zone/client.h b/zone/client.h index b7bb8f6a5..22b413b50 100644 --- a/zone/client.h +++ b/zone/client.h @@ -428,6 +428,7 @@ public: inline virtual int32 GetPR() const { return PR; } inline virtual int32 GetCR() const { return CR; } inline virtual int32 GetCorrup() const { return Corrup; } + inline virtual int32 GetPhR() const { return PhR; } int32 GetMaxStat() const; int32 GetMaxResist() const; @@ -452,6 +453,7 @@ public: inline uint8 GetBaseAGI() const { return m_pp.AGI; } inline uint8 GetBaseWIS() const { return m_pp.WIS; } inline uint8 GetBaseCorrup() const { return 15; } // Same for all + inline uint8 GetBasePhR() const { return 0; } // Guessing at 0 as base inline virtual int32 GetHeroicSTR() const { return itembonuses.HeroicSTR; } inline virtual int32 GetHeroicSTA() const { return itembonuses.HeroicSTA; } @@ -466,6 +468,7 @@ public: inline virtual int32 GetHeroicPR() const { return itembonuses.HeroicPR; } inline virtual int32 GetHeroicCR() const { return itembonuses.HeroicCR; } inline virtual int32 GetHeroicCorrup() const { return itembonuses.HeroicCorrup; } + inline virtual int32 GetHeroicPhR() const { return 0; } // Heroic PhR not implemented yet // Mod2 inline virtual int32 GetShielding() const { return itembonuses.MeleeMitigation; } inline virtual int32 GetSpellShield() const { return itembonuses.SpellShield; } @@ -832,20 +835,19 @@ public: void SetProxyItemID(uint32 proxyItemID) { m_ProxyItemID = proxyItemID; } // mainly for saylinks..but, not limited to void SetProxyText(const char* proxyText) { m_ProxyText = proxyText; } // overrides standard text use void SetTaskUse() { m_TaskUse = true; } - void SetClientVersion(EQClientVersion clientVersion) { m_ClientVersion = EQLimits::ValidateClientVersion(clientVersion); } std::string GenerateLink(); bool LinkError() { return m_Error; } - const char* GetLink(); // contains full format: '/12x' '' '' '/12x' - const char* GetLinkBody(); // contains format: '' - const char* GetLinkText(); // contains format: '' - std::string GetLinkString(); - std::string GetLinkBodyString(); - std::string GetLinkTextString(); + std::string GetLink() { return m_Link; } // contains full string format: '/12x' '' '' '/12x' + std::string GetLinkBody() { return m_LinkBody; } // contains string format: '' + std::string GetLinkText() { return m_LinkText; } // contains string format: '' void Reset(); + static bool DegenerateLinkBody(TextLinkBody_Struct& textLinkBodyStruct, const std::string& textLinkBody); + static bool GenerateLinkBody(std::string& textLinkBody, const TextLinkBody_Struct& textLinkBodyStruct); + private: void generate_body(); void generate_text(); @@ -857,10 +859,10 @@ public: uint32 m_ProxyItemID; const char* m_ProxyText; bool m_TaskUse; + TextLinkBody_Struct m_LinkBodyStruct; std::string m_Link; std::string m_LinkBody; std::string m_LinkText; - EQClientVersion m_ClientVersion; bool m_Error; }; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index cf1dc71b8..c9ec08b30 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -9925,12 +9925,14 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) uint32 PetCommand = pet->command; // Handle Sit/Stand toggle in UF and later. + /* if (GetClientVersion() >= EQClientUnderfoot) { if (PetCommand == PET_SITDOWN) if (mypet->GetPetOrder() == SPO_Sit) PetCommand = PET_STANDUP; } + */ switch (PetCommand) { @@ -9968,6 +9970,31 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) } break; } + case PET_QATTACK: { + if (mypet->IsFeared()) + break; //prevent pet from attacking stuff while feared + + if (!GetTarget()) + break; + if (GetTarget()->IsMezzed()) { + Message_StringID(10, CANNOT_WAKE, mypet->GetCleanName(), GetTarget()->GetCleanName()); + break; + } + + if (!mypet->IsAttackAllowed(GetTarget())) { + mypet->Say_StringID(NOT_LEGAL_TARGET); + break; + } + + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 2) || mypet->GetPetType() != petAnimation) { + if (GetTarget() != this && mypet->DistNoRootNoZ(*GetTarget()) <= (RuleR(Pets, AttackCommandRange)*RuleR(Pets, AttackCommandRange))) { + zone->AddAggroMob(); + mypet->AddToHateList(GetTarget(), 1); + Message_StringID(MT_PetResponse, PET_ATTACKING, mypet->GetCleanName(), GetTarget()->GetCleanName()); + } + } + break; + } case PET_BACKOFF: { if (mypet->IsFeared()) break; //keeps pet running while feared @@ -10036,13 +10063,28 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) break; } case PET_TAUNT: { + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { + if (mypet->CastToNPC()->IsTaunting()) + { + Message_StringID(MT_PetResponse, PET_NO_TAUNT); + mypet->CastToNPC()->SetTaunting(false); + } + else + { + Message_StringID(MT_PetResponse, PET_DO_TAUNT); + mypet->CastToNPC()->SetTaunting(true); + } + } + break; + } + case PET_TAUNT_ON: { if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { Message_StringID(MT_PetResponse, PET_DO_TAUNT); mypet->CastToNPC()->SetTaunting(true); } break; } - case PET_NOTAUNT: { + case PET_TAUNT_OFF: { if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { Message_StringID(MT_PetResponse, PET_NO_TAUNT); mypet->CastToNPC()->SetTaunting(false); @@ -10060,6 +10102,38 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) } break; } + case PET_SIT: { + if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { + if (mypet->GetPetOrder() == SPO_Sit) + { + mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); + mypet->SetPetOrder(SPO_Follow); + mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); + } + else + { + mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); + mypet->SetPetOrder(SPO_Sit); + mypet->SetRunAnimSpeed(0); + if (!mypet->UseBardSpellLogic()) //maybe we can have a bard pet + mypet->InterruptSpell(); //No cast 4 u. //i guess the pet should start casting + mypet->SendAppearancePacket(AT_Anim, ANIM_SIT); + } + } + break; + } + case PET_STANDUP: { + if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { + mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); + mypet->SetPetOrder(SPO_Follow); + mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); + } + break; + } case PET_SITDOWN: { if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF @@ -10073,17 +10147,21 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) } break; } - case PET_STANDUP: { + case PET_SLUMBER: { if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF - if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { + if (mypet->GetPetType() != petAnimation) { + // Needs to have an IsSleeping() check added and this case should toggle on/off mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); - mypet->SetPetOrder(SPO_Follow); - mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); + mypet->SetPetOrder(SPO_Sit); + mypet->SetRunAnimSpeed(0); + if (!mypet->UseBardSpellLogic()) //maybe we can have a bard pet + mypet->InterruptSpell(); //No cast 4 u. //i guess the pet should start casting + mypet->SendAppearancePacket(AT_Anim, ANIM_DEATH); } break; } - case PET_SLUMBER: { + case PET_SLUMBER_ON: { if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF if (mypet->GetPetType() != petAnimation) { @@ -10096,14 +10174,32 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) } break; } + case PET_SLUMBER_OFF: { + if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + + if (mypet->GetPetType() != petAnimation) { + mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); + mypet->SetPetOrder(SPO_Follow); + mypet->SetRunAnimSpeed(mypet->GetBaseRunspeed()); + mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); + } + break; + } case PET_HOLD: { if (GetAA(aaPetDiscipline) && mypet->IsNPC()){ if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF - mypet->Say_StringID(MT_PetResponse, PET_ON_HOLD); - mypet->WipeHateList(); - mypet->SetHeld(true); + if (mypet->IsHeld()) + { + mypet->SetHeld(false); + } + else + { + mypet->Say_StringID(MT_PetResponse, PET_ON_HOLD); + mypet->WipeHateList(); + mypet->SetHeld(true); + } } break; } @@ -10123,7 +10219,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) mypet->SetHeld(false); break; } - case PET_NOCAST: { + case PET_SPELLHOLD: { if (GetAA(aaAdvancedPetDiscipline) == 2 && mypet->IsNPC()) { if (mypet->IsFeared()) break; @@ -10138,6 +10234,28 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) } break; } + case PET_SPELLHOLD_ON: { + if (GetAA(aaAdvancedPetDiscipline) == 2 && mypet->IsNPC()) { + if (mypet->IsFeared()) + break; + if (!mypet->IsNoCast()) { + Message_StringID(MT_PetResponse, PET_NOT_CASTING); + mypet->CastToNPC()->SetNoCast(true); + } + } + break; + } + case PET_SPELLHOLD_OFF: { + if (GetAA(aaAdvancedPetDiscipline) == 2 && mypet->IsNPC()) { + if (mypet->IsFeared()) + break; + if (mypet->IsNoCast()) { + Message_StringID(MT_PetResponse, PET_CASTING); + mypet->CastToNPC()->SetNoCast(false); + } + } + break; + } case PET_FOCUS: { if (GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { if (mypet->IsFeared()) diff --git a/zone/command.cpp b/zone/command.cpp index d31ea97b7..b33a3ee6d 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -2621,7 +2621,6 @@ void command_peekinv(Client *c, const Seperator *sep) std::string item_link; Client::TextLink linker; linker.SetLinkType(linker.linkItemInst); - linker.SetClientVersion(c->GetClientVersion()); c->Message(0, "Displaying inventory for %s...", targetClient->GetName()); @@ -5574,7 +5573,6 @@ void command_itemsearch(Client *c, const Seperator *sep) std::string item_link; Client::TextLink linker; linker.SetLinkType(linker.linkItemData); - linker.SetClientVersion(c->GetClientVersion()); if (Seperator::IsNumber(search_criteria)) { item = database.GetItem(atoi(search_criteria)); diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 380697eac..d5db03856 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -1258,23 +1258,16 @@ void Corpse::LootItem(Client* client, const EQApplicationPacket* app) { SetPlayerKillItemID(0); } - /* Send message with item link to groups and such */ - Client::TextLink linker; - linker.SetLinkType(linker.linkItemInst); - linker.SetItemInst(inst); - linker.SetClientVersion(client->GetClientVersion()); + /* Send message with item link to groups and such */ + Client::TextLink linker; + linker.SetLinkType(linker.linkItemInst); + linker.SetItemInst(inst); auto item_link = linker.GenerateLink(); client->Message_StringID(MT_LootMessages, LOOTED_MESSAGE, item_link.c_str()); - if(!IsPlayerCorpse()) { - // When sending to multiple/unknown client types, we set for the highest client.. - // ..which is processed when 'EQClientUnknown,' or default value, is selected. - // This should help with any current issues..or it may create more! O.o - linker.SetClientVersion(EQClientUnknown); - item_link = linker.GenerateLink(); - + if (!IsPlayerCorpse()) { Group *g = client->GetGroup(); if(g != nullptr) { g->GroupMessage_StringID(client, MT_LootMessages, OTHER_LOOTED_MESSAGE, client->GetName(), item_link.c_str()); diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index 92afdd8b3..26bb08f0a 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -576,6 +576,11 @@ int Lua_Mob::GetCorruption() { return self->GetCorrup(); } +int Lua_Mob::GetPhR() { + Lua_Safe_Call_Int(); + return self->GetPhR(); +} + int Lua_Mob::GetMaxSTR() { Lua_Safe_Call_Int(); return self->GetMaxSTR(); @@ -1962,6 +1967,7 @@ luabind::scope lua_register_mob() { .def("GetPR", &Lua_Mob::GetPR) .def("GetCR", &Lua_Mob::GetCR) .def("GetCorruption", &Lua_Mob::GetCorruption) + .def("GetPhR", &Lua_Mob::GetPhR) .def("GetMaxSTR", &Lua_Mob::GetMaxSTR) .def("GetMaxSTA", &Lua_Mob::GetMaxSTA) .def("GetMaxDEX", &Lua_Mob::GetMaxDEX) diff --git a/zone/lua_mob.h b/zone/lua_mob.h index a5c00c5f0..f272cd440 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -128,6 +128,7 @@ public: int GetPR(); int GetCR(); int GetCorruption(); + int GetPhR(); int GetMaxSTR(); int GetMaxSTA(); int GetMaxDEX(); diff --git a/zone/mob.cpp b/zone/mob.cpp index 79391de5c..3ea572eb6 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1296,7 +1296,7 @@ void Mob::ShowStats(Client* client) client->Message(0, " Mana: %i Max Mana: %i", GetMana(), GetMaxMana()); client->Message(0, " Total ATK: %i Worn/Spell ATK (Cap %i): %i", GetATK(), RuleI(Character, ItemATKCap), GetATKBonus()); client->Message(0, " STR: %i STA: %i DEX: %i AGI: %i INT: %i WIS: %i CHA: %i", GetSTR(), GetSTA(), GetDEX(), GetAGI(), GetINT(), GetWIS(), GetCHA()); - client->Message(0, " MR: %i PR: %i FR: %i CR: %i DR: %i Corruption: %i", GetMR(), GetPR(), GetFR(), GetCR(), GetDR(), GetCorrup()); + client->Message(0, " MR: %i PR: %i FR: %i CR: %i DR: %i Corruption: %i PhR: %i", GetMR(), GetPR(), GetFR(), GetCR(), GetDR(), GetCorrup(), GetPhR()); client->Message(0, " Race: %i BaseRace: %i Texture: %i HelmTexture: %i Gender: %i BaseGender: %i", GetRace(), GetBaseRace(), GetTexture(), GetHelmTexture(), GetGender(), GetBaseGender()); if (client->Admin() >= 100) client->Message(0, " EntityID: %i PetID: %i OwnerID: %i AIControlled: %i Targetted: %i", GetID(), GetPetID(), GetOwnerID(), IsAIControlled(), targeted); diff --git a/zone/mob.h b/zone/mob.h index 47d2cca2f..5997ffeae 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -371,7 +371,7 @@ public: inline virtual int32 GetPR() const { return PR + itembonuses.PR + spellbonuses.PR; } inline virtual int32 GetCR() const { return CR + itembonuses.CR + spellbonuses.CR; } inline virtual int32 GetCorrup() const { return Corrup + itembonuses.Corrup + spellbonuses.Corrup; } - inline virtual int32 GetPhR() const { return PhR; } + inline virtual int32 GetPhR() const { return PhR; } // PhR bonuses not implemented yet inline StatBonuses GetItemBonuses() const { return itembonuses; } inline StatBonuses GetSpellBonuses() const { return spellbonuses; } inline StatBonuses GetAABonuses() const { return aabonuses; } diff --git a/zone/npc.cpp b/zone/npc.cpp index a478e3b51..d6cf9ed9f 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -515,7 +515,6 @@ void NPC::QueryLoot(Client* to) Client::TextLink linker; linker.SetLinkType(linker.linkItemData); linker.SetItemData(item); - linker.SetClientVersion(to->GetClientVersion()); auto item_link = linker.GenerateLink(); diff --git a/zone/npc.h b/zone/npc.h index 72cc7f38c..e7a72ff11 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -266,6 +266,7 @@ public: void SetPetSpellID(uint16 amt) {pet_spell_id = amt;} uint32 GetMaxDamage(uint8 tlevel); void SetTaunting(bool tog) {taunting = tog;} + bool IsTaunting() const { return taunting; } void PickPocket(Client* thief); void StartSwarmTimer(uint32 duration) { swarm_timer.Start(duration); } void AddLootDrop(const Item_Struct*dbitem, ItemList* itemlistconst, int16 charges, uint8 minlevel, uint8 maxlevel, bool equipit, bool wearchange = false); diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index 7977c3dd1..af07cfcdf 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -2933,6 +2933,32 @@ XS(XS_Mob_GetCorruption) XSRETURN(1); } +XS(XS_Mob_GetPhR); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetPhR) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetPhR(THIS)"); + { + Mob * THIS; + int32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Mob *, tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Mob"); + if (THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + RETVAL = THIS->GetPhR(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + XS(XS_Mob_GetMaxSTR); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_GetMaxSTR) { @@ -8462,7 +8488,8 @@ XS(boot_Mob) newXSproto(strcpy(buf, "GetDR"), XS_Mob_GetDR, file, "$"); newXSproto(strcpy(buf, "GetPR"), XS_Mob_GetPR, file, "$"); newXSproto(strcpy(buf, "GetCR"), XS_Mob_GetCR, file, "$"); - newXSproto(strcpy(buf, "GetCorruption"), XS_Mob_GetCR, file, "$"); + newXSproto(strcpy(buf, "GetCorruption"), XS_Mob_GetCorruption, file, "$"); + newXSproto(strcpy(buf, "GetPhR"), XS_Mob_GetPhR, file, "$"); newXSproto(strcpy(buf, "GetMaxSTR"), XS_Mob_GetMaxSTR, file, "$"); newXSproto(strcpy(buf, "GetMaxSTA"), XS_Mob_GetMaxSTA, file, "$"); newXSproto(strcpy(buf, "GetMaxDEX"), XS_Mob_GetMaxDEX, file, "$"); diff --git a/zone/pets.h b/zone/pets.h index db78accea..f0c71fbe7 100644 --- a/zone/pets.h +++ b/zone/pets.h @@ -1,26 +1,37 @@ #ifndef PETS_H #define PETS_H -#define PET_BACKOFF 1 -#define PET_GETLOST 2 -#define PET_HEALTHREPORT 4 -#define PET_GUARDHERE 5 -#define PET_GUARDME 6 -#define PET_ATTACK 7 -#define PET_FOLLOWME 8 -#define PET_SITDOWN 9 -#define PET_STANDUP 10 -#define PET_TAUNT 11 -#define PET_HOLD 12 -#define PET_NOTAUNT 14 -#define PET_LEADER 16 -#define PET_SLUMBER 17 -#define PET_NOCAST 18 -#define PET_FOCUS 19 -#define PET_FOCUS_ON 25 -#define PET_FOCUS_OFF 26 -#define PET_HOLD_ON 27 -#define PET_HOLD_OFF 28 +// Defines based on the RoF2 Client +#define PET_HEALTHREPORT 0 // 0x00 - /pet health or Pet Window +#define PET_LEADER 1 // 0x01 - /pet leader or Pet Window +#define PET_ATTACK 2 // 0x02 - /pet attack or Pet Window +#define PET_QATTACK 3 // 0x03 - /pet qattack or Pet Window +#define PET_FOLLOWME 4 // 0x04 - /pet follow or Pet Window +#define PET_GUARDHERE 5 // 0x05 - /pet guard or Pet Window +#define PET_SIT 6 // 0x06 - /pet sit or Pet Window +#define PET_SITDOWN 7 // 0x07 - /pet sit on +#define PET_STANDUP 8 // 0x08 - /pet sit off +#define PET_STOP 9 // 0x09 - /pet stop or Pet Window - Not implemented +#define PET_STOP_ON 10 // 0x0a - /pet stop on - Not implemented +#define PET_STOP_OFF 11 // 0x0b - /pet stop off - Not implemented +#define PET_TAUNT 12 // 0x0c - /pet taunt or Pet Window +#define PET_TAUNT_ON 13 // 0x0d - /pet taunt on +#define PET_TAUNT_OFF 14 // 0x0e - /pet taunt off +#define PET_HOLD 15 // 0x0f - /pet hold or Pet Window +#define PET_HOLD_ON 16 // 0x10 - /pet hold on +#define PET_HOLD_OFF 17 // 0x11 - /pet hold off +#define PET_SLUMBER 18 // 0x12 - What activates this? - define guessed +#define PET_SLUMBER_ON 19 // 0x13 - What activates this? - define guessed +#define PET_SLUMBER_OFF 20 // 0x14 - What activates this? - define guessed +#define PET_SPELLHOLD 21 // 0x15 - /pet no cast or /pet spellhold or Pet Window +#define PET_SPELLHOLD_ON 22 // 0x16 - /pet spellhold on +#define PET_SPELLHOLD_OFF 23 // 0x17 - /pet spellhold off +#define PET_FOCUS 24 // 0x18 - /pet focus or Pet Window +#define PET_FOCUS_ON 25 // 0x19 - /pet focus on +#define PET_FOCUS_OFF 26 // 0x1a - /pet focus off +#define PET_BACKOFF 28 // 0x1c - /pet back off +#define PET_GETLOST 29 // 0x1d - /pet get lost +#define PET_GUARDME 30 // 0x1e - Same as /pet follow, but different message in older clients - define not from client class Mob; struct NPCType; diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 3fb714d7e..53eed6787 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1234,7 +1234,6 @@ void QuestManager::itemlink(int item_id) { Client::TextLink linker; linker.SetLinkType(linker.linkItemData); linker.SetItemData(item); - linker.SetClientVersion(initiator->GetClientVersion()); auto item_link = linker.GenerateLink(); @@ -2474,8 +2473,6 @@ const char* QuestManager::varlink(char* perltext, int item_id) { Client::TextLink linker; linker.SetLinkType(linker.linkItemData); linker.SetItemData(item); - if (initiator) - linker.SetClientVersion(initiator->GetClientVersion()); auto item_link = linker.GenerateLink(); strcpy(perltext, item_link.c_str()); // link length is currently ranged from 1 to 250 in TextLink::GenerateLink() @@ -2668,8 +2665,6 @@ const char* QuestManager::saylink(char* Phrase, bool silent, const char* LinkNam Client::TextLink linker; linker.SetProxyItemID(sayid); linker.SetProxyText(LinkName); - if (initiator) - linker.SetClientVersion(initiator->GetClientVersion()); auto say_link = linker.GenerateLink(); strcpy(Phrase, say_link.c_str()); // link length is currently ranged from 1 to 250 in TextLink::GenerateLink() diff --git a/zone/spells.cpp b/zone/spells.cpp index ce5d9df8d..2b5536bb3 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -274,7 +274,15 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot, database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking race/class restricted item with an invalid class"); } else { - Message_StringID(13, CANNOT_USE_ITEM); + if (CastToClient()->GetClientVersion() >= EQClientRoF) + { + // Line 181 in eqstr_us.txt was changed in RoF+ + Message(15, "Your race, class, or deity cannot use this item."); + } + else + { + Message_StringID(13, CANNOT_USE_ITEM); + } } return(false); } diff --git a/zone/tasks.cpp b/zone/tasks.cpp index 79efb1115..1f6e381fd 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -2779,7 +2779,6 @@ void TaskManager::SendActiveTaskDescription(Client *c, int TaskID, int SequenceN Client::TextLink linker; linker.SetLinkType(linker.linkItemData); linker.SetItemData(reward_item); - linker.SetClientVersion(c->GetClientVersion()); linker.SetTaskUse(); if (strlen(Tasks[TaskID]->Reward) != 0) linker.SetProxyText(Tasks[TaskID]->Reward);