diff --git a/changelog.txt b/changelog.txt index 393a954c8..931ccf079 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,12 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 02/20/2015 == +demonstar55: Implement claims for RoF/RoF2 (should no longer crash the client!) + - fixed bugs related to claims for the rest of the clients (woo infinite loops) + +== 02/18/2015 == +Trevius: Fix for potential recursive loops if using RemoveFromHateList() within EVENT_HATE_LIST. + == 02/17/2015 == Uleat: Implemented per-client character creation limiting. Caps are unknown atm..so, don't get over-zealous! Notes: diff --git a/common/base_packet.h b/common/base_packet.h index facd98809..027b8d2fb 100644 --- a/common/base_packet.h +++ b/common/base_packet.h @@ -63,6 +63,7 @@ public: void WriteFloat(float value) { *(float *)(pBuffer + _wpos) = value; _wpos += sizeof(float); } void WriteDouble(double value) { *(double *)(pBuffer + _wpos) = value; _wpos += sizeof(double); } void WriteString(const char * str) { uint32 len = static_cast(strlen(str)) + 1; memcpy(pBuffer + _wpos, str, len); _wpos += len; } + void WriteData(const void *ptr, size_t n) { memcpy(pBuffer + _wpos, ptr, n); _wpos += n; } uint8 ReadUInt8() { uint8 value = *(uint8 *)(pBuffer + _rpos); _rpos += sizeof(uint8); return value; } uint8 ReadUInt8(uint32 Offset) const { uint8 value = *(uint8 *)(pBuffer + Offset); return value; } diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 46ee3d556..eec3ecba9 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -2776,7 +2776,8 @@ struct BazaarWelcome_Struct { BazaarWindowStart_Struct Beginning; uint32 Traders; uint32 Items; - uint8 Unknown012[8]; + uint32 Unknown012; + uint32 Unknown016; }; struct BazaarSearch_Struct { @@ -3207,7 +3208,7 @@ struct TraderDelItem_Struct{ struct TraderClick_Struct{ /*000*/ uint32 TraderID; -/*004*/ uint32 Unknown004; +/*004*/ uint32 Code; /*008*/ uint32 Unknown008; /*012*/ uint32 Approval; /*016*/ @@ -4552,19 +4553,12 @@ struct InternalVeteranReward /*012*/ InternalVeteranRewardItem items[8]; }; -struct VeteranClaimReply +struct VeteranClaim { -/*000*/ char name[64]; -/*064*/ uint32 claim_id; -/*068*/ uint32 reject_field; -/*072*/ uint32 unknown072; -}; - -struct VeteranClaimRequest -{ -/*000*/ char name_data[64]; //name + other data +/*000*/ char name[64]; //name + other data /*064*/ uint32 claim_id; /*068*/ uint32 unknown068; +/*072*/ uint32 action; }; struct GMSearchCorpse_Struct diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index f1f0e1f5b..0304dc1d7 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -15,6 +15,8 @@ #include #include +#include +#include namespace RoF { @@ -3608,37 +3610,71 @@ namespace RoF FINISH_ENCODE(); } + ENCODE(OP_VetClaimReply) + { + ENCODE_LENGTH_EXACT(VeteranClaim); + SETUP_DIRECT_ENCODE(VeteranClaim, structs::VeteranClaim); + + memcpy(eq->name, emu->name, sizeof(emu->name)); + OUT(claim_id); + OUT(action); + + FINISH_ENCODE(); + } + ENCODE(OP_VetRewardsAvaliable) { EQApplicationPacket *inapp = *p; - unsigned char * __emu_buffer = inapp->pBuffer; + auto __emu_buffer = inapp->pBuffer; uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward)); - *p = nullptr; - EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count)); - uchar *old_data = __emu_buffer; - uchar *data = outapp_create->pBuffer; - for (unsigned int i = 0; i < count; ++i) - { - structs::VeteranReward *vr = (structs::VeteranReward*)data; - InternalVeteranReward *ivr = (InternalVeteranReward*)old_data; + // calculate size of names, note the packet DOES NOT have null termed c-strings + std::vector name_lengths; + for (int i = 0; i < count; ++i) { + InternalVeteranReward *ivr = (InternalVeteranReward *)__emu_buffer; - vr->claim_count = ivr->claim_count; - vr->claim_id = ivr->claim_id; - vr->number_available = ivr->number_available; - for (int x = 0; x < 8; ++x) - { - vr->items[x].item_id = ivr->items[x].item_id; - strncpy(vr->items[x].item_name, ivr->items[x].item_name, sizeof(vr->items[x].item_name)); - vr->items[x].charges = ivr->items[x].charges; + for (int i = 0; i < ivr->claim_count; i++) { + uint32 length = strnlen(ivr->items[i].item_name, 63); + if (length) + name_lengths.push_back(length); } - old_data += sizeof(InternalVeteranReward); - data += sizeof(structs::VeteranReward); + __emu_buffer += sizeof(InternalVeteranReward); } - dest->FastQueuePacket(&outapp_create); + uint32 packet_size = std::accumulate(name_lengths.begin(), name_lengths.end(), 0) + + sizeof(structs::VeteranReward) + (sizeof(structs::VeteranRewardEntry) * count) + + // size of name_lengths is the same as item count + (sizeof(structs::VeteranRewardItem) * name_lengths.size()); + + // build packet now! + auto outapp = new EQApplicationPacket(OP_VetRewardsAvaliable, packet_size); + __emu_buffer = inapp->pBuffer; + + outapp->WriteUInt32(count); + auto name_itr = name_lengths.begin(); + for (int i = 0; i < count; i++) { + InternalVeteranReward *ivr = (InternalVeteranReward *)__emu_buffer; + + outapp->WriteUInt32(ivr->claim_id); + outapp->WriteUInt32(ivr->number_available); + outapp->WriteUInt32(ivr->claim_count); + outapp->WriteUInt8(1); // enabled + + for (int j = 0; j < ivr->claim_count; j++) { + assert(name_itr != name_lengths.end()); // the way it's written, it should never happen, so just assert + outapp->WriteUInt32(*name_itr); + outapp->WriteData(ivr->items[j].item_name, *name_itr); + outapp->WriteUInt32(ivr->items[j].item_id); + outapp->WriteUInt32(ivr->items[j].charges); + ++name_itr; + } + + __emu_buffer += sizeof(InternalVeteranReward); + } + + dest->FastQueuePacket(&outapp); delete inapp; } @@ -4924,6 +4960,16 @@ namespace RoF FINISH_DIRECT_DECODE(); } + DECODE(OP_VetClaimRequest) + { + DECODE_LENGTH_EXACT(structs::VeteranClaim); + SETUP_DIRECT_DECODE(VeteranClaim, structs::VeteranClaim); + + IN(claim_id); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_ZoneChange) { DECODE_LENGTH_EXACT(structs::ZoneChange_Struct); diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 005d332f4..63a015ab7 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -15,6 +15,8 @@ #include #include +#include +#include namespace RoF2 { @@ -3667,6 +3669,23 @@ namespace RoF2 FINISH_ENCODE(); } + else if (psize == sizeof(BazaarWelcome_Struct)) + { + ENCODE_LENGTH_EXACT(BazaarWelcome_Struct); + SETUP_DIRECT_ENCODE(BazaarWelcome_Struct, structs::BazaarWelcome_Struct); + + eq->Code = emu->Beginning.Action; + eq->EntityID = emu->Unknown012; + OUT(Traders); + OUT(Items); + eq->Traders2 = emu->Traders; + eq->Items2 = emu->Items; + + Log.Out(Logs::Detail, Logs::Trading, "ENCODE(OP_TraderShop): BazaarWelcome_Struct Code %d, Traders %d, Items %d", + eq->Code, eq->Traders, eq->Items); + + FINISH_ENCODE(); + } else if (psize == sizeof(TraderBuy_Struct)) { ENCODE_LENGTH_EXACT(TraderBuy_Struct); @@ -3732,37 +3751,71 @@ namespace RoF2 FINISH_ENCODE(); } + ENCODE(OP_VetClaimReply) + { + ENCODE_LENGTH_EXACT(VeteranClaim); + SETUP_DIRECT_ENCODE(VeteranClaim, structs::VeteranClaim); + + memcpy(eq->name, emu->name, sizeof(emu->name)); + OUT(claim_id); + OUT(action); + + FINISH_ENCODE(); + } + ENCODE(OP_VetRewardsAvaliable) { EQApplicationPacket *inapp = *p; - unsigned char * __emu_buffer = inapp->pBuffer; + auto __emu_buffer = inapp->pBuffer; uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward)); - *p = nullptr; - EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count)); - uchar *old_data = __emu_buffer; - uchar *data = outapp_create->pBuffer; - for (unsigned int i = 0; i < count; ++i) - { - structs::VeteranReward *vr = (structs::VeteranReward*)data; - InternalVeteranReward *ivr = (InternalVeteranReward*)old_data; + // calculate size of names, note the packet DOES NOT have null termed c-strings + std::vector name_lengths; + for (int i = 0; i < count; ++i) { + InternalVeteranReward *ivr = (InternalVeteranReward *)__emu_buffer; - vr->claim_count = ivr->claim_count; - vr->claim_id = ivr->claim_id; - vr->number_available = ivr->number_available; - for (int x = 0; x < 8; ++x) - { - vr->items[x].item_id = ivr->items[x].item_id; - strncpy(vr->items[x].item_name, ivr->items[x].item_name, sizeof(vr->items[x].item_name)); - vr->items[x].charges = ivr->items[x].charges; + for (int i = 0; i < ivr->claim_count; i++) { + uint32 length = strnlen(ivr->items[i].item_name, 63); + if (length) + name_lengths.push_back(length); } - old_data += sizeof(InternalVeteranReward); - data += sizeof(structs::VeteranReward); + __emu_buffer += sizeof(InternalVeteranReward); } - dest->FastQueuePacket(&outapp_create); + uint32 packet_size = std::accumulate(name_lengths.begin(), name_lengths.end(), 0) + + sizeof(structs::VeteranReward) + (sizeof(structs::VeteranRewardEntry) * count) + + // size of name_lengths is the same as item count + (sizeof(structs::VeteranRewardItem) * name_lengths.size()); + + // build packet now! + auto outapp = new EQApplicationPacket(OP_VetRewardsAvaliable, packet_size); + __emu_buffer = inapp->pBuffer; + + outapp->WriteUInt32(count); + auto name_itr = name_lengths.begin(); + for (int i = 0; i < count; i++) { + InternalVeteranReward *ivr = (InternalVeteranReward *)__emu_buffer; + + outapp->WriteUInt32(ivr->claim_id); + outapp->WriteUInt32(ivr->number_available); + outapp->WriteUInt32(ivr->claim_count); + outapp->WriteUInt8(1); // enabled + + for (int j = 0; j < ivr->claim_count; j++) { + assert(name_itr != name_lengths.end()); // the way it's written, it should never happen, so just assert + outapp->WriteUInt32(*name_itr); + outapp->WriteData(ivr->items[j].item_name, *name_itr); + outapp->WriteUInt32(ivr->items[j].item_id); + outapp->WriteUInt32(ivr->items[j].charges); + ++name_itr; + } + + __emu_buffer += sizeof(InternalVeteranReward); + } + + dest->FastQueuePacket(&outapp); delete inapp; } @@ -5009,8 +5062,25 @@ namespace RoF2 DECODE_LENGTH_EXACT(structs::TraderClick_Struct); SETUP_DIRECT_DECODE(TraderClick_Struct, structs::TraderClick_Struct); + IN(Code); IN(TraderID); IN(Approval); + Log.Out(Logs::Detail, Logs::Trading, "DECODE(OP_TraderShop): TraderClick_Struct Code %d, TraderID %d, Approval %d", + eq->Code, eq->TraderID, eq->Approval); + + FINISH_DIRECT_DECODE(); + } + else if (psize == sizeof(structs::BazaarWelcome_Struct)) + { + // Don't think this size gets used in RoF+ - Leaving for now... + DECODE_LENGTH_EXACT(structs::BazaarWelcome_Struct); + SETUP_DIRECT_DECODE(BazaarWelcome_Struct, structs::BazaarWelcome_Struct); + + emu->Beginning.Action = eq->Code; + IN(Traders); + IN(Items); + Log.Out(Logs::Detail, Logs::Trading, "DECODE(OP_TraderShop): BazaarWelcome_Struct Code %d, Traders %d, Items %d", + eq->Code, eq->Traders, eq->Items); FINISH_DIRECT_DECODE(); } @@ -5026,9 +5096,9 @@ namespace RoF2 memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName)); IN(ItemID); IN(Quantity); - Log.Out(Logs::Detail, Logs::Trading, "DECODE(OP_TraderShop): (Unknowns) Unknown004 %d, Unknown008 %d, Unknown012 %d, Unknown076 %d, Unknown276 %d", + Log.Out(Logs::Detail, Logs::Trading, "DECODE(OP_TraderShop): TraderBuy_Struct (Unknowns) Unknown004 %d, Unknown008 %d, Unknown012 %d, Unknown076 %d, Unknown276 %d", eq->Unknown004, eq->Unknown008, eq->Unknown012, eq->Unknown076, eq->Unknown276); - Log.Out(Logs::Detail, Logs::Trading, "DECODE(OP_TraderShop): Buy Action %d, Price %d, Trader %d, ItemID %d, Quantity %d, ItemName, %s", + Log.Out(Logs::Detail, Logs::Trading, "DECODE(OP_TraderShop): TraderBuy_Struct Buy Action %d, Price %d, Trader %d, ItemID %d, Quantity %d, ItemName, %s", eq->Action, eq->Price, eq->TraderID, eq->ItemID, eq->Quantity, eq->ItemName); FINISH_DIRECT_DECODE(); @@ -5088,6 +5158,16 @@ namespace RoF2 FINISH_DIRECT_DECODE(); } + DECODE(OP_VetClaimRequest) + { + DECODE_LENGTH_EXACT(structs::VeteranClaim); + SETUP_DIRECT_DECODE(VeteranClaim, structs::VeteranClaim); + + IN(claim_id); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_ZoneChange) { DECODE_LENGTH_EXACT(structs::ZoneChange_Struct); diff --git a/common/patches/rof2_ops.h b/common/patches/rof2_ops.h index 3edcac459..4e4400cdf 100644 --- a/common/patches/rof2_ops.h +++ b/common/patches/rof2_ops.h @@ -111,6 +111,7 @@ E(OP_Trader) E(OP_TraderBuy) E(OP_TributeInfo) E(OP_TributeItem) +E(OP_VetClaimReply) E(OP_VetRewardsAvaliable) E(OP_WearChange) E(OP_WhoAllResponse) @@ -174,6 +175,7 @@ D(OP_Trader) D(OP_TraderBuy) D(OP_TradeSkillCombine) D(OP_TributeItem) +D(OP_VetClaimRequest) D(OP_WhoAllRequest) D(OP_ZoneChange) D(OP_ZoneEntry) diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index 65eb99084..dd5b95863 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -2940,10 +2940,12 @@ struct BazaarWindowStart_Struct { struct BazaarWelcome_Struct { - BazaarWindowStart_Struct Beginning; - uint32 Traders; - uint32 Items; - uint8 Unknown012[8]; + uint32 Code; + uint32 EntityID; + uint32 Traders; + uint32 Items; + uint32 Traders2; + uint32 Items2; }; struct BazaarSearch_Struct { @@ -4725,17 +4727,33 @@ struct AugmentInfo_Struct struct VeteranRewardItem { -/*000*/ uint32 item_id; -/*004*/ uint32 charges; -/*008*/ char item_name[64]; +/*000*/ uint32 name_length; +/*004*/ //char item_name[0]; // THIS IS NOT NULL TERMED +/*???*/ uint32 item_id; +/*???*/ uint32 charges; +}; + +struct VeteranRewardEntry +{ +/*000*/ uint32 claim_id; // guessed +/*004*/ uint32 avaliable_count; +/*008*/ uint32 claim_count; +/*012*/ char enabled; +/*013*/ //VeteranRewardItem items[0]; }; struct VeteranReward { -/*000*/ uint32 claim_id; -/*004*/ uint32 number_available; -/*008*/ uint32 claim_count; -/*012*/ VeteranRewardItem items[8]; +/*000*/ uint32 claim_count; +/*004*/ //VeteranRewardEntry entries[0]; +}; + +struct VeteranClaim +{ +/*000*/ char name[68]; //name + other data +/*068*/ uint32 claim_id; +/*072*/ uint32 unknown072; +/*076*/ uint32 action; }; struct ExpeditionEntryHeader_Struct diff --git a/common/patches/rof_ops.h b/common/patches/rof_ops.h index 8c55cd857..08e8647f3 100644 --- a/common/patches/rof_ops.h +++ b/common/patches/rof_ops.h @@ -96,6 +96,7 @@ E(OP_Trader) E(OP_TraderBuy) E(OP_TributeInfo) E(OP_TributeItem) +E(OP_VetClaimReply) E(OP_VetRewardsAvaliable) E(OP_WearChange) E(OP_WhoAllResponse) @@ -159,6 +160,7 @@ D(OP_Trader) D(OP_TraderBuy) D(OP_TradeSkillCombine) D(OP_TributeItem) +D(OP_VetClaimRequest) D(OP_WhoAllRequest) D(OP_ZoneChange) D(OP_ZoneEntry) diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index 721d9888e..a97afdd5b 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -4718,17 +4718,33 @@ struct AugmentInfo_Struct struct VeteranRewardItem { -/*000*/ uint32 item_id; -/*004*/ uint32 charges; -/*008*/ char item_name[64]; +/*000*/ uint32 name_length; +/*004*/ //char item_name[0]; // THIS IS NOT NULL TERMED +/*???*/ uint32 item_id; +/*???*/ uint32 charges; +}; + +struct VeteranRewardEntry +{ +/*000*/ uint32 claim_id; // guessed +/*004*/ uint32 avaliable_count; +/*008*/ uint32 claim_count; +/*012*/ char enabled; +/*013*/ //VeteranRewardItem items[0]; }; struct VeteranReward { -/*000*/ uint32 claim_id; -/*004*/ uint32 number_available; -/*008*/ uint32 claim_count; -/*012*/ VeteranRewardItem items[8]; +/*000*/ uint32 claim_count; +/*004*/ //VeteranRewardEntry entries[0]; +}; + +struct VeteranClaim +{ +/*000*/ char name[68]; //name + other data +/*068*/ uint32 claim_id; +/*072*/ uint32 unknown072; +/*076*/ uint32 action; }; struct ExpeditionEntryHeader_Struct diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index 347e7e929..a46946c93 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -1918,7 +1918,7 @@ namespace SoD if (emu->CharCount == 0) { ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, sizeof(structs::CharacterSelect_Struct)); eq->CharCount = emu->CharCount; - eq->TotalChars = eq->TotalChars; + eq->TotalChars = emu->TotalChars; if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT) eq->TotalChars = consts::CHARACTER_CREATION_LIMIT; diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index 130953c1d..ba1bf74cf 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -1577,7 +1577,7 @@ namespace SoF if (emu->CharCount == 0) { ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, sizeof(structs::CharacterSelect_Struct)); eq->CharCount = emu->CharCount; - eq->TotalChars = eq->TotalChars; + eq->TotalChars = emu->TotalChars; if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT) eq->TotalChars = consts::CHARACTER_CREATION_LIMIT; diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index dbe80198b..87d091dd3 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -1192,12 +1192,12 @@ namespace Titanium } eq->SecondaryIDFile[char_index] = emu_cse->SecondaryIDFile; - eq->Unknown820[char_index] = 0xFF; + eq->Unknown820[char_index] = (uint8)0xFF; eq->Deity[char_index] = emu_cse->Deity; eq->GoHome[char_index] = emu_cse->GoHome; eq->Tutorial[char_index] = emu_cse->Tutorial; eq->Beard[char_index] = emu_cse->Beard; - eq->Unknown902[char_index] = 0xFF; + eq->Unknown902[char_index] = (uint8)0xFF; eq->PrimaryIDFile[char_index] = emu_cse->PrimaryIDFile; eq->HairColor[char_index] = emu_cse->HairColor; eq->Zone[char_index] = emu_cse->Zone; @@ -1229,18 +1229,20 @@ namespace Titanium } eq->SecondaryIDFile[char_index] = 0; - eq->Unknown820[char_index] = 0xFF; + eq->Unknown820[char_index] = (uint8)0xFF; eq->Deity[char_index] = 0; eq->GoHome[char_index] = 0; eq->Tutorial[char_index] = 0; eq->Beard[char_index] = 0; - eq->Unknown902[char_index] = 0xFF; + eq->Unknown902[char_index] = (uint8)0xFF; eq->PrimaryIDFile[char_index] = 0; eq->HairColor[char_index] = 0; eq->Zone[char_index] = 0; eq->Class[char_index] = 0; eq->Face[char_index] = 0; - //eq->Name[char_index][0] = '\0'; // Cleared above + + strncpy(eq->Name[char_index], "", 6); + eq->Gender[char_index] = 0; eq->EyeColor1[char_index] = 0; eq->EyeColor2[char_index] = 0; diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index 4935b349e..eb525b1ec 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -2201,7 +2201,7 @@ namespace UF if (emu->CharCount == 0) { ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, sizeof(structs::CharacterSelect_Struct)); eq->CharCount = emu->CharCount; - eq->TotalChars = eq->TotalChars; + eq->TotalChars = emu->TotalChars; if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT) eq->TotalChars = consts::CHARACTER_CREATION_LIMIT; diff --git a/utils/patches/patch_Titanium.conf b/utils/patches/patch_Titanium.conf index 841f77bf5..2660738de 100644 --- a/utils/patches/patch_Titanium.conf +++ b/utils/patches/patch_Titanium.conf @@ -525,7 +525,7 @@ OP_KnowledgeBase=0x0000 OP_SlashAdventure=0x571a # /adventure OP_VetRewardsAvaliable=0x0557 OP_VetClaimRequest=0x6ba0 -OP_VetClaimReply=0x0000 +OP_VetClaimReply=0x407e OP_BecomePVPPrompt=0x36B2 #guessed from ASM OP_PVPStats=0x5cc0 OP_PVPLeaderBoardRequest=0x61d2 diff --git a/world/worlddb.cpp b/world/worlddb.cpp index 33e8c03dc..2acfd0f95 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -115,7 +115,16 @@ void WorldDatabase::GetCharSelectInfo(uint32 accountID, EQApplicationPacket **ou cse->Instance = 0; cse->Gender = (uint8)atoi(row[2]); cse->Face = (uint8)atoi(row[15]); - cse->Equip[0] = { 0 }; // Processed below + + for (uint32 matslot = 0; matslot < _MaterialCount; matslot++) { // Processed below + cse->Equip[matslot].Material = 0; + cse->Equip[matslot].Unknown1 = 0; + cse->Equip[matslot].EliteMaterial = 0; + cse->Equip[matslot].HeroForgeModel = 0; + cse->Equip[matslot].Material2 = 0; + cse->Equip[matslot].Color.Color = 0; + } + cse->Unknown15 = 0xFF; cse->Unknown19 = 0xFF; cse->DrakkinTattoo = (uint32)atoi(row[17]); @@ -194,6 +203,18 @@ void WorldDatabase::GetCharSelectInfo(uint32 accountID, EQApplicationPacket **ou } /* Bind End */ + /* Load Character Material Data for Char Select */ + cquery = StringFormat("SELECT slot, red, green, blue, use_tint, color FROM `character_material` WHERE `id` = %u", character_id); + auto results_b = database.QueryDatabase(cquery); uint8 slot = 0; + for (auto row_b = results_b.begin(); row_b != results_b.end(); ++row_b) { + slot = atoi(row_b[0]); + pp.item_tint[slot].RGB.Red = atoi(row_b[1]); + pp.item_tint[slot].RGB.Green = atoi(row_b[2]); + pp.item_tint[slot].RGB.Blue = atoi(row_b[3]); + pp.item_tint[slot].RGB.UseTint = atoi(row_b[4]); + } + /* Character Material Data End */ + /* Load Inventory */ // If we ensure that the material data is updated appropriately, we can do away with inventory loads if (GetInventory(accountID, cse->Name, &inv)) { @@ -252,17 +273,6 @@ void WorldDatabase::GetCharSelectInfo(uint32 accountID, EQApplicationPacket **ou } /* Load Inventory End */ - /* Load Character Material Data for Char Select */ - cquery = StringFormat("SELECT slot, red, green, blue, use_tint, color FROM `character_material` WHERE `id` = %u", character_id); - auto results_b = database.QueryDatabase(cquery); uint8 slot = 0; - for (auto row_b = results_b.begin(); row_b != results_b.end(); ++row_b) { - slot = atoi(row_b[0]); - pp.item_tint[slot].RGB.Red = atoi(row_b[1]); - pp.item_tint[slot].RGB.Green = atoi(row_b[2]); - pp.item_tint[slot].RGB.Blue = atoi(row_b[3]); - pp.item_tint[slot].RGB.UseTint = atoi(row_b[4]); - } - buff_ptr += sizeof(CharacterSelectEntry_Struct); } } diff --git a/zone/attack.cpp b/zone/attack.cpp index 87bbd8b7c..95b408443 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -917,6 +917,7 @@ int Mob::GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate { int dmg = 0; int banedmg = 0; + int x = 0; if(!against || against->GetInvul() || against->GetSpecialAbility(IMMUNE_MELEE)){ return 0; @@ -945,10 +946,20 @@ int Mob::GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate bool MagicWeapon = false; if(weapon_item->GetItem() && weapon_item->GetItem()->Magic) MagicWeapon = true; - else { + else if(spellbonuses.MagicWeapon || itembonuses.MagicWeapon) MagicWeapon = true; - } + else + // An augment on the weapon that is marked magic makes + // the item magical. + for(x = 0; MagicWeapon == false && x < EmuConstants::ITEM_COMMON_SIZE; x++) + { + if(weapon_item->GetAugment(x) && weapon_item->GetAugment(x)->GetItem()) + { + if (weapon_item->GetAugment(x)->GetItem()->Magic) + MagicWeapon = true; + } + } if(MagicWeapon) { diff --git a/zone/client.cpp b/zone/client.cpp index 9db2e7919..2efe91138 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -5362,35 +5362,35 @@ void Client::SendRewards() FastQueuePacket(&vetapp); } -bool Client::TryReward(uint32 claim_id) { - //Make sure we have an open spot - //Make sure we have it in our acct and count > 0 - //Make sure the entry was found - //If we meet all the criteria: - //Decrement our count by 1 if it > 1 delete if it == 1 - //Create our item in bag if necessary at the free inv slot - //save +bool Client::TryReward(uint32 claim_id) +{ + // Make sure we have an open spot + // Make sure we have it in our acct and count > 0 + // Make sure the entry was found + // If we meet all the criteria: + // Decrement our count by 1 if it > 1 delete if it == 1 + // Create our item in bag if necessary at the free inv slot + // save uint32 free_slot = 0xFFFFFFFF; - for(int i = EmuConstants::GENERAL_BEGIN; i <= EmuConstants::GENERAL_END; ++i) { + for (int i = EmuConstants::GENERAL_BEGIN; i <= EmuConstants::GENERAL_END; ++i) { ItemInst *item = GetInv().GetItem(i); - if(!item) { + if (!item) { free_slot = i; break; } } - if(free_slot == 0xFFFFFFFF) + if (free_slot == 0xFFFFFFFF) return false; char errbuf[MYSQL_ERRMSG_SIZE]; std::string query = StringFormat("SELECT amount FROM account_rewards " - "WHERE account_id = %i AND reward_id = %i", - AccountID(), claim_id); + "WHERE account_id = %i AND reward_id = %i", + AccountID(), claim_id); auto results = database.QueryDatabase(query); - if (!results.Success()) { + if (!results.Success()) return false; - } if (results.RowCount() == 0) return false; @@ -5398,58 +5398,57 @@ bool Client::TryReward(uint32 claim_id) { auto row = results.begin(); uint32 amt = atoi(row[0]); - if(amt == 0) + if (amt == 0) return false; - std::list::iterator iter = zone->VeteranRewards.begin(); - for (; iter != zone->VeteranRewards.end(); ++row) - if((*iter).claim_id == claim_id) - break; + auto iter = std::find_if(zone->VeteranRewards.begin(), zone->VeteranRewards.end(), + [claim_id](const InternalVeteranReward &a) { return a.claim_id == claim_id; }); - if(iter == zone->VeteranRewards.end()) + if (iter == zone->VeteranRewards.end()) return false; - if(amt == 1) { + if (amt == 1) { query = StringFormat("DELETE FROM account_rewards " - "WHERE account_id = %i AND reward_id = %i", - AccountID(), claim_id); + "WHERE account_id = %i AND reward_id = %i", + AccountID(), claim_id); auto results = database.QueryDatabase(query); - } - else { + } else { query = StringFormat("UPDATE account_rewards SET amount = (amount-1) " - "WHERE account_id = %i AND reward_id = %i", - AccountID(), claim_id); + "WHERE account_id = %i AND reward_id = %i", + AccountID(), claim_id); auto results = database.QueryDatabase(query); } - InternalVeteranReward ivr = (*iter); + auto &ivr = (*iter); ItemInst *claim = database.CreateItemOld(ivr.items[0].item_id, ivr.items[0].charges); - if(!claim) { + if (!claim) { Save(); return true; } bool lore_conflict = CheckLoreConflict(claim->GetItem()); - for(int y = 1; y < 8; y++) - if(ivr.items[y].item_id && claim->GetItem()->ItemClass == 1) { + for (int y = 1; y < 8; y++) + if (ivr.items[y].item_id && claim->GetItem()->ItemClass == 1) { ItemInst *item_temp = database.CreateItemOld(ivr.items[y].item_id, ivr.items[y].charges); - if(item_temp) { - if(CheckLoreConflict(item_temp->GetItem())) { + if (item_temp) { + if (CheckLoreConflict(item_temp->GetItem())) { lore_conflict = true; DuplicateLoreMessage(ivr.items[y].item_id); } - claim->PutItem(y-1, *item_temp); + claim->PutItem(y - 1, *item_temp); + safe_delete(item_temp); } } - if(lore_conflict) { + if (lore_conflict) { safe_delete(claim); return true; } PutItemInInventory(free_slot, *claim); SendItemPacket(free_slot, claim, ItemPacketTrade); + safe_delete(claim); Save(); return true; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 83bcebc28..190a3e272 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -13516,49 +13516,68 @@ void Client::Handle_OP_TraderShop(const EQApplicationPacket *app) if (app->size == sizeof(TraderClick_Struct)) { - // This is when a potential purchaser right clicks on this client who is in Trader mode to - // browse their goods. + TraderClick_Struct* tcs = (TraderClick_Struct*)app->pBuffer; - EQApplicationPacket* outapp = new EQApplicationPacket(OP_TraderShop, sizeof(TraderClick_Struct)); + Log.Out(Logs::Detail, Logs::Trading, "Handle_OP_TraderShop: TraderClick_Struct TraderID %d, Code %d, Unknown008 %d, Approval %d", + tcs->TraderID, tcs->Code, tcs->Unknown008, tcs->Approval); - TraderClick_Struct* outtcs = (TraderClick_Struct*)outapp->pBuffer; - - Client* Trader = entity_list.GetClientByID(tcs->TraderID); - - if (Trader) + if (tcs->Code == BazaarWelcome) { - outtcs->Approval = Trader->WithCustomer(GetID()); - Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Shop Request (%s) to (%s) with Approval: %d", GetCleanName(), Trader->GetCleanName(), outtcs->Approval); - } - else { - Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: entity_list.GetClientByID(tcs->traderid)" - " returned a nullptr pointer"); - return; - } - - outtcs->TraderID = tcs->TraderID; - - outtcs->Unknown008 = 0x3f800000; - - QueuePacket(outapp); - - - if (outtcs->Approval) { - this->BulkSendTraderInventory(Trader->CharacterID()); - Trader->Trader_CustomerBrowsing(this); - TraderID = tcs->TraderID; - Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Trader Inventory Sent"); + Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Sent Bazaar Welcome Info"); + SendBazaarWelcome(); } else { - Message_StringID(clientMessageYellow, TRADER_BUSY); - Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Trader Busy"); + // This is when a potential purchaser right clicks on this client who is in Trader mode to + // browse their goods. + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TraderShop, sizeof(TraderClick_Struct)); + + TraderClick_Struct* outtcs = (TraderClick_Struct*)outapp->pBuffer; + + Client* Trader = entity_list.GetClientByID(tcs->TraderID); + + if (Trader) + { + outtcs->Approval = Trader->WithCustomer(GetID()); + Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Shop Request (%s) to (%s) with Approval: %d", GetCleanName(), Trader->GetCleanName(), outtcs->Approval); + } + else { + Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: entity_list.GetClientByID(tcs->traderid)" + " returned a nullptr pointer"); + return; + } + + outtcs->TraderID = tcs->TraderID; + + outtcs->Unknown008 = 0x3f800000; + + QueuePacket(outapp); + + + if (outtcs->Approval) { + this->BulkSendTraderInventory(Trader->CharacterID()); + Trader->Trader_CustomerBrowsing(this); + TraderID = tcs->TraderID; + Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Trader Inventory Sent"); + } + else + { + Message_StringID(clientMessageYellow, TRADER_BUSY); + Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Trader Busy"); + } + + safe_delete(outapp); + return; } - safe_delete(outapp); - - return; + } + else if (app->size == sizeof(BazaarWelcome_Struct)) + { + // RoF+ + // Client requested Bazaar Welcome Info (Trader and Item Total Counts) + SendBazaarWelcome(); + Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Sent Bazaar Welcome Info"); } else if (app->size == sizeof(TraderBuy_Struct)) { @@ -13774,41 +13793,32 @@ void Client::Handle_OP_TributeUpdate(const EQApplicationPacket *app) void Client::Handle_OP_VetClaimRequest(const EQApplicationPacket *app) { - if (app->size < sizeof(VeteranClaimRequest)) - { - Log.Out(Logs::General, Logs::None, "OP_VetClaimRequest size lower than expected: got %u expected at least %u", app->size, sizeof(VeteranClaimRequest)); + if (app->size < sizeof(VeteranClaim)) { + Log.Out(Logs::General, Logs::None, + "OP_VetClaimRequest size lower than expected: got %u expected at least %u", app->size, + sizeof(VeteranClaim)); DumpPacket(app); return; } - VeteranClaimRequest *vcr = (VeteranClaimRequest*)app->pBuffer; + VeteranClaim *vcr = (VeteranClaim *)app->pBuffer; - if (vcr->claim_id == 0xFFFFFFFF) //request update packet - { + if (vcr->claim_id == 0xFFFFFFFF) { // request update packet SendRewards(); + return; } - else //try to claim something! - { - if (!TryReward(vcr->claim_id)) - { - Message(13, "Your claim has been rejected."); - EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaimReply)); - VeteranClaimReply * cr = (VeteranClaimReply*)vetapp->pBuffer; - strcpy(cr->name, GetName()); - cr->claim_id = vcr->claim_id; - cr->reject_field = -1; - FastQueuePacket(&vetapp); - } - else - { - EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaimReply)); - VeteranClaimReply * cr = (VeteranClaimReply*)vetapp->pBuffer; - strcpy(cr->name, GetName()); - cr->claim_id = vcr->claim_id; - cr->reject_field = 0; - FastQueuePacket(&vetapp); - } - } + // try to claim something! + EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaim)); + VeteranClaim *cr = (VeteranClaim *)vetapp->pBuffer; + strcpy(cr->name, GetName()); + cr->claim_id = vcr->claim_id; + + if (!TryReward(vcr->claim_id)) + cr->action = 1; + else + cr->action = 0; + + FastQueuePacket(&vetapp); } void Client::Handle_OP_VoiceMacroIn(const EQApplicationPacket *app) diff --git a/zone/client_process.cpp b/zone/client_process.cpp index fe748d5b3..938bfbc87 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -1844,7 +1844,7 @@ void Client::DoHPRegen() { } void Client::DoManaRegen() { - if (GetMana() >= max_mana) + if (GetMana() >= max_mana && spellbonuses.ManaRegen >= 0) return; SetMana(GetMana() + CalcManaRegen() + RestRegenMana); diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index df76216ae..e68924844 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -219,17 +219,17 @@ bool HateList::RemoveEntFromHateList(Mob *in_entity) { if ((*iterator)->entity_on_hatelist == in_entity) { - if (in_entity) - parse->EventNPC(EVENT_HATE_LIST, hate_owner->CastToNPC(), in_entity, "0", 0); is_found = true; - if (in_entity && in_entity->IsClient()) in_entity->CastToClient()->DecrementAggroCount(); delete (*iterator); iterator = list.erase(iterator); + if (in_entity) + parse->EventNPC(EVENT_HATE_LIST, hate_owner->CastToNPC(), in_entity, "0", 0); + } else ++iterator; diff --git a/zone/trading.cpp b/zone/trading.cpp index 81a70740e..b5bb3e42f 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -1558,18 +1558,7 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs, Client* Trader, const EQApplic return; } - EQApplicationPacket* outapp = nullptr; - - if (Trader->GetClientVersion() >= ClientVersion::RoF) - { - //outapp = new EQApplicationPacket(OP_TraderShop, sizeof(TraderBuy_Struct)); - } - else - { - //outapp = new EQApplicationPacket(OP_Trader, sizeof(TraderBuy_Struct)); - } - - outapp = new EQApplicationPacket(OP_Trader, sizeof(TraderBuy_Struct)); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Trader, sizeof(TraderBuy_Struct)); TraderBuy_Struct* outtbs = (TraderBuy_Struct*)outapp->pBuffer; @@ -1701,7 +1690,15 @@ void Client::SendBazaarWelcome() if (results.Success() && results.RowCount() == 1){ auto row = results.begin(); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarWelcome_Struct)); + EQApplicationPacket* outapp = nullptr; + if (GetClientVersion() >= ClientVersion::RoF) + { + outapp = new EQApplicationPacket(OP_TraderShop, sizeof(BazaarWelcome_Struct)); + } + else + { + outapp = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarWelcome_Struct)); + } memset(outapp->pBuffer,0,outapp->size); @@ -1712,6 +1709,11 @@ void Client::SendBazaarWelcome() bws->Traders = atoi(row[0]); bws->Items = atoi(row[1]); + if (GetClientVersion() >= ClientVersion::RoF) + { + bws->Unknown012 = GetID(); + } + QueuePacket(outapp); safe_delete(outapp); diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 70c02344c..e3df3f5e3 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1231,20 +1231,25 @@ bool ZoneDatabase::LoadCharacterTribute(uint32 character_id, PlayerProfile_Struc return true; } -bool ZoneDatabase::LoadCharacterPotions(uint32 character_id, PlayerProfile_Struct* pp) +bool ZoneDatabase::LoadCharacterPotions(uint32 character_id, PlayerProfile_Struct *pp) { - std::string query = StringFormat("SELECT `potion_id`, `item_id`, `icon` FROM `character_potionbelt` WHERE `id` = %u LIMIT %u", - character_id, EmuConstants::POTION_BELT_ITEM_COUNT); - auto results = database.QueryDatabase(query); int i = 0; - for (i = 0; i < EmuConstants::POTION_BELT_ITEM_COUNT; i++){ + std::string query = + StringFormat("SELECT `potion_id`, `item_id`, `icon` FROM `character_potionbelt` WHERE `id` = %u LIMIT %u", + character_id, EmuConstants::POTION_BELT_ITEM_COUNT); + auto results = database.QueryDatabase(query); + int i = 0; + for (i = 0; i < EmuConstants::POTION_BELT_ITEM_COUNT; i++) { pp->potionbelt.Items[i].Icon = 0; pp->potionbelt.Items[i].ID = 0; pp->potionbelt.Items[i].Name[0] = '\0'; } for (auto row = results.begin(); row != results.end(); ++row) { + i = atoi(row[0]); const ItemData *item_data = database.GetItem(atoi(row[1])); - if (item_data == nullptr) { continue; } + if (!item_data) + continue; + pp->potionbelt.Items[i].ID = item_data->ID; pp->potionbelt.Items[i].Icon = atoi(row[2]); strncpy(pp->potionbelt.Items[i].Name, item_data->Name, 64);