From ec5a9d0bd49673da1e47307c093ef7fd19284156 Mon Sep 17 00:00:00 2001 From: dannuic Date: Fri, 17 Apr 2026 18:07:39 -0600 Subject: [PATCH] Validated up to OP_MemorizeSpell (still needs testing in client) --- common/patches/tob.cpp | 34 ++++++++++++++++++++++++++++++++++ common/patches/tob_ops.h | 2 ++ common/patches/tob_structs.h | 9 ++++++++- tob/opcodes.md | 10 +++++----- zone/client_packet.cpp | 3 +++ 5 files changed, 52 insertions(+), 6 deletions(-) diff --git a/common/patches/tob.cpp b/common/patches/tob.cpp index 159ec2643..83a379b11 100644 --- a/common/patches/tob.cpp +++ b/common/patches/tob.cpp @@ -957,6 +957,23 @@ namespace TOB FINISH_ENCODE(); } + ENCODE(OP_MemorizeSpell) { + ENCODE_LENGTH_EXACT(MemorizeSpell_Struct); + SETUP_DIRECT_ENCODE(MemorizeSpell_Struct, structs::MemorizeSpell_Struct); + + // TODO: TOB has a "finish memming" value (2) here, might be needed to keep client spell gems in sync + if (emu->scribing == 2) + eq->scribing = 3; + else + OUT(scribing); + + OUT(slot); + OUT(spell_id); + OUT(reduction); + + FINISH_ENCODE(); + } + ENCODE(OP_MobHealth) { ENCODE_LENGTH_EXACT(SpawnHPUpdate_Struct2); SETUP_DIRECT_ENCODE(SpawnHPUpdate_Struct2, structs::MobHealth_Struct); @@ -3821,6 +3838,23 @@ namespace TOB DECODE_FORWARD(OP_GroupInvite); } + DECODE(OP_MemorizeSpell) { + DECODE_LENGTH_EXACT(structs::MemorizeSpell_Struct); + SETUP_DIRECT_DECODE(MemorizeSpell_Struct, structs::MemorizeSpell_Struct); + + // TODO: TOB has a "finish memming" value (2) here, might be needed to keep client spell gems in sync + if (emu->scribing == 3) + eq->scribing = 2; + else + IN(scribing); + + IN(slot); + IN(spell_id); + IN(reduction); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_MoveItem) { DECODE_LENGTH_EXACT(structs::MoveItem_Struct); diff --git a/common/patches/tob_ops.h b/common/patches/tob_ops.h index 9e8b8e07b..40a7c9adf 100644 --- a/common/patches/tob_ops.h +++ b/common/patches/tob_ops.h @@ -33,6 +33,7 @@ E(OP_Illusion) E(OP_ItemPacket) E(OP_LogServer) E(OP_ManaChange) +E(OP_MemorizeSpell) E(OP_MobHealth) E(OP_MoneyOnCorpse) E(OP_MoveItem) @@ -84,6 +85,7 @@ D(OP_GMTraining) D(OP_GroupDisband) D(OP_GroupInvite) D(OP_GroupInvite2) +D(OP_MemorizeSpell) D(OP_MoveItem) D(OP_RemoveBlockedBuffs) D(OP_SetServerFilter) diff --git a/common/patches/tob_structs.h b/common/patches/tob_structs.h index b2211c7f0..e003490ea 100644 --- a/common/patches/tob_structs.h +++ b/common/patches/tob_structs.h @@ -675,11 +675,18 @@ namespace TOB { /*000*/ uint32 spell_id; /*004*/ uint16 caster_id; /*006*/ uint32 cast_time; // in miliseconds - /*010*/ uint32 unknown0a; // I think this is caster effective level but im not sure. live always sends 0 + /*010*/ uint32 unknown0a; // I think this is caster effective level but im not sure. live always sends 0. The client uses this for the spell link /*014*/ uint8 unknown0e; // 0 will short circuit the cast, seen 1 from live usually, maybe related to interrupts or particles or something /*015*/ }; + struct MemorizeSpell_Struct { + uint32 slot; // Spot in the spell book/memorized slot + uint32 spell_id; // Spell id (200 or c8 is minor healing, etc) + uint32 scribing; // -1 refreshes book, 0 scribe to book, 2 end mem, 1 start mem, 3 unmem, 4 set activated item keyring -- client will send back 2 if a 0 operation updated a memorized spell of the same group + subgroup + uint32 reduction; // lower reuse (only used if scribing is 4) + }; + //I've observed 5 s16 that are all -1. //Clicky items don't even trigger this as far as i can tell so not sure what this is for now. //One of these could have changed to a s32 but im not sure. diff --git a/tob/opcodes.md b/tob/opcodes.md index f8113c535..3dbee363b 100644 --- a/tob/opcodes.md +++ b/tob/opcodes.md @@ -63,7 +63,7 @@ Below is a status list for the 450 opcodes we currently use on the server for th | `OP_BecomeCorpse` | 🔴 Not-Set | | | | `OP_BecomeTrader` | 🔴 Not-Set | | | | `OP_Begging` | 🟡 Unverified | | | -| `OP_BeginCast` | 🟡 Unverified | | | +| `OP_BeginCast` | 🟢 Verified | | | | `OP_Bind_Wound` | 🟡 Unverified | | | | `OP_BlockedBuffs` | 🟢 Verified | | | | `OP_BoardBoat` | 🟡 Unverified | | | @@ -103,14 +103,14 @@ Below is a status list for the 450 opcodes we currently use on the server for th | `OP_ClientUpdate` | 🟢 Verified | | | | `OP_CloseContainer` | 🔴 Not-Set | | | | `OP_CloseTributeMaster` | 🔴 Not-Set | | | -| `OP_ColoredText` | 🟡 Unverified | | | +| `OP_ColoredText` | 🟢 Verified | | | | `OP_CombatAbility` | 🟡 Unverified | | | | `OP_Command` | 🔴 Not-Set | | | | `OP_CompletedTasks` | 🔴 Not-Set | | | | `OP_ConfirmDelete` | 🟡 Unverified | | | | `OP_Consent` | 🟡 Unverified | | | | `OP_ConsentDeny` | 🟡 Unverified | | | -| `OP_ConsentResponse` | 🟡 Unverified | | | +| `OP_ConsentResponse` | 🟢 Verified | | | | `OP_Consider` | 🟡 Unverified | | | | `OP_ConsiderCorpse` | 🟡 Unverified | | | | `OP_Consume` | 🟡 Unverified | | | @@ -351,7 +351,7 @@ Below is a status list for the 450 opcodes we currently use on the server for th | `OP_MarkNPC` | 🔴 Not-Set | | | | `OP_MarkRaidNPC` | 🔴 Not-Set | | | | `OP_Marquee` | 🟡 Unverified | | | -| `OP_MemorizeSpell` | 🟡 Unverified | | | +| `OP_MemorizeSpell` | 🟢 Verified | | | | `OP_Mend` | 🟡 Unverified | | | | `OP_MendHPUpdate` | 🔴 Not-Set | | | | `OP_MercenaryAssign` | 🔴 Not-Set | | | @@ -435,7 +435,7 @@ Below is a status list for the 450 opcodes we currently use on the server for th | `OP_PVPLeaderBoardRequest` | 🔴 Not-Set | | | | `OP_PVPStats` | 🔴 Not-Set | | | | `OP_QueryResponseThing` | 🔴 Not-Set | | | -| `OP_QueryUCSServerStatus` | 🟡 Unverified | | | +| `OP_QueryUCSServerStatus` | 🟢 Verified | | | | `OP_RaidDelegateAbility` | 🔴 Not-Set | | | | `OP_RaidClearNPCMarks` | 🔴 Not-Set | | | | `OP_RaidInvite` | 🔴 Not-Set | | | diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index e568265c5..f1f241359 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -12239,6 +12239,9 @@ void Client::Handle_OP_QueryUCSServerStatus(const EQApplicationPacket *app) case EQ::versions::ClientVersion::RoF2: ConnectionType = EQ::versions::ucsRoF2Combined; break; + case EQ::versions::ClientVersion::TOB: + ConnectionType = EQ::versions::ucsTOBCombined; + break; default: ConnectionType = EQ::versions::ucsUnknown; break;