diff --git a/changelog.txt b/changelog.txt index f515e8e49..f2f200fa6 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,25 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 03/07/2018 == +Uleat: Added command '#ucs' to force a reconnect to UCS server. + - Works in place of client auto-reconnect packet in zones where feature is unsupported + - Currently, you will need to manually re-join channels + +== 03/04/2018 == +Uleat: Updated UCS versioning + - SoF and higher clients have a new opcode identified (update your *.conf files) + - Rework of previous ucs connectivity code + - Unrelated: Zone::weatherSend() now takes an optional parameter for singular updates (as in client entering zone) + -- prior to this, every client already in-zone received a weather update packet whenever a new client zoned in + +== 02/18/2018 == +Uleat: Bug reporting fix and overhaul. + - Fixed bug reporting for SoD+ clients + - Added ability to disable bug reporting (set rule 'Bugs:ReportingSystemActive' to 'false') + - Implemented a more detailed reporting system (set rule 'Bugs:UseOldReportingMethod' to 'false') + -- New system is not currently compatible with script-based monitoring + - Soft-removal of defunct 'Petition Bug' system + == 02/14/2018 == mackal: Fix Heading -- Quests broken diff --git a/common/emu_constants.cpp b/common/emu_constants.cpp index a8fa73d1c..e5e9ae841 100644 --- a/common/emu_constants.cpp +++ b/common/emu_constants.cpp @@ -18,3 +18,68 @@ */ #include "emu_constants.h" + + +const char* EQEmu::bug::CategoryIDToCategoryName(CategoryID category_id) { + switch (category_id) { + case catVideo: + return "Video"; + case catAudio: + return "Audio"; + case catPathing: + return "Pathing"; + case catQuest: + return "Quest"; + case catTradeskills: + return "Tradeskills"; + case catSpellStacking: + return "Spell stacking"; + case catDoorsPortals: + return "Doors/Portals"; + case catItems: + return "Items"; + case catNPC: + return "NPC"; + case catDialogs: + return "Dialogs"; + case catLoNTCG: + return "LoN - TCG"; + case catMercenaries: + return "Mercenaries"; + case catOther: + default: + return "Other"; + } +} + +EQEmu::bug::CategoryID EQEmu::bug::CategoryNameToCategoryID(const char* category_name) { + if (!category_name) + return catOther; + + if (!strcmp(category_name, "Video")) + return catVideo; + if (!strcmp(category_name, "Audio")) + return catAudio; + if (!strcmp(category_name, "Pathing")) + return catPathing; + if (!strcmp(category_name, "Quest")) + return catQuest; + if (!strcmp(category_name, "Tradeskills")) + return catTradeskills; + if (!strcmp(category_name, "Spell stacking")) + return catSpellStacking; + if (!strcmp(category_name, "Doors/Portals")) + return catDoorsPortals; + if (!strcmp(category_name, "Items")) + return catItems; + if (!strcmp(category_name, "NPC")) + return catNPC; + if (!strcmp(category_name, "Dialogs")) + return catDialogs; + if (!strcmp(category_name, "LoN - TCG")) + return catLoNTCG; + if (!strcmp(category_name, "Mercenaries")) + return catMercenaries; + + return catOther; +} diff --git a/common/emu_constants.h b/common/emu_constants.h index e156fe98a..32968458c 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -24,7 +24,7 @@ #include "emu_legacy.h" #include "emu_versions.h" -#include +#include namespace EQEmu @@ -114,7 +114,11 @@ namespace EQEmu const EQEmu::versions::ClientVersion CharacterCreationClient = EQEmu::versions::ClientVersion::RoF2; const size_t CharacterCreationMax = RoF2::constants::CharacterCreationLimit; + const size_t SayLinkOpenerSize = 1; const size_t SayLinkBodySize = RoF2::constants::SayLinkBodySize; + const size_t SayLinkTextSize = 256; // this may be varied until it breaks something (tested:374) - the others are constant + const size_t SayLinkCloserSize = 1; + const size_t SayLinkMaximumSize = (SayLinkOpenerSize + SayLinkBodySize + SayLinkTextSize + SayLinkCloserSize); const int LongBuffs = RoF2::constants::LongBuffs; const int ShortBuffs = RoF2::constants::ShortBuffs; @@ -126,6 +130,37 @@ namespace EQEmu } /*constants*/ + namespace bug { + enum CategoryID : uint32 { + catOther = 0, + catVideo, + catAudio, + catPathing, + catQuest, + catTradeskills, + catSpellStacking, + catDoorsPortals, + catItems, + catNPC, + catDialogs, + catLoNTCG, + catMercenaries + }; + + enum OptionalInfoFlag : uint32 { + infoNoOptionalInfo = 0x0, + infoCanDuplicate = 0x1, + infoCrashBug = 0x2, + infoTargetInfo = 0x4, + infoCharacterFlags = 0x8, + infoUnknownValue = 0xFFFFFFF0 + }; + + const char* CategoryIDToCategoryName(CategoryID category_id); + CategoryID CategoryNameToCategoryID(const char* category_name); + + } // namespace bug + enum class CastingSlot : uint32 { Gem1 = 0, Gem2 = 1, diff --git a/common/emu_legacy.h b/common/emu_legacy.h index 0f3cf0a62..812a33ca1 100644 --- a/common/emu_legacy.h +++ b/common/emu_legacy.h @@ -175,8 +175,6 @@ namespace EQEmu // POTION_BELT_SIZE sets maximum limit..active limit will need to be handled by the appropriate AA or spell (or item?) static const size_t POTION_BELT_ITEM_COUNT = 5; - - static const size_t TEXT_LINK_BODY_LENGTH = 56; } } diff --git a/common/emu_oplist.h b/common/emu_oplist.h index 89e0126a8..5078cca68 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -393,6 +393,7 @@ N(OP_PVPLeaderBoardReply), N(OP_PVPLeaderBoardRequest), N(OP_PVPStats), N(OP_QueryResponseThing), +N(OP_QueryUCSServerStatus), N(OP_RaidInvite), N(OP_RaidJoin), N(OP_RaidUpdate), diff --git a/common/emu_versions.h b/common/emu_versions.h index 9d9e1f580..a5f3d0183 100644 --- a/common/emu_versions.h +++ b/common/emu_versions.h @@ -28,7 +28,7 @@ namespace EQEmu { namespace versions { - enum class ClientVersion { + enum class ClientVersion : uint32 { Unknown = 0, Client62, // Build: 'Aug 4 2005 15:40:59' Titanium, // Build: 'Oct 31 2005 10:33:37' @@ -72,7 +72,7 @@ namespace EQEmu uint32 ConvertClientVersionToExpansion(ClientVersion client_version); - enum class MobVersion { + enum class MobVersion : uint32 { Unknown = 0, Client62, Titanium, @@ -121,6 +121,20 @@ namespace EQEmu ClientVersion ConvertOfflinePCMobVersionToClientVersion(MobVersion mob_version); MobVersion ConvertClientVersionToOfflinePCMobVersion(ClientVersion client_version); + + enum UCSVersion : char { + ucsUnknown = '\0', + ucsClient62Chat = 'A', + ucsClient62Mail = 'a', + ucsTitaniumChat = 'B', + ucsTitaniumMail = 'b', + ucsSoFCombined = 'C', + ucsSoDCombined = 'D', + ucsUFCombined = 'E', + ucsRoFCombined = 'F', + ucsRoF2Combined = 'G' + }; + } /*versions*/ } /*EQEmu*/ diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 8452813f5..fd09f4d56 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -280,7 +280,7 @@ union // horse: 0=brown, 1=white, 2=black, 3=tan }; /*0340*/ uint32 spawnId; // Spawn Id -/*0344*/ uint8 unknown0344[3]; +/*0344*/ float bounding_radius; // used in melee, overrides calc /*0347*/ uint8 IsMercenary; /*0348*/ EQEmu::TintProfile equipment_tint; /*0384*/ uint8 lfg; // 0=off, 1=lfg on @@ -1253,21 +1253,22 @@ struct Action_Struct { /* 00 */ uint16 target; // id of target /* 02 */ uint16 source; // id of caster - /* 04 */ uint16 level; // level of caster - /* 06 */ uint16 instrument_mod; - /* 08 */ uint32 bard_focus_id; - /* 12 */ uint16 unknown16; -// some kind of sequence that's the same in both actions -// as well as the combat damage, to tie em together? - /* 14 */ uint32 sequence; - /* 18 */ uint32 unknown18; - /* 22 */ uint8 type; // 231 (0xE7) for spells - /* 23 */ uint32 unknown23; + /* 04 */ uint16 level; // level of caster for spells, OSX dump says attack rating, guess spells use it for level + /* 06 */ uint32 instrument_mod; // OSX dump says base damage, spells use it for bard song (different from newer clients) + /* 10 */ float force; + /* 14 */ float hit_heading; + /* 18 */ float hit_pitch; + /* 22 */ uint8 type; // 231 (0xE7) for spells, skill + /* 23 */ uint16 unknown23; // OSX says min_damage + /* 25 */ uint16 unknown25; // OSX says tohit /* 27 */ uint16 spell; // spell id being cast - /* 29 */ uint8 unknown29; + /* 29 */ uint8 spell_level; // this field seems to be some sort of success flag, if it's 4 - /* 30 */ uint8 buff_unknown; // if this is 4, a buff icon is made - /* 31 */ + /* 30 */ uint8 effect_flag; // if this is 4, a buff icon is made +// newer clients have some data for setting LaunchSpellData when effect_flag & 4 +// /* 31 */ uint8 spell_gem; +// /* 32 */ uint32 inventory_slot; +// /* 36 */ uint32 item_cast_type; }; // this is what prints the You have been struck. and the regular @@ -1277,12 +1278,12 @@ struct CombatDamage_Struct { /* 00 */ uint16 target; /* 02 */ uint16 source; -/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells +/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells, skill /* 05 */ uint16 spellid; /* 07 */ uint32 damage; /* 11 */ float force; -/* 15 */ float meleepush_xy; // see above notes in Action_Struct -/* 19 */ float meleepush_z; +/* 15 */ float hit_heading; // see above notes in Action_Struct +/* 19 */ float hit_pitch; /* 23 */ uint32 special; // 2 = Rampage, 1 = Wild Rampage }; @@ -3323,23 +3324,32 @@ struct GuildMakeLeader{ char target[64]; }; -struct BugStruct{ -/*0000*/ char chartype[64]; -/*0064*/ char name[96]; -/*0160*/ char ui[128]; -/*0288*/ float x; -/*0292*/ float y; -/*0296*/ float z; -/*0300*/ float heading; -/*0304*/ uint32 unknown304; -/*0308*/ char unknown308[160]; -/*0468*/ char target_name[64]; -/*0532*/ uint32 type; -/*0536*/ char unknown536[2052]; -/*2584*/ char bug[2048]; -/*4632*/ char unknown4632[6]; -/*4638*/ char system_info[4094]; +struct BugReport_Struct { +/*0000*/ uint32 category_id; +/*0004*/ char category_name[64]; +/*0068*/ char reporter_name[64]; +/*0132*/ char unused_0132[32]; +/*0164*/ char ui_path[128]; +/*0292*/ float pos_x; +/*0296*/ float pos_y; +/*0300*/ float pos_z; +/*0304*/ uint32 heading; +/*0308*/ uint32 unused_0308; +/*0312*/ uint32 time_played; +/*0316*/ char padding_0316[8]; +/*0324*/ uint32 target_id; +/*0328*/ char padding_0328[140]; +/*0468*/ uint32 unknown_0468; // seems to always be '0' +/*0472*/ char target_name[64]; +/*0536*/ uint32 optional_info_mask; + +// this looks like a butchered 8k buffer with 2 trailing dword fields +/*0540*/ char unused_0540[2052]; +/*2592*/ char bug_report[2050]; +/*4642*/ char system_info[4098]; +/*8740*/ }; + struct Make_Pet_Struct { //Simple struct for getting pet info uint8 level; uint8 class_; @@ -3366,20 +3376,21 @@ struct Ground_Spawn{ struct Ground_Spawns { struct Ground_Spawn spawn[50]; //Assigned max number to allow }; -struct PetitionBug_Struct{ - uint32 petition_number; - uint32 unknown4; - char accountname[64]; - uint32 zoneid; - char name[64]; - uint32 level; - uint32 class_; - uint32 race; - uint32 unknown152[3]; - uint32 time; - uint32 unknown168; - char text[1028]; -}; + +//struct PetitionBug_Struct{ +// uint32 petition_number; +// uint32 unknown4; +// char accountname[64]; +// uint32 zoneid; +// char name[64]; +// uint32 level; +// uint32 class_; +// uint32 race; +// uint32 unknown152[3]; +// uint32 time; +// uint32 unknown168; +// char text[1028]; +//}; struct ApproveZone_Struct { char name[64]; @@ -5356,6 +5367,23 @@ struct AuraDestory_Struct { }; // I think we can assume it's just action for 2, client doesn't seem to do anything with the rest of the data in that case +struct SayLinkBodyFrame_Struct { +/*000*/ char ActionID[1]; +/*001*/ char ItemID[5]; +/*006*/ char Augment1[5]; +/*011*/ char Augment2[5]; +/*016*/ char Augment3[5]; +/*021*/ char Augment4[5]; +/*026*/ char Augment5[5]; +/*031*/ char Augment6[5]; +/*036*/ char IsEvolving[1]; +/*037*/ char EvolveGroup[4]; +/*041*/ char EvolveLevel[2]; +/*043*/ char OrnamentIcon[5]; +/*048*/ char Hash[8]; +/*056*/ +}; + // Restore structure packing to default #pragma pack() diff --git a/common/features.h b/common/features.h index 486255f28..a35431bff 100644 --- a/common/features.h +++ b/common/features.h @@ -219,6 +219,9 @@ enum { //some random constants //the square of the maximum range at whihc you could possibly use NPC services (shop, tribute, etc) #define USE_NPC_RANGE2 200*200 //arbitrary right now +// Squared range for rampage 75.0 * 75.0 for now +#define NPC_RAMPAGE_RANGE2 5625.0f + //the formula for experience for killing a mob. //level is the only valid variable to use #define EXP_FORMULA level*level*75*35/10 diff --git a/common/misc_functions.cpp b/common/misc_functions.cpp index aaa20f3d4..e31da211f 100644 --- a/common/misc_functions.cpp +++ b/common/misc_functions.cpp @@ -242,6 +242,24 @@ int FloatToEQSpeedRun(float d) return static_cast(d * 40.0f); } +float FixHeading(float in) +{ + int i = 0; + if (in >= 512.0f) { + do { + in -= 512.0f; + } while (in >= 512.0f && i++ <= 5); + } + i = 0; + if (in < 0.0f) { + do { + in += 512.0f; + } while (in < 0.0f && i++ <= 5); + } + + return in; +} + /* Heading of 0 points in the pure positive Y direction diff --git a/common/misc_functions.h b/common/misc_functions.h index 9ecb08378..8214c7df0 100644 --- a/common/misc_functions.h +++ b/common/misc_functions.h @@ -66,6 +66,9 @@ int FloatToEQ10(float d); float EQSpeedRunToFloat(int d); int FloatToEQSpeedRun(float d); +// brings heading back into EQ angles range +float FixHeading(float in); + uint32 SwapBits21and22(uint32 mask); uint32 Catch22(uint32 mask); diff --git a/common/net/daybreak_connection.cpp b/common/net/daybreak_connection.cpp index 095fab525..d32f9f678 100644 --- a/common/net/daybreak_connection.cpp +++ b/common/net/daybreak_connection.cpp @@ -165,7 +165,7 @@ void EQ::Net::DaybreakConnectionManager::ProcessResend() { auto iter = m_connections.begin(); while (iter != m_connections.end()) { - auto connection = iter->second; + auto &connection = iter->second; auto status = connection->m_status; switch (status) @@ -1397,4 +1397,4 @@ EQ::Net::SequenceOrder EQ::Net::DaybreakConnection::CompareSequence(uint16_t exp } return SequencePast; -} \ No newline at end of file +} diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index a57e798db..62ef6751a 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -57,11 +57,11 @@ namespace RoF static inline uint32 RoFToServerTypelessSlot(structs::TypelessInventorySlot_Struct rofSlot); static inline uint32 RoFToServerCorpseSlot(uint32 rofCorpseSlot); - // server to client text link converter - static inline void ServerToRoFTextLink(std::string& rofTextLink, const std::string& serverTextLink); + // server to client say link converter + static inline void ServerToRoFSayLink(std::string& rofSayLink, const std::string& serverSayLink); - // client to server text link converter - static inline void RoFToServerTextLink(std::string& serverTextLink, const std::string& rofTextLink); + // client to server say link converter + static inline void RoFToServerSayLink(std::string& serverSayLink, const std::string& rofSayLink); static inline CastingSlot ServerToRoFCastingSlot(EQEmu::CastingSlot slot); static inline EQEmu::CastingSlot RoFToServerCastingSlot(CastingSlot slot); @@ -163,22 +163,23 @@ namespace RoF OUT(level); eq->unknown06 = 0; eq->instrument_mod = 1.0f + (emu->instrument_mod - 10) / 10.0f; - eq->bard_focus_id = emu->bard_focus_id; - eq->knockback_angle = emu->sequence; - eq->unknown22 = 0; + OUT(force); + OUT(hit_heading); + OUT(hit_pitch); OUT(type); eq->damage = 0; eq->unknown31 = 0; OUT(spell); - eq->level2 = eq->level; - eq->effect_flag = emu->buff_unknown; - eq->unknown39 = 14; - eq->unknown43 = 0; - eq->unknown44 = 17; - eq->unknown45 = 0; - eq->unknown46 = -1; - eq->unknown50 = 0; - eq->unknown54 = 0; + OUT(spell_level); + OUT(effect_flag); + eq->spell_gem = 0; + eq->slot.Type = INVALID_INDEX; + eq->slot.Unknown02 = 0; + eq->slot.Slot = INVALID_INDEX; + eq->slot.SubIndex = INVALID_INDEX; + eq->slot.AugIndex = INVALID_INDEX; + eq->slot.Unknown01 = 0; + eq->item_cast_type = 0; FINISH_ENCODE(); } @@ -520,7 +521,7 @@ namespace RoF std::string old_message = emu->message; std::string new_message; - ServerToRoFTextLink(new_message, old_message); + ServerToRoFSayLink(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; @@ -659,8 +660,8 @@ namespace RoF OUT(spellid); OUT(damage); OUT(force); - OUT(meleepush_xy); - OUT(meleepush_z); + OUT(hit_heading); + OUT(hit_pitch); OUT(special); FINISH_ENCODE(); @@ -847,7 +848,7 @@ namespace RoF std::string old_message = emu->message; std::string new_message; - ServerToRoFTextLink(new_message, old_message); + ServerToRoFSayLink(new_message, old_message); //if (new_message.length() > 512) // length restricted in packet building function due vari-length name size (no nullterm) // new_message = new_message.substr(0, 512); @@ -899,7 +900,7 @@ namespace RoF for (int i = 0; i < 9; ++i) { if (old_message_array[i].length() == 0) { break; } - ServerToRoFTextLink(new_message_array[i], old_message_array[i]); + ServerToRoFSayLink(new_message_array[i], old_message_array[i]); new_message_size += new_message_array[i].length() + 1; } @@ -3295,7 +3296,7 @@ namespace RoF std::string old_message = &emu->message[strlen(emu->sayer)]; std::string new_message; - ServerToRoFTextLink(new_message, old_message); + ServerToRoFSayLink(new_message, old_message); //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; in->size = strlen(emu->sayer) + new_message.length() + 25; @@ -3369,7 +3370,7 @@ namespace RoF std::string old_message = InBuffer; // start 'Reward' as string std::string new_message; - ServerToRoFTextLink(new_message, old_message); + ServerToRoFSayLink(new_message, old_message); in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+ sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_Struct)+ @@ -4355,7 +4356,7 @@ namespace RoF std::string old_message = InBuffer; std::string new_message; - RoFToServerTextLink(new_message, old_message); + RoFToServerSayLink(new_message, old_message); //__packet->size = sizeof(ChannelMessage_Struct)+strlen(InBuffer) + 1; __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; @@ -4466,7 +4467,7 @@ namespace RoF IN(type); IN(spellid); IN(damage); - IN(meleepush_xy); + IN(hit_heading); FINISH_DIRECT_DECODE(); } @@ -4489,7 +4490,7 @@ namespace RoF std::string old_message = (char *)&__eq_buffer[4]; // unknown01 offset std::string new_message; - RoFToServerTextLink(new_message, old_message); + RoFToServerSayLink(new_message, old_message); __packet->size = sizeof(Emote_Struct); __packet->pBuffer = new unsigned char[__packet->size]; @@ -5916,19 +5917,19 @@ namespace RoF return (rofCorpseSlot - 1); } - static inline void ServerToRoFTextLink(std::string& rofTextLink, const std::string& serverTextLink) + static inline void ServerToRoFSayLink(std::string& rofSayLink, const std::string& serverSayLink) { - if ((constants::SayLinkBodySize == EQEmu::legacy::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find('\x12') == std::string::npos)) { - rofTextLink = serverTextLink; + if ((constants::SayLinkBodySize == EQEmu::constants::SayLinkBodySize) || (serverSayLink.find('\x12') == std::string::npos)) { + rofSayLink = serverSayLink; return; } - auto segments = SplitString(serverTextLink, '\x12'); + auto segments = SplitString(serverSayLink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { - if (segments[segment_iter].length() <= EQEmu::legacy::TEXT_LINK_BODY_LENGTH) { - rofTextLink.append(segments[segment_iter]); + if (segments[segment_iter].length() <= EQEmu::constants::SayLinkBodySize) { + rofSayLink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -5938,36 +5939,36 @@ namespace RoF // RoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (55) // Diff: ^ - rofTextLink.push_back('\x12'); - rofTextLink.append(segments[segment_iter].substr(0, 41)); + rofSayLink.push_back('\x12'); + rofSayLink.append(segments[segment_iter].substr(0, 41)); if (segments[segment_iter][41] == '0') - rofTextLink.push_back(segments[segment_iter][42]); + rofSayLink.push_back(segments[segment_iter][42]); else - rofTextLink.push_back('F'); + rofSayLink.push_back('F'); - rofTextLink.append(segments[segment_iter].substr(43)); - rofTextLink.push_back('\x12'); + rofSayLink.append(segments[segment_iter].substr(43)); + rofSayLink.push_back('\x12'); } else { - rofTextLink.append(segments[segment_iter]); + rofSayLink.append(segments[segment_iter]); } } } - static inline void RoFToServerTextLink(std::string& serverTextLink, const std::string& rofTextLink) + static inline void RoFToServerSayLink(std::string& serverSayLink, const std::string& rofSayLink) { - if ((EQEmu::legacy::TEXT_LINK_BODY_LENGTH == constants::SayLinkBodySize) || (rofTextLink.find('\x12') == std::string::npos)) { - serverTextLink = rofTextLink; + if ((EQEmu::constants::SayLinkBodySize == constants::SayLinkBodySize) || (rofSayLink.find('\x12') == std::string::npos)) { + serverSayLink = rofSayLink; return; } - auto segments = SplitString(rofTextLink, '\x12'); + auto segments = SplitString(rofSayLink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { if (segments[segment_iter].length() <= constants::SayLinkBodySize) { - serverTextLink.append(segments[segment_iter]); + serverSayLink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -5977,14 +5978,14 @@ namespace RoF // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) // Diff: ^ - serverTextLink.push_back('\x12'); - serverTextLink.append(segments[segment_iter].substr(0, 41)); - serverTextLink.push_back('0'); - serverTextLink.append(segments[segment_iter].substr(41)); - serverTextLink.push_back('\x12'); + serverSayLink.push_back('\x12'); + serverSayLink.append(segments[segment_iter].substr(0, 41)); + serverSayLink.push_back('0'); + serverSayLink.append(segments[segment_iter].substr(41)); + serverSayLink.push_back('\x12'); } else { - serverTextLink.append(segments[segment_iter]); + serverSayLink.append(segments[segment_iter]); } } } diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 64b991ddc..5a30c1695 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -57,11 +57,11 @@ namespace RoF2 static inline uint32 RoF2ToServerTypelessSlot(structs::TypelessInventorySlot_Struct rof2Slot); static inline uint32 RoF2ToServerCorpseSlot(uint32 rof2CorpseSlot); - // server to client text link converter - static inline void ServerToRoF2TextLink(std::string& rof2TextLink, const std::string& serverTextLink); + // server to client say link converter + static inline void ServerToRoF2SayLink(std::string& rof2SayLink, const std::string& serverSayLink); - // client to server text link converter - static inline void RoF2ToServerTextLink(std::string& serverTextLink, const std::string& rof2TextLink); + // client to server say link converter + static inline void RoF2ToServerSayLink(std::string& serverSayLink, const std::string& rof2SayLink); static inline CastingSlot ServerToRoF2CastingSlot(EQEmu::CastingSlot slot); static inline EQEmu::CastingSlot RoF2ToServerCastingSlot(CastingSlot slot); @@ -232,22 +232,23 @@ namespace RoF2 OUT(level); eq->unknown06 = 0; eq->instrument_mod = 1.0f + (emu->instrument_mod - 10) / 10.0f; - eq->bard_focus_id = emu->bard_focus_id; - eq->knockback_angle = emu->sequence; - eq->unknown22 = 0; + OUT(force); + OUT(hit_heading); + OUT(hit_pitch); OUT(type); eq->damage = 0; eq->unknown31 = 0; OUT(spell); - eq->level2 = eq->level; - eq->effect_flag = emu->buff_unknown; - eq->unknown39 = 14; - eq->unknown43 = 0; - eq->unknown44 = 17; - eq->unknown45 = 0; - eq->unknown46 = -1; - eq->unknown50 = 0; - eq->unknown54 = 0; + OUT(spell_level); + OUT(effect_flag); + eq->spell_gem = 0; + eq->slot.Type = INVALID_INDEX; + eq->slot.Unknown02 = 0; + eq->slot.Slot = INVALID_INDEX; + eq->slot.SubIndex = INVALID_INDEX; + eq->slot.AugIndex = INVALID_INDEX; + eq->slot.Unknown01 = 0; + eq->item_cast_type = 0; FINISH_ENCODE(); } @@ -588,7 +589,7 @@ namespace RoF2 std::string old_message = emu->message; std::string new_message; - ServerToRoF2TextLink(new_message, old_message); + ServerToRoF2SayLink(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; @@ -727,8 +728,8 @@ namespace RoF2 OUT(spellid); OUT(damage); OUT(force); - OUT(meleepush_xy); - OUT(meleepush_z); + OUT(hit_heading); + OUT(hit_pitch); OUT(special); FINISH_ENCODE(); @@ -915,7 +916,7 @@ namespace RoF2 std::string old_message = emu->message; std::string new_message; - ServerToRoF2TextLink(new_message, old_message); + ServerToRoF2SayLink(new_message, old_message); //if (new_message.length() > 512) // length restricted in packet building function due vari-length name size (no nullterm) // new_message = new_message.substr(0, 512); @@ -967,7 +968,7 @@ namespace RoF2 for (int i = 0; i < 9; ++i) { if (old_message_array[i].length() == 0) { break; } - ServerToRoF2TextLink(new_message_array[i], old_message_array[i]); + ServerToRoF2SayLink(new_message_array[i], old_message_array[i]); new_message_size += new_message_array[i].length() + 1; } @@ -3364,7 +3365,7 @@ namespace RoF2 std::string old_message = &emu->message[strlen(emu->sayer)]; std::string new_message; - ServerToRoF2TextLink(new_message, old_message); + ServerToRoF2SayLink(new_message, old_message); //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; in->size = strlen(emu->sayer) + new_message.length() + 25; @@ -3438,7 +3439,7 @@ namespace RoF2 std::string old_message = InBuffer; // start 'Reward' as string std::string new_message; - ServerToRoF2TextLink(new_message, old_message); + ServerToRoF2SayLink(new_message, old_message); in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+ sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_Struct)+ @@ -4595,7 +4596,7 @@ namespace RoF2 std::string old_message = InBuffer; std::string new_message; - RoF2ToServerTextLink(new_message, old_message); + RoF2ToServerSayLink(new_message, old_message); //__packet->size = sizeof(ChannelMessage_Struct)+strlen(InBuffer) + 1; __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; @@ -4706,7 +4707,7 @@ namespace RoF2 IN(type); IN(spellid); IN(damage); - IN(meleepush_xy); + IN(hit_heading); FINISH_DIRECT_DECODE(); } @@ -4729,7 +4730,7 @@ namespace RoF2 std::string old_message = (char *)&__eq_buffer[4]; // unknown01 offset std::string new_message; - RoF2ToServerTextLink(new_message, old_message); + RoF2ToServerSayLink(new_message, old_message); __packet->size = sizeof(Emote_Struct); __packet->pBuffer = new unsigned char[__packet->size]; @@ -6233,19 +6234,19 @@ namespace RoF2 return (rof2CorpseSlot + EQEmu::legacy::CORPSE_BEGIN - 1); } - static inline void ServerToRoF2TextLink(std::string& rof2TextLink, const std::string& serverTextLink) + static inline void ServerToRoF2SayLink(std::string& rof2SayLink, const std::string& serverSayLink) { - if ((constants::SayLinkBodySize == EQEmu::legacy::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find('\x12') == std::string::npos)) { - rof2TextLink = serverTextLink; + if ((constants::SayLinkBodySize == EQEmu::constants::SayLinkBodySize) || (serverSayLink.find('\x12') == std::string::npos)) { + rof2SayLink = serverSayLink; return; } - auto segments = SplitString(serverTextLink, '\x12'); + auto segments = SplitString(serverSayLink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { - if (segments[segment_iter].length() <= EQEmu::legacy::TEXT_LINK_BODY_LENGTH) { - rof2TextLink.append(segments[segment_iter]); + if (segments[segment_iter].length() <= EQEmu::constants::SayLinkBodySize) { + rof2SayLink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -6255,29 +6256,29 @@ namespace RoF2 // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) // Diff: - rof2TextLink.push_back('\x12'); - rof2TextLink.append(segments[segment_iter]); - rof2TextLink.push_back('\x12'); + rof2SayLink.push_back('\x12'); + rof2SayLink.append(segments[segment_iter]); + rof2SayLink.push_back('\x12'); } else { - rof2TextLink.append(segments[segment_iter]); + rof2SayLink.append(segments[segment_iter]); } } } - static inline void RoF2ToServerTextLink(std::string& serverTextLink, const std::string& rof2TextLink) + static inline void RoF2ToServerSayLink(std::string& serverSayLink, const std::string& rof2SayLink) { - if ((EQEmu::legacy::TEXT_LINK_BODY_LENGTH == constants::SayLinkBodySize) || (rof2TextLink.find('\x12') == std::string::npos)) { - serverTextLink = rof2TextLink; + if ((EQEmu::constants::SayLinkBodySize == constants::SayLinkBodySize) || (rof2SayLink.find('\x12') == std::string::npos)) { + serverSayLink = rof2SayLink; return; } - auto segments = SplitString(rof2TextLink, '\x12'); + auto segments = SplitString(rof2SayLink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { if (segments[segment_iter].length() <= constants::SayLinkBodySize) { - serverTextLink.append(segments[segment_iter]); + serverSayLink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -6287,12 +6288,12 @@ namespace RoF2 // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) // Diff: - serverTextLink.push_back('\x12'); - serverTextLink.append(segments[segment_iter]); - serverTextLink.push_back('\x12'); + serverSayLink.push_back('\x12'); + serverSayLink.append(segments[segment_iter]); + serverSayLink.push_back('\x12'); } else { - serverTextLink.append(segments[segment_iter]); + serverSayLink.append(segments[segment_iter]); } } } diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index 5d42d9046..5be96e071 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -430,7 +430,7 @@ struct Spawn_Struct /*0000*/ //uint8 nullterm1; // hack to null terminate name /*0064*/ uint32 spawnId; /*0068*/ uint8 level; -/*0069*/ float unknown1; +/*0069*/ float bounding_radius; // used in melee, overrides calc /*0073*/ uint8 NPC; // 0=player,1=npc,2=pc corpse,3=npc corpse Spawn_Struct_Bitfields Bitfields; /*0000*/ uint8 otherData; // & 4 - has title, & 8 - has suffix, & 1 - it's a chest or untargetable @@ -1462,17 +1462,17 @@ struct Action_Struct { /*00*/ uint16 target; // id of target /*02*/ uint16 source; // id of caster -/*04*/ uint16 level; // level of caster - Seen 0 -/*06*/ uint32 unknown06; +/*04*/ uint16 level; // level of caster for spells, OSX dump says attack rating, guess spells use it for level +/*06*/ uint32 unknown06; // OSX dump says base_damage, was used for bard mod too, this is 0'd :( /*10*/ float instrument_mod; -/*14*/ uint32 bard_focus_id; // seen 0 -/*18*/ float knockback_angle; //seems to go from 0-512 then it rolls over again -/*22*/ uint32 unknown22; -/*26*/ uint8 type; -/*27*/ uint32 damage; -/*31*/ uint16 unknown31; +/*14*/ float force; +/*18*/ float hit_heading; +/*22*/ float hit_pitch; +/*26*/ uint8 type; // 231 (0xE7) for spells, skill +/*27*/ uint32 damage; // OSX says min_damage +/*31*/ uint16 unknown31; // OSX says tohit /*33*/ uint32 spell; // spell id being cast -/*37*/ uint8 level2; // level of caster again? Or maybe the castee +/*37*/ uint8 spell_level; // level of caster again? Or maybe the castee /*38*/ uint8 effect_flag; // if this is 4, the effect is valid: or if two are sent at the same time? /*39*/ }; @@ -1484,25 +1484,21 @@ struct ActionAlt_Struct { /*00*/ uint16 target; // id of target /*02*/ uint16 source; // id of caster -/*04*/ uint16 level; // level of caster - Seen 0 -/*06*/ uint32 unknown06; +/*04*/ uint16 level; // level of caster for spells, OSX dump says attack rating, guess spells use it for level +/*06*/ uint32 unknown06; // OSX dump says base_damage, was used for bard mod too, this is 0'd :( /*10*/ float instrument_mod; -/*14*/ uint32 bard_focus_id; // seen 0 -/*18*/ float knockback_angle; //seems to go from 0-512 then it rolls over again -/*22*/ uint32 unknown22; -/*26*/ uint8 type; -/*27*/ uint32 damage; -/*31*/ uint16 unknown31; +/*14*/ float force; +/*18*/ float hit_heading; +/*22*/ float hit_pitch; +/*26*/ uint8 type; // 231 (0xE7) for spells, skill +/*27*/ uint32 damage; // OSX says min_damage +/*31*/ uint16 unknown31; // OSX says tohit /*33*/ uint32 spell; // spell id being cast -/*37*/ uint8 level2; // level of caster again? Or maybe the castee +/*37*/ uint8 spell_level; // level of caster again? Or maybe the castee /*38*/ uint8 effect_flag; // if this is 4, the effect is valid: or if two are sent at the same time? -/*39*/ uint32 unknown39; // New field to Underfoot - Seen 14 -/*43*/ uint8 unknown43; // New field to Underfoot - Seen 0 -/*44*/ uint8 unknown44; // New field to Underfoot - Seen 17 -/*45*/ uint8 unknown45; // New field to Underfoot - Seen 0 -/*46*/ int32 unknown46; // New field to Underfoot - Seen -1 -/*50*/ uint32 unknown50; // New field to Underfoot - Seen 0 -/*54*/ uint16 unknown54; // New field to Underfoot - Seen 0 +/*39*/ uint8 spell_gem; +/*40*/ InventorySlot_Struct slot; +/*52*/ uint32 item_cast_type; // ItemSpellTypes enum from MQ2 /*56*/ }; @@ -1517,9 +1513,9 @@ struct CombatDamage_Struct /* 05 */ uint32 spellid; /* 09 */ int32 damage; /* 13 */ float force; // cd cc cc 3d -/* 17 */ float meleepush_xy; // see above notes in Action_Struct -/* 21 */ float meleepush_z; -/* 25 */ uint8 unknown25; // was [9] +/* 17 */ float hit_heading; // see above notes in Action_Struct +/* 21 */ float hit_pitch; +/* 25 */ uint8 secondary; // 0 for primary hand, 1 for secondary /* 26 */ uint32 special; // 2 = Rampage, 1 = Wild Rampage /* 30 */ }; @@ -3605,21 +3601,6 @@ struct GuildSetRank_Struct /*80*/ }; -struct BugStruct{ -/*0000*/ char chartype[64]; -/*0064*/ char name[96]; -/*0160*/ char ui[128]; -/*0288*/ float x; -/*0292*/ float y; -/*0296*/ float z; -/*0300*/ float heading; -/*0304*/ uint32 unknown304; -/*0308*/ uint32 type; -/*0312*/ char unknown312[2144]; -/*2456*/ char bug[1024]; -/*3480*/ char placeholder[2]; -/*3482*/ char system_info[4098]; -}; struct Make_Pet_Struct { //Simple struct for getting pet info uint8 level; uint8 class_; @@ -3646,20 +3627,21 @@ struct Ground_Spawn{ struct Ground_Spawns { struct Ground_Spawn spawn[50]; //Assigned max number to allow }; -struct PetitionBug_Struct{ - uint32 petition_number; - uint32 unknown4; - char accountname[64]; - uint32 zoneid; - char name[64]; - uint32 level; - uint32 class_; - uint32 race; - uint32 unknown152[3]; - uint32 time; - uint32 unknown168; - char text[1028]; -}; + +//struct PetitionBug_Struct{ +// uint32 petition_number; +// uint32 unknown4; +// char accountname[64]; +// uint32 zoneid; +// char name[64]; +// uint32 level; +// uint32 class_; +// uint32 race; +// uint32 unknown152[3]; +// uint32 time; +// uint32 unknown168; +// char text[1028]; +//}; struct ApproveZone_Struct { char name[64]; @@ -5103,6 +5085,23 @@ struct CrystalCountUpdate_Struct /*012*/ uint32 CareerEbonCrystals; }; +struct SayLinkBodyFrame_Struct { +/*000*/ char ActionID[1]; +/*001*/ char ItemID[5]; +/*006*/ char Augment1[5]; +/*011*/ char Augment2[5]; +/*016*/ char Augment3[5]; +/*021*/ char Augment4[5]; +/*026*/ char Augment5[5]; +/*031*/ char Augment6[5]; +/*036*/ char IsEvolving[1]; +/*037*/ char EvolveGroup[4]; +/*041*/ char EvolveLevel[2]; +/*043*/ char OrnamentIcon[5]; +/*048*/ char Hash[8]; +/*056*/ +}; + }; /*structs*/ }; /*RoF2*/ diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index 8de4fb9ab..08e3647be 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -408,7 +408,7 @@ struct Spawn_Struct /*0000*/ //uint8 nullterm1; // hack to null terminate name /*0064*/ uint32 spawnId; /*0068*/ uint8 level; -/*0069*/ float unknown1; +/*0069*/ float bounding_radius; // used in melee, overrides calc /*0073*/ uint8 NPC; // 0=player,1=npc,2=pc corpse,3=npc corpse Spawn_Struct_Bitfields Bitfields; /*0000*/ uint8 otherData; // & 4 - has title, & 8 - has suffix, & 1 - it's a chest or untargetable @@ -1450,17 +1450,17 @@ struct Action_Struct { /*00*/ uint16 target; // id of target /*02*/ uint16 source; // id of caster -/*04*/ uint16 level; // level of caster - Seen 0 -/*06*/ uint32 unknown06; +/*04*/ uint16 level; // level of caster for spells, OSX dump says attack rating, guess spells use it for level +/*06*/ uint32 unknown06; // OSX dump says base_damage, was used for bard mod too, this is 0'd :( /*10*/ float instrument_mod; -/*14*/ uint32 bard_focus_id; // seen 0 -/*18*/ float knockback_angle; //seems to go from 0-512 then it rolls over again -/*22*/ uint32 unknown22; -/*26*/ uint8 type; -/*27*/ uint32 damage; -/*31*/ uint16 unknown31; +/*14*/ float force; +/*18*/ float hit_heading; +/*22*/ float hit_pitch; +/*26*/ uint8 type; // 231 (0xE7) for spells, skill +/*27*/ uint32 damage; // OSX says min_damage +/*31*/ uint16 unknown31; // OSX says tohit /*33*/ uint32 spell; // spell id being cast -/*37*/ uint8 level2; // level of caster again? Or maybe the castee +/*37*/ uint8 spell_level; // level of caster again? Or maybe the castee /*38*/ uint8 effect_flag; // if this is 4, the effect is valid: or if two are sent at the same time? /*39*/ }; @@ -1472,25 +1472,21 @@ struct ActionAlt_Struct { /*00*/ uint16 target; // id of target /*02*/ uint16 source; // id of caster -/*04*/ uint16 level; // level of caster - Seen 0 -/*06*/ uint32 unknown06; +/*04*/ uint16 level; // level of caster for spells, OSX dump says attack rating, guess spells use it for level +/*06*/ uint32 unknown06; // OSX dump says base_damage, was used for bard mod too, this is 0'd :( /*10*/ float instrument_mod; -/*14*/ uint32 bard_focus_id; // seen 0 -/*18*/ float knockback_angle; //seems to go from 0-512 then it rolls over again -/*22*/ uint32 unknown22; -/*26*/ uint8 type; -/*27*/ uint32 damage; -/*31*/ uint16 unknown31; +/*14*/ float force; +/*18*/ float hit_heading; +/*22*/ float hit_pitch; +/*26*/ uint8 type; // 231 (0xE7) for spells, skill +/*27*/ uint32 damage; // OSX says min_damage +/*31*/ uint16 unknown31; // OSX says tohit /*33*/ uint32 spell; // spell id being cast -/*37*/ uint8 level2; // level of caster again? Or maybe the castee +/*37*/ uint8 spell_level; // level of caster again? Or maybe the castee /*38*/ uint8 effect_flag; // if this is 4, the effect is valid: or if two are sent at the same time? -/*39*/ uint32 unknown39; // New field to Underfoot - Seen 14 -/*43*/ uint8 unknown43; // New field to Underfoot - Seen 0 -/*44*/ uint8 unknown44; // New field to Underfoot - Seen 17 -/*45*/ uint8 unknown45; // New field to Underfoot - Seen 0 -/*46*/ int32 unknown46; // New field to Underfoot - Seen -1 -/*50*/ uint32 unknown50; // New field to Underfoot - Seen 0 -/*54*/ uint16 unknown54; // New field to Underfoot - Seen 0 +/*39*/ uint8 spell_gem; +/*40*/ InventorySlot_Struct slot; +/*52*/ uint32 item_cast_type; // ItemSpellTypes enum from MQ2 /*56*/ }; @@ -1505,9 +1501,9 @@ struct CombatDamage_Struct /* 05 */ uint32 spellid; /* 09 */ int32 damage; /* 13 */ float force; // cd cc cc 3d -/* 17 */ float meleepush_xy; // see above notes in Action_Struct -/* 21 */ float meleepush_z; -/* 25 */ uint8 unknown25; // was [9] +/* 17 */ float hit_heading; // see above notes in Action_Struct +/* 21 */ float hit_pitch; +/* 25 */ uint8 secondary; // 0 for primary hand, 1 for secondary /* 26 */ uint32 special; // 2 = Rampage, 1 = Wild Rampage /* 30 */ }; @@ -3545,21 +3541,6 @@ struct GuildSetRank_Struct /*80*/ }; -struct BugStruct{ -/*0000*/ char chartype[64]; -/*0064*/ char name[96]; -/*0160*/ char ui[128]; -/*0288*/ float x; -/*0292*/ float y; -/*0296*/ float z; -/*0300*/ float heading; -/*0304*/ uint32 unknown304; -/*0308*/ uint32 type; -/*0312*/ char unknown312[2144]; -/*2456*/ char bug[1024]; -/*3480*/ char placeholder[2]; -/*3482*/ char system_info[4098]; -}; struct Make_Pet_Struct { //Simple struct for getting pet info uint8 level; uint8 class_; @@ -3586,20 +3567,21 @@ struct Ground_Spawn{ struct Ground_Spawns { struct Ground_Spawn spawn[50]; //Assigned max number to allow }; -struct PetitionBug_Struct{ - uint32 petition_number; - uint32 unknown4; - char accountname[64]; - uint32 zoneid; - char name[64]; - uint32 level; - uint32 class_; - uint32 race; - uint32 unknown152[3]; - uint32 time; - uint32 unknown168; - char text[1028]; -}; + +//struct PetitionBug_Struct{ +// uint32 petition_number; +// uint32 unknown4; +// char accountname[64]; +// uint32 zoneid; +// char name[64]; +// uint32 level; +// uint32 class_; +// uint32 race; +// uint32 unknown152[3]; +// uint32 time; +// uint32 unknown168; +// char text[1028]; +//}; struct ApproveZone_Struct { char name[64]; @@ -5020,6 +5002,23 @@ struct MercenaryMerchantRequest_Struct { struct MercenaryMerchantResponse_Struct { /*0000*/ uint32 ResponseType; /*0004*/ +}; + +struct SayLinkBodyFrame_Struct { +/*000*/ char ActionID[1]; +/*001*/ char ItemID[5]; +/*006*/ char Augment1[5]; +/*011*/ char Augment2[5]; +/*016*/ char Augment3[5]; +/*021*/ char Augment4[5]; +/*026*/ char Augment5[5]; +/*031*/ char Augment6[5]; +/*036*/ char IsEvolving[1]; +/*037*/ char EvolveGroup[4]; +/*041*/ char EvolveLevel[1]; +/*042*/ char OrnamentIcon[5]; +/*047*/ char Hash[8]; +/*055*/ }; }; /*structs*/ diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index a77feedbc..560151bfa 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -53,11 +53,11 @@ namespace SoD static inline uint32 SoDToServerSlot(uint32 sodSlot); static inline uint32 SoDToServerCorpseSlot(uint32 sodCorpseSlot); - // server to client text link converter - static inline void ServerToSoDTextLink(std::string& sodTextLink, const std::string& serverTextLink); + // server to client say link converter + static inline void ServerToSoDSayLink(std::string& sodSayLink, const std::string& serverSayLink); - // client to server text link converter - static inline void SoDToServerTextLink(std::string& serverTextLink, const std::string& sodTextLink); + // client to server say link converter + static inline void SoDToServerSayLink(std::string& serverSayLink, const std::string& sodSayLink); static inline CastingSlot ServerToSoDCastingSlot(EQEmu::CastingSlot slot); static inline EQEmu::CastingSlot SoDToServerCastingSlot(CastingSlot slot); @@ -161,15 +161,14 @@ namespace SoD OUT(source); OUT(level); OUT(instrument_mod); - eq->sequence = emu->sequence; + OUT(force); + OUT(hit_heading); + OUT(hit_pitch); OUT(type); //OUT(damage); OUT(spell); - eq->level2 = emu->level; - OUT(buff_unknown); // if this is 4, a buff icon is made - //eq->unknown0036 = -1; - //eq->unknown0040 = -1; - //eq->unknown0044 = -1; + OUT(spell_level); + OUT(effect_flag); // if this is 4, a buff icon is made FINISH_ENCODE(); } @@ -346,7 +345,7 @@ namespace SoD std::string old_message = emu->message; std::string new_message; - ServerToSoDTextLink(new_message, old_message); + ServerToSoDSayLink(new_message, old_message); in->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; @@ -458,8 +457,8 @@ namespace SoD OUT(spellid); OUT(damage); OUT(force); - OUT(meleepush_xy); - OUT(meleepush_z); + OUT(hit_heading); + OUT(hit_pitch); OUT(special); FINISH_ENCODE(); @@ -625,7 +624,7 @@ namespace SoD std::string old_message = emu->message; std::string new_message; - ServerToSoDTextLink(new_message, old_message); + ServerToSoDSayLink(new_message, old_message); //if (new_message.length() > 512) // length restricted in packet building function due vari-length name size (no nullterm) // new_message = new_message.substr(0, 512); @@ -677,7 +676,7 @@ namespace SoD for (int i = 0; i < 9; ++i) { if (old_message_array[i].length() == 0) { break; } - ServerToSoDTextLink(new_message_array[i], old_message_array[i]); + ServerToSoDSayLink(new_message_array[i], old_message_array[i]); new_message_size += new_message_array[i].length() + 1; } @@ -2156,7 +2155,7 @@ namespace SoD std::string old_message = &emu->message[strlen(emu->sayer)]; std::string new_message; - ServerToSoDTextLink(new_message, old_message); + ServerToSoDSayLink(new_message, old_message); //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; in->size = strlen(emu->sayer) + new_message.length() + 25; @@ -2252,7 +2251,7 @@ namespace SoD std::string old_message = InBuffer; // start 'Reward' as string std::string new_message; - ServerToSoDTextLink(new_message, old_message); + ServerToSoDSayLink(new_message, old_message); in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+ sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_Struct)+ @@ -2933,25 +2932,6 @@ namespace SoD FINISH_DIRECT_DECODE(); } - DECODE(OP_Bug) - { - DECODE_LENGTH_EXACT(structs::BugStruct); - SETUP_DIRECT_DECODE(BugStruct, structs::BugStruct); - - strn0cpy(emu->chartype, eq->chartype, sizeof(emu->chartype)); - strn0cpy(emu->name, eq->name, sizeof(emu->name)); - strn0cpy(emu->ui, eq->ui, sizeof(emu->ui)); - IN(x); - IN(y); - IN(z); - IN(heading); - strn0cpy(emu->target_name, eq->target_name, sizeof(emu->target_name)); - strn0cpy(emu->bug, eq->bug, sizeof(emu->bug)); - strn0cpy(emu->system_info, eq->system_info, sizeof(emu->system_info)); - - FINISH_DIRECT_DECODE(); - } - DECODE(OP_CastSpell) { DECODE_LENGTH_EXACT(structs::CastSpell_Struct); @@ -2972,7 +2952,7 @@ namespace SoD std::string old_message = (char *)&__eq_buffer[sizeof(ChannelMessage_Struct)]; std::string new_message; - SoDToServerTextLink(new_message, old_message); + SoDToServerSayLink(new_message, old_message); __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; __packet->pBuffer = new unsigned char[__packet->size]; @@ -3086,7 +3066,7 @@ namespace SoD std::string old_message = (char *)&__eq_buffer[4]; // unknown01 offset std::string new_message; - SoDToServerTextLink(new_message, old_message); + SoDToServerSayLink(new_message, old_message); __packet->size = sizeof(Emote_Struct); __packet->pBuffer = new unsigned char[__packet->size]; @@ -3936,19 +3916,19 @@ namespace SoD return (sodCorpseSlot - 1); } - static inline void ServerToSoDTextLink(std::string& sodTextLink, const std::string& serverTextLink) + static inline void ServerToSoDSayLink(std::string& sodSayLink, const std::string& serverSayLink) { - if ((constants::SayLinkBodySize == EQEmu::legacy::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find('\x12') == std::string::npos)) { - sodTextLink = serverTextLink; + if ((constants::SayLinkBodySize == EQEmu::constants::SayLinkBodySize) || (serverSayLink.find('\x12') == std::string::npos)) { + sodSayLink = serverSayLink; return; } - auto segments = SplitString(serverTextLink, '\x12'); + auto segments = SplitString(serverSayLink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { - if (segments[segment_iter].length() <= EQEmu::legacy::TEXT_LINK_BODY_LENGTH) { - sodTextLink.append(segments[segment_iter]); + if (segments[segment_iter].length() <= EQEmu::constants::SayLinkBodySize) { + sodSayLink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -3958,37 +3938,37 @@ namespace SoD // SoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (50) // Diff: ^^^^^ ^ - sodTextLink.push_back('\x12'); - sodTextLink.append(segments[segment_iter].substr(0, 31)); - sodTextLink.append(segments[segment_iter].substr(36, 5)); + sodSayLink.push_back('\x12'); + sodSayLink.append(segments[segment_iter].substr(0, 31)); + sodSayLink.append(segments[segment_iter].substr(36, 5)); if (segments[segment_iter][41] == '0') - sodTextLink.push_back(segments[segment_iter][42]); + sodSayLink.push_back(segments[segment_iter][42]); else - sodTextLink.push_back('F'); + sodSayLink.push_back('F'); - sodTextLink.append(segments[segment_iter].substr(43)); - sodTextLink.push_back('\x12'); + sodSayLink.append(segments[segment_iter].substr(43)); + sodSayLink.push_back('\x12'); } else { - sodTextLink.append(segments[segment_iter]); + sodSayLink.append(segments[segment_iter]); } } } - static inline void SoDToServerTextLink(std::string& serverTextLink, const std::string& sodTextLink) + static inline void SoDToServerSayLink(std::string& serverSayLink, const std::string& sodSayLink) { - if ((EQEmu::legacy::TEXT_LINK_BODY_LENGTH == constants::SayLinkBodySize) || (sodTextLink.find('\x12') == std::string::npos)) { - serverTextLink = sodTextLink; + if ((EQEmu::constants::SayLinkBodySize == constants::SayLinkBodySize) || (sodSayLink.find('\x12') == std::string::npos)) { + serverSayLink = sodSayLink; return; } - auto segments = SplitString(sodTextLink, '\x12'); + auto segments = SplitString(sodSayLink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { if (segments[segment_iter].length() <= constants::SayLinkBodySize) { - serverTextLink.append(segments[segment_iter]); + serverSayLink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -3998,16 +3978,16 @@ namespace SoD // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) // Diff: ^^^^^ ^ - serverTextLink.push_back('\x12'); - serverTextLink.append(segments[segment_iter].substr(0, 31)); - serverTextLink.append("00000"); - serverTextLink.append(segments[segment_iter].substr(31, 5)); - serverTextLink.push_back('0'); - serverTextLink.append(segments[segment_iter].substr(36)); - serverTextLink.push_back('\x12'); + serverSayLink.push_back('\x12'); + serverSayLink.append(segments[segment_iter].substr(0, 31)); + serverSayLink.append("00000"); + serverSayLink.append(segments[segment_iter].substr(31, 5)); + serverSayLink.push_back('0'); + serverSayLink.append(segments[segment_iter].substr(36)); + serverSayLink.push_back('\x12'); } else { - serverTextLink.append(segments[segment_iter]); + serverSayLink.append(segments[segment_iter]); } } } diff --git a/common/patches/sod_ops.h b/common/patches/sod_ops.h index 90c28b2eb..7c1109499 100644 --- a/common/patches/sod_ops.h +++ b/common/patches/sod_ops.h @@ -104,7 +104,6 @@ D(OP_AugmentInfo) D(OP_AugmentItem) D(OP_BazaarSearch) D(OP_Buff) -D(OP_Bug) D(OP_CastSpell) D(OP_ChannelMessage) D(OP_CharacterCreate) diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 88a78c6ee..6b5c2f72c 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -292,7 +292,7 @@ struct Spawn_Struct /*0000*/ //uint8 nullterm1; // hack to null terminate name /*0064*/ uint32 spawnId; /*0068*/ uint8 level; -/*0069*/ float unknown1; +/*0069*/ float bounding_radius; // used in melee, overrides calc /*0073*/ uint8 NPC; // 0=player,1=npc,2=pc corpse,3=npc corpse Spawn_Struct_Bitfields Bitfields; /*0000*/ uint8 otherData; // & 4 - has title, & 8 - has suffix, & 1 - it's a chest or untargetable @@ -1215,20 +1215,18 @@ struct Action_Struct { /* 00 */ uint16 target; // id of target /* 02 */ uint16 source; // id of caster - /* 04 */ uint16 level; // level of caster - /* 06 */ uint16 instrument_mod; // seems to be fixed to 0x0A - /* 08 */ uint32 unknown08; - /* 12 */ uint16 unknown16; -// some kind of sequence that's the same in both actions -// as well as the combat damage, to tie em together? - /* 14 */ float sequence; // was uint32 - /* 18 */ uint32 unknown18; - /* 22 */ uint8 type; // 231 (0xE7) for spells - /* 23 */ uint32 unknown23; + /* 04 */ uint16 level; // level of caster for spells, OSX dump says attack rating, guess spells use it for level + /* 06 */ uint32 instrument_mod; // OSX dump says base damage, spells use it for bard song (different from newer clients) + /* 10 */ float force; + /* 14 */ float hit_heading; + /* 18 */ float hit_pitch; + /* 22 */ uint8 type; // 231 (0xE7) for spells, skill + /* 23 */ uint16 unknown23; // OSX says min_damage + /* 25 */ uint16 unknown25; // OSX says tohit /* 27 */ uint16 spell; // spell id being cast - /* 29 */ uint8 level2; // level of caster again? Or maybe the castee + /* 29 */ uint8 spell_level; // level of caster again? Or maybe the castee // this field seems to be some sort of success flag, if it's 4 - /* 30 */ uint8 buff_unknown; // if this is 4, a buff icon is made + /* 30 */ uint8 effect_flag; // if this is 4, a buff icon is made /* 31 */ }; @@ -1237,26 +1235,23 @@ struct Action_Struct // has to do with buff blocking?? struct ActionAlt_Struct // ActionAlt_Struct - Size: 56 bytes { -/*0000*/ uint16 target; // Target ID -/*0002*/ uint16 source; // SourceID -/*0004*/ uint16 level; // level of caster -/*0006*/ uint16 instrument_mod; // seems to be fixed to 0x0A -/*0008*/ uint32 unknown08; -/*0012*/ uint16 unknown16; -/*0014*/ uint32 sequence; -/*0018*/ uint32 unknown18; -/*0022*/ uint8 type; // Casts, Falls, Bashes, etc... -/*0023*/ uint32 damage; // Amount of Damage -/*0027*/ uint16 spell; // SpellID -/*0029*/ uint8 unknown29; -/*0030*/ uint8 buff_unknown; // if this is 4, a buff icon is made -/*0031*/ uint32 unknown0031; // seen 00 00 00 00 -/*0035*/ uint8 unknown0035; // seen 00 -/*0036*/ uint32 unknown0036; // seen ff ff ff ff -/*0040*/ uint32 unknown0040; // seen ff ff ff ff -/*0044*/ uint32 unknown0044; // seen ff ff ff ff -/*0048*/ uint32 unknown0048; // seen 00 00 00 00 -/*0052*/ uint32 unknown0052; // seen 00 00 00 00 +/*0000*/ uint16 target; // id of target +/*0002*/ uint16 source; // id of caster +/*0004*/ uint16 level; // level of caster for spells, OSX dump says attack rating, guess spells use it for level +/*0006*/ uint32 instrument_mod; // OSX dump says base damage, spells use it for bard song (different from newer clients) +/*0010*/ float force; +/*0014*/ float hit_heading; +/*0018*/ float hit_pitch; +/*0022*/ uint8 type; // 231 (0xE7) for spells, skill +/*0023*/ uint16 unknown23; // OSX says min_damage +/*0025*/ uint16 unknown25; // OSX says tohit +/*0027*/ uint16 spell; // spell id being cast +/*0029*/ uint8 spell_level; // level of caster again? Or maybe the castee +// this field seems to be some sort of success flag, if it's 4 +/*0030*/ uint8 effect_flag; // if this is 4, a buff icon is made +/*0031*/ uint8 spell_slot; +/*0032*/ uint32 slot[5]; +/*0052*/ uint32 item_cast_type; // ItemSpellTypes enum from MQ2 /*0056*/ }; @@ -1271,9 +1266,9 @@ struct CombatDamage_Struct /* 05 */ uint16 spellid; /* 07 */ int32 damage; /* 11 */ float force; // cd cc cc 3d -/* 15 */ float meleepush_xy; // see above notes in Action_Struct -/* 19 */ float meleepush_z; -/* 23 */ uint8 unknown23; // was [9] +/* 15 */ float hit_heading; // see above notes in Action_Struct +/* 19 */ float hit_pitch; +/* 23 */ uint8 secondary; // 0 for primary hand, 1 for secondary /* 24 */ uint32 special; // 2 = Rampage, 1 = Wild Rampage /* 28 */ }; @@ -3008,24 +3003,6 @@ struct GuildMakeLeader{ char target[64]; }; -struct BugStruct{ -/*0000*/ uint32 type1; //seems to be just a different way of seeing type; seems to be ordered completely differently -/*0004*/ char chartype[64]; -/*0068*/ char name[96]; -/*0164*/ char ui[128]; -/*0292*/ float x; -/*0296*/ float y; -/*0300*/ float z; -/*0304*/ float heading; -/*0308*/ uint32 unknown304; -/*0312*/ char unknown308[160]; -/*0472*/ char target_name[64]; -/*0536*/ uint32 type; -/*0540*/ char unknown536[2052]; -/*2588*/ char bug[2048]; -/*4636*/ char unknown4632[6]; -/*4642*/ char system_info[4094]; -}; struct Make_Pet_Struct { //Simple struct for getting pet info uint8 level; uint8 class_; @@ -3052,20 +3029,21 @@ struct Ground_Spawn{ struct Ground_Spawns { struct Ground_Spawn spawn[50]; //Assigned max number to allow }; -struct PetitionBug_Struct{ - uint32 petition_number; - uint32 unknown4; - char accountname[64]; - uint32 zoneid; - char name[64]; - uint32 level; - uint32 class_; - uint32 race; - uint32 unknown152[3]; - uint32 time; - uint32 unknown168; - char text[1028]; -}; + +//struct PetitionBug_Struct{ +// uint32 petition_number; +// uint32 unknown4; +// char accountname[64]; +// uint32 zoneid; +// char name[64]; +// uint32 level; +// uint32 class_; +// uint32 race; +// uint32 unknown152[3]; +// uint32 time; +// uint32 unknown168; +// char text[1028]; +//}; struct ApproveZone_Struct { char name[64]; @@ -4402,6 +4380,22 @@ struct MercenaryAssign_Struct { /*0004*/ uint32 MercUnk01; // /*0008*/ uint32 MercUnk02; // /*0012*/ +}; + +struct SayLinkBodyFrame_Struct { +/*000*/ char ActionID[1]; +/*001*/ char ItemID[5]; +/*006*/ char Augment1[5]; +/*011*/ char Augment2[5]; +/*016*/ char Augment3[5]; +/*021*/ char Augment4[5]; +/*026*/ char Augment5[5]; +/*031*/ char IsEvolving[1]; +/*032*/ char EvolveGroup[4]; +/*036*/ char EvolveLevel[1]; +/*037*/ char OrnamentIcon[5]; +/*042*/ char Hash[8]; +/*050*/ }; }; /*structs*/ diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index 9df6ca1df..c16bed086 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -53,11 +53,11 @@ namespace SoF static inline uint32 SoFToServerSlot(uint32 sofSlot); static inline uint32 SoFToServerCorpseSlot(uint32 sofCorpseSlot); - // server to client text link converter - static inline void ServerToSoFTextLink(std::string& sofTextLink, const std::string& serverTextLink); + // server to client say link converter + static inline void ServerToSoFSayLink(std::string& sofSayLink, const std::string& serverSayLink); - // client to server text link converter - static inline void SoFToServerTextLink(std::string& serverTextLink, const std::string& sofTextLink); + // client to server say link converter + static inline void SoFToServerSayLink(std::string& serverSayLink, const std::string& sofSayLink); static inline CastingSlot ServerToSoFCastingSlot(EQEmu::CastingSlot slot); static inline EQEmu::CastingSlot SoFToServerCastingSlot(CastingSlot slot, uint32 itemlocation); @@ -161,15 +161,14 @@ namespace SoF OUT(source); OUT(level); OUT(instrument_mod); - eq->sequence = emu->sequence; + OUT(force); + OUT(hit_heading); + OUT(hit_pitch); OUT(type); //OUT(damage); OUT(spell); - eq->level2 = emu->level; - OUT(buff_unknown); // if this is 4, a buff icon is made - //eq->unknown0036 = -1; - //eq->unknown0040 = -1; - //eq->unknown0044 = -1; + OUT(spell_level); + OUT(effect_flag); // if this is 4, a buff icon is made FINISH_ENCODE(); } @@ -328,7 +327,7 @@ namespace SoF std::string old_message = emu->message; std::string new_message; - ServerToSoFTextLink(new_message, old_message); + ServerToSoFSayLink(new_message, old_message); in->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; @@ -440,8 +439,8 @@ namespace SoF OUT(spellid); OUT(damage); OUT(force); - OUT(meleepush_xy); - OUT(meleepush_z); + OUT(hit_heading); + OUT(hit_pitch); FINISH_ENCODE(); } @@ -613,7 +612,7 @@ namespace SoF std::string old_message = emu->message; std::string new_message; - ServerToSoFTextLink(new_message, old_message); + ServerToSoFSayLink(new_message, old_message); //if (new_message.length() > 512) // length restricted in packet building function due vari-length name size (no nullterm) // new_message = new_message.substr(0, 512); @@ -665,7 +664,7 @@ namespace SoF for (int i = 0; i < 9; ++i) { if (old_message_array[i].length() == 0) { break; } - ServerToSoFTextLink(new_message_array[i], old_message_array[i]); + ServerToSoFSayLink(new_message_array[i], old_message_array[i]); new_message_size += new_message_array[i].length() + 1; } @@ -1814,7 +1813,7 @@ namespace SoF std::string old_message = &emu->message[strlen(emu->sayer)]; std::string new_message; - ServerToSoFTextLink(new_message, old_message); + ServerToSoFSayLink(new_message, old_message); //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; in->size = strlen(emu->sayer) + new_message.length() + 25; @@ -1882,7 +1881,7 @@ namespace SoF std::string old_message = InBuffer; // start 'Reward' as string std::string new_message; - ServerToSoFTextLink(new_message, old_message); + ServerToSoFSayLink(new_message, old_message); in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+ sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_Struct)+ @@ -2385,6 +2384,17 @@ namespace SoF FINISH_DIRECT_DECODE(); } + DECODE(OP_Bug) + { + DECODE_LENGTH_EXACT(structs::BugReport_Struct); + SETUP_DIRECT_DECODE(BugReport_Struct, structs::BugReport_Struct); + + emu->category_id = EQEmu::bug::CategoryNameToCategoryID(eq->category_name); + memcpy(emu->category_name, eq, sizeof(structs::BugReport_Struct)); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_CastSpell) { DECODE_LENGTH_EXACT(structs::CastSpell_Struct); @@ -2404,7 +2414,7 @@ namespace SoF std::string old_message = (char *)&__eq_buffer[sizeof(ChannelMessage_Struct)]; std::string new_message; - SoFToServerTextLink(new_message, old_message); + SoFToServerSayLink(new_message, old_message); __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; __packet->pBuffer = new unsigned char[__packet->size]; @@ -2518,7 +2528,7 @@ namespace SoF std::string old_message = (char *)&__eq_buffer[4]; // unknown01 offset std::string new_message; - SoFToServerTextLink(new_message, old_message); + SoFToServerSayLink(new_message, old_message); __packet->size = sizeof(Emote_Struct); __packet->pBuffer = new unsigned char[__packet->size]; @@ -3305,19 +3315,19 @@ namespace SoF return (sofCorpseSlot - 1); } - static inline void ServerToSoFTextLink(std::string& sofTextLink, const std::string& serverTextLink) + static inline void ServerToSoFSayLink(std::string& sofSayLink, const std::string& serverSayLink) { - if ((constants::SayLinkBodySize == EQEmu::legacy::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find('\x12') == std::string::npos)) { - sofTextLink = serverTextLink; + if ((constants::SayLinkBodySize == EQEmu::constants::SayLinkBodySize) || (serverSayLink.find('\x12') == std::string::npos)) { + sofSayLink = serverSayLink; return; } - auto segments = SplitString(serverTextLink, '\x12'); + auto segments = SplitString(serverSayLink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { - if (segments[segment_iter].length() <= EQEmu::legacy::TEXT_LINK_BODY_LENGTH) { - sofTextLink.append(segments[segment_iter]); + if (segments[segment_iter].length() <= EQEmu::constants::SayLinkBodySize) { + sofSayLink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -3327,37 +3337,37 @@ namespace SoF // SoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (50) // Diff: ^^^^^ ^ - sofTextLink.push_back('\x12'); - sofTextLink.append(segments[segment_iter].substr(0, 31)); - sofTextLink.append(segments[segment_iter].substr(36, 5)); + sofSayLink.push_back('\x12'); + sofSayLink.append(segments[segment_iter].substr(0, 31)); + sofSayLink.append(segments[segment_iter].substr(36, 5)); if (segments[segment_iter][41] == '0') - sofTextLink.push_back(segments[segment_iter][42]); + sofSayLink.push_back(segments[segment_iter][42]); else - sofTextLink.push_back('F'); + sofSayLink.push_back('F'); - sofTextLink.append(segments[segment_iter].substr(43)); - sofTextLink.push_back('\x12'); + sofSayLink.append(segments[segment_iter].substr(43)); + sofSayLink.push_back('\x12'); } else { - sofTextLink.append(segments[segment_iter]); + sofSayLink.append(segments[segment_iter]); } } } - static inline void SoFToServerTextLink(std::string& serverTextLink, const std::string& sofTextLink) + static inline void SoFToServerSayLink(std::string& serverSayLink, const std::string& sofSayLink) { - if ((EQEmu::legacy::TEXT_LINK_BODY_LENGTH == constants::SayLinkBodySize) || (sofTextLink.find('\x12') == std::string::npos)) { - serverTextLink = sofTextLink; + if ((EQEmu::constants::SayLinkBodySize == constants::SayLinkBodySize) || (sofSayLink.find('\x12') == std::string::npos)) { + serverSayLink = sofSayLink; return; } - auto segments = SplitString(sofTextLink, '\x12'); + auto segments = SplitString(sofSayLink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { if (segments[segment_iter].length() <= constants::SayLinkBodySize) { - serverTextLink.append(segments[segment_iter]); + serverSayLink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -3367,16 +3377,16 @@ namespace SoF // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) // Diff: ^^^^^ ^ - serverTextLink.push_back('\x12'); - serverTextLink.append(segments[segment_iter].substr(0, 31)); - serverTextLink.append("00000"); - serverTextLink.append(segments[segment_iter].substr(31, 5)); - serverTextLink.push_back('0'); - serverTextLink.append(segments[segment_iter].substr(36)); - serverTextLink.push_back('\x12'); + serverSayLink.push_back('\x12'); + serverSayLink.append(segments[segment_iter].substr(0, 31)); + serverSayLink.append("00000"); + serverSayLink.append(segments[segment_iter].substr(31, 5)); + serverSayLink.push_back('0'); + serverSayLink.append(segments[segment_iter].substr(36)); + serverSayLink.push_back('\x12'); } else { - serverTextLink.append(segments[segment_iter]); + serverSayLink.append(segments[segment_iter]); } } } diff --git a/common/patches/sof_ops.h b/common/patches/sof_ops.h index 180857388..c4e42d8d4 100644 --- a/common/patches/sof_ops.h +++ b/common/patches/sof_ops.h @@ -95,6 +95,7 @@ D(OP_ApplyPoison) D(OP_AugmentInfo) D(OP_AugmentItem) D(OP_Buff) +D(OP_Bug) D(OP_CastSpell) D(OP_ChannelMessage) D(OP_CharacterCreate) diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index 9d87bd83a..29272a7a6 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -326,7 +326,8 @@ union /*0725*/ uint8 targetable; // 1 = Targetable 0 = Not Targetable (is_npc?) /*0726*/ uint8 unknown0726[4]; /*0730*/ uint8 NPC; // 0=player,1=npc,2=pc corpse,3=npc corpse -/*0731*/ uint8 unknown0731[11]; +/*0731*/ float bounding_radius; // used in melee, overrides calc +/*0735*/ uint8 unknown0731[7]; /*0742*/ uint8 targetable_with_hotkey; /*0743*/ signed padding00:12; // ***Placeholder signed x:19; // x coord @@ -1214,20 +1215,18 @@ struct Action_Struct { /* 00 */ uint16 target; // id of target /* 02 */ uint16 source; // id of caster - /* 04 */ uint16 level; // level of caster - /* 06 */ uint16 instrument_mod; // seems to be fixed to 0x0A - /* 08 */ uint32 unknown08; - /* 12 */ uint16 unknown16; -// some kind of sequence that's the same in both actions -// as well as the combat damage, to tie em together? - /* 14 */ float sequence; // was uint32 - /* 18 */ uint32 unknown18; - /* 22 */ uint8 type; // 231 (0xE7) for spells - /* 23 */ uint32 unknown23; + /* 04 */ uint16 level; // level of caster for spells, OSX dump says attack rating, guess spells use it for level + /* 06 */ uint32 instrument_mod; // OSX dump says base damage, spells use it for bard song (different from newer clients) + /* 10 */ float force; + /* 14 */ float hit_heading; + /* 18 */ float hit_pitch; + /* 22 */ uint8 type; // 231 (0xE7) for spells, skill + /* 23 */ uint16 unknown23; // OSX says min_damage + /* 25 */ uint16 unknown25; // OSX says tohit /* 27 */ uint16 spell; // spell id being cast - /* 29 */ uint8 level2; // level of caster again? Or maybe the castee + /* 29 */ uint8 spell_level; // level of caster again? Or maybe the castee // this field seems to be some sort of success flag, if it's 4 - /* 30 */ uint8 buff_unknown; // if this is 4, a buff icon is made + /* 30 */ uint8 effect_flag; // if this is 4, a buff icon is made /* 31 */ }; @@ -1236,26 +1235,23 @@ struct Action_Struct // has to do with buff blocking?? struct ActionAlt_Struct // ActionAlt_Struct - Size: 56 bytes { -/*0000*/ uint16 target; // Target ID -/*0002*/ uint16 source; // SourceID -/*0004*/ uint16 level; // level of caster -/*0006*/ uint16 instrument_mod; // seems to be fixed to 0x0A -/*0008*/ uint32 unknown08; -/*0012*/ uint16 unknown16; -/*0014*/ uint32 sequence; -/*0018*/ uint32 unknown18; -/*0022*/ uint8 type; // Casts, Falls, Bashes, etc... -/*0023*/ uint32 damage; // Amount of Damage -/*0027*/ uint16 spell; // SpellID -/*0029*/ uint8 unknown29; -/*0030*/ uint8 buff_unknown; // if this is 4, a buff icon is made -/*0031*/ uint32 unknown0031; // seen 00 00 00 00 -/*0035*/ uint8 unknown0035; // seen 00 -/*0036*/ uint32 unknown0036; // seen ff ff ff ff -/*0040*/ uint32 unknown0040; // seen ff ff ff ff -/*0044*/ uint32 unknown0044; // seen ff ff ff ff -/*0048*/ uint32 unknown0048; // seen 00 00 00 00 -/*0052*/ uint32 unknown0052; // seen 00 00 00 00 +/*0000*/ uint16 target; // id of target +/*0002*/ uint16 source; // id of caster +/*0004*/ uint16 level; // level of caster for spells, OSX dump says attack rating, guess spells use it for level +/*0006*/ uint32 instrument_mod; // OSX dump says base damage, spells use it for bard song (different from newer clients) +/*0010*/ float force; +/*0014*/ float hit_heading; +/*0018*/ float hit_pitch; +/*0022*/ uint8 type; // 231 (0xE7) for spells, skill +/*0023*/ uint16 unknown23; // OSX says min_damage +/*0025*/ uint16 unknown25; // OSX says tohit +/*0027*/ uint16 spell; // spell id being cast +/*0029*/ uint8 spell_level; // level of caster again? Or maybe the castee +// this field seems to be some sort of success flag, if it's 4 +/*0030*/ uint8 effect_flag; // if this is 4, a buff icon is made +/*0031*/ uint8 spell_slot; +/*0032*/ uint32 slot[5]; +/*0052*/ uint32 item_cast_type; // ItemSpellTypes enum from MQ2 /*0056*/ }; @@ -1270,9 +1266,10 @@ struct CombatDamage_Struct /* 05 */ uint16 spellid; /* 07 */ int32 damage; /* 11 */ float force; // cd cc cc 3d -/* 15 */ float meleepush_xy; // see above notes in Action_Struct -/* 19 */ float meleepush_z; -/* 23 */ uint8 unknown23[5]; // was [9] this appears unrelated to the stuff the other clients do here? +/* 15 */ float hit_heading; // see above notes in Action_Struct +/* 19 */ float hit_pitch; +/* 23 */ uint8 secondary; // 0 for primary hand, 1 for secondary +/* 24 */ uint32 special; // 2 = Rampage, 1 = Wild Rampage, Report function doesn't seem to check this :P /* 28 */ }; @@ -2906,23 +2903,31 @@ struct GuildMakeLeader{ char target[64]; }; +struct BugReport_Struct { +/*0000*/ char category_name[64]; +/*0064*/ char character_name[64]; +/*0128*/ char unused_0128[32]; +/*0160*/ char ui_path[128]; +/*0288*/ float pos_x; +/*0292*/ float pos_y; +/*0296*/ float pos_z; +/*0300*/ uint32 heading; +/*0304*/ uint32 unused_0304; +/*0308*/ uint32 time_played; +/*0312*/ char padding_0312[8]; +/*0320*/ uint32 target_id; +/*0324*/ char padding_0324[140]; +/*0464*/ uint32 unknown_0464; // seems to always be '0' +/*0468*/ char target_name[64]; +/*0532*/ uint32 optional_info_mask; - -struct BugStruct{ -/*0000*/ char chartype[64]; -/*0064*/ char name[96]; -/*0160*/ char ui[128]; -/*0288*/ float x; -/*0292*/ float y; -/*0296*/ float z; -/*0300*/ float heading; -/*0304*/ uint32 unknown304; -/*0308*/ uint32 type; -/*0312*/ char unknown312[2144]; -/*2456*/ char bug[1024]; -/*3480*/ char placeholder[2]; -/*3482*/ char system_info[4098]; +// this looks like a butchered 8k buffer with 2 trailing dword fields +/*0536*/ char unused_0536[2052]; +/*2588*/ char bug_report[2050]; +/*4638*/ char system_info[4098]; +/*8736*/ }; + struct Make_Pet_Struct { //Simple struct for getting pet info uint8 level; uint8 class_; @@ -2949,20 +2954,21 @@ struct Ground_Spawn{ struct Ground_Spawns { struct Ground_Spawn spawn[50]; //Assigned max number to allow }; -struct PetitionBug_Struct{ - uint32 petition_number; - uint32 unknown4; - char accountname[64]; - uint32 zoneid; - char name[64]; - uint32 level; - uint32 class_; - uint32 race; - uint32 unknown152[3]; - uint32 time; - uint32 unknown168; - char text[1028]; -}; + +//struct PetitionBug_Struct{ +// uint32 petition_number; +// uint32 unknown4; +// char accountname[64]; +// uint32 zoneid; +// char name[64]; +// uint32 level; +// uint32 class_; +// uint32 race; +// uint32 unknown152[3]; +// uint32 time; +// uint32 unknown168; +// char text[1028]; +//}; struct ApproveZone_Struct { char name[64]; @@ -4146,6 +4152,22 @@ struct AltCurrencySellItem_Struct { /*004*/ uint32 slot_id; /*006*/ uint32 charges; /*010*/ uint32 cost; +}; + +struct SayLinkBodyFrame_Struct { +/*000*/ char ActionID[1]; +/*001*/ char ItemID[5]; +/*006*/ char Augment1[5]; +/*011*/ char Augment2[5]; +/*016*/ char Augment3[5]; +/*021*/ char Augment4[5]; +/*026*/ char Augment5[5]; +/*031*/ char IsEvolving[1]; +/*032*/ char EvolveGroup[4]; +/*036*/ char EvolveLevel[1]; +/*037*/ char OrnamentIcon[5]; +/*042*/ char Hash[8]; +/*050*/ }; }; /*structs*/ diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index 45f267223..bf3c3d359 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -52,11 +52,11 @@ namespace Titanium static inline uint32 TitaniumToServerSlot(int16 titaniumSlot); static inline uint32 TitaniumToServerCorpseSlot(int16 titaniumCorpseSlot); - // server to client text link converter - static inline void ServerToTitaniumTextLink(std::string& titaniumTextLink, const std::string& serverTextLink); + // server to client say link converter + static inline void ServerToTitaniumSayLink(std::string& titaniumSayLink, const std::string& serverSayLink); - // client to server text link converter - static inline void TitaniumToServerTextLink(std::string& serverTextLink, const std::string& titaniumTextLink); + // client to server say link converter + static inline void TitaniumToServerSayLink(std::string& serverSayLink, const std::string& titaniumSayLink); static inline CastingSlot ServerToTitaniumCastingSlot(EQEmu::CastingSlot slot); static inline EQEmu::CastingSlot TitaniumToServerCastingSlot(CastingSlot slot, uint32 itemlocation); @@ -164,11 +164,14 @@ namespace Titanium OUT(source); OUT(level); OUT(instrument_mod); - OUT(sequence); + OUT(force); + OUT(hit_heading); + OUT(hit_pitch); OUT(type); //OUT(damage); OUT(spell); - OUT(buff_unknown); // if this is 4, a buff icon is made + OUT(spell_level); + OUT(effect_flag); // if this is 4, a buff icon is made FINISH_ENCODE(); } @@ -290,7 +293,7 @@ namespace Titanium std::string old_message = emu->message; std::string new_message; - ServerToTitaniumTextLink(new_message, old_message); + ServerToTitaniumSayLink(new_message, old_message); in->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; @@ -358,8 +361,8 @@ namespace Titanium OUT(spellid); OUT(damage); OUT(force); - OUT(meleepush_xy); - OUT(meleepush_z); + OUT(hit_heading); + OUT(hit_pitch); FINISH_ENCODE(); } @@ -532,7 +535,7 @@ namespace Titanium std::string old_message = emu->message; std::string new_message; - ServerToTitaniumTextLink(new_message, old_message); + ServerToTitaniumSayLink(new_message, old_message); //if (new_message.length() > 512) // length restricted in packet building function due vari-length name size (no nullterm) // new_message = new_message.substr(0, 512); @@ -574,7 +577,7 @@ namespace Titanium for (int i = 0; i < 9; ++i) { if (old_message_array[i].length() == 0) { break; } - ServerToTitaniumTextLink(new_message_array[i], old_message_array[i]); + ServerToTitaniumSayLink(new_message_array[i], old_message_array[i]); new_message_size += new_message_array[i].length() + 1; } @@ -1402,7 +1405,7 @@ namespace Titanium std::string old_message = &emu->message[strlen(emu->sayer)]; std::string new_message; - ServerToTitaniumTextLink(new_message, old_message); + ServerToTitaniumSayLink(new_message, old_message); //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; in->size = strlen(emu->sayer) + new_message.length() + 25; @@ -1458,7 +1461,7 @@ namespace Titanium std::string old_message = InBuffer; // start 'Reward' as string std::string new_message; - ServerToTitaniumTextLink(new_message, old_message); + ServerToTitaniumSayLink(new_message, old_message); in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct) + sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_Struct) + @@ -1789,6 +1792,17 @@ namespace Titanium FINISH_DIRECT_DECODE(); } + DECODE(OP_Bug) + { + DECODE_LENGTH_EXACT(structs::BugReport_Struct); + SETUP_DIRECT_DECODE(BugReport_Struct, structs::BugReport_Struct); + + emu->category_id = EQEmu::bug::CategoryNameToCategoryID(eq->category_name); + memcpy(emu->category_name, eq, sizeof(structs::BugReport_Struct)); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_CastSpell) { DECODE_LENGTH_EXACT(structs::CastSpell_Struct); @@ -1808,7 +1822,7 @@ namespace Titanium std::string old_message = (char *)&__eq_buffer[sizeof(ChannelMessage_Struct)]; std::string new_message; - TitaniumToServerTextLink(new_message, old_message); + TitaniumToServerSayLink(new_message, old_message); __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; __packet->pBuffer = new unsigned char[__packet->size]; @@ -1880,7 +1894,7 @@ namespace Titanium std::string old_message = (char *)&__eq_buffer[4]; // unknown01 offset std::string new_message; - TitaniumToServerTextLink(new_message, old_message); + TitaniumToServerSayLink(new_message, old_message); __packet->size = sizeof(Emote_Struct); __packet->pBuffer = new unsigned char[__packet->size]; @@ -2474,19 +2488,19 @@ namespace Titanium return titaniumCorpseSlot; } - static inline void ServerToTitaniumTextLink(std::string& titaniumTextLink, const std::string& serverTextLink) + static inline void ServerToTitaniumSayLink(std::string& titaniumSayLink, const std::string& serverSayLink) { - if ((constants::SayLinkBodySize == EQEmu::legacy::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find('\x12') == std::string::npos)) { - titaniumTextLink = serverTextLink; + if ((constants::SayLinkBodySize == EQEmu::constants::SayLinkBodySize) || (serverSayLink.find('\x12') == std::string::npos)) { + titaniumSayLink = serverSayLink; return; } - auto segments = SplitString(serverTextLink, '\x12'); + auto segments = SplitString(serverSayLink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { - if (segments[segment_iter].length() <= EQEmu::legacy::TEXT_LINK_BODY_LENGTH) { - titaniumTextLink.append(segments[segment_iter]); + if (segments[segment_iter].length() <= EQEmu::constants::SayLinkBodySize) { + titaniumSayLink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -2496,37 +2510,37 @@ namespace Titanium // 6.2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXXXXX (45) // Diff: ^^^^^ ^ ^^^^^ - titaniumTextLink.push_back('\x12'); - titaniumTextLink.append(segments[segment_iter].substr(0, 31)); - titaniumTextLink.append(segments[segment_iter].substr(36, 5)); + titaniumSayLink.push_back('\x12'); + titaniumSayLink.append(segments[segment_iter].substr(0, 31)); + titaniumSayLink.append(segments[segment_iter].substr(36, 5)); if (segments[segment_iter][41] == '0') - titaniumTextLink.push_back(segments[segment_iter][42]); + titaniumSayLink.push_back(segments[segment_iter][42]); else - titaniumTextLink.push_back('F'); + titaniumSayLink.push_back('F'); - titaniumTextLink.append(segments[segment_iter].substr(48)); - titaniumTextLink.push_back('\x12'); + titaniumSayLink.append(segments[segment_iter].substr(48)); + titaniumSayLink.push_back('\x12'); } else { - titaniumTextLink.append(segments[segment_iter]); + titaniumSayLink.append(segments[segment_iter]); } } } - static inline void TitaniumToServerTextLink(std::string& serverTextLink, const std::string& titaniumTextLink) + static inline void TitaniumToServerSayLink(std::string& serverSayLink, const std::string& titaniumSayLink) { - if ((EQEmu::legacy::TEXT_LINK_BODY_LENGTH == constants::SayLinkBodySize) || (titaniumTextLink.find('\x12') == std::string::npos)) { - serverTextLink = titaniumTextLink; + if ((EQEmu::constants::SayLinkBodySize == constants::SayLinkBodySize) || (titaniumSayLink.find('\x12') == std::string::npos)) { + serverSayLink = titaniumSayLink; return; } - auto segments = SplitString(titaniumTextLink, '\x12'); + auto segments = SplitString(titaniumSayLink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { if (segments[segment_iter].length() <= constants::SayLinkBodySize) { - serverTextLink.append(segments[segment_iter]); + serverSayLink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -2536,18 +2550,18 @@ namespace Titanium // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) // Diff: ^^^^^ ^ ^^^^^ - serverTextLink.push_back('\x12'); - serverTextLink.append(segments[segment_iter].substr(0, 31)); - serverTextLink.append("00000"); - serverTextLink.append(segments[segment_iter].substr(31, 5)); - serverTextLink.push_back('0'); - serverTextLink.push_back(segments[segment_iter][36]); - serverTextLink.append("00000"); - serverTextLink.append(segments[segment_iter].substr(37)); - serverTextLink.push_back('\x12'); + serverSayLink.push_back('\x12'); + serverSayLink.append(segments[segment_iter].substr(0, 31)); + serverSayLink.append("00000"); + serverSayLink.append(segments[segment_iter].substr(31, 5)); + serverSayLink.push_back('0'); + serverSayLink.push_back(segments[segment_iter][36]); + serverSayLink.append("00000"); + serverSayLink.append(segments[segment_iter].substr(37)); + serverSayLink.push_back('\x12'); } else { - serverTextLink.append(segments[segment_iter]); + serverSayLink.append(segments[segment_iter]); } } } diff --git a/common/patches/titanium_ops.h b/common/patches/titanium_ops.h index 9f77a7077..dafb8b903 100644 --- a/common/patches/titanium_ops.h +++ b/common/patches/titanium_ops.h @@ -78,6 +78,7 @@ D(OP_AdventureMerchantSell) D(OP_ApplyPoison) D(OP_AugmentItem) D(OP_Buff) +D(OP_Bug) D(OP_CastSpell) D(OP_ChannelMessage) D(OP_CharacterCreate) diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index e52b7628c..9f2d9423d 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -327,7 +327,7 @@ union // horse: 0=brown, 1=white, 2=black, 3=tan }; /*0340*/ uint32 spawnId; // Spawn Id -/*0344*/ uint8 unknown0344[4]; +/*0344*/ float bounding_radius; // used in melee, overrides calc /*0348*/ TintProfile equipment_tint; /*0384*/ uint8 lfg; // 0=off, 1=lfg on /*0385*/ @@ -1119,20 +1119,18 @@ struct Action_Struct { /* 00 */ uint16 target; // id of target /* 02 */ uint16 source; // id of caster - /* 04 */ uint16 level; // level of caster - /* 06 */ uint16 instrument_mod; - /* 08 */ uint32 unknown08; - /* 12 */ uint16 unknown16; -// some kind of sequence that's the same in both actions -// as well as the combat damage, to tie em together? - /* 14 */ uint32 sequence; - /* 18 */ uint32 unknown18; - /* 22 */ uint8 type; // 231 (0xE7) for spells - /* 23 */ uint32 unknown23; + /* 04 */ uint16 level; // level of caster for spells, OSX dump says attack rating, guess spells use it for level + /* 06 */ uint32 instrument_mod; // OSX dump says base damage, spells use it for bard song (different from newer clients) + /* 10 */ float force; + /* 14 */ float hit_heading; + /* 18 */ float hit_pitch; + /* 22 */ uint8 type; // 231 (0xE7) for spells, skill + /* 23 */ uint16 unknown23; // OSX says min_damage + /* 25 */ uint16 unknown25; // OSX says tohit /* 27 */ uint16 spell; // spell id being cast - /* 29 */ uint8 unknown29; + /* 29 */ uint8 spell_level; // this field seems to be some sort of success flag, if it's 4 - /* 30 */ uint8 buff_unknown; // if this is 4, a buff icon is made + /* 30 */ uint8 effect_flag; // if this is 4, a buff icon is made /* 31 */ }; @@ -1143,12 +1141,12 @@ struct CombatDamage_Struct { /* 00 */ uint16 target; /* 02 */ uint16 source; -/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells +/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells, skill /* 05 */ uint16 spellid; /* 07 */ uint32 damage; /* 11 */ float force; -/* 15 */ float meleepush_xy; // see above notes in Action_Struct -/* 19 */ float meleepush_z; +/* 15 */ float hit_heading; // see above notes in Action_Struct +/* 19 */ float hit_pitch; /* 23 */ }; @@ -2571,21 +2569,32 @@ struct GuildMakeLeader{ char name[64]; char target[64]; }; -struct BugStruct{ -/*0000*/ char chartype[64]; -/*0064*/ char name[96]; -/*0160*/ char ui[128]; -/*0288*/ float x; -/*0292*/ float y; -/*0296*/ float z; -/*0300*/ float heading; -/*0304*/ uint32 unknown304; -/*0308*/ uint32 type; -/*0312*/ char unknown312[2144]; -/*2456*/ char bug[1024]; -/*3480*/ char placeholder[2]; -/*3482*/ char system_info[4098]; + +struct BugReport_Struct { +/*0000*/ char category_name[64]; +/*0064*/ char character_name[64]; +/*0128*/ char unused_0128[32]; +/*0160*/ char ui_path[128]; +/*0288*/ float pos_x; +/*0292*/ float pos_y; +/*0296*/ float pos_z; +/*0300*/ uint32 heading; +/*0304*/ uint32 unused_0304; +/*0308*/ uint32 time_played; +/*0312*/ char padding_0312[8]; +/*0320*/ uint32 target_id; +/*0324*/ char padding_0324[140]; +/*0464*/ uint32 unknown_0464; // seems to always be '0' +/*0468*/ char target_name[64]; +/*0532*/ uint32 optional_info_mask; + +// this looks like a butchered 8k buffer with 2 trailing dword fields +/*0536*/ char unused_0536[2052]; +/*2588*/ char bug_report[2050]; +/*4638*/ char system_info[4098]; +/*8736*/ }; + struct Make_Pet_Struct { //Simple struct for getting pet info uint8 level; uint8 class_; @@ -2612,20 +2621,21 @@ struct Ground_Spawn{ struct Ground_Spawns { struct Ground_Spawn spawn[50]; //Assigned max number to allow }; -struct PetitionBug_Struct{ - uint32 petition_number; - uint32 unknown4; - char accountname[64]; - uint32 zoneid; - char name[64]; - uint32 level; - uint32 class_; - uint32 race; - uint32 unknown152[3]; - uint32 time; - uint32 unknown168; - char text[1028]; -}; + +//struct PetitionBug_Struct{ +// uint32 petition_number; +// uint32 unknown4; +// char accountname[64]; +// uint32 zoneid; +// char name[64]; +// uint32 level; +// uint32 class_; +// uint32 race; +// uint32 unknown152[3]; +// uint32 time; +// uint32 unknown168; +// char text[1028]; +//}; struct ApproveZone_Struct { char name[64]; @@ -3553,6 +3563,21 @@ struct LFGuild_GuildToggle_Struct // char ScrollName; // '0' //}; +struct SayLinkBodyFrame_Struct { +/*000*/ char ActionID[1]; +/*001*/ char ItemID[5]; +/*006*/ char Augment1[5]; +/*011*/ char Augment2[5]; +/*016*/ char Augment3[5]; +/*021*/ char Augment4[5]; +/*026*/ char Augment5[5]; +/*031*/ char IsEvolving[1]; +/*032*/ char EvolveGroup[4]; +/*036*/ char EvolveLevel[1]; +/*037*/ char Hash[8]; +/*045*/ +}; + }; /*structs*/ }; /*Titanium*/ diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index cf0c17176..79fdca333 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -53,11 +53,11 @@ namespace UF static inline uint32 UFToServerSlot(uint32 ufSlot); static inline uint32 UFToServerCorpseSlot(uint32 ufCorpseSlot); - // server to client text link converter - static inline void ServerToUFTextLink(std::string& ufTextLink, const std::string& serverTextLink); + // server to client say link converter + static inline void ServerToUFSayLink(std::string& ufSayLink, const std::string& serverSayLink); - // client to server text link converter - static inline void UFToServerTextLink(std::string& serverTextLink, const std::string& ufTextLink); + // client to server say link converter + static inline void UFToServerSayLink(std::string& serverSayLink, const std::string& ufSayLink); static inline CastingSlot ServerToUFCastingSlot(EQEmu::CastingSlot slot); static inline EQEmu::CastingSlot UFToServerCastingSlot(CastingSlot slot); @@ -161,29 +161,20 @@ namespace UF OUT(source); OUT(level); eq->instrument_mod = 1.0f + (emu->instrument_mod - 10) / 10.0f; - eq->knockback_angle = emu->sequence; + OUT(force); + OUT(hit_heading); + OUT(hit_pitch); OUT(type); OUT(spell); - eq->level2 = eq->level; - eq->effect_flag = emu->buff_unknown; - eq->unknown37 = 0x01; - eq->unknown44 = 0xFFFFFFFF; - eq->unknown48 = 0xFFFFFFFF; - eq->unknown52 = 0xFFFFFFFF; - - /*OUT(target); - OUT(source); - OUT(level); - OUT(instrument_mod); - eq->sequence = emu->sequence; - OUT(type); - //OUT(damage); - OUT(spell); - eq->level2 = emu->level; - OUT(buff_unknown); // if this is 4, a buff icon is made - //eq->unknown0036 = -1; - //eq->unknown0040 = -1; - //eq->unknown0044 = -1;*/ + OUT(spell_level); + OUT(effect_flag); + eq->spell_gem = 0; + eq->slot[0] = -1; // type + eq->slot[1] = -1; // slot + eq->slot[2] = -1; // sub index + eq->slot[3] = -1; // aug index + eq->slot[4] = -1; // unknown + eq->item_cast_type = 0; FINISH_ENCODE(); } @@ -463,7 +454,7 @@ namespace UF std::string old_message = emu->message; std::string new_message; - ServerToUFTextLink(new_message, old_message); + ServerToUFSayLink(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; @@ -586,8 +577,8 @@ namespace UF OUT(spellid); OUT(damage); OUT(force); - OUT(meleepush_xy); - OUT(meleepush_z); + OUT(hit_heading); + OUT(hit_pitch); OUT(special); FINISH_ENCODE(); @@ -762,7 +753,7 @@ namespace UF std::string old_message = emu->message; std::string new_message; - ServerToUFTextLink(new_message, old_message); + ServerToUFSayLink(new_message, old_message); //if (new_message.length() > 512) // length restricted in packet building function due vari-length name size (no nullterm) // new_message = new_message.substr(0, 512); @@ -814,7 +805,7 @@ namespace UF for (int i = 0; i < 9; ++i) { if (old_message_array[i].length() == 0) { break; } - ServerToUFTextLink(new_message_array[i], old_message_array[i]); + ServerToUFSayLink(new_message_array[i], old_message_array[i]); new_message_size += new_message_array[i].length() + 1; } @@ -2469,7 +2460,7 @@ namespace UF std::string old_message = &emu->message[strlen(emu->sayer)]; std::string new_message; - ServerToUFTextLink(new_message, old_message); + ServerToUFSayLink(new_message, old_message); //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; in->size = strlen(emu->sayer) + new_message.length() + 25; @@ -2539,7 +2530,7 @@ namespace UF std::string old_message = InBuffer; // start 'Reward' as string std::string new_message; - ServerToUFTextLink(new_message, old_message); + ServerToUFSayLink(new_message, old_message); in->size = sizeof(TaskDescriptionHeader_Struct) + sizeof(TaskDescriptionData1_Struct)+ sizeof(TaskDescriptionData2_Struct) + sizeof(TaskDescriptionTrailer_Struct)+ @@ -3287,7 +3278,7 @@ namespace UF std::string old_message = InBuffer; std::string new_message; - UFToServerTextLink(new_message, old_message); + UFToServerSayLink(new_message, old_message); //__packet->size = sizeof(ChannelMessage_Struct)+strlen(InBuffer) + 1; __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; @@ -3398,7 +3389,7 @@ namespace UF IN(type); IN(spellid); IN(damage); - IN(meleepush_xy); + IN(hit_heading); FINISH_DIRECT_DECODE(); } @@ -3421,7 +3412,7 @@ namespace UF std::string old_message = (char *)&__eq_buffer[4]; // unknown01 offset std::string new_message; - UFToServerTextLink(new_message, old_message); + UFToServerSayLink(new_message, old_message); __packet->size = sizeof(Emote_Struct); __packet->pBuffer = new unsigned char[__packet->size]; @@ -4290,19 +4281,19 @@ namespace UF return (ufCorpseSlot - 1); } - static inline void ServerToUFTextLink(std::string& ufTextLink, const std::string& serverTextLink) + static inline void ServerToUFSayLink(std::string& ufSayLink, const std::string& serverSayLink) { - if ((constants::SayLinkBodySize == EQEmu::legacy::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find('\x12') == std::string::npos)) { - ufTextLink = serverTextLink; + if ((constants::SayLinkBodySize == EQEmu::constants::SayLinkBodySize) || (serverSayLink.find('\x12') == std::string::npos)) { + ufSayLink = serverSayLink; return; } - auto segments = SplitString(serverTextLink, '\x12'); + auto segments = SplitString(serverSayLink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { - if (segments[segment_iter].length() <= EQEmu::legacy::TEXT_LINK_BODY_LENGTH) { - ufTextLink.append(segments[segment_iter]); + if (segments[segment_iter].length() <= EQEmu::constants::SayLinkBodySize) { + ufSayLink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -4312,37 +4303,37 @@ namespace UF // SoF: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXX XXXXXXXX (50) // Diff: ^^^^^ ^ - ufTextLink.push_back('\x12'); - ufTextLink.append(segments[segment_iter].substr(0, 31)); - ufTextLink.append(segments[segment_iter].substr(36, 5)); + ufSayLink.push_back('\x12'); + ufSayLink.append(segments[segment_iter].substr(0, 31)); + ufSayLink.append(segments[segment_iter].substr(36, 5)); if (segments[segment_iter][41] == '0') - ufTextLink.push_back(segments[segment_iter][42]); + ufSayLink.push_back(segments[segment_iter][42]); else - ufTextLink.push_back('F'); + ufSayLink.push_back('F'); - ufTextLink.append(segments[segment_iter].substr(43)); - ufTextLink.push_back('\x12'); + ufSayLink.append(segments[segment_iter].substr(43)); + ufSayLink.push_back('\x12'); } else { - ufTextLink.append(segments[segment_iter]); + ufSayLink.append(segments[segment_iter]); } } } - static inline void UFToServerTextLink(std::string& serverTextLink, const std::string& ufTextLink) + static inline void UFToServerSayLink(std::string& serverSayLink, const std::string& ufSayLink) { - if ((EQEmu::legacy::TEXT_LINK_BODY_LENGTH == constants::SayLinkBodySize) || (ufTextLink.find('\x12') == std::string::npos)) { - serverTextLink = ufTextLink; + if ((EQEmu::constants::SayLinkBodySize == constants::SayLinkBodySize) || (ufSayLink.find('\x12') == std::string::npos)) { + serverSayLink = ufSayLink; return; } - auto segments = SplitString(ufTextLink, '\x12'); + auto segments = SplitString(ufSayLink, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { if (segments[segment_iter].length() <= constants::SayLinkBodySize) { - serverTextLink.append(segments[segment_iter]); + serverSayLink.append(segments[segment_iter]); // TODO: log size mismatch error continue; } @@ -4352,16 +4343,16 @@ namespace UF // RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56) // Diff: ^^^^^ ^ - serverTextLink.push_back('\x12'); - serverTextLink.append(segments[segment_iter].substr(0, 31)); - serverTextLink.append("00000"); - serverTextLink.append(segments[segment_iter].substr(31, 5)); - serverTextLink.push_back('0'); - serverTextLink.append(segments[segment_iter].substr(36)); - serverTextLink.push_back('\x12'); + serverSayLink.push_back('\x12'); + serverSayLink.append(segments[segment_iter].substr(0, 31)); + serverSayLink.append("00000"); + serverSayLink.append(segments[segment_iter].substr(31, 5)); + serverSayLink.push_back('0'); + serverSayLink.append(segments[segment_iter].substr(36)); + serverSayLink.push_back('\x12'); } else { - serverTextLink.append(segments[segment_iter]); + serverSayLink.append(segments[segment_iter]); } } } diff --git a/common/patches/uf_structs.h b/common/patches/uf_structs.h index cc5564e11..dc66afd43 100644 --- a/common/patches/uf_structs.h +++ b/common/patches/uf_structs.h @@ -292,7 +292,7 @@ struct Spawn_Struct /*0000*/ //uint8 nullterm1; // hack to null terminate name /*0064*/ uint32 spawnId; /*0068*/ uint8 level; -/*0069*/ float unknown1; +/*0069*/ float bounding_radius; // used in melee, overrides calc /*0073*/ uint8 NPC; // 0=player,1=npc,2=pc corpse,3=npc corpse Spawn_Struct_Bitfields Bitfields; /*0000*/ uint8 otherData; // & 4 - has title, & 8 - has suffix, & 1 - it's a chest or untargetable @@ -1252,19 +1252,19 @@ struct Action_Struct { /*00*/ uint16 target; // id of target /*02*/ uint16 source; // id of caster -/*04*/ uint16 level; // level of caster - Seen 0 -/*06*/ uint32 unknown06; -/*10*/ uint16 instrument_focus; -/*12*/ uint16 unknown12; // seems to always be set to something and it doesn't change between casts except in special cases like changing instrument mods -/*14*/ uint32 unknown14; // seen 0 -/*18*/ float knockback_angle; //seems to go from 0-512 then it rolls over again -/*22*/ uint32 unknown22; -/*26*/ uint8 type; -/*27*/ uint32 damage; -/*31*/ uint16 unknown31; +/*04*/ uint16 level; // level of caster for spells, OSX dump says attack rating, guess spells use it for level +/*06*/ uint32 unknown06; // OSX dump says base_damage, was used for bard mod too, this is 0'd :( +/*10*/ float instrument_mod; +/*14*/ float force; +/*18*/ float hit_heading; +/*22*/ float hit_pitch; +/*26*/ uint8 type; // 231 (0xE7) for spells, skill +/*27*/ uint32 damage; // OSX says min_damage +/*31*/ uint16 unknown31; // OSX says tohit /*33*/ uint16 spell; // spell id being cast -/*35*/ uint8 level2; // level of caster again? Or maybe the castee +/*35*/ uint8 spell_level; // level of caster again? Or maybe the castee /*36*/ uint8 effect_flag; // if this is 4, the effect is valid: or if two are sent at the same time? +/*37*/ }; @@ -1276,27 +1276,22 @@ struct ActionAlt_Struct { /*00*/ uint16 target; // id of target /*02*/ uint16 source; // id of caster -/*04*/ uint16 level; // level of caster - Seen 0 -/*06*/ uint32 unknown06; +/*04*/ uint16 level; // level of caster for spells, OSX dump says attack rating, guess spells use it for level +/*06*/ uint32 unknown06; // OSX dump says base_damage, was used for bard mod too, this is 0'd :( /*10*/ float instrument_mod; -/*14*/ uint32 unknown14; // seen 0 -/*18*/ float knockback_angle; //seems to go from 0-512 then it rolls over again -/*22*/ uint32 unknown22; -/*26*/ uint8 type; -/*27*/ uint32 damage; -/*31*/ uint16 unknown31; +/*14*/ float force; +/*18*/ float hit_heading; +/*22*/ float hit_pitch; +/*26*/ uint8 type; // 231 (0xE7) for spells, skill +/*27*/ uint32 damage; // OSX says min_damage +/*31*/ uint16 unknown31; // OSX says tohit /*33*/ uint16 spell; // spell id being cast -/*35*/ uint8 level2; // level of caster again? Or maybe the castee +/*35*/ uint8 spell_level; // level of caster again? Or maybe the castee /*36*/ uint8 effect_flag; // if this is 4, the effect is valid: or if two are sent at the same time? -/*37*/ uint32 unknown37; // New field to Underfoot - Seen 14 -/*41*/ uint8 unknown41; // New field to Underfoot - Seen 0 -/*42*/ uint8 unknown42; // New field to Underfoot - Seen 0 -/*43*/ uint8 unknown43; // New field to Underfoot - Seen 0 -/*44*/ uint32 unknown44; // New field to Underfoot - Seen 23 -/*48*/ uint32 unknown48; // New field to Underfoot - Seen -1 -/*52*/ uint32 unknown52; // New field to Underfoot - Seen -1 -/*56*/ uint32 unknown56; // New field to Underfoot - Seen 0 -/*60*/ uint32 unknown60; // New field to Underfoot - Seen 0 +/*37*/ uint8 spell_gem; +/*38*/ uint8 padding38[2]; +/*40*/ uint32 slot[5]; +/*60*/ uint32 item_cast_type; // ItemSpellTypes enum from MQ2 /*64*/ }; @@ -1311,9 +1306,9 @@ struct CombatDamage_Struct /* 05 */ uint16 spellid; /* 07 */ int32 damage; /* 11 */ float force; // cd cc cc 3d -/* 15 */ float meleepush_xy; // see above notes in Action_Struct -/* 19 */ float meleepush_z; -/* 23 */ uint8 unknown23; // was [9] +/* 15 */ float hit_heading; // see above notes in Action_Struct +/* 19 */ float hit_pitch; +/* 23 */ uint8 secondary; // 0 for primary hand, 1 for secondary /* 24 */ uint32 special; // 2 = Rampage, 1 = Wild Rampage /* 28 */ }; @@ -3060,23 +3055,6 @@ struct GuildMakeLeader{ char target[64]; }; - - -struct BugStruct{ -/*0000*/ char chartype[64]; -/*0064*/ char name[96]; -/*0160*/ char ui[128]; -/*0288*/ float x; -/*0292*/ float y; -/*0296*/ float z; -/*0300*/ float heading; -/*0304*/ uint32 unknown304; -/*0308*/ uint32 type; -/*0312*/ char unknown312[2144]; -/*2456*/ char bug[1024]; -/*3480*/ char placeholder[2]; -/*3482*/ char system_info[4098]; -}; struct Make_Pet_Struct { //Simple struct for getting pet info uint8 level; uint8 class_; @@ -3103,20 +3081,21 @@ struct Ground_Spawn{ struct Ground_Spawns { struct Ground_Spawn spawn[50]; //Assigned max number to allow }; -struct PetitionBug_Struct{ - uint32 petition_number; - uint32 unknown4; - char accountname[64]; - uint32 zoneid; - char name[64]; - uint32 level; - uint32 class_; - uint32 race; - uint32 unknown152[3]; - uint32 time; - uint32 unknown168; - char text[1028]; -}; + +//struct PetitionBug_Struct{ +// uint32 petition_number; +// uint32 unknown4; +// char accountname[64]; +// uint32 zoneid; +// char name[64]; +// uint32 level; +// uint32 class_; +// uint32 race; +// uint32 unknown152[3]; +// uint32 time; +// uint32 unknown168; +// char text[1028]; +//}; struct ApproveZone_Struct { char name[64]; @@ -4505,6 +4484,22 @@ struct MercenaryAssign_Struct { /*0004*/ uint32 MercUnk01; // /*0008*/ uint32 MercUnk02; // /*0012*/ +}; + +struct SayLinkBodyFrame_Struct { +/*000*/ char ActionID[1]; +/*001*/ char ItemID[5]; +/*006*/ char Augment1[5]; +/*011*/ char Augment2[5]; +/*016*/ char Augment3[5]; +/*021*/ char Augment4[5]; +/*026*/ char Augment5[5]; +/*031*/ char IsEvolving[1]; +/*032*/ char EvolveGroup[4]; +/*036*/ char EvolveLevel[1]; +/*037*/ char OrnamentIcon[5]; +/*042*/ char Hash[8]; +/*050*/ }; }; /*structs*/ diff --git a/common/ruletypes.h b/common/ruletypes.h index 24d9e809b..6484a8586 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -156,6 +156,7 @@ RULE_BOOL(Character, UseOldBindWound, false) // Uses the original bind wound beh RULE_BOOL(Character, GrantHoTTOnCreate, false) // Grant Health of Target's Target leadership AA on character creation RULE_BOOL(Character, UseOldConSystem, false) // Grant Health of Target's Target leadership AA on character creation RULE_BOOL(Character, OPClientUpdateVisualDebug, false) // Shows a pulse and forward directional particle each time the client sends its position to server +RULE_BOOL(Character, PetsUseReagents, true) //Pets use reagent on spells RULE_CATEGORY_END() RULE_CATEGORY(Mercs) @@ -233,6 +234,7 @@ RULE_INT(World, TitaniumStartZoneID, -1) //Sets the Starting Zone for Titanium C RULE_INT(World, ExpansionSettings, 16383) // Sets the expansion settings for the server, This is sent on login to world and affects client expansion settings. Defaults to all expansions enabled up to TSS. RULE_BOOL(World, UseClientBasedExpansionSettings, true) // if true it will overrule World, ExpansionSettings and set someone's expansion based on the client they're using RULE_INT(World, PVPSettings, 0) // Sets the PVP settings for the server, 1 = Rallos Zek RuleSet, 2 = Tallon/Vallon Zek Ruleset, 4 = Sullon Zek Ruleset, 6 = Discord Ruleset, anything above 6 is the Discord Ruleset without the no-drop restrictions removed. TODO: Edit IsAttackAllowed in Zone to accomodate for these rules. +RULE_INT(World, PVPMinLevel, 0) // minimum level to pvp RULE_BOOL (World, IsGMPetitionWindowEnabled, false) RULE_INT (World, FVNoDropFlag, 0) // Sets the Firiona Vie settings on the client. If set to 2, the flag will be set for GMs only, allowing trading of no-drop items. RULE_BOOL (World, IPLimitDisconnectAll, false) @@ -400,6 +402,7 @@ RULE_BOOL(Spells, IgnoreSpellDmgLvlRestriction, false) // ignore the 5 level spr RULE_BOOL(Spells, AllowItemTGB, false) // TGB doesn't work with items on live, custom servers want it though RULE_BOOL(Spells, NPCInnateProcOverride, true) // NPC innate procs override the target type to single target. RULE_BOOL(Spells, OldRainTargets, false) // use old incorrectly implemented max targets for rains +RULE_BOOL(Spells, NPCSpellPush, false) // enable spell push on NPCs RULE_CATEGORY_END() RULE_CATEGORY(Combat) @@ -720,6 +723,12 @@ RULE_BOOL(Client, UseLiveFactionMessage, false) // Allows players to see faction RULE_BOOL(Client, UseLiveBlockedMessage, false) // Allows players to see faction adjustments like Live RULE_CATEGORY_END() +RULE_CATEGORY(Bugs) +RULE_BOOL(Bugs, ReportingSystemActive, true) // Activates bug reporting +RULE_BOOL(Bugs, UseOldReportingMethod, true) // Forces the use of the old bug reporting system +RULE_BOOL(Bugs, DumpTargetEntity, false) // Dumps the target entity, if one is provided +RULE_CATEGORY_END() + #undef RULE_CATEGORY #undef RULE_INT #undef RULE_REAL diff --git a/common/say_link.cpp b/common/say_link.cpp index 923e53407..e2b43c107 100644 --- a/common/say_link.cpp +++ b/common/say_link.cpp @@ -29,10 +29,10 @@ bool EQEmu::saylink::DegenerateLinkBody(SayLinkBody_Struct& say_link_body_struct, const std::string& say_link_body) { memset(&say_link_body_struct, 0, sizeof(say_link_body_struct)); - if (say_link_body.length() != EQEmu::legacy::TEXT_LINK_BODY_LENGTH) + if (say_link_body.length() != EQEmu::constants::SayLinkBodySize) return false; - say_link_body_struct.unknown_1 = (uint8)strtol(say_link_body.substr(0, 1).c_str(), nullptr, 16); + say_link_body_struct.action_id = (uint8)strtol(say_link_body.substr(0, 1).c_str(), nullptr, 16); say_link_body_struct.item_id = (uint32)strtol(say_link_body.substr(1, 5).c_str(), nullptr, 16); say_link_body_struct.augment_1 = (uint32)strtol(say_link_body.substr(6, 5).c_str(), nullptr, 16); say_link_body_struct.augment_2 = (uint32)strtol(say_link_body.substr(11, 5).c_str(), nullptr, 16); @@ -44,7 +44,7 @@ bool EQEmu::saylink::DegenerateLinkBody(SayLinkBody_Struct& say_link_body_struct say_link_body_struct.evolve_group = (uint32)strtol(say_link_body.substr(37, 4).c_str(), nullptr, 16); say_link_body_struct.evolve_level = (uint8)strtol(say_link_body.substr(41, 2).c_str(), nullptr, 16); say_link_body_struct.ornament_icon = (uint32)strtol(say_link_body.substr(43, 5).c_str(), nullptr, 16); - say_link_body_struct.hash = (int)strtol(say_link_body.substr(48, 8).c_str(), nullptr, 16); + say_link_body_struct.hash = (uint32)strtol(say_link_body.substr(48, 8).c_str(), nullptr, 16); return true; } @@ -53,7 +53,7 @@ bool EQEmu::saylink::GenerateLinkBody(std::string& say_link_body, const SayLinkB { say_link_body = StringFormat( "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%02X" "%05X" "%08X", - (0x0F & say_link_body_struct.unknown_1), + (0x0F & say_link_body_struct.action_id), (0x000FFFFF & say_link_body_struct.item_id), (0x000FFFFF & say_link_body_struct.augment_1), (0x000FFFFF & say_link_body_struct.augment_2), @@ -68,7 +68,7 @@ bool EQEmu::saylink::GenerateLinkBody(std::string& say_link_body, const SayLinkB (0xFFFFFFFF & say_link_body_struct.hash) ); - if (say_link_body.length() != EQEmu::legacy::TEXT_LINK_BODY_LENGTH) + if (say_link_body.length() != EQEmu::constants::SayLinkBodySize) return false; return true; @@ -79,7 +79,7 @@ EQEmu::SayLinkEngine::SayLinkEngine() Reset(); } -std::string EQEmu::SayLinkEngine::GenerateLink() +const std::string& EQEmu::SayLinkEngine::GenerateLink() { m_Link.clear(); m_LinkBody.clear(); @@ -88,18 +88,26 @@ std::string EQEmu::SayLinkEngine::GenerateLink() generate_body(); generate_text(); - if ((m_LinkBody.length() == EQEmu::legacy::TEXT_LINK_BODY_LENGTH) && (m_LinkText.length() > 0)) { + if ((m_LinkBody.length() == EQEmu::constants::SayLinkBodySize) && (m_LinkText.length() > 0)) { m_Link.push_back(0x12); m_Link.append(m_LinkBody); m_Link.append(m_LinkText); m_Link.push_back(0x12); } - if ((m_Link.length() == 0) || (m_Link.length() > 250)) { + if ((m_Link.length() == 0) || (m_Link.length() > (EQEmu::constants::SayLinkMaximumSize))) { m_Error = true; m_Link = ""; - Log(Logs::General, Logs::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()); + Log(Logs::General, Logs::Error, "SayLinkEngine::GenerateLink() failed to generate a useable say link"); + Log(Logs::General, Logs::Error, ">> LinkType: %i, Lengths: {link: %u(%u), body: %u(%u), text: %u(%u)}", + m_LinkType, + m_Link.length(), + EQEmu::constants::SayLinkMaximumSize, + m_LinkBody.length(), + EQEmu::constants::SayLinkBodySize, + m_LinkText.length(), + EQEmu::constants::SayLinkTextSize + ); Log(Logs::General, Logs::Error, ">> LinkBody: %s", m_LinkBody.c_str()); Log(Logs::General, Logs::Error, ">> LinkText: %s", m_LinkText.c_str()); } @@ -113,20 +121,10 @@ void EQEmu::SayLinkEngine::Reset() m_ItemData = nullptr; m_LootData = nullptr; m_ItemInst = nullptr; - m_Proxy_unknown_1 = 0; - m_ProxyItemID = 0; - m_ProxyAugment1ID = 0; - m_ProxyAugment2ID = 0; - m_ProxyAugment3ID = 0; - m_ProxyAugment4ID = 0; - m_ProxyAugment5ID = 0; - m_ProxyAugment6ID = 0; - m_ProxyIsEvolving = 0; - m_ProxyEvolveGroup = 0; - m_ProxyEvolveLevel = 0; - m_ProxyOrnamentIcon = 0; - m_ProxyHash = 0; - m_ProxyText = nullptr; + + memset(&m_LinkBodyStruct, 0, sizeof(SayLinkBody_Struct)); + memset(&m_LinkProxyStruct, 0, sizeof(SayLinkProxy_Struct)); + m_TaskUse = false; m_Link.clear(); m_LinkBody.clear(); @@ -194,32 +192,32 @@ void EQEmu::SayLinkEngine::generate_body() break; } - if (m_Proxy_unknown_1) - m_LinkBodyStruct.unknown_1 = m_Proxy_unknown_1; - if (m_ProxyItemID) - m_LinkBodyStruct.item_id = m_ProxyItemID; - if (m_ProxyAugment1ID) - m_LinkBodyStruct.augment_1 = m_ProxyAugment1ID; - if (m_ProxyAugment2ID) - m_LinkBodyStruct.augment_2 = m_ProxyAugment2ID; - if (m_ProxyAugment3ID) - m_LinkBodyStruct.augment_3 = m_ProxyAugment3ID; - if (m_ProxyAugment4ID) - m_LinkBodyStruct.augment_4 = m_ProxyAugment4ID; - if (m_ProxyAugment5ID) - m_LinkBodyStruct.augment_5 = m_ProxyAugment5ID; - if (m_ProxyAugment6ID) - m_LinkBodyStruct.augment_6 = m_ProxyAugment6ID; - if (m_ProxyIsEvolving) - m_LinkBodyStruct.is_evolving = m_ProxyIsEvolving; - if (m_ProxyEvolveGroup) - m_LinkBodyStruct.evolve_group = m_ProxyEvolveGroup; - if (m_ProxyEvolveLevel) - m_LinkBodyStruct.evolve_level = m_ProxyEvolveLevel; - if (m_ProxyOrnamentIcon) - m_LinkBodyStruct.ornament_icon = m_ProxyOrnamentIcon; - if (m_ProxyHash) - m_LinkBodyStruct.hash = m_ProxyHash; + if (m_LinkProxyStruct.action_id) + m_LinkBodyStruct.action_id = m_LinkProxyStruct.action_id; + if (m_LinkProxyStruct.item_id) + m_LinkBodyStruct.item_id = m_LinkProxyStruct.item_id; + if (m_LinkProxyStruct.augment_1) + m_LinkBodyStruct.augment_1 = m_LinkProxyStruct.augment_1; + if (m_LinkProxyStruct.augment_2) + m_LinkBodyStruct.augment_2 = m_LinkProxyStruct.augment_2; + if (m_LinkProxyStruct.augment_3) + m_LinkBodyStruct.augment_3 = m_LinkProxyStruct.augment_3; + if (m_LinkProxyStruct.augment_4) + m_LinkBodyStruct.augment_4 = m_LinkProxyStruct.augment_4; + if (m_LinkProxyStruct.augment_5) + m_LinkBodyStruct.augment_5 = m_LinkProxyStruct.augment_5; + if (m_LinkProxyStruct.augment_6) + m_LinkBodyStruct.augment_6 = m_LinkProxyStruct.augment_6; + if (m_LinkProxyStruct.is_evolving) + m_LinkBodyStruct.is_evolving = m_LinkProxyStruct.is_evolving; + if (m_LinkProxyStruct.evolve_group) + m_LinkBodyStruct.evolve_group = m_LinkProxyStruct.evolve_group; + if (m_LinkProxyStruct.evolve_level) + m_LinkBodyStruct.evolve_level = m_LinkProxyStruct.evolve_level; + if (m_LinkProxyStruct.ornament_icon) + m_LinkBodyStruct.ornament_icon = m_LinkProxyStruct.ornament_icon; + if (m_LinkProxyStruct.hash) + m_LinkBodyStruct.hash = m_LinkProxyStruct.hash; if (m_TaskUse) @@ -227,7 +225,7 @@ void EQEmu::SayLinkEngine::generate_body() m_LinkBody = StringFormat( "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%02X" "%05X" "%08X", - (0x0F & m_LinkBodyStruct.unknown_1), + (0x0F & m_LinkBodyStruct.action_id), (0x000FFFFF & m_LinkBodyStruct.item_id), (0x000FFFFF & m_LinkBodyStruct.augment_1), (0x000FFFFF & m_LinkBodyStruct.augment_2), @@ -245,8 +243,8 @@ void EQEmu::SayLinkEngine::generate_body() void EQEmu::SayLinkEngine::generate_text() { - if (m_ProxyText != nullptr) { - m_LinkText = m_ProxyText; + if (m_LinkProxyStruct.text != nullptr) { + m_LinkText = m_LinkProxyStruct.text; return; } diff --git a/common/say_link.h b/common/say_link.h index 6bc3e5535..55e3a8237 100644 --- a/common/say_link.h +++ b/common/say_link.h @@ -47,7 +47,7 @@ namespace EQEmu } /*saylink*/ struct SayLinkBody_Struct { - uint8 unknown_1; /* %1X */ + uint8 action_id; /* %1X */ uint32 item_id; /* %05X */ uint32 augment_1; /* %05X */ uint32 augment_2; /* %05X */ @@ -56,13 +56,18 @@ namespace EQEmu uint32 augment_5; /* %05X */ uint32 augment_6; /* %05X */ uint8 is_evolving; /* %1X */ - uint32 evolve_group; /* %05X */ + uint32 evolve_group; /* %04X */ uint8 evolve_level; /* %02X */ uint32 ornament_icon; /* %05X */ - int hash; /* %08X */ + uint32 hash; /* %08X */ + }; + + struct SayLinkProxy_Struct : SayLinkBody_Struct { + const char* text; }; class SayLinkEngine { + // TODO: consider methods for direct 'saylink' assignments public: SayLinkEngine(); @@ -72,29 +77,29 @@ namespace EQEmu void SetItemInst(const ItemInstance* item_inst) { m_ItemInst = item_inst; } // mainly for saylinks..but, not limited to - void SetProxyUnknown1(uint8 proxy_unknown_1) { m_Proxy_unknown_1 = proxy_unknown_1; } - void SetProxyItemID(uint32 proxy_item_id) { m_ProxyItemID = proxy_item_id; } - void SetProxyAugment1ID(uint32 proxy_augment_id) { m_ProxyAugment1ID = proxy_augment_id; } - void SetProxyAugment2ID(uint32 proxy_augment_id) { m_ProxyAugment2ID = proxy_augment_id; } - void SetProxyAugment3ID(uint32 proxy_augment_id) { m_ProxyAugment3ID = proxy_augment_id; } - void SetProxyAugment4ID(uint32 proxy_augment_id) { m_ProxyAugment4ID = proxy_augment_id; } - void SetProxyAugment5ID(uint32 proxy_augment_id) { m_ProxyAugment5ID = proxy_augment_id; } - void SetProxyAugment6ID(uint32 proxy_augment_id) { m_ProxyAugment6ID = proxy_augment_id; } - void SetProxyIsEvolving(uint8 proxy_is_evolving) { m_ProxyIsEvolving = proxy_is_evolving; } - void SetProxyEvolveGroup(uint32 proxy_evolve_group) { m_ProxyEvolveGroup = proxy_evolve_group; } - void SetProxyEvolveLevel(uint8 proxy_evolve_level) { m_ProxyEvolveLevel = proxy_evolve_level; } - void SetProxyOrnamentIcon(uint32 proxy_ornament_icon) { m_ProxyOrnamentIcon = proxy_ornament_icon; } - void SetProxyHash(int proxy_hash) { m_ProxyHash = proxy_hash; } + void SetProxyActionID(uint8 proxy_action_id) { m_LinkProxyStruct.action_id = proxy_action_id; } // should always be '0' + void SetProxyItemID(uint32 proxy_item_id) { m_LinkProxyStruct.item_id = proxy_item_id; } + void SetProxyAugment1ID(uint32 proxy_augment_id) { m_LinkProxyStruct.augment_1 = proxy_augment_id; } + void SetProxyAugment2ID(uint32 proxy_augment_id) { m_LinkProxyStruct.augment_2 = proxy_augment_id; } + void SetProxyAugment3ID(uint32 proxy_augment_id) { m_LinkProxyStruct.augment_3 = proxy_augment_id; } + void SetProxyAugment4ID(uint32 proxy_augment_id) { m_LinkProxyStruct.augment_4 = proxy_augment_id; } + void SetProxyAugment5ID(uint32 proxy_augment_id) { m_LinkProxyStruct.augment_5 = proxy_augment_id; } + void SetProxyAugment6ID(uint32 proxy_augment_id) { m_LinkProxyStruct.augment_6 = proxy_augment_id; } + void SetProxyIsEvolving(uint8 proxy_is_evolving) { m_LinkProxyStruct.is_evolving = proxy_is_evolving; } + void SetProxyEvolveGroup(uint32 proxy_evolve_group) { m_LinkProxyStruct.evolve_group = proxy_evolve_group; } + void SetProxyEvolveLevel(uint8 proxy_evolve_level) { m_LinkProxyStruct.evolve_level = proxy_evolve_level; } + void SetProxyOrnamentIcon(uint32 proxy_ornament_icon) { m_LinkProxyStruct.ornament_icon = proxy_ornament_icon; } + void SetProxyHash(uint32 proxy_hash) { m_LinkProxyStruct.hash = proxy_hash; } - void SetProxyText(const char* proxy_text) { m_ProxyText = proxy_text; } // overrides standard text use + void SetProxyText(const char* proxy_text) { m_LinkProxyStruct.text = proxy_text; } // overrides standard text use void SetTaskUse() { m_TaskUse = true; } - std::string GenerateLink(); + const std::string& GenerateLink(); bool LinkError() { return m_Error; } - std::string Link() { return m_Link; } // contains full string format: '/12x' '' '' '/12x' - std::string LinkBody() { return m_LinkBody; } // contains string format: '' - std::string LinkText() { return m_LinkText; } // contains string format: '' + const std::string& Link() { return m_Link; } // contains full string format: '\x12' '' '' '\x12' + const std::string& LinkBody() { return m_LinkBody; } // contains string format: '' + const std::string& LinkText() { return m_LinkText; } // contains string format: '' void Reset(); @@ -106,23 +111,9 @@ namespace EQEmu const ItemData* m_ItemData; const ServerLootItem_Struct* m_LootData; const ItemInstance* m_ItemInst; - - uint8 m_Proxy_unknown_1; - uint32 m_ProxyItemID; - uint32 m_ProxyAugment1ID; - uint32 m_ProxyAugment2ID; - uint32 m_ProxyAugment3ID; - uint32 m_ProxyAugment4ID; - uint32 m_ProxyAugment5ID; - uint32 m_ProxyAugment6ID; - uint8 m_ProxyIsEvolving; - uint32 m_ProxyEvolveGroup; - uint8 m_ProxyEvolveLevel; - uint32 m_ProxyOrnamentIcon; - int m_ProxyHash; - const char* m_ProxyText; - bool m_TaskUse; SayLinkBody_Struct m_LinkBodyStruct; + SayLinkProxy_Struct m_LinkProxyStruct; + bool m_TaskUse; std::string m_Link; std::string m_LinkBody; std::string m_LinkText; diff --git a/common/servertalk.h b/common/servertalk.h index f5965cd74..e1d019fc4 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -190,6 +190,8 @@ #define ServerOP_ReloadLogs 0x4010 #define ServerOP_ReloadPerlExportSettings 0x4011 #define ServerOP_CZSetEntityVariableByClientName 0x4012 +#define ServerOP_UCSServerStatusRequest 0x4013 +#define ServerOP_UCSServerStatusReply 0x4014 /* Query Server OP Codes */ #define ServerOP_QSPlayerLogTrades 0x5010 #define ServerOP_QSPlayerLogHandins 0x5011 @@ -1278,6 +1280,17 @@ struct ServerRequestTellQueue_Struct { char name[64]; }; +struct UCSServerStatus_Struct { + uint8 available; // non-zero=true, 0=false + union { + struct { + uint16 port; + uint16 unused; + }; + uint32 timestamp; + }; +}; + #pragma pack() #endif diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 09c936425..a4c6e54d1 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -110,6 +110,41 @@ uint32 SharedDatabase::GetTotalTimeEntitledOnAccount(uint32 AccountID) { return EntitledTime; } +void SharedDatabase::SetMailKey(int CharID, int IPAddress, int MailKey) +{ + char MailKeyString[17]; + + if (RuleB(Chat, EnableMailKeyIPVerification) == true) + sprintf(MailKeyString, "%08X%08X", IPAddress, MailKey); + else + sprintf(MailKeyString, "%08X", MailKey); + + std::string query = StringFormat("UPDATE character_data SET mailkey = '%s' WHERE id = '%i'", + MailKeyString, CharID); + auto results = QueryDatabase(query); + if (!results.Success()) + Log(Logs::General, Logs::Error, "SharedDatabase::SetMailKey(%i, %s) : %s", CharID, MailKeyString, results.ErrorMessage().c_str()); + +} + +std::string SharedDatabase::GetMailKey(int CharID, bool key_only) +{ + std::string query = StringFormat("SELECT `mailkey` FROM `character_data` WHERE `id`='%i' LIMIT 1", CharID); + auto results = QueryDatabase(query); + if (!results.Success()) { + Log(Logs::Detail, Logs::MySQLError, "Error retrieving mailkey from database: %s", results.ErrorMessage().c_str()); + return std::string(); + } + + auto row = results.begin(); + std::string mail_key = row[0]; + + if (mail_key.length() > 8 && key_only) + return mail_key.substr(8); + else + return mail_key; +} + bool SharedDatabase::SaveCursor(uint32 char_id, std::list::const_iterator &start, std::list::const_iterator &end) { // Delete cursor items diff --git a/common/shareddb.h b/common/shareddb.h index d3d8020a1..bbd8dc9a0 100644 --- a/common/shareddb.h +++ b/common/shareddb.h @@ -72,6 +72,8 @@ class SharedDatabase : public Database void SaveCharacterInspectMessage(uint32 character_id, const InspectMessage_Struct* message); bool GetCommandSettings(std::map>> &command_settings); uint32 GetTotalTimeEntitledOnAccount(uint32 AccountID); + void SetMailKey(int CharID, int IPAddress, int MailKey); + std::string GetMailKey(int CharID, bool key_only = false); /* Character InventoryProfile diff --git a/common/spdat.h b/common/spdat.h index 51ec8327b..c6aab7e7e 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -972,7 +972,6 @@ bool IsCastWhileInvis(uint16 spell_id); bool IsEffectIgnoredInStacking(int spa); int CalcPetHp(int levelb, int classb, int STA = 75); -const char *GetRandPetName(); int GetSpellEffectDescNum(uint16 spell_id); DmgShieldType GetDamageShieldType(uint16 spell_id, int32 DSType = 0); bool DetrimentalSpellAllowsRest(uint16 spell_id); diff --git a/common/version.h b/common/version.h index 412223b8c..e003dc33f 100644 --- a/common/version.h +++ b/common/version.h @@ -30,7 +30,7 @@ Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9120 +#define CURRENT_BINARY_DATABASE_VERSION 9122 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9018 #else diff --git a/ucs/chatchannel.cpp b/ucs/chatchannel.cpp index 16403c295..11a000712 100644 --- a/ucs/chatchannel.cpp +++ b/ucs/chatchannel.cpp @@ -28,6 +28,10 @@ extern Database database; extern uint32 ChatMessagesSent; +void ServerToClient45SayLink(std::string& clientSayLink, const std::string& serverSayLink); +void ServerToClient50SayLink(std::string& clientSayLink, const std::string& serverSayLink); +void ServerToClient55SayLink(std::string& clientSayLink, const std::string& serverSayLink); + ChatChannel::ChatChannel(std::string inName, std::string inOwner, std::string inPassword, bool inPermanent, int inMinimumStatus) : DeleteTimer(0) { @@ -384,6 +388,8 @@ void ChatChannel::SendMessageToChannel(std::string Message, Client* Sender) { if(!Sender) return; + std::string cv_messages[EQEmu::versions::ClientVersionCount]; + ChatMessagesSent++; LinkedListIterator iterator(ClientsInChannel); @@ -398,7 +404,28 @@ void ChatChannel::SendMessageToChannel(std::string Message, Client* Sender) { { Log(Logs::Detail, Logs::UCS_Server, "Sending message to %s from %s", ChannelClient->GetName().c_str(), Sender->GetName().c_str()); - ChannelClient->SendChannelMessage(Name, Message, Sender); + + if (cv_messages[static_cast(ChannelClient->GetClientVersion())].length() == 0) { + switch (ChannelClient->GetClientVersion()) { + case EQEmu::versions::ClientVersion::Titanium: + ServerToClient45SayLink(cv_messages[static_cast(ChannelClient->GetClientVersion())], Message); + break; + case EQEmu::versions::ClientVersion::SoF: + case EQEmu::versions::ClientVersion::SoD: + case EQEmu::versions::ClientVersion::UF: + ServerToClient50SayLink(cv_messages[static_cast(ChannelClient->GetClientVersion())], Message); + break; + case EQEmu::versions::ClientVersion::RoF: + ServerToClient55SayLink(cv_messages[static_cast(ChannelClient->GetClientVersion())], Message); + break; + case EQEmu::versions::ClientVersion::RoF2: + default: + cv_messages[static_cast(ChannelClient->GetClientVersion())] = Message; + break; + } + } + + ChannelClient->SendChannelMessage(Name, cv_messages[static_cast(ChannelClient->GetClientVersion())], Sender); } iterator.Advance(); @@ -655,3 +682,118 @@ std::string CapitaliseName(std::string inString) { return NormalisedName; } +void ServerToClient45SayLink(std::string& clientSayLink, const std::string& serverSayLink) { + if (serverSayLink.find('\x12') == std::string::npos) { + clientSayLink = serverSayLink; + return; + } + + auto segments = SplitString(serverSayLink, '\x12'); + + for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { + if (segment_iter & 1) { + if (segments[segment_iter].length() <= 56) { + clientSayLink.append(segments[segment_iter]); + // TODO: log size mismatch error + continue; + } + + // 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: ^^^^^ ^ ^^^^^ + + clientSayLink.push_back('\x12'); + clientSayLink.append(segments[segment_iter].substr(0, 31)); + clientSayLink.append(segments[segment_iter].substr(36, 5)); + + if (segments[segment_iter][41] == '0') + clientSayLink.push_back(segments[segment_iter][42]); + else + clientSayLink.push_back('F'); + + clientSayLink.append(segments[segment_iter].substr(48)); + clientSayLink.push_back('\x12'); + } + else { + clientSayLink.append(segments[segment_iter]); + } + } +} + +void ServerToClient50SayLink(std::string& clientSayLink, const std::string& serverSayLink) { + if (serverSayLink.find('\x12') == std::string::npos) { + clientSayLink = serverSayLink; + return; + } + + auto segments = SplitString(serverSayLink, '\x12'); + + for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { + if (segment_iter & 1) { + if (segments[segment_iter].length() <= 56) { + clientSayLink.append(segments[segment_iter]); + // TODO: log size mismatch error + continue; + } + + // 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: ^^^^^ ^ + + clientSayLink.push_back('\x12'); + clientSayLink.append(segments[segment_iter].substr(0, 31)); + clientSayLink.append(segments[segment_iter].substr(36, 5)); + + if (segments[segment_iter][41] == '0') + clientSayLink.push_back(segments[segment_iter][42]); + else + clientSayLink.push_back('F'); + + clientSayLink.append(segments[segment_iter].substr(43)); + clientSayLink.push_back('\x12'); + } + else { + clientSayLink.append(segments[segment_iter]); + } + } +} + +void ServerToClient55SayLink(std::string& clientSayLink, const std::string& serverSayLink) { + if (serverSayLink.find('\x12') == std::string::npos) { + clientSayLink = serverSayLink; + return; + } + + auto segments = SplitString(serverSayLink, '\x12'); + + for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { + if (segment_iter & 1) { + if (segments[segment_iter].length() <= 56) { + clientSayLink.append(segments[segment_iter]); + // TODO: log size mismatch error + continue; + } + + // 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: ^ + + clientSayLink.push_back('\x12'); + clientSayLink.append(segments[segment_iter].substr(0, 41)); + + if (segments[segment_iter][41] == '0') + clientSayLink.push_back(segments[segment_iter][42]); + else + clientSayLink.push_back('F'); + + clientSayLink.append(segments[segment_iter].substr(43)); + clientSayLink.push_back('\x12'); + } + else { + clientSayLink.append(segments[segment_iter]); + } + } +} diff --git a/ucs/clientlist.cpp b/ucs/clientlist.cpp index 998410ec1..130914952 100644 --- a/ucs/clientlist.cpp +++ b/ucs/clientlist.cpp @@ -513,6 +513,7 @@ Client::Client(std::shared_ptr eqs) { GlobalChatLimiterTimer = new Timer(RuleI(Chat, IntervalDurationMS)); TypeOfConnection = ConnectionTypeUnknown; + ClientVersion_ = EQEmu::versions::ClientVersion::Unknown; UnderfootOrLater = false; } @@ -681,6 +682,7 @@ void Clientlist::Process() it = ClientChatConnections.erase(it); continue; } + ++it; } } @@ -2134,34 +2136,62 @@ void Client::SetConnectionType(char c) { switch (c) { - case 'S': - { - TypeOfConnection = ConnectionTypeCombined; - Log(Logs::Detail, Logs::UCS_Server, "Connection type is Combined (SoF/SoD)"); - break; - } - case 'U': - { - TypeOfConnection = ConnectionTypeCombined; - UnderfootOrLater = true; - Log(Logs::Detail, Logs::UCS_Server, "Connection type is Combined (Underfoot+)"); - break; - } - case 'M': - { - TypeOfConnection = ConnectionTypeMail; - Log(Logs::Detail, Logs::UCS_Server, "Connection type is Mail (6.2 or Titanium client)"); - break; - } - case 'C': + case EQEmu::versions::ucsTitaniumChat: { TypeOfConnection = ConnectionTypeChat; - Log(Logs::Detail, Logs::UCS_Server, "Connection type is Chat (6.2 or Titanium client)"); + ClientVersion_ = EQEmu::versions::ClientVersion::Titanium; + Log(Logs::Detail, Logs::UCS_Server, "Connection type is Chat (Titanium)"); + break; + } + case EQEmu::versions::ucsTitaniumMail: + { + TypeOfConnection = ConnectionTypeMail; + ClientVersion_ = EQEmu::versions::ClientVersion::Titanium; + Log(Logs::Detail, Logs::UCS_Server, "Connection type is Mail (Titanium)"); + break; + } + case EQEmu::versions::ucsSoFCombined: + { + TypeOfConnection = ConnectionTypeCombined; + ClientVersion_ = EQEmu::versions::ClientVersion::SoF; + Log(Logs::Detail, Logs::UCS_Server, "Connection type is Combined (SoF)"); + break; + } + case EQEmu::versions::ucsSoDCombined: + { + TypeOfConnection = ConnectionTypeCombined; + ClientVersion_ = EQEmu::versions::ClientVersion::SoD; + Log(Logs::Detail, Logs::UCS_Server, "Connection type is Combined (SoD)"); + break; + } + case EQEmu::versions::ucsUFCombined: + { + TypeOfConnection = ConnectionTypeCombined; + ClientVersion_ = EQEmu::versions::ClientVersion::UF; + UnderfootOrLater = true; + Log(Logs::Detail, Logs::UCS_Server, "Connection type is Combined (Underfoot)"); + break; + } + case EQEmu::versions::ucsRoFCombined: + { + TypeOfConnection = ConnectionTypeCombined; + ClientVersion_ = EQEmu::versions::ClientVersion::RoF; + UnderfootOrLater = true; + Log(Logs::Detail, Logs::UCS_Server, "Connection type is Combined (RoF)"); + break; + } + case EQEmu::versions::ucsRoF2Combined: + { + TypeOfConnection = ConnectionTypeCombined; + ClientVersion_ = EQEmu::versions::ClientVersion::RoF2; + UnderfootOrLater = true; + Log(Logs::Detail, Logs::UCS_Server, "Connection type is Combined (RoF2)"); break; } default: { TypeOfConnection = ConnectionTypeUnknown; + ClientVersion_ = EQEmu::versions::ClientVersion::Unknown; Log(Logs::Detail, Logs::UCS_Server, "Connection type is unknown."); } } diff --git a/ucs/clientlist.h b/ucs/clientlist.h index 9b72df51a..6021c5c0e 100644 --- a/ucs/clientlist.h +++ b/ucs/clientlist.h @@ -139,8 +139,11 @@ public: std::string MailBoxName(); int GetMailBoxNumber() { return CurrentMailBox; } int GetMailBoxNumber(std::string CharacterName); + void SetConnectionType(char c); ConnectionType GetConnectionType() { return TypeOfConnection; } + EQEmu::versions::ClientVersion GetClientVersion() { return ClientVersion_; } + inline bool IsMailConnection() { return (TypeOfConnection == ConnectionTypeMail) || (TypeOfConnection == ConnectionTypeCombined); } void SendNotification(int MailBoxNumber, std::string From, std::string Subject, int MessageID); void ChangeMailBox(int NewMailBox); @@ -167,7 +170,9 @@ private: Timer *GlobalChatLimiterTimer; //60 seconds int AttemptedMessages; bool ForceDisconnect; + ConnectionType TypeOfConnection; + EQEmu::versions::ClientVersion ClientVersion_; bool UnderfootOrLater; }; @@ -183,7 +188,6 @@ public: void ProcessOPMailCommand(Client *c, std::string CommandString); private: - EQ::Net::EQStreamManager *chatsf; std::list ClientChatConnections; diff --git a/ucs/worldserver.cpp b/ucs/worldserver.cpp index 141211032..b3df5d99d 100644 --- a/ucs/worldserver.cpp +++ b/ucs/worldserver.cpp @@ -21,6 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "../common/misc_functions.h" #include "../common/packet_functions.h" #include "../common/md5.h" +#include "../common/string_util.h" #include "worldserver.h" #include "clientlist.h" #include "ucsconfig.h" @@ -41,6 +42,10 @@ extern Database database; void ProcessMailTo(Client *c, std::string from, std::string subject, std::string message); +void Client45ToServerSayLink(std::string& serverSayLink, const std::string& clientSayLink); +void Client50ToServerSayLink(std::string& serverSayLink, const std::string& clientSayLink); +void Client55ToServerSayLink(std::string& serverSayLink, const std::string& clientSayLink); + WorldServer::WorldServer() { m_connection.reset(new EQ::Net::ServertalkClient(Config->WorldIP, Config->WorldTCPPort, false, "UCS", Config->SharedKey)); @@ -94,7 +99,26 @@ void WorldServer::ProcessMessage(uint16 opcode, EQ::Net::Packet &p) if (Message[0] == ';') { - c->SendChannelMessageByNumber(Message.substr(1, std::string::npos)); + std::string new_message; + switch (c->GetClientVersion()) { + case EQEmu::versions::ClientVersion::Titanium: + Client45ToServerSayLink(new_message, Message.substr(1, std::string::npos)); + break; + case EQEmu::versions::ClientVersion::SoF: + case EQEmu::versions::ClientVersion::SoD: + case EQEmu::versions::ClientVersion::UF: + Client50ToServerSayLink(new_message, Message.substr(1, std::string::npos)); + break; + case EQEmu::versions::ClientVersion::RoF: + Client55ToServerSayLink(new_message, Message.substr(1, std::string::npos)); + break; + case EQEmu::versions::ClientVersion::RoF2: + default: + new_message = Message.substr(1, std::string::npos); + break; + } + + c->SendChannelMessageByNumber(new_message); } else if (Message[0] == '[') { @@ -116,3 +140,108 @@ void WorldServer::ProcessMessage(uint16 opcode, EQ::Net::Packet &p) } } } + +void Client45ToServerSayLink(std::string& serverSayLink, const std::string& clientSayLink) { + if (clientSayLink.find('\x12') == std::string::npos) { + serverSayLink = clientSayLink; + return; + } + + auto segments = SplitString(clientSayLink, '\x12'); + + for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { + if (segment_iter & 1) { + if (segments[segment_iter].length() <= 45) { + serverSayLink.append(segments[segment_iter]); + // TODO: log size mismatch error + continue; + } + + // 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: ^^^^^ ^ ^^^^^ + + serverSayLink.push_back('\x12'); + serverSayLink.append(segments[segment_iter].substr(0, 31)); + serverSayLink.append("00000"); + serverSayLink.append(segments[segment_iter].substr(31, 5)); + serverSayLink.push_back('0'); + serverSayLink.push_back(segments[segment_iter][36]); + serverSayLink.append("00000"); + serverSayLink.append(segments[segment_iter].substr(37)); + serverSayLink.push_back('\x12'); + } + else { + serverSayLink.append(segments[segment_iter]); + } + } +} + +void Client50ToServerSayLink(std::string& serverSayLink, const std::string& clientSayLink) { + if (clientSayLink.find('\x12') == std::string::npos) { + serverSayLink = clientSayLink; + return; + } + + auto segments = SplitString(clientSayLink, '\x12'); + + for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { + if (segment_iter & 1) { + if (segments[segment_iter].length() <= 50) { + serverSayLink.append(segments[segment_iter]); + // TODO: log size mismatch error + continue; + } + + // 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: ^^^^^ ^ + + serverSayLink.push_back('\x12'); + serverSayLink.append(segments[segment_iter].substr(0, 31)); + serverSayLink.append("00000"); + serverSayLink.append(segments[segment_iter].substr(31, 5)); + serverSayLink.push_back('0'); + serverSayLink.append(segments[segment_iter].substr(36)); + serverSayLink.push_back('\x12'); + } + else { + serverSayLink.append(segments[segment_iter]); + } + } +} + +void Client55ToServerSayLink(std::string& serverSayLink, const std::string& clientSayLink) { + if (clientSayLink.find('\x12') == std::string::npos) { + serverSayLink = clientSayLink; + return; + } + + auto segments = SplitString(clientSayLink, '\x12'); + + for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { + if (segment_iter & 1) { + if (segments[segment_iter].length() <= 55) { + serverSayLink.append(segments[segment_iter]); + // TODO: log size mismatch error + continue; + } + + // 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: ^ + + serverSayLink.push_back('\x12'); + serverSayLink.append(segments[segment_iter].substr(0, 41)); + serverSayLink.push_back('0'); + serverSayLink.append(segments[segment_iter].substr(41)); + serverSayLink.push_back('\x12'); + } + else { + serverSayLink.append(segments[segment_iter]); + } + } +} diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf index 074d5751d..b348ea1ad 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -165,6 +165,7 @@ OP_GMNameChange=0x3077 # Was 0x4434 OP_GMLastName=0x4dd7 # Was 0x3077 # Misc Opcodes +OP_QueryUCSServerStatus=0x6964 OP_InspectRequest=0x23f1 OP_InspectAnswer=0x5794 OP_InspectMessageUpdate=0x3064 diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index dd58b3c11..9bd507bc1 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -164,6 +164,7 @@ OP_GMNameChange=0x035f OP_GMLastName=0x46ce # Misc Opcodes +OP_QueryUCSServerStatus=0x398f OP_InspectRequest=0x57bc OP_InspectAnswer=0x71ac OP_InspectMessageUpdate=0x4d25 diff --git a/utils/patches/patch_SoD.conf b/utils/patches/patch_SoD.conf index 5913670ae..ab8267a8a 100644 --- a/utils/patches/patch_SoD.conf +++ b/utils/patches/patch_SoD.conf @@ -165,6 +165,7 @@ OP_GMKill=0x6685 # C OP_GMNameChange=0x565d # C OP_GMLastName=0x3563 # C +OP_QueryUCSServerStatus=0x4036 OP_InspectAnswer=0x4938 # C OP_Action2=0x7e4d # C OP_Damage? OP_BeginCast=0x0d5a # C diff --git a/utils/patches/patch_UF.conf b/utils/patches/patch_UF.conf index a96b4208a..c08edbca3 100644 --- a/utils/patches/patch_UF.conf +++ b/utils/patches/patch_UF.conf @@ -168,6 +168,7 @@ OP_GMKill=0x799c # C OP_GMNameChange=0x0f48 # C OP_GMLastName=0x7bfb # C +OP_QueryUCSServerStatus=0x4481 OP_InspectAnswer=0x0c2b # C OP_BeginCast=0x0d5a # C OP_ColoredText=0x71bf # C diff --git a/utils/scripts/linux_installer/install.sh b/utils/scripts/linux_installer/install.sh index 1a94320ae..bffa2d939 100644 --- a/utils/scripts/linux_installer/install.sh +++ b/utils/scripts/linux_installer/install.sh @@ -121,17 +121,13 @@ if [[ "$OS" == "Debian" ]]; then apt-get $apt_options install libsodium18 apt-get $apt_options install libjson-perl - # If libsodium18 isn't installed (Debian), let's download both that and the dev package and install them. - if dpkg-query -s "libsodium18" 1>/dev/null 2>&1; then - echo "Sodium library already installed." - else - wget http://ftp.us.debian.org/debian/pool/main/libs/libsodium/libsodium-dev_1.0.11-1~bpo8+1_amd64.deb -O /home/eqemu/libsodium-dev.deb - wget http://ftp.us.debian.org/debian/pool/main/libs/libsodium/libsodium18_1.0.11-1~bpo8+1_amd64.deb -O /home/eqemu/libsodium18.deb - dpkg -i /home/eqemu/libsodium*.deb - # Cleanup after ourselves - rm -f /home/eqemu/libsodium-dev.deb - rm -f /home/eqemu/libsodium18.deb - fi + # Install libsodium + wget http://ftp.us.debian.org/debian/pool/main/libs/libsodium/libsodium-dev_1.0.11-1~bpo8+1_amd64.deb -O /home/eqemu/libsodium-dev.deb + wget http://ftp.us.debian.org/debian/pool/main/libs/libsodium/libsodium18_1.0.11-1~bpo8+1_amd64.deb -O /home/eqemu/libsodium18.deb + dpkg -i /home/eqemu/libsodium*.deb + # Cleanup after ourselves + rm -f /home/eqemu/libsodium-dev.deb + rm -f /home/eqemu/libsodium18.deb #::: Install FTP for remote FTP access echo "proftpd-basic shared/proftpd/inetd_or_standalone select standalone" | debconf-set-selections diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 4de5f3d8f..db0a04ca8 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -374,6 +374,8 @@ 9118|2018_02_04_Charm_Stats.sql|SHOW COLUMNS FROM `npc_types` LIKE 'charm_ac'|empty| 9119|2018_02_10_GlobalLoot.sql|SHOW TABLES LIKE 'global_loot'|empty| 9120|2018_02_13_Heading.sql|SELECT value FROM variables WHERE varname = 'fixed_heading'|empty| +9121|2018_02_18_bug_reports.sql|SHOW TABLES LIKE 'bug_reports'|empty| +9122|2018_03_07_ucs_command.sql|SELECT * FROM `command_settings` WHERE `command` LIKE 'ucs'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2018_02_13_Heading.sql b/utils/sql/git/required/2018_02_13_Heading.sql index 4f4736625..e7869b575 100644 --- a/utils/sql/git/required/2018_02_13_Heading.sql +++ b/utils/sql/git/required/2018_02_13_Heading.sql @@ -1,3 +1,3 @@ UPDATE spawn2 SET heading = heading * 8.0 / 4.0; UPDATE grid_entries SET heading = heading * 8.0 / 4.0 WHERE heading <> -1; -INSERT INTO variables (varname, value) VALUES ('fixed_heading', 1); -- hack +INSERT INTO variables (varname, value, information) VALUES ('fixed_heading', 1, 'manifest heading fix hack'); -- hack diff --git a/utils/sql/git/required/2018_02_18_bug_reports.sql b/utils/sql/git/required/2018_02_18_bug_reports.sql new file mode 100644 index 000000000..92afbd7c9 --- /dev/null +++ b/utils/sql/git/required/2018_02_18_bug_reports.sql @@ -0,0 +1,44 @@ +CREATE TABLE `bug_reports` ( + `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, + `zone` VARCHAR(32) NOT NULL DEFAULT 'Unknown', + `client_version_id` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `client_version_name` VARCHAR(24) NOT NULL DEFAULT 'Unknown', + `account_id` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `character_id` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `character_name` VARCHAR(64) NOT NULL DEFAULT 'Unknown', + `reporter_spoof` TINYINT(1) NOT NULL DEFAULT '1', + `category_id` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `category_name` VARCHAR(64) NOT NULL DEFAULT 'Other', + `reporter_name` VARCHAR(64) NOT NULL DEFAULT 'Unknown', + `ui_path` VARCHAR(128) NOT NULL DEFAULT 'Unknown', + `pos_x` FLOAT NOT NULL DEFAULT '0', + `pos_y` FLOAT NOT NULL DEFAULT '0', + `pos_z` FLOAT NOT NULL DEFAULT '0', + `heading` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `time_played` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `target_id` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `target_name` VARCHAR(64) NOT NULL DEFAULT 'Unknown', + `optional_info_mask` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `_can_duplicate` TINYINT(1) NOT NULL DEFAULT '0', + `_crash_bug` TINYINT(1) NOT NULL DEFAULT '0', + `_target_info` TINYINT(1) NOT NULL DEFAULT '0', + `_character_flags` TINYINT(1) NOT NULL DEFAULT '0', + `_unknown_value` TINYINT(1) NOT NULL DEFAULT '0', + `bug_report` VARCHAR(1024) NOT NULL DEFAULT '', + `system_info` VARCHAR(1024) NOT NULL DEFAULT '', + `report_datetime` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `bug_status` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + `last_review` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `last_reviewer` VARCHAR(64) NOT NULL DEFAULT 'None', + `reviewer_notes` VARCHAR(1024) NOT NULL DEFAULT '', + PRIMARY KEY (`id`), + UNIQUE INDEX `id` (`id`) +) +COLLATE='utf8_general_ci' +ENGINE=InnoDB +; + +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES +(1, 'Bugs:ReportingSystemActive', 'true', 'Activates bug reporting'), +(1, 'Bugs:UseOldReportingMethod', 'true', 'Forces the use of the old bug reporting system'), +(1, 'Bugs:DumpTargetEntity', 'false', 'Dumps the target entity, if one is provided'); diff --git a/utils/sql/git/required/2018_03_07_ucs_command.sql b/utils/sql/git/required/2018_03_07_ucs_command.sql new file mode 100644 index 000000000..fdad0dda1 --- /dev/null +++ b/utils/sql/git/required/2018_03_07_ucs_command.sql @@ -0,0 +1 @@ +INSERT INTO `command_settings` VALUES ('ucs', '0', ''); diff --git a/world/client.cpp b/world/client.cpp index 7210a65c4..81e815c3f 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -84,8 +84,9 @@ extern LoginServerList loginserverlist; extern ClientList client_list; extern EQEmu::Random emu_random; extern uint32 numclients; -extern volatile bool RunLoops; extern NatsManager nats; +extern volatile bool RunLoops; +extern volatile bool UCSServerAvailable_; Client::Client(EQStreamInterface* ieqs) : autobootup_timeout(RuleI(World, ZoneAutobootTimeoutMS)), @@ -894,53 +895,84 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { } QueuePacket(outapp); safe_delete(outapp); - + + // set mailkey - used for duration of character session int MailKey = emu_random.Int(1, INT_MAX); database.SetMailKey(charid, GetIP(), MailKey); + if (UCSServerAvailable_) { + const WorldConfig *Config = WorldConfig::get(); + std::string buffer; - char ConnectionType; + EQEmu::versions::UCSVersion ConnectionType = EQEmu::versions::ucsUnknown; - if (m_ClientVersionBit & EQEmu::versions::bit_UFAndLater) - ConnectionType = 'U'; - else if (m_ClientVersionBit & EQEmu::versions::bit_SoFAndLater) - ConnectionType = 'S'; - else - ConnectionType = 'C'; + // chat server packet + switch (GetClientVersion()) { + case EQEmu::versions::ClientVersion::Titanium: + ConnectionType = EQEmu::versions::ucsTitaniumChat; + break; + case EQEmu::versions::ClientVersion::SoF: + ConnectionType = EQEmu::versions::ucsSoFCombined; + break; + case EQEmu::versions::ClientVersion::SoD: + ConnectionType = EQEmu::versions::ucsSoDCombined; + break; + case EQEmu::versions::ClientVersion::UF: + ConnectionType = EQEmu::versions::ucsUFCombined; + break; + case EQEmu::versions::ClientVersion::RoF: + ConnectionType = EQEmu::versions::ucsRoFCombined; + break; + case EQEmu::versions::ClientVersion::RoF2: + ConnectionType = EQEmu::versions::ucsRoF2Combined; + break; + default: + ConnectionType = EQEmu::versions::ucsUnknown; + break; + } - auto outapp2 = new EQApplicationPacket(OP_SetChatServer); - char buffer[112]; + buffer = StringFormat("%s,%i,%s.%s,%c%08X", + Config->ChatHost.c_str(), + Config->ChatPort, + Config->ShortName.c_str(), + GetCharName(), + ConnectionType, + MailKey + ); - const WorldConfig *Config = WorldConfig::get(); + outapp = new EQApplicationPacket(OP_SetChatServer, (buffer.length() + 1)); + memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); + outapp->pBuffer[buffer.length()] = '\0'; - sprintf(buffer,"%s,%i,%s.%s,%c%08X", - Config->ChatHost.c_str(), - Config->ChatPort, - Config->ShortName.c_str(), - this->GetCharName(), ConnectionType, MailKey - ); - outapp2->size=strlen(buffer)+1; - outapp2->pBuffer = new uchar[outapp2->size]; - memcpy(outapp2->pBuffer,buffer,outapp2->size); - QueuePacket(outapp2); - safe_delete(outapp2); + QueuePacket(outapp); + safe_delete(outapp); - outapp2 = new EQApplicationPacket(OP_SetChatServer2); + // mail server packet + switch (GetClientVersion()) { + case EQEmu::versions::ClientVersion::Titanium: + ConnectionType = EQEmu::versions::ucsTitaniumMail; + break; + default: + // retain value from previous switch + break; + } - if (m_ClientVersionBit & EQEmu::versions::bit_TitaniumAndEarlier) - ConnectionType = 'M'; + buffer = StringFormat("%s,%i,%s.%s,%c%08X", + Config->MailHost.c_str(), + Config->MailPort, + Config->ShortName.c_str(), + GetCharName(), + ConnectionType, + MailKey + ); - sprintf(buffer,"%s,%i,%s.%s,%c%08X", - Config->MailHost.c_str(), - Config->MailPort, - Config->ShortName.c_str(), - this->GetCharName(), ConnectionType, MailKey - ); - outapp2->size=strlen(buffer)+1; - outapp2->pBuffer = new uchar[outapp2->size]; - memcpy(outapp2->pBuffer,buffer,outapp2->size); - QueuePacket(outapp2); - safe_delete(outapp2); + outapp = new EQApplicationPacket(OP_SetChatServer2, (buffer.length() + 1)); + memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); + outapp->pBuffer[buffer.length()] = '\0'; + + QueuePacket(outapp); + safe_delete(outapp); + } EnterWorld(); diff --git a/world/client.h b/world/client.h index 88dc27c57..d36dc692d 100644 --- a/world/client.h +++ b/world/client.h @@ -68,6 +68,7 @@ public: inline const char* GetLSKey() { if (cle) { return cle->GetLSKey(); } return "NOKEY"; } inline uint32 GetCharID() { return charid; } inline const char* GetCharName() { return char_name; } + inline EQEmu::versions::ClientVersion GetClientVersion() { return m_ClientVersion; } inline ClientListEntry* GetCLE() { return cle; } inline void SetCLE(ClientListEntry* iCLE) { cle = iCLE; } private: diff --git a/world/net.cpp b/world/net.cpp index ff1750140..646335ece 100644 --- a/world/net.cpp +++ b/world/net.cpp @@ -465,6 +465,8 @@ int main(int argc, char** argv) { connection->Handle()->RemoteIP(), connection->Handle()->RemotePort(), connection->GetUUID()); UCSLink.SetConnection(connection); + + zoneserver_list.UpdateUCSServerAvailable(); }); server_connection->OnConnectionRemoved("UCS", [](std::shared_ptr connection) { @@ -472,6 +474,8 @@ int main(int argc, char** argv) { connection->GetUUID()); UCSLink.SetConnection(nullptr); + + zoneserver_list.UpdateUCSServerAvailable(false); }); server_connection->OnConnectionIdentified("WebInterface", [](std::shared_ptr connection) { diff --git a/world/worlddb.cpp b/world/worlddb.cpp index 0b118ff2e..cbf901cf7 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -516,23 +516,6 @@ void WorldDatabase::GetLauncherList(std::vector &rl) { } -void WorldDatabase::SetMailKey(int CharID, int IPAddress, int MailKey) -{ - char MailKeyString[17]; - - if(RuleB(Chat, EnableMailKeyIPVerification) == true) - sprintf(MailKeyString, "%08X%08X", IPAddress, MailKey); - else - sprintf(MailKeyString, "%08X", MailKey); - - std::string query = StringFormat("UPDATE character_data SET mailkey = '%s' WHERE id = '%i'", - MailKeyString, CharID); - auto results = QueryDatabase(query); - if (!results.Success()) - Log(Logs::General, Logs::Error, "WorldDatabase::SetMailKey(%i, %s) : %s", CharID, MailKeyString, results.ErrorMessage().c_str()); - -} - bool WorldDatabase::GetCharacterLevel(const char *name, int &level) { std::string query = StringFormat("SELECT level FROM character_data WHERE name = '%s'", name); diff --git a/world/worlddb.h b/world/worlddb.h index b0c2ff221..2afd69920 100644 --- a/world/worlddb.h +++ b/world/worlddb.h @@ -34,7 +34,6 @@ public: int MoveCharacterToBind(int CharID, uint8 bindnum = 0); void GetLauncherList(std::vector &result); - void SetMailKey(int CharID, int IPAddress, int MailKey); bool GetCharacterLevel(const char *name, int &level); bool LoadCharacterCreateAllocations(); diff --git a/world/zonelist.cpp b/world/zonelist.cpp index a1f0a60ff..d86f727f6 100644 --- a/world/zonelist.cpp +++ b/world/zonelist.cpp @@ -32,6 +32,7 @@ extern uint32 numzones; extern bool holdzones; extern EQEmu::Random emu_random; extern WebInterfaceList web_interface; +volatile bool UCSServerAvailable_ = false; void CatchSignal(int sig_num); ZSList::ZSList() @@ -669,6 +670,16 @@ void ZSList::GetZoneIDList(std::vector &zones) { } } +void ZSList::UpdateUCSServerAvailable(bool ucss_available) { + UCSServerAvailable_ = ucss_available; + auto outapp = new ServerPacket(ServerOP_UCSServerStatusReply, sizeof(UCSServerStatus_Struct)); + auto ucsss = (UCSServerStatus_Struct*)outapp->pBuffer; + ucsss->available = (ucss_available ? 1 : 0); + ucsss->timestamp = Timer::GetCurrentTime(); + SendPacket(outapp); + safe_delete(outapp); +} + void ZSList::WorldShutDown(uint32 time, uint32 interval) { if (time > 0) { diff --git a/world/zonelist.h b/world/zonelist.h index e152e5046..9911b87e0 100644 --- a/world/zonelist.h +++ b/world/zonelist.h @@ -54,7 +54,8 @@ public: Timer* reminder; void NextGroupIDs(uint32 &start, uint32 &end); void SendLSZones(); - uint16 GetAvailableZonePort(); + uint16 GetAvailableZonePort(); + void UpdateUCSServerAvailable(bool ucss_available = true); int GetZoneCount(); void GetZoneIDList(std::vector &zones); diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 2ef893018..1d4dc8cbe 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -42,6 +42,7 @@ extern GroupLFPList LFPGroupList; extern ZSList zoneserver_list; extern LoginServerList loginserverlist; extern volatile bool RunLoops; +extern volatile bool UCSServerAvailable_; extern AdventureManager adventure_manager; extern UCSConnection UCSLink; extern QueryServConnection QSLink; @@ -1271,6 +1272,24 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { UCSLink.SendPacket(pack); break; } + + case ServerOP_UCSServerStatusRequest: + { + auto ucsss = (UCSServerStatus_Struct*)pack->pBuffer; + auto zs = zoneserver_list.FindByPort(ucsss->port); + if (!zs) + break; + + auto outapp = new ServerPacket(ServerOP_UCSServerStatusReply, sizeof(UCSServerStatus_Struct)); + ucsss = (UCSServerStatus_Struct*)outapp->pBuffer; + ucsss->available = (UCSServerAvailable_ ? 1 : 0); + ucsss->timestamp = Timer::GetCurrentTime(); + zs->SendPacket(outapp); + safe_delete(outapp); + + break; + } + case ServerOP_QSSendQuery: case ServerOP_QueryServGeneric: case ServerOP_Speech: diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index c8d9ed998..c6d020894 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -28,6 +28,7 @@ SET(zone_sources encounter.cpp entity.cpp exp.cpp + fastmath.cpp fearpath.cpp forage.cpp groups.cpp @@ -159,6 +160,7 @@ SET(zone_headers entity.h errmsg.h event_codes.h + fastmath.h forage.h global_loot_manager.h groups.h diff --git a/zone/attack.cpp b/zone/attack.cpp index 05155cdfc..33d61c126 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -33,6 +33,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "zone.h" #include "lua_parser.h" #include "nats_manager.h" +#include "fastmath.h" #include #include @@ -44,6 +45,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA extern QueryServ* QServ; extern WorldServer worldserver; +extern FastMath g_Math; #ifdef _WINDOWS #define snprintf _snprintf @@ -3614,26 +3616,35 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const a->special = 2; else a->special = 0; - a->meleepush_xy = attacker ? attacker->GetHeading() : 0.0f; + a->hit_heading = attacker ? attacker->GetHeading() : 0.0f; if (RuleB(Combat, MeleePush) && damage > 0 && !IsRooted() && (IsClient() || zone->random.Roll(RuleI(Combat, MeleePushChance)))) { a->force = EQEmu::skills::GetSkillMeleePushForce(skill_used); + if (IsNPC()) { + if (attacker->IsNPC()) + a->force = 0.0f; // 2013 change that disabled NPC vs NPC push + else + a->force *= 0.10f; // force against NPCs is divided by 10 I guess? ex bash is 0.3, parsed 0.03 against an NPC + } // update NPC stuff - auto new_pos = glm::vec3(m_Position.x + (a->force * std::cos(a->meleepush_xy) + m_Delta.x), - m_Position.y + (a->force * std::sin(a->meleepush_xy) + m_Delta.y), m_Position.z); - if (zone->zonemap && zone->zonemap->CheckLoS(glm::vec3(m_Position), new_pos)) { // If we have LoS on the new loc it should be reachable. - if (IsNPC()) { - // Is this adequate? + if (a->force != 0.0f) { + auto new_pos = glm::vec3( + m_Position.x + (a->force * g_Math.FastSin(a->hit_heading) + m_Delta.x), + m_Position.y + (a->force * g_Math.FastCos(a->hit_heading) + m_Delta.y), m_Position.z); + if ((!IsNPC() || position_update_melee_push_timer.Check()) && zone->zonemap && + zone->zonemap->CheckLoS( + glm::vec3(m_Position), + new_pos)) { // If we have LoS on the new loc it should be reachable. + if (IsNPC()) { + // Is this adequate? - Teleport(new_pos); - if (position_update_melee_push_timer.Check()) { + Teleport(new_pos); SendPositionUpdate(); } + } else { + a->force = 0.0f; // we couldn't move there, so lets not } } - else { - a->force = 0.0f; // we couldn't move there, so lets not - } } //Note: if players can become pets, they will not receive damage messages of their own diff --git a/zone/aura.cpp b/zone/aura.cpp index 8c54b75ee..8a885c67b 100644 --- a/zone/aura.cpp +++ b/zone/aura.cpp @@ -616,6 +616,7 @@ bool Aura::Process() it = spawned_for.erase(it); } } + safe_delete(app); } // TODO: waypoints? @@ -757,6 +758,8 @@ void Mob::MakeAura(uint16 spell_id) auto npc = new Aura(npc_type, this, record); npc->SetAuraID(spell_id); + if (trap) + npc->TryMoveAlong(5.0f, 0.0f, false); // try to place 5 units in front entity_list.AddNPC(npc, false); if (trap) diff --git a/zone/bot.cpp b/zone/bot.cpp index b0435dbe4..8751d40f7 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3053,7 +3053,7 @@ void Bot::Depop() { NPC::Depop(false); } -void Bot::Spawn(Client* botCharacterOwner) { +bool Bot::Spawn(Client* botCharacterOwner) { if(GetBotID() > 0 && _botOwnerCharacterID > 0 && botCharacterOwner && botCharacterOwner->CharacterID() == _botOwnerCharacterID) { // Rename the bot name to make sure that Mob::GetName() matches Mob::GetCleanName() so we dont have a bot named "Jesuschrist001" strcpy(name, GetCleanName()); @@ -3097,7 +3097,11 @@ void Bot::Spawn(Client* botCharacterOwner) { this->SendWearChange(materialFromSlot); } } + + return true; } + + return false; } // Deletes the inventory record for the specified item from the database for this bot. @@ -3246,16 +3250,20 @@ void Bot::LoadAndSpawnAllZonedBots(Client* botOwner) { if(!ActiveBots.empty()) { for(std::list::iterator itr = ActiveBots.begin(); itr != ActiveBots.end(); ++itr) { Bot* activeBot = Bot::LoadBot(*itr); + if (!activeBot) + continue; - if(activeBot) { - activeBot->Spawn(botOwner); - g->UpdatePlayer(activeBot); - // follow the bot owner, not the group leader we just zoned with our owner. - if(g->IsGroupMember(botOwner) && g->IsGroupMember(activeBot)) - activeBot->SetFollowID(botOwner->GetID()); + if (!activeBot->Spawn(botOwner)) { + safe_delete(activeBot); + continue; } - if(activeBot && !botOwner->HasGroup()) + g->UpdatePlayer(activeBot); + // follow the bot owner, not the group leader we just zoned with our owner. + if (g->IsGroupMember(botOwner) && g->IsGroupMember(activeBot)) + activeBot->SetFollowID(botOwner->GetID()); + + if(!botOwner->HasGroup()) database.SetGroupID(activeBot->GetCleanName(), 0, activeBot->GetBotID()); } } @@ -8886,6 +8894,8 @@ bool Bot::DyeArmor(int16 slot_id, uint32 rgb, bool all_flag, bool save_flag) std::string Bot::CreateSayLink(Client* c, const char* message, const char* name) { + // TODO: review + int saylink_size = strlen(message); char* escaped_string = new char[saylink_size * 2]; diff --git a/zone/bot.h b/zone/bot.h index c69405011..1d38e5f57 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -275,7 +275,7 @@ public: static bool IsValidRaceClassCombo(uint16 r, uint8 c); bool IsValidName(); static bool IsValidName(std::string& name); - void Spawn(Client* botCharacterOwner); + bool Spawn(Client* botCharacterOwner); virtual void SetLevel(uint8 in_level, bool command = false); virtual void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho); virtual bool Process(); diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index e94825929..a91624df1 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -5049,7 +5049,11 @@ void bot_subcommand_bot_spawn(Client *c, const Seperator *sep) return; } - my_bot->Spawn(c); + if (!my_bot->Spawn(c)) { + c->Message(m_fail, "Failed to spawn bot '%s' (id: %i)", bot_name.c_str(), bot_id); + safe_delete(my_bot); + return; + } static const char* bot_spawn_message[16] = { "A solid weapon is my ally!", // WARRIOR / 'generic' @@ -5805,18 +5809,22 @@ void bot_subcommand_botgroup_load(Client *c, const Seperator *sep) return; } if (!leader_id) { - c->Message(m_fail, "Can not locate bot-group leader id for '%s'", botgroup_name_arg.c_str()); + c->Message(m_fail, "Cannot locate bot-group leader id for '%s'", botgroup_name_arg.c_str()); return; } auto botgroup_leader = Bot::LoadBot(leader_id); if (!botgroup_leader) { - c->Message(m_fail, "Could not spawn bot-group leader for '%s'", botgroup_name_arg.c_str()); + c->Message(m_fail, "Could not load bot-group leader for '%s'", botgroup_name_arg.c_str()); safe_delete(botgroup_leader); return; } - botgroup_leader->Spawn(c); + if (!botgroup_leader->Spawn(c)) { + c->Message(m_fail, "Could not spawn bot-group leader %s for '%s'", botgroup_leader->GetName(), botgroup_name_arg.c_str()); + safe_delete(botgroup_leader); + return; + } Group* group_inst = new Group(botgroup_leader); @@ -5835,7 +5843,12 @@ void bot_subcommand_botgroup_load(Client *c, const Seperator *sep) return; } - botgroup_member->Spawn(c); + if (!botgroup_member->Spawn(c)) { + c->Message(m_fail, "Could not spawn bot '%s' (id: %i)", botgroup_member->GetName(), member_iter); + safe_delete(botgroup_member); + return; + } + Bot::AddBotToGroup(botgroup_member, group_inst); } @@ -7072,7 +7085,6 @@ void bot_subcommand_inventory_list(Client *c, const Seperator *sep) const EQEmu::ItemData* item = nullptr; bool is2Hweapon = false; - std::string item_link; EQEmu::SayLinkEngine linker; linker.SetLinkType(EQEmu::saylink::SayLinkItemInst); @@ -7093,8 +7105,7 @@ void bot_subcommand_inventory_list(Client *c, const Seperator *sep) } linker.SetItemInst(inst); - item_link = linker.GenerateLink(); - c->Message(m_message, "Using %s in my %s (slot %i)", item_link.c_str(), GetBotEquipSlotName(i), (i == 22 ? EQEmu::inventory::slotPowerSource : i)); + c->Message(m_message, "Using %s in my %s (slot %i)", linker.GenerateLink().c_str(), GetBotEquipSlotName(i), (i == 22 ? EQEmu::inventory::slotPowerSource : i)); ++inventory_count; } @@ -7237,8 +7248,8 @@ void bot_subcommand_inventory_window(Client *c, const Seperator *sep) std::string window_text; //std::string item_link; - //Client::TextLink linker; - //linker.SetLinkType(linker.linkItemInst); + //EQEmu::SayLinkEngine linker; + //linker.SetLinkType(EQEmu::saylink::SayLinkItemInst); for (int i = EQEmu::legacy::EQUIPMENT_BEGIN; i <= (EQEmu::legacy::EQUIPMENT_END + 1); ++i) { const EQEmu::ItemData* item = nullptr; diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index d8511c54c..4c5aabb82 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -273,7 +273,10 @@ bool BotDatabase::LoadBotID(const uint32 owner_id, const std::string& bot_name, if (!owner_id || bot_name.empty()) return false; - query = StringFormat("SELECT `bot_id` FROM `bot_data` WHERE `name` = '%s' LIMIT 1", bot_name.c_str()); + query = StringFormat( + "SELECT `bot_id` FROM `bot_data` WHERE `owner_id` = '%u' AND `name` = '%s' LIMIT 1", + owner_id, bot_name.c_str() + ); auto results = QueryDatabase(query); if (!results.Success()) return false; diff --git a/zone/client.cpp b/zone/client.cpp index 69c43ef5a..16157c807 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -254,7 +254,7 @@ Client::Client(EQStreamInterface* ieqs) mercSlot = 0; InitializeMercInfo(); SetMerc(0); - + if (RuleI(World, PVPMinLevel) > 0 && level >= RuleI(World, PVPMinLevel) && m_pp.pvp == 0) SetPVP(true, false); logging_enabled = CLIENT_DEFAULT_LOGGING_ENABLED; //for good measure: @@ -337,6 +337,7 @@ Client::Client(EQStreamInterface* ieqs) m_pp.InnateSkills[i] = InnateDisabled; temp_pvp = false; + is_client_moving = false; AI_Init(); } @@ -7907,7 +7908,7 @@ void Client::GarbleMessage(char *message, uint8 variance) for (size_t i = 0; i < strlen(message); i++) { // Client expects hex values inside of a text link body if (message[i] == delimiter) { - if (!(delimiter_count & 1)) { i += EQEmu::legacy::TEXT_LINK_BODY_LENGTH; } + if (!(delimiter_count & 1)) { i += EQEmu::constants::SayLinkBodySize; } ++delimiter_count; continue; } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 2e43faf50..466b49b1b 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -75,6 +75,7 @@ extern NatsManager nats; typedef void (Client::*ClientPacketProc)(const EQApplicationPacket *app); + //Use a map for connecting opcodes since it dosent get used a lot and is sparse std::map ConnectingOpcodes; //Use a static array for connected, for speed @@ -318,6 +319,7 @@ void MapOpcodes() ConnectedOpcodes[OP_PurchaseLeadershipAA] = &Client::Handle_OP_PurchaseLeadershipAA; ConnectedOpcodes[OP_PVPLeaderBoardDetailsRequest] = &Client::Handle_OP_PVPLeaderBoardDetailsRequest; ConnectedOpcodes[OP_PVPLeaderBoardRequest] = &Client::Handle_OP_PVPLeaderBoardRequest; + ConnectedOpcodes[OP_QueryUCSServerStatus] = &Client::Handle_OP_QueryUCSServerStatus; ConnectedOpcodes[OP_RaidInvite] = &Client::Handle_OP_RaidCommand; ConnectedOpcodes[OP_RandomReq] = &Client::Handle_OP_RandomReq; ConnectedOpcodes[OP_ReadBook] = &Client::Handle_OP_ReadBook; @@ -794,7 +796,7 @@ void Client::CompleteConnect() } if (zone) - zone->weatherSend(); + zone->weatherSend(this); TotalKarma = database.GetKarma(AccountID()); SendDisciplineTimers(); @@ -2866,41 +2868,60 @@ void Client::Handle_OP_ApplyPoison(const EQApplicationPacket *app) DumpPacket(app); return; } + uint32 ApplyPoisonSuccessResult = 0; ApplyPoison_Struct* ApplyPoisonData = (ApplyPoison_Struct*)app->pBuffer; const EQEmu::ItemInstance* PrimaryWeapon = GetInv().GetItem(EQEmu::inventory::slotPrimary); const EQEmu::ItemInstance* SecondaryWeapon = GetInv().GetItem(EQEmu::inventory::slotSecondary); const EQEmu::ItemInstance* PoisonItemInstance = GetInv()[ApplyPoisonData->inventorySlot]; + const EQEmu::ItemData* poison=PoisonItemInstance->GetItem(); + const EQEmu::ItemData* primary=nullptr; + const EQEmu::ItemData* secondary=nullptr; + bool IsPoison = PoisonItemInstance && + (poison->ItemType == EQEmu::item::ItemTypePoison); - bool IsPoison = PoisonItemInstance && (PoisonItemInstance->GetItem()->ItemType == EQEmu::item::ItemTypePoison); - - if (!IsPoison) - { - Log(Logs::Detail, Logs::Spells, "Item used to cast spell effect from a poison item was missing from inventory slot %d " - "after casting, or is not a poison!", ApplyPoisonData->inventorySlot); - - Message(0, "Error: item not found for inventory slot #%i or is not a poison", ApplyPoisonData->inventorySlot); + if (PrimaryWeapon) { + primary=PrimaryWeapon->GetItem(); } - else if (GetClass() == ROGUE) - { - if ((PrimaryWeapon && PrimaryWeapon->GetItem()->ItemType == EQEmu::item::ItemType1HPiercing) || - (SecondaryWeapon && SecondaryWeapon->GetItem()->ItemType == EQEmu::item::ItemType1HPiercing)) { - float SuccessChance = (GetSkill(EQEmu::skills::SkillApplyPoison) + GetLevel()) / 400.0f; + + if (SecondaryWeapon) { + secondary=SecondaryWeapon->GetItem(); + } + + if (IsPoison && GetClass() == ROGUE) { + + // Live always checks for skillup, even when poison is too high + CheckIncreaseSkill(EQEmu::skills::SkillApplyPoison, nullptr, 10); + + if (poison->Proc.Level2 > GetLevel()) { + // Poison is too high to apply. + Message_StringID(clientMessageTradeskill, POISON_TOO_HIGH); + } + else if ((primary && + primary->ItemType == EQEmu::item::ItemType1HPiercing) || + (secondary && + secondary->ItemType == EQEmu::item::ItemType1HPiercing)) { + double ChanceRoll = zone->random.Real(0, 1); - CheckIncreaseSkill(EQEmu::skills::SkillApplyPoison, nullptr, 10); + // Poisons that use this skill (old world poisons) almost + // never fail to apply. I did 25 applies of a trivial 120+ + // poison with an apply skill of 48 and they all worked. + // Also did 25 straight poisons at apply skill 248 for very + // high end and they never failed. + // Apply poison ranging from 1-9, 28/30 worked for a level 18.. + // Poisons that don't proc until a level higher than the + // rogue simply won't apply at all, no skill check done. - if (ChanceRoll < SuccessChance) { + if (ChanceRoll < (.9 + GetLevel()/1000)) { ApplyPoisonSuccessResult = 1; - // NOTE: Someone may want to tweak the chance to proc the poison effect that is added to the weapon here. - // My thinking was that DEX should be apart of the calculation. - AddProcToWeapon(PoisonItemInstance->GetItem()->Proc.Effect, false, (GetDEX() / 100) + 103); + AddProcToWeapon(poison->Proc.Effect, false, + (GetDEX() / 100) + 103); } - - DeleteItemInInventory(ApplyPoisonData->inventorySlot, 1, true); - - Log(Logs::General, Logs::None, "Chance to Apply Poison was %f. Roll was %f. Result is %u.", SuccessChance, ChanceRoll, ApplyPoisonSuccessResult); } + + // Live always deletes the item, success or failure. Even if too high. + DeleteItemInInventory(ApplyPoisonData->inventorySlot, 1, true); } auto outapp = new EQApplicationPacket(OP_ApplyPoison, nullptr, sizeof(ApplyPoison_Struct)); @@ -3965,12 +3986,23 @@ void Client::Handle_OP_BuffRemoveRequest(const EQApplicationPacket *app) void Client::Handle_OP_Bug(const EQApplicationPacket *app) { - if (app->size != sizeof(BugStruct)) - printf("Wrong size of BugStruct got %d expected %zu!\n", app->size, sizeof(BugStruct)); - else { - BugStruct* bug = (BugStruct*)app->pBuffer; - database.UpdateBug(bug); + if (!RuleB(Bugs, ReportingSystemActive)) { + Message(0, "Bug reporting is disabled on this server."); + return; } + + if (app->size != sizeof(BugReport_Struct)) { + printf("Wrong size of BugReport_Struct got %d expected %zu!\n", app->size, sizeof(BugReport_Struct)); + } + else { + BugReport_Struct* bug_report = (BugReport_Struct*)app->pBuffer; + + if (RuleB(Bugs, UseOldReportingMethod)) + database.RegisterBug(bug_report); + else + database.RegisterBug(this, bug_report); + } + return; } @@ -6716,7 +6748,11 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) // Inviter and Invitee are in the same zone if (inviter != nullptr && inviter->IsClient()) { - if (GroupFollow(inviter->CastToClient())) + if (!inviter->CastToClient()->Connected()) + { + Log(Logs::General, Logs::Error, "%s attempted to join group while leader %s was zoning.", GetName(), inviter->GetName()); + } + else if (GroupFollow(inviter->CastToClient())) { strn0cpy(gf->name1, inviter->GetName(), sizeof(gf->name1)); strn0cpy(gf->name2, GetName(), sizeof(gf->name2)); @@ -10573,11 +10609,8 @@ void Client::Handle_OP_Petition(const EQApplicationPacket *app) void Client::Handle_OP_PetitionBug(const EQApplicationPacket *app) { - if (app->size != sizeof(PetitionBug_Struct)) - printf("Wrong size of BugStruct! Expected: %zu, Got: %i\n", sizeof(PetitionBug_Struct), app->size); - else { - Message(0, "Petition Bugs are not supported, please use /bug."); - } + Message(0, "Petition Bugs are not supported, please use /bug."); + return; } @@ -10997,6 +11030,84 @@ void Client::Handle_OP_PVPLeaderBoardRequest(const EQApplicationPacket *app) safe_delete(outapp); } +void Client::Handle_OP_QueryUCSServerStatus(const EQApplicationPacket *app) +{ + if (zone->IsUCSServerAvailable()) { + EQApplicationPacket* outapp = nullptr; + std::string buffer; + + std::string MailKey = database.GetMailKey(CharacterID(), true); + EQEmu::versions::UCSVersion ConnectionType = EQEmu::versions::ucsUnknown; + + // chat server packet + switch (ClientVersion()) { + case EQEmu::versions::ClientVersion::Titanium: + ConnectionType = EQEmu::versions::ucsTitaniumChat; + break; + case EQEmu::versions::ClientVersion::SoF: + ConnectionType = EQEmu::versions::ucsSoFCombined; + break; + case EQEmu::versions::ClientVersion::SoD: + ConnectionType = EQEmu::versions::ucsSoDCombined; + break; + case EQEmu::versions::ClientVersion::UF: + ConnectionType = EQEmu::versions::ucsUFCombined; + break; + case EQEmu::versions::ClientVersion::RoF: + ConnectionType = EQEmu::versions::ucsRoFCombined; + break; + case EQEmu::versions::ClientVersion::RoF2: + ConnectionType = EQEmu::versions::ucsRoF2Combined; + break; + default: + ConnectionType = EQEmu::versions::ucsUnknown; + break; + } + + buffer = StringFormat("%s,%i,%s.%s,%c%s", + Config->ChatHost.c_str(), + Config->ChatPort, + Config->ShortName.c_str(), + GetName(), + ConnectionType, + MailKey.c_str() + ); + + outapp = new EQApplicationPacket(OP_SetChatServer, (buffer.length() + 1)); + memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); + outapp->pBuffer[buffer.length()] = '\0'; + + QueuePacket(outapp); + safe_delete(outapp); + + // mail server packet + switch (ClientVersion()) { + case EQEmu::versions::ClientVersion::Titanium: + ConnectionType = EQEmu::versions::ucsTitaniumMail; + break; + default: + // retain value from previous switch + break; + } + + buffer = StringFormat("%s,%i,%s.%s,%c%s", + Config->MailHost.c_str(), + Config->MailPort, + Config->ShortName.c_str(), + GetName(), + ConnectionType, + MailKey.c_str() + ); + + outapp = new EQApplicationPacket(OP_SetChatServer2, (buffer.length() + 1)); + memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); + outapp->pBuffer[buffer.length()] = '\0'; + + QueuePacket(outapp); + safe_delete(outapp); + } +} + void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) { if (app->size < sizeof(RaidGeneral_Struct)) { diff --git a/zone/client_packet.h b/zone/client_packet.h index 1e7981ed9..4f9902599 100644 --- a/zone/client_packet.h +++ b/zone/client_packet.h @@ -228,6 +228,7 @@ void Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app); void Handle_OP_PVPLeaderBoardDetailsRequest(const EQApplicationPacket *app); void Handle_OP_PVPLeaderBoardRequest(const EQApplicationPacket *app); + void Handle_OP_QueryUCSServerStatus(const EQApplicationPacket *app); void Handle_OP_RaidCommand(const EQApplicationPacket *app); void Handle_OP_RandomReq(const EQApplicationPacket *app); void Handle_OP_ReadBook(const EQApplicationPacket *app); diff --git a/zone/command.cpp b/zone/command.cpp index a8e8ff7b8..3e2ae3d3a 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -391,6 +391,7 @@ int command_init(void) command_add("traindisc", "[level] - Trains all the disciplines usable by the target, up to level specified. (may freeze client for a few seconds)", 150, command_traindisc) || command_add("trapinfo", "- Gets infomation about the traps currently spawned in the zone.", 81, command_trapinfo) || command_add("tune", "Calculate ideal statical values related to combat.", 100, command_tune) || + command_add("ucs", "- Attempts to reconnect to the UCS server", 0, command_ucs) || command_add("undyeme", "- Remove dye from all of your armor slots", 0, command_undyeme) || command_add("unfreeze", "- Unfreeze your target", 80, command_unfreeze) || command_add("unlock", "- Unlock the worldserver", 150, command_unlock) || @@ -2326,14 +2327,18 @@ void command_race(Client *c, const Seperator *sep) { Mob *t=c->CastToMob(); - // Need to figure out max race for LoY/LDoN: going with upper bound of 500 now for testing - if (sep->IsNumber(1) && atoi(sep->arg[1]) >= 0 && atoi(sep->arg[1]) <= 724) { - if ((c->GetTarget()) && c->Admin() >= commandRaceOthers) - t=c->GetTarget(); - t->SendIllusionPacket(atoi(sep->arg[1])); + if (sep->IsNumber(1)) { + auto race = atoi(sep->arg[1]); + if ((race >= 0 && race <= 732) || (race >= 2253 && race <= 2259)) { + if ((c->GetTarget()) && c->Admin() >= commandRaceOthers) + t = c->GetTarget(); + t->SendIllusionPacket(race); + } else { + c->Message(0, "Usage: #race [0-732, 2253-2259] (0 for back to normal)"); + } + } else { + c->Message(0, "Usage: #race [0-732, 2253-2259] (0 for back to normal)"); } - else - c->Message(0, "Usage: #race [0-724] (0 for back to normal)"); } void command_gender(Client *c, const Seperator *sep) @@ -2553,7 +2558,7 @@ void command_peekinv(Client *c, const Seperator *sep) const EQEmu::ItemInstance* inst_main = nullptr; const EQEmu::ItemInstance* inst_sub = nullptr; const EQEmu::ItemData* item_data = nullptr; - std::string item_link; + EQEmu::SayLinkEngine linker; linker.SetLinkType(EQEmu::saylink::SayLinkItemInst); @@ -2565,10 +2570,8 @@ void command_peekinv(Client *c, const Seperator *sep) item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem(); linker.SetItemInst(inst_main); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "WornSlot: %i, Item: %i (%s), Charges: %i", - indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); + indexMain, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); } if ((scopeWhere & peekWorn) && (targetClient->ClientVersion() >= EQEmu::versions::ClientVersion::SoF)) { @@ -2576,10 +2579,8 @@ void command_peekinv(Client *c, const Seperator *sep) item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem(); linker.SetItemInst(inst_main); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "WornSlot: %i, Item: %i (%s), Charges: %i", - EQEmu::inventory::slotPowerSource, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); + EQEmu::inventory::slotPowerSource, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); } // inv @@ -2588,20 +2589,16 @@ void command_peekinv(Client *c, const Seperator *sep) item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem(); linker.SetItemInst(inst_main); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "InvSlot: %i, Item: %i (%s), Charges: %i", - indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); + indexMain, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); for (uint8 indexSub = EQEmu::inventory::containerBegin; inst_main && inst_main->IsClassBag() && (indexSub < EQEmu::inventory::ContainerCount); ++indexSub) { inst_sub = inst_main->GetItem(indexSub); item_data = (inst_sub == nullptr) ? nullptr : inst_sub->GetItem(); linker.SetItemInst(inst_sub); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), " InvBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", - EQEmu::InventoryProfile::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); + EQEmu::InventoryProfile::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); } } @@ -2610,10 +2607,8 @@ void command_peekinv(Client *c, const Seperator *sep) if (targetClient->GetInv().CursorEmpty()) { linker.SetItemInst(nullptr); - item_link = linker.GenerateLink(); - c->Message(1, "CursorSlot: %i, Item: %i (%s), Charges: %i", - EQEmu::inventory::slotCursor, 0, item_link.c_str(), 0); + EQEmu::inventory::slotCursor, 0, linker.GenerateLink().c_str(), 0); } else { int cursorDepth = 0; @@ -2622,20 +2617,16 @@ void command_peekinv(Client *c, const Seperator *sep) item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem(); linker.SetItemInst(inst_main); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "CursorSlot: %i, Depth: %i, Item: %i (%s), Charges: %i", - EQEmu::inventory::slotCursor, cursorDepth, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); + EQEmu::inventory::slotCursor, cursorDepth, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); for (uint8 indexSub = EQEmu::inventory::containerBegin; (cursorDepth == 0) && inst_main && inst_main->IsClassBag() && (indexSub < EQEmu::inventory::ContainerCount); ++indexSub) { inst_sub = inst_main->GetItem(indexSub); item_data = (inst_sub == nullptr) ? nullptr : inst_sub->GetItem(); linker.SetItemInst(inst_sub); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), " CursorBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", - EQEmu::InventoryProfile::CalcSlotId(EQEmu::inventory::slotCursor, indexSub), EQEmu::inventory::slotCursor, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); + EQEmu::InventoryProfile::CalcSlotId(EQEmu::inventory::slotCursor, indexSub), EQEmu::inventory::slotCursor, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); } } } @@ -2647,10 +2638,8 @@ void command_peekinv(Client *c, const Seperator *sep) item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem(); linker.SetItemInst(inst_main); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "TributeSlot: %i, Item: %i (%s), Charges: %i", - indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); + indexMain, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); } // bank @@ -2659,20 +2648,16 @@ void command_peekinv(Client *c, const Seperator *sep) item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem(); linker.SetItemInst(inst_main); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "BankSlot: %i, Item: %i (%s), Charges: %i", - indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); + indexMain, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); for (uint8 indexSub = EQEmu::inventory::containerBegin; inst_main && inst_main->IsClassBag() && (indexSub < EQEmu::inventory::ContainerCount); ++indexSub) { inst_sub = inst_main->GetItem(indexSub); item_data = (inst_sub == nullptr) ? nullptr : inst_sub->GetItem(); linker.SetItemInst(inst_sub); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), " BankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", - EQEmu::InventoryProfile::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); + EQEmu::InventoryProfile::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); } } @@ -2681,20 +2666,16 @@ void command_peekinv(Client *c, const Seperator *sep) item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem(); linker.SetItemInst(inst_main); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "SharedBankSlot: %i, Item: %i (%s), Charges: %i", - indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); + indexMain, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); for (uint8 indexSub = EQEmu::inventory::containerBegin; inst_main && inst_main->IsClassBag() && (indexSub < EQEmu::inventory::ContainerCount); ++indexSub) { inst_sub = inst_main->GetItem(indexSub); item_data = (inst_sub == nullptr) ? nullptr : inst_sub->GetItem(); linker.SetItemInst(inst_sub); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), " SharedBankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", - EQEmu::InventoryProfile::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); + EQEmu::InventoryProfile::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); } } @@ -2704,20 +2685,16 @@ void command_peekinv(Client *c, const Seperator *sep) item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem(); linker.SetItemInst(inst_main); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "TradeSlot: %i, Item: %i (%s), Charges: %i", - indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); + indexMain, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); for (uint8 indexSub = EQEmu::inventory::containerBegin; inst_main && inst_main->IsClassBag() && (indexSub < EQEmu::inventory::ContainerCount); ++indexSub) { inst_sub = inst_main->GetItem(indexSub); item_data = (inst_sub == nullptr) ? nullptr : inst_sub->GetItem(); linker.SetItemInst(inst_sub); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), " TradeBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", - EQEmu::InventoryProfile::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); + EQEmu::InventoryProfile::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); } } @@ -2736,20 +2713,16 @@ void command_peekinv(Client *c, const Seperator *sep) item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem(); linker.SetItemInst(inst_main); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), "WorldSlot: %i, Item: %i (%s), Charges: %i", - (EQEmu::legacy::WORLD_BEGIN + indexMain), ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); + (EQEmu::legacy::WORLD_BEGIN + indexMain), ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges())); for (uint8 indexSub = EQEmu::inventory::containerBegin; inst_main && inst_main->IsType(EQEmu::item::ItemClassBag) && (indexSub < EQEmu::inventory::ContainerCount); ++indexSub) { inst_sub = inst_main->GetItem(indexSub); item_data = (inst_sub == nullptr) ? nullptr : inst_sub->GetItem(); linker.SetItemInst(inst_sub); - item_link = linker.GenerateLink(); - c->Message((item_data == nullptr), " WorldBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", - INVALID_INDEX, indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); + INVALID_INDEX, indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), linker.GenerateLink().c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())); } } } @@ -4404,9 +4377,7 @@ void command_iteminfo(Client *c, const Seperator *sep) linker.SetLinkType(EQEmu::saylink::SayLinkItemInst); linker.SetItemInst(inst); - auto item_link = linker.GenerateLink(); - - c->Message(0, "*** Item Info for [%s] ***", item_link.c_str()); + c->Message(0, "*** Item Info for [%s] ***", linker.GenerateLink().c_str()); c->Message(0, ">> ID: %u, ItemUseType: %u, ItemClassType: %u", item->ID, item->ItemType, item->ItemClass); c->Message(0, ">> IDFile: '%s', IconID: %u", item->IDFile, item->Icon); c->Message(0, ">> Size: %u, Weight: %u, Price: %u, LDoNPrice: %u", item->Size, item->Weight, item->Price, item->LDoNPrice); @@ -5550,9 +5521,9 @@ void command_summonitem(Client *c, const Seperator *sep) std::string cmd_msg = sep->msg; size_t link_open = cmd_msg.find('\x12'); size_t link_close = cmd_msg.find_last_of('\x12'); - if (link_open != link_close && (cmd_msg.length() - link_open) > EQEmu::legacy::TEXT_LINK_BODY_LENGTH) { + if (link_open != link_close && (cmd_msg.length() - link_open) > EQEmu::constants::SayLinkBodySize) { EQEmu::SayLinkBody_Struct link_body; - EQEmu::saylink::DegenerateLinkBody(link_body, cmd_msg.substr(link_open + 1, EQEmu::legacy::TEXT_LINK_BODY_LENGTH)); + EQEmu::saylink::DegenerateLinkBody(link_body, cmd_msg.substr(link_open + 1, EQEmu::constants::SayLinkBodySize)); itemid = link_body.item_id; } else if (!sep->IsNumber(1)) { @@ -5661,7 +5632,6 @@ void command_itemsearch(Client *c, const Seperator *sep) const char *search_criteria=sep->argplus[1]; const EQEmu::ItemData* item = nullptr; - std::string item_link; EQEmu::SayLinkEngine linker; linker.SetLinkType(EQEmu::saylink::SayLinkItemData); @@ -5670,9 +5640,7 @@ void command_itemsearch(Client *c, const Seperator *sep) if (item) { linker.SetItemData(item); - item_link = linker.GenerateLink(); - - c->Message(0, "%u: %s", item->ID, item_link.c_str()); + c->Message(0, "%u: %s", item->ID, linker.GenerateLink().c_str()); } else { c->Message(0, "Item #%s not found", search_criteria); @@ -5695,9 +5663,7 @@ void command_itemsearch(Client *c, const Seperator *sep) if (pdest != nullptr) { linker.SetItemData(item); - item_link = linker.GenerateLink(); - - c->Message(0, "%u: %s", item->ID, item_link.c_str()); + c->Message(0, "%u: %s", item->ID, linker.GenerateLink().c_str()); ++count; } @@ -7201,6 +7167,90 @@ void command_undye(Client *c, const Seperator *sep) } } +void command_ucs(Client *c, const Seperator *sep) +{ + if (!c) + return; + + Log(Logs::Detail, Logs::UCS_Server, "Character %s attempting ucs reconnect while ucs server is %savailable", + c->GetName(), (zone->IsUCSServerAvailable() ? "" : "un")); + + if (zone->IsUCSServerAvailable()) { + EQApplicationPacket* outapp = nullptr; + std::string buffer; + + std::string MailKey = database.GetMailKey(c->CharacterID(), true); + EQEmu::versions::UCSVersion ConnectionType = EQEmu::versions::ucsUnknown; + + // chat server packet + switch (c->ClientVersion()) { + case EQEmu::versions::ClientVersion::Titanium: + ConnectionType = EQEmu::versions::ucsTitaniumChat; + break; + case EQEmu::versions::ClientVersion::SoF: + ConnectionType = EQEmu::versions::ucsSoFCombined; + break; + case EQEmu::versions::ClientVersion::SoD: + ConnectionType = EQEmu::versions::ucsSoDCombined; + break; + case EQEmu::versions::ClientVersion::UF: + ConnectionType = EQEmu::versions::ucsUFCombined; + break; + case EQEmu::versions::ClientVersion::RoF: + ConnectionType = EQEmu::versions::ucsRoFCombined; + break; + case EQEmu::versions::ClientVersion::RoF2: + ConnectionType = EQEmu::versions::ucsRoF2Combined; + break; + default: + ConnectionType = EQEmu::versions::ucsUnknown; + break; + } + + buffer = StringFormat("%s,%i,%s.%s,%c%s", + Config->ChatHost.c_str(), + Config->ChatPort, + Config->ShortName.c_str(), + c->GetName(), + ConnectionType, + MailKey.c_str() + ); + + outapp = new EQApplicationPacket(OP_SetChatServer, (buffer.length() + 1)); + memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); + outapp->pBuffer[buffer.length()] = '\0'; + + c->QueuePacket(outapp); + safe_delete(outapp); + + // mail server packet + switch (c->ClientVersion()) { + case EQEmu::versions::ClientVersion::Titanium: + ConnectionType = EQEmu::versions::ucsTitaniumMail; + break; + default: + // retain value from previous switch + break; + } + + buffer = StringFormat("%s,%i,%s.%s,%c%s", + Config->MailHost.c_str(), + Config->MailPort, + Config->ShortName.c_str(), + c->GetName(), + ConnectionType, + MailKey.c_str() + ); + + outapp = new EQApplicationPacket(OP_SetChatServer2, (buffer.length() + 1)); + memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); + outapp->pBuffer[buffer.length()] = '\0'; + + c->QueuePacket(outapp); + safe_delete(outapp); + } +} + void command_undyeme(Client *c, const Seperator *sep) { if(c) { @@ -10839,7 +10889,7 @@ void command_hotfix(Client *c, const Seperator *sep) { } worldserver.SendPacket(&pack); - c->Message(0, "Hotfix applied"); + if (c) c->Message(0, "Hotfix applied"); }); t1.detach(); diff --git a/zone/command.h b/zone/command.h index b8d688a54..580dd00ff 100644 --- a/zone/command.h +++ b/zone/command.h @@ -302,6 +302,7 @@ void command_titlesuffix(Client *c, const Seperator *sep); void command_traindisc(Client *c, const Seperator *sep); void command_trapinfo(Client* c, const Seperator *sep); void command_tune(Client *c, const Seperator *sep); +void command_ucs(Client *c, const Seperator *sep); void command_undye(Client *c, const Seperator *sep); void command_undyeme(Client *c, const Seperator *sep); void command_unfreeze(Client *c, const Seperator *sep); diff --git a/zone/corpse.cpp b/zone/corpse.cpp index a8f3e7ba1..63031ec45 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -1253,20 +1253,20 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) linker.SetLinkType(EQEmu::saylink::SayLinkItemInst); linker.SetItemInst(inst); - auto item_link = linker.GenerateLink(); + linker.GenerateLink(); - client->Message_StringID(MT_LootMessages, LOOTED_MESSAGE, item_link.c_str()); + client->Message_StringID(MT_LootMessages, LOOTED_MESSAGE, linker.Link().c_str()); if (!IsPlayerCorpse()) { Group *g = client->GetGroup(); if (g != nullptr) { g->GroupMessage_StringID(client, MT_LootMessages, OTHER_LOOTED_MESSAGE, - client->GetName(), item_link.c_str()); + client->GetName(), linker.Link().c_str()); } else { Raid *r = client->GetRaid(); if (r != nullptr) { r->RaidMessage_StringID(client, MT_LootMessages, OTHER_LOOTED_MESSAGE, - client->GetName(), item_link.c_str()); + client->GetName(), linker.Link().c_str()); } } } diff --git a/zone/exp.cpp b/zone/exp.cpp index be9d01c13..2902610a8 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -831,6 +831,8 @@ void Client::SetLevel(uint8 set_level, bool command) SetHP(CalcMaxHP()); // Why not, lets give them a free heal } + if (RuleI(World, PVPMinLevel) > 0 && level >= RuleI(World, PVPMinLevel) && m_pp.pvp == 0) SetPVP(true); + DoTributeUpdate(); SendHPUpdate(); SetMana(CalcMaxMana()); diff --git a/zone/fastmath.cpp b/zone/fastmath.cpp new file mode 100644 index 000000000..0da87dc87 --- /dev/null +++ b/zone/fastmath.cpp @@ -0,0 +1,35 @@ +#if defined(_MSC_VER) +#define _USE_MATH_DEFINES +#endif +#include +#include "fastmath.h" + +FastMath g_Math; + +// This should match EQ's sin/cos LUTs +// Some values didn't match on linux, but they were the "same" float :P +FastMath::FastMath() +{ + int ci = 0; + int si = 128; + float res; + do { + res = std::cos(static_cast(ci) * M_PI * 2 / 512); + lut_cos[ci] = res; + if (si == 512) + si = 0; + lut_sin[si] = res; + ++ci; + ++si; + } while (ci < 512); + + lut_sin[0] = 0.0f; + lut_sin[128] = 1.0f; + lut_sin[256] = 0.0f; + lut_sin[384] = -1.0f; + + lut_cos[0] = 1.0f; + lut_cos[128] = 0.0f; + lut_cos[384] = 0.0f; +} + diff --git a/zone/fastmath.h b/zone/fastmath.h new file mode 100644 index 000000000..083198e48 --- /dev/null +++ b/zone/fastmath.h @@ -0,0 +1,18 @@ +#ifndef FASTMATH_H +#define FASTMATH_H + +class FastMath +{ +private: + float lut_cos[512]; + float lut_sin[512]; + +public: + FastMath(); + + inline float FastSin(float a) { return lut_sin[static_cast(a) & 0x1ff]; } + inline float FastCos(float a) { return lut_cos[static_cast(a) & 0x1ff]; } + +}; + +#endif /* !FASTMATH_H */ diff --git a/zone/inventory.cpp b/zone/inventory.cpp index bdaa9e450..40060f2f4 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -1329,7 +1329,7 @@ int Client::GetItemLinkHash(const EQEmu::ItemInstance* inst) { return hash; } -// This appears to still be in use... The core of this should be incorporated into class Client::TextLink +// This appears to still be in use... The core of this should be incorporated into class EQEmu::SayLinkEngine void Client::SendItemLink(const EQEmu::ItemInstance* inst, bool send_to_all) { /* diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index bd5cddbcc..3793e6b9d 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -282,6 +282,16 @@ void Lua_Mob::GMMove(double x, double y, double z, double heading, bool send_upd self->GMMove(static_cast(x), static_cast(y), static_cast(z), static_cast(heading), send_update); } +void Lua_Mob::TryMoveAlong(float distance, float angle) { + Lua_Safe_Call_Void(); + self->TryMoveAlong(distance, angle); +} + +void Lua_Mob::TryMoveAlong(float distance, float angle, bool send) { + Lua_Safe_Call_Void(); + self->TryMoveAlong(distance, angle, send); +} + bool Lua_Mob::HasProcs() { Lua_Safe_Call_Bool(); return self->HasProcs(); @@ -2197,6 +2207,8 @@ luabind::scope lua_register_mob() { .def("GMMove", (void(Lua_Mob::*)(double,double,double))&Lua_Mob::GMMove) .def("GMMove", (void(Lua_Mob::*)(double,double,double,double))&Lua_Mob::GMMove) .def("GMMove", (void(Lua_Mob::*)(double,double,double,double,bool))&Lua_Mob::GMMove) + .def("TryMoveAlong", (void(Lua_Mob::*)(float,float))&Lua_Mob::TryMoveAlong) + .def("TryMoveAlong", (void(Lua_Mob::*)(float,float,bool))&Lua_Mob::TryMoveAlong) .def("HasProcs", &Lua_Mob::HasProcs) .def("IsInvisible", (bool(Lua_Mob::*)(void))&Lua_Mob::IsInvisible) .def("IsInvisible", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::IsInvisible) diff --git a/zone/lua_mob.h b/zone/lua_mob.h index 7aeb700e6..17dbc5c43 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -74,6 +74,8 @@ public: void GMMove(double x, double y, double z); void GMMove(double x, double y, double z, double heading); void GMMove(double x, double y, double z, double heading, bool send_update); + void TryMoveAlong(float distance, float heading); + void TryMoveAlong(float distance, float heading, bool send); bool HasProcs(); bool IsInvisible(); bool IsInvisible(Lua_Mob other); diff --git a/zone/map.cpp b/zone/map.cpp index 39f8ad3d7..478e9c55c 100644 --- a/zone/map.cpp +++ b/zone/map.cpp @@ -14,573 +14,548 @@ namespace EQEmu { - uint32 EstimateDeflateBuffer(uint32_t len) { - z_stream zstream; - memset(&zstream, 0, sizeof(zstream)); - - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - if (deflateInit(&zstream, Z_FINISH) != Z_OK) - return 0; +uint32 EstimateDeflateBuffer(uint32_t len) { + z_stream zstream; + memset(&zstream, 0, sizeof(zstream)); - return deflateBound(&zstream, len); - } + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + if (deflateInit(&zstream, Z_FINISH) != Z_OK) + return 0; - uint32_t DeflateData(const char *buffer, uint32_t len, char *out_buffer, uint32_t out_len_max) { - z_stream zstream; - memset(&zstream, 0, sizeof(zstream)); - int zerror; + return deflateBound(&zstream, len); +} - zstream.next_in = const_cast(reinterpret_cast(buffer)); - zstream.avail_in = len; - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - deflateInit(&zstream, Z_FINISH); +uint32_t DeflateData(const char *buffer, uint32_t len, char *out_buffer, uint32_t out_len_max) { + z_stream zstream; + memset(&zstream, 0, sizeof(zstream)); + int zerror; - zstream.next_out = reinterpret_cast(out_buffer); - zstream.avail_out = out_len_max; - zerror = deflate(&zstream, Z_FINISH); + zstream.next_in = const_cast(reinterpret_cast(buffer)); + zstream.avail_in = len; + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + deflateInit(&zstream, Z_FINISH); - if (zerror == Z_STREAM_END) - { - deflateEnd(&zstream); - return (uint32_t)zstream.total_out; - } - else - { - zerror = deflateEnd(&zstream); - return 0; - } - } + zstream.next_out = reinterpret_cast(out_buffer); + zstream.avail_out = out_len_max; + zerror = deflate(&zstream, Z_FINISH); - uint32 InflateData(const char* buffer, uint32 len, char* out_buffer, uint32 out_len_max) { - z_stream zstream; - int zerror = 0; - int i; - - zstream.next_in = const_cast(reinterpret_cast(buffer)); - zstream.avail_in = len; - zstream.next_out = reinterpret_cast(out_buffer);; - zstream.avail_out = out_len_max; - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - - i = inflateInit2(&zstream, 15); - if (i != Z_OK) { - return 0; - } - - zerror = inflate(&zstream, Z_FINISH); - if (zerror == Z_STREAM_END) { - inflateEnd(&zstream); - return zstream.total_out; - } - else { - if (zerror == -4 && zstream.msg == 0) - { - return 0; - } - - zerror = inflateEnd(&zstream); - return 0; - } - } - - struct Map::impl + if (zerror == Z_STREAM_END) { - RaycastMesh *rm; - }; + deflateEnd(&zstream); + return (uint32_t)zstream.total_out; + } + else + { + zerror = deflateEnd(&zstream); + return 0; + } +} - Map::Map() { - imp = nullptr; +uint32 InflateData(const char* buffer, uint32 len, char* out_buffer, uint32 out_len_max) { + z_stream zstream; + int zerror = 0; + int i; + + zstream.next_in = const_cast(reinterpret_cast(buffer)); + zstream.avail_in = len; + zstream.next_out = reinterpret_cast(out_buffer);; + zstream.avail_out = out_len_max; + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + + i = inflateInit2(&zstream, 15); + if (i != Z_OK) { + return 0; } - Map::~Map() { - if(imp) { - imp->rm->release(); - safe_delete(imp); - } + zerror = inflate(&zstream, Z_FINISH); + if (zerror == Z_STREAM_END) { + inflateEnd(&zstream); + return zstream.total_out; } - - float Map::FindBestZ(glm::vec3 &start, glm::vec3 *result) const { - if (!imp) - return BEST_Z_INVALID; - - glm::vec3 tmp; - if(!result) - result = &tmp; - - start.z += RuleI(Map, FindBestZHeightAdjust); - glm::vec3 from(start.x, start.y, start.z); - glm::vec3 to(start.x, start.y, BEST_Z_INVALID); - float hit_distance; - bool hit = false; - - hit = imp->rm->raycast((const RmReal*)&from, (const RmReal*)&to, (RmReal*)result, nullptr, &hit_distance); - if(hit) { - return result->z; - } - - // Find nearest Z above us - - to.z = -BEST_Z_INVALID; - hit = imp->rm->raycast((const RmReal*)&from, (const RmReal*)&to, (RmReal*)result, nullptr, &hit_distance); - if (hit) + else { + if (zerror == -4 && zstream.msg == 0) { - return result->z; + return 0; } - + + zerror = inflateEnd(&zstream); + return 0; + } +} + +struct Map::impl +{ + RaycastMesh *rm; +}; + +Map::Map() { + imp = nullptr; +} + +Map::~Map() { + if (imp) { + imp->rm->release(); + safe_delete(imp); + } +} + +float Map::FindBestZ(glm::vec3 &start, glm::vec3 *result) const { + if (!imp) return BEST_Z_INVALID; + + glm::vec3 tmp; + if (!result) + result = &tmp; + + start.z += RuleI(Map, FindBestZHeightAdjust); + glm::vec3 from(start.x, start.y, start.z); + glm::vec3 to(start.x, start.y, BEST_Z_INVALID); + float hit_distance; + bool hit = false; + + hit = imp->rm->raycast((const RmReal*)&from, (const RmReal*)&to, (RmReal*)result, nullptr, &hit_distance); + if (hit) { + return result->z; } - float Map::FindClosestZ(glm::vec3 &start, glm::vec3 *result) const { - // Unlike FindBestZ, this method finds the closest Z value above or below the specified point. - // - if (!imp) - return false; - - float ClosestZ = BEST_Z_INVALID; - - glm::vec3 tmp; - if (!result) - result = &tmp; - - glm::vec3 from(start.x, start.y, start.z); - glm::vec3 to(start.x, start.y, BEST_Z_INVALID); - float hit_distance; - bool hit = false; - - // first check is below us - hit = imp->rm->raycast((const RmReal*)&from, (const RmReal*)&to, (RmReal*)result, nullptr, &hit_distance); - if (hit) { - ClosestZ = result->z; - - } - - // Find nearest Z above us - to.z = -BEST_Z_INVALID; - hit = imp->rm->raycast((const RmReal*)&from, (const RmReal*)&to, (RmReal*)result, nullptr, &hit_distance); - if (hit) { - if (std::abs(from.z - result->z) < std::abs(ClosestZ - from.z)) - return result->z; - } + // Find nearest Z above us - return ClosestZ; - } - - bool Map::LineIntersectsZone(glm::vec3 start, glm::vec3 end, float step, glm::vec3 *result) const { - if(!imp) - return false; - return imp->rm->raycast((const RmReal*)&start, (const RmReal*)&end, (RmReal*)result, nullptr, nullptr); - } - - bool Map::LineIntersectsZoneNoZLeaps(glm::vec3 start, glm::vec3 end, float step_mag, glm::vec3 *result) const { - if (!imp) - return false; - - float z = BEST_Z_INVALID; - glm::vec3 step; - glm::vec3 cur; - cur.x = start.x; - cur.y = start.y; - cur.z = start.z; - - step.x = end.x - start.x; - step.y = end.y - start.y; - step.z = end.z - start.z; - float factor = step_mag / sqrt(step.x*step.x + step.y*step.y + step.z*step.z); - - step.x *= factor; - step.y *= factor; - step.z *= factor; - - int steps = 0; - - if (step.x > 0 && step.x < 0.001f) - step.x = 0.001f; - if (step.y > 0 && step.y < 0.001f) - step.y = 0.001f; - if (step.z > 0 && step.z < 0.001f) - step.z = 0.001f; - if (step.x < 0 && step.x > -0.001f) - step.x = -0.001f; - if (step.y < 0 && step.y > -0.001f) - step.y = -0.001f; - if (step.z < 0 && step.z > -0.001f) - step.z = -0.001f; - - //while we are not past end - //always do this once, even if start == end. - while(cur.x != end.x || cur.y != end.y || cur.z != end.z) - { - steps++; - glm::vec3 me; - me.x = cur.x; - me.y = cur.y; - me.z = cur.z; - glm::vec3 hit; - - float best_z = FindBestZ(me, &hit); - float diff = best_z - z; - diff = diff < 0 ? -diff : diff; - - if (z <= BEST_Z_INVALID || best_z <= BEST_Z_INVALID || diff < 12.0) - z = best_z; - else - return true; - - //look at current location - if(LineIntersectsZone(start, end, step_mag, result)) - { - return true; - } - - //move 1 step - if (cur.x != end.x) - cur.x += step.x; - if (cur.y != end.y) - cur.y += step.y; - if (cur.z != end.z) - cur.z += step.z; - - //watch for end conditions - if ( (cur.x > end.x && end.x >= start.x) || (cur.x < end.x && end.x <= start.x) || (step.x == 0) ) { - cur.x = end.x; - } - if ( (cur.y > end.y && end.y >= start.y) || (cur.y < end.y && end.y <= start.y) || (step.y == 0) ) { - cur.y = end.y; - } - if ( (cur.z > end.z && end.z >= start.z) || (cur.z < end.z && end.z < start.z) || (step.z == 0) ) { - cur.z = end.z; - } - } - - //walked entire line and didnt run into anything... - return false; - } - - bool Map::CheckLoS(glm::vec3 myloc, glm::vec3 oloc) const { - if(!imp) - return false; - - return !imp->rm->raycast((const RmReal*)&myloc, (const RmReal*)&oloc, nullptr, nullptr, nullptr); - } - - inline bool file_exists(const std::string& name) { - std::ifstream f(name.c_str()); - return f.good(); - } - - Map *Map::LoadMapFile(std::string file) { - - std::string filename = ""; - if (file_exists("maps")) { - filename = "maps"; - } - else if (file_exists("Maps")) { - filename = "Maps"; - } - else { - filename = Config->MapDir; - } - std::transform(file.begin(), file.end(), file.begin(), ::tolower); - filename += "/"; - filename += file; - filename += ".map"; - - Log(Logs::General, Logs::Status, "Attempting to load Map File :: '%s'", filename.c_str()); - - auto m = new Map(); - if (m->Load(filename)) { - return m; - } - - delete m; - return nullptr; - } - - #ifdef USE_MAP_MMFS - bool Map::Load(std::string filename, bool force_mmf_overwrite) + to.z = -BEST_Z_INVALID; + hit = imp->rm->raycast((const RmReal*)&from, (const RmReal*)&to, (RmReal*)result, nullptr, &hit_distance); + if (hit) { - if (LoadMMF(filename, force_mmf_overwrite)) { - Log(Logs::General, Logs::Status, "Loaded .MMF Map File in place of '%s'", filename.c_str()); + return result->z; + } + + return BEST_Z_INVALID; +} + +float Map::FindClosestZ(glm::vec3 &start, glm::vec3 *result) const { + // Unlike FindBestZ, this method finds the closest Z value above or below the specified point. + // + if (!imp) + return false; + + float ClosestZ = BEST_Z_INVALID; + + glm::vec3 tmp; + if (!result) + result = &tmp; + + glm::vec3 from(start.x, start.y, start.z); + glm::vec3 to(start.x, start.y, BEST_Z_INVALID); + float hit_distance; + bool hit = false; + + // first check is below us + hit = imp->rm->raycast((const RmReal*)&from, (const RmReal*)&to, (RmReal*)result, nullptr, &hit_distance); + if (hit) { + ClosestZ = result->z; + + } + + // Find nearest Z above us + to.z = -BEST_Z_INVALID; + hit = imp->rm->raycast((const RmReal*)&from, (const RmReal*)&to, (RmReal*)result, nullptr, &hit_distance); + if (hit) { + if (std::abs(from.z - result->z) < std::abs(ClosestZ - from.z)) + return result->z; + } + + return ClosestZ; +} + +bool Map::LineIntersectsZone(glm::vec3 start, glm::vec3 end, float step, glm::vec3 *result) const { + if (!imp) + return false; + return imp->rm->raycast((const RmReal*)&start, (const RmReal*)&end, (RmReal*)result, nullptr, nullptr); +} + +bool Map::LineIntersectsZoneNoZLeaps(glm::vec3 start, glm::vec3 end, float step_mag, glm::vec3 *result) const { + if (!imp) + return false; + + float z = BEST_Z_INVALID; + glm::vec3 step; + glm::vec3 cur; + cur.x = start.x; + cur.y = start.y; + cur.z = start.z; + + step.x = end.x - start.x; + step.y = end.y - start.y; + step.z = end.z - start.z; + float factor = step_mag / sqrt(step.x*step.x + step.y*step.y + step.z*step.z); + + step.x *= factor; + step.y *= factor; + step.z *= factor; + + int steps = 0; + + if (step.x > 0 && step.x < 0.001f) + step.x = 0.001f; + if (step.y > 0 && step.y < 0.001f) + step.y = 0.001f; + if (step.z > 0 && step.z < 0.001f) + step.z = 0.001f; + if (step.x < 0 && step.x > -0.001f) + step.x = -0.001f; + if (step.y < 0 && step.y > -0.001f) + step.y = -0.001f; + if (step.z < 0 && step.z > -0.001f) + step.z = -0.001f; + + //while we are not past end + //always do this once, even if start == end. + while (cur.x != end.x || cur.y != end.y || cur.z != end.z) + { + steps++; + glm::vec3 me; + me.x = cur.x; + me.y = cur.y; + me.z = cur.z; + glm::vec3 hit; + + float best_z = FindBestZ(me, &hit); + float diff = best_z - z; + diff = diff < 0 ? -diff : diff; + + if (z <= BEST_Z_INVALID || best_z <= BEST_Z_INVALID || diff < 12.0) + z = best_z; + else + return true; + + //look at current location + if (LineIntersectsZone(start, end, step_mag, result)) + { return true; } - #else - bool Map::Load(std::string filename) - { - #endif /*USE_MAP_MMFS*/ - FILE *f = fopen(filename.c_str(), "rb"); - if(f) { - uint32 version; - if(fread(&version, sizeof(version), 1, f) != 1) { - fclose(f); - return false; - } - - if(version == 0x01000000) { - Log(Logs::General, Logs::Status, "Loaded V1 Map File :: '%s'", filename.c_str()); - bool v = LoadV1(f); - fclose(f); + //move 1 step + if (cur.x != end.x) + cur.x += step.x; + if (cur.y != end.y) + cur.y += step.y; + if (cur.z != end.z) + cur.z += step.z; - #ifdef USE_MAP_MMFS - if (v) - return SaveMMF(filename, force_mmf_overwrite); - #endif /*USE_MAP_MMFS*/ - - return v; - } else if(version == 0x02000000) { - Log(Logs::General, Logs::Status, "Loaded V2 Map File :: '%s'", filename.c_str()); - bool v = LoadV2(f); - fclose(f); - - #ifdef USE_MAP_MMFS - if (v) - return SaveMMF(filename, force_mmf_overwrite); - #endif /*USE_MAP_MMFS*/ - - return v; - } else { - fclose(f); - return false; - } + //watch for end conditions + if ((cur.x > end.x && end.x >= start.x) || (cur.x < end.x && end.x <= start.x) || (step.x == 0)) { + cur.x = end.x; } - + if ((cur.y > end.y && end.y >= start.y) || (cur.y < end.y && end.y <= start.y) || (step.y == 0)) { + cur.y = end.y; + } + if ((cur.z > end.z && end.z >= start.z) || (cur.z < end.z && end.z < start.z) || (step.z == 0)) { + cur.z = end.z; + } + } + + //walked entire line and didnt run into anything... + return false; +} + +bool Map::CheckLoS(glm::vec3 myloc, glm::vec3 oloc) const { + if (!imp) + return false; + + return !imp->rm->raycast((const RmReal*)&myloc, (const RmReal*)&oloc, nullptr, nullptr, nullptr); +} + +// returns true if a collision happens +bool Map::DoCollisionCheck(glm::vec3 myloc, glm::vec3 oloc, glm::vec3 &outnorm, float &distance) const { + if (!imp) + return false; + + return imp->rm->raycast((const RmReal*)&myloc, (const RmReal*)&oloc, nullptr, (RmReal *)&outnorm, (RmReal *)&distance); +} + +inline bool file_exists(const std::string& name) { + std::ifstream f(name.c_str()); + return f.good(); +} + +Map *Map::LoadMapFile(std::string file) { + + std::string filename = ""; + if (file_exists("maps")) { + filename = "maps"; + } + else if (file_exists("Maps")) { + filename = "Maps"; + } + else { + filename = Config->MapDir; + } + std::transform(file.begin(), file.end(), file.begin(), ::tolower); + filename += "/"; + filename += file; + filename += ".map"; + + Log(Logs::General, Logs::Status, "Attempting to load Map File :: '%s'", filename.c_str()); + + auto m = new Map(); + if (m->Load(filename)) { + return m; + } + + delete m; + return nullptr; +} + +#ifdef USE_MAP_MMFS +bool Map::Load(std::string filename, bool force_mmf_overwrite) +{ + if (LoadMMF(filename, force_mmf_overwrite)) { + Log(Logs::General, Logs::Status, "Loaded .MMF Map File in place of '%s'", filename.c_str()); + return true; + } +#else +bool Map::Load(std::string filename) +{ +#endif /*USE_MAP_MMFS*/ + + FILE *f = fopen(filename.c_str(), "rb"); + if (f) { + uint32 version; + if (fread(&version, sizeof(version), 1, f) != 1) { + fclose(f); + return false; + } + + if (version == 0x01000000) { + Log(Logs::General, Logs::Status, "Loaded V1 Map File :: '%s'", filename.c_str()); + bool v = LoadV1(f); + fclose(f); + +#ifdef USE_MAP_MMFS + if (v) + return SaveMMF(filename, force_mmf_overwrite); +#endif /*USE_MAP_MMFS*/ + + return v; + } + else if (version == 0x02000000) { + Log(Logs::General, Logs::Status, "Loaded V2 Map File :: '%s'", filename.c_str()); + bool v = LoadV2(f); + fclose(f); + +#ifdef USE_MAP_MMFS + if (v) + return SaveMMF(filename, force_mmf_overwrite); +#endif /*USE_MAP_MMFS*/ + + return v; + } + else { + fclose(f); + return false; + } + } + + return false; +} + +bool Map::LoadV1(FILE *f) { + uint32 face_count; + uint16 node_count; + uint32 facelist_count; + + if (fread(&face_count, sizeof(face_count), 1, f) != 1) { return false; } - bool Map::LoadV1(FILE *f) { - uint32 face_count; - uint16 node_count; - uint32 facelist_count; - - if(fread(&face_count, sizeof(face_count), 1, f) != 1) { - return false; - } - - if(fread(&node_count, sizeof(node_count), 1, f) != 1) { - return false; - } - - if(fread(&facelist_count, sizeof(facelist_count), 1, f) != 1) { - return false; - } - - std::vector verts; - std::vector indices; - for(uint32 i = 0; i < face_count; ++i) { - glm::vec3 a; - glm::vec3 b; - glm::vec3 c; - float normals[4]; - if(fread(&a, sizeof(glm::vec3), 1, f) != 1) { - return false; - } - - if(fread(&b, sizeof(glm::vec3), 1, f) != 1) { - return false; - } - - if(fread(&c, sizeof(glm::vec3), 1, f) != 1) { - return false; - } - - if(fread(normals, sizeof(normals), 1, f) != 1) { - return false; - } - - size_t sz = verts.size(); - verts.push_back(a); - indices.push_back((uint32)sz); - - verts.push_back(b); - indices.push_back((uint32)sz + 1); - - verts.push_back(c); - indices.push_back((uint32)sz + 2); - } - - if(imp) { - imp->rm->release(); - imp->rm = nullptr; - } else { - imp = new impl; - } - - imp->rm = createRaycastMesh((RmUint32)verts.size(), (const RmReal*)&verts[0], face_count, &indices[0]); - - if(!imp->rm) { - delete imp; - imp = nullptr; - return false; - } - - return true; + if (fread(&node_count, sizeof(node_count), 1, f) != 1) { + return false; } - struct ModelEntry + if (fread(&facelist_count, sizeof(facelist_count), 1, f) != 1) { + return false; + } + + std::vector verts; + std::vector indices; + for (uint32 i = 0; i < face_count; ++i) { + glm::vec3 a; + glm::vec3 b; + glm::vec3 c; + float normals[4]; + if (fread(&a, sizeof(glm::vec3), 1, f) != 1) { + return false; + } + + if (fread(&b, sizeof(glm::vec3), 1, f) != 1) { + return false; + } + + if (fread(&c, sizeof(glm::vec3), 1, f) != 1) { + return false; + } + + if (fread(normals, sizeof(normals), 1, f) != 1) { + return false; + } + + size_t sz = verts.size(); + verts.push_back(a); + indices.push_back((uint32)sz); + + verts.push_back(b); + indices.push_back((uint32)sz + 1); + + verts.push_back(c); + indices.push_back((uint32)sz + 2); + } + + if (imp) { + imp->rm->release(); + imp->rm = nullptr; + } + else { + imp = new impl; + } + + imp->rm = createRaycastMesh((RmUint32)verts.size(), (const RmReal*)&verts[0], face_count, &indices[0]); + + if (!imp->rm) { + delete imp; + imp = nullptr; + return false; + } + + return true; +} + +struct ModelEntry +{ + struct Poly { - struct Poly - { - uint32 v1, v2, v3; - uint8 vis; - }; - std::vector verts; - std::vector polys; + uint32 v1, v2, v3; + uint8 vis; }; + std::vector verts; + std::vector polys; +}; - bool Map::LoadV2(FILE *f) { - uint32 data_size; - if (fread(&data_size, sizeof(data_size), 1, f) != 1) { - return false; - } +bool Map::LoadV2(FILE *f) { + uint32 data_size; + if (fread(&data_size, sizeof(data_size), 1, f) != 1) { + return false; + } - uint32 buffer_size; - if (fread(&buffer_size, sizeof(buffer_size), 1, f) != 1) { - return false; - } + uint32 buffer_size; + if (fread(&buffer_size, sizeof(buffer_size), 1, f) != 1) { + return false; + } - std::vector data; - data.resize(data_size); - if (fread(&data[0], data_size, 1, f) != 1) { - return false; - } + std::vector data; + data.resize(data_size); + if (fread(&data[0], data_size, 1, f) != 1) { + return false; + } - std::vector buffer; - buffer.resize(buffer_size); - uint32 v = InflateData(&data[0], data_size, &buffer[0], buffer_size); + std::vector buffer; + buffer.resize(buffer_size); + uint32 v = InflateData(&data[0], data_size, &buffer[0], buffer_size); - char *buf = &buffer[0]; - uint32 vert_count; - uint32 ind_count; - uint32 nc_vert_count; - uint32 nc_ind_count; - uint32 model_count; - uint32 plac_count; - uint32 plac_group_count; - uint32 tile_count; - uint32 quads_per_tile; - float units_per_vertex; + char *buf = &buffer[0]; + uint32 vert_count; + uint32 ind_count; + uint32 nc_vert_count; + uint32 nc_ind_count; + uint32 model_count; + uint32 plac_count; + uint32 plac_group_count; + uint32 tile_count; + uint32 quads_per_tile; + float units_per_vertex; - vert_count = *(uint32*)buf; - buf += sizeof(uint32); + vert_count = *(uint32*)buf; + buf += sizeof(uint32); - ind_count = *(uint32*)buf; - buf += sizeof(uint32); + ind_count = *(uint32*)buf; + buf += sizeof(uint32); - nc_vert_count = *(uint32*)buf; - buf += sizeof(uint32); + nc_vert_count = *(uint32*)buf; + buf += sizeof(uint32); - nc_ind_count = *(uint32*)buf; - buf += sizeof(uint32); + nc_ind_count = *(uint32*)buf; + buf += sizeof(uint32); - model_count = *(uint32*)buf; - buf += sizeof(uint32); + model_count = *(uint32*)buf; + buf += sizeof(uint32); - plac_count = *(uint32*)buf; - buf += sizeof(uint32); + plac_count = *(uint32*)buf; + buf += sizeof(uint32); - plac_group_count = *(uint32*)buf; - buf += sizeof(uint32); + plac_group_count = *(uint32*)buf; + buf += sizeof(uint32); - tile_count = *(uint32*)buf; - buf += sizeof(uint32); + tile_count = *(uint32*)buf; + buf += sizeof(uint32); - quads_per_tile = *(uint32*)buf; - buf += sizeof(uint32); + quads_per_tile = *(uint32*)buf; + buf += sizeof(uint32); - units_per_vertex = *(float*)buf; + units_per_vertex = *(float*)buf; + buf += sizeof(float); + + std::vector verts; + verts.reserve(vert_count); + std::vector indices; + indices.reserve(ind_count); + + for (uint32 i = 0; i < vert_count; ++i) { + float x; + float y; + float z; + + x = *(float*)buf; buf += sizeof(float); - std::vector verts; - verts.reserve(vert_count); - std::vector indices; - indices.reserve(ind_count); + y = *(float*)buf; + buf += sizeof(float); - for (uint32 i = 0; i < vert_count; ++i) { - float x; - float y; - float z; + z = *(float*)buf; + buf += sizeof(float); - x = *(float*)buf; - buf += sizeof(float); + verts.emplace_back(x, y, z); + } - y = *(float*)buf; - buf += sizeof(float); + for (uint32 i = 0; i < ind_count; ++i) { + indices.emplace_back(*(uint32 *)buf); + buf += sizeof(uint32); + } - z = *(float*)buf; - buf += sizeof(float); + for (uint32 i = 0; i < nc_vert_count; ++i) { + buf += sizeof(float) * 3; + } - verts.emplace_back(x, y, z); - } + for (uint32 i = 0; i < nc_ind_count; ++i) { + buf += sizeof(uint32); + } - for (uint32 i = 0; i < ind_count; ++i) { - indices.emplace_back(*(uint32 *)buf); - buf += sizeof(uint32); - } + std::map> models; + for (uint32 i = 0; i < model_count; ++i) { + std::unique_ptr me(new ModelEntry); + std::string name = buf; + buf += name.length() + 1; - for (uint32 i = 0; i < nc_vert_count; ++i) { - buf += sizeof(float) * 3; - } + uint32 vert_count = *(uint32*)buf; + buf += sizeof(uint32); - for (uint32 i = 0; i < nc_ind_count; ++i) { - buf += sizeof(uint32); - } - - std::map> models; - for (uint32 i = 0; i < model_count; ++i) { - std::unique_ptr me(new ModelEntry); - std::string name = buf; - buf += name.length() + 1; - - uint32 vert_count = *(uint32*)buf; - buf += sizeof(uint32); - - uint32 poly_count = *(uint32*)buf; - buf += sizeof(uint32); - - me->verts.reserve(vert_count); - for (uint32 j = 0; j < vert_count; ++j) { - float x = *(float*)buf; - buf += sizeof(float); - float y = *(float*)buf; - buf += sizeof(float); - float z = *(float*)buf; - buf += sizeof(float); - - me->verts.emplace_back(x, y, z); - } - - me->polys.reserve(poly_count); - for (uint32 j = 0; j < poly_count; ++j) { - uint32 v1 = *(uint32*)buf; - buf += sizeof(uint32); - uint32 v2 = *(uint32*)buf; - buf += sizeof(uint32); - uint32 v3 = *(uint32*)buf; - buf += sizeof(uint32); - uint8 vis = *(uint8*)buf; - buf += sizeof(uint8); - - ModelEntry::Poly p; - p.v1 = v1; - p.v2 = v2; - p.v3 = v3; - p.vis = vis; - me->polys.push_back(p); - } - - models[name] = std::move(me); - } - - for (uint32 i = 0; i < plac_count; ++i) { - std::string name = buf; - buf += name.length() + 1; + uint32 poly_count = *(uint32*)buf; + buf += sizeof(uint32); + me->verts.reserve(vert_count); + for (uint32 j = 0; j < vert_count; ++j) { float x = *(float*)buf; buf += sizeof(float); float y = *(float*)buf; @@ -588,42 +563,216 @@ namespace EQEmu { float z = *(float*)buf; buf += sizeof(float); - float x_rot = *(float*)buf; + me->verts.emplace_back(x, y, z); + } + + me->polys.reserve(poly_count); + for (uint32 j = 0; j < poly_count; ++j) { + uint32 v1 = *(uint32*)buf; + buf += sizeof(uint32); + uint32 v2 = *(uint32*)buf; + buf += sizeof(uint32); + uint32 v3 = *(uint32*)buf; + buf += sizeof(uint32); + uint8 vis = *(uint8*)buf; + buf += sizeof(uint8); + + ModelEntry::Poly p; + p.v1 = v1; + p.v2 = v2; + p.v3 = v3; + p.vis = vis; + me->polys.push_back(p); + } + + models[name] = std::move(me); + } + + for (uint32 i = 0; i < plac_count; ++i) { + std::string name = buf; + buf += name.length() + 1; + + float x = *(float*)buf; + buf += sizeof(float); + float y = *(float*)buf; + buf += sizeof(float); + float z = *(float*)buf; + buf += sizeof(float); + + float x_rot = *(float*)buf; + buf += sizeof(float); + float y_rot = *(float*)buf; + buf += sizeof(float); + float z_rot = *(float*)buf; + buf += sizeof(float); + + float x_scale = *(float*)buf; + buf += sizeof(float); + float y_scale = *(float*)buf; + buf += sizeof(float); + float z_scale = *(float*)buf; + buf += sizeof(float); + + if (models.count(name) == 0) + continue; + + auto &model = models[name]; + auto &mod_polys = model->polys; + auto &mod_verts = model->verts; + for (uint32 j = 0; j < mod_polys.size(); ++j) { + auto ¤t_poly = mod_polys[j]; + if (current_poly.vis == 0) + continue; + auto v1 = mod_verts[current_poly.v1]; + auto v2 = mod_verts[current_poly.v2]; + auto v3 = mod_verts[current_poly.v3]; + + RotateVertex(v1, x_rot, y_rot, z_rot); + RotateVertex(v2, x_rot, y_rot, z_rot); + RotateVertex(v3, x_rot, y_rot, z_rot); + + ScaleVertex(v1, x_scale, y_scale, z_scale); + ScaleVertex(v2, x_scale, y_scale, z_scale); + ScaleVertex(v3, x_scale, y_scale, z_scale); + + TranslateVertex(v1, x, y, z); + TranslateVertex(v2, x, y, z); + TranslateVertex(v3, x, y, z); + + verts.emplace_back(v1.y, v1.x, v1.z); // x/y swapped + verts.emplace_back(v2.y, v2.x, v2.z); + verts.emplace_back(v3.y, v3.x, v3.z); + + indices.emplace_back((uint32)verts.size() - 3); + indices.emplace_back((uint32)verts.size() - 2); + indices.emplace_back((uint32)verts.size() - 1); + } + } + + for (uint32 i = 0; i < plac_group_count; ++i) { + float x = *(float*)buf; + buf += sizeof(float); + float y = *(float*)buf; + buf += sizeof(float); + float z = *(float*)buf; + buf += sizeof(float); + + float x_rot = *(float*)buf; + buf += sizeof(float); + float y_rot = *(float*)buf; + buf += sizeof(float); + float z_rot = *(float*)buf; + buf += sizeof(float); + + float x_scale = *(float*)buf; + buf += sizeof(float); + float y_scale = *(float*)buf; + buf += sizeof(float); + float z_scale = *(float*)buf; + buf += sizeof(float); + + float x_tile = *(float*)buf; + buf += sizeof(float); + float y_tile = *(float*)buf; + buf += sizeof(float); + float z_tile = *(float*)buf; + buf += sizeof(float); + + uint32 p_count = *(uint32*)buf; + buf += sizeof(uint32); + + for (uint32 j = 0; j < p_count; ++j) { + std::string name = buf; + buf += name.length() + 1; + + float p_x = *(float*)buf; buf += sizeof(float); - float y_rot = *(float*)buf; + float p_y = *(float*)buf; buf += sizeof(float); - float z_rot = *(float*)buf; + float p_z = *(float*)buf; buf += sizeof(float); - float x_scale = *(float*)buf; + float p_x_rot = *(float*)buf * 3.14159f / 180; buf += sizeof(float); - float y_scale = *(float*)buf; + float p_y_rot = *(float*)buf * 3.14159f / 180; buf += sizeof(float); - float z_scale = *(float*)buf; + float p_z_rot = *(float*)buf * 3.14159f / 180; + buf += sizeof(float); + + float p_x_scale = *(float*)buf; + buf += sizeof(float); + float p_y_scale = *(float*)buf; + buf += sizeof(float); + float p_z_scale = *(float*)buf; buf += sizeof(float); if (models.count(name) == 0) continue; auto &model = models[name]; - auto &mod_polys = model->polys; - auto &mod_verts = model->verts; - for (uint32 j = 0; j < mod_polys.size(); ++j) { - auto ¤t_poly = mod_polys[j]; - if (current_poly.vis == 0) - continue; - auto v1 = mod_verts[current_poly.v1]; - auto v2 = mod_verts[current_poly.v2]; - auto v3 = mod_verts[current_poly.v3]; - RotateVertex(v1, x_rot, y_rot, z_rot); - RotateVertex(v2, x_rot, y_rot, z_rot); - RotateVertex(v3, x_rot, y_rot, z_rot); + for (size_t k = 0; k < model->polys.size(); ++k) { + auto &poly = model->polys[k]; + if (poly.vis == 0) + continue; + glm::vec3 v1, v2, v3; + + v1 = model->verts[poly.v1]; + v2 = model->verts[poly.v2]; + v3 = model->verts[poly.v3]; + + ScaleVertex(v1, p_x_scale, p_y_scale, p_z_scale); + ScaleVertex(v2, p_x_scale, p_y_scale, p_z_scale); + ScaleVertex(v3, p_x_scale, p_y_scale, p_z_scale); + + TranslateVertex(v1, p_x, p_y, p_z); + TranslateVertex(v2, p_x, p_y, p_z); + TranslateVertex(v3, p_x, p_y, p_z); + + RotateVertex(v1, x_rot * 3.14159f / 180.0f, 0, 0); + RotateVertex(v2, x_rot * 3.14159f / 180.0f, 0, 0); + RotateVertex(v3, x_rot * 3.14159f / 180.0f, 0, 0); + + RotateVertex(v1, 0, y_rot * 3.14159f / 180.0f, 0); + RotateVertex(v2, 0, y_rot * 3.14159f / 180.0f, 0); + RotateVertex(v3, 0, y_rot * 3.14159f / 180.0f, 0); + + glm::vec3 correction(p_x, p_y, p_z); + + RotateVertex(correction, x_rot * 3.14159f / 180.0f, 0, 0); + + TranslateVertex(v1, -correction.x, -correction.y, -correction.z); + TranslateVertex(v2, -correction.x, -correction.y, -correction.z); + TranslateVertex(v3, -correction.x, -correction.y, -correction.z); + + RotateVertex(v1, p_x_rot, 0, 0); + RotateVertex(v2, p_x_rot, 0, 0); + RotateVertex(v3, p_x_rot, 0, 0); + + RotateVertex(v1, 0, -p_y_rot, 0); + RotateVertex(v2, 0, -p_y_rot, 0); + RotateVertex(v3, 0, -p_y_rot, 0); + + RotateVertex(v1, 0, 0, p_z_rot); + RotateVertex(v2, 0, 0, p_z_rot); + RotateVertex(v3, 0, 0, p_z_rot); + + TranslateVertex(v1, correction.x, correction.y, correction.z); + TranslateVertex(v2, correction.x, correction.y, correction.z); + TranslateVertex(v3, correction.x, correction.y, correction.z); + + RotateVertex(v1, 0, 0, z_rot * 3.14159f / 180.0f); + RotateVertex(v2, 0, 0, z_rot * 3.14159f / 180.0f); + RotateVertex(v3, 0, 0, z_rot * 3.14159f / 180.0f); ScaleVertex(v1, x_scale, y_scale, z_scale); ScaleVertex(v2, x_scale, y_scale, z_scale); ScaleVertex(v3, x_scale, y_scale, z_scale); + TranslateVertex(v1, x_tile, y_tile, z_tile); + TranslateVertex(v2, x_tile, y_tile, z_tile); + TranslateVertex(v3, x_tile, y_tile, z_tile); + TranslateVertex(v1, x, y, z); TranslateVertex(v2, x, y, z); TranslateVertex(v3, x, y, z); @@ -637,546 +786,408 @@ namespace EQEmu { indices.emplace_back((uint32)verts.size() - 1); } } + } - for (uint32 i = 0; i < plac_group_count; ++i) { - float x = *(float*)buf; - buf += sizeof(float); - float y = *(float*)buf; - buf += sizeof(float); - float z = *(float*)buf; + uint32 ter_quad_count = (quads_per_tile * quads_per_tile); + uint32 ter_vert_count = ((quads_per_tile + 1) * (quads_per_tile + 1)); + std::vector flags; + std::vector floats; + flags.resize(ter_quad_count); + floats.resize(ter_vert_count); + for (uint32 i = 0; i < tile_count; ++i) { + bool flat; + flat = *(bool*)buf; + buf += sizeof(bool); + + float x; + x = *(float*)buf; + buf += sizeof(float); + + float y; + y = *(float*)buf; + buf += sizeof(float); + + if (flat) { + float z; + z = *(float*)buf; buf += sizeof(float); - float x_rot = *(float*)buf; - buf += sizeof(float); - float y_rot = *(float*)buf; - buf += sizeof(float); - float z_rot = *(float*)buf; - buf += sizeof(float); + float QuadVertex1X = x; + float QuadVertex1Y = y; + float QuadVertex1Z = z; - float x_scale = *(float*)buf; - buf += sizeof(float); - float y_scale = *(float*)buf; - buf += sizeof(float); - float z_scale = *(float*)buf; - buf += sizeof(float); + float QuadVertex2X = QuadVertex1X + (quads_per_tile * units_per_vertex); + float QuadVertex2Y = QuadVertex1Y; + float QuadVertex2Z = QuadVertex1Z; - float x_tile = *(float*)buf; - buf += sizeof(float); - float y_tile = *(float*)buf; - buf += sizeof(float); - float z_tile = *(float*)buf; - buf += sizeof(float); + float QuadVertex3X = QuadVertex2X; + float QuadVertex3Y = QuadVertex1Y + (quads_per_tile * units_per_vertex); + float QuadVertex3Z = QuadVertex1Z; - uint32 p_count = *(uint32*)buf; - buf += sizeof(uint32); + float QuadVertex4X = QuadVertex1X; + float QuadVertex4Y = QuadVertex3Y; + float QuadVertex4Z = QuadVertex1Z; - for (uint32 j = 0; j < p_count; ++j) { - std::string name = buf; - buf += name.length() + 1; + uint32 current_vert = (uint32)verts.size() + 3; + verts.emplace_back(QuadVertex1X, QuadVertex1Y, QuadVertex1Z); + verts.emplace_back(QuadVertex2X, QuadVertex2Y, QuadVertex2Z); + verts.emplace_back(QuadVertex3X, QuadVertex3Y, QuadVertex3Z); + verts.emplace_back(QuadVertex4X, QuadVertex4Y, QuadVertex4Z); - float p_x = *(float*)buf; - buf += sizeof(float); - float p_y = *(float*)buf; - buf += sizeof(float); - float p_z = *(float*)buf; + indices.emplace_back(current_vert); + indices.emplace_back(current_vert - 2); + indices.emplace_back(current_vert - 1); + + indices.emplace_back(current_vert); + indices.emplace_back(current_vert - 3); + indices.emplace_back(current_vert - 2); + } + else { + //read flags + for (uint32 j = 0; j < ter_quad_count; ++j) { + uint8 f; + f = *(uint8*)buf; + buf += sizeof(uint8); + + flags[j] = f; + } + + //read floats + for (uint32 j = 0; j < ter_vert_count; ++j) { + float f; + f = *(float*)buf; buf += sizeof(float); - float p_x_rot = *(float*)buf * 3.14159f / 180; - buf += sizeof(float); - float p_y_rot = *(float*)buf * 3.14159f / 180; - buf += sizeof(float); - float p_z_rot = *(float*)buf * 3.14159f / 180; - buf += sizeof(float); + floats[j] = f; + } - float p_x_scale = *(float*)buf; - buf += sizeof(float); - float p_y_scale = *(float*)buf; - buf += sizeof(float); - float p_z_scale = *(float*)buf; - buf += sizeof(float); + int row_number = -1; + std::map, uint32> cur_verts; + for (uint32 quad = 0; quad < ter_quad_count; ++quad) { + if ((quad % quads_per_tile) == 0) { + ++row_number; + } - if (models.count(name) == 0) + if (flags[quad] & 0x01) continue; - auto &model = models[name]; + float QuadVertex1X = x + (row_number * units_per_vertex); + float QuadVertex1Y = y + (quad % quads_per_tile) * units_per_vertex; + float QuadVertex1Z = floats[quad + row_number]; - for (size_t k = 0; k < model->polys.size(); ++k) { - auto &poly = model->polys[k]; - if (poly.vis == 0) - continue; - glm::vec3 v1, v2, v3; - - v1 = model->verts[poly.v1]; - v2 = model->verts[poly.v2]; - v3 = model->verts[poly.v3]; - - ScaleVertex(v1, p_x_scale, p_y_scale, p_z_scale); - ScaleVertex(v2, p_x_scale, p_y_scale, p_z_scale); - ScaleVertex(v3, p_x_scale, p_y_scale, p_z_scale); - - TranslateVertex(v1, p_x, p_y, p_z); - TranslateVertex(v2, p_x, p_y, p_z); - TranslateVertex(v3, p_x, p_y, p_z); - - RotateVertex(v1, x_rot * 3.14159f / 180.0f, 0, 0); - RotateVertex(v2, x_rot * 3.14159f / 180.0f, 0, 0); - RotateVertex(v3, x_rot * 3.14159f / 180.0f, 0, 0); - - RotateVertex(v1, 0, y_rot * 3.14159f / 180.0f, 0); - RotateVertex(v2, 0, y_rot * 3.14159f / 180.0f, 0); - RotateVertex(v3, 0, y_rot * 3.14159f / 180.0f, 0); - - glm::vec3 correction(p_x, p_y, p_z); - - RotateVertex(correction, x_rot * 3.14159f / 180.0f, 0, 0); - - TranslateVertex(v1, -correction.x, -correction.y, -correction.z); - TranslateVertex(v2, -correction.x, -correction.y, -correction.z); - TranslateVertex(v3, -correction.x, -correction.y, -correction.z); - - RotateVertex(v1, p_x_rot, 0, 0); - RotateVertex(v2, p_x_rot, 0, 0); - RotateVertex(v3, p_x_rot, 0, 0); - - RotateVertex(v1, 0, -p_y_rot, 0); - RotateVertex(v2, 0, -p_y_rot, 0); - RotateVertex(v3, 0, -p_y_rot, 0); - - RotateVertex(v1, 0, 0, p_z_rot); - RotateVertex(v2, 0, 0, p_z_rot); - RotateVertex(v3, 0, 0, p_z_rot); - - TranslateVertex(v1, correction.x, correction.y, correction.z); - TranslateVertex(v2, correction.x, correction.y, correction.z); - TranslateVertex(v3, correction.x, correction.y, correction.z); - - RotateVertex(v1, 0, 0, z_rot * 3.14159f / 180.0f); - RotateVertex(v2, 0, 0, z_rot * 3.14159f / 180.0f); - RotateVertex(v3, 0, 0, z_rot * 3.14159f / 180.0f); - - ScaleVertex(v1, x_scale, y_scale, z_scale); - ScaleVertex(v2, x_scale, y_scale, z_scale); - ScaleVertex(v3, x_scale, y_scale, z_scale); - - TranslateVertex(v1, x_tile, y_tile, z_tile); - TranslateVertex(v2, x_tile, y_tile, z_tile); - TranslateVertex(v3, x_tile, y_tile, z_tile); - - TranslateVertex(v1, x, y, z); - TranslateVertex(v2, x, y, z); - TranslateVertex(v3, x, y, z); - - verts.emplace_back(v1.y, v1.x, v1.z); // x/y swapped - verts.emplace_back(v2.y, v2.x, v2.z); - verts.emplace_back(v3.y, v3.x, v3.z); - - indices.emplace_back((uint32)verts.size() - 3); - indices.emplace_back((uint32)verts.size() - 2); - indices.emplace_back((uint32)verts.size() - 1); - } - } - } - - uint32 ter_quad_count = (quads_per_tile * quads_per_tile); - uint32 ter_vert_count = ((quads_per_tile + 1) * (quads_per_tile + 1)); - std::vector flags; - std::vector floats; - flags.resize(ter_quad_count); - floats.resize(ter_vert_count); - for (uint32 i = 0; i < tile_count; ++i) { - bool flat; - flat = *(bool*)buf; - buf += sizeof(bool); - - float x; - x = *(float*)buf; - buf += sizeof(float); - - float y; - y = *(float*)buf; - buf += sizeof(float); - - if (flat) { - float z; - z = *(float*)buf; - buf += sizeof(float); - - float QuadVertex1X = x; - float QuadVertex1Y = y; - float QuadVertex1Z = z; - - float QuadVertex2X = QuadVertex1X + (quads_per_tile * units_per_vertex); + float QuadVertex2X = QuadVertex1X + units_per_vertex; float QuadVertex2Y = QuadVertex1Y; - float QuadVertex2Z = QuadVertex1Z; + float QuadVertex2Z = floats[quad + row_number + quads_per_tile + 1]; - float QuadVertex3X = QuadVertex2X; - float QuadVertex3Y = QuadVertex1Y + (quads_per_tile * units_per_vertex); - float QuadVertex3Z = QuadVertex1Z; + float QuadVertex3X = QuadVertex1X + units_per_vertex; + float QuadVertex3Y = QuadVertex1Y + units_per_vertex; + float QuadVertex3Z = floats[quad + row_number + quads_per_tile + 2]; float QuadVertex4X = QuadVertex1X; - float QuadVertex4Y = QuadVertex3Y; - float QuadVertex4Z = QuadVertex1Z; + float QuadVertex4Y = QuadVertex1Y + units_per_vertex; + float QuadVertex4Z = floats[quad + row_number + 1]; - uint32 current_vert = (uint32)verts.size() + 3; - verts.emplace_back(QuadVertex1X, QuadVertex1Y, QuadVertex1Z); - verts.emplace_back(QuadVertex2X, QuadVertex2Y, QuadVertex2Z); - verts.emplace_back(QuadVertex3X, QuadVertex3Y, QuadVertex3Z); - verts.emplace_back(QuadVertex4X, QuadVertex4Y, QuadVertex4Z); - - indices.emplace_back(current_vert); - indices.emplace_back(current_vert - 2); - indices.emplace_back(current_vert - 1); - - indices.emplace_back(current_vert); - indices.emplace_back(current_vert - 3); - indices.emplace_back(current_vert - 2); - } - else { - //read flags - for (uint32 j = 0; j < ter_quad_count; ++j) { - uint8 f; - f = *(uint8*)buf; - buf += sizeof(uint8); - - flags[j] = f; + uint32 i1, i2, i3, i4; + std::tuple t = std::make_tuple(QuadVertex1X, QuadVertex1Y, QuadVertex1Z); + auto iter = cur_verts.find(t); + if (iter != cur_verts.end()) { + i1 = iter->second; + } + else { + i1 = (uint32)verts.size(); + verts.emplace_back(QuadVertex1X, QuadVertex1Y, QuadVertex1Z); + cur_verts[std::make_tuple(QuadVertex1X, QuadVertex1Y, QuadVertex1Z)] = i1; } - //read floats - for (uint32 j = 0; j < ter_vert_count; ++j) { - float f; - f = *(float*)buf; - buf += sizeof(float); - - floats[j] = f; + t = std::make_tuple(QuadVertex2X, QuadVertex2Y, QuadVertex2Z); + iter = cur_verts.find(t); + if (iter != cur_verts.end()) { + i2 = iter->second; + } + else { + i2 = (uint32)verts.size(); + verts.emplace_back(QuadVertex2X, QuadVertex2Y, QuadVertex2Z); + cur_verts[std::make_tuple(QuadVertex2X, QuadVertex2Y, QuadVertex2Z)] = i2; } - int row_number = -1; - std::map, uint32> cur_verts; - for (uint32 quad = 0; quad < ter_quad_count; ++quad) { - if ((quad % quads_per_tile) == 0) { - ++row_number; - } - - if (flags[quad] & 0x01) - continue; - - float QuadVertex1X = x + (row_number * units_per_vertex); - float QuadVertex1Y = y + (quad % quads_per_tile) * units_per_vertex; - float QuadVertex1Z = floats[quad + row_number]; - - float QuadVertex2X = QuadVertex1X + units_per_vertex; - float QuadVertex2Y = QuadVertex1Y; - float QuadVertex2Z = floats[quad + row_number + quads_per_tile + 1]; - - float QuadVertex3X = QuadVertex1X + units_per_vertex; - float QuadVertex3Y = QuadVertex1Y + units_per_vertex; - float QuadVertex3Z = floats[quad + row_number + quads_per_tile + 2]; - - float QuadVertex4X = QuadVertex1X; - float QuadVertex4Y = QuadVertex1Y + units_per_vertex; - float QuadVertex4Z = floats[quad + row_number + 1]; - - uint32 i1, i2, i3, i4; - std::tuple t = std::make_tuple(QuadVertex1X, QuadVertex1Y, QuadVertex1Z); - auto iter = cur_verts.find(t); - if (iter != cur_verts.end()) { - i1 = iter->second; - } - else { - i1 = (uint32)verts.size(); - verts.emplace_back(QuadVertex1X, QuadVertex1Y, QuadVertex1Z); - cur_verts[std::make_tuple(QuadVertex1X, QuadVertex1Y, QuadVertex1Z)] = i1; - } - - t = std::make_tuple(QuadVertex2X, QuadVertex2Y, QuadVertex2Z); - iter = cur_verts.find(t); - if (iter != cur_verts.end()) { - i2 = iter->second; - } - else { - i2 = (uint32)verts.size(); - verts.emplace_back(QuadVertex2X, QuadVertex2Y, QuadVertex2Z); - cur_verts[std::make_tuple(QuadVertex2X, QuadVertex2Y, QuadVertex2Z)] = i2; - } - - t = std::make_tuple(QuadVertex3X, QuadVertex3Y, QuadVertex3Z); - iter = cur_verts.find(t); - if (iter != cur_verts.end()) { - i3 = iter->second; - } - else { - i3 = (uint32)verts.size(); - verts.emplace_back(QuadVertex3X, QuadVertex3Y, QuadVertex3Z); - cur_verts[std::make_tuple(QuadVertex3X, QuadVertex3Y, QuadVertex3Z)] = i3; - } - - t = std::make_tuple(QuadVertex4X, QuadVertex4Y, QuadVertex4Z); - iter = cur_verts.find(t); - if (iter != cur_verts.end()) { - i4 = iter->second; - } - else { - i4 = (uint32)verts.size(); - verts.emplace_back(QuadVertex4X, QuadVertex4Y, QuadVertex4Z); - cur_verts[std::make_tuple(QuadVertex4X, QuadVertex4Y, QuadVertex4Z)] = i4; - } - - indices.emplace_back(i4); - indices.emplace_back(i2); - indices.emplace_back(i3); - - indices.emplace_back(i4); - indices.emplace_back(i1); - indices.emplace_back(i2); + t = std::make_tuple(QuadVertex3X, QuadVertex3Y, QuadVertex3Z); + iter = cur_verts.find(t); + if (iter != cur_verts.end()) { + i3 = iter->second; } + else { + i3 = (uint32)verts.size(); + verts.emplace_back(QuadVertex3X, QuadVertex3Y, QuadVertex3Z); + cur_verts[std::make_tuple(QuadVertex3X, QuadVertex3Y, QuadVertex3Z)] = i3; + } + + t = std::make_tuple(QuadVertex4X, QuadVertex4Y, QuadVertex4Z); + iter = cur_verts.find(t); + if (iter != cur_verts.end()) { + i4 = iter->second; + } + else { + i4 = (uint32)verts.size(); + verts.emplace_back(QuadVertex4X, QuadVertex4Y, QuadVertex4Z); + cur_verts[std::make_tuple(QuadVertex4X, QuadVertex4Y, QuadVertex4Z)] = i4; + } + + indices.emplace_back(i4); + indices.emplace_back(i2); + indices.emplace_back(i3); + + indices.emplace_back(i4); + indices.emplace_back(i1); + indices.emplace_back(i2); } } - - uint32 face_count = indices.size() / 3; - - if (imp) { - imp->rm->release(); - imp->rm = nullptr; - } - else { - imp = new impl; - } - - imp->rm = createRaycastMesh((RmUint32)verts.size(), (const RmReal*)&verts[0], face_count, &indices[0]); - - if (!imp->rm) { - delete imp; - imp = nullptr; - return false; - } - - return true; } - void Map::RotateVertex(glm::vec3 &v, float rx, float ry, float rz) { - glm::vec3 nv = v; + uint32 face_count = indices.size() / 3; - nv.y = (std::cos(rx) * v.y) - (std::sin(rx) * v.z); - nv.z = (std::sin(rx) * v.y) + (std::cos(rx) * v.z); - - v = nv; - - nv.x = (std::cos(ry) * v.x) + (std::sin(ry) * v.z); - nv.z = -(std::sin(ry) * v.x) + (std::cos(ry) * v.z); - - v = nv; - - nv.x = (std::cos(rz) * v.x) - (std::sin(rz) * v.y); - nv.y = (std::sin(rz) * v.x) + (std::cos(rz) * v.y); - - v = nv; + if (imp) { + imp->rm->release(); + imp->rm = nullptr; + } + else { + imp = new impl; } - void Map::ScaleVertex(glm::vec3 &v, float sx, float sy, float sz) { - v.x = v.x * sx; - v.y = v.y * sy; - v.z = v.z * sz; + imp->rm = createRaycastMesh((RmUint32)verts.size(), (const RmReal*)&verts[0], face_count, &indices[0]); + + if (!imp->rm) { + delete imp; + imp = nullptr; + return false; } - void Map::TranslateVertex(glm::vec3 &v, float tx, float ty, float tz) { - v.x = v.x + tx; - v.y = v.y + ty; - v.z = v.z + tz; + return true; +} + +void Map::RotateVertex(glm::vec3 &v, float rx, float ry, float rz) { + glm::vec3 nv = v; + + nv.y = (std::cos(rx) * v.y) - (std::sin(rx) * v.z); + nv.z = (std::sin(rx) * v.y) + (std::cos(rx) * v.z); + + v = nv; + + nv.x = (std::cos(ry) * v.x) + (std::sin(ry) * v.z); + nv.z = -(std::sin(ry) * v.x) + (std::cos(ry) * v.z); + + v = nv; + + nv.x = (std::cos(rz) * v.x) - (std::sin(rz) * v.y); + nv.y = (std::sin(rz) * v.x) + (std::cos(rz) * v.y); + + v = nv; +} + +void Map::ScaleVertex(glm::vec3 &v, float sx, float sy, float sz) { + v.x = v.x * sx; + v.y = v.y * sy; + v.z = v.z * sz; +} + +void Map::TranslateVertex(glm::vec3 &v, float tx, float ty, float tz) { + v.x = v.x + tx; + v.y = v.y + ty; + v.z = v.z + tz; +} + +#ifdef USE_MAP_MMFS +inline void strip_map_extension(std::string& map_file_name) +{ + auto ext_off = map_file_name.find(".map"); + if (ext_off != std::string::npos) + map_file_name.erase(ext_off, strlen(".map")); +} + +inline bool add_mmf_extension(std::string& mmf_file_name) +{ + if (mmf_file_name.empty()) + return false; + + mmf_file_name.append(".mmf"); + size_t dot_check = std::count(mmf_file_name.begin(), mmf_file_name.end(), '.'); + + return (dot_check == 1); +} + +bool Map::LoadMMF(const std::string& map_file_name, bool force_mmf_overwrite) +{ + if (force_mmf_overwrite) + return false; + + std::string mmf_file_name = map_file_name; + strip_map_extension(mmf_file_name); + if (!add_mmf_extension(mmf_file_name)) { + Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s'", mmf_file_name.c_str()); + return false; } - #ifdef USE_MAP_MMFS - inline void strip_map_extension(std::string& map_file_name) - { - auto ext_off = map_file_name.find(".map"); - if (ext_off != std::string::npos) - map_file_name.erase(ext_off, strlen(".map")); + FILE *f = fopen(mmf_file_name.c_str(), "rb"); + if (!f) { + Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s' - could not open file", mmf_file_name.c_str()); + return false; } - inline bool add_mmf_extension(std::string& mmf_file_name) - { - if (mmf_file_name.empty()) - return false; - - mmf_file_name.append(".mmf"); - size_t dot_check = std::count(mmf_file_name.begin(), mmf_file_name.end(), '.'); - - return (dot_check == 1); - } - - bool Map::LoadMMF(const std::string& map_file_name, bool force_mmf_overwrite) - { - if (force_mmf_overwrite) - return false; - - std::string mmf_file_name = map_file_name; - strip_map_extension(mmf_file_name); - if (!add_mmf_extension(mmf_file_name)) { - Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s'", mmf_file_name.c_str()); - return false; - } - - FILE *f = fopen(mmf_file_name.c_str(), "rb"); - if (!f) { - Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s' - could not open file", mmf_file_name.c_str()); - return false; - } - - uint32 file_version; - if (fread(&file_version, sizeof(uint32), 1, f) != 1) { - fclose(f); - Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s' - f@file_version", mmf_file_name.c_str()); - return false; - } - - uint32 rm_buffer_size; - if (fread(&rm_buffer_size, sizeof(uint32), 1, f) != 1) { - fclose(f); - Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s' - f@rm_buffer_size", mmf_file_name.c_str()); - return false; - } - - uint32 rm_buffer_crc32; - if (fread(&rm_buffer_crc32, sizeof(uint32), 1, f) != 1) { - fclose(f); - Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s' - f@rm_buffer_crc32", mmf_file_name.c_str()); - return false; - } - if (rm_buffer_crc32 != /*crc32_check*/ 0) { - fclose(f); - Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s' - bad rm_buffer checksum", mmf_file_name.c_str()); - return false; - } - - uint32 mmf_buffer_size; - if (fread(&mmf_buffer_size, sizeof(uint32), 1, f) != 1) { - fclose(f); - Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s' - f@mmf_buffer_size", mmf_file_name.c_str()); - return false; - } - - std::vector mmf_buffer(mmf_buffer_size); - if (fread(mmf_buffer.data(), mmf_buffer_size, 1, f) != 1) { - fclose(f); - Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s' - f@mmf_buffer", mmf_file_name.c_str()); - return false; - } - + uint32 file_version; + if (fread(&file_version, sizeof(uint32), 1, f) != 1) { fclose(f); - - std::vector rm_buffer(rm_buffer_size); - uint32 v = InflateData(mmf_buffer.data(), mmf_buffer_size, rm_buffer.data(), rm_buffer_size); - - if (imp) { - imp->rm->release(); - imp->rm = nullptr; - } - else { - imp = new impl; - } - - bool load_success = false; - imp->rm = loadRaycastMesh(rm_buffer, load_success); - if (imp->rm && !load_success) { - imp->rm->release(); - imp->rm = nullptr; - } - - if (!imp->rm) { - delete imp; - imp = nullptr; - Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s' - null RaycastMesh", mmf_file_name.c_str()); - return false; - } - - return true; + Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s' - f@file_version", mmf_file_name.c_str()); + return false; } - bool Map::SaveMMF(const std::string& map_file_name, bool force_mmf_overwrite) - { - if (!imp || !imp->rm) { - Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file - No implementation (map_file_name: '%s')", map_file_name.c_str()); - return false; - } - - std::string mmf_file_name = map_file_name; - strip_map_extension(mmf_file_name); - if (!add_mmf_extension(mmf_file_name)) { - Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s'", mmf_file_name.c_str()); - return false; - } - - FILE* f = fopen(mmf_file_name.c_str(), "rb"); - if (f) { - fclose(f); - if (!force_mmf_overwrite) - return true; - } - - std::vector rm_buffer; // size set in MyRaycastMesh::serialize() - serializeRaycastMesh(imp->rm, rm_buffer); - if (rm_buffer.empty()) { - Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s' - empty RaycastMesh buffer", mmf_file_name.c_str()); - return false; - } - - uint32 rm_buffer_size = rm_buffer.size(); - uint32 mmf_buffer_size = EstimateDeflateBuffer(rm_buffer.size()); - - std::vector mmf_buffer(mmf_buffer_size); - - mmf_buffer_size = DeflateData(rm_buffer.data(), rm_buffer.size(), mmf_buffer.data(), mmf_buffer.size()); - if (!mmf_buffer_size) { - Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s' - null MMF buffer size", mmf_file_name.c_str()); - return false; - } - - f = fopen(mmf_file_name.c_str(), "wb"); - if (!f) { - Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s' - could not open file", mmf_file_name.c_str()); - return false; - } - - uint32 file_version = 0; - if (fwrite(&file_version, sizeof(uint32), 1, f) != 1) { - fclose(f); - std::remove(mmf_file_name.c_str()); - Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s' - f@file_version", mmf_file_name.c_str()); - return false; - } - - if (fwrite(&rm_buffer_size, sizeof(uint32), 1, f) != 1) { - fclose(f); - std::remove(mmf_file_name.c_str()); - Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s' - f@rm_buffer_size", mmf_file_name.c_str()); - return false; - } - - uint32 rm_buffer_crc32 = 0; - if (fwrite(&rm_buffer_crc32, sizeof(uint32), 1, f) != 1) { - fclose(f); - std::remove(mmf_file_name.c_str()); - Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s' - f@rm_buffer_crc32", mmf_file_name.c_str()); - return false; - } - - if (fwrite(&mmf_buffer_size, sizeof(uint32), 1, f) != 1) { - fclose(f); - std::remove(mmf_file_name.c_str()); - Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s' - f@mmf_buffer_size", mmf_file_name.c_str()); - return false; - } - - if (fwrite(mmf_buffer.data(), mmf_buffer_size, 1, f) != 1) { - fclose(f); - std::remove(mmf_file_name.c_str()); - Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s' - f@mmf_buffer", mmf_file_name.c_str()); - return false; - } - + uint32 rm_buffer_size; + if (fread(&rm_buffer_size, sizeof(uint32), 1, f) != 1) { fclose(f); - - return true; + Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s' - f@rm_buffer_size", mmf_file_name.c_str()); + return false; } - #endif /*USE_MAP_MMFS*/ + uint32 rm_buffer_crc32; + if (fread(&rm_buffer_crc32, sizeof(uint32), 1, f) != 1) { + fclose(f); + Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s' - f@rm_buffer_crc32", mmf_file_name.c_str()); + return false; + } + if (rm_buffer_crc32 != /*crc32_check*/ 0) { + fclose(f); + Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s' - bad rm_buffer checksum", mmf_file_name.c_str()); + return false; + } + + uint32 mmf_buffer_size; + if (fread(&mmf_buffer_size, sizeof(uint32), 1, f) != 1) { + fclose(f); + Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s' - f@mmf_buffer_size", mmf_file_name.c_str()); + return false; + } + + std::vector mmf_buffer(mmf_buffer_size); + if (fread(mmf_buffer.data(), mmf_buffer_size, 1, f) != 1) { + fclose(f); + Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s' - f@mmf_buffer", mmf_file_name.c_str()); + return false; + } + + fclose(f); + + std::vector rm_buffer(rm_buffer_size); + uint32 v = InflateData(mmf_buffer.data(), mmf_buffer_size, rm_buffer.data(), rm_buffer_size); + + if (imp) { + imp->rm->release(); + imp->rm = nullptr; + } + else { + imp = new impl; + } + + bool load_success = false; + imp->rm = loadRaycastMesh(rm_buffer, load_success); + if (imp->rm && !load_success) { + imp->rm->release(); + imp->rm = nullptr; + } + + if (!imp->rm) { + delete imp; + imp = nullptr; + Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s' - null RaycastMesh", mmf_file_name.c_str()); + return false; + } + + return true; +} + +bool Map::SaveMMF(const std::string& map_file_name, bool force_mmf_overwrite) +{ + if (!imp || !imp->rm) { + Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file - No implementation (map_file_name: '%s')", map_file_name.c_str()); + return false; + } + + std::string mmf_file_name = map_file_name; + strip_map_extension(mmf_file_name); + if (!add_mmf_extension(mmf_file_name)) { + Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s'", mmf_file_name.c_str()); + return false; + } + + FILE* f = fopen(mmf_file_name.c_str(), "rb"); + if (f) { + fclose(f); + if (!force_mmf_overwrite) + return true; + } + + std::vector rm_buffer; // size set in MyRaycastMesh::serialize() + serializeRaycastMesh(imp->rm, rm_buffer); + if (rm_buffer.empty()) { + Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s' - empty RaycastMesh buffer", mmf_file_name.c_str()); + return false; + } + + uint32 rm_buffer_size = rm_buffer.size(); + uint32 mmf_buffer_size = EstimateDeflateBuffer(rm_buffer.size()); + + std::vector mmf_buffer(mmf_buffer_size); + + mmf_buffer_size = DeflateData(rm_buffer.data(), rm_buffer.size(), mmf_buffer.data(), mmf_buffer.size()); + if (!mmf_buffer_size) { + Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s' - null MMF buffer size", mmf_file_name.c_str()); + return false; + } + + f = fopen(mmf_file_name.c_str(), "wb"); + if (!f) { + Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s' - could not open file", mmf_file_name.c_str()); + return false; + } + + uint32 file_version = 0; + if (fwrite(&file_version, sizeof(uint32), 1, f) != 1) { + fclose(f); + std::remove(mmf_file_name.c_str()); + Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s' - f@file_version", mmf_file_name.c_str()); + return false; + } + + if (fwrite(&rm_buffer_size, sizeof(uint32), 1, f) != 1) { + fclose(f); + std::remove(mmf_file_name.c_str()); + Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s' - f@rm_buffer_size", mmf_file_name.c_str()); + return false; + } + + uint32 rm_buffer_crc32 = 0; + if (fwrite(&rm_buffer_crc32, sizeof(uint32), 1, f) != 1) { + fclose(f); + std::remove(mmf_file_name.c_str()); + Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s' - f@rm_buffer_crc32", mmf_file_name.c_str()); + return false; + } + + if (fwrite(&mmf_buffer_size, sizeof(uint32), 1, f) != 1) { + fclose(f); + std::remove(mmf_file_name.c_str()); + Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s' - f@mmf_buffer_size", mmf_file_name.c_str()); + return false; + } + + if (fwrite(mmf_buffer.data(), mmf_buffer_size, 1, f) != 1) { + fclose(f); + std::remove(mmf_file_name.c_str()); + Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s' - f@mmf_buffer", mmf_file_name.c_str()); + return false; + } + + fclose(f); + + return true; +} + +#endif /*USE_MAP_MMFS*/ } \ No newline at end of file diff --git a/zone/map.h b/zone/map.h index b21fba023..7dbede97d 100644 --- a/zone/map.h +++ b/zone/map.h @@ -30,7 +30,7 @@ #define BEST_Z_INVALID -99999 extern const ZoneConfig *Config; -namespace EQEmu +namespace EQEmu { class Map { @@ -43,6 +43,7 @@ namespace EQEmu bool LineIntersectsZone(glm::vec3 start, glm::vec3 end, float step, glm::vec3 *result) const; bool LineIntersectsZoneNoZLeaps(glm::vec3 start, glm::vec3 end, float step_mag, glm::vec3 *result) const; bool CheckLoS(glm::vec3 myloc, glm::vec3 oloc) const; + bool DoCollisionCheck(glm::vec3 myloc, glm::vec3 oloc, glm::vec3 &outnorm, float &distance) const; #ifdef USE_MAP_MMFS bool Load(std::string filename, bool force_mmf_overwrite = false); @@ -67,4 +68,4 @@ namespace EQEmu impl *imp; }; } -#endif +#endif \ No newline at end of file diff --git a/zone/mob.cpp b/zone/mob.cpp index 818148cc6..4a93a766e 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -117,7 +117,7 @@ Mob::Mob(const char* in_name, fix_z_timer(300), fix_z_timer_engaged(100), attack_anim_timer(1000), - position_update_melee_push_timer(1000), + position_update_melee_push_timer(500), mHateListCleanup(6000) { targeted = 0; @@ -395,6 +395,7 @@ Mob::Mob(const char* in_name, permarooted = (runspeed > 0) ? false : true; movetimercompleted = false; + ForcedMovement = 0; roamer = false; rooted = false; charmed = false; diff --git a/zone/mob.h b/zone/mob.h index 23c0004df..a160b2a45 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -50,6 +50,8 @@ struct AuraRecord; struct NewSpawn_Struct; struct PlayerPositionUpdateServer_Struct; +const int COLLISION_BOX_SIZE = 4; + namespace EQEmu { struct ItemData; @@ -578,6 +580,8 @@ public: void SetFlyMode(uint8 flymode); inline void Teleport(glm::vec3 NewPosition) { m_Position.x = NewPosition.x; m_Position.y = NewPosition.y; m_Position.z = NewPosition.z; }; + void TryMoveAlong(float distance, float angle, bool send = true); + void ProcessForcedMovement(); //AI static uint32 GetLevelCon(uint8 mylevel, uint8 iOtherLevel); @@ -1313,6 +1317,9 @@ protected: char lastname[64]; glm::vec4 m_Delta; + // just locs around them to double check, if we do expand collision this should be cached on movement + // ideally we should use real models, but this should be quick and work mostly + glm::vec4 m_CollisionBox[COLLISION_BOX_SIZE]; EQEmu::LightSourceProfile m_Light; @@ -1423,6 +1430,7 @@ protected: std::unique_ptr AI_movement_timer; std::unique_ptr AI_target_check_timer; bool movetimercompleted; + int8 ForcedMovement; // push bool permarooted; std::unique_ptr AI_scan_area_timer; std::unique_ptr AI_walking_timer; diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index ed55d1146..18fc5d441 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -29,12 +29,16 @@ #include "quest_parser_collection.h" #include "string_ids.h" #include "water_map.h" +#include "fastmath.h" +#include #include #include +#include #include extern EntityList entity_list; +extern FastMath g_Math; extern Zone *zone; @@ -918,29 +922,26 @@ void Client::AI_Process() } } - if(IsPet()) - { - Mob* owner = GetOwner(); - if(owner == nullptr) + if (IsPet()) { + Mob *owner = GetOwner(); + if (owner == nullptr) return; float dist = DistanceSquared(m_Position, owner->GetPosition()); - if (dist >= 400) - { - if(AI_movement_timer->Check()) - { - int nspeed = (dist >= 5625 ? GetRunspeed() : GetWalkspeed()); + if (dist >= 202500) { // >= 450 distance + Teleport(static_cast(owner->GetPosition())); + SendPositionUpdate(); // this shouldn't happen a lot (and hard to make it) so lets not rate limit + } else if (dist >= 400) { // >=20 + if (AI_movement_timer->Check()) { + int nspeed = (dist >= 1225 ? GetRunspeed() : GetWalkspeed()); // >= 35 animation = nspeed; nspeed *= 2; SetCurrentSpeed(nspeed); CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), nspeed); } - } - else - { - if(moved) - { + } else { + if (moved) { SetCurrentSpeed(0); moved = false; } @@ -949,6 +950,71 @@ void Client::AI_Process() } } +void Mob::ProcessForcedMovement() +{ + // we are being pushed, we will hijack this movement timer + // this also needs to be done before casting to have a chance to interrupt + // this flag won't be set if the mob can't be pushed (rooted etc) + if (AI_movement_timer->Check()) { + bool bPassed = true; + auto z_off = GetZOffset(); + glm::vec3 normal; + glm::vec3 new_pos = m_Position + m_Delta; + + // no zone map = fucked + if (zone->HasMap()) { + // in front + m_CollisionBox[0].x = m_Position.x + 3.0f * g_Math.FastSin(0.0f); + m_CollisionBox[0].y = m_Position.y + 3.0f * g_Math.FastCos(0.0f); + m_CollisionBox[0].z = m_Position.z + z_off; + + // to right + m_CollisionBox[1].x = m_Position.x + 3.0f * g_Math.FastSin(128.0f); + m_CollisionBox[1].y = m_Position.y + 3.0f * g_Math.FastCos(128.0f); + m_CollisionBox[1].z = m_Position.z + z_off; + + // behind + m_CollisionBox[2].x = m_Position.x + 3.0f * g_Math.FastSin(256.0f); + m_CollisionBox[2].y = m_Position.y + 3.0f * g_Math.FastCos(256.0f); + m_CollisionBox[2].z = m_Position.z + z_off; + + // to left + m_CollisionBox[3].x = m_Position.x + 3.0f * g_Math.FastSin(384.0f); + m_CollisionBox[3].y = m_Position.y + 3.0f * g_Math.FastCos(384.0f); + m_CollisionBox[3].z = m_Position.z + z_off; + + // collision happened, need to move along the wall + float distance = 0.0f, shortest = std::numeric_limits::infinity(); + glm::vec3 tmp_nrm; + for (auto &vec : m_CollisionBox) { + if (zone->zonemap->DoCollisionCheck(vec, new_pos, tmp_nrm, distance)) { + bPassed = false; // lets try with new projection next pass + if (distance < shortest) { + normal = tmp_nrm; + shortest = distance; + } + } + } + } + + if (bPassed) { + ForcedMovement = 0; + m_Delta = glm::vec4(); + Teleport(new_pos); + SendPositionUpdate(); + pLastChange = Timer::GetCurrentTime(); + FixZ(); // so we teleport to the ground locally, we want the client to interpolate falling etc + } else if (--ForcedMovement) { + auto proj = glm::proj(static_cast(m_Delta), normal); + m_Delta.x -= proj.x; + m_Delta.y -= proj.y; + m_Delta.z -= proj.z; + } else { + m_Delta = glm::vec4(); // well, we failed to find a spot to be forced to, lets give up + } + } +} + void Mob::AI_Process() { if (!IsAIControlled()) return; @@ -956,6 +1022,7 @@ void Mob::AI_Process() { if (!(AI_think_timer->Check() || attack_timer.Check(false))) return; + if (IsCasting()) return; @@ -1428,10 +1495,10 @@ void Mob::AI_Process() { if (dist >= 400 || distz > 100) { int speed = GetWalkspeed(); - if (dist >= 5625) + if (dist >= 1225) // 35 speed = GetRunspeed(); - if (distz > 100) + if (dist >= 202500 || distz > 100) // dist >= 450 { m_Position = ownerPos; SendPositionUpdate(); @@ -2074,15 +2141,34 @@ bool Mob::Rampage(ExtraAttackOptions *opts) if (m_target) { if (m_target == GetTarget()) continue; - if (CombatRange(m_target)) { + if (DistanceSquaredNoZ(GetPosition(), m_target->GetPosition()) <= NPC_RAMPAGE_RANGE2) { ProcessAttackRounds(m_target, opts); index_hit++; } } } - if (RuleB(Combat, RampageHitsTarget) && index_hit < rampage_targets) - ProcessAttackRounds(GetTarget(), opts); + if (RuleB(Combat, RampageHitsTarget)) { + if (index_hit < rampage_targets) + ProcessAttackRounds(GetTarget(), opts); + } else { // let's do correct behavior here, if they set above rule we can assume they want non-live like behavior + if (index_hit < rampage_targets) { + // so we go over in reverse order and skip range check + // lets do it this way to still support non-live-like >1 rampage targets + // likely live is just a fall through of the last valid mob + for (auto i = RampageArray.crbegin(); i != RampageArray.crend(); ++i) { + if (index_hit >= rampage_targets) + break; + auto m_target = entity_list.GetMob(*i); + if (m_target) { + if (m_target == GetTarget()) + continue; + ProcessAttackRounds(m_target, opts); + index_hit++; + } + } + } + } m_specialattacks = eSpecialAttacks::None; diff --git a/zone/nats_manager.cpp b/zone/nats_manager.cpp index 5a12a65dd..6f8eaf7a9 100644 --- a/zone/nats_manager.cpp +++ b/zone/nats_manager.cpp @@ -679,7 +679,6 @@ void NatsManager::OnSpawnEvent(const EmuOpcode op, uint32 entity_id, Spawn_Struc event.set_equip_chest2(spawn->equip_chest2); event.set_mount_color(spawn->mount_color); event.set_spawnid(spawn->spawnId); - event.set_unknown0344(*spawn->unknown0344); event.set_ismercenary(spawn->IsMercenary); //event.set_equipment_tint(spawn->equipment_tint); event.set_lfg(spawn->lfg); @@ -800,8 +799,8 @@ void NatsManager::OnDamageEvent(uint32 entity_id, CombatDamage_Struct *cd) { event.set_spellid(cd->spellid); event.set_damage(cd->damage); event.set_force(cd->force); - event.set_meleepush_xy(cd->meleepush_xy); - event.set_meleepush_z(cd->meleepush_z); + event.set_meleepush_xy(cd->hit_heading); + event.set_meleepush_z(cd->hit_pitch); if (!event.SerializeToString(&pubMessage)) { Log(Logs::General, Logs::NATS, "Failed to serialize message to string"); return; } auto finalEvent = eqproto::Event(); diff --git a/zone/npc.cpp b/zone/npc.cpp index 9f50e7ff9..c872fb680 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -583,9 +583,7 @@ void NPC::QueryLoot(Client* to) linker.SetLinkType(EQEmu::saylink::SayLinkLootItem); linker.SetLootData(*cur); - auto item_link = linker.GenerateLink(); - - to->Message(0, "%s, ID: %u, Level: (min: %u, max: %u)", item_link.c_str(), (*cur)->item_id, (*cur)->min_level, (*cur)->max_level); + to->Message(0, "%s, ID: %u, Level: (min: %u, max: %u)", linker.GenerateLink().c_str(), (*cur)->item_id, (*cur)->min_level, (*cur)->max_level); } to->Message(0, "%i items on %s.", x, GetName()); @@ -764,6 +762,10 @@ bool NPC::Process() reface_timer->Disable(); } + // needs to be done before mez and stun + if (ForcedMovement) + ProcessForcedMovement(); + if (IsMezzed()) return true; diff --git a/zone/pathing.cpp b/zone/pathing.cpp index 0bc054d58..7f2d8c880 100644 --- a/zone/pathing.cpp +++ b/zone/pathing.cpp @@ -560,7 +560,8 @@ void PathManager::SpawnPathNodes() sprintf(npc_type->lastname, "%i", PathNodes[i].id); npc_type->cur_hp = 4000000; npc_type->max_hp = 4000000; - npc_type->race = 151; + npc_type->race = 2253; + npc_type->size = 3.0f; npc_type->gender = 2; npc_type->class_ = 9; npc_type->deity= 1; @@ -1377,7 +1378,7 @@ void PathManager::ShowPathNodeNeighbours(Client *c) Mob *m = entity_list.GetMob(Name); if(m) - m->SendIllusionPacket(151); + m->ChangeSize(3.0f); } std::stringstream Neighbours; @@ -1401,7 +1402,7 @@ void PathManager::ShowPathNodeNeighbours(Client *c) Mob *m = entity_list.GetMob(Name); if(m) - m->SendIllusionPacket(46); + m->ChangeSize(5.0f); } c->Message(0, "Neighbours: %s", Neighbours.str().c_str()); } @@ -1560,7 +1561,8 @@ int32 PathManager::AddNode(float x, float y, float z, float best_z, int32 reques sprintf(npc_type->lastname, "%i", new_id); npc_type->cur_hp = 4000000; npc_type->max_hp = 4000000; - npc_type->race = 151; + npc_type->race = 2253; + npc_type->size = 3.0f; npc_type->gender = 2; npc_type->class_ = 9; npc_type->deity= 1; @@ -1621,7 +1623,8 @@ int32 PathManager::AddNode(float x, float y, float z, float best_z, int32 reques sprintf(npc_type->lastname, "%i", new_id); npc_type->cur_hp = 4000000; npc_type->max_hp = 4000000; - npc_type->race = 151; + npc_type->race = 2253; + npc_type->size = 3.0f; npc_type->gender = 2; npc_type->class_ = 9; npc_type->deity= 1; diff --git a/zone/pets.cpp b/zone/pets.cpp index bbc4f8eea..be17a3293 100644 --- a/zone/pets.cpp +++ b/zone/pets.cpp @@ -28,6 +28,8 @@ #include "pets.h" #include "zonedb.h" +#include + #ifdef BOTS #include "bot.h" #endif @@ -38,82 +40,42 @@ #endif -const char *GetRandPetName() +// need to pass in a char array of 64 chars +void GetRandPetName(char *name) { - static const char *petnames[] = { "Gabaner","Gabann","Gabantik","Gabarab","Gabarer","Gabarn","Gabartik", - "Gabekab","Gabeker","Gabekn","Gaber","Gabn","Gabobab","Gabobn","Gabtik", - "Ganer","Gann","Gantik","Garab","Garaner","Garann","Garantik","Gararn", - "Garekn","Garer","Garn","Gartik","Gasaner","Gasann","Gasantik","Gasarer", - "Gasartik","Gasekn","Gaser","Gebann","Gebantik","Gebarer","Gebarn","Gebartik", - "Gebeker","Gebekn","Gebn","Gekab","Geker","Gekn","Genaner","Genann","Genantik", - "Genarer","Genarn","Gener","Genn","Genobtik","Gibaner","Gibann","Gibantik", - "Gibarn","Gibartik","Gibekn","Giber","Gibn","Gibobtik","Gibtik","Gobaber", - "Gobaner","Gobann","Gobarn","Gobartik","Gober","Gobn","Gobober","Gobobn", - "Gobobtik","Gobtik","Gonaner","Gonann","Gonantik","Gonarab","Gonarer", - "Gonarn","Gonartik","Gonekab","Gonekn","Goner","Gonobtik","Gontik","Gotik", - "Jabaner","Jabann","Jabantik","Jabarab","Jabarer","Jabarn","Jabartik", - "Jabekab","Jabeker","Jabekn","Jaber","Jabn","Jabobtik","Jabtik","Janab", - "Janer","Jann","Jantik","Jarab","Jaranab","Jaraner","Jararer","Jararn", - "Jarartik","Jareker","Jarekn","Jarer","Jarn","Jarobn","Jarobtik","Jartik", - "Jasab","Jasaner","Jasantik","Jasarer","Jasartik","Jasekab","Jaseker", - "Jasekn","Jaser","Jasn","Jasobab","Jasober","Jastik","Jebanab","Jebann", - "Jebantik","Jebarab","Jebarar","Jebarer","Jebarn","Jebartik","Jebeker", - "Jebekn","Jeber","Jebobn","Jebtik","Jekab","Jeker","Jekn","Jenann", - "Jenantik","Jenarer","Jeneker","Jenekn","Jentik","Jibaner","Jibann", - "Jibantik","Jibarer","Jibarn","Jibartik","Jibeker","Jibn","Jibobn", - "Jibtik","Jobab","Jobaner","Jobann","Jobantik","Jobarn","Jobartik", - "Jobekab","Jobeker","Jober","Jobn","Jobtik","Jonanab","Jonaner", - "Jonann","Jonantik","Jonarer","Jonarn","Jonartik","Jonekab","Joneker", - "Jonekn","Joner","Jonn","Jonnarn","Jonober","Jonobn","Jonobtik","Jontik", - "Kabanab","Kabaner","Kabann","Kabantik","Kabarer","Kabarn","Kabartik", - "Kabeker","Kabekn","Kaber","Kabn","Kabober","Kabobn","Kabobtik","Kabtik", - "Kanab","Kaner","Kann","Kantik","Karab","Karanab","Karaner","Karann", - "Karantik","Kararer","Karartik","Kareker","Karer","Karn","Karobab","Karobn", - "Kartik","Kasaner","Kasann","Kasarer","Kasartik","Kaseker","Kasekn","Kaser", - "Kasn","Kasober","Kastik","Kebann","Kebantik","Kebarab","Kebartik","Kebeker", - "Kebekn","Kebn","Kebobab","Kebtik","Kekab","Keker","Kekn","Kenab","Kenaner", - "Kenantik","Kenarer","Kenarn","Keneker","Kener","Kenn","Kenobn","Kenobtik", - "Kentik","Kibab","Kibaner","Kibantik","Kibarn","Kibartik","Kibekab","Kibeker", - "Kibekn","Kibn","Kibobn","Kibobtik","Kobab","Kobanab","Kobaner","Kobann", - "Kobantik","Kobarer","Kobarn","Kobartik","Kobeker","Kobekn","Kober","Kobn", - "Kobober","Kobobn","Kobtik","Konanab","Konaner","Konann","Konantik","Konarab", - "Konarer","Konarn","Konekab","Koneker","Konekn","Koner","Konn","Konobn", - "Konobtik","Kontik","Labanab","Labaner","Labann","Labarab","Labarer", - "Labarn","Labartik","Labeker","Labekn","Laner","Lann","Larab","Larantik", - "Lararer","Lararn","Larartik","Lareker","Larer","Larn","Lartik","Lasaner", - "Lasann","Lasarer","Laseker","Laser","Lasik","Lasn","Lastik","Lebaner", - "Lebarer","Lebartik","Lebekn","Lebtik","Lekab","Lekn","Lenanab","Lenaner", - "Lenann","Lenartik","Lenekab","Leneker","Lenekn","Lentik","Libab","Libaner", - "Libann","Libantik","Libarer","Libarn","Libartik","Libeker","Libekn","Lobann", - "Lobarab","Lobarn","Lobartik","Lobekn","Lobn","Lobober","Lobobn","Lobtik", - "Lonaner","Lonann","Lonantik","Lonarab","Lonarer","Lonarn","Lonartik","Lonekn", - "Loner","Lonobtik","Lontik","Vabanab","Vabaner","Vabann","Vabantik","Vabarer", - "Vabarn","Vabartik","Vabeker","Vabekn","Vabtik","Vanikk","Vann","Varartik","Varn", - "Vartik","Vasann","Vasantik","Vasarab","Vasarer","Vaseker","Vebaner","Vebantik", - "Vebarab","Vebeker","Vebekn","Vebobn","Vekab","Veker","Venaner","Venantik","Venar", - "Venarn","Vener","Ventik","Vibann","Vibantik","Viber","Vibobtik","Vobann", - "Vobarer","Vobartik","Vobekn","Vober","Vobn","Vobtik","Vonaner","Vonann", - "Vonantik","Vonarab","Vonarn","Vonartik","Voneker","Vonn","Xabanab","Xabaner", - "Xabarer","Xabarn","Xabartik","Xabekab","Xabeker","Xabekn","Xaber","Xabober", - "Xaner","Xann","Xarab","Xaranab","Xarann","Xarantik","Xararer","Xarartik","Xarer", - "Xarn","Xartik","Xasaner","Xasann","Xasarab","Xasarn","Xasekab","Xaseker", - "Xebarer","Xebarn","Xebeker","Xeber","Xebober","Xebtik","Xekab","Xeker", - "Xekn","Xenann","Xenantik","Xenarer","Xenartik","Xenekn","Xener","Xenober", - "Xentik","Xibantik","Xibarer","Xibekab","Xibeker","Xibobab","Xibober","Xibobn", - "Xobaner","Xobann","Xobarab","Xobarn","Xobekab","Xobeker","Xobekn","Xober", - "Xobn","Xobobn","Xobtik","Xonaner","Xonann","Xonantik","Xonarer","Xonartik", - "Xonekab","Xoneker","Xonekn","Xoner","Xonober","Xtik","Zabaner","Zabantik", - "Zabarab","Zabekab","Zabekn","Zaber","Zabn","Zabobab","Zabober","Zabtik", - "Zaner","Zantik","Zarann","Zarantik","Zararn","Zarartik","Zareker","Zarekn", - "Zarer","Zarn","Zarober","Zartik","Zasaner","Zasarer","Zaseker","Zasekn","Zasn", - "Zebantik","Zebarer","Zebarn","Zebartik","Zebobab","Zekab","Zekn","Zenann", - "Zenantik","Zenarer","Zenarn","Zenekab","Zeneker","Zenobtik","Zibanab","Zibaner", - "Zibann","Zibarer","Zibartik","Zibekn","Zibn","Zibobn","Zobaner","Zobann", - "Zobarn","Zober","Zobn","Zonanab","Zonaner","Zonann","Zonantik","Zonarer", - "Zonartik","Zonobn","Zonobtik","Zontik","Ztik" }; - int r = zone->random.Int(0, (sizeof(petnames)/sizeof(const char *))-1); - printf("Pet being created: %s\n",petnames[r]); // DO NOT COMMENT THIS OUT! - return petnames[r]; + std::string temp; + temp.reserve(64); + // note these orders are used to make the exclusions cheap :P + static const char *part1[] = {"G", "J", "K", "L", "V", "X", "Z"}; + static const char *part2[] = {nullptr, "ab", "ar", "as", "eb", "en", "ib", "ob", "on"}; + static const char *part3[] = {nullptr, "an", "ar", "ek", "ob"}; + static const char *part4[] = {"er", "ab", "n", "tik"}; + + const char *first = part1[zone->random.Int(0, (sizeof(part1) / sizeof(const char *)) - 1)]; + const char *second = part2[zone->random.Int(0, (sizeof(part2) / sizeof(const char *)) - 1)]; + const char *third = part3[zone->random.Int(0, (sizeof(part3) / sizeof(const char *)) - 1)]; + const char *fourth = part4[zone->random.Int(0, (sizeof(part4) / sizeof(const char *)) - 1)]; + + // if both of these are empty, we would get an illegally short name + if (second == nullptr && third == nullptr) + fourth = part4[(sizeof(part4) / sizeof(const char *)) - 1]; + + // "ektik" isn't allowed either I guess? + if (third == part3[3] && fourth == part4[3]) + fourth = part4[zone->random.Int(0, (sizeof(part4) / sizeof(const char *)) - 2)]; + + // "Laser" isn't allowed either I guess? + if (first == part1[3] && second == part2[3] && third == nullptr && fourth == part4[0]) + fourth = part4[zone->random.Int(1, (sizeof(part4) / sizeof(const char *)) - 2)]; + + temp += first; + if (second != nullptr) + temp += second; + if (third != nullptr) + temp += third; + temp += fourth; + + strn0cpy(name, temp.c_str(), 64); } //not used anymore @@ -325,7 +287,7 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, } else if (record.petnaming == 4) { // Keep the DB name } else if (record.petnaming == 3 && IsClient()) { - strcpy(npc_type->name, GetRandPetName()); + GetRandPetName(npc_type->name); } else if (record.petnaming == 5 && IsClient()) { strcpy(npc_type->name, this->GetName()); npc_type->name[24] = '\0'; diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 6c0bdfe0a..569afcd42 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1319,9 +1319,7 @@ void QuestManager::itemlink(int item_id) { linker.SetLinkType(EQEmu::saylink::SayLinkItemData); linker.SetItemData(item); - auto item_link = linker.GenerateLink(); - - initiator->Message(0, "%s tells you, %s", owner->GetCleanName(), item_link.c_str()); + initiator->Message(0, "%s tells you, %s", owner->GetCleanName(), linker.GenerateLink().c_str()); } } @@ -1989,8 +1987,8 @@ void QuestManager::npcfeature(char *feature, int setting) QuestManagerCurrentQuestVars(); uint16 Race = owner->GetRace(); uint8 Gender = owner->GetGender(); - uint8 Texture = 0xFF; - uint8 HelmTexture = 0xFF; + uint8 Texture = owner->GetTexture(); + uint8 HelmTexture = owner->GetHelmTexture(); uint8 HairColor = owner->GetHairColor(); uint8 BeardColor = owner->GetBeardColor(); uint8 EyeColor1 = owner->GetEyeColor1(); @@ -2549,9 +2547,8 @@ const char* QuestManager::varlink(char* perltext, int item_id) { linker.SetLinkType(EQEmu::saylink::SayLinkItemData); linker.SetItemData(item); - auto item_link = linker.GenerateLink(); - strcpy(perltext, item_link.c_str()); // link length is currently ranged from 1 to 250 in TextLink::GenerateLink() - + strcpy(perltext, linker.GenerateLink().c_str()); + return perltext; } @@ -2773,8 +2770,7 @@ const char* QuestManager::saylink(char* Phrase, bool silent, const char* LinkNam linker.SetProxyAugment1ID(sayid); linker.SetProxyText(LinkName); - auto say_link = linker.GenerateLink(); - strcpy(Phrase, say_link.c_str()); // link length is currently ranged from 1 to 250 in TextLink::GenerateLink() + strcpy(Phrase, linker.GenerateLink().c_str()); return Phrase; } diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 03b144b5a..7c91c97a7 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -1443,7 +1443,7 @@ void Mob::SendItemAnimation(Mob *to, const EQEmu::ItemData *item, EQEmu::skills: //these angle and tilt used together seem to make the arrow/knife throw as straight as I can make it - as->launch_angle = CalculateHeadingToTarget(to->GetX(), to->GetY()) * 2; + as->launch_angle = CalculateHeadingToTarget(to->GetX(), to->GetY()); as->tilt = 125; as->arc = 50; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index b44cf987d..d258df899 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -915,16 +915,16 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove action->source = caster ? caster->GetID() : GetID(); action->level = 65; action->instrument_mod = 10; - action->sequence = static_cast((GetHeading() * 12345 / 2)); + action->hit_heading = GetHeading(); action->type = 231; action->spell = spell_id; - action->buff_unknown = 4; + action->effect_flag = 4; cd->target = action->target; cd->source = action->source; cd->type = action->type; cd->spellid = action->spell; - cd->meleepush_xy = action->sequence; + cd->hit_heading = action->hit_heading; CastToClient()->QueuePacket(action_packet); if(caster && caster->IsClient() && caster != this) @@ -967,16 +967,16 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove action->source = caster ? caster->GetID() : GetID(); action->level = 65; action->instrument_mod = 10; - action->sequence = static_cast((GetHeading() * 12345 / 2)); + action->hit_heading = GetHeading(); action->type = 231; action->spell = spell_id; - action->buff_unknown = 4; + action->effect_flag = 4; cd->target = action->target; cd->source = action->source; cd->type = action->type; cd->spellid = action->spell; - cd->meleepush_xy = action->sequence; + cd->hit_heading = action->hit_heading; CastToClient()->QueuePacket(action_packet); if(caster->IsClient() && caster != this) @@ -1006,16 +1006,16 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove action->source = caster ? caster->GetID() : GetID(); action->level = 65; action->instrument_mod = 10; - action->sequence = static_cast((GetHeading() * 12345 / 2)); + action->hit_heading = GetHeading(); action->type = 231; action->spell = spell_id; - action->buff_unknown = 4; + action->effect_flag = 4; cd->target = action->target; cd->source = action->source; cd->type = action->type; cd->spellid = action->spell; - cd->meleepush_xy = action->sequence; + cd->hit_heading = action->hit_heading; CastToClient()->QueuePacket(action_packet); if(caster->IsClient() && caster != this) @@ -2080,61 +2080,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Toss Up: %d", effect_value); #endif - double toss_amt = (double)spells[spell_id].base[i]; - if(toss_amt < 0) - toss_amt = -toss_amt; - - if(IsNPC()) - { - Stun(static_cast(toss_amt)); + if (IsNPC()) { + Damage(caster, std::abs(effect_value), spell_id, spell.skill, false, buffslot, false); } - toss_amt = sqrt(toss_amt)-2.0; - - if(toss_amt < 0.0) - toss_amt = 0.0; - - if(toss_amt > 20.0) - toss_amt = 20.0; - - if(IsClient()) - { - CastToClient()->SetKnockBackExemption(true); - } - - double look_heading = GetHeading(); - look_heading /= 256; - look_heading *= 360; - look_heading += 180; - if(look_heading > 360) - look_heading -= 360; - - //x and y are crossed mkay - double new_x = spells[spell_id].pushback * sin(double(look_heading * 3.141592 / 180.0)); - double new_y = spells[spell_id].pushback * cos(double(look_heading * 3.141592 / 180.0)); - - auto outapp_push = - new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); - PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)outapp_push->pBuffer; - - spu->spawn_id = GetID(); - spu->x_pos = FloatToEQ19(GetX()); - spu->y_pos = FloatToEQ19(GetY()); - spu->z_pos = FloatToEQ19(GetZ()); - spu->delta_x = FloatToEQ13(new_x); - spu->delta_y = FloatToEQ13(new_y); - spu->delta_z = FloatToEQ13(toss_amt); - spu->heading = FloatToEQ12(GetHeading()); - spu->padding0002 =0; - spu->padding0006 =7; - spu->padding0014 =0x7f; - spu->padding0018 =0x5df27; - spu->animation = 0; - spu->delta_heading = FloatToEQ10(0); - outapp_push->priority = 5; - entity_list.QueueClients(this, outapp_push, true); - if(IsClient()) - CastToClient()->FastQueuePacket(&outapp_push); - break; } diff --git a/zone/spells.cpp b/zone/spells.cpp index 59cec489d..cb47fe30a 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -82,6 +82,7 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) #include "string_ids.h" #include "worldserver.h" #include "nats_manager.h" +#include "fastmath.h" #include #include @@ -106,6 +107,7 @@ extern Zone* zone; extern volatile bool is_zone_loaded; extern WorldServer worldserver; extern NatsManager nats; +extern FastMath g_Math; using EQEmu::CastingSlot; @@ -1213,7 +1215,10 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo // handle the components for traditional casters else { - if(c->GetInv().HasItem(component, component_count, invWhereWorn|invWherePersonal) == -1) // item not found + if (!RuleB(Character, PetsUseReagents) && IsEffectInSpell(spell_id, SE_SummonPet)) { + //bypass reagent cost + } + else if(c->GetInv().HasItem(component, component_count, invWhereWorn|invWherePersonal) == -1) // item not found { if (!missingreags) { @@ -1243,6 +1248,9 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo return; } } + else if (!RuleB(Character, PetsUseReagents) && IsEffectInSpell(spell_id, SE_SummonPet)) { + //bypass reagent cost + } else if (!bard_song_mode) { int noexpend; @@ -2651,20 +2659,18 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) { action->source = caster->GetID(); action->target = GetID(); action->spell = spell_id; - action->sequence = (uint32) (GetHeading()); // just some random number + action->force = spells[spell_id].pushback; + action->hit_heading = GetHeading(); + action->hit_pitch = spells[spell_id].pushup; action->instrument_mod = caster->GetInstrumentMod(spell_id); - action->buff_unknown = 0; - action->level = buffs[buffs_i].casterlevel; + action->effect_flag = 0; + action->spell_level = action->level = buffs[buffs_i].casterlevel; action->type = DamageTypeSpell; entity_list.QueueCloseClients(this, packet, false, RuleI(Range, SongMessages), 0, true, IsClient() ? FilterPCSpells : FilterNPCSpells); - action->buff_unknown = 4; + action->effect_flag = 4; - if(IsEffectInSpell(spell_id, SE_TossUp)) - { - action->buff_unknown = 0; - } - else if(spells[spell_id].pushback > 0 || spells[spell_id].pushup > 0) + if(spells[spell_id].pushback != 0.0f || spells[spell_id].pushup != 0.0f) { if(IsClient()) { @@ -2672,38 +2678,6 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) { { CastToClient()->SetKnockBackExemption(true); - action->buff_unknown = 0; - auto outapp_push = new EQApplicationPacket( - OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); - PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)outapp_push->pBuffer; - - double look_heading = caster->CalculateHeadingToTarget(GetX(), GetY()); - look_heading /= 256; - look_heading *= 360; - if(look_heading > 360) - look_heading -= 360; - - //x and y are crossed mkay - double new_x = spells[spell_id].pushback * sin(double(look_heading * 3.141592 / 180.0)); - double new_y = spells[spell_id].pushback * cos(double(look_heading * 3.141592 / 180.0)); - - spu->spawn_id = GetID(); - spu->x_pos = FloatToEQ19(GetX()); - spu->y_pos = FloatToEQ19(GetY()); - spu->z_pos = FloatToEQ19(GetZ()); - spu->delta_x = FloatToEQ13(new_x); - spu->delta_y = FloatToEQ13(new_y); - spu->delta_z = FloatToEQ13(spells[spell_id].pushup); - spu->heading = FloatToEQ12(GetHeading()); - spu->padding0002 =0; - spu->padding0006 =7; - spu->padding0014 =0x7f; - spu->padding0018 =0x5df27; - spu->animation = 0; - spu->delta_heading = FloatToEQ10(0); - outapp_push->priority = 6; - entity_list.QueueClients(this, outapp_push, true); - CastToClient()->FastQueuePacket(&outapp_push); } } } @@ -2724,7 +2698,9 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) { cd->source = action->source; cd->type = DamageTypeSpell; cd->spellid = action->spell; - cd->meleepush_xy = action->sequence; + cd->force = action->force; + cd->hit_heading = action->hit_heading; + cd->hit_pitch = action->hit_pitch; cd->damage = 0; if(!IsEffectInSpell(spell_id, SE_BindAffinity)) { @@ -3532,12 +3508,14 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r action->target = spelltar->GetID(); } - action->level = caster_level; // caster level, for animation only + action->spell_level = action->level = caster_level; // caster level, for animation only action->type = 231; // 231 means a spell action->spell = spell_id; - action->sequence = (uint32) (GetHeading()); // just some random number + action->force = spells[spell_id].pushback; + action->hit_heading = GetHeading(); + action->hit_pitch = spells[spell_id].pushup; action->instrument_mod = GetInstrumentMod(spell_id); - action->buff_unknown = 0; + action->effect_flag = 0; if(spelltar != this && spelltar->IsClient()) // send to target spelltar->CastToClient()->QueuePacket(action_packet); @@ -3964,53 +3942,21 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r // NOTE: this is what causes the buff icon to appear on the client, if // this is a buff - but it sortof relies on the first packet. // the complete sequence is 2 actions and 1 damage message - action->buff_unknown = 0x04; // this is a success flag + action->effect_flag = 0x04; // this is a success flag - if(IsEffectInSpell(spell_id, SE_TossUp)) - { - action->buff_unknown = 0; - } - else if(spells[spell_id].pushback > 0 || spells[spell_id].pushup > 0) + if(spells[spell_id].pushback != 0.0f || spells[spell_id].pushup != 0.0f) { if(spelltar->IsClient()) { if(!IsBuffSpell(spell_id)) { spelltar->CastToClient()->SetKnockBackExemption(true); - - action->buff_unknown = 0; - auto outapp_push = - new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); - PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)outapp_push->pBuffer; - - double look_heading = CalculateHeadingToTarget(spelltar->GetX(), spelltar->GetY()); - look_heading /= 256; - look_heading *= 360; - if(look_heading > 360) - look_heading -= 360; - - //x and y are crossed mkay - double new_x = spells[spell_id].pushback * sin(double(look_heading * 3.141592 / 180.0)); - double new_y = spells[spell_id].pushback * cos(double(look_heading * 3.141592 / 180.0)); - - spu->spawn_id = spelltar->GetID(); - spu->x_pos = FloatToEQ19(spelltar->GetX()); - spu->y_pos = FloatToEQ19(spelltar->GetY()); - spu->z_pos = FloatToEQ19(spelltar->GetZ()); - spu->delta_x = FloatToEQ13(new_x); - spu->delta_y = FloatToEQ13(new_y); - spu->delta_z = FloatToEQ13(spells[spell_id].pushup); - spu->heading = FloatToEQ12(spelltar->GetHeading()); - spu->padding0002 =0; - spu->padding0006 =7; - spu->padding0014 =0x7f; - spu->padding0018 =0x5df27; - spu->animation = 0; - spu->delta_heading = FloatToEQ10(0); - outapp_push->priority = 6; - entity_list.QueueClients(this, outapp_push, true); - spelltar->CastToClient()->FastQueuePacket(&outapp_push); } + } else if (RuleB(Spells, NPCSpellPush) && !spelltar->IsRooted() && spelltar->ForcedMovement == 0) { + spelltar->m_Delta.x += action->force * g_Math.FastSin(action->hit_heading); + spelltar->m_Delta.y += action->force * g_Math.FastCos(action->hit_heading); + spelltar->m_Delta.z += action->hit_pitch; + spelltar->ForcedMovement = 6; } } @@ -4038,7 +3984,9 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r cd->source = action->source; cd->type = action->type; cd->spellid = action->spell; - cd->meleepush_xy = action->sequence; + cd->force = action->force; + cd->hit_heading = action->hit_heading; + cd->hit_pitch = action->hit_pitch; cd->damage = 0; if(!IsEffectInSpell(spell_id, SE_BindAffinity)){ entity_list.QueueCloseClients( @@ -5722,7 +5670,7 @@ void Client::SendSpellAnim(uint16 targetid, uint16 spell_id) a->source = this->GetID(); a->type = 231; a->spell = spell_id; - a->sequence = 231; + a->hit_heading = GetHeading(); app.priority = 1; entity_list.QueueCloseClients(this, &app, false, RuleI(Range, SpellParticles)); diff --git a/zone/string_ids.h b/zone/string_ids.h index 997858bed..7786d12dc 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -120,6 +120,7 @@ #define FAIL_DISARM_DETECTED_TRAP 370 //You fail to disarm the detected trap. #define LOOT_LORE_ERROR 371 //You cannot loot this Lore Item. You already have one. #define PICK_LORE 379 //You cannot pick up a lore item you already possess. +#define POISON_TOO_HIGH 382 // This poison is too high level for you to apply. #define CONSENT_DENIED 390 //You do not have consent to summon that corpse. #define DISCIPLINE_RDY 393 //You are ready to use a new discipline now. #define CONSENT_INVALID_NAME 397 //Not a valid consent name. diff --git a/zone/tasks.cpp b/zone/tasks.cpp index bcddfcf0a..06e3b4c59 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -2804,8 +2804,7 @@ void TaskManager::SendActiveTaskDescription(Client *c, int TaskID, int SequenceN if (strlen(Tasks[TaskID]->Reward) != 0) linker.SetProxyText(Tasks[TaskID]->Reward); - auto reward_link = linker.GenerateLink(); - reward_text.append(reward_link); + reward_text.append(linker.GenerateLink()); } else { reward_text.append(Tasks[TaskID]->Reward); diff --git a/zone/trap.cpp b/zone/trap.cpp index e70d7f070..fb263ee2c 100644 --- a/zone/trap.cpp +++ b/zone/trap.cpp @@ -218,7 +218,7 @@ void Trap::Trigger(Mob* trigger) int dmg = zone->random.Int(effectvalue, effectvalue2); trigger->SetHP(trigger->GetHP() - dmg); a->damage = dmg; - a->meleepush_xy = zone->random.Int(0, 1234567); + a->hit_heading = 0.0f; a->source = GetHiddenTrigger()!=nullptr ? GetHiddenTrigger()->GetID() : trigger->GetID(); a->spellid = 0; a->target = trigger->GetID(); diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index 45d199277..55a2cc511 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -29,10 +29,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "npc.h" #include "quest_parser_collection.h" #include "water_map.h" +#include "fastmath.h" #include #include +extern FastMath g_Math; + struct wp_distance { float dist; @@ -990,6 +993,35 @@ float Mob::GetZOffset() const { return 0.2 * GetSize() * offset; } +// This function will try to move the mob along the relative angle a set distance +// if it can't be moved, it will lower the distance and try again +// If we want to move on like say a spawn, we can pass send as false +void Mob::TryMoveAlong(float distance, float angle, bool send) +{ + angle += GetHeading(); + angle = FixHeading(angle); + + glm::vec3 tmp_pos; + glm::vec3 new_pos = GetPosition(); + new_pos.x += distance * g_Math.FastSin(angle); + new_pos.y += distance * g_Math.FastCos(angle); + new_pos.z += GetZOffset(); + + if (zone->HasMap()) { + auto new_z = zone->zonemap->FindClosestZ(new_pos, nullptr); + if (new_z != BEST_Z_INVALID) + new_pos.z = new_z; + + if (zone->zonemap->LineIntersectsZone(GetPosition(), new_pos, 0.0f, &tmp_pos)) + new_pos = tmp_pos; + } + + new_pos.z = GetFixedZ(new_pos); + Teleport(new_pos); + if (send) + SendPositionUpdate(); +} + int ZoneDatabase::GetHighestGrid(uint32 zoneid) { std::string query = StringFormat("SELECT COALESCE(MAX(id), 0) FROM grid WHERE zoneid = %i", zoneid); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 6417b9c1d..7c60bffe0 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include #include #include +#include #ifdef _WINDOWS #include @@ -1813,6 +1814,13 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) break; } + case ServerOP_UCSServerStatusReply: + { + auto ucsss = (UCSServerStatus_Struct*)pack->pBuffer; + if (zone) + zone->SetUCSServerAvailable((ucsss->available != 0), ucsss->timestamp); + break; + } case ServerOP_CZSetEntityVariableByNPCTypeID: { CZSetEntVarByNPCTypeID_Struct* CZM = (CZSetEntVarByNPCTypeID_Struct*)pack->pBuffer; diff --git a/zone/zone.cpp b/zone/zone.cpp index cefd4e402..08728528b 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -146,6 +146,8 @@ bool Zone::Bootup(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone) { UpdateWindowTitle(); zone->GetTimeSync(); + zone->RequestUCSServerStatus(); + /* Set Logging */ LogSys.StartFileLogs(StringFormat("%s_version_%u_inst_id_%u_port_%u", zone->GetShortName(), zone->GetInstanceVersion(), zone->GetInstanceID(), ZoneConfig::get()->ZonePort)); @@ -847,6 +849,9 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) GuildBanks = new GuildBankManager; else GuildBanks = nullptr; + + m_ucss_available = false; + m_last_ucss_update = 0; } Zone::~Zone() { @@ -1863,14 +1868,17 @@ bool ZoneDatabase::GetDecayTimes(npcDecayTimes_Struct* npcCorpseDecayTimes) { return true; } -void Zone::weatherSend() +void Zone::weatherSend(Client* client) { auto outapp = new EQApplicationPacket(OP_Weather, 8); if(zone_weather>0) outapp->pBuffer[0] = zone_weather-1; if(zone_weather>0) outapp->pBuffer[4] = zone->weather_intensity; - entity_list.QueueClients(0, outapp); + if (client) + client->QueuePacket(outapp); + else + entity_list.QueueClients(0, outapp); safe_delete(outapp); } @@ -2336,3 +2344,22 @@ void Zone::UpdateHotzone() is_hotzone = atoi(row[0]) == 0 ? false: true; } +void Zone::RequestUCSServerStatus() { + auto outapp = new ServerPacket(ServerOP_UCSServerStatusRequest, sizeof(UCSServerStatus_Struct)); + auto ucsss = (UCSServerStatus_Struct*)outapp->pBuffer; + ucsss->available = 0; + ucsss->port = Config->ZonePort; + ucsss->unused = 0; + worldserver.SendPacket(outapp); + safe_delete(outapp); +} + +void Zone::SetUCSServerAvailable(bool ucss_available, uint32 update_timestamp) { + if (m_last_ucss_update == update_timestamp && m_ucss_available != ucss_available) { + m_ucss_available = false; + RequestUCSServerStatus(); + return; + } + if (m_last_ucss_update < update_timestamp) + m_ucss_available = ucss_available; +} diff --git a/zone/zone.h b/zone/zone.h index e8fa7b509..e55aa93f1 100644 --- a/zone/zone.h +++ b/zone/zone.h @@ -224,7 +224,7 @@ public: void SetDate(uint16 year, uint8 month, uint8 day, uint8 hour, uint8 minute); void SetTime(uint8 hour, uint8 minute, bool update_world = true); - void weatherSend(); + void weatherSend(Client* client = nullptr); bool CanBind() const { return(can_bind); } bool IsCity() const { return(is_city); } bool CanDoCombat() const { return(can_combat); } @@ -275,6 +275,10 @@ public: inline void ShowZoneGlobalLoot(Client *to) { m_global_loot.ShowZoneGlobalLoot(to); } inline void ShowNPCGlobalLoot(Client *to, NPC *who) { m_global_loot.ShowNPCGlobalLoot(to, who); } + void RequestUCSServerStatus(); + void SetUCSServerAvailable(bool ucss_available, uint32 update_timestamp); + bool IsUCSServerAvailable() { return m_ucss_available; } + // random object that provides random values for the zone EQEmu::Random random; @@ -355,6 +359,9 @@ private: Timer hotzone_timer; GlobalLootManager m_global_loot; + + bool m_ucss_available; + uint32 m_last_ucss_update; }; #endif diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 01add9944..cef24a649 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -325,62 +325,260 @@ bool ZoneDatabase::logevents(const char* accountname,uint32 accountid,uint8 stat return true; } +void ZoneDatabase::RegisterBug(BugReport_Struct* bug_report) { + if (!bug_report) + return; -void ZoneDatabase::UpdateBug(BugStruct* bug) { - - uint32 len = strlen(bug->bug); - char* bugtext = nullptr; - if(len > 0) - { - bugtext = new char[2*len+1]; - memset(bugtext, 0, 2*len+1); - DoEscapeString(bugtext, bug->bug, len); + size_t len = 0; + char* name_ = nullptr; + char* ui_ = nullptr; + char* type_ = nullptr; + char* target_ = nullptr; + char* bug_ = nullptr; + + len = strlen(bug_report->reporter_name); + if (len) { + if (len > 63) // check against db column size + len = 63; + name_ = new char[(2 * len + 1)]; + memset(name_, 0, (2 * len + 1)); + DoEscapeString(name_, bug_report->reporter_name, len); } - len = strlen(bug->ui); - char* uitext = nullptr; - if(len > 0) - { - uitext = new char[2*len+1]; - memset(uitext, 0, 2*len+1); - DoEscapeString(uitext, bug->ui, len); + len = strlen(bug_report->ui_path); + if (len) { + if (len > 127) + len = 127; + ui_ = new char[(2 * len + 1)]; + memset(ui_, 0, (2 * len + 1)); + DoEscapeString(ui_, bug_report->ui_path, len); } - len = strlen(bug->target_name); - char* targettext = nullptr; - if(len > 0) - { - targettext = new char[2*len+1]; - memset(targettext, 0, 2*len+1); - DoEscapeString(targettext, bug->target_name, len); + len = strlen(bug_report->category_name); + if (len) { + if (len > 63) + len = 63; + type_ = new char[(2 * len + 1)]; + memset(type_, 0, (2 * len + 1)); + DoEscapeString(type_, bug_report->category_name, len); } - //x and y are intentionally swapped because eq is inversexy coords - std::string query = StringFormat("INSERT INTO bugs (zone, name, ui, x, y, z, type, flag, target, bug, date) " + len = strlen(bug_report->target_name); + if (len) { + if (len > 63) + len = 63; + target_ = new char[(2 * len + 1)]; + memset(target_, 0, (2 * len + 1)); + DoEscapeString(target_, bug_report->target_name, len); + } + + len = strlen(bug_report->bug_report); + if (len) { + if (len > 1023) + len = 1023; + bug_ = new char[(2 * len + 1)]; + memset(bug_, 0, (2 * len + 1)); + DoEscapeString(bug_, bug_report->bug_report, len); + } + + //x and y are intentionally swapped because eq is inversexy coords //is this msg out-of-date or are the parameters wrong? + std::string query = StringFormat( + "INSERT INTO `bugs` (`zone`, `name`, `ui`, `x`, `y`, `z`, `type`, `flag`, `target`, `bug`, `date`) " "VALUES('%s', '%s', '%s', '%.2f', '%.2f', '%.2f', '%s', %d, '%s', '%s', CURDATE())", - zone->GetShortName(), bug->name, uitext == nullptr ? "": uitext, - bug->x, bug->y, bug->z, bug->chartype, bug->type, targettext == nullptr? "Unknown Target": targettext, - bugtext==nullptr?"":bugtext); - safe_delete_array(bugtext); - safe_delete_array(uitext); - safe_delete_array(targettext); + zone->GetShortName(), + (name_ ? name_ : ""), + (ui_ ? ui_ : ""), + bug_report->pos_x, + bug_report->pos_y, + bug_report->pos_z, + (type_ ? type_ : ""), + bug_report->optional_info_mask, + (target_ ? target_ : "Unknown Target"), + (bug_ ? bug_ : "") + ); + safe_delete_array(name_); + safe_delete_array(ui_); + safe_delete_array(type_); + safe_delete_array(target_); + safe_delete_array(bug_); + QueryDatabase(query); } -void ZoneDatabase::UpdateBug(PetitionBug_Struct* bug){ +void ZoneDatabase::RegisterBug(Client* client, BugReport_Struct* bug_report) { + if (!client || !bug_report) + return; - uint32 len = strlen(bug->text); - auto bugtext = new char[2 * len + 1]; - memset(bugtext, 0, 2*len+1); - DoEscapeString(bugtext, bug->text, len); + size_t len = 0; + char* category_name_ = nullptr; + char* reporter_name_ = nullptr; + char* ui_path_ = nullptr; + char* target_name_ = nullptr; + char* bug_report_ = nullptr; + char* system_info_ = nullptr; - std::string query = StringFormat("INSERT INTO bugs (type, name, bugtext, flag) " - "VALUES('%s', '%s', '%s', %i)", - "Petition", bug->name, bugtext, 25); - safe_delete_array(bugtext); - QueryDatabase(query); + len = strlen(bug_report->category_name); + if (len) { + if (len > 63) // check against db column size + len = 63; + category_name_ = new char[(2 * len + 1)]; + memset(category_name_, 0, (2 * len + 1)); + DoEscapeString(category_name_, bug_report->category_name, len); + } + + len = strlen(bug_report->reporter_name); + if (len) { + if (len > 63) + len = 63; + reporter_name_ = new char[(2 * len + 1)]; + memset(reporter_name_, 0, (2 * len + 1)); + DoEscapeString(reporter_name_, bug_report->reporter_name, len); + } + + len = strlen(bug_report->ui_path); + if (len) { + if (len > 127) + len = 127; + ui_path_ = new char[(2 * len + 1)]; + memset(ui_path_, 0, (2 * len + 1)); + DoEscapeString(ui_path_, bug_report->ui_path, len); + } + + len = strlen(bug_report->target_name); + if (len) { + if (len > 63) + len = 63; + target_name_ = new char[(2 * len + 1)]; + memset(target_name_, 0, (2 * len + 1)); + DoEscapeString(target_name_, bug_report->target_name, len); + } + + len = strlen(bug_report->bug_report); + if (len) { + if (len > 1023) + len = 1023; + bug_report_ = new char[(2 * len + 1)]; + memset(bug_report_, 0, (2 * len + 1)); + DoEscapeString(bug_report_, bug_report->bug_report, len); + } + + len = strlen(bug_report->system_info); + if (len) { + if (len > 1023) + len = 1023; + system_info_ = new char[(2 * len + 1)]; + memset(system_info_, 0, (2 * len + 1)); + DoEscapeString(system_info_, bug_report->system_info, len); + } + + std::string query = StringFormat( + "INSERT INTO `bug_reports` " + "(`zone`," + " `client_version_id`," + " `client_version_name`," + " `account_id`," + " `character_id`," + " `character_name`," + " `reporter_spoof`," + " `category_id`," + " `category_name`," + " `reporter_name`," + " `ui_path`," + " `pos_x`," + " `pos_y`," + " `pos_z`," + " `heading`," + " `time_played`," + " `target_id`," + " `target_name`," + " `optional_info_mask`," + " `_can_duplicate`," + " `_crash_bug`," + " `_target_info`," + " `_character_flags`," + " `_unknown_value`," + " `bug_report`," + " `system_info`) " + "VALUES " + "('%s'," + " '%u'," + " '%s'," + " '%u'," + " '%u'," + " '%s'," + " '%u'," + " '%u'," + " '%s'," + " '%s'," + " '%s'," + " '%1.1f'," + " '%1.1f'," + " '%1.1f'," + " '%u'," + " '%u'," + " '%u'," + " '%s'," + " '%u'," + " '%u'," + " '%u'," + " '%u'," + " '%u'," + " '%u'," + " '%s'," + " '%s')", + zone->GetShortName(), + client->ClientVersion(), + EQEmu::versions::ClientVersionName(client->ClientVersion()), + client->AccountID(), + client->CharacterID(), + client->GetName(), + (strcmp(client->GetName(), reporter_name_) != 0 ? 1 : 0), + bug_report->category_id, + (category_name_ ? category_name_ : ""), + (reporter_name_ ? reporter_name_ : ""), + (ui_path_ ? ui_path_ : ""), + bug_report->pos_x, + bug_report->pos_y, + bug_report->pos_z, + bug_report->heading, + bug_report->time_played, + bug_report->target_id, + (target_name_ ? target_name_ : ""), + bug_report->optional_info_mask, + ((bug_report->optional_info_mask & EQEmu::bug::infoCanDuplicate) != 0 ? 1 : 0), + ((bug_report->optional_info_mask & EQEmu::bug::infoCrashBug) != 0 ? 1 : 0), + ((bug_report->optional_info_mask & EQEmu::bug::infoTargetInfo) != 0 ? 1 : 0), + ((bug_report->optional_info_mask & EQEmu::bug::infoCharacterFlags) != 0 ? 1 : 0), + ((bug_report->optional_info_mask & EQEmu::bug::infoUnknownValue) != 0 ? 1 : 0), + (bug_report_ ? bug_report_ : ""), + (system_info_ ? system_info_ : "") + ); + safe_delete_array(category_name_); + safe_delete_array(reporter_name_); + safe_delete_array(ui_path_); + safe_delete_array(target_name_); + safe_delete_array(bug_report_); + safe_delete_array(system_info_); + + auto result = QueryDatabase(query); + + // TODO: Entity dumping [RuleB(Bugs, DumpTargetEntity)] } +//void ZoneDatabase::UpdateBug(PetitionBug_Struct* bug) { +// +// uint32 len = strlen(bug->text); +// auto bugtext = new char[2 * len + 1]; +// memset(bugtext, 0, 2 * len + 1); +// DoEscapeString(bugtext, bug->text, len); +// +// std::string query = StringFormat("INSERT INTO bugs (type, name, bugtext, flag) " +// "VALUES('%s', '%s', '%s', %i)", +// "Petition", bug->name, bugtext, 25); +// safe_delete_array(bugtext); +// QueryDatabase(query); +//} + bool ZoneDatabase::SetSpecialAttkFlag(uint8 id, const char* flag) { std::string query = StringFormat("UPDATE npc_types SET npcspecialattks='%s' WHERE id = %i;", flag, id); @@ -1425,6 +1623,8 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla if (account_id <= 0) return false; + std::string mail_key = database.GetMailKey(character_id); + clock_t t = std::clock(); /* Function timer start */ std::string query = StringFormat( "REPLACE INTO `character_data` (" @@ -1521,7 +1721,8 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla " e_aa_effects, " " e_percent_to_aa, " " e_expended_aa_spent, " - " e_last_invsnapshot " + " e_last_invsnapshot, " + " mailkey " ") " "VALUES (" "%u," // id " id, " @@ -1617,7 +1818,8 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla "%u," // e_aa_effects "%u," // e_percent_to_aa "%u," // e_expended_aa_spent - "%u" // e_last_invsnapshot + "%u," // e_last_invsnapshot + "'%s'" // mailkey mail_key ")", character_id, // " id, " account_id, // " account_id, " @@ -1712,7 +1914,8 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla m_epp->aa_effects, m_epp->perAA, m_epp->expended_aa, - m_epp->last_invsnapshot_time + m_epp->last_invsnapshot_time, + mail_key.c_str() ); auto results = database.QueryDatabase(query); Log(Logs::General, Logs::None, "ZoneDatabase::SaveCharacterData %i, done... Took %f seconds", character_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); diff --git a/zone/zonedb.h b/zone/zonedb.h index 6f65bae91..d0e7b1a99 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -442,8 +442,9 @@ public: bool DeleteMerc(uint32 merc_id); /* Petitions */ - void UpdateBug(BugStruct* bug); - void UpdateBug(PetitionBug_Struct* bug); + void RegisterBug(BugReport_Struct* bug_report); // old method + void RegisterBug(Client* client, BugReport_Struct* bug_report); // new method + //void UpdateBug(PetitionBug_Struct* bug); void DeletePetitionFromDB(Petition* wpet); void UpdatePetitionToDB(Petition* wpet); void InsertPetitionToDB(Petition* wpet);