Merchant window startup

This commit is contained in:
KimLS 2024-12-05 19:39:59 -08:00
parent 2db1b1b9b0
commit d3ac751dd1
10 changed files with 149 additions and 78 deletions

View File

@ -71,6 +71,9 @@ namespace Class {
constexpr uint8 FellowshipMaster = 69; constexpr uint8 FellowshipMaster = 69;
constexpr uint8 AlternateCurrencyMerchant = 70; constexpr uint8 AlternateCurrencyMerchant = 70;
constexpr uint8 MercenaryLiaison = 71; constexpr uint8 MercenaryLiaison = 71;
constexpr uint8 RealEstateMerchant = 72;
constexpr uint8 LoyaltyMerchant = 73;
constexpr uint8 TributeMaster2 = 74;
constexpr uint8 PLAYER_CLASS_COUNT = 16; constexpr uint8 PLAYER_CLASS_COUNT = 16;
constexpr uint16 ALL_CLASSES_BITMASK = 65535; constexpr uint16 ALL_CLASSES_BITMASK = 65535;

View File

@ -354,6 +354,7 @@ N(OP_MercenaryTimer),
N(OP_MercenaryTimerRequest), N(OP_MercenaryTimerRequest),
N(OP_MercenaryUnknown1), N(OP_MercenaryUnknown1),
N(OP_MercenaryUnsuspendResponse), N(OP_MercenaryUnsuspendResponse),
N(OP_MerchantBulkItems),
N(OP_MobEnduranceUpdate), N(OP_MobEnduranceUpdate),
N(OP_MobHealth), N(OP_MobHealth),
N(OP_MobManaUpdate), N(OP_MobManaUpdate),

View File

@ -71,7 +71,7 @@ namespace Laurion
static inline uint32 LaurionToServerCorpseMainSlot(uint32 laurion_corpse_slot); static inline uint32 LaurionToServerCorpseMainSlot(uint32 laurion_corpse_slot);
static inline uint32 LaurionToServerTypelessSlot(structs::TypelessInventorySlot_Struct laurion_slot, int16 laurion_type); static inline uint32 LaurionToServerTypelessSlot(structs::TypelessInventorySlot_Struct laurion_slot, int16 laurion_type);
structs::ItemPacketType ServerToLaurionItemPacketType(ItemPacketType laurion_type); item::ItemPacketType ServerToLaurionItemPacketType(ItemPacketType laurion_type);
void Register(EQStreamIdentifier& into) void Register(EQStreamIdentifier& into)
{ {
@ -246,35 +246,35 @@ namespace Laurion
eq->entries[0] = -1; // Max AA Restriction eq->entries[0] = -1; // Max AA Restriction
eq->entries[1] = -1; // Max Level Restriction eq->entries[1] = -1; // Max Level Restriction
eq->entries[2] = -1; // Max Char Slots per Account (not used by client?) eq->entries[2] = -1; // Max Char Slots per Account (not used by client?)
eq->entries[3] = -1; // 1 for Silver eq->entries[3] = -1; // SpellTier
eq->entries[4] = -1; // Main Inventory Size eq->entries[4] = -1; // Main Inventory Size
eq->entries[5] = -1; // Max Platinum per level eq->entries[5] = -1; // Max Platinum per level
eq->entries[6] = 1; // Send Mail eq->entries[6] = 1; // Send Mail
eq->entries[7] = 1; // Use Parcels? eq->entries[7] = 1; // Use Parcels
eq->entries[8] = 1; // Voice Chat eq->entries[8] = 1; // Loyalty
eq->entries[9] = -1; // Merc Tiers eq->entries[9] = -1; // Merc Tiers
eq->entries[10] = 1; // Create Guilds eq->entries[10] = 1; // Housing
eq->entries[11] = -1; // Shared Bank Slots eq->entries[11] = -1; // Shared Bank Slots
eq->entries[12] = -1; // Max Journal Quests eq->entries[12] = -1; // Max Journal Quests
eq->entries[13] = 1; // Housing Enabled eq->entries[13] = 1; // CreateGuild
eq->entries[14] = 1; // Prestiege eq->entries[14] = 1; // Bazaar
eq->entries[15] = 1; // Broker System eq->entries[15] = 1; // Barter
eq->entries[16] = 1; // Chat eq->entries[16] = 1; // Chat
eq->entries[17] = 1; // Progression Server Access eq->entries[17] = 1; // Petition
eq->entries[18] = 1; // Customer Support eq->entries[18] = 1; // Advertising
eq->entries[19] = -1; // Popup reminders? eq->entries[19] = -1; // UseItem
eq->entries[20] = -1; // Exit Popup? eq->entries[20] = -1; // StartingCity
eq->entries[21] = 0; eq->entries[21] = 1; // Ornament
eq->entries[22] = 0; eq->entries[22] = 0; // HeroicCharacter
eq->entries[23] = 0; // This is the highest we actually see in detail entries eq->entries[23] = 0; // AutoGrantAA
eq->entries[24] = 0; eq->entries[24] = 0; // MountKeyRingSlots
eq->entries[25] = 0; eq->entries[25] = 0; // IllusionKeyRingSlots
eq->entries[26] = 0; eq->entries[26] = 0; // FamiliarKeyRingSlots
eq->entries[27] = 0; eq->entries[27] = 0; // FamiliarAutoLeave
eq->entries[28] = 0; eq->entries[28] = 0; // HeroForgeKeyRingSlots
eq->entries[29] = 0; eq->entries[29] = 0; // DragonHoardSlots
eq->entries[30] = 0; eq->entries[30] = 0; // TeleportKeyRingSlots
eq->entries[31] = 0; eq->entries[31] = 0; // PersonalDepotSlots
eq->entries[32] = 0; eq->entries[32] = 0;
FINISH_ENCODE(); FINISH_ENCODE();
@ -2860,14 +2860,14 @@ namespace Laurion
ItemPacket_Struct* old_item_pkt = (ItemPacket_Struct*)__emu_buffer; ItemPacket_Struct* old_item_pkt = (ItemPacket_Struct*)__emu_buffer;
auto type = ServerToLaurionItemPacketType(old_item_pkt->PacketType); auto type = ServerToLaurionItemPacketType(old_item_pkt->PacketType);
if (type == structs::ItemPacketInvalid) { if (type == item::ItemPacketType::ItemPacketInvalid) {
delete in; delete in;
return; return;
} }
switch (type) switch (type)
{ {
case structs::ItemPacketType::ItemPacketParcel: { case item::ItemPacketType::ItemPacketParcel: {
ParcelMessaging_Struct pms{}; ParcelMessaging_Struct pms{};
EQ::Util::MemoryStreamReader ss(reinterpret_cast<char*>(in->pBuffer), in->size); EQ::Util::MemoryStreamReader ss(reinterpret_cast<char*>(in->pBuffer), in->size);
cereal::BinaryInputArchive ar(ss); cereal::BinaryInputArchive ar(ss);
@ -2906,6 +2906,26 @@ namespace Laurion
delete in; delete in;
} }
ENCODE(OP_ShopRequest)
{
ENCODE_LENGTH_EXACT(MerchantClick_Struct);
SETUP_DIRECT_ENCODE(MerchantClick_Struct, structs::MerchantClickResponse_Struct);
if (emu->command == 0) {
OUT(player_id);
eq->npc_id = 0;
}
else {
OUT(npc_id);
OUT(player_id);
OUT(rate);
OUT(tab_display);
eq->unknown028 = 256;
}
FINISH_ENCODE();
}
// DECODE methods // DECODE methods
DECODE(OP_EnterWorld) DECODE(OP_EnterWorld)
@ -3094,6 +3114,16 @@ namespace Laurion
FINISH_DIRECT_DECODE(); FINISH_DIRECT_DECODE();
} }
DECODE(OP_ShopRequest)
{
DECODE_LENGTH_EXACT(structs::MerchantClickRequest_Struct);
SETUP_DIRECT_DECODE(MerchantClick_Struct, structs::MerchantClickRequest_Struct);
IN(npc_id);
FINISH_DIRECT_DECODE();
}
//Naive version but should work well enough for now //Naive version but should work well enough for now
int ExtractIDFile(const std::string& input) { int ExtractIDFile(const std::string& input) {
std::string number; std::string number;
@ -4451,30 +4481,30 @@ namespace Laurion
return ServerSlot; return ServerSlot;
} }
structs::ItemPacketType ServerToLaurionItemPacketType(ItemPacketType server_type) { item::ItemPacketType ServerToLaurionItemPacketType(ItemPacketType server_type) {
switch (server_type) { switch (server_type) {
case ItemPacketType::ItemPacketMerchant: case ItemPacketType::ItemPacketMerchant:
return structs::ItemPacketType::ItemPacketMerchant; return item::ItemPacketType::ItemPacketMerchant;
case ItemPacketType::ItemPacketTradeView: case ItemPacketType::ItemPacketTradeView:
return structs::ItemPacketType::ItemPacketTradeView; return item::ItemPacketType::ItemPacketTradeView;
case ItemPacketType::ItemPacketLoot: case ItemPacketType::ItemPacketLoot:
return structs::ItemPacketType::ItemPacketLoot; return item::ItemPacketType::ItemPacketLoot;
case ItemPacketType::ItemPacketTrade: case ItemPacketType::ItemPacketTrade:
return structs::ItemPacketType::ItemPacketTrade; return item::ItemPacketType::ItemPacketTrade;
case ItemPacketType::ItemPacketCharInventory: case ItemPacketType::ItemPacketCharInventory:
return structs::ItemPacketType::ItemPacketCharInventory; return item::ItemPacketType::ItemPacketCharInventory;
case ItemPacketType::ItemPacketLimbo: case ItemPacketType::ItemPacketLimbo:
return structs::ItemPacketType::ItemPacketLimbo; return item::ItemPacketType::ItemPacketLimbo;
case ItemPacketType::ItemPacketWorldContainer: case ItemPacketType::ItemPacketWorldContainer:
return structs::ItemPacketType::ItemPacketWorldContainer; return item::ItemPacketType::ItemPacketWorldContainer;
case ItemPacketType::ItemPacketTributeItem: case ItemPacketType::ItemPacketTributeItem:
return structs::ItemPacketType::ItemPacketTributeItem; return item::ItemPacketType::ItemPacketTributeItem;
case ItemPacketType::ItemPacketGuildTribute: case ItemPacketType::ItemPacketGuildTribute:
return structs::ItemPacketType::ItemPacketGuildTribute; return item::ItemPacketType::ItemPacketGuildTribute;
case ItemPacketType::ItemPacketCharmUpdate: case ItemPacketType::ItemPacketCharmUpdate:
return structs::ItemPacketType::ItemPacketCharmUpdate; return item::ItemPacketType::ItemPacketCharmUpdate;
default: default:
return structs::ItemPacketType::ItemPacketInvalid; return item::ItemPacketType::ItemPacketInvalid;
} }
} }
} /*Laurion*/ } /*Laurion*/

View File

@ -249,20 +249,26 @@ namespace Laurion
//}; //};
enum ItemPacketType : int { enum ItemPacketType : int {
ItemPacketMerchant = 100, ItemPacketMerchant = 0x64,
ItemPacketTradeView = 101, ItemPacketTradeView = 0x65,
ItemPacketLoot = 102, ItemPacketLoot = 0x66,
ItemPacketTrade = 103, ItemPacketTrade = 0x67,
ItemPacketCharInventory = 105, //looks like they added something at 0x68 that didn't exist before and shifted everything after it up by 1
ItemPacketLimbo = 106, ItemPacketUnknown068 = 0x68, //Not sure but it seems to deal with the cursor somehow.
ItemPacketWorldContainer = 107, ItemPacketCharInventory = 0x6A, //Rof 0x69 -> Larion 0x6a (requires translation)
ItemPacketTributeItem = 108, ItemPacketLimbo = 0x6B, //0x6A -> 0x6B
ItemPacketGuildTribute = 109, ItemPacketWorldContainer = 0x6C,
ItemPacket10 = 110, ItemPacketTributeItem = 0x6D,
ItemPacket11 = 111, ItemPacketGuildTribute = 0x6E,
ItemPacket12 = 112, ItemPacketCharmUpdate = 0x6f,
ItemPacketRecovery = 113, ItemPacketRecovery = 0x72,
ItemPacket14 = 115 // Parcel? adds to merchant window too ItemPacketParcel = 0x74,
ItemPacketUnknown075 = 0x75, //Not sure but uses a lot of the same logic as the trade and char inventory types
ItemPacketOverflow = 0x76,
ItemPacketDragonHoard = 0x77,
ItemPacketTradeskill = 0x78,
ItemPacketTradeskillDepot = 0x79,
ItemPacketInvalid = 0xFF
}; };
} /*item*/ } /*item*/

View File

@ -34,6 +34,7 @@ E(OP_MoveItem)
E(OP_ExpUpdate) E(OP_ExpUpdate)
E(OP_SendAATable) E(OP_SendAATable)
E(OP_ItemPacket) E(OP_ItemPacket)
E(OP_ShopRequest)
//list of packets we need to decode on the way in: //list of packets we need to decode on the way in:
D(OP_EnterWorld) D(OP_EnterWorld)
D(OP_ZoneEntry) D(OP_ZoneEntry)
@ -47,6 +48,7 @@ D(OP_ConsiderCorpse)
D(OP_ClickDoor) D(OP_ClickDoor)
D(OP_SpawnAppearance) D(OP_SpawnAppearance)
D(OP_MoveItem) D(OP_MoveItem)
D(OP_ShopRequest)
#undef E #undef E
#undef D #undef D

View File

@ -602,29 +602,23 @@ namespace Laurion {
/*0028*/ /*0028*/
}; };
//These are significantly changed in Laurion from RoF2 struct MerchantClickRequest_Struct
enum ItemPacketType { {
//ItemPacketViewLink = 0x00, /*000*/ uint32 npc_id; // Merchant NPC's entity id
ItemPacketMerchant = 0x64, /*004*/
ItemPacketTradeView = 0x65, };
ItemPacketLoot = 0x66,
ItemPacketTrade = 0x67, struct MerchantClickResponse_Struct
//looks like they added something at 0x68 that didn't exist before and shifted everything after it up by 1 {
ItemPacketUnknown068 = 0x68, //Not sure but it seems to deal with the cursor somehow. /*000*/ uint32 npc_id; // Merchant NPC's entity id
ItemPacketCharInventory = 0x6A, //Rof 0x69 -> Larion 0x6a (requires translation) /*004*/ uint32 player_id;
ItemPacketLimbo = 0x6B, //0x6A -> 0x6B /*008*/ float rate;
ItemPacketWorldContainer = 0x6C, /*012*/ uint32 tab_display; // bitmask b000 none, b001 Purchase/Sell, b010 Recover, b100 Parcels
ItemPacketTributeItem = 0x6D, /*016*/ uint32 ldon_category; // ldon cat for ldon merchants
ItemPacketGuildTribute = 0x6E, /*020*/ uint32 alt_currency1; //These two usually match but I imagine they could be different?
ItemPacketCharmUpdate = 0x6f, /*024*/ uint32 alt_currency2;
ItemPacketRecovery = 0x72, /*028*/ uint32 unknown028; // Observed 256
ItemPacketParcel = 0x74, /*032*/
ItemPacketUnknown075 = 0x75, //Not sure but uses a lot of the same logic as the trade and char inventory types
ItemPacketOverflow = 0x76,
ItemPacketDragonHoard = 0x77,
ItemPacketTradeskill = 0x78,
ItemPacketTradeskillDepot = 0x79,
ItemPacketInvalid = 0xFF
}; };
#pragma pack() #pragma pack()

View File

@ -3602,6 +3602,21 @@ namespace RoF2
FINISH_ENCODE(); FINISH_ENCODE();
} }
ENCODE(OP_ShopRequest)
{
ENCODE_LENGTH_EXACT(MerchantClick_Struct);
SETUP_DIRECT_ENCODE(MerchantClick_Struct, structs::MerchantClick_Struct);
OUT(npc_id);
OUT(player_id);
OUT(command);
OUT(rate);
OUT(tab_display);
eq->unknown02 = emu->unknown020;
FINISH_ENCODE();
}
ENCODE(OP_SkillUpdate) ENCODE(OP_SkillUpdate)
{ {
@ -5993,6 +6008,21 @@ namespace RoF2
FINISH_DIRECT_DECODE(); FINISH_DIRECT_DECODE();
} }
DECODE(OP_ShopRequest)
{
DECODE_LENGTH_EXACT(structs::MerchantClick_Struct);
SETUP_DIRECT_DECODE(MerchantClick_Struct, structs::MerchantClick_Struct);
IN(npc_id);
IN(player_id);
IN(command);
IN(rate);
IN(tab_display);
emu->unknown020 = 0;
FINISH_DIRECT_DECODE();
}
DECODE(OP_Save) DECODE(OP_Save)
{ {
DECODE_LENGTH_EXACT(structs::Save_Struct); DECODE_LENGTH_EXACT(structs::Save_Struct);

View File

@ -118,6 +118,7 @@ E(OP_SendZonepoints)
E(OP_SetGuildRank) E(OP_SetGuildRank)
E(OP_ShopPlayerBuy) E(OP_ShopPlayerBuy)
E(OP_ShopPlayerSell) E(OP_ShopPlayerSell)
E(OP_ShopRequest)
E(OP_SkillUpdate) E(OP_SkillUpdate)
E(OP_SomeItemPacketMaybe) E(OP_SomeItemPacketMaybe)
E(OP_SpawnAppearance) E(OP_SpawnAppearance)
@ -203,6 +204,7 @@ D(OP_Save)
D(OP_SetServerFilter) D(OP_SetServerFilter)
D(OP_ShopPlayerBuy) D(OP_ShopPlayerBuy)
D(OP_ShopPlayerSell) D(OP_ShopPlayerSell)
D(OP_ShopRequest)
D(OP_ShopSendParcel) D(OP_ShopSendParcel)
D(OP_Trader) D(OP_Trader)
D(OP_TraderBuy) D(OP_TraderBuy)

View File

@ -464,9 +464,9 @@ OP_ItemAdvancedLoreText=0x0000
# merchant stuff # merchant stuff
OP_ShopPlayerSell=0x0000 OP_ShopPlayerSell=0x0000
OP_ShopRequest=0x0000 OP_ShopRequest=0x840
OP_ShopEnd=0x0000 OP_ShopEnd=0x74bb
OP_ShopEndConfirm=0x0000 OP_ShopEndConfirm=0x2ed1
OP_ShopPlayerBuy=0x0000 OP_ShopPlayerBuy=0x0000
OP_ShopDelItem=0x0000 OP_ShopDelItem=0x0000
OP_ShopSendParcel=0x0000 OP_ShopSendParcel=0x0000

View File

@ -14639,7 +14639,10 @@ void Client::Handle_OP_ShopRequest(const EQApplicationPacket *app)
mco->rate = 1 / buy_cost_mod; mco->rate = 1 / buy_cost_mod;
} }
outapp->priority = 6; if (m_ClientVersion >= EQ::versions::ClientVersion::Laurion) {
mco->player_id = GetID();
}
QueuePacket(outapp); QueuePacket(outapp);
safe_delete(outapp); safe_delete(outapp);