You can subscribe to this list here.
| 2004 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
(18) |
Oct
(11) |
Nov
(9) |
Dec
|
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2005 |
Jan
|
Feb
(6) |
Mar
|
Apr
(10) |
May
(38) |
Jun
(15) |
Jul
(32) |
Aug
|
Sep
(17) |
Oct
(11) |
Nov
|
Dec
(14) |
| 2006 |
Jan
(2) |
Feb
(5) |
Mar
(3) |
Apr
(2) |
May
(3) |
Jun
(7) |
Jul
|
Aug
|
Sep
(3) |
Oct
(2) |
Nov
(6) |
Dec
(3) |
| 2007 |
Jan
|
Feb
(17) |
Mar
(5) |
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
| 2008 |
Jan
(11) |
Feb
|
Mar
|
Apr
(4) |
May
|
Jun
(6) |
Jul
|
Aug
|
Sep
(6) |
Oct
(4) |
Nov
(1) |
Dec
|
| 2009 |
Jan
(3) |
Feb
(2) |
Mar
(4) |
Apr
|
May
(4) |
Jun
(3) |
Jul
|
Aug
(5) |
Sep
(3) |
Oct
(2) |
Nov
|
Dec
(6) |
| 2010 |
Jan
|
Feb
(6) |
Mar
|
Apr
|
May
|
Jun
|
Jul
(2) |
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
| 2011 |
Jan
(2) |
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
| 2012 |
Jan
|
Feb
|
Mar
(1) |
Apr
|
May
|
Jun
|
Jul
(8) |
Aug
|
Sep
(2) |
Oct
(2) |
Nov
(4) |
Dec
(5) |
| 2013 |
Jan
(2) |
Feb
|
Mar
(2) |
Apr
(2) |
May
(1) |
Jun
(2) |
Jul
(2) |
Aug
(2) |
Sep
|
Oct
(2) |
Nov
(2) |
Dec
(2) |
| 2014 |
Jan
(3) |
Feb
(2) |
Mar
(3) |
Apr
(4) |
May
(4) |
Jun
(3) |
Jul
(4) |
Aug
(5) |
Sep
|
Oct
(2) |
Nov
(5) |
Dec
(2) |
| 2015 |
Jan
|
Feb
(2) |
Mar
|
Apr
|
May
(2) |
Jun
|
Jul
(2) |
Aug
|
Sep
|
Oct
|
Nov
(10) |
Dec
|
| 2016 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
(4) |
Aug
(2) |
Sep
(2) |
Oct
(3) |
Nov
(4) |
Dec
(2) |
| 2017 |
Jan
(2) |
Feb
|
Mar
(2) |
Apr
(4) |
May
(2) |
Jun
(2) |
Jul
(2) |
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(2) |
| 2018 |
Jan
(4) |
Feb
(2) |
Mar
(2) |
Apr
(2) |
May
(2) |
Jun
(2) |
Jul
(8) |
Aug
(2) |
Sep
(2) |
Oct
(2) |
Nov
(4) |
Dec
(6) |
| 2019 |
Jan
(2) |
Feb
(2) |
Mar
(2) |
Apr
(4) |
May
(2) |
Jun
(3) |
Jul
(3) |
Aug
(2) |
Sep
(2) |
Oct
(13) |
Nov
(6) |
Dec
(4) |
| 2020 |
Jan
(7) |
Feb
(7) |
Mar
(7) |
Apr
(8) |
May
(5) |
Jun
(3) |
Jul
(10) |
Aug
(4) |
Sep
|
Oct
(37) |
Nov
(30) |
Dec
(8) |
| 2021 |
Jan
(14) |
Feb
(9) |
Mar
(7) |
Apr
(17) |
May
(3) |
Jun
(10) |
Jul
(7) |
Aug
(3) |
Sep
(3) |
Oct
(4) |
Nov
(15) |
Dec
(22) |
| 2022 |
Jan
(3) |
Feb
(3) |
Mar
(3) |
Apr
(3) |
May
(3) |
Jun
(3) |
Jul
(3) |
Aug
(5) |
Sep
(6) |
Oct
(4) |
Nov
(34) |
Dec
(17) |
| 2023 |
Jan
(3) |
Feb
(3) |
Mar
(29) |
Apr
(2) |
May
(18) |
Jun
(12) |
Jul
(12) |
Aug
(2) |
Sep
(7) |
Oct
(2) |
Nov
(2) |
Dec
(2) |
| 2024 |
Jan
(2) |
Feb
(10) |
Mar
(17) |
Apr
(6) |
May
(3) |
Jun
(9) |
Jul
(22) |
Aug
(48) |
Sep
(3) |
Oct
(2) |
Nov
(2) |
Dec
(8) |
| 2025 |
Jan
(3) |
Feb
(2) |
Mar
(9) |
Apr
(7) |
May
(3) |
Jun
(3) |
Jul
(2) |
Aug
(2) |
Sep
(11) |
Oct
(6) |
Nov
(9) |
Dec
(4) |
| 2026 |
Jan
(5) |
Feb
(6) |
Mar
(3) |
Apr
(7) |
May
(2) |
Jun
(11) |
Jul
(30) |
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
|
From: <xer...@us...> - 2026-07-30 16:08:47
|
Revision: 1690
http://sourceforge.net/p/seq/svn/1690
Author: xerxes00
Date: 2026-07-30 16:08:44 +0000 (Thu, 30 Jul 2026)
Log Message:
-----------
Suppress spurious "zone name not set" warning on first zone-in
Mirror of xerxes-trunk-dev r1689. saveSpawnPoints() flushes the previous
zone before the new name is set, so the session's first flush has an
empty zone name - expected (no prior zone to save), not an error. Return
quietly. zoneNameFromID() synthesizes "unk_zone_<id>" for unrecognized
ids, so a real zone never reaches this path empty.
Modified Paths:
--------------
showeq/branches/showeqlegends/src/spawnmonitor.cpp
Modified: showeq/branches/showeqlegends/src/spawnmonitor.cpp
===================================================================
--- showeq/branches/showeqlegends/src/spawnmonitor.cpp 2026-07-30 16:07:55 UTC (rev 1689)
+++ showeq/branches/showeqlegends/src/spawnmonitor.cpp 2026-07-30 16:08:44 UTC (rev 1690)
@@ -286,7 +286,9 @@
if ( !m_zoneName.length() )
{
- seqWarn("Zone name not set in 'SpawnMonitor::saveSpawnPoints'!" );
+ // Expected on the first zone of a session: spawn points get recorded
+ // before the zone name is set, so the initial flush has no prior zone
+ // to save. Return quietly - not an error.
return;
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <xer...@us...> - 2026-07-30 16:07:56
|
Revision: 1689
http://sourceforge.net/p/seq/svn/1689
Author: xerxes00
Date: 2026-07-30 16:07:55 +0000 (Thu, 30 Jul 2026)
Log Message:
-----------
Suppress spurious "zone name not set" warning on first zone-in
saveSpawnPoints() flushes the previous zone's points before the new
zone name is set, so the first flush of a session has an empty zone
name. That is expected - there is no prior zone to save - not an error.
Return quietly instead of warning.
A real zone never reaches this path empty: zoneNameFromID() synthesizes
"unk_zone_<id>" for unrecognized zone ids, so the empty case only ever
occurs on the session's first flush.
Modified Paths:
--------------
showeq/branches/xerxes-trunk-dev/src/spawnmonitor.cpp
Modified: showeq/branches/xerxes-trunk-dev/src/spawnmonitor.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/spawnmonitor.cpp 2026-07-30 15:31:08 UTC (rev 1688)
+++ showeq/branches/xerxes-trunk-dev/src/spawnmonitor.cpp 2026-07-30 16:07:55 UTC (rev 1689)
@@ -286,7 +286,9 @@
if ( !m_zoneName.length() )
{
- seqWarn("Zone name not set in 'SpawnMonitor::saveSpawnPoints'!" );
+ // Expected on the first zone of a session: spawn points get recorded
+ // before the zone name is set, so the initial flush has no prior zone
+ // to save. Return quietly - not an error.
return;
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <xer...@us...> - 2026-07-30 15:31:10
|
Revision: 1688
http://sourceforge.net/p/seq/svn/1688
Author: xerxes00
Date: 2026-07-30 15:31:08 +0000 (Thu, 30 Jul 2026)
Log Message:
-----------
Legends: drop dangling spawnAnimationStruct payload on OP_Animation
Modified Paths:
--------------
showeq/branches/showeqlegends/conf/zoneopcodes.xml
Modified: showeq/branches/showeqlegends/conf/zoneopcodes.xml
===================================================================
--- showeq/branches/showeqlegends/conf/zoneopcodes.xml 2026-07-30 15:26:50 UTC (rev 1687)
+++ showeq/branches/showeqlegends/conf/zoneopcodes.xml 2026-07-30 15:31:08 UTC (rev 1688)
@@ -197,7 +197,6 @@
</opcode>
<opcode id="486e" name="OP_Animation" priority="0" updated="07/29/26">
<priorityNote>noHandler</priorityNote>
- <payload dir="both" typename="spawnAnimationStruct" sizechecktype="none"/>
</opcode>
<opcode id="6426" name="OP_InspectRequest" priority="0" updated="07/29/26">
<priorityNote>noHandler</priorityNote>
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <xer...@us...> - 2026-07-30 15:26:52
|
Revision: 1687
http://sourceforge.net/p/seq/svn/1687
Author: xerxes00
Date: 2026-07-30 15:26:50 +0000 (Thu, 30 Jul 2026)
Log Message:
-----------
Legends: remove dead self-state path, buffChange structs, cleanup
- drop 38-byte eqlClientSelfPosStruct + len==38 branch (dead on 0729)
- buffChange decodes via buffChangeStruct/buffChangeEQLStruct (live 16B@8, Legends 24B@12)
Modified Paths:
--------------
showeq/branches/showeqlegends/src/everquest.h
showeq/branches/showeqlegends/src/packetformat.h
showeq/branches/showeqlegends/src/packetstream.cpp
showeq/branches/showeqlegends/src/player.cpp
showeq/branches/showeqlegends/src/s_everquest.h
showeq/branches/showeqlegends/src/spellshell.cpp
Modified: showeq/branches/showeqlegends/src/everquest.h
===================================================================
--- showeq/branches/showeqlegends/src/everquest.h 2026-07-30 02:10:06 UTC (rev 1686)
+++ showeq/branches/showeqlegends/src/everquest.h 2026-07-30 15:26:50 UTC (rev 1687)
@@ -1161,8 +1161,8 @@
struct
{
- // EQ Legends 2026-07-29 posData: low 19 bits of three
- // consecutive words, field >> 3. Field names keep the EQL
+ // posData: low 19 bits of three consecutive words,
+ // field >> 3. Field names keep the EQL
// transposed convention consumed by setPos in spawn.cpp.
// High 13 bits presumed deltas (unvalidated); words 3-4
// unmapped and zeroed at consumption.
@@ -1591,7 +1591,7 @@
struct playerPosUpdateEQLStruct
{
/*0000*/ uint32_t spawnId;
-/*0004*/ signed z:19; // >>3 - EQL 0729 layout
+/*0004*/ signed z:19; // >>3
signed deltaZ:13;
/*0008*/ signed x:19; // >>3
signed deltaX:13;
@@ -2612,32 +2612,8 @@
/*0042*/
};
-// EQ Legends 2026-07-28 real-time client self-state (the 38-byte body on
-// the c2s OP_ClientUpdate/OP_SelfPosEQL stream). Wire-verified against
-// /loc: y@6, z@10, heading@26 (facing packed in the low 21 bits), x@34.
-struct eqlClientSelfPosStruct
-{
-/*0000*/ uint16_t ctr; // update counter
-/*0002*/ uint16_t spawnId; // player's spawn id
-/*0004*/ uint16_t unknown0004;
-/*0006*/ float y; // y coord (loc value 2)
-/*0010*/ float z; // z coord (loc value 3)
-/*0014*/ uint8_t unknown0014[8];
-/*0022*/ union {
- uint32_t headingRaw; // raw dword @22
- struct {
- unsigned :20; // low 20 bits unused for facing
- unsigned heading :11; // facing 0..2047 (bits 20-30); swept a full circle on a spin
- unsigned :1;
- };
- };
-/*0026*/ uint8_t unknown0026[8];
-/*0034*/ float x; // x coord (loc value 1)
-/*0038*/
-};
-
-// EQ Legends 2026-07-29 client self-state on the c2s OP_ClientUpdate
-// 42-byte record - freshest self position + facing (EQ h2048).
+// Client self-state on the c2s OP_ClientUpdate 42-byte record -
+// freshest self position + facing (EQ h2048).
// A 44-byte variant exists; trailing bytes are tolerated.
struct eqlClientUpdateSelfStruct
{
@@ -2872,10 +2848,36 @@
/*0154*/ uint8_t unknown0106[6];
/*0160*/ uint32_t spellslot; // buff slot in buff window
/*0164*/ uint32_t changetype; // 1=buff fading,2=buff duration
-/*0168*/
+/*0168*/
};
/*
+** buffChangeStruct - single buff apply/fade (OP_Buff, server direction).
+** One handler decodes both servers; changeType sits at a different offset
+** per server, so each has its own struct, selected by record length:
+** buffChangeStruct - live, 16-byte record, changeType @8
+** buffChangeEQLStruct - Legends, 24-byte record, changeType @12
+** changeType: 1 = faded/clicked off, 4 = applied.
+*/
+
+struct buffChangeStruct
+{
+/*0000*/ uint32_t buffSlot; // buff-window slot (0-14)
+/*0004*/ uint32_t spellId; // spell id
+/*0008*/ uint32_t changeType; // 1 = fade, 4 = apply
+/*0012*/
+};
+
+struct buffChangeEQLStruct
+{
+/*0000*/ uint32_t buffSlot; // buff-window slot (0-14)
+/*0004*/ uint32_t spellId; // spell id
+/*0008*/ uint32_t unknown0008;
+/*0012*/ uint32_t changeType; // 1 = fade, 4 = apply
+/*0016*/
+};
+
+/*
** Guild Member Update structure
** Length: 88 Octets
**
Modified: showeq/branches/showeqlegends/src/packetformat.h
===================================================================
--- showeq/branches/showeqlegends/src/packetformat.h 2026-07-30 02:10:06 UTC (rev 1686)
+++ showeq/branches/showeqlegends/src/packetformat.h 2026-07-30 15:26:50 UTC (rev 1687)
@@ -161,19 +161,18 @@
// Bare packets come in two wire forms on EQ Legends, matching
// live's model. Flag-split packets ([op_lo][flag][op_hi]
// [payload][crc16], flag 0xa5 raw or 0x5a zlib) use the
- // opcode-split format - the 0x0b03 OP_ClientUpdate family
- // (client self-state and server spawn waypoints), 0x2824, and
- // OP_NpcMoveUpdate (relocated to 0x2824 on Legends,
- // live-identical bit format, wire-verified: spawnIds in its
- // records match live zone NPCs) travel this way. Plain packets (net op LE
- // uint16, payload at byte 2, no flags, no CRC - the 0x71fc
+ // opcode-split format - the OP_ClientUpdate family (client
+ // self-state and server spawn waypoints) and OP_NpcMoveUpdate
+ // (live-identical bit format, wire-verified: spawnIds in its
+ // records match live zone NPCs) travel this way. Plain packets
+ // (net op LE uint16, payload at byte 2, no flags, no CRC - the
// keepalive family) do not. Split only when byte 1 is a real
// protocol flag. Restricted to the 0x03 low-byte family:
// every legitimate flag-split channel observed has low byte
// 0x03, and an unrestricted byte-1 test false-fires on plain
- // opcodes whose HIGH byte is 0xa5 or 0x5a (OP_NewZone 0x5ab6
- // is one), mangling them into random opcodes with shifted
- // payloads - the source class of the PC-racing regression.
+ // opcodes whose HIGH byte is 0xa5 or 0x5a (OP_NewZone is one),
+ // mangling them into random opcodes with shifted payloads -
+ // the source class of the PC-racing regression.
return m_length >= 3 &&
eqFlagSplitLowByte[m_packet[0]] &&
(m_packet[1] == PROTOCOL_FLAG_COMPRESSED ||
Modified: showeq/branches/showeqlegends/src/packetstream.cpp
===================================================================
--- showeq/branches/showeqlegends/src/packetstream.cpp 2026-07-30 02:10:06 UTC (rev 1686)
+++ showeq/branches/showeqlegends/src/packetstream.cpp 2026-07-30 15:26:50 UTC (rev 1687)
@@ -628,17 +628,6 @@
packet.getNetOpCode() == 0x0b03)
return;
- // EQ Legends: the bare flag-split form of the spawn waypoint opcode
- // (0x0b03, server direction) is a majority-undecoded record layout
- // that misreads as position garbage. Only the OP_Packet-wrapped form
- // is dispatched - the exact milestone-f population, which field
- // testing confirmed acceptable. Client direction (self-state) uses a
- // separate stream and is unaffected. Remove when the record layout
- // is wire-solved.
- if (m_dir == DIR_Server && packet.hasFlags() && !packet.isSubpacket() &&
- packet.getNetOpCode() == 0x0b03)
- return;
-
processPacket(packet, false); // false = isn't subpacket
// if the cache isn't empty, then process it.
Modified: showeq/branches/showeqlegends/src/player.cpp
===================================================================
--- showeq/branches/showeqlegends/src/player.cpp 2026-07-30 02:10:06 UTC (rev 1686)
+++ showeq/branches/showeqlegends/src/player.cpp 2026-07-30 15:26:50 UTC (rev 1687)
@@ -945,169 +945,11 @@
if (dir == DIR_Client)
{
- // Movement experiment: keepalive position, glide deltas, and
- // travel-direction heading are disabled (2026-07-08: glide was
- // re-tried against the 0x0201 record variants and rejected in
- // game - live does not glide the self dot either). Self position
- // comes from the last-record scan in playerSelfState; facing from
- // the 42B self-state variant.
+ // Client-direction self-updates are a no-op here: self position
+ // comes from the last-record scan in playerSelfState, and facing
+ // from the self-state variant. This handler serves only the
+ // server-direction path below.
return;
-
- if (len < 12)
- return;
-
- // Legends client upload: 17-byte records tiled from offset 0.
- // float x@0, float y@4, float z@8, flag u8@12, msec counter u16@13,
- // sequence u16@15. Big packets (~1/sec while moving) carry a full
- // second of sub-second samples; the tail window of those samples
- // supplies velocity and travel heading. The client transmits no
- // facing while stationary, so heading holds its last value at rest.
- bool found = false;
- float fx = 0.0f, fy = 0.0f, fz = 0.0f;
- uint16_t lastCtr = 0;
- for (size_t off = 0; off + 17 <= len; off += 17)
- {
- const eqlSelfPosRecord* r = (const eqlSelfPosRecord*)(data + off);
- float tx = r->y, ty = r->x, tz = r->z;
- if (tx > -100000.0f && tx < 100000.0f &&
- ty > -100000.0f && ty < 100000.0f &&
- tz > -10000.0f && tz < 10000.0f &&
- (tx != 0.0f || ty != 0.0f))
- {
- fx = tx; fy = ty; fz = tz; found = true;
- lastCtr = r->counter;
- }
- }
- if (!found)
- return;
-
- // Velocity window: the sane record closest to 250ms before the last
- // record, located via the per-record msec counters (u16 wrap safe).
- // A tail window tracks the current direction of travel instead of
- // averaging across the whole second, which cuts corners on turns.
- bool haveBack = false;
- float bx = 0.0f, by = 0.0f, bz = 0.0f;
- uint16_t backCtr = 0;
- for (size_t off = 0; off + 17 <= len; off += 17)
- {
- const eqlSelfPosRecord* r = (const eqlSelfPosRecord*)(data + off);
- float tx = r->y, ty = r->x, tz = r->z;
- if (!(tx > -100000.0f && tx < 100000.0f &&
- ty > -100000.0f && ty < 100000.0f &&
- tz > -10000.0f && tz < 10000.0f &&
- (tx != 0.0f || ty != 0.0f)))
- continue;
- uint16_t tctr = r->counter;
- uint16_t age = (uint16_t)(lastCtr - tctr);
- if (age >= 250 && age < 5000)
- {
- bx = tx; by = ty; bz = tz; backCtr = tctr; haveBack = true;
- }
- }
-
- // Freshness arbitration: this packet's newest record is about
- // one second stale on arrival, so while the self-state stream
- // (playerSelfState) is delivering positions, applying it would
- // snap the dot backwards by up to a second of travel. When
- // self-state goes quiet (some movement modes emit it at
- // multi-second gaps) this becomes the freshest source and takes
- // over as the 1Hz fallback.
- bool selfStateFresh = m_selfStateAge.isValid() &&
- m_selfStateAge.elapsed() < 2500;
- if (!selfStateFresh)
- {
- int16_t px = int16_t(fy);
- int16_t py = int16_t(fx);
- int16_t pz = int16_t(fz);
- setPos(px, py, pz, showeq_params->walkpathrecord, showeq_params->walkpathlength);
- }
-
- // Map convention transpose (map x = wire y) applies to velocity too
- float vxm = 0.0f, vym = 0.0f, vzm = 0.0f;
- if (haveBack)
- {
- float dts = ((uint16_t)(lastCtr - backCtr)) / 1000.0f;
- if (dts > 0.05f)
- {
- vxm = (fy - by) / dts;
- vym = (fx - bx) / dts;
- vzm = (fz - bz) / dts;
- }
- }
-
- // Stop detection: a short window over the last ~100ms of records.
- // If end-of-packet speed collapsed relative to the tail window, the
- // player stopped as the packet closed - zero the glide immediately
- // instead of overshooting for up to a second and snapping back.
- bool haveShort = false;
- float sx = 0.0f, sy = 0.0f;
- uint16_t shortCtr = 0;
- for (size_t off = 0; off + 17 <= len; off += 17)
- {
- const eqlSelfPosRecord* r = (const eqlSelfPosRecord*)(data + off);
- float tx = r->y, ty = r->x, tz = r->z;
- if (!(tx > -100000.0f && tx < 100000.0f &&
- ty > -100000.0f && ty < 100000.0f &&
- tz > -10000.0f && tz < 10000.0f &&
- (tx != 0.0f || ty != 0.0f)))
- continue;
- uint16_t tctr = r->counter;
- uint16_t age = (uint16_t)(lastCtr - tctr);
- if (age >= 80 && age < 250)
- {
- sx = tx; sy = ty; shortCtr = tctr; haveShort = true;
- }
- }
- float shortSpd = -1.0f;
- if (haveShort)
- {
- float sdts = ((uint16_t)(lastCtr - shortCtr)) / 1000.0f;
- if (sdts > 0.02f)
- shortSpd = hypotf((fy - sy) / sdts, (fx - sx) / sdts);
- }
- bool stopping = (shortSpd >= 0.0f) &&
- (shortSpd < 0.35f * hypotf(vxm, vym));
-
- if (hypotf(vxm, vym) > 1.0f)
- {
- // setDeltas cooks delta * 0.0013 per msec, so delta = v / 1.3
- // glides the dot at true speed under AnimateSpawnMovement.
- // A stopping packet still updates heading but does not glide.
- if (stopping)
- setDeltas(0, 0, 0);
- else
- setDeltas(int16_t(lrintf(vxm / 1.3f)),
- int16_t(lrintf(vym / 1.3f)),
- int16_t(lrintf(vzm / 1.3f)));
- // Travel direction converted to EQ heading units (0-2047, CCW
- // from north), the same space live's self-update carries, so the
- // live compass/reticle formulas below apply unchanged
- // Travel-direction heading is the fallback only: the
- // self-state stream carries true facing at a higher rate, so
- // heading is derived from velocity only when that stream is
- // quiet.
- if (!selfStateFresh)
- {
- float eqdeg = atan2f(vxm, vym) * (180.0f / (float)M_PI);
- if (eqdeg < 0.0f)
- eqdeg += 360.0f;
- int h2048 = ((int)lrintf(eqdeg * 2048.0f / 360.0f)) & 0x7ff;
- setHeading(h2048, 0);
- m_headingDegrees = 360 - ((h2048 * 360) >> 11);
- }
- }
- else
- {
- setDeltas(0, 0, 0);
- }
-
- m_validPos = true;
- updateLast();
- emit headingChanged(m_headingDegrees);
- emit posChanged(x(), y(), z(), deltaX(), deltaY(), deltaZ(), m_headingDegrees);
- updateLastChanged();
- emit changeItem(this, tSpawnChangedPosition);
- return;
}
if ((dir != DIR_Client) && (pupdate->spawnId != id()))
@@ -1271,51 +1113,8 @@
if (dir != DIR_Client || len < 16)
return;
- // EQ Legends 2026-07-15: the real-time CLIENT self-state - a 38-byte
- // float body carrying the player's own spawnId @2, the freshest
- // position, AND the only client-side facing.
- // 2026-07-28 patch: the 0728 client rotated the field offsets within
- // the same 38-byte body (wire-verified from delivered bytes - @6/@34
- // tracked movement smoothly, @14 became a ~0 velocity delta, @18 went
- // to a constant 0, and @26 now carries the quiet-NaN facing):
- // float Z @10, float mapX @6, float mapY @34,
- // u32 heading @26 (facing in the low 21 bits; full circle 2^21, so
- // (&0x1FFFFF)>>10 = EQ h2048).
- // Old 0714 layout was mapX@14, heading@18, mapY@26.
- // This supersedes the 1.5-2s-stale %17 flush below for freshness
- // (m_selfStateAge gate). If the dot lands mirrored, swap @6/@34.
- if (len == 38)
- {
- const eqlClientSelfPosStruct *sp = (const eqlClientSelfPosStruct*)data;
- float zz = sp->z, mx = sp->x, my = sp->y;
- if ((mx != 0.0f || my != 0.0f) &&
- fabsf(mx) < 100000.0f && fabsf(my) < 100000.0f && fabsf(zz) < 10000.0f)
- {
- m_selfStateAge.start();
- setPos(int16_t(mx), int16_t(my), int16_t(zz),
- showeq_params->walkpathrecord, showeq_params->walkpathlength);
- setDeltas(0, 0, 0);
- m_validPos = true;
- updateLast();
- emit posChanged(x(), y(), z(), deltaX(), deltaY(), deltaZ(),
- m_headingDegrees);
- updateLastChanged();
- emit changeItem(this, tSpawnChangedPosition);
- }
- int h2048 = sp->heading;
- if (h2048 <= 2047)
- {
- setHeading(h2048, 0);
- m_headingDegrees = 360 - ((h2048 * 360) >> 11);
- emit headingChanged(m_headingDegrees);
- updateLastChanged();
- emit changeItem(this, tSpawnChangedPosition);
- }
- return;
- }
-
- // EQ Legends 2026-07-08: the float-leading variants (len % 17 == 1:
- // 18/35/52/69/86 and the 1700-3400B moving-state flushes) tile
+ // Float-leading variants (len % 17 == 1: 18/35/52/69/86 and the
+ // 1700-3400B moving-state flushes) tile
// 17-byte records from offset 0 (float map-Y, float map-X, float Z,
// flag u8, u16 ms counter, u16 seq). The LAST sane record is the
// freshest sample; reading only the first record was the source of
@@ -1326,7 +1125,6 @@
if (len % 17 == 1)
{
float wx = 0, wy = 0, wz = 0;
- uint16_t lastCtr = 0;
bool plausible = false;
for (size_t off = 0; off + 17 <= len; off += 17)
{
@@ -1340,7 +1138,6 @@
if (ok)
{
wy = ty; wx = tx; wz = tz;
- lastCtr = r->counter;
plausible = true;
}
}
@@ -1352,9 +1149,8 @@
m_selfStateAge.elapsed() < 2500;
if (plausible && !selfStateFresh)
{
- // Deltas stay zero by explicit decision (2026-07-08, second
- // rejection in game): dead-reckoned glide overshoots on stops
- // and does not match how Tom runs live. Pure stepping on the
+ // Deltas stay zero: dead-reckoned glide overshoots on stops and
+ // does not match live client behavior. Pure stepping on the
// freshest record.
// standing EQ Legends transposition: float0 is map Y, float1 is
// map X; assign accordingly.
Modified: showeq/branches/showeqlegends/src/s_everquest.h
===================================================================
--- showeq/branches/showeqlegends/src/s_everquest.h 2026-07-30 02:10:06 UTC (rev 1686)
+++ showeq/branches/showeqlegends/src/s_everquest.h 2026-07-30 15:26:50 UTC (rev 1687)
@@ -111,7 +111,6 @@
AddStruct(randomStruct);
AddStruct(playerSpawnPosStruct);
AddStruct(playerSelfPosStruct);
-AddStruct(eqlClientSelfPosStruct);
AddStruct(eqlClientUpdateSelfStruct);
AddStruct(eqlSelfPosRecord);
AddStruct(spawnAppearanceStruct);
@@ -131,6 +130,8 @@
AddStruct(serverLFGStruct);
AddStruct(clientLFGStruct);
AddStruct(buffStruct);
+AddStruct(buffChangeStruct);
+AddStruct(buffChangeEQLStruct);
AddStruct(GuildMemberUpdate);
AddStruct(bazaarTraderRequest);
AddStruct(bazaarSearchQueryStruct);
Modified: showeq/branches/showeqlegends/src/spellshell.cpp
===================================================================
--- showeq/branches/showeqlegends/src/spellshell.cpp 2026-07-30 02:10:06 UTC (rev 1686)
+++ showeq/branches/showeqlegends/src/spellshell.cpp 2026-07-30 15:26:50 UTC (rev 1687)
@@ -352,10 +352,20 @@
if (dir == DIR_Client || len < 16)
return;
- uint32_t buffSlot = *(const uint32_t*)data;
- uint32_t spellId = *(const uint32_t*)(data + 4);
- uint32_t changeType = (len >= 24) ? *(const uint32_t*)(data + 12)
- : *(const uint32_t*)(data + 8);
+ // Same handler serves both servers; changeType offset differs, so
+ // select the per-server struct by record length (layout lives in
+ // buffChangeStruct / buffChangeEQLStruct).
+ uint32_t buffSlot, spellId, changeType;
+ if (len >= 24) // Legends 24-byte record, changeType @12
+ {
+ const buffChangeEQLStruct* b = (const buffChangeEQLStruct*)data;
+ buffSlot = b->buffSlot; spellId = b->spellId; changeType = b->changeType;
+ }
+ else // live 16-byte record, changeType @8
+ {
+ const buffChangeStruct* b = (const buffChangeStruct*)data;
+ buffSlot = b->buffSlot; spellId = b->spellId; changeType = b->changeType;
+ }
// Real buff-window slots are 0-14; higher slots are scribes/bar refreshes.
if (buffSlot >= 15)
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <xer...@us...> - 2026-07-30 02:10:07
|
Revision: 1686
http://sourceforge.net/p/seq/svn/1686
Author: xerxes00
Date: 2026-07-30 02:10:06 +0000 (Thu, 30 Jul 2026)
Log Message:
-----------
EQ Legends: fix reticle freeze - heading is an 11-bit h2048 field
Modified Paths:
--------------
showeq/branches/showeqlegends/src/everquest.h
Modified: showeq/branches/showeqlegends/src/everquest.h
===================================================================
--- showeq/branches/showeqlegends/src/everquest.h 2026-07-30 00:29:19 UTC (rev 1685)
+++ showeq/branches/showeqlegends/src/everquest.h 2026-07-30 02:10:06 UTC (rev 1686)
@@ -2648,7 +2648,8 @@
/*0014*/ uint32_t unknown0014;
/*0018*/ float unknown0018;
/*0022*/ float x;
-/*0026*/ uint16_t heading;
+/*0026*/ uint16_t heading:11; // EQ h2048 facing (low 11 bits)
+ uint16_t unknown0026hi:5; // upper bits carry other state
/*0028*/ uint16_t unknown0028;
/*0030*/ float unknown0030;
/*0034*/ float z;
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <xer...@us...> - 2026-07-30 00:29:22
|
Revision: 1685
http://sourceforge.net/p/seq/svn/1685
Author: xerxes00
Date: 2026-07-30 00:29:19 +0000 (Thu, 30 Jul 2026)
Log Message:
-----------
EQ Legends 2026-07-29 patch: opcode rotation + position struct updates
- 0729 zone opcode rotation
- everquest.h: 0729 position / self-state / heading struct layouts
- player.cpp, spawnshell.cpp: read positions through the updated structs
- s_everquest.h: register the new structs
Modified Paths:
--------------
showeq/branches/showeqlegends/conf/zoneopcodes.xml
showeq/branches/showeqlegends/src/everquest.h
showeq/branches/showeqlegends/src/player.cpp
showeq/branches/showeqlegends/src/s_everquest.h
showeq/branches/showeqlegends/src/spawnshell.cpp
Modified: showeq/branches/showeqlegends/conf/zoneopcodes.xml
===================================================================
--- showeq/branches/showeqlegends/conf/zoneopcodes.xml 2026-07-29 18:42:09 UTC (rev 1684)
+++ showeq/branches/showeqlegends/conf/zoneopcodes.xml 2026-07-30 00:29:19 UTC (rev 1685)
@@ -16,97 +16,82 @@
-->
<!-- Critical opcodes used directly by ShowEQ -->
- <opcode id="50e5" name="OP_PlayerProfile" priority="10" updated="07/28/26">
- <comment>CharProfileCode</comment>
+ <opcode id="014a" name="OP_PlayerProfile" priority="10" updated="07/29/26">
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="5c7f" name="OP_ZoneEntry" priority="10" updated="07/28/26">
- <comment>ZoneEntryCode</comment>
+ <opcode id="5aaf" name="OP_ZoneEntry" priority="10" updated="07/29/26">
<payload dir="client" typename="ClientZoneEntryStruct" sizechecktype="match"/>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="7377" name="OP_TimeOfDay" priority="3" updated="07/28/26">
+ <opcode id="5920" name="OP_TimeOfDay" priority="3" updated="07/29/26">
<payload dir="server" typename="timeOfDayStruct" sizechecktype="match"/>
</opcode>
- <opcode id="47f2" name="OP_NewZone" priority="10" updated="07/28/26">
- <comment>NewZoneCode</comment>
+ <opcode id="0a2e" name="OP_NewZone" priority="10" updated="07/29/26">
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="3f1b" name="OP_SpawnDoor" priority="9" updated="07/28/26">
+ <opcode id="1e04" name="OP_SpawnDoor" priority="9" updated="07/29/26">
<payload dir="server" typename="doorStruct" sizechecktype="modulus"/>
</opcode>
- <opcode id="5331" name="OP_GroundSpawn" priority="9" updated="07/28/26">
- <comment>MakeDropCode</comment>
+ <opcode id="5784" name="OP_GroundSpawn" priority="9" updated="07/29/26">
<payload dir="server" typename="makeDropStruct" sizechecktype="none"/>
</opcode>
- <opcode id="0834" name="OP_SendZonePoints" priority="5" updated="07/28/26">
- <comment>Coords in a zone that will port you to another zone</comment>
+ <opcode id="3b84" name="OP_SendZonePoints" priority="5" updated="07/29/26">
<payload dir="server" typename="zonePointsStruct" sizechecktype="none"/>
</opcode>
- <opcode id="1d38" name="OP_AAExpUpdate" priority="4" updated="07/28/26">
+ <opcode id="3b21" name="OP_AAExpUpdate" priority="4" updated="07/29/26">
+ <comment>AA exp + unassigned points</comment>
<payload dir="server" typename="altExpUpdateStruct" sizechecktype="match"/>
</opcode>
- <opcode id="5abf" name="OP_ExpUpdate" priority="4" updated="07/28/26">
+ <opcode id="28d4" name="OP_ExpUpdate" priority="4" updated="07/29/26">
<payload dir="server" typename="expUpdateStruct" sizechecktype="match"/>
</opcode>
- <opcode id="70c5" name="OP_GuildMOTD" priority="2" updated="07/28/26">
- <comment>GuildMOTD</comment>
+ <opcode id="7e2a" name="OP_GuildMOTD" priority="2" updated="07/29/26">
<payload dir="server" typename="guildMOTDStruct" sizechecktype="none"/>
</opcode>
- <opcode id="5bfd" name="OP_ClientUpdate" priority="10" updated="07/28/26">
- <comment>Position updates</comment>
+ <opcode id="5380" name="OP_ClientUpdate" priority="10" updated="07/29/26">
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
<payload dir="client" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="26d8" name="OP_MobUpdate" priority="10" updated="07/28/26">
- <comment>MobUpdateCode</comment>
+ <opcode id="4eda" name="OP_MobUpdate" priority="10" updated="07/29/26">
<payload dir="both" typename="spawnPositionUpdate" sizechecktype="none"/>
</opcode>
- <opcode id="1856" name="OP_DeleteSpawn" priority="10" updated="07/28/26">
- <comment>DeleteSpawnCode</comment>
+ <opcode id="1f3d" name="OP_DeleteSpawn" priority="10" updated="07/29/26">
<payload dir="both" typename="deleteSpawnStruct" sizechecktype="match"/>
</opcode>
- <opcode id="27cb" name="OP_RemoveSpawn" priority="10" updated="07/28/26">
- <comment>Remove spawn from zone</comment>
+ <opcode id="0113" name="OP_RemoveSpawn" priority="10" updated="07/29/26">
<payload dir="both" typename="removeSpawnStruct" sizechecktype="none"/>
</opcode>
- <opcode id="4a78" name="OP_WearChange" priority="2" updated="07/28/26">
+ <opcode id="4155" name="OP_WearChange" priority="2" updated="07/29/26">
<priorityNote>stock handler no-op; id mapped for log id only</priorityNote>
- <comment>SpawnUpdateCode</comment>
<payload dir="both" typename="SpawnUpdateStruct" sizechecktype="match"/>
</opcode>
- <opcode id="384f" name="OP_SpawnAppearance" priority="10" updated="07/28/26">
- <comment>SpawnAppearanceCode</comment>
+ <opcode id="03f8" name="OP_SpawnAppearance" priority="10" updated="07/29/26">
<payload dir="both" typename="spawnEventEQLStruct" sizechecktype="match"/>
</opcode>
- <opcode id="17a1" name="OP_Stamina" priority="4" updated="07/28/26">
+ <opcode id="03ce" name="OP_Stamina" priority="4" updated="07/29/26">
+ <comment>food/water ticks</comment>
<payload dir="server" typename="staminaStruct" sizechecktype="match"/>
</opcode>
- <opcode id="3139" name="OP_HPUpdate" priority="10" updated="07/28/26">
- <comment>NpcHpUpdateCode Update HP % of a PC or NPC</comment>
+ <opcode id="50c2" name="OP_HPUpdate" priority="10" updated="07/29/26">
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="0aa2" name="OP_GuildMemberUpdate" priority="3" updated="07/28/26">
+ <opcode id="16d9" name="OP_GuildMemberUpdate" priority="3" updated="07/29/26">
<payload dir="server" typename="GuildMemberUpdate" sizechecktype="match"/>
</opcode>
- <opcode id="23ee" name="OP_ClickObject" priority="9" updated="07/28/26">
- <comment>Server removing a ground drop (pickup)</comment>
+ <opcode id="4ee1" name="OP_ClickObject" priority="9" updated="07/29/26">
<payload dir="server" typename="remDropStruct" sizechecktype="match"/>
</opcode>
- <opcode id="50e8" name="OP_Action" priority="7" updated="07/28/26">
- <comment>Spells cast etc</comment>
+ <opcode id="130a" name="OP_Action" priority="7" updated="07/29/26">
<payload dir="both" typename="actionStruct" sizechecktype="match"/>
<payload dir="both" typename="actionAltStruct" sizechecktype="match"/>
</opcode>
- <opcode id="7df9" name="OP_Action2" priority="7" updated="07/28/26">
- <comment>Combat actions i.e. bash, kick etc</comment>
+ <opcode id="0ed3" name="OP_Action2" priority="7" updated="07/29/26">
<payload dir="both" typename="action2Struct" sizechecktype="match"/>
</opcode>
- <opcode id="64c4" name="OP_Consider" priority="9" updated="07/28/26">
- <comment>Consider request/response</comment>
+ <opcode id="53f1" name="OP_Consider" priority="9" updated="07/29/26">
<payload dir="both" typename="considerStruct" sizechecktype="match"/>
</opcode>
- <opcode id="3897" name="OP_TargetMouse" priority="9" updated="07/28/26">
+ <opcode id="784e" name="OP_TargetMouse" priority="9" updated="07/29/26">
<comment>Targeting a spawn via mouse</comment>
<payload dir="both" typename="clientTargetStruct" sizechecktype="match"/>
</opcode>
@@ -114,93 +99,78 @@
<comment>Spawns getting renamed after initial NewSpawn</comment>
<payload dir="server" typename="spawnRenameStruct" sizechecktype="match"/>
</opcode>
- <opcode id="5201" name="OP_Illusion" priority="8" updated="07/28/26">
- <comment>Spawn being illusioned (changing forms)</comment>
+ <opcode id="4037" name="OP_Illusion" priority="8" updated="07/29/26">
<payload dir="both" typename="spawnIllusionStruct" sizechecktype="match"/>
</opcode>
- <opcode id="3360" name="OP_ZoneChange" priority="10" updated="07/28/26">
+ <opcode id="07b6" name="OP_ZoneChange" priority="10" updated="07/29/26">
<priorityNote>server never sends it; handler only sets m_zoning</priorityNote>
- <comment>old ZoneChangeCode</comment>
<payload dir="both" typename="zoneChangeEQLStruct" sizechecktype="match"/>
</opcode>
- <opcode id="63c6" name="OP_GroupInvite" priority="4" updated="07/28/26">
- <comment>You invite someone while ungrouped or get invited by someone ungrouped</comment>
+ <opcode id="0cb5" name="OP_GroupInvite" priority="4" updated="07/29/26">
<payload dir="both" typename="groupInviteStruct" sizechecktype="match"/>
</opcode>
- <opcode id="7a77" name="OP_GroupCancelInvite" priority="4" updated="07/28/26">
+ <opcode id="70a8" name="OP_GroupCancelInvite" priority="4" updated="07/29/26">
<priorityNote>single sample; reason byte unverified</priorityNote>
- <comment>Declining to join a group</comment>
<payload dir="both" typename="groupDeclineStruct" sizechecktype="none"/>
</opcode>
- <opcode id="4e40" name="OP_GroupFollow" priority="4" updated="07/28/26">
+ <opcode id="6711" name="OP_GroupFollow" priority="4" updated="07/29/26">
<payload dir="server" typename="groupFollowStruct" sizechecktype="match"/>
</opcode>
- <opcode id="68d3" name="OP_GroupUpdate" priority="6" updated="07/28/26">
+ <opcode id="09ec" name="OP_GroupUpdate" priority="6" updated="07/29/26">
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="216d" name="OP_GroupDisband" priority="4" updated="07/28/26">
+ <opcode id="587b" name="OP_GroupDisband" priority="4" updated="07/29/26">
<payload dir="server" typename="groupDisbandStruct" sizechecktype="match"/>
</opcode>
- <opcode id="18e2" name="OP_GroupLeader" priority="4" updated="07/28/26">
+ <opcode id="5767" name="OP_GroupLeader" priority="4" updated="07/29/26">
<priorityNote>single sample; unconfirmed on /makeleader</priorityNote>
<payload dir="server" typename="groupLeaderChangeStruct" sizechecktype="none"/>
</opcode>
- <opcode id="122f" name="OP_Buff" priority="6" updated="07/28/26">
+ <opcode id="272c" name="OP_Buff" priority="6" updated="07/29/26">
<priorityNote>client dir = memorize/forget, consumed silently</priorityNote>
- <comment>old BuffDropCode</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
<payload dir="client" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="2c36" name="OP_BeginCast" priority="5" updated="07/28/26">
+ <opcode id="4970" name="OP_BeginCast" priority="5" updated="07/29/26">
<payload dir="both" typename="beginCastStruct" sizechecktype="none"/>
</opcode>
- <opcode id="155e" name="OP_CastSpell" priority="5" updated="07/28/26">
+ <opcode id="224f" name="OP_CastSpell" priority="5" updated="07/29/26">
<payload dir="both" typename="startCastStruct" sizechecktype="match"/>
</opcode>
- <opcode id="4459" name="OP_SwapSpell" priority="3" updated="07/28/26">
- <comment>TradeSpellBookSlotsCode</comment>
+ <opcode id="0386" name="OP_SwapSpell" priority="3" updated="07/29/26">
<payload dir="both" typename="tradeSpellBookSlotsStruct" sizechecktype="match"/>
</opcode>
- <opcode id="5503" name="OP_InspectAnswer" priority="3" updated="07/28/26">
- <comment>InspectDataCode</comment>
+ <opcode id="104d" name="OP_InspectAnswer" priority="3" updated="07/29/26">
<payload dir="server" typename="inspectDataStruct" sizechecktype="match"/>
</opcode>
- <opcode id="6d34" name="OP_Emote" priority="4" updated="07/28/26">
- <comment>EmoteTextCode</comment>
+ <opcode id="185c" name="OP_Emote" priority="4" updated="07/29/26">
<payload dir="both" typename="emoteTextStruct" sizechecktype="none"/>
</opcode>
- <opcode id="72cb" name="OP_SimpleMessage" priority="4" updated="07/28/26">
- <comment>SimpleMessageCode</comment>
+ <opcode id="0c5f" name="OP_SimpleMessage" priority="4" updated="07/29/26">
<payload dir="server" typename="simpleMessageStruct" sizechecktype="match"/>
</opcode>
- <opcode id="06f1" name="OP_FormattedMessage" priority="4" updated="07/28/26">
- <comment>FormattedMessageCode i.e. pet dismissed etc</comment>
+ <opcode id="7445" name="OP_FormattedMessage" priority="4" updated="07/29/26">
<payload dir="server" typename="formattedMessageStruct" sizechecktype="none"/>
</opcode>
- <opcode id="37fa" name="OP_CommonMessage" priority="9" updated="07/28/26">
- <comment>ChannelMessageCode i.e. /tell /ooc /shout etc</comment>
+ <opcode id="419f" name="OP_CommonMessage" priority="9" updated="07/29/26">
<payload dir="both" typename="channelMessageStruct" sizechecktype="none"/>
</opcode>
- <opcode id="4e60" name="OP_SpecialMesg" priority="4" updated="07/28/26">
- <comment>Communicate textual info to client including hail responses etc</comment>
+ <opcode id="6ee3" name="OP_SpecialMesg" priority="4" updated="07/29/26">
<payload dir="server" typename="specialMessageStruct" sizechecktype="none"/>
</opcode>
- <opcode id="05b1" name="OP_RandomReq" priority="2" updated="07/28/26">
+ <opcode id="612d" name="OP_RandomReq" priority="2" updated="07/29/26">
<payload dir="client" typename="randomReqStruct" sizechecktype="none"/>
</opcode>
- <opcode id="79a0" name="OP_RandomReply" priority="2" updated="07/28/26">
+ <opcode id="5d02" name="OP_RandomReply" priority="2" updated="07/29/26">
<payload dir="server" typename="randomStruct" sizechecktype="match"/>
</opcode>
- <opcode id="492d" name="OP_ConsentResponse" priority="3" updated="07/28/26">
- <comment>Server replying with consent information after /consent</comment>
+ <opcode id="3ed1" name="OP_ConsentResponse" priority="3" updated="07/29/26">
<payload dir="server" typename="consentResponseStruct" sizechecktype="match"/>
</opcode>
- <opcode id="3991" name="OP_DenyResponse" priority="3" updated="07/28/26">
- <comment>Server replying with deny information after /deny</comment>
+ <opcode id="3a86" name="OP_DenyResponse" priority="3" updated="07/29/26">
<payload dir="server" typename="consentResponseStruct" sizechecktype="match"/>
</opcode>
- <opcode id="2b95" name="OP_ManaChange" priority="5" updated="07/28/26">
- <comment>Mana change. Bards send this up with no size. Casters receive this for mana updates.</comment>
+ <opcode id="3b08" name="OP_ManaChange" priority="5" updated="07/29/26">
<payload dir="server" typename="manaDecrementStruct" sizechecktype="match"/>
</opcode>
<opcode id="ffff" name="OP_MoneyOnCorpse" priority="4" updated="07/19/23">
@@ -207,14 +177,12 @@
<comment>MoneyOnCorpseCode</comment>
<payload dir="server" typename="moneyOnCorpseStruct" sizechecktype="match"/>
</opcode>
- <opcode id="0462" name="OP_SkillUpdate" priority="5" updated="07/28/26">
+ <opcode id="1ee1" name="OP_SkillUpdate" priority="5" updated="07/29/26">
<priorityNote>prior id 0dc7 was the zone-in bulk dump (OOB crash); bounds guard kept</priorityNote>
- <comment>Skill up code</comment>
<payload dir="server" typename="skillIncStruct" sizechecktype="match"/>
</opcode>
- <opcode id="70c6" name="OP_LevelUpdate" priority="8" updated="07/28/26">
+ <opcode id="485a" name="OP_LevelUpdate" priority="8" updated="07/29/26">
<priorityNote>single sample</priorityNote>
- <comment>LevelUpUpdateCode</comment>
<payload dir="server" typename="levelUpUpdateStruct" sizechecktype="none"/>
</opcode>
<opcode id="ffff" name="OP_CorpseLocResponse" priority="2" updated="12/12/12">
@@ -223,37 +191,31 @@
</opcode>
<!-- Not necessary for SEQ to run but here to name packets in logs. -->
- <opcode id="1160" name="OP_Logout" priority="3" updated="07/28/26">
+ <opcode id="30ce" name="OP_Logout" priority="3" updated="07/29/26">
<priorityNote>handler wired DIR_Client only</priorityNote>
- <comment>Camp/logout handshake</comment>
<payload dir="both" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="652b" name="OP_Animation" priority="0" updated="07/28/26">
+ <opcode id="486e" name="OP_Animation" priority="0" updated="07/29/26">
<priorityNote>noHandler</priorityNote>
- <comment>spawnId + action + value; fires per spawn animation</comment>
<payload dir="both" typename="spawnAnimationStruct" sizechecktype="none"/>
</opcode>
- <opcode id="3fb1" name="OP_InspectRequest" priority="0" updated="07/28/26">
+ <opcode id="6426" name="OP_InspectRequest" priority="0" updated="07/29/26">
<priorityNote>noHandler</priorityNote>
<payload dir="client" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="7199" name="OP_LootMessageEQL" priority="4" updated="07/28/26">
- <comment>Loot-and-sell message</comment>
+ <opcode id="482e" name="OP_LootMessageEQL" priority="4" updated="07/29/26">
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="03c3" name="OP_MoneyUpdateEQL" priority="4" updated="07/28/26">
- <comment>Running money total [plat,gold,silver,copper]</comment>
+ <opcode id="64a2" name="OP_MoneyUpdateEQL" priority="4" updated="07/29/26">
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="157b" name="OP_GuildsInZoneList" priority="8" updated="07/28/26">
- <comment>Listing of guild names present in the current zone. Generally seen on zoning</comment>
+ <opcode id="6540" name="OP_GuildsInZoneList" priority="8" updated="07/29/26">
<payload dir="server" typename="guildsInZoneListStruct" sizechecktype="none"/>
</opcode>
- <opcode id="63a5" name="OP_NewGuildInZone" priority="8" updated="07/28/26">
- <comment>An additional guild name has appeared in the current zone. Seen when a guilded player enters the current zone</comment>
+ <opcode id="4c75" name="OP_NewGuildInZone" priority="8" updated="07/29/26">
<payload dir="server" typename="newGuildInZoneStruct" sizechecktype="none"/>
</opcode>
- <opcode id="767d" name="OP_Find" priority="0" updated="07/28/26">
+ <opcode id="0ad1" name="OP_Find" priority="0" updated="07/29/26">
<priorityNote>noHandler</priorityNote>
<payload dir="client" typename="uint8_t" sizechecktype="none"/>
</opcode>
@@ -262,49 +224,41 @@
Using these marks unknown opcodes in logs for easier reading -->
<!-- Below are unverified -->
- <opcode id="2a24" name="OP_SendAATable" priority="5" updated="07/28/26">
- <comment>AA definition table</comment>
+ <opcode id="43f1" name="OP_SendAATable" priority="5" updated="07/29/26">
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="6ec9" name="OP_HideCorpse" priority="0" updated="07/28/26">
+ <opcode id="1368" name="OP_HideCorpse" priority="0" updated="07/29/26">
<priorityNote>noHandler</priorityNote>
<payload dir="client" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="6323" name="OP_SelfPosEQL" priority="10" updated="07/28/26">
- <comment>Client self-position</comment>
+ <opcode id="16ac" name="OP_SelfPosEQL" priority="10" updated="07/29/26">
<payload dir="client" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="3e7b" name="OP_NpcMoveUpdate" priority="10" updated="07/28/26">
- <comment>Position updates</comment>
+ <opcode id="2f15" name="OP_NpcMoveUpdate" priority="10" updated="07/29/26">
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="2087" name="OP_Death" priority="7" updated="07/28/26">
+ <opcode id="6737" name="OP_Death" priority="7" updated="07/29/26">
<priorityNote>also fires on corpse decay</priorityNote>
- <comment>old NewCorpseCode</comment>
<payload dir="server" typename="newCorpseStruct" sizechecktype="match"/>
</opcode>
- <opcode id="58ab" name="OP_DeathEQL" priority="3" updated="07/28/26">
- <comment>Death/combat subcode channel</comment>
+ <opcode id="35f6" name="OP_DeathEQL" priority="3" updated="07/29/26">
<payload dir="both" typename="deathRecordEQLStruct" sizechecktype="none"/>
</opcode>
- <opcode id="6e10" name="OP_GuildMemberList" priority="3" updated="07/28/26">
+ <opcode id="56ef" name="OP_GuildMemberList" priority="3" updated="07/29/26">
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="7bda" name="OP_BuffList" priority="6" updated="07/28/26">
- <comment>Per-spawn buff list</comment>
+ <opcode id="7e11" name="OP_BuffList" priority="6" updated="07/29/26">
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="4c71" name="OP_LoadoutSwapEQL" priority="3" updated="07/28/26">
- <comment>Loadout-swap self refresh</comment>
+ <opcode id="8758" name="OP_LoadoutSwapEQL" priority="3" updated="07/29/26">
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="2a6f" name="OP_LootDrops" priority="4" updated="07/28/26">
+ <opcode id="3989" name="OP_LootDrops" priority="4" updated="07/29/26">
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="2216" name="OP_DzSwitchInfoEQL" priority="3" updated="07/28/26">
+ <opcode id="01fb" name="OP_DzSwitchInfoEQL" priority="3" updated="07/29/26">
<priorityNote>wired but untested on Legends</priorityNote>
- <comment>Dynamic-zone switch info</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
@@ -1213,11 +1167,11 @@
<comment>SpellFadeCode</comment>
<payload dir="both" typename="spellFadedStruct" sizechecktype="none"/>
</opcode>
- <opcode id="0b49" name="OP_Stance" priority="0" updated="07/28/26">
+ <opcode id="2865" name="OP_Stance" priority="0" updated="07/29/26">
<priorityNote>noHandler</priorityNote>
<payload dir="both" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="2ae5" name="OP_Invocation" priority="0" updated="07/28/26">
+ <opcode id="6d48" name="OP_Invocation" priority="0" updated="07/29/26">
<priorityNote>noHandler</priorityNote>
<payload dir="both" typename="uint8_t" sizechecktype="none"/>
</opcode>
Modified: showeq/branches/showeqlegends/src/everquest.h
===================================================================
--- showeq/branches/showeqlegends/src/everquest.h 2026-07-29 18:42:09 UTC (rev 1684)
+++ showeq/branches/showeqlegends/src/everquest.h 2026-07-30 00:29:19 UTC (rev 1685)
@@ -1161,31 +1161,23 @@
struct
{
- // 2026-07-07 EQL layout: z, x, y are the low 19 bits of
- // three consecutive words, world value = field >> 3 (live
- // 19-bit convention). x/y keep the EQ Legends transposed
- // naming so setPos receives the same world axes as before.
- // The high 13 bits of each coord word are presumed deltas
- // but are unvalidated; heading/animation/pitch positions
- // in words 3-4 are unmapped (0x6000/0 observed) - all are
- // zeroed at consumption in spawn.cpp until wire-mapped.
- // EQ Legends 2026-07-28 posData layout (spawnshell skips the
- // one leading junk word, then reads these 5). word0 and
- // word3 carry the two horizontal coords; assigning y to
- // word0 and x to word3 rotates the dots to align with the
- // zone map (the reverse assignment came out 90 deg off).
- signed y:19; // y coord (EQ Legends x/y transposed) - word0
- signed deltaY:13; // presumed change in y (unvalidated)
+ // EQ Legends 2026-07-29 posData: low 19 bits of three
+ // consecutive words, field >> 3. Field names keep the EQL
+ // transposed convention consumed by setPos in spawn.cpp.
+ // High 13 bits presumed deltas (unvalidated); words 3-4
+ // unmapped and zeroed at consumption.
+ signed z:19;
+ signed deltaZ:13;
- signed z:19; // z coord (loc value 1) - word1
- signed deltaZ:13; // presumed change in z (unvalidated)
+ signed y:19;
+ signed deltaY:13;
- signed padCoord:32; // gap word (0 observed) - word2
+ signed x:19;
+ signed deltaX:13;
- signed x:19; // x coord (EQ Legends x/y transposed) - word3
- signed deltaX:13; // presumed change in x (unvalidated)
+ signed padCoord:32;
- unsigned heading:12; // word4 (unmapped/zeroed)
+ unsigned heading:12;
unsigned padding00:20;
};
@@ -1599,15 +1591,16 @@
struct playerPosUpdateEQLStruct
{
/*0000*/ uint32_t spawnId;
-/*0004*/ uint32_t unknown0004;
-/*0008*/ signed x:19; // map-X coord, >>3 to Spawn units (bit64)
+/*0004*/ signed z:19; // >>3 - EQL 0729 layout
+ signed deltaZ:13;
+/*0008*/ signed x:19; // >>3
signed deltaX:13;
-/*0012*/ signed z:19; // z coord, >>3 (bit96)
- signed deltaZ:13;
-/*0016*/ uint32_t unknown0016; // heading/animation region
-/*0020*/ signed y:19; // map-Y coord, >>3 (bit160)
+/*0012*/ signed y:19; // >>3
signed deltaY:13;
-/*0024*/
+/*0016*/ uint32_t unknown0016;
+/*0020*/ uint32_t unknown0020;
+/*0024*/ uint32_t unknown0024;
+/*0028*/
};
/*
@@ -2643,6 +2636,40 @@
/*0038*/
};
+// EQ Legends 2026-07-29 client self-state on the c2s OP_ClientUpdate
+// 42-byte record - freshest self position + facing (EQ h2048).
+// A 44-byte variant exists; trailing bytes are tolerated.
+struct eqlClientUpdateSelfStruct
+{
+/*0000*/ uint32_t seq;
+/*0004*/ uint16_t unknown0004;
+/*0006*/ float unknown0006;
+/*0010*/ float y;
+/*0014*/ uint32_t unknown0014;
+/*0018*/ float unknown0018;
+/*0022*/ float x;
+/*0026*/ uint16_t heading;
+/*0028*/ uint16_t unknown0028;
+/*0030*/ float unknown0030;
+/*0034*/ float z;
+/*0038*/ uint8_t tail[4];
+/*0042*/
+};
+
+// EQ Legends c2s self-position sample - 17-byte records tiled from
+// offset 0 in the OP_SelfPosEQL stream. The freshest plausible record
+// is the current position.
+struct eqlSelfPosRecord
+{
+/*0000*/ float y;
+/*0004*/ float x;
+/*0008*/ float z;
+/*0012*/ uint8_t flag;
+/*0013*/ uint16_t counter;
+/*0015*/ uint16_t seq;
+/*0017*/
+};
+
/*
** Spawn Appearance
** Length: 8 Octets
Modified: showeq/branches/showeqlegends/src/player.cpp
===================================================================
--- showeq/branches/showeqlegends/src/player.cpp 2026-07-29 18:42:09 UTC (rev 1684)
+++ showeq/branches/showeqlegends/src/player.cpp 2026-07-30 00:29:19 UTC (rev 1685)
@@ -967,10 +967,8 @@
uint16_t lastCtr = 0;
for (size_t off = 0; off + 17 <= len; off += 17)
{
- float tx, ty, tz;
- memcpy(&tx, data + off, 4);
- memcpy(&ty, data + off + 4, 4);
- memcpy(&tz, data + off + 8, 4);
+ const eqlSelfPosRecord* r = (const eqlSelfPosRecord*)(data + off);
+ float tx = r->y, ty = r->x, tz = r->z;
if (tx > -100000.0f && tx < 100000.0f &&
ty > -100000.0f && ty < 100000.0f &&
tz > -10000.0f && tz < 10000.0f &&
@@ -977,7 +975,7 @@
(tx != 0.0f || ty != 0.0f))
{
fx = tx; fy = ty; fz = tz; found = true;
- memcpy(&lastCtr, data + off + 13, 2);
+ lastCtr = r->counter;
}
}
if (!found)
@@ -992,17 +990,14 @@
uint16_t backCtr = 0;
for (size_t off = 0; off + 17 <= len; off += 17)
{
- float tx, ty, tz;
- memcpy(&tx, data + off, 4);
- memcpy(&ty, data + off + 4, 4);
- memcpy(&tz, data + off + 8, 4);
+ const eqlSelfPosRecord* r = (const eqlSelfPosRecord*)(data + off);
+ float tx = r->y, ty = r->x, tz = r->z;
if (!(tx > -100000.0f && tx < 100000.0f &&
ty > -100000.0f && ty < 100000.0f &&
tz > -10000.0f && tz < 10000.0f &&
(tx != 0.0f || ty != 0.0f)))
continue;
- uint16_t tctr;
- memcpy(&tctr, data + off + 13, 2);
+ uint16_t tctr = r->counter;
uint16_t age = (uint16_t)(lastCtr - tctr);
if (age >= 250 && age < 5000)
{
@@ -1049,17 +1044,14 @@
uint16_t shortCtr = 0;
for (size_t off = 0; off + 17 <= len; off += 17)
{
- float tx, ty, tz;
- memcpy(&tx, data + off, 4);
- memcpy(&ty, data + off + 4, 4);
- memcpy(&tz, data + off + 8, 4);
+ const eqlSelfPosRecord* r = (const eqlSelfPosRecord*)(data + off);
+ float tx = r->y, ty = r->x, tz = r->z;
if (!(tx > -100000.0f && tx < 100000.0f &&
ty > -100000.0f && ty < 100000.0f &&
tz > -10000.0f && tz < 10000.0f &&
(tx != 0.0f || ty != 0.0f)))
continue;
- uint16_t tctr;
- memcpy(&tctr, data + off + 13, 2);
+ uint16_t tctr = r->counter;
uint16_t age = (uint16_t)(lastCtr - tctr);
if (age >= 80 && age < 250)
{
@@ -1338,10 +1330,8 @@
bool plausible = false;
for (size_t off = 0; off + 17 <= len; off += 17)
{
- float ty, tx, tz;
- memcpy(&ty, data + off, 4);
- memcpy(&tx, data + off + 4, 4);
- memcpy(&tz, data + off + 8, 4);
+ const eqlSelfPosRecord* r = (const eqlSelfPosRecord*)(data + off);
+ float ty = r->y, tx = r->x, tz = r->z;
bool ok =
(tx == 0.0f || (fabsf(tx) >= 0.001f && fabsf(tx) < 100000.0f)) &&
(ty == 0.0f || (fabsf(ty) >= 0.001f && fabsf(ty) < 100000.0f)) &&
@@ -1350,7 +1340,7 @@
if (ok)
{
wy = ty; wx = tx; wz = tz;
- memcpy(&lastCtr, data + off + 13, 2);
+ lastCtr = r->counter;
plausible = true;
}
}
@@ -1384,27 +1374,16 @@
return;
}
- // The 42-byte self-state (client dir; live analog of the
- // pre-patch 0x0b03 42B struct). Layout, wire-validated 2026-07-08
- // against a controlled facing test and a tracked run:
- // u32 seq @0, u16 zero @4, float @6 (turn-rate-like, unconsumed),
- // float map-Y @10, u32 heading @14 in EQ h2048 units (0 = north,
- // counterclockwise, wraps at 2048 - the stock convention),
- // float map-X @18, u8 moving-flag @22 (0x18 moving, 0 at rest),
- // float @26 (velocity-like, unconsumed), float Z @30, tail @34.
- // Position here is the freshest on the wire (~0.3s, 1 Hz standing,
- // faster while turning); the record flushes above are 1.5-2s stale
- // and act as backfill only. NOTE: this packet was silently dropped
- // until the OP_ClientUpdate connect2/XML payload mismatch was fixed
- // (2026-07-08) - that outage is why its fields were long believed
- // frozen. A 44B variant exists; trailing bytes are tolerated.
+ // The 42-byte client self-state (live analog of the pre-patch 0x0b03
+ // 42B struct); field layout lives in eqlClientUpdateSelfStruct. Position
+ // here is the freshest on the wire; the record flushes above are staler
+ // backfill. A 44B variant exists; trailing bytes are tolerated.
if (len < 42 || len > 46)
return;
- float wy2, wx2, wz2;
- memcpy(&wy2, data + 10, 4);
- memcpy(&wx2, data + 18, 4);
- memcpy(&wz2, data + 30, 4);
+ const eqlClientUpdateSelfStruct* ss =
+ (const eqlClientUpdateSelfStruct*)data;
+ float wy2 = ss->y, wx2 = ss->x, wz2 = ss->z;
bool posOk =
(fabsf(wx2) >= 0.001f && fabsf(wx2) < 100000.0f) &&
(fabsf(wy2) >= 0.001f && fabsf(wy2) < 100000.0f) &&
@@ -1429,8 +1408,7 @@
}
}
- uint32_t hword;
- memcpy(&hword, data + 14, 4);
+ uint32_t hword = ss->heading;
if (hword > 2047)
return;
Modified: showeq/branches/showeqlegends/src/s_everquest.h
===================================================================
--- showeq/branches/showeqlegends/src/s_everquest.h 2026-07-29 18:42:09 UTC (rev 1684)
+++ showeq/branches/showeqlegends/src/s_everquest.h 2026-07-30 00:29:19 UTC (rev 1685)
@@ -112,6 +112,8 @@
AddStruct(playerSpawnPosStruct);
AddStruct(playerSelfPosStruct);
AddStruct(eqlClientSelfPosStruct);
+AddStruct(eqlClientUpdateSelfStruct);
+AddStruct(eqlSelfPosRecord);
AddStruct(spawnAppearanceStruct);
AddStruct(bindWoundStruct);
AddStruct(inspectedStruct);
Modified: showeq/branches/showeqlegends/src/spawnshell.cpp
===================================================================
--- showeq/branches/showeqlegends/src/spawnshell.cpp 2026-07-29 18:42:09 UTC (rev 1684)
+++ showeq/branches/showeqlegends/src/spawnshell.cpp 2026-07-30 00:29:19 UTC (rev 1685)
@@ -823,13 +823,8 @@
spawn->equipment[8].equip0 = netStream.readUInt32NC();
}
- // 2026-07-07 EQL patch inserted 8 bytes here, between the equipment
- // block and the position words (content unidentified - possibly the
- // relocated heading/animation fields; word values 0x6000/0 observed).
- // One leading junk word precedes the coordinate block; skip it, then
- // read the 5-word posData block (X, Z, pad, Y, heading) whose layout
- // lives in spawnStruct. Byte count matches the prior skip8+read4.
- netStream.skipBytes(4);
+ // EQ Legends: posData block starts immediately after the equipment
+ // block. The 5-word block's layout lives in spawnStruct.
for (int i = 0; i < 5; ++i)
spawn->posData[i] = netStream.readUInt32NC();
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <xer...@us...> - 2026-07-29 18:42:12
|
Revision: 1684
http://sourceforge.net/p/seq/svn/1684
Author: xerxes00
Date: 2026-07-29 18:42:09 +0000 (Wed, 29 Jul 2026)
Log Message:
-----------
Fix con color: yellow band is +1..+5, red at +6
Verified against the client's runtime 131x131 con table: yellow spans
5 levels above the player. Widen the yellow band in fillConTable so
spawn-list/map con colors match the game. The lower bands
(grey/green/cyan/blue) already matched the client table.
Modified Paths:
--------------
showeq/branches/xerxes-trunk-dev/src/player.cpp
Modified: showeq/branches/xerxes-trunk-dev/src/player.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/player.cpp 2026-07-29 18:42:07 UTC (rev 1683)
+++ showeq/branches/xerxes-trunk-dev/src/player.cpp 2026-07-29 18:42:09 UTC (rev 1684)
@@ -1278,8 +1278,8 @@
// Even is our level, natch.
m_conTable[spawnLevel++] = m_conColorBases[tEvenSpawn];
- // 3 levels of yellow.
- for (; spawnLevel < level() + 4; spawnLevel++)
+ // 5 levels of yellow.
+ for (; spawnLevel < level() + 6; spawnLevel++)
{
m_conTable[spawnLevel] = m_conColorBases[tYellowSpawn];
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <xer...@us...> - 2026-07-29 18:42:08
|
Revision: 1683
http://sourceforge.net/p/seq/svn/1683
Author: xerxes00
Date: 2026-07-29 18:42:07 +0000 (Wed, 29 Jul 2026)
Log Message:
-----------
Fix con color: yellow band is +1..+5, red at +6
Verified against the client's runtime 131x131 con table: yellow spans
5 levels above the player. Widen the yellow band in fillConTable so
spawn-list/map con colors match the game. The lower bands
(grey/green/cyan/blue) already matched the client table.
Modified Paths:
--------------
showeq/branches/showeqlegends/src/player.cpp
Modified: showeq/branches/showeqlegends/src/player.cpp
===================================================================
--- showeq/branches/showeqlegends/src/player.cpp 2026-07-29 02:38:41 UTC (rev 1682)
+++ showeq/branches/showeqlegends/src/player.cpp 2026-07-29 18:42:07 UTC (rev 1683)
@@ -1785,8 +1785,8 @@
// Even is our level, natch.
m_conTable[spawnLevel++] = m_conColorBases[tEvenSpawn];
- // 3 levels of yellow.
- for (; spawnLevel < level() + 4; spawnLevel++)
+ // 5 levels of yellow.
+ for (; spawnLevel < level() + 6; spawnLevel++)
{
m_conTable[spawnLevel] = m_conColorBases[tYellowSpawn];
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <xer...@us...> - 2026-07-29 02:38:42
|
Revision: 1682
http://sourceforge.net/p/seq/svn/1682
Author: xerxes00
Date: 2026-07-29 02:38:41 +0000 (Wed, 29 Jul 2026)
Log Message:
-----------
EQ Legends 2026-07-28 patch: opcodes updated + fixes
Opcodes: remapped the zone opcode set for the Legends 2026-07-28 patch.
Code:
- position/heading/self-state decode via structs in everquest.h
(eqlClientSelfPosStruct, playerPosUpdateEQLStruct, spawnStruct posData)
- spell-id widened uint16 -> uint32 end to end; high-id spells no longer alias
- spell window shows only own buffs + buffs the player cast (per-record
caster filter)
- loadout-swap re-creates a deleted spawn instead of dropping it (fixes
spawns stuck as Unknown)
- statlist: guard food/water percent against zero max (SIGFPE fix)
- moved inline EQL packet layouts into everquest.h structs
(moneyUpdateEQLStruct, dzSwitchInfoEQLStruct, spawnEventEQLStruct)
- aalist: QT_VERSION guard for setSectionResizeMode (Qt4 build) - thanks kkmonte
Modified Paths:
--------------
showeq/branches/showeqlegends/conf/zoneopcodes.xml
showeq/branches/showeqlegends/src/aalist.cpp
showeq/branches/showeqlegends/src/everquest.h
showeq/branches/showeqlegends/src/group.cpp
showeq/branches/showeqlegends/src/guildshell.cpp
showeq/branches/showeqlegends/src/interface.cpp
showeq/branches/showeqlegends/src/message.h
showeq/branches/showeqlegends/src/messageshell.cpp
showeq/branches/showeqlegends/src/packetformat.cpp
showeq/branches/showeqlegends/src/player.cpp
showeq/branches/showeqlegends/src/s_everquest.h
showeq/branches/showeqlegends/src/spawnshell.cpp
showeq/branches/showeqlegends/src/spells.cpp
showeq/branches/showeqlegends/src/spells.h
showeq/branches/showeqlegends/src/spellshell.cpp
showeq/branches/showeqlegends/src/spellshell.h
showeq/branches/showeqlegends/src/statlist.cpp
showeq/branches/showeqlegends/src/util.cpp
showeq/branches/showeqlegends/src/util.h
showeq/branches/showeqlegends/src/zonemgr.cpp
Modified: showeq/branches/showeqlegends/conf/zoneopcodes.xml
===================================================================
--- showeq/branches/showeqlegends/conf/zoneopcodes.xml 2026-07-24 20:43:30 UTC (rev 1681)
+++ showeq/branches/showeqlegends/conf/zoneopcodes.xml 2026-07-29 02:38:41 UTC (rev 1682)
@@ -16,104 +16,98 @@
-->
<!-- Critical opcodes used directly by ShowEQ -->
- <opcode id="6a88" name="OP_PlayerProfile" priority="10" updated="07/14/26">
+ <opcode id="50e5" name="OP_PlayerProfile" priority="10" updated="07/28/26">
<comment>CharProfileCode</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="144f" name="OP_ZoneEntry" priority="10" updated="07/14/26">
+ <opcode id="5c7f" name="OP_ZoneEntry" priority="10" updated="07/28/26">
<comment>ZoneEntryCode</comment>
<payload dir="client" typename="ClientZoneEntryStruct" sizechecktype="match"/>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="0b7f" name="OP_TimeOfDay" priority="3" updated="07/14/26">
- <comment>TimeOfDayCode</comment>
+ <opcode id="7377" name="OP_TimeOfDay" priority="3" updated="07/28/26">
<payload dir="server" typename="timeOfDayStruct" sizechecktype="match"/>
</opcode>
- <opcode id="65dd" name="OP_NewZone" priority="10" updated="07/14/26">
+ <opcode id="47f2" name="OP_NewZone" priority="10" updated="07/28/26">
<comment>NewZoneCode</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="75dd" name="OP_SpawnDoor" priority="9" updated="07/14/26">
- <comment>DoorSpawnsCode</comment>
+ <opcode id="3f1b" name="OP_SpawnDoor" priority="9" updated="07/28/26">
<payload dir="server" typename="doorStruct" sizechecktype="modulus"/>
</opcode>
- <opcode id="7e02" name="OP_GroundSpawn" priority="9" updated="07/14/26">
+ <opcode id="5331" name="OP_GroundSpawn" priority="9" updated="07/28/26">
<comment>MakeDropCode</comment>
<payload dir="server" typename="makeDropStruct" sizechecktype="none"/>
</opcode>
- <opcode id="5926" name="OP_SendZonePoints" priority="5" updated="07/14/26">
+ <opcode id="0834" name="OP_SendZonePoints" priority="5" updated="07/28/26">
<comment>Coords in a zone that will port you to another zone</comment>
<payload dir="server" typename="zonePointsStruct" sizechecktype="none"/>
</opcode>
- <opcode id="7830" name="OP_AAExpUpdate" priority="4" updated="07/14/26">
- <comment>Receiving AA experience. Also when percent to AA changes.</comment>
+ <opcode id="1d38" name="OP_AAExpUpdate" priority="4" updated="07/28/26">
<payload dir="server" typename="altExpUpdateStruct" sizechecktype="match"/>
</opcode>
- <opcode id="53b7" name="OP_ExpUpdate" priority="4" updated="07/14/26">
- <comment>ExpUpdateCode</comment>
+ <opcode id="5abf" name="OP_ExpUpdate" priority="4" updated="07/28/26">
<payload dir="server" typename="expUpdateStruct" sizechecktype="match"/>
</opcode>
- <opcode id="5924" name="OP_GuildMOTD" priority="2" updated="07/14/26">
+ <opcode id="70c5" name="OP_GuildMOTD" priority="2" updated="07/28/26">
<comment>GuildMOTD</comment>
<payload dir="server" typename="guildMOTDStruct" sizechecktype="none"/>
</opcode>
- <opcode id="5188" name="OP_ClientUpdate" priority="10" updated="07/14/26">
+ <opcode id="5bfd" name="OP_ClientUpdate" priority="10" updated="07/28/26">
<comment>Position updates</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
<payload dir="client" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="3348" name="OP_MobUpdate" priority="10" updated="07/14/26">
+ <opcode id="26d8" name="OP_MobUpdate" priority="10" updated="07/28/26">
<comment>MobUpdateCode</comment>
<payload dir="both" typename="spawnPositionUpdate" sizechecktype="none"/>
</opcode>
- <opcode id="0e2c" name="OP_DeleteSpawn" priority="10" updated="07/14/26">
+ <opcode id="1856" name="OP_DeleteSpawn" priority="10" updated="07/28/26">
<comment>DeleteSpawnCode</comment>
<payload dir="both" typename="deleteSpawnStruct" sizechecktype="match"/>
</opcode>
- <opcode id="483c" name="OP_RemoveSpawn" priority="10" updated="07/14/26">
+ <opcode id="27cb" name="OP_RemoveSpawn" priority="10" updated="07/28/26">
<comment>Remove spawn from zone</comment>
<payload dir="both" typename="removeSpawnStruct" sizechecktype="none"/>
</opcode>
- <opcode id="2874" name="OP_WearChange" priority="2" updated="07/14/26">
+ <opcode id="4a78" name="OP_WearChange" priority="2" updated="07/28/26">
<priorityNote>stock handler no-op; id mapped for log id only</priorityNote>
<comment>SpawnUpdateCode</comment>
<payload dir="both" typename="SpawnUpdateStruct" sizechecktype="match"/>
</opcode>
- <opcode id="4170" name="OP_SpawnAppearance" priority="10" updated="07/14/26">
+ <opcode id="384f" name="OP_SpawnAppearance" priority="10" updated="07/28/26">
<comment>SpawnAppearanceCode</comment>
<payload dir="both" typename="spawnEventEQLStruct" sizechecktype="match"/>
</opcode>
- <opcode id="3b0c" name="OP_Stamina" priority="4" updated="07/14/26">
- <comment>Server updating on hunger/thirst</comment>
+ <opcode id="17a1" name="OP_Stamina" priority="4" updated="07/28/26">
<payload dir="server" typename="staminaStruct" sizechecktype="match"/>
</opcode>
- <opcode id="a5c0" name="OP_HPUpdate" priority="10" updated="07/14/26">
+ <opcode id="3139" name="OP_HPUpdate" priority="10" updated="07/28/26">
<comment>NpcHpUpdateCode Update HP % of a PC or NPC</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="0717" name="OP_GuildMemberUpdate" priority="3" updated="07/14/26">
- <comment>Info regarding guild members</comment>
+ <opcode id="0aa2" name="OP_GuildMemberUpdate" priority="3" updated="07/28/26">
<payload dir="server" typename="GuildMemberUpdate" sizechecktype="match"/>
</opcode>
- <opcode id="597e" name="OP_ClickObject" priority="9" updated="07/14/26">
- <comment>Items dropped on the ground</comment>
+ <opcode id="23ee" name="OP_ClickObject" priority="9" updated="07/28/26">
+ <comment>Server removing a ground drop (pickup)</comment>
<payload dir="server" typename="remDropStruct" sizechecktype="match"/>
</opcode>
- <opcode id="76e5" name="OP_Action" priority="7" updated="07/14/26">
+ <opcode id="50e8" name="OP_Action" priority="7" updated="07/28/26">
<comment>Spells cast etc</comment>
<payload dir="both" typename="actionStruct" sizechecktype="match"/>
<payload dir="both" typename="actionAltStruct" sizechecktype="match"/>
</opcode>
- <opcode id="5446" name="OP_Action2" priority="7" updated="07/14/26">
+ <opcode id="7df9" name="OP_Action2" priority="7" updated="07/28/26">
<comment>Combat actions i.e. bash, kick etc</comment>
<payload dir="both" typename="action2Struct" sizechecktype="match"/>
</opcode>
- <opcode id="1f81" name="OP_Consider" priority="9" updated="07/14/26">
- <comment>ConsiderCode</comment>
+ <opcode id="64c4" name="OP_Consider" priority="9" updated="07/28/26">
+ <comment>Consider request/response</comment>
<payload dir="both" typename="considerStruct" sizechecktype="match"/>
</opcode>
- <opcode id="3ef7" name="OP_TargetMouse" priority="9" updated="07/14/26">
- <comment>Targeting a person - old ClientTargetCode</comment>
+ <opcode id="3897" name="OP_TargetMouse" priority="9" updated="07/28/26">
+ <comment>Targeting a spawn via mouse</comment>
<payload dir="both" typename="clientTargetStruct" sizechecktype="match"/>
</opcode>
<opcode id="ffff" name="OP_SpawnRename" priority="6" updated="07/19/23">
@@ -120,101 +114,92 @@
<comment>Spawns getting renamed after initial NewSpawn</comment>
<payload dir="server" typename="spawnRenameStruct" sizechecktype="match"/>
</opcode>
- <opcode id="2e7d" name="OP_Illusion" priority="8" updated="07/14/26">
+ <opcode id="5201" name="OP_Illusion" priority="8" updated="07/28/26">
<comment>Spawn being illusioned (changing forms)</comment>
<payload dir="both" typename="spawnIllusionStruct" sizechecktype="match"/>
</opcode>
- <opcode id="4ca8" name="OP_ZoneChange" priority="10" updated="07/14/26">
+ <opcode id="3360" name="OP_ZoneChange" priority="10" updated="07/28/26">
<priorityNote>server never sends it; handler only sets m_zoning</priorityNote>
<comment>old ZoneChangeCode</comment>
<payload dir="both" typename="zoneChangeEQLStruct" sizechecktype="match"/>
</opcode>
- <opcode id="7869" name="OP_GroupInvite" priority="4" updated="07/14/26">
+ <opcode id="63c6" name="OP_GroupInvite" priority="4" updated="07/28/26">
<comment>You invite someone while ungrouped or get invited by someone ungrouped</comment>
<payload dir="both" typename="groupInviteStruct" sizechecktype="match"/>
</opcode>
- <opcode id="0ff2" name="OP_GroupCancelInvite" priority="4" updated="07/14/26">
+ <opcode id="7a77" name="OP_GroupCancelInvite" priority="4" updated="07/28/26">
<priorityNote>single sample; reason byte unverified</priorityNote>
<comment>Declining to join a group</comment>
<payload dir="both" typename="groupDeclineStruct" sizechecktype="none"/>
</opcode>
- <opcode id="41d7" name="OP_GroupFollow" priority="4" updated="07/14/26">
- <comment>You join a group or player joins group</comment>
+ <opcode id="4e40" name="OP_GroupFollow" priority="4" updated="07/28/26">
<payload dir="server" typename="groupFollowStruct" sizechecktype="match"/>
</opcode>
- <opcode id="d305" name="OP_GroupUpdate" priority="6" updated="07/14/26">
- <comment>Group updates</comment>
- <comment>168B group roster refresh (fires at join/disband, member name@64)</comment>
+ <opcode id="68d3" name="OP_GroupUpdate" priority="6" updated="07/28/26">
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="227d" name="OP_GroupDisband" priority="4" updated="07/14/26">
- <comment>You disband from group</comment>
+ <opcode id="216d" name="OP_GroupDisband" priority="4" updated="07/28/26">
<payload dir="server" typename="groupDisbandStruct" sizechecktype="match"/>
</opcode>
- <opcode id="4546" name="OP_GroupLeader" priority="4" updated="07/14/26">
+ <opcode id="18e2" name="OP_GroupLeader" priority="4" updated="07/28/26">
<priorityNote>single sample; unconfirmed on /makeleader</priorityNote>
- <comment>Group leader change</comment>
<payload dir="server" typename="groupLeaderChangeStruct" sizechecktype="none"/>
</opcode>
- <opcode id="6c3c" name="OP_Buff" priority="6" updated="07/14/26">
+ <opcode id="122f" name="OP_Buff" priority="6" updated="07/28/26">
<priorityNote>client dir = memorize/forget, consumed silently</priorityNote>
<comment>old BuffDropCode</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
<payload dir="client" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="6cbd" name="OP_BeginCast" priority="5" updated="07/14/26">
- <comment>BeginCastCode</comment>
+ <opcode id="2c36" name="OP_BeginCast" priority="5" updated="07/28/26">
<payload dir="both" typename="beginCastStruct" sizechecktype="none"/>
</opcode>
- <opcode id="7a2f" name="OP_CastSpell" priority="5" updated="07/14/26">
- <comment>StartCastCode</comment>
+ <opcode id="155e" name="OP_CastSpell" priority="5" updated="07/28/26">
<payload dir="both" typename="startCastStruct" sizechecktype="match"/>
</opcode>
- <opcode id="6f46" name="OP_SwapSpell" priority="3" updated="07/14/26">
+ <opcode id="4459" name="OP_SwapSpell" priority="3" updated="07/28/26">
<comment>TradeSpellBookSlotsCode</comment>
<payload dir="both" typename="tradeSpellBookSlotsStruct" sizechecktype="match"/>
</opcode>
- <opcode id="6a04" name="OP_InspectAnswer" priority="3" updated="07/14/26">
+ <opcode id="5503" name="OP_InspectAnswer" priority="3" updated="07/28/26">
<comment>InspectDataCode</comment>
<payload dir="server" typename="inspectDataStruct" sizechecktype="match"/>
</opcode>
- <opcode id="61a5" name="OP_Emote" priority="4" updated="07/14/26">
+ <opcode id="6d34" name="OP_Emote" priority="4" updated="07/28/26">
<comment>EmoteTextCode</comment>
<payload dir="both" typename="emoteTextStruct" sizechecktype="none"/>
</opcode>
- <opcode id="7b92" name="OP_SimpleMessage" priority="4" updated="07/14/26">
+ <opcode id="72cb" name="OP_SimpleMessage" priority="4" updated="07/28/26">
<comment>SimpleMessageCode</comment>
<payload dir="server" typename="simpleMessageStruct" sizechecktype="match"/>
</opcode>
- <opcode id="15d0" name="OP_FormattedMessage" priority="4" updated="07/14/26">
+ <opcode id="06f1" name="OP_FormattedMessage" priority="4" updated="07/28/26">
<comment>FormattedMessageCode i.e. pet dismissed etc</comment>
<payload dir="server" typename="formattedMessageStruct" sizechecktype="none"/>
</opcode>
- <opcode id="7015" name="OP_CommonMessage" priority="9" updated="07/14/26">
+ <opcode id="37fa" name="OP_CommonMessage" priority="9" updated="07/28/26">
<comment>ChannelMessageCode i.e. /tell /ooc /shout etc</comment>
<payload dir="both" typename="channelMessageStruct" sizechecktype="none"/>
</opcode>
- <opcode id="2189" name="OP_SpecialMesg" priority="4" updated="07/14/26">
+ <opcode id="4e60" name="OP_SpecialMesg" priority="4" updated="07/28/26">
<comment>Communicate textual info to client including hail responses etc</comment>
<payload dir="server" typename="specialMessageStruct" sizechecktype="none"/>
</opcode>
- <opcode id="5e9e" name="OP_RandomReq" priority="2" updated="07/14/26">
- <comment>RandomReqCode</comment>
+ <opcode id="05b1" name="OP_RandomReq" priority="2" updated="07/28/26">
<payload dir="client" typename="randomReqStruct" sizechecktype="none"/>
</opcode>
- <opcode id="77bd" name="OP_RandomReply" priority="2" updated="07/14/26">
- <comment>RandomCode</comment>
+ <opcode id="79a0" name="OP_RandomReply" priority="2" updated="07/28/26">
<payload dir="server" typename="randomStruct" sizechecktype="match"/>
</opcode>
- <opcode id="2265" name="OP_ConsentResponse" priority="3" updated="07/14/26">
+ <opcode id="492d" name="OP_ConsentResponse" priority="3" updated="07/28/26">
<comment>Server replying with consent information after /consent</comment>
<payload dir="server" typename="consentResponseStruct" sizechecktype="match"/>
</opcode>
- <opcode id="05fc" name="OP_DenyResponse" priority="3" updated="07/14/26">
+ <opcode id="3991" name="OP_DenyResponse" priority="3" updated="07/28/26">
<comment>Server replying with deny information after /deny</comment>
<payload dir="server" typename="consentResponseStruct" sizechecktype="match"/>
</opcode>
- <opcode id="2255" name="OP_ManaChange" priority="5" updated="07/14/26">
+ <opcode id="2b95" name="OP_ManaChange" priority="5" updated="07/28/26">
<comment>Mana change. Bards send this up with no size. Casters receive this for mana updates.</comment>
<payload dir="server" typename="manaDecrementStruct" sizechecktype="match"/>
</opcode>
@@ -222,12 +207,12 @@
<comment>MoneyOnCorpseCode</comment>
<payload dir="server" typename="moneyOnCorpseStruct" sizechecktype="match"/>
</opcode>
- <opcode id="3873" name="OP_SkillUpdate" priority="5" updated="07/14/26">
+ <opcode id="0462" name="OP_SkillUpdate" priority="5" updated="07/28/26">
<priorityNote>prior id 0dc7 was the zone-in bulk dump (OOB crash); bounds guard kept</priorityNote>
<comment>Skill up code</comment>
<payload dir="server" typename="skillIncStruct" sizechecktype="match"/>
</opcode>
- <opcode id="0d68" name="OP_LevelUpdate" priority="8" updated="07/14/26">
+ <opcode id="70c6" name="OP_LevelUpdate" priority="8" updated="07/28/26">
<priorityNote>single sample</priorityNote>
<comment>LevelUpUpdateCode</comment>
<payload dir="server" typename="levelUpUpdateStruct" sizechecktype="none"/>
@@ -238,46 +223,39 @@
</opcode>
<!-- Not necessary for SEQ to run but here to name packets in logs. -->
- <opcode id="2ac8" name="OP_Logout" priority="3" updated="07/14/26">
+ <opcode id="1160" name="OP_Logout" priority="3" updated="07/28/26">
<priorityNote>handler wired DIR_Client only</priorityNote>
<comment>Camp/logout handshake</comment>
<payload dir="both" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="729e" name="OP_ExpandedGuildInfo" priority="0" updated="07/14/26">
+ <opcode id="652b" name="OP_Animation" priority="0" updated="07/28/26">
<priorityNote>noHandler</priorityNote>
- <comment>Guild ranks and other misc guild data - Variable length</comment>
- <payload dir="server" typename="uint8_t" sizechecktype="none"/>
+ <comment>spawnId + action + value; fires per spawn animation</comment>
+ <payload dir="both" typename="spawnAnimationStruct" sizechecktype="none"/>
</opcode>
- <opcode id="4ecf" name="OP_Animation" priority="0" updated="07/14/26">
+ <opcode id="3fb1" name="OP_InspectRequest" priority="0" updated="07/28/26">
<priorityNote>noHandler</priorityNote>
- <comment>Combat animation, emote animations etc - 4 bytes</comment>
- <payload dir="both" typename="uint8_t" sizechecktype="none"/>
- </opcode>
- <opcode id="0cdf" name="OP_InspectRequest" priority="0" updated="07/14/26">
- <priorityNote>noHandler</priorityNote>
- <comment>InspectRequestCode - 8 bytes</comment>
<payload dir="client" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="7d46" name="OP_LootMessageEQL" priority="4" updated="07/14/26">
+ <opcode id="7199" name="OP_LootMessageEQL" priority="4" updated="07/28/26">
<comment>Loot-and-sell message</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="4d77" name="OP_MoneyUpdateEQL" priority="4" updated="07/14/26">
- <comment>Money totals</comment>
+ <opcode id="03c3" name="OP_MoneyUpdateEQL" priority="4" updated="07/28/26">
+ <comment>Running money total [plat,gold,silver,copper]</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="3959" name="OP_GuildsInZoneList" priority="8" updated="07/14/26">
+ <opcode id="157b" name="OP_GuildsInZoneList" priority="8" updated="07/28/26">
<comment>Listing of guild names present in the current zone. Generally seen on zoning</comment>
<payload dir="server" typename="guildsInZoneListStruct" sizechecktype="none"/>
</opcode>
- <opcode id="6c4c" name="OP_NewGuildInZone" priority="8" updated="07/14/26">
+ <opcode id="63a5" name="OP_NewGuildInZone" priority="8" updated="07/28/26">
<comment>An additional guild name has appeared in the current zone. Seen when a guilded player enters the current zone</comment>
<payload dir="server" typename="newGuildInZoneStruct" sizechecktype="none"/>
</opcode>
- <opcode id="1a0b" name="OP_Find" priority="0" updated="07/14/26">
+ <opcode id="767d" name="OP_Find" priority="0" updated="07/28/26">
<priorityNote>noHandler</priorityNote>
- <comment>Find window data - variable length, 24 min</comment>
- <payload dir="server" typename="uint8_t" sizechecktype="none"/>
+ <payload dir="client" typename="uint8_t" sizechecktype="none"/>
</opcode>
<!-- Below are used to help make sense of the logs when searching for opcodes
@@ -284,58 +262,47 @@
Using these marks unknown opcodes in logs for easier reading -->
<!-- Below are unverified -->
- <opcode id="31ae" name="OP_SendAATable" priority="5" updated="07/14/26">
+ <opcode id="2a24" name="OP_SendAATable" priority="5" updated="07/28/26">
<comment>AA definition table</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
- </opcode>
+ </opcode>
- <opcode id="1ce7" name="OP_HideCorpse" priority="0" updated="07/14/26">
+ <opcode id="6ec9" name="OP_HideCorpse" priority="0" updated="07/28/26">
<priorityNote>noHandler</priorityNote>
- <comment>/hidecorpse request</comment>
<payload dir="client" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="4fb6" name="OP_SelfPosEQL" priority="10" updated="07/14/26">
+ <opcode id="6323" name="OP_SelfPosEQL" priority="10" updated="07/28/26">
<comment>Client self-position</comment>
<payload dir="client" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="021e" name="OP_NpcMoveUpdate" priority="10" updated="07/14/26">
+ <opcode id="3e7b" name="OP_NpcMoveUpdate" priority="10" updated="07/28/26">
<comment>Position updates</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="36bf" name="OP_Death" priority="7" updated="07/14/26">
+ <opcode id="2087" name="OP_Death" priority="7" updated="07/28/26">
<priorityNote>also fires on corpse decay</priorityNote>
<comment>old NewCorpseCode</comment>
<payload dir="server" typename="newCorpseStruct" sizechecktype="match"/>
</opcode>
- <opcode id="7d1c" name="OP_DeathEQL" priority="9" updated="07/14/26">
+ <opcode id="58ab" name="OP_DeathEQL" priority="3" updated="07/28/26">
<comment>Death/combat subcode channel</comment>
<payload dir="both" typename="deathRecordEQLStruct" sizechecktype="none"/>
</opcode>
- <opcode id="591d" name="OP_GuildMemberList" priority="3" updated="07/14/26">
- <comment>List of guild members - Variable length</comment>
+ <opcode id="6e10" name="OP_GuildMemberList" priority="3" updated="07/28/26">
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="73e2" name="OP_BuffList" priority="6" updated="07/14/26">
+ <opcode id="7bda" name="OP_BuffList" priority="6" updated="07/28/26">
<comment>Per-spawn buff list</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="631c" name="OP_LoadoutSwapEQL" priority="3" updated="07/14/26">
+ <opcode id="4c71" name="OP_LoadoutSwapEQL" priority="3" updated="07/28/26">
<comment>Loadout-swap self refresh</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="6768" name="OP_LootDrops" priority="4" updated="07/14/26">
- <comment>Personal loot list</comment>
+ <opcode id="2a6f" name="OP_LootDrops" priority="4" updated="07/28/26">
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="713a" name="OP_BuffList2" priority="6" updated="07/14/26">
- <comment>Per-spawn buff list (channel 2)</comment>
- <payload dir="server" typename="uint8_t" sizechecktype="none"/>
- </opcode>
- <opcode id="5c47" name="OP_BuffList3" priority="6" updated="07/14/26">
- <comment>Per-spawn buff list (channel 3)</comment>
- <payload dir="server" typename="uint8_t" sizechecktype="none"/>
- </opcode>
- <opcode id="18ba" name="OP_DzSwitchInfoEQL" priority="3" updated="07/14/26">
+ <opcode id="2216" name="OP_DzSwitchInfoEQL" priority="3" updated="07/28/26">
<priorityNote>wired but untested on Legends</priorityNote>
<comment>Dynamic-zone switch info</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
@@ -1246,14 +1213,12 @@
<comment>SpellFadeCode</comment>
<payload dir="both" typename="spellFadedStruct" sizechecktype="none"/>
</opcode>
- <opcode id="0fab" name="OP_Stance" priority="0" updated="07/14/26">
+ <opcode id="0b49" name="OP_Stance" priority="0" updated="07/28/26">
<priorityNote>noHandler</priorityNote>
- <comment>EQ Legends swappable stance; server echo carries active ability id - 4 bytes</comment>
- <payload dir="server" typename="uint8_t" sizechecktype="none"/>
+ <payload dir="both" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="3b12" name="OP_Invocation" priority="0" updated="07/14/26">
+ <opcode id="2ae5" name="OP_Invocation" priority="0" updated="07/28/26">
<priorityNote>noHandler</priorityNote>
- <comment>EQ Legends swappable invocation; server echo carries active ability id - 4 bytes</comment>
- <payload dir="server" typename="uint8_t" sizechecktype="none"/>
+ <payload dir="both" typename="uint8_t" sizechecktype="none"/>
</opcode>
</seqopcodes>
Modified: showeq/branches/showeqlegends/src/aalist.cpp
===================================================================
--- showeq/branches/showeqlegends/src/aalist.cpp 2026-07-24 20:43:30 UTC (rev 1681)
+++ showeq/branches/showeqlegends/src/aalist.cpp 2026-07-29 02:38:41 UTC (rev 1682)
@@ -48,9 +48,15 @@
// Points are pinned to the same small width; nothing stretches to fill
// the pane (empty space to the right).
header()->setStretchLastSection(false);
+#if QT_VERSION >= 0x050000
header()->setSectionResizeMode(AACOL_NAME, QHeaderView::ResizeToContents);
header()->setSectionResizeMode(AACOL_RANK, QHeaderView::Interactive);
header()->setSectionResizeMode(AACOL_POINTS, QHeaderView::Interactive);
+#else
+ header()->setResizeMode(AACOL_NAME, QHeaderView::ResizeToContents);
+ header()->setResizeMode(AACOL_RANK, QHeaderView::Interactive);
+ header()->setResizeMode(AACOL_POINTS, QHeaderView::Interactive);
+#endif
setColumnWidth(AACOL_RANK, 56);
setColumnWidth(AACOL_POINTS, 56);
Modified: showeq/branches/showeqlegends/src/everquest.h
===================================================================
--- showeq/branches/showeqlegends/src/everquest.h 2026-07-24 20:43:30 UTC (rev 1681)
+++ showeq/branches/showeqlegends/src/everquest.h 2026-07-29 02:38:41 UTC (rev 1682)
@@ -686,6 +686,22 @@
/*0032*/
};
+// EQ Legends dynamic-zone switch info container (176B, instance-create).
+// Restructured from the stock dzSwitchInfo above; decoded by
+// ZoneMgr::dzSwitchInfoEQL, which translates it into the stock struct.
+struct dzSwitchInfoEQLStruct
+{
+/*0000*/ uint16_t zoneID;
+/*0002*/ uint16_t instanceID;
+/*0004*/ uint32_t unknown0004; // -1 observed
+/*0008*/ float y;
+/*0012*/ float x;
+/*0016*/ float z;
+/*0020*/ float heading; // heading? unvalidated
+/*0024*/ uint32_t type;
+/*0028*/ // zero tail (container is 176B)
+};
+
/*
** Dynamic Zone Info Struct
** Length: 212 Octets
@@ -1153,22 +1169,25 @@
// but are unvalidated; heading/animation/pitch positions
// in words 3-4 are unmapped (0x6000/0 observed) - all are
// zeroed at consumption in spawn.cpp until wire-mapped.
- signed z:19; // z coord (loc value 1)
+ // EQ Legends 2026-07-28 posData layout (spawnshell skips the
+ // one leading junk word, then reads these 5). word0 and
+ // word3 carry the two horizontal coords; assigning y to
+ // word0 and x to word3 rotates the dots to align with the
+ // zone map (the reverse assignment came out 90 deg off).
+ signed y:19; // y coord (EQ Legends x/y transposed) - word0
+ signed deltaY:13; // presumed change in y (unvalidated)
+
+ signed z:19; // z coord (loc value 1) - word1
signed deltaZ:13; // presumed change in z (unvalidated)
- signed x:19; // x coord (EQ Legends x/y transposed)
+ signed padCoord:32; // gap word (0 observed) - word2
+
+ signed x:19; // x coord (EQ Legends x/y transposed) - word3
signed deltaX:13; // presumed change in x (unvalidated)
- signed y:19; // y coord (EQ Legends x/y transposed)
- signed deltaY:13; // presumed change in y (unvalidated)
-
- unsigned heading:12; // candidate heading word (unvalidated)
+ unsigned heading:12; // word4 (unmapped/zeroed)
unsigned padding00:20;
- signed deltaHeading:10; // unmapped
- signed animation:10; // unmapped
- unsigned pitch:12; // unmapped
-
};
int32_t posData[5];
};
@@ -1551,7 +1570,7 @@
};
/*
-** EQ Legends OP_MobUpdate (opcode 0x67e0, 14 bytes, 2026-07-07 wire).
+** EQ Legends OP_MobUpdate (14 bytes).
** Coordinate block starts at payload offset 4 and packs, low bit first:
** x : bits 0..18 z : bits 19..37 (7-bit gap) y : bits 45..63
** each signed 19-bit, world value = field >> 3 (live convention).
@@ -1574,24 +1593,25 @@
/*
** EQ Legends player waypoint position update
-** Wire: one-byte-netop 0x03 family, LE opcode 0x0b03, fixed 24 bytes.
+** Wire: one-byte-netop 0x03 family, fixed 24 bytes.
** posData words use the same bit layout as the spawnStruct union.
*/
struct playerPosUpdateEQLStruct
{
/*0000*/ uint32_t spawnId;
-/*0004*/ uint32_t flags; // 0x00010842 observed constant
-/*0008*/ uint32_t unknown0008; // zero observed
-/*0012*/ signed z:19, deltaZ:13; // coord block, same 19-bit
-/*0016*/ signed x:19; // >>3 packing as spawnStruct
- unsigned heading:13; // h2048 heading (0..2047) in x-word high bits
-/*0020*/ signed y:19, deltaY:13; // deltas unvalidated, unused
-/*0024*/ uint32_t tail; // zero observed
-/*0028*/
+/*0004*/ uint32_t unknown0004;
+/*0008*/ signed x:19; // map-X coord, >>3 to Spawn units (bit64)
+ signed deltaX:13;
+/*0012*/ signed z:19; // z coord, >>3 (bit96)
+ signed deltaZ:13;
+/*0016*/ uint32_t unknown0016; // heading/animation region
+/*0020*/ signed y:19; // map-Y coord, >>3 (bit160)
+ signed deltaY:13;
+/*0024*/
};
/*
-** EQ Legends spawn appearance event (OP_SpawnAppearance = 0x1bdc)
+** EQ Legends spawn appearance event (OP_SpawnAppearance)
** Stock 8B spawnAppearanceStruct u32-widened; fixed 24 bytes on the
** wire (3748/3748 captured packets, both directions).
** Decoded by SpawnShell::spawnEventEQL.
@@ -1607,7 +1627,7 @@
};
/*
-** EQ Legends client zone-change request (OP_ZoneChange = 0x76d3)
+** EQ Legends client zone-change request (OP_ZoneChange)
** Fixed 484 bytes on the wire (32/32 captured packets). Carries
** position but no zone id; the server never sends this opcode (a
** zone change is a session teardown + new session). Layout unmapped;
@@ -2092,7 +2112,7 @@
};
/*
-** EQ Legends death/combat record (opcode 0x295d, subcode 0x07, 36 bytes)
+** EQ Legends death/combat record (subcode 0x07, 36 bytes)
** A combat damage event; the fatal hit is not flagged - spawn removal
** at death is carried by OP_RemoveSpawn/OP_DeleteSpawn, so consumers
** must treat this as informational only.
@@ -2235,10 +2255,10 @@
/*0018*/ uint32_t targetId; // The current selected target
/*0022*/ uint8_t unknown0022[2]; // ***Placeholder
/*0024*/ uint8_t unknown0024[2]; // ***Placeholder
-/*0026*/ uint8_t unknown0026[14]; // ***Placeholder (EQL wire
- // is fixed 40B, 76/76
- // packets; stock was 39)
-/*0040*/
+/*0026*/ uint8_t unknown0026[18]; // ***Placeholder (EQL wire
+ // is fixed 44B - slot@0,
+ // spellId@4, targetId@18)
+/*0044*/
};
/*
@@ -2384,11 +2404,7 @@
/*0000*/ uint32_t exp; // EQ Legends: level progress
// on a 0-100000 scale
// (hundredths of a percent);
- // live was x/330. NOTE: on
- // Legends this rides opcode
- // 6801 (in-game-calibrated
- // against the exp bar; the
- // ids were cross-wired)
+ // live was x/330
/*0004*/ uint32_t unknown0004; // 0 observed
/*0008*/ uint32_t aaUnspent; // unassigned AA points
// (in-game-verified)
@@ -2405,12 +2421,7 @@
{
/*0000*/ uint32_t altexp; // EQ Legends: AA per-point
// progress on a 0-100000
- // scale (live was x/330).
- // NOTE: on Legends this
- // rides opcode 42d1
- // (in-game-calibrated
- // against the AA bar; the
- // ids were cross-wired)
+ // scale (live was x/330)
/*0004*/ uint32_t aapoints; // unassigned AA points
// (in-game-verified)
/*0008*/ uint32_t unknown0008; // varying tail (suffix u32
@@ -2608,6 +2619,30 @@
/*0042*/
};
+// EQ Legends 2026-07-28 real-time client self-state (the 38-byte body on
+// the c2s OP_ClientUpdate/OP_SelfPosEQL stream). Wire-verified against
+// /loc: y@6, z@10, heading@26 (facing packed in the low 21 bits), x@34.
+struct eqlClientSelfPosStruct
+{
+/*0000*/ uint16_t ctr; // update counter
+/*0002*/ uint16_t spawnId; // player's spawn id
+/*0004*/ uint16_t unknown0004;
+/*0006*/ float y; // y coord (loc value 2)
+/*0010*/ float z; // z coord (loc value 3)
+/*0014*/ uint8_t unknown0014[8];
+/*0022*/ union {
+ uint32_t headingRaw; // raw dword @22
+ struct {
+ unsigned :20; // low 20 bits unused for facing
+ unsigned heading :11; // facing 0..2047 (bits 20-30); swept a full circle on a spin
+ unsigned :1;
+ };
+ };
+/*0026*/ uint8_t unknown0026[8];
+/*0034*/ float x; // x coord (loc value 1)
+/*0038*/
+};
+
/*
** Spawn Appearance
** Length: 8 Octets
@@ -2693,6 +2728,17 @@
/*0012*/
};
+// EQ Legends money update (OP_MoneyUpdateEQL): the player's full running
+// coin totals (not a live-style per-coin delta), sent when money changes.
+struct moneyUpdateEQLStruct
+{
+/*0000*/ uint32_t platinum; // current platinum total
+/*0004*/ uint32_t gold; // current gold total
+/*0008*/ uint32_t silver; // current silver total
+/*0012*/ uint32_t copper; // current copper total
+/*0016*/
+};
+
/* Memorize slot operations, mem, forget, etc */
struct memorizeSlotStruct
Modified: showeq/branches/showeqlegends/src/group.cpp
===================================================================
--- showeq/branches/showeqlegends/src/group.cpp 2026-07-24 20:43:30 UTC (rev 1681)
+++ showeq/branches/showeqlegends/src/group.cpp 2026-07-29 02:38:41 UTC (rev 1682)
@@ -70,8 +70,8 @@
// EQ Legends does NOT resend the group roster on zone-in (verified
// 2026-07-09: zoned mid-group, no group packets followed the
// profile), so we must NOT clear here - the group persists across
- // zones and is only changed by the per-member add/remove opcodes
- // (0x1512 / 0x09aa). Clearing here (stock behavior, which relied on
+ // zones and is only changed by the per-member add/remove opcodes.
+ // Clearing here (stock behavior, which relied on
// a post-profile groupUpdate to repopulate) would wipe the group on
// every zone with nothing to refill it.
(void)player;
@@ -243,10 +243,10 @@
void GroupMgr::groupRosterEQL(const uint8_t* data, size_t len)
{
- // EQ Legends full group roster (OP_GroupUpdate = 0x397d), resent at
- // zone-in - the group analog of the buff list (0x77ae): it loads the
- // members you already have when ShowEQ zones in or starts up mid-
- // group, which the incremental 0x1512/0x09aa updates cannot. The
+ // EQ Legends full group roster (OP_GroupUpdate), resent at zone-in -
+ // the group analog of the buff list: it loads the members you already
+ // have when ShowEQ zones in or starts up mid-group, which the
+ // incremental member add/remove updates cannot. The
// stock groupUpdate parser cannot read this format (verified), so it
// needs a dedicated decoder. Header: u32 groupId | u32 count |
// leader name @8; then count 44-byte member records from offset 18
Modified: showeq/branches/showeqlegends/src/guildshell.cpp
===================================================================
--- showeq/branches/showeqlegends/src/guildshell.cpp 2026-07-24 20:43:30 UTC (rev 1681)
+++ showeq/branches/showeqlegends/src/guildshell.cpp 2026-07-29 02:38:41 UTC (rev 1682)
@@ -115,13 +115,13 @@
void GuildMember::update(const GuildMemberUpdate* gmu)
{
- // EQ Legends: the 0x51ed member-update reliably carries only
+ // EQ Legends: the member-update reliably carries only
// guildId, name, and rank - the zone/instance/lastOn tail is
// uninitialized server memory (0x7fff.. pointer bytes observed).
// A member logging off sends one of these with a garbage zone
// (e.g. 5376), which then drove a bogus zoneNameFromID lookup and
// a spurious "zone not found" console line for an offline member.
- // Zone/lastOn are authoritative from the roster (0x31ed) only;
+ // Zone/lastOn are authoritative from the roster only;
// leave them untouched here.
(void)gmu;
#ifdef GUILDSHELL_DIAG
Modified: showeq/branches/showeqlegends/src/interface.cpp
===================================================================
--- showeq/branches/showeqlegends/src/interface.cpp 2026-07-24 20:43:30 UTC (rev 1681)
+++ showeq/branches/showeqlegends/src/interface.cpp 2026-07-29 02:38:41 UTC (rev 1682)
@@ -631,7 +631,7 @@
{
connect(m_zoneMgr, SIGNAL(playerProfile(const charProfileStruct*)),
m_groupMgr, SLOT(player(const charProfileStruct*)));
- // EQL: 0x397d is the full group roster, resent at zone-in (the
+ // EQL: the full group roster, resent at zone-in (the
// Legends wire uses per-member 0x1512/0x09aa for live changes and
// this full roster on zone). Authoritative - clears + repopulates.
m_packet->connect2("OP_GroupUpdate", SP_Zone, DIR_Server,
@@ -710,7 +710,7 @@
if (m_guildShell)
{
- // EQL: 0x31ed roster is stock's layout except a 1-byte-wider
+ // EQL: the roster is stock's layout except a 1-byte-wider
// header, classMask in the class slot, and zoneId in live's
// skipped tail; GuildShell handles all three.
m_packet->connect2("OP_GuildMemberList", SP_Zone, DIR_Server,
@@ -765,7 +765,7 @@
m_packet->connect2("OP_Emote", SP_Zone, DIR_Server|DIR_Client,
"emoteTextStruct", SZC_None,
m_messageShell, SLOT(emoteText(const uint8_t*)));
- // EQL: 0x17af inspect answer is the stock inspectDataStruct
+ // EQL: the inspect answer is the stock inspectDataStruct
// (head byte-perfect; the 1956B wire size equals the struct
// incl. its unknown1772 tail - 3/3 captured inspects), so the
// size check is back on.
@@ -806,7 +806,7 @@
m_packet->connect2("OP_MemorizeSpell", SP_Zone, DIR_Server|DIR_Client,
"memSpellStruct", SZC_Match,
m_messageShell, SLOT(handleSpell(const uint8_t*, size_t, uint8_t)));
- // EQL: 0x5591 = stock beginCastStruct (spellId u16@0, spawnId
+ // EQL: stock beginCastStruct (spellId u16@0, spawnId
// u16@4 wire-verified); 19B vs stock 11B so no size check.
m_packet->connect2("OP_BeginCast", SP_Zone, DIR_Server|DIR_Client,
"beginCastStruct", SZC_None,
@@ -814,9 +814,9 @@
m_packet->connect2("OP_BuffFadeMsg", SP_Zone, DIR_Server|DIR_Client,
"spellFadedStruct", SZC_None,
m_messageShell, SLOT(spellFaded(const uint8_t*)));
- // EQL: 0x10b5 = stock startCastStruct (slot@0, spellId@4,
+ // EQL: stock startCastStruct (slot@0, spellId@4,
// targetId@18). startCast prints the player's own cast WITH its
- // target; OP_BeginCast (0x5591) prints other spawns' casts and
+ // target; OP_BeginCast prints other spawns' casts and
// skips self, so together there is exactly one line per cast and
// the self line keeps its target.
m_packet->connect2("OP_CastSpell", SP_Zone, DIR_Server|DIR_Client,
@@ -926,9 +926,9 @@
m_packet->connect2("OP_Action2", SP_Zone, DIR_Client|DIR_Server,
"action2Struct", SZC_Match,
this, SLOT(action2Message(const uint8_t*)));
- // EQL: 0x66cb is stock OP_Death - newCorpseStruct fits byte-perfect
+ // EQL: stock OP_Death - newCorpseStruct fits byte-perfect
// (40B; corpseid always 0 since corpses never spawn on the Legends
- // wire; community-corroborated 2026-07-09). The 0x295d subcode
+ // wire; community-corroborated community wire). The subcode
// channel is registered separately as OP_DeathEQL.
m_packet->connect2("OP_Death", SP_Zone, DIR_Server,
"newCorpseStruct", SZC_Match,
@@ -1025,7 +1025,7 @@
m_packet->connect2("OP_Death", SP_Zone, DIR_Server,
"newCorpseStruct", SZC_Match,
m_spawnShell, SLOT(killSpawn(const uint8_t*)));
- // EQL: report-only decoder for the 0x295d subcode channel (both dirs
+ // EQL: report-only decoder for the subcode channel (both dirs
// so the c2s 2/9/25B classes are consumed without dispatcher warnings).
m_packet->connect2("OP_DeathEQL", SP_Zone, DIR_Server|DIR_Client,
"deathRecordEQLStruct", SZC_None,
@@ -1044,7 +1044,7 @@
m_packet->connect2("OP_LootDrops", SP_Zone, DIR_Server,
"uint8_t", SZC_None,
m_messageShell, SLOT(lootDrops(const uint8_t*, size_t, uint8_t)));
- // EQL: live money-total update (0x5203); print PlayerMoney on change
+ // EQL: live money-total update; print PlayerMoney on change
m_packet->connect2("OP_MoneyUpdateEQL", SP_Zone, DIR_Server,
"uint8_t", SZC_None,
m_messageShell, SLOT(moneyUpdateEQL(const uint8_t*, size_t, uint8_t)));
@@ -1064,7 +1064,7 @@
m_packet->connect2("OP_Consider", SP_Zone, DIR_Server|DIR_Client,
"considerStruct", SZC_Match,
m_spawnShell, SLOT(consMessage(const uint8_t*, size_t, uint8_t)));
- // EQ Legends 2026-07-07: opcode 0x2735 is a stat tick channel, not
+ // EQ Legends: this opcode is a stat tick channel, not
// movement (xml name OP_SpawnStatEQL); movement rides OP_MobUpdate.
// The old npcMoveUpdate binding is disabled - its 13-24 byte accept
// window decoded the 21B stat ticks as coordinates and leaked
@@ -1072,7 +1072,7 @@
// m_packet->connect2("OP_NpcMoveUpdate", SP_Zone, DIR_Server,
// "uint8_t", SZC_None,
// m_spawnShell, SLOT(npcMoveUpdate(const uint8_t*, size_t, uint8_t)));
- // EQ Legends 2026-07-08: OP_NpcMoveUpdate relocated to 0x7352 - the
+ // EQ Legends: OP_NpcMoveUpdate relocated - the
// live-rate NPC pathing channel lost in the 07/07 rotation. EQL bit
// layout differs from stock (sign+magnitude coords, pad word), so a
// dedicated decoder consumes it (SpawnShell::npcMoveUpdateEQL).
@@ -1088,7 +1088,7 @@
m_packet->connect2("OP_HPUpdate", SP_Zone, DIR_Server,
"uint8_t", SZC_None,
m_spawnShell, SLOT(statUpdateEQL(const uint8_t*, size_t, uint8_t)));
- // EQL: 0x1bdc is stock OP_SpawnAppearance with a u32-widened struct
+ // EQL: stock OP_SpawnAppearance with a u32-widened struct
// (was misnamed OP_Consider, then OP_SpawnEventEQL); dedicated
// decoder, stock 8B spawnAppearanceStruct handler disabled.
m_packet->connect2("OP_SpawnAppearance", SP_Zone, DIR_Server|DIR_Client,
@@ -1097,7 +1097,7 @@
// EQ Legends: stock live handlers playerUpdate/playerUpdate2 pass
// deltas and animation from live struct layouts into updateSpawn.
// EQL 0x0b03 records do not use those layouts; when the
- // OP_ClientUpdate name moved to 0x0b03 these connects came alive
+ // OP_ClientUpdate name moved; these connects came alive
// and garbage deltas sent player dots racing north. Disconnected
// until an EQL delta source is wire-validated.
m_packet->connect2("OP_CorpseLocResponse", SP_Zone, DIR_Server,
@@ -1133,26 +1133,19 @@
m_packet->connect2("OP_CastSpell", SP_Zone, DIR_Server|DIR_Client,
"startCastStruct", SZC_Match,
m_spellShell, SLOT(selfStartSpellCast(const uint8_t*)));
- // 0x3ada is OP_Buff as a compact record (24B here); the shared buffChange
+ // OP_Buff as a compact record (24B here); the shared buffChange
// decoder consumes it (no size check - it also takes the 16B live form).
// The stock 168B buffStruct never occurs on this wire.
m_packet->connect2("OP_Buff", SP_Zone, DIR_Server,
"uint8_t", SZC_None,
m_spellShell, SLOT(buffChange(const uint8_t*, size_t, uint8_t)));
- // EQL: 0x77ae is the per-spawn buff LIST (authoritative; loads
- // active buffs at zone-in with real remaining durations).
+ // EQL: the per-spawn buff LIST (authoritative; loads active buffs at
+ // zone-in with real remaining durations). A single feed now carries
+ // every spawn population (self/pet/other), keyed by spawn id; the
+ // earlier split across separate ids was consolidated into this one.
m_packet->connect2("OP_BuffList", SP_Zone, DIR_Server,
"uint8_t", SZC_None,
m_spellShell, SLOT(buffList(const uint8_t*, size_t, uint8_t)));
- // EQL: Legends splits the buff-list feed across three ids by spawn
- // population (self/pet/other); identical format, same decoder.
- // Duplicate-name wiring follows the stock OP_Group*2 pattern.
- m_packet->connect2("OP_BuffList2", SP_Zone, DIR_Server,
- "uint8_t", SZC_None,
- m_spellShell, SLOT(buffList(const uint8_t*, size_t, uint8_t)));
- m_packet->connect2("OP_BuffList3", SP_Zone, DIR_Server,
- "uint8_t", SZC_None,
- m_spellShell, SLOT(buffList(const uint8_t*, size_t, uint8_t)));
m_packet->connect2("OP_Action", SP_Zone, DIR_Server|DIR_Client,
"actionStruct", SZC_Match,
m_spellShell, SLOT(action(const uint8_t*, size_t, uint8_t)));
@@ -1183,7 +1176,7 @@
m_packet->connect2("OP_ClientUpdate", SP_Zone, DIR_Client,
"uint8_t", SZC_None,
m_player, SLOT(playerSelfState(const uint8_t*, size_t, uint8_t)));
- // EQ Legends 2026-07-07: the c2s self-state moved to opcode 0x0201
+ // EQ Legends: the c2s self-state moved to a new opcode
// (xml name OP_SelfPosEQL); same handler, new opcode.
m_packet->connect2("OP_SelfPosEQL", SP_Zone, DIR_Client,
"uint8_t", SZC_None,
Modified: showeq/branches/showeqlegends/src/message.h
===================================================================
--- showeq/branches/showeqlegends/src/message.h 2026-07-24 20:43:30 UTC (rev 1681)
+++ showeq/branches/showeqlegends/src/message.h 2026-07-29 02:38:41 UTC (rev 1682)
@@ -69,7 +69,7 @@
MT_Hunt,
MT_Locate,
MT_ChatChannel, // EQL UCS: cross-zone/General channel chat (port 9877)
- MT_Loot, // EQL loot-and-sell notifications (OP_LootMessageEQL 0x32f7)
+ MT_Loot, // EQL loot-and-sell notifications (OP_LootMessageEQL)
MT_Max = MT_Loot,
};
Modified: showeq/branches/showeqlegends/src/messageshell.cpp
===================================================================
--- showeq/branches/showeqlegends/src/messageshell.cpp 2026-07-24 20:43:30 UTC (rev 1681)
+++ showeq/branches/showeqlegends/src/messageshell.cpp 2026-07-29 02:38:41 UTC (rev 1682)
@@ -547,7 +547,7 @@
}
-// EQL loot-and-sell notification (OP_LootMessageEQL 0x32f7):
+// EQL loot-and-sell notification (OP_LootMessageEQL):
// [u32 formatId][NUL-terminated preformatted text]
// formatId 286 = "You looted a <item> from a <corpse> and sold it for
// <money>." The item is an inline serialized link - a long uppercase-hex
@@ -604,7 +604,7 @@
m_messages->addMessage(MT_Loot, text);
}
-// EQL personal loot list (OP_LootDrops 0x5a29): the items YOU can loot
+// EQL personal loot list (OP_LootDrops): the items YOU can loot
// off a corpse (Legends forced personal loot). Layout:
// [2 bytes][u32][u32 corpseId @6][u32 count @10][corpse name NUL]
// then per-item fields. Each item name is a clean NUL-terminated
@@ -715,7 +715,7 @@
m_messages->addMessage(MT_Money, "Update");
}
-// EQL live money update (OP_MoneyUpdateEQL 0x5203): 16 bytes,
+// EQL live money update (OP_MoneyUpdateEQL): 16 bytes,
// [u32 platinum][u32 gold][u32 silver][u32 copper] = current totals on
// the player. Fires periodically; announce only when the totals change
// so the console money line tracks coin live (stock only sets it from
@@ -724,10 +724,11 @@
{
if (dir != DIR_Server || len < 16)
return;
- uint32_t p = eqtohuint32(data);
- uint32_t g = eqtohuint32(data + 4);
- uint32_t s = eqtohuint32(data + 8);
- uint32_t c = eqtohuint32(data + 12);
+ const moneyUpdateEQLStruct* m = (const moneyUpdateEQLStruct*)data;
+ uint32_t p = m->platinum;
+ uint32_t g = m->gold;
+ uint32_t s = m->silver;
+ uint32_t c = m->copper;
static uint32_t lp = ~0u, lg = ~0u, ls = ~0u, lc = ~0u;
if (p == lp && g == lg && s == ls && c == lc)
return;
Modified: showeq/branches/showeqlegends/src/packetformat.cpp
===================================================================
--- showeq/branches/showeqlegends/src/packetformat.cpp 2026-07-24 20:43:30 UTC (rev 1681)
+++ showeq/branches/showeqlegends/src/packetformat.cpp 2026-07-29 02:38:41 UTC (rev 1682)
@@ -29,17 +29,17 @@
#include <zlib.h>
// UPSTREAM: mechanism benefits live too; the floor bytes below are the
-// historically-observed values (the 0x88 example is a Legends id).
+// historically-observed low-byte seed values.
// Low bytes of the flag-split opcode channels (see packetformat.h). Seeded
// with the historically-observed bytes as a floor; EQPacket overwrites/adds
// to this from the loaded opcode map so it tracks OP_ClientUpdate's current
-// id automatically. 0x88 = OP_ClientUpdate 0x5188 (2026-07-14 rotation).
+// id automatically.
bool eqFlagSplitLowByte[256] = { false };
static struct EqFlagSplitInit {
EqFlagSplitInit() {
- eqFlagSplitLowByte[0x71] = true; // pre-2026-07 OP_ClientUpdate family
+ eqFlagSplitLowByte[0x71] = true; // pre-rotation OP_ClientUpdate family
eqFlagSplitLowByte[0x52] = true;
- eqFlagSplitLowByte[0x88] = true; // 2026-07-14 OP_ClientUpdate 0x5188
+ eqFlagSplitLowByte[0x88] = true; // a later OP_ClientUpdate rotation
}
} g_eqFlagSplitInit;
Modified: showeq/branches/showeqlegends/src/player.cpp
===================================================================
--- showeq/branches/showeqlegends/src/player.cpp 2026-07-24 20:43:30 UTC (rev 1681)
+++ showeq/branches/showeqlegends/src/player.cpp 2026-07-29 02:38:41 UTC (rev 1682)
@@ -1260,7 +1260,7 @@
void Player::playerSelfState(const uint8_t* data, size_t len, uint8_t dir)
{
/*
- EQ Legends OP_ClientUpdate (0x0b03) client direction: the 42 byte
+ EQ Legends OP_ClientUpdate client direction: the 42 byte
self-state struct. On the wire it is flag-split raw or zlib; the
packet layer reconstitutes the opcode and inflates, so the payload
arrives here aligned and plaintext (a 44 byte variant exists and its
@@ -1271,7 +1271,7 @@
same space the live self-update carries. Wire x/y transpose to map
y/x, the same convention as the keepalive path above.
*/
- // EQ Legends 2026-07-07: self-state moved to c2s opcode 0x0201 with
+ // EQ Legends: self-state moved to a c2s opcode with
// floats at offsets 0/4/8 in the order (map Y, map X, Z) - the same
// transposed convention as the spawn and MobUpdate paths. Verified
// against backfill position. Minimum observed length is 16 bytes of
@@ -1281,22 +1281,21 @@
// EQ Legends 2026-07-15: the real-time CLIENT self-state - a 38-byte
// float body carrying the player's own spawnId @2, the freshest
- // position, AND the only client-side facing. Wire-verified from
- // ShowEQ's delivered bytes (id matched, heading held constant on a
- // straight run then swept on a turn):
- // u16 ctr @0, u16 spawnId @2, float Z @10, float mapX @14,
- // u32 heading @18 (facing in the low 21 bits; full circle 2^21, so
- // (&0x1FFFFF)>>10 = EQ h2048), float mapY @26.
+ // position, AND the only client-side facing.
+ // 2026-07-28 patch: the 0728 client rotated the field offsets within
+ // the same 38-byte body (wire-verified from delivered bytes - @6/@34
+ // tracked movement smoothly, @14 became a ~0 velocity delta, @18 went
+ // to a constant 0, and @26 now carries the quiet-NaN facing):
+ // float Z @10, float mapX @6, float mapY @34,
+ // u32 heading @26 (facing in the low 21 bits; full circle 2^21, so
+ // (&0x1FFFFF)>>10 = EQ h2048).
+ // Old 0714 layout was mapX@14, heading@18, mapY@26.
// This supersedes the 1.5-2s-stale %17 flush below for freshness
- // (m_selfStateAge gate). If the dot lands mirrored, swap @14/@26.
+ // (m_selfStateAge gate). If the dot lands mirrored, swap @6/@34.
if (len == 38)
{
- float zz, mx, my;
- uint32_t hraw;
- memcpy(&zz, data + 10, 4);
- memcpy(&mx, data + 14, 4);
- memcpy(&hraw, data + 18, 4);
- memcpy(&my, data + 26, 4);
+ const eqlClientSelfPosStruct *sp = (const eqlClientSelfPosStruct*)data;
+ float zz = sp->z, mx = sp->x, my = sp->y;
if ((mx != 0.0f || my != 0.0f) &&
fabsf(mx) < 100000.0f && fabsf(my) < 100000.0f && fabsf(zz) < 10000.0f)
{
@@ -1311,7 +1310,7 @@
updateLastChanged();
emit changeItem(this, tSpawnChangedPosition);
}
- int h2048 = (hraw & 0x1FFFFF) >> 10;
+ int h2048 = sp->heading;
if (h2048 <= 2047)
{
setHeading(h2048, 0);
@@ -1385,7 +1384,7 @@
return;
}
- // The 42-byte self-state (0x7171 client dir; live analog of the
+ // The 42-byte self-state (client dir; live analog of the
// pre-patch 0x0b03 42B struct). Layout, wire-validated 2026-07-08
// against a controlled facing test and a tracked run:
// u32 seq @0, u16 zero @4, float @6 (turn-rate-like, unconsumed),
Modified: showeq/branches/showeqlegends/src/s_everquest.h
===================================================================
--- showeq/branches/showeqlegends/src/s_everquest.h 2026-07-24 20:43:30 UTC (rev 1681)
+++ showeq/branches/showeqlegends/src/s_everquest.h 2026-07-29 02:38:41 UTC (rev 1682)
@@ -30,6 +30,7 @@
AddStruct(ClientZoneEntryStruct);
AddStruct(newZoneStruct);
AddStruct(dzSwitchInfo);
+AddStruct(dzSwitchInfoEQLStruct);
AddStruct(dzInfo);
AddStruct(playerProfileStruct);
AddStruct(charProfileStruct);
@@ -110,6 +111,7 @@
AddStruct(randomStruct);
AddStruct(playerSpawnPosStruct);
AddStruct(playerSelfPosStruct);
+AddStruct(eqlClientSelfPosStruct);
AddStruct(spawnAppearanceStruct);
AddStruct(bindWoundStruct);
AddStruct(inspectedStruct);
@@ -118,6 +120,7 @@
AddStruct(newGuildInZoneStruct);
AddStruct(guildsInZoneListStruct);
AddStruct(moneyUpdateStruct);
+AddStruct(moneyUpdateEQLStruct);
AddStruct(memorizeSlotStruct);
AddStruct(cRunToggleStruct);
AddStruct(cChatFiltersStruct);
Modified: showeq/branches/showeqlegends/src/spawnshell.cpp
===================================================================
--- showeq/branches/showeqlegends/src/spawnshell.cpp 2026-07-24 20:43:30 UTC (rev 1681)
+++ showeq/branches/showeqlegends/src/spawnshell.cpp 2026-07-29 02:38:41 UTC (rev 1682)
@@ -826,15 +826,12 @@
// 2026-07-07 EQL patch inserted 8 bytes here, between the equipment
// block and the position words (content unidentified - possibly the
// relocated heading/animation fields; word values 0x6000/0 observed).
- netStream.skipBytes(8);
-
- // EQ Legends posData is 4 words (z/x/y coord words + the 0x6000
- // heading word); live's 5th word does not exist on this wire -
- // it was previously absorbing the first 4 bytes of the string
- // block, truncating titles (the first 4 title chars were lost).
- for (int i = 0; i < 4; ++i)
+ // One leadi...
[truncated message content] |
|
From: <xer...@us...> - 2026-07-24 20:43:32
|
Revision: 1681
http://sourceforge.net/p/seq/svn/1681
Author: xerxes00
Date: 2026-07-24 20:43:30 +0000 (Fri, 24 Jul 2026)
Log Message:
-----------
Document capture/map/log settings in seqdef.xml; fix EQDateTimeFormat typo
Add CaptureBufferSize, CaptureSnapLen, MapDir, LogWorldPackets,
WorldLogFilename and DBStrFile to seqdef.xml with their existing code
defaults, so they appear in --dump-config and can be edited without
knowing the internal name. No behavior change.
Fix a misspelled preference key in MessageWindow: it read
"EQDateTimeForamt" while Terminal reads/writes "EQDateTimeFormat", so
the message window never picked up the configured format.
Modified Paths:
--------------
showeq/branches/xerxes-trunk-dev/conf/seqdef.xml
showeq/branches/xerxes-trunk-dev/src/messagewindow.cpp
Modified: showeq/branches/xerxes-trunk-dev/conf/seqdef.xml
===================================================================
--- showeq/branches/xerxes-trunk-dev/conf/seqdef.xml 2026-07-24 20:22:38 UTC (rev 1680)
+++ showeq/branches/xerxes-trunk-dev/conf/seqdef.xml 2026-07-24 20:43:30 UTC (rev 1681)
@@ -9,6 +9,14 @@
<seqpreferences version="1.0" >
<!-- ============================================================= -->
<section name="Network" >
+ <property name="CaptureBufferSize" >
+ <int value="4" />
+ <comment>libpcap capture buffer size in MB</comment>
+ </property>
+ <property name="CaptureSnapLen" >
+ <int value="2" />
+ <comment>libpcap per-packet snap length in KB (value is multiplied by 1024)</comment>
+ </property>
<property name="Device" >
<string value="eth0" />
<comment>your network device</comment>
@@ -560,6 +568,10 @@
<!-- ============================================================= -->
<!-- Map Manager Options -->
<section name="MapMgr" >
+ <property name="MapDir" >
+ <string value="" />
+ <comment>Shared maps directory (also settable with --maps-dir); empty = use the user/pkg maps dir</comment>
+ </property>
<property name="DefaultLineColor" >
<color name="gray" />
<comment>Default color for new lines</comment>
@@ -2110,6 +2122,14 @@
<!-- ============================================================= -->
<!-- Packet Logging options -->
<section name="PacketLogging" >
+ <property name="LogWorldPackets" >
+ <bool value="false" />
+ <comment>Log world packets to the world log file</comment>
+ </property>
+ <property name="WorldLogFilename" >
+ <string value="world.log" />
+ <comment>Filename for world packet logging</comment>
+ </property>
<property name="LogAllPackets" >
<bool value="false" />
<comment>Log al packets to global log file</comment>
@@ -2351,6 +2371,10 @@
<!-- ============================================================= -->
<!-- Options to Control the User Interface -->
<section name="Interface" >
+ <property name="DBStrFile" >
+ <string value="dbstr_us.txt" />
+ <comment>String database file (AA titles, etc.); required for AA ability names</comment>
+ </property>
<property name="Caption" >
<string value="ShowEQ - Main (ctrl+alt+t to toggle menubar)" />
<comment>Window caption</comment>
Modified: showeq/branches/xerxes-trunk-dev/src/messagewindow.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/messagewindow.cpp 2026-07-24 20:22:38 UTC (rev 1680)
+++ showeq/branches/xerxes-trunk-dev/src/messagewindow.cpp 2026-07-24 20:43:30 UTC (rev 1681)
@@ -498,7 +498,7 @@
m_dateTimeFormat = pSEQPrefs->getPrefString("DateTimeFormat",
preferenceName(),
m_dateTimeFormat);
- m_eqDateTimeFormat = pSEQPrefs->getPrefString("EQDateTimeForamt",
+ m_eqDateTimeFormat = pSEQPrefs->getPrefString("EQDateTimeFormat",
preferenceName(),
m_eqDateTimeFormat);
m_displayType = pSEQPrefs->getPrefBool("DisplayType", preferenceName(),
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <xer...@us...> - 2026-07-24 20:22:40
|
Revision: 1680
http://sourceforge.net/p/seq/svn/1680
Author: xerxes00
Date: 2026-07-24 20:22:38 +0000 (Fri, 24 Jul 2026)
Log Message:
-----------
Add command-line options to select data locations for multiple configs
ShowEQ locates its user config, opcode files, EQ client data files, and
maps relative to a single per-user directory ($HOME/.showeq). Running
several independent configurations (e.g. one per server or game version)
therefore meant juggling $HOME, which also affects everything else the
process resolves from home. These options let a location be chosen
explicitly instead, so multiple configs can coexist from one install
without repurposing $HOME.
New options:
--user-dir=DIR Use DIR as the per-user data directory instead of
$HOME/.showeq (resolved before the config file is
read, so it must be scanned ahead of getopt_long).
--game-files=DIR Read spells_us.txt, dbstr_us.txt and eqstr_us.txt
from DIR (these differ per game version).
--maps-dir=DIR Shared maps directory, searched (read and write)
ahead of the user/pkg maps dirs. Also settable as
the MapDir preference under [MapMgr].
--zone-opcodes=FILE Override the zone/world opcodes file for this run.
--world-opcodes=FILE
--dump-config Print every effective preference with its value and
source (cmdline/user/default), then exit.
DataLocationMgr::findExistingFile() now uses an absolute filename as-is,
so any file preference (SpellsFile, ZoneOPCodes, ...) can point at a file
shared between configurations rather than copied into each user dir.
Command-line overrides are applied as Runtime prefs (not written back to
showeq.xml). At startup a short summary of any active location overrides
is printed. All options are optional; with none given, behavior is
unchanged. Documented in --help, showeq.1, and README.
Modified Paths:
--------------
showeq/branches/xerxes-trunk-dev/README
showeq/branches/xerxes-trunk-dev/showeq.1
showeq/branches/xerxes-trunk-dev/src/datalocationmgr.cpp
showeq/branches/xerxes-trunk-dev/src/datalocationmgr.h
showeq/branches/xerxes-trunk-dev/src/main.cpp
showeq/branches/xerxes-trunk-dev/src/xmlpreferences.cpp
showeq/branches/xerxes-trunk-dev/src/xmlpreferences.h
Modified: showeq/branches/xerxes-trunk-dev/README
===================================================================
--- showeq/branches/xerxes-trunk-dev/README 2026-07-24 02:40:56 UTC (rev 1679)
+++ showeq/branches/xerxes-trunk-dev/README 2026-07-24 20:22:38 UTC (rev 1680)
@@ -52,6 +52,41 @@
The file name each feature looks for can be overridden in the config
(e.g. the DBStrFile setting under [Interface]).
+CHOOSING DATA LOCATIONS
+-----------------------
+Several command-line options select where ShowEQ reads and writes, which
+makes it easy to run more than one independent configuration (for example
+one per server or game version) from a single install:
+
+ --user-dir=DIR Use DIR as the per-user data directory (config, logs,
+ opcodes, filters) instead of $HOME/.showeq. Each config
+ gets its own DIR; nothing is shared through $HOME.
+ --game-files=DIR Read the EverQuest client data files (spells_us.txt,
+ dbstr_us.txt, eqstr_us.txt) from DIR. These differ per
+ game version, so a directory per version keeps one copy
+ of each rather than duplicating them.
+ --maps-dir=DIR Use DIR as a shared maps directory (maps are usually the
+ same across configurations). Also settable as the MapDir
+ preference under [MapMgr]; the option overrides the pref.
+ --zone-opcodes=FILE Override the zone opcodes file for this run. FILE may be
+ --world-opcodes=FILE a bare name (found in the data dir) or an absolute path
+ to a file shared elsewhere on disk.
+
+Any file preference (SpellsFile, DBStrFile, ZoneOPCodes, ...) may be set to
+an absolute path in the config, in which case that exact file is used - a
+convenient way to share one copy of a data file between configurations
+instead of copying it into each user directory.
+
+Command-line overrides apply for that run only and are not written back to
+your config file. Preference lookup order is: command line, then your config
+file (showeq.xml), then the installed defaults (seqdef.xml). Run
+
+ ./showeq --dump-config
+
+to print every effective preference, its value, and which of those three it
+comes from - useful for seeing settings that are supplied by the defaults
+file rather than your own config.
+
ALTERNATE ADVANCEMENT WINDOW
----------------------------
View -> Alternate Advancement lists the AAs your character owns, with the
Modified: showeq/branches/xerxes-trunk-dev/showeq.1
===================================================================
--- showeq/branches/xerxes-trunk-dev/showeq.1 2026-07-24 02:40:56 UTC (rev 1679)
+++ showeq/branches/xerxes-trunk-dev/showeq.1 2026-07-24 20:22:38 UTC (rev 1680)
@@ -17,9 +17,40 @@
.PP
.SH "OPTIONS"
.PP
-.IP "\fB-i <device>\fP"
+.IP "\fB-i <device>\fP"
Specify which network device to bind to
-.IP
+.IP
+.IP "\fB--user-dir <dir>\fP"
+Use <dir> as the user data directory (per-user config, logs, maps and
+filters) instead of the default ~/.showeq. Lets several independent
+ShowEQ configurations coexist without relying on $HOME.
+.IP
+.IP "\fB--zone-opcodes <file>\fP"
+Use <file> as the zone opcodes file for this session, overriding the
+ZoneOPCodes preference. <file> may be a bare name (resolved in the data
+directory) or an absolute path to a file shared elsewhere on disk.
+.IP
+.IP "\fB--world-opcodes <file>\fP"
+As --zone-opcodes, but for the world opcodes file (WorldOPCodes).
+.IP
+.IP "\fB--game-files <dir>\fP"
+Read the EverQuest client data files (spells_us.txt, dbstr_us.txt and
+eqstr_us.txt) from <dir>. These differ per game version, so a directory
+per version keeps one copy of each instead of duplicating them in every
+user directory.
+.IP
+.IP "\fB--maps-dir <dir>\fP"
+Use <dir> as a shared maps directory, searched (for both reading and
+writing) ahead of the per-user and package maps directories. Lets a
+single set of maps be shared between ShowEQ configurations. Overrides the
+MapDir preference (section MapMgr) if both are given.
+.IP
+.IP "\fB--dump-config\fP"
+Print every effective preference, its value, and the tier it comes from
+(cmdline / user / default), then exit. Command-line overrides are applied
+first, so this shows exactly what ShowEQ would use. Useful for seeing
+settings that come from the defaults file rather than your own config.
+.IP
.IP "\fB-b\fP"
Broken Decode option to skip spawn packet handling
when Verant changes the coding.
Modified: showeq/branches/xerxes-trunk-dev/src/datalocationmgr.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/datalocationmgr.cpp 2026-07-24 02:40:56 UTC (rev 1679)
+++ showeq/branches/xerxes-trunk-dev/src/datalocationmgr.cpp 2026-07-24 20:22:38 UTC (rev 1680)
@@ -30,13 +30,19 @@
#include <QFileInfo>
#include <QRegExp>
-DataLocationMgr::DataLocationMgr(const QString& homeSubDir)
+DataLocationMgr::DataLocationMgr(const QString& homeSubDir,
+ const QString& configDir)
{
// create package directory object
m_pkgData = PKGDATADIR;
- // create the user directory object
- m_userData = QDir::homePath() + "/" + homeSubDir;
+ // create the user directory object; an explicit user directory (from the
+ // --user-dir command-line option) is used verbatim, otherwise it defaults
+ // to homeSubDir under the user's home directory (i.e. ~/.showeq).
+ if (!configDir.isEmpty())
+ m_userData = configDir;
+ else
+ m_userData = QDir::homePath() + "/" + homeSubDir;
}
DataLocationMgr::~DataLocationMgr()
@@ -63,11 +69,32 @@
return true;
}
-QFileInfo DataLocationMgr::findExistingFile(const QString& subdir,
+QFileInfo DataLocationMgr::findExistingFile(const QString& subdir,
const QString& filename,
- bool caseSensitive,
+ bool caseSensitive,
bool preferUser) const
{
+ // an absolute filename is used as-is, bypassing the data-directory search.
+ // This lets a preference or command-line option (e.g. --zone-opcodes) point
+ // at a file anywhere on disk, including one shared between several ShowEQ
+ // configurations rather than copied into each user directory.
+ if (QDir::isAbsolutePath(filename))
+ return QFileInfo(filename);
+
+ // a shared maps directory (--maps-dir), when set, is searched ahead of the
+ // per-user/pkg maps dirs; fall through to the normal search if the file is
+ // not found there.
+ if (subdir == "maps" && !m_mapsDir.isEmpty())
+ {
+ QDir mapsDir(m_mapsDir);
+ if (mapsDir.exists())
+ {
+ QFileInfo fi = findFile(mapsDir, filename, caseSensitive, false);
+ if (fi.exists())
+ return fi;
+ }
+ }
+
// find the file using the preferred search ordering
if (preferUser)
return findFile(m_userData, m_pkgData, subdir, filename, caseSensitive);
@@ -80,6 +107,14 @@
bool caseSensitive,
bool preferUser) const
{
+ // writes to the maps subdir go to the shared maps directory when one is set
+ if (subdir == "maps" && !m_mapsDir.isEmpty())
+ {
+ QFileInfo mapsDirInfo(m_mapsDir);
+ if (mapsDirInfo.exists() && mapsDirInfo.isDir() && mapsDirInfo.isWritable())
+ return QFileInfo(QDir(m_mapsDir), filename);
+ }
+
if (preferUser)
return findWriteFile(m_userData, m_pkgData, subdir, filename,
caseSensitive);
Modified: showeq/branches/xerxes-trunk-dev/src/datalocationmgr.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/datalocationmgr.h 2026-07-24 02:40:56 UTC (rev 1679)
+++ showeq/branches/xerxes-trunk-dev/src/datalocationmgr.h 2026-07-24 20:22:38 UTC (rev 1680)
@@ -31,9 +31,14 @@
class DataLocationMgr
{
public:
- DataLocationMgr(const QString& homeSubDir);
+ DataLocationMgr(const QString& homeSubDir, const QString& configDir = QString());
~DataLocationMgr();
bool setupUserDirectory();
+
+ // Set a shared maps directory (from --maps-dir). When set, map files are
+ // looked up here ahead of the per-user/pkg maps dirs, so a single copy of
+ // the maps can be shared between ShowEQ configurations.
+ void setMapsDir(const QString& dir) { m_mapsDir = dir; }
QFileInfo findExistingFile(const QString& subdir, const QString& filename,
bool caseSensitive = false,
bool preferUser = true) const;
@@ -57,6 +62,7 @@
QString m_pkgData;
QString m_userData;
+ QString m_mapsDir;
};
#endif // _DATALOCATIONMGR_H_
Modified: showeq/branches/xerxes-trunk-dev/src/main.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/main.cpp 2026-07-24 02:40:56 UTC (rev 1679)
+++ showeq/branches/xerxes-trunk-dev/src/main.cpp 2026-07-24 20:22:38 UTC (rev 1680)
@@ -99,6 +99,12 @@
#define RESTORE_ZONE_STATE 7
#define RESTORE_SPAWNS 8
#define RESTORE_ALL 9
+#define USER_DIR_OPTION 128
+#define ZONE_OPCODES_OPTION 129
+#define WORLD_OPCODES_OPTION 130
+#define GAME_FILES_OPTION 131
+#define MAPS_DIR_OPTION 132
+#define DUMP_CONFIG_OPTION 133
/* Note that ASCII 32 is a space, best to stop at 31 and pick up again
at 128 or higher
@@ -142,6 +148,12 @@
{"default-class", required_argument, NULL, 'Z'},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'v'},
+ {"user-dir", required_argument, NULL, USER_DIR_OPTION},
+ {"zone-opcodes", required_argument, NULL, ZONE_OPCODES_OPTION},
+ {"world-opcodes", required_argument, NULL, WORLD_OPCODES_OPTION},
+ {"game-files", required_argument, NULL, GAME_FILES_OPTION},
+ {"maps-dir", required_argument, NULL, MAPS_DIR_OPTION},
+ {"dump-config", no_argument, NULL, DUMP_CONFIG_OPTION},
{"ip-address", required_argument, NULL, IPADDR_OPTION},
{"mac-address", required_argument, NULL, MACADDR_OPTION},
{"global-log-filename", required_argument, NULL, GLOBAL_LOG_FILENAME_OPTION},
@@ -183,6 +195,7 @@
int option_index = 0;
bool bOptionHelp = false;
+ bool bDumpConfig = false;
#undef OFFTEST
#ifdef OFFTEST
@@ -207,9 +220,31 @@
/* Print the version number */
displayVersion();
- // create the data location manager (with user data under ~/.showeq
- DataLocationMgr dataLocMgr(".showeq");
+ // --user-dir must be resolved before the DataLocationMgr is constructed,
+ // because the configuration file is read (below) before getopt_long()
+ // parses the command line further down. Do a minimal early scan for it
+ // here; the option is still listed in option_list so it is accepted (as a
+ // no-op) by the main parse loop and appears in the usage output.
+ QString userDir;
+ // additional data-location overrides captured during option parsing, for
+ // the startup summary printed once the command line has been processed
+ QString mapsDirOverride, gameFilesOverride, zoneOpcodesOverride, worldOpcodesOverride;
+ for (int i = 1; i < argc; i++)
+ {
+ const QString arg = QString::fromLocal8Bit(argv[i]);
+ if (arg == "--user-dir")
+ {
+ if (i + 1 < argc)
+ userDir = QString::fromLocal8Bit(argv[++i]);
+ }
+ else if (arg.startsWith("--user-dir="))
+ userDir = arg.section('=', 1);
+ }
+ // create the data location manager (with user data under ~/.showeq,
+ // unless --user-dir provided an explicit directory)
+ DataLocationMgr dataLocMgr(".showeq", userDir);
+
// setup the user directory
dataLocMgr.setupUserDirectory();
@@ -292,6 +327,14 @@
showeq_params->saveRestoreBaseFilename = dataLocMgr.findWriteFile("tmp", pSEQPrefs->getPrefString("BaseFilename", section, "last")).absoluteFilePath();
showeq_params->filterZoneDataLog = 0;
+ // a shared maps directory may be set in the config file (MapDir); the
+ // --maps-dir command-line option below overrides it.
+ {
+ QString mapDirPref = pSEQPrefs->getPrefString("MapDir", "MapMgr", "");
+ if (!mapDirPref.isEmpty())
+ dataLocMgr.setMapsDir(mapDirPref);
+ }
+
/* Parse the commandline for commandline parameters */
while ((opt = getopt_long( argc,
argv,
@@ -716,6 +759,53 @@
}
+ /* Override the zone/world opcode file for this session. The value may
+ be a bare filename (resolved in the user/pkg data dir) or an
+ absolute path to a file anywhere on disk. */
+ case ZONE_OPCODES_OPTION:
+ zoneOpcodesOverride = QString::fromLocal8Bit(optarg);
+ pSEQPrefs->setPrefString("ZoneOPCodes", "Network", optarg,
+ XMLPreferences::Runtime);
+ break;
+
+ case WORLD_OPCODES_OPTION:
+ worldOpcodesOverride = QString::fromLocal8Bit(optarg);
+ pSEQPrefs->setPrefString("WorldOPCodes", "Network", optarg,
+ XMLPreferences::Runtime);
+ break;
+
+ /* --game-files DIR: point the EQ client data files (spells, dbstr,
+ eqstr) at DIR. These differ per game version, so grouping them under
+ one directory keeps a single copy per version. */
+ case GAME_FILES_OPTION:
+ gameFilesOverride = QString::fromLocal8Bit(optarg);
+ pSEQPrefs->setPrefString("SpellsFile", "Interface",
+ QString::fromLocal8Bit(optarg) + "/spells_us.txt",
+ XMLPreferences::Runtime);
+ pSEQPrefs->setPrefString("DBStrFile", "Interface",
+ QString::fromLocal8Bit(optarg) + "/dbstr_us.txt",
+ XMLPreferences::Runtime);
+ pSEQPrefs->setPrefString("FormatFile", "Interface",
+ QString::fromLocal8Bit(optarg) + "/eqstr_us.txt",
+ XMLPreferences::Runtime);
+ break;
+
+ /* --maps-dir DIR: use DIR as a shared maps directory, searched ahead
+ of the per-user/pkg maps dirs (maps are the same across versions). */
+ case MAPS_DIR_OPTION:
+ mapsDirOverride = QString::fromLocal8Bit(optarg);
+ dataLocMgr.setMapsDir(mapsDirOverride);
+ break;
+
+ /* Print the effective configuration and exit (handled after the loop) */
+ case DUMP_CONFIG_OPTION:
+ bDumpConfig = true;
+ break;
+
+ /* Handled by the early --user-dir pre-scan above; accept as no-op */
+ case USER_DIR_OPTION:
+ break;
+
/* Spit out the help */
case 'h': /* Fall through */
default:
@@ -732,6 +822,33 @@
exit (0);
}
+ // Report any data-location overrides given on the command line, so it is
+ // clear at startup which non-default directories/files are in use.
+ if (!userDir.isEmpty() || !gameFilesOverride.isEmpty() ||
+ !mapsDirOverride.isEmpty() || !zoneOpcodesOverride.isEmpty() ||
+ !worldOpcodesOverride.isEmpty())
+ {
+ printf("ShowEQ location overrides:\n");
+ if (!userDir.isEmpty())
+ printf(" user dir: %s\n", userDir.toLatin1().data());
+ if (!gameFilesOverride.isEmpty())
+ printf(" game files: %s\n", gameFilesOverride.toLatin1().data());
+ if (!mapsDirOverride.isEmpty())
+ printf(" maps dir: %s\n", mapsDirOverride.toLatin1().data());
+ if (!zoneOpcodesOverride.isEmpty())
+ printf(" zone opcodes: %s\n", zoneOpcodesOverride.toLatin1().data());
+ if (!worldOpcodesOverride.isEmpty())
+ printf(" world opcodes: %s\n", worldOpcodesOverride.toLatin1().data());
+ }
+
+ // dump the effective configuration (with command-line overrides applied)
+ // and exit, for config discoverability.
+ if (bDumpConfig)
+ {
+ pSEQPrefs->dumpEffectivePreferences();
+ exit (0);
+ }
+
/* Set up individual files for logging selected packet types based on
a common filename base. The types to log were found by following
where pre_worked was a precondition for further analysis.
@@ -830,6 +947,18 @@
printf (" -h, --help Shows this help\n");
printf (" -o CONFIGFILE Alternate showeq.xml pathname\n");
+ printf (" --user-dir=DIR User data directory for config, logs,\n");
+ printf (" maps and filters (default: ~/.showeq)\n");
+ printf (" --zone-opcodes=FILE Use FILE as the zone opcodes file\n");
+ printf (" (bare name in the data dir, or abs path)\n");
+ printf (" --world-opcodes=FILE Use FILE as the world opcodes file\n");
+ printf (" (bare name in the data dir, or abs path)\n");
+ printf (" --game-files=DIR Read spells_us.txt, dbstr_us.txt and\n");
+ printf (" eqstr_us.txt from DIR (per game version)\n");
+ printf (" --maps-dir=DIR Shared maps directory, searched ahead of\n");
+ printf (" the user/pkg maps dirs\n");
+ printf (" --dump-config Print every effective preference and its\n");
+ printf (" source (cmdline/user/default), then exit\n");
printf (" -V, --version Prints ShowEQ version number\n");
printf (" -i, --net-interface=DEVICE Specify which network device to bind to\n");
printf (" -r, --realtime Set the network thread realtime\n");
Modified: showeq/branches/xerxes-trunk-dev/src/xmlpreferences.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/xmlpreferences.cpp 2026-07-24 02:40:56 UTC (rev 1679)
+++ showeq/branches/xerxes-trunk-dev/src/xmlpreferences.cpp 2026-07-24 20:22:38 UTC (rev 1680)
@@ -37,6 +37,7 @@
#include <QRegExp>
#include <QKeySequence>
#include <QTextStream>
+#include <QSet>
const float seqPrefVersion = 1.0;
@@ -148,7 +149,54 @@
loadPreferences(m_filename, m_userSections);
}
-void XMLPreferences::loadPreferences(const QString& filename,
+void XMLPreferences::dumpEffectivePreferences() const
+{
+ // union of all section names across the three tiers
+ QSet<QString> sectionSet;
+ for (PrefSectionDict::const_iterator it = m_runtimeSections.constBegin();
+ it != m_runtimeSections.constEnd(); ++it)
+ sectionSet.insert(it.key());
+ for (PrefSectionDict::const_iterator it = m_userSections.constBegin();
+ it != m_userSections.constEnd(); ++it)
+ sectionSet.insert(it.key());
+ for (PrefSectionDict::const_iterator it = m_defaultsSections.constBegin();
+ it != m_defaultsSections.constEnd(); ++it)
+ sectionSet.insert(it.key());
+
+ QStringList sections = sectionSet.values();
+ sections.sort();
+
+ printf("Effective ShowEQ preferences (name = value <- source):\n");
+ for (const QString& section : sections)
+ {
+ printf("[%s]\n", section.toLatin1().data());
+
+ PreferenceDict* rd = m_runtimeSections.value(section, nullptr);
+ PreferenceDict* ud = m_userSections.value(section, nullptr);
+ PreferenceDict* dd = m_defaultsSections.value(section, nullptr);
+
+ // union of preference names in this section across the three tiers
+ QSet<QString> nameSet;
+ if (rd) for (PreferenceDict::const_iterator it = rd->constBegin(); it != rd->constEnd(); ++it) nameSet.insert(it.key());
+ if (ud) for (PreferenceDict::const_iterator it = ud->constBegin(); it != ud->constEnd(); ++it) nameSet.insert(it.key());
+ if (dd) for (PreferenceDict::const_iterator it = dd->constBegin(); it != dd->constEnd(); ++it) nameSet.insert(it.key());
+
+ QStringList names = nameSet.values();
+ names.sort();
+ for (const QString& name : names)
+ {
+ QVariant* v = nullptr;
+ const char* src = "?";
+ if (rd && (v = rd->value(name, nullptr)) != nullptr) src = "cmdline";
+ else if (ud && (v = ud->value(name, nullptr)) != nullptr) src = "user";
+ else if (dd && (v = dd->value(name, nullptr)) != nullptr) src = "default";
+ printf(" %-32s = %-26s <- %s\n", name.toLatin1().data(),
+ v ? v->toString().toLatin1().data() : "", src);
+ }
+ }
+}
+
+void XMLPreferences::loadPreferences(const QString& filename,
PrefSectionDict& dict)
{
QDomDocument doc(seqPrefName);
Modified: showeq/branches/xerxes-trunk-dev/src/xmlpreferences.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/xmlpreferences.h 2026-07-24 02:40:56 UTC (rev 1679)
+++ showeq/branches/xerxes-trunk-dev/src/xmlpreferences.h 2026-07-24 20:22:38 UTC (rev 1680)
@@ -196,7 +196,12 @@
void save(); // Saves all modified values to the appropriate file
void load(); // Loads values from files into the appropriate caches
void revert(); // Reloads values from files into the appropriate caches
-
+
+ // print every effective preference (across all sections) to stdout, showing
+ // its value and which tier it comes from (cmdline / user / default). Used by
+ // the --dump-config command-line option for config discoverability.
+ void dumpEffectivePreferences() const;
+
protected:
// loads a preference dictionary from the specified filename
void loadPreferences(const QString& filename, PrefSectionDict& dict);
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <xer...@us...> - 2026-07-24 02:40:59
|
Revision: 1679
http://sourceforge.net/p/seq/svn/1679
Author: xerxes00
Date: 2026-07-24 02:40:56 +0000 (Fri, 24 Jul 2026)
Log Message:
-----------
Spell list: honor the NPC display-name option
- Caster/target columns now render through the NPC display-name option
(transformed/cleaned/unstripped), matching the spawn windows; refreshed
in place when the option changes.
- These are strings captured at cast time, so the transform still works
after the spawn despawns.
- Store the raw target name from the buff-list decode instead of
pre-stripping its digits, so the option can control it (default still
shows the clean name).
Depends on the buff-window commit (OP_BuffList/OP_PetBuffList decode);
merge after r1671/r1672.
Modified Paths:
--------------
showeq/branches/xerxes-trunk-dev/src/interface.cpp
showeq/branches/xerxes-trunk-dev/src/spelllist.cpp
showeq/branches/xerxes-trunk-dev/src/spelllist.h
showeq/branches/xerxes-trunk-dev/src/spellshell.cpp
Modified: showeq/branches/xerxes-trunk-dev/src/interface.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/interface.cpp 2026-07-24 02:39:26 UTC (rev 1678)
+++ showeq/branches/xerxes-trunk-dev/src/interface.cpp 2026-07-24 02:40:56 UTC (rev 1679)
@@ -4088,6 +4088,8 @@
m_spawnList2->refreshItemNames();
if (m_spawnPointList != 0)
m_spawnPointList->spawnPointList()->refresh();
+ if (m_spellList != 0)
+ m_spellList->spellList()->refreshItemNames();
}
void
Modified: showeq/branches/xerxes-trunk-dev/src/spelllist.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/spelllist.cpp 2026-07-24 02:39:26 UTC (rev 1678)
+++ showeq/branches/xerxes-trunk-dev/src/spelllist.cpp 2026-07-24 02:40:56 UTC (rev 1679)
@@ -30,9 +30,11 @@
#include <QLayout>
#include <QList>
#include <QMenu>
+#include <QScrollBar>
#include "spelllist.h"
#include "main.h"
+#include "spawn.h"
SpellListItem::SpellListItem(SEQListViewItem *parent) : SEQListViewItem(parent)
{
@@ -92,9 +94,14 @@
setText(SPELLCOL_SPELLID, QString("%1").arg(m_item->spellId()));
setText(SPELLCOL_SPELLNAME, m_item->spellName());
setText(SPELLCOL_CASTERID, QString("%1").arg(m_item->casterId()));
- setText(SPELLCOL_CASTERNAME, m_item->casterName());
+ // caster/target were captured as raw names at cast time; honor the NPC
+ // display-name option at render (still works after the spawn is gone,
+ // since these are stored strings, not live spawn lookups)
+ setText(SPELLCOL_CASTERNAME,
+ npcDisplayName(m_item->casterName(), showeq_params->npcNameDisplay));
setText(SPELLCOL_TARGETID, QString("%1").arg(m_item->targetId()));
- setText(SPELLCOL_TARGETNAME, m_item->targetName());
+ setText(SPELLCOL_TARGETNAME,
+ npcDisplayName(m_item->targetName(), showeq_params->npcNameDisplay));
setText(SPELLCOL_CASTTIME, m_item->castTimeStr());
setText(SPELLCOL_DURATION, m_item->durationStr());
}
@@ -308,6 +315,25 @@
}
}
+// Re-render every spell row so the caster/target columns pick up the current
+// NPC display-name option. In place (no rebuild), so selection is preserved.
+void SpellList::refreshItemNames()
+{
+ bool wasSorting = isSortingEnabled();
+ setSortingEnabled(false);
+ int scrollPos = verticalScrollBar()->value();
+
+ SEQListViewItemIterator it(this);
+ while (*it)
+ {
+ ((SpellListItem*)*it)->update();
+ ++it;
+ }
+
+ setSortingEnabled(wasSorting);
+ verticalScrollBar()->setValue(scrollPos);
+}
+
void SpellList::clear()
{
SEQListView::clear();
Modified: showeq/branches/xerxes-trunk-dev/src/spelllist.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/spelllist.h 2026-07-24 02:39:26 UTC (rev 1678)
+++ showeq/branches/xerxes-trunk-dev/src/spelllist.h 2026-07-24 02:40:56 UTC (rev 1679)
@@ -77,6 +77,7 @@
SpellListItem* Selected();
SpellListItem* InsertSpell(const SpellItem *item);
void DeleteItem(const SpellItem *item);
+ void refreshItemNames();
//SpellItem* AddCategory(QString& name, QColor color = Qt::black);
//void RemCategory(SpellListItem *);
//void clearCategories();
Modified: showeq/branches/xerxes-trunk-dev/src/spellshell.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/spellshell.cpp 2026-07-24 02:39:26 UTC (rev 1678)
+++ showeq/branches/xerxes-trunk-dev/src/spellshell.cpp 2026-07-24 02:40:56 UTC (rev 1679)
@@ -383,11 +383,9 @@
const Item* tgt = m_spawnShell->findID(tSpawn, spawnId);
if (!tgt)
return;
+ // store the raw name; the spell list applies the NPC display-name option
+ // (strip/transform) at render time
targetName = tgt->name();
- // Spawn names carry a numeric instance suffix (e.g. a trailing "00"); show
- // the clean name in the spell list.
- while (!targetName.isEmpty() && targetName.at(targetName.length() - 1).isDigit())
- targetName.chop(1);
}
size_t pos = 15;
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <xer...@us...> - 2026-07-24 02:39:28
|
Revision: 1678
http://sourceforge.net/p/seq/svn/1678
Author: xerxes00
Date: 2026-07-24 02:39:26 +0000 (Fri, 24 Jul 2026)
Log Message:
-----------
Add NPC display-name option; fix spawn-list self/pet/zone bugs
NPC Display Name feature
- New Options submenu "NPC Display Name" with a tri-state radio, saved as
[SpawnList] NpcNameDisplay; replaces the old showRealName bool:
- Transformed (default): stripped and article-reordered for name
sorting, e.g. "a_Giant_Spider00" -> "Giant Spider, a".
- Cleaned: digits and underscores removed, article left in place,
e.g. "a_Giant_Spider00" -> "a Giant Spider".
- Unstripped: the raw wire name, e.g. "a_Giant_Spider00".
- Shared name helpers in spawn.cpp (cleanSpawnName/transformSpawnName/
npcDisplayName) and Item::displayName(); applied at render across the
spawn list, spawn list 2, spawn point list (Name+Last), spawn detail
view, map hover tooltips, and the spawn message-log line. Toggling
refreshes the open windows in place (selection, scroll, sort, and tree
expansion preserved).
Display vs filters (decoupled)
- The option only changes the name TEXT shown in the windows. Filter
matching is unaffected: it still runs on filterString()/filterFlags()
(built from transformedName()), independent of the display option, so
the same spawns are caught/filtered/alerted regardless of which name
form is displayed. Map aggro and filter notifications likewise still
match on transformedName(). No filter files need to change.
Bug fixes surfaced along the way
- seqwindow: fall back to the default caption when the saved one is empty
(fixes the blank spawn point window title).
- spawnlist: fix the File -> Rebuild SpawnList menu action. It was
rebuilding the list but dropping most spawns and leaving every category
count at 0 (an internal category iterator wasn't being reset between
spawns). A rebuild now repopulates every category correctly.
- spawnlist: self-player now added to the tree (the shell announces it
via changeItem, not addItem).
- spawnlist: re-parent pets under an owner added after them (pets were
stranded at top level; fixes pet not nesting under the player).
- spawnlist clear(): preserve category expansion across a zone.
- seqdef.xml: SpawnPointList font matched to the spawn lists.
Modified Paths:
--------------
showeq/branches/xerxes-trunk-dev/conf/seqdef.xml
showeq/branches/xerxes-trunk-dev/src/interface.cpp
showeq/branches/xerxes-trunk-dev/src/interface.h
showeq/branches/xerxes-trunk-dev/src/main.cpp
showeq/branches/xerxes-trunk-dev/src/main.h
showeq/branches/xerxes-trunk-dev/src/map.cpp
showeq/branches/xerxes-trunk-dev/src/messageshell.cpp
showeq/branches/xerxes-trunk-dev/src/seqwindow.cpp
showeq/branches/xerxes-trunk-dev/src/showspawn.cpp
showeq/branches/xerxes-trunk-dev/src/spawn.cpp
showeq/branches/xerxes-trunk-dev/src/spawn.h
showeq/branches/xerxes-trunk-dev/src/spawnlist.cpp
showeq/branches/xerxes-trunk-dev/src/spawnlist.h
showeq/branches/xerxes-trunk-dev/src/spawnlist2.cpp
showeq/branches/xerxes-trunk-dev/src/spawnlist2.h
showeq/branches/xerxes-trunk-dev/src/spawnlistcommon.cpp
showeq/branches/xerxes-trunk-dev/src/spawnpointlist.cpp
Modified: showeq/branches/xerxes-trunk-dev/conf/seqdef.xml
===================================================================
--- showeq/branches/xerxes-trunk-dev/conf/seqdef.xml 2026-07-23 02:45:49 UTC (rev 1677)
+++ showeq/branches/xerxes-trunk-dev/conf/seqdef.xml 2026-07-24 02:39:26 UTC (rev 1678)
@@ -3185,7 +3185,7 @@
<!-- ============================================================= -->
<section name="SpawnPointList" >
<property name="Font" >
- <font family="Helvetica" bold="false" strikeout="false" underline="false" pointsize="8" italic="false" />
+ <font family="adobe-helvetica" bold="false" strikeout="false" underline="false" pointsize="8" italic="false" />
</property>
<property name="ShowCoord1" >
<bool value="true" />
Modified: showeq/branches/xerxes-trunk-dev/src/interface.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/interface.cpp 2026-07-23 02:45:49 UTC (rev 1677)
+++ showeq/branches/xerxes-trunk-dev/src/interface.cpp 2026-07-24 02:39:26 UTC (rev 1678)
@@ -1801,6 +1801,32 @@
m_action_opt_KeepSelectedVisible->setCheckable(true);
m_action_opt_KeepSelectedVisible->setChecked(showeq_params->keep_selected_visible);
+ // NPC Display Name submenu - how NPC names render in the spawn lists,
+ // spawn point list, and spawn detail view (does not affect filtering)
+ QMenu* pNpcNameMenu = pOptMenu->addMenu("NPC Display Name");
+ m_actionGroup_npcNameDisplay = new QActionGroup(this);
+ m_actionGroup_npcNameDisplay->setExclusive(true);
+ m_action_npcName_transformed = pNpcNameMenu->addAction("Show NPC Transformed Name");
+ m_action_npcName_transformed->setCheckable(true);
+ m_action_npcName_transformed->setData(tNpcNameTransformed);
+ m_actionGroup_npcNameDisplay->addAction(m_action_npcName_transformed);
+ m_action_npcName_cleaned = pNpcNameMenu->addAction("Show NPC Cleaned Name");
+ m_action_npcName_cleaned->setCheckable(true);
+ m_action_npcName_cleaned->setData(tNpcNameCleaned);
+ m_actionGroup_npcNameDisplay->addAction(m_action_npcName_cleaned);
+ m_action_npcName_unstripped = pNpcNameMenu->addAction("Show NPC Unstripped Name");
+ m_action_npcName_unstripped->setCheckable(true);
+ m_action_npcName_unstripped->setData(tNpcNameUnstripped);
+ m_actionGroup_npcNameDisplay->addAction(m_action_npcName_unstripped);
+ switch (showeq_params->npcNameDisplay)
+ {
+ case tNpcNameCleaned: m_action_npcName_cleaned->setChecked(true); break;
+ case tNpcNameUnstripped: m_action_npcName_unstripped->setChecked(true); break;
+ default: m_action_npcName_transformed->setChecked(true); break;
+ }
+ connect(m_actionGroup_npcNameDisplay, SIGNAL(triggered(QAction*)),
+ this, SLOT(select_opt_NpcNameDisplay(QAction*)));
+
m_action_opt_LogSpawns = pOptMenu->addAction("Log Spawns", this,
SLOT(toggle_opt_LogSpawns()));
m_action_opt_LogSpawns->setCheckable(true);
@@ -4043,6 +4069,28 @@
}
void
+EQInterface::select_opt_NpcNameDisplay (QAction* action)
+{
+ if (action == 0)
+ return;
+
+ showeq_params->npcNameDisplay = (uint8_t)action->data().toInt();
+ pSEQPrefs->setPrefInt("NpcNameDisplay", "SpawnList",
+ showeq_params->npcNameDisplay);
+
+ // redraw the affected windows so the new form shows immediately. The
+ // category tree is refreshed in place so it keeps its expansion state;
+ // spawn list 2 is a flat list (nothing to collapse) so it rebuilds; the
+ // point list re-runs its row update.
+ if (m_spawnList != 0)
+ m_spawnList->spawnList()->refreshItemNames();
+ if (m_spawnList2 != 0)
+ m_spawnList2->refreshItemNames();
+ if (m_spawnPointList != 0)
+ m_spawnPointList->spawnPointList()->refresh();
+}
+
+void
EQInterface::toggle_opt_UseUpdateRadius (void)
{
showeq_params->useUpdateRadius = !(showeq_params->useUpdateRadius);
Modified: showeq/branches/xerxes-trunk-dev/src/interface.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/interface.h 2026-07-23 02:45:49 UTC (rev 1677)
+++ showeq/branches/xerxes-trunk-dev/src/interface.h 2026-07-24 02:39:26 UTC (rev 1678)
@@ -263,6 +263,7 @@
void toggle_opt_TarSelect();
void toggle_opt_TarDeselect();
void toggle_opt_KeepSelectedVisible();
+ void select_opt_NpcNameDisplay(QAction* action);
void toggle_opt_LogSpawns();
void toggle_opt_PvPTeams();
void toggle_opt_PvPDeity();
@@ -506,6 +507,10 @@
QAction* m_action_opt_TarSelect;
QAction* m_action_opt_TarDeselect;
QAction* m_action_opt_KeepSelectedVisible;
+ QActionGroup* m_actionGroup_npcNameDisplay;
+ QAction* m_action_npcName_transformed;
+ QAction* m_action_npcName_cleaned;
+ QAction* m_action_npcName_unstripped;
QAction* m_action_opt_LogSpawns;
QAction* m_action_opt_PvPTeams;
QAction* m_action_opt_PvPDeity;
Modified: showeq/branches/xerxes-trunk-dev/src/main.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/main.cpp 2026-07-23 02:45:49 UTC (rev 1677)
+++ showeq/branches/xerxes-trunk-dev/src/main.cpp 2026-07-24 02:39:26 UTC (rev 1678)
@@ -274,7 +274,7 @@
/* Tells SEQ whether or not to display casting messages (Turn this off if you're on a big raid) */
section = "SpawnList";
- showeq_params->showRealName = pSEQPrefs->getPrefBool("ShowRealName", section, false);
+ showeq_params->npcNameDisplay = (uint8_t)pSEQPrefs->getPrefInt("NpcNameDisplay", section, tNpcNameTransformed);
/* Different files for different kinds of raw data */
Modified: showeq/branches/xerxes-trunk-dev/src/main.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/main.h 2026-07-23 02:45:49 UTC (rev 1677)
+++ showeq/branches/xerxes-trunk-dev/src/main.h 2026-07-24 02:39:26 UTC (rev 1678)
@@ -37,6 +37,15 @@
#include "config.h"
//----------------------------------------------------------------------
+// how NPC names are shown in the spawn/spawn-point lists and the spawn
+// detail view (does not affect filter matching)
+enum NpcNameDisplay
+{
+ tNpcNameTransformed = 0, // "giant spider, a" (stripped + article reordered)
+ tNpcNameCleaned = 1, // "a giant spider" (stripped, article in place)
+ tNpcNameUnstripped = 2, // "a_giant_spider15" (raw wire name)
+};
+
// bogus structure that should die soon
struct ShowEQParams
{
@@ -49,8 +58,8 @@
bool walkpathrecord;
uint32_t walkpathlength;
bool systime_spawntime;
- bool showRealName;
-
+ uint8_t npcNameDisplay; // one of enum NpcNameDisplay
+
bool saveZoneState;
bool savePlayerState;
bool saveSpawns;
Modified: showeq/branches/xerxes-trunk-dev/src/map.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/map.cpp 2026-07-23 02:45:49 UTC (rev 1677)
+++ showeq/branches/xerxes-trunk-dev/src/map.cpp 2026-07-24 02:39:26 UTC (rev 1678)
@@ -4421,12 +4421,12 @@
"%.3s/Z: %5d/%5d/%5d\n"
"Last: %s\n"
"Spawned: %s\t Remaining: %s\t Count: %d",
- sp->name().toLatin1().data(),
+ npcDisplayName(sp->name(), showeq_params->npcNameDisplay).toLatin1().data(),
showeq_params->retarded_coords ? "Y/X" : "X/Y",
showeq_params->retarded_coords ? sp->y() : sp->x(),
showeq_params->retarded_coords ? sp->x() : sp->y(),
sp->z(),
- sp->last().toLatin1().data(),
+ npcDisplayName(sp->last(), showeq_params->npcNameDisplay).toLatin1().data(),
spawned.toLatin1().data(),
remaining.toLatin1().data(),
sp->count());
@@ -4435,12 +4435,12 @@
"%.3s/Z: %5d/%5d/%5d\n"
"Last: %s\n"
"Spawned: %s\t Remaining: %s\t Count: %d",
- sp->name().toLatin1().data(),
+ npcDisplayName(sp->name(), showeq_params->npcNameDisplay).toLatin1().data(),
showeq_params->retarded_coords ? "Y/X" : "X/Y",
showeq_params->retarded_coords ? sp->y() : sp->x(),
showeq_params->retarded_coords ? sp->x() : sp->y(),
sp->z(),
- sp->last().toLatin1().data(),
+ npcDisplayName(sp->last(), showeq_params->npcNameDisplay).toLatin1().data(),
spawned.toLatin1().data(),
remaining.toLatin1().data(),
sp->count());
@@ -4502,7 +4502,7 @@
string = QString::asprintf("%s %s%s\n"
"Level: %2d\tHP: %s\t %.3s/Z: %5d/%5d/%5d\n"
"Race: %s\t Class: %s",
- spawn->transformedName().toUtf8().data(),
+ spawn->displayName().toUtf8().data(),
lastName.toUtf8().data(),
guild.toLatin1().data(),
spawn->level(), hp.toLatin1().data(),
@@ -4516,7 +4516,7 @@
string.sprintf("%s %s%s\n"
"Level: %2d\tHP: %s\t %.3s/Z: %5d/%5d/%5d\n"
"Race: %s\t Class: %s",
- spawn->transformedName().toUtf8().data(),
+ spawn->displayName().toUtf8().data(),
lastName.toUtf8().data(),
guild.toLatin1().data(),
spawn->level(), hp.toLatin1().data(),
@@ -4543,7 +4543,7 @@
string = QString::asprintf("%s\n"
"%.3s/Z: %5d/%5d/%5d\n"
"Race: %s\t Class: %s",
- item->transformedName().toUtf8().data(),
+ item->displayName().toUtf8().data(),
showeq_params->retarded_coords ? "Y/X" : "X/Y",
showeq_params->retarded_coords ? item->y() : item->x(),
showeq_params->retarded_coords ? item->x() : item->y(),
@@ -4554,7 +4554,7 @@
string.sprintf("%s\n"
"%.3s/Z: %5d/%5d/%5d\n"
"Race: %s\t Class: %s",
- item->transformedName().toUtf8().data(),
+ item->displayName().toUtf8().data(),
showeq_params->retarded_coords ? "Y/X" : "X/Y",
showeq_params->retarded_coords ? item->y() : item->x(),
showeq_params->retarded_coords ? item->x() : item->y(),
Modified: showeq/branches/xerxes-trunk-dev/src/messageshell.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/messageshell.cpp 2026-07-23 02:45:49 UTC (rev 1677)
+++ showeq/branches/xerxes-trunk-dev/src/messageshell.cpp 2026-07-24 02:39:26 UTC (rev 1678)
@@ -1661,7 +1661,7 @@
else
message = "%1: %2/%3/%4 at %6,%5,%7%8";
- m_messages->addMessage(type, message.arg(prefix).arg(item->transformedName())
+ m_messages->addMessage(type, message.arg(prefix).arg(item->displayName())
.arg(item->raceString()).arg(item->classString())
.arg(item->x()).arg(item->y()).arg(item->z())
.arg(spawnInfo));
Modified: showeq/branches/xerxes-trunk-dev/src/seqwindow.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/seqwindow.cpp 2026-07-23 02:45:49 UTC (rev 1677)
+++ showeq/branches/xerxes-trunk-dev/src/seqwindow.cpp 2026-07-24 02:39:26 UTC (rev 1678)
@@ -39,9 +39,13 @@
{
setObjectName(name);
setWindowFlags(f);
- // set the windows caption
- QDockWidget::setWindowTitle(pSEQPrefs->getPrefString("Caption", preferenceName(),
- caption));
+ // set the windows caption - fall back to the passed-in default when the
+ // saved caption is empty (an empty stored value should never blank the title)
+ QString savedCaption = pSEQPrefs->getPrefString("Caption", preferenceName(),
+ caption);
+ if (savedCaption.isEmpty())
+ savedCaption = caption;
+ QDockWidget::setWindowTitle(savedCaption);
setFeatures(QDockWidget::DockWidgetClosable |
QDockWidget::DockWidgetMovable |
Modified: showeq/branches/xerxes-trunk-dev/src/showspawn.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/showspawn.cpp 2026-07-23 02:45:49 UTC (rev 1677)
+++ showeq/branches/xerxes-trunk-dev/src/showspawn.cpp 2026-07-24 02:39:26 UTC (rev 1678)
@@ -245,7 +245,7 @@
// display the data
out << "<H1>" << spawn.realName() << "</H1>\n";
- out << "<P>ShortName: " << spawn.transformedName() << "<BR>\n";
+ out << "<P>ShortName: " << spawn.displayName() << "<BR>\n";
out << "Level: " << spawn.level() << "<BR>\n";
out << "HP: " << spawn.HP() << "/"
<< spawn.maxHP() << "<BR>\n";
Modified: showeq/branches/xerxes-trunk-dev/src/spawn.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/spawn.cpp 2026-07-23 02:45:49 UTC (rev 1677)
+++ showeq/branches/xerxes-trunk-dev/src/spawn.cpp 2026-07-24 02:39:26 UTC (rev 1678)
@@ -42,6 +42,7 @@
#include "spawnshell.h"
#include "fixpt.h"
#include "util.h"
+#include "main.h"
//----------------------------------------------------------------------
// constants
@@ -272,6 +273,74 @@
return name();
}
+QString Item::cleanedName() const
+{
+ return name();
+}
+
+QString Item::displayName() const
+{
+ // resolve through the virtual name methods so spawns get their stripped
+ // forms while non-spawn items (doors/drops) stay on their raw name
+ switch (showeq_params->npcNameDisplay)
+ {
+ case tNpcNameCleaned: return cleanedName();
+ case tNpcNameUnstripped: return name();
+ default: return transformedName();
+ }
+}
+
+//----------------------------------------------------------------------
+// NPC name normalization helpers (see spawn.h)
+QString cleanSpawnName(const QString& rawName)
+{
+ QString newName = rawName;
+ newName.replace(QRegExp("[0-9]"), "");
+ newName.replace(QRegExp("_"), " ");
+ return newName;
+}
+
+QString transformSpawnName(const QString& rawName)
+{
+ QString temp = cleanSpawnName(rawName);
+ QString article;
+
+ if (temp.startsWith("a "))
+ {
+ temp = temp.mid(2);
+ article = "a";
+ }
+ else if (temp.startsWith("an "))
+ {
+ temp = temp.mid(3);
+ article = "an";
+ }
+ else if (temp.startsWith("the "))
+ {
+ temp = temp.mid(4);
+ article = "the";
+ }
+
+ if (!article.isEmpty())
+ {
+ temp += ", ";
+ temp += article;
+ }
+
+ return temp;
+}
+
+QString npcDisplayName(const QString& rawName, int mode)
+{
+ // mode values mirror enum NpcNameDisplay in main.h
+ switch (mode)
+ {
+ case 1: return cleanSpawnName(rawName); // tNpcNameCleaned
+ case 2: return rawName; // tNpcNameUnstripped
+ default: return transformSpawnName(rawName); // tNpcNameTransformed
+ }
+}
+
uint16_t Item::race() const
{
return 0;
@@ -833,40 +902,12 @@
QString Spawn::cleanedName() const
{
- QString newName = name();
- newName.replace(QRegExp("[0-9]"), "");
- newName.replace(QRegExp("_"), " ");
- return newName;
+ return cleanSpawnName(name());
}
QString Spawn::transformedName() const
{
- QString temp = cleanedName();
- QString article;
-
- if (temp.startsWith( "a " ))
- {
- temp = temp.mid( 2 );
- article = "a";
- }
- else if (temp.startsWith( "an " ))
- {
- temp = temp.mid( 3 );
- article = "an";
- }
- else if (temp.startsWith( "the " ))
- {
- temp = temp.mid( 4 );
- article = "the";
- }
-
- if (!article.isEmpty())
- {
- temp += ", ";
- temp += article;
- }
-
- return temp;
+ return transformSpawnName(name());
}
uint16_t Spawn::race() const
Modified: showeq/branches/xerxes-trunk-dev/src/spawn.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/spawn.h 2026-07-23 02:45:49 UTC (rev 1677)
+++ showeq/branches/xerxes-trunk-dev/src/spawn.h 2026-07-24 02:39:26 UTC (rev 1678)
@@ -57,6 +57,17 @@
struct doorStruct;
//----------------------------------------------------------------------
+// NPC name normalization helpers (shared by Spawn, SpawnPoint, and the
+// spawn detail view). All operate on the raw wire name.
+// cleanSpawnName: strip trailing spawn digits, turn '_' into ' '
+// transformSpawnName: cleanSpawnName, then move a leading a/an/the article
+// to the end (for name-sorted lists)
+// npcDisplayName: pick a form by NpcNameDisplay mode (see main.h)
+QString cleanSpawnName(const QString& rawName);
+QString transformSpawnName(const QString& rawName);
+QString npcDisplayName(const QString& rawName, int mode);
+
+//----------------------------------------------------------------------
// enumerated types
// type of item
enum spawnItemType
@@ -153,6 +164,11 @@
// virtual methods that provide reasonable default values/behaviour
virtual QString name() const;
virtual QString transformedName() const;
+ virtual QString cleanedName() const;
+ // the name to show in the UI, resolved from the NPC display-name option.
+ // Non-spawn items keep their raw name in every mode (their transformed/
+ // cleaned forms both return name()).
+ QString displayName() const;
virtual uint16_t race() const;
virtual QString raceString() const;
virtual uint8_t classVal() const;
Modified: showeq/branches/xerxes-trunk-dev/src/spawnlist.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/spawnlist.cpp 2026-07-23 02:45:49 UTC (rev 1677)
+++ showeq/branches/xerxes-trunk-dev/src/spawnlist.cpp 2026-07-24 02:39:26 UTC (rev 1678)
@@ -50,6 +50,8 @@
#include <cmath>
#include <regex.h>
#include <QMenu>
+#include <QScrollBar>
+#include <QSet>
#include <QApplication>
// ------------------------------------------------------
@@ -198,6 +200,17 @@
SEQListViewItemIterator it(this);
SpawnListItem *i = Find(it, item);
+
+ // if the item isn't in the tree yet, add it. The self-player is announced
+ // by the spawn shell via changeItem (not addItem), so without this it would
+ // never appear in the spawn list (it does appear in spawn list 2, which
+ // adds-on-change).
+ if (i == NULL)
+ {
+ addItem(item);
+ return;
+ }
+
while (i)
{
// reinsert only if level, NPC or filterFlags changes
@@ -442,6 +455,31 @@
j->update(m_player, tSpawnChangedALL);
} // else
+ // If this item can own pets (it's a spawn/player that is not itself a pet),
+ // re-parent any of its pets that were added before it. Pets added before
+ // their owner appeared went to the top level and never nested; now that the
+ // owner exists, re-add them so they nest. Covers the self-player, whose pet
+ // is often present before the player row is created.
+ if ((spawn != NULL) && (spawn->petOwnerID() == 0))
+ {
+ const ItemMap& spawnMap = m_spawnShell->getConstMap(tSpawn);
+ ItemConstIterator sit(spawnMap);
+ QList<const Item*> pets;
+ while (sit.hasNext())
+ {
+ sit.next();
+ const Item* s = sit.value();
+ if (s && (s->type() == tSpawn) &&
+ (((const Spawn*)s)->petOwnerID() == item->id()))
+ pets.append(s);
+ }
+ for (int p = 0; p < pets.count(); p++)
+ {
+ delItem(pets[p]);
+ addItem(pets[p]);
+ }
+ }
+
return;
} // end addItem
@@ -728,6 +766,15 @@
void SpawnList::clear(void)
{
//seqDebug("SpawnList::clear()");
+ // remember which categories are expanded so the tree keeps its expansion
+ // state across a rebuild (e.g. on zoning). Category pointers are stable
+ // across zones, so we key on them.
+ QSet<const void*> expanded;
+ QHash<void*, SpawnListItem*>::const_iterator eit;
+ for (eit = m_categoryListItems.begin(); eit != m_categoryListItems.end(); ++eit)
+ if (eit.value() && eit.value()->isExpanded())
+ expanded.insert(eit.key());
+
SEQListView::clear();
m_categoryListItems.clear();
@@ -752,6 +799,10 @@
// update count
litem->updateTitle(cat->name());
+
+ // restore prior expansion state
+ if (expanded.contains((void*)cat))
+ litem->setExpanded(true);
}
} // end clear
@@ -834,6 +885,36 @@
repaint();
}
+// Re-set the displayed name on every spawn row in place. Used when the NPC
+// display-name option changes - avoids a full rebuild so the category tree
+// keeps its expansion and selection state.
+void SpawnList::refreshItemNames()
+{
+ // suspend sorting while renaming: changing a row's name re-sorts the tree,
+ // which would move rows out from under the live iterator (visited twice or
+ // skipped, leaving a mix of old and new names). Re-sort once at the end.
+ bool wasSorting = isSortingEnabled();
+ setSortingEnabled(false);
+
+ // remember the scroll position so the view does not snap when the re-sort
+ // moves rows around (names change their sort key)
+ int scrollPos = verticalScrollBar()->value();
+
+ SEQListViewItemIterator it(this);
+ while (*it)
+ {
+ SpawnListItem* slitem = (SpawnListItem*)*it;
+ // skip category header rows (they have no backing item)
+ if (slitem->item() != NULL)
+ slitem->update(m_player, tSpawnChangedName);
+ ++it;
+ }
+
+ // re-enable sorting, which resorts by the current sort column/order
+ setSortingEnabled(wasSorting);
+ verticalScrollBar()->setValue(scrollPos);
+}
+
void SpawnList::playerLevelChanged(uint8_t)
{
SEQListViewItemIterator it(this);
@@ -984,6 +1065,10 @@
// retrieve the filter string
filterStr = filterString(item, flags);
+ // reset the category iterator for this spawn (it is shared across
+ // the whole populate, so it must be rewound before each spawn)
+ cit.toFront();
+
// iterate over all the categories
while (cit.hasNext())
{
@@ -1020,8 +1105,9 @@
}
}
- // done adding items, now iterate over all the categories and
+ // done adding items, now iterate over all the categories and
// update the counts
+ cit.toFront();
while (cit.hasNext())
{
cat = cit.next();
Modified: showeq/branches/xerxes-trunk-dev/src/spawnlist.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/spawnlist.h 2026-07-23 02:45:49 UTC (rev 1677)
+++ showeq/branches/xerxes-trunk-dev/src/spawnlist.h 2026-07-24 02:39:26 UTC (rev 1678)
@@ -118,6 +118,7 @@
void loadedCategories(void);
void rebuildSpawnList();
+ void refreshItemNames();
void playerLevelChanged(uint8_t);
void styleChanged();
Modified: showeq/branches/xerxes-trunk-dev/src/spawnlist2.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/spawnlist2.cpp 2026-07-23 02:45:49 UTC (rev 1677)
+++ showeq/branches/xerxes-trunk-dev/src/spawnlist2.cpp 2026-07-24 02:39:26 UTC (rev 1678)
@@ -28,6 +28,7 @@
#include "main.h"
#include <QComboBox>
+#include <QScrollBar>
#include <QSpinBox>
#include <QTimer>
#include <QLayout>
@@ -617,6 +618,32 @@
populateSpawns();
}
+// Re-set the displayed name on every visible row in place. Used when the NPC
+// display-name option changes - a full rebuild would clear the selection and
+// re-emit spawnSelected, hijacking the player's current target.
+void SpawnListWindow2::refreshItemNames(void)
+{
+ // suspend sorting while renaming so the live iterator is not disturbed
+ bool wasSorting = m_spawnList->isSortingEnabled();
+ m_spawnList->setSortingEnabled(false);
+
+ // remember the scroll position so the view does not snap when the re-sort
+ // moves rows around (names change their sort key)
+ int scrollPos = m_spawnList->verticalScrollBar()->value();
+
+ SEQListViewItemIterator it(m_spawnList);
+ while (*it)
+ {
+ SpawnListItem* slitem = (SpawnListItem*)*it;
+ if (slitem->item() != NULL)
+ slitem->update(m_player, tSpawnChangedName);
+ ++it;
+ }
+
+ m_spawnList->setSortingEnabled(wasSorting);
+ m_spawnList->verticalScrollBar()->setValue(scrollPos);
+}
+
void SpawnListWindow2::refresh(void)
{
#if 0 // ZBTEMP
Modified: showeq/branches/xerxes-trunk-dev/src/spawnlist2.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/spawnlist2.h 2026-07-23 02:45:49 UTC (rev 1677)
+++ showeq/branches/xerxes-trunk-dev/src/spawnlist2.h 2026-07-24 02:39:26 UTC (rev 1678)
@@ -89,6 +89,7 @@
int32_t degrees);
void rebuildSpawnList(void);
+ void refreshItemNames(void);
void refresh(void);
virtual void savePrefs(void);
Modified: showeq/branches/xerxes-trunk-dev/src/spawnlistcommon.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/spawnlistcommon.cpp 2026-07-23 02:45:49 UTC (rev 1677)
+++ showeq/branches/xerxes-trunk-dev/src/spawnlistcommon.cpp 2026-07-24 02:39:26 UTC (rev 1678)
@@ -183,11 +183,8 @@
if (changeType & tSpawnChangedName)
{
- // Name
- if (!showeq_params->showRealName)
- buff = item()->transformedName();
- else
- buff = item()->name();
+ // Name - honor the NPC display-name option (non-spawn items keep raw name)
+ buff = item()->displayName();
if (spawn != NULL)
{
Modified: showeq/branches/xerxes-trunk-dev/src/spawnpointlist.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/spawnpointlist.cpp 2026-07-23 02:45:49 UTC (rev 1677)
+++ showeq/branches/xerxes-trunk-dev/src/spawnpointlist.cpp 2026-07-24 02:39:26 UTC (rev 1678)
@@ -87,8 +87,8 @@
setText(tSpawnPointRemaining, tmpStr);
// set the name and last spawn info
- setText(tSpawnPointName, m_spawnPoint->name());
- setText(tSpawnPointLast, m_spawnPoint->last());
+ setText(tSpawnPointName, npcDisplayName(m_spawnPoint->name(), showeq_params->npcNameDisplay));
+ setText(tSpawnPointLast, npcDisplayName(m_spawnPoint->last(), showeq_params->npcNameDisplay));
// construct and set the spawned string
QDateTime dateTime;
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <xer...@us...> - 2026-07-23 02:45:52
|
Revision: 1677
http://sourceforge.net/p/seq/svn/1677
Author: xerxes00
Date: 2026-07-23 02:45:49 +0000 (Thu, 23 Jul 2026)
Log Message:
-----------
EQL: exact self mana from the stat-sync wide form; map stance/invocation ids
Mana (patch from Skik):
- Route the self mana pair from the stat-sync wide form (statUpdateEQL
case 2) into the new Player::setMana. Only EQL source with a real max,
and the only one that updates during regen. Stock OP_ManaChange stays
wired (cast-only, current-only).
- Keep the wire max in its own field (m_wireMaxMana, uint32) so
calcMaxMana recomputes and max-less ManaChange emits can't clobber it.
StatList prefers it over the high-water guess, so the displayed max
follows gear and buffs. Inert on live - nothing sets the wire max
there, so the guess behavior is unchanged.
Opcodes (ids from Skik):
- Map OP_Stance and OP_Invocation: Legends swappable stance / invocation
server echoes, 4 byte ability id. Mapped only, priority 0 / noHandler -
didn't take the handler half.
Thanks to Skik for both.
Modified Paths:
--------------
showeq/branches/showeqlegends/conf/zoneopcodes.xml
showeq/branches/showeqlegends/src/player.cpp
showeq/branches/showeqlegends/src/player.h
showeq/branches/showeqlegends/src/spawnshell.cpp
showeq/branches/showeqlegends/src/statlist.cpp
Modified: showeq/branches/showeqlegends/conf/zoneopcodes.xml
===================================================================
--- showeq/branches/showeqlegends/conf/zoneopcodes.xml 2026-07-22 23:28:54 UTC (rev 1676)
+++ showeq/branches/showeqlegends/conf/zoneopcodes.xml 2026-07-23 02:45:49 UTC (rev 1677)
@@ -1246,4 +1246,14 @@
<comment>SpellFadeCode</comment>
<payload dir="both" typename="spellFadedStruct" sizechecktype="none"/>
</opcode>
+ <opcode id="0fab" name="OP_Stance" priority="0" updated="07/14/26">
+ <priorityNote>noHandler</priorityNote>
+ <comment>EQ Legends swappable stance; server echo carries active ability id - 4 bytes</comment>
+ <payload dir="server" typename="uint8_t" sizechecktype="none"/>
+ </opcode>
+ <opcode id="3b12" name="OP_Invocation" priority="0" updated="07/14/26">
+ <priorityNote>noHandler</priorityNote>
+ <comment>EQ Legends swappable invocation; server echo carries active ability id - 4 bytes</comment>
+ <payload dir="server" typename="uint8_t" sizechecktype="none"/>
+ </opcode>
</seqopcodes>
Modified: showeq/branches/showeqlegends/src/player.cpp
===================================================================
--- showeq/branches/showeqlegends/src/player.cpp 2026-07-22 23:28:54 UTC (rev 1676)
+++ showeq/branches/showeqlegends/src/player.cpp 2026-07-23 02:45:49 UTC (rev 1677)
@@ -151,6 +151,7 @@
m_maxAGI = 0;
m_maxWIS = 0;
m_maxMana = 0;
+ m_wireMaxMana = 0; // EQL
m_maxHP = 0;
m_curHP = 0;
m_food = 0;
@@ -648,6 +649,25 @@
savePlayerState();
}
+void Player::setMana(uint32_t cur, uint32_t max)
+{
+ // EQL stat-sync wide form carries the player's exact current+max mana
+ // (gear+buff-inclusive). OP_ManaChange also rides the Legends wire, but it
+ // only fires on a cast and carries current with no max, so this is the only
+ // authoritative max and the only source that tracks regen. Hold the max
+ // separately as the wire max so a later calcMaxMana recompute (wear/remove
+ // item) can't clobber the displayed value.
+ m_mana = cur;
+ m_maxMana = max;
+ m_wireMaxMana = max;
+ m_validMana = true;
+
+ emit manaChanged(m_mana, m_maxMana);
+
+ if (showeq_params->savePlayerState)
+ savePlayerState();
+}
+
void Player::updateAltExp(const uint8_t* data)
{
const altExpUpdateStruct* altexp = (const altExpUpdateStruct*)data;
Modified: showeq/branches/showeqlegends/src/player.h
===================================================================
--- showeq/branches/showeqlegends/src/player.h 2026-07-22 23:28:54 UTC (rev 1676)
+++ showeq/branches/showeqlegends/src/player.h 2026-07-23 02:45:49 UTC (rev 1677)
@@ -81,6 +81,12 @@
void loadProfile(const playerProfileStruct& player);
void increaseSkill(const uint8_t* skilli);
void manaChange(const uint8_t* mana);
+ // EQL: exact current+max mana from the Legends stat channel
+ // (SpawnShell::statUpdateEQL wide form) - the only EQL source carrying a
+ // max (OP_ManaChange is cast-only and current-only). Stores the wire max
+ // separately (m_wireMaxMana) so a later calcMaxMana estimate can't clobber
+ // the display.
+ void setMana(uint32_t cur, uint32_t max);
void updateExp(const uint8_t* exp);
void updateAltExp(const uint8_t* altexp);
void updateLevel(const uint8_t* levelup);
@@ -144,6 +150,9 @@
uint8_t getMaxWIS() { return m_maxWIS; }
uint16_t getMaxMana() { return m_maxMana; }
uint16_t getMana() { return m_mana; }
+ // Exact gear+buff-inclusive max mana from the EQL stat-sync wide form;
+ // 0 on Live/Test (no wire max), where the display uses the estimate.
+ uint32_t wireMaxMana() const { return m_wireMaxMana; }
uint32_t getSpellBookSlot(uint32_t slotid) { return m_spellBookSlots[slotid]; }
uint32_t getCurrentExp() { return m_currentExp; }
@@ -242,6 +251,9 @@
uint16_t m_plusHP;
uint16_t m_maxMana;
+ // EQL: exact wire max mana; 0 => use estimate. uint32 not uint16 - the
+ // stat-sync wide form carries 64-bit values and setMana takes uint32.
+ uint32_t m_wireMaxMana = 0;
uint8_t m_maxSTR;
uint8_t m_maxSTA;
uint8_t m_maxCHA;
Modified: showeq/branches/showeqlegends/src/spawnshell.cpp
===================================================================
--- showeq/branches/showeqlegends/src/spawnshell.cpp 2026-07-22 23:28:54 UTC (rev 1676)
+++ showeq/branches/showeqlegends/src/spawnshell.cpp 2026-07-23 02:45:49 UTC (rev 1677)
@@ -1160,10 +1160,12 @@
// This decoder only PARSES the Legends container; it emits a stock
// hpNpcUpdateStruct into the stock display paths (SpawnShell::updateNpcHP
// for spawns, Player::updateNpcHP for self - each does its own spawnId
- // routing, exactly as stock wires OP_HPUpdate to both). Mana rides stock
- // OP_ManaChange; self endurance goes to Player::updateEnduranceEQL (Stam
- // row). Food/water do NOT ride this channel (verified with destroyed-food
- // + out-of-food captures).
+ // routing, exactly as stock wires OP_HPUpdate to both). Self mana (exact
+ // cur/max, gear+buff-inclusive) goes to Player::setMana; stock OP_ManaChange
+ // still fires alongside on casts (current only, no max) and stays wired.
+ // Self endurance goes to Player::updateEnduranceEQL (Stam row). Food/water
+ // do NOT ride this channel (verified with destroyed-food + out-of-food
+ // captures).
if (dir != DIR_Server)
return;
@@ -1227,7 +1229,11 @@
m_player->updateNpcHP((const uint8_t*)&hp);
break;
}
- case 2: // mana - the player's mana rides stock OP_ManaChange
+ case 2: // mana - EQL carries the player's exact cur/max here; OP_ManaChange
+ // fires only on a cast and carries no max, so this is the only
+ // source of a real max for the display (vs the calcMaxMana estimate).
+ if (wide && uint16_t(spawnId) == m_player->id())
+ m_player->setMana(uint32_t(cur), uint32_t(max));
break;
case 3: // endurance - self only, onto the Stam row
if (uint16_t(spawnId) == m_player->id())
Modified: showeq/branches/showeqlegends/src/statlist.cpp
===================================================================
--- showeq/branches/showeqlegends/src/statlist.cpp 2026-07-22 23:28:54 UTC (rev 1676)
+++ showeq/branches/showeqlegends/src/statlist.cpp 2026-07-23 02:45:49 UTC (rev 1677)
@@ -178,12 +178,21 @@
m_statList[LIST_MANA]->setText (1, buf);
- if (val > m_guessMaxMana) {
- m_guessMaxMana = val;
+ // EQL supplies the exact gear+buff-inclusive max mana on the wire
+ // (Player::setMana). When present it is authoritative and tracks
+ // equip/unequip and buffs, so use it directly instead of the high-water
+ // estimate, which only ever climbs and drifts a point above the true max.
+ uint32_t wireMax = m_player->wireMaxMana();
+ if (wireMax > 0) {
+ m_guessMaxMana = wireMax;
+ } else {
+ if (val > m_guessMaxMana) {
+ m_guessMaxMana = val;
+ }
+ if (max > m_guessMaxMana) {
+ m_guessMaxMana = max;
+ }
}
- if (max > m_guessMaxMana) {
- m_guessMaxMana = max;
- }
if (m_guessMaxMana < 0)
m_guessMaxMana = 0;
@@ -288,11 +297,18 @@
{
valStr = QString::number(value);
- // attempt to calculate the maximum amount of mana
- if (value > m_guessMaxMana)
- m_guessMaxMana = value;
- if (max > m_guessMaxMana)
- m_guessMaxMana = max;
+ // attempt to calculate the maximum amount of mana; EQL provides the
+ // exact wire max (Player::setMana) which is authoritative when present.
+ uint32_t wireMax = m_player->wireMaxMana();
+ if (wireMax > 0)
+ m_guessMaxMana = wireMax;
+ else
+ {
+ if (value > m_guessMaxMana)
+ m_guessMaxMana = value;
+ if (max > m_guessMaxMana)
+ m_guessMaxMana = max;
+ }
if (m_guessMaxMana < 0)
m_guessMaxMana = 0;
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <cn...@us...> - 2026-07-22 23:28:56
|
Revision: 1676
http://sourceforge.net/p/seq/svn/1676
Author: cn187
Date: 2026-07-22 23:28:54 +0000 (Wed, 22 Jul 2026)
Log Message:
-----------
Release 6.4.25
- Updated version to 6.4.25
- Support for EQ Patch 07/15/26
- Struct and opcode updates (Newby)
Modified Paths:
--------------
showeq/branches/cn187_devel/ChangeLog
showeq/branches/cn187_devel/conf/zoneopcodes.xml
showeq/branches/cn187_devel/configure.in
showeq/branches/cn187_devel/src/everquest.h
Modified: showeq/branches/cn187_devel/ChangeLog
===================================================================
--- showeq/branches/cn187_devel/ChangeLog 2026-07-18 03:23:00 UTC (rev 1675)
+++ showeq/branches/cn187_devel/ChangeLog 2026-07-22 23:28:54 UTC (rev 1676)
@@ -1,3 +1,9 @@
+cn187 (7/15/26)
+----------------
+- Update to version 6.4.25
+- Support for EQ Patch 7/15/26
+- Struct and opcode updates (Newby)
+
BlueAdept (5/21/26)
----------------
- Update to version 6.4.24
Modified: showeq/branches/cn187_devel/conf/zoneopcodes.xml
===================================================================
--- showeq/branches/cn187_devel/conf/zoneopcodes.xml 2026-07-18 03:23:00 UTC (rev 1675)
+++ showeq/branches/cn187_devel/conf/zoneopcodes.xml 2026-07-22 23:28:54 UTC (rev 1676)
@@ -3,37 +3,37 @@
<seqopcodes>
<!-- Critical opcodes used directly by ShowEQ -->
- <opcode id="e284" name="OP_PlayerProfile" updated="05/20/26">
+ <opcode id="d4b8" name="OP_PlayerProfile" updated="07/15/26">
<comment>CharProfileCode</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="a5bf" name="OP_ZoneEntry" updated="05/20/26">
+ <opcode id="abdb" name="OP_ZoneEntry" updated="07/15/26">
<comment>ZoneEntryCode</comment>
<payload dir="client" typename="ClientZoneEntryStruct" sizechecktype="match"/>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="7e22" name="OP_TimeOfDay" updated="05/20/26">
+ <opcode id="4968" name="OP_TimeOfDay" updated="07/15/26">
<comment>TimeOfDayCode</comment>
<payload dir="server" typename="timeOfDayStruct" sizechecktype="match"/>
</opcode>
- <opcode id="a923" name="OP_NewZone" updated="05/20/26">
+ <opcode id="1a58" name="OP_NewZone" updated="07/15/26">
<comment>NewZoneCode</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="33ec" name="OP_SpawnDoor" updated="05/20/26">
+ <opcode id="cb57" name="OP_SpawnDoor" updated="07/15/26">
<comment>DoorSpawnsCode</comment>
<payload dir="server" typename="doorStruct" sizechecktype="modulus"/>
</opcode>
- <opcode id="92dc" name="OP_GroundSpawn" updated="05/20/26">
+ <opcode id="66a8" name="OP_GroundSpawn" updated="07/15/26">
<comment>MakeDropCode</comment>
<payload dir="server" typename="makeDropStruct" sizechecktype="none"/>
<payload dir="client" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="ad5c" name="OP_SendZonePoints" updated="05/20/26">
+ <opcode id="57f6" name="OP_SendZonePoints" updated="07/15/26">
<comment>Coords in a zone that will port you to another zone</comment>
<payload dir="server" typename="zonePointsStruct" sizechecktype="none"/>
</opcode>
- <opcode id="0c97" name="OP_AAExpUpdate" updated="05/20/26">
+ <opcode id="b10f" name="OP_AAExpUpdate" updated="07/15/26">
<comment>Receiving AA experience. Also when percent to AA changes.</comment>
<payload dir="server" typename="altExpUpdateStruct" sizechecktype="match"/>
</opcode>
@@ -41,32 +41,32 @@
<comment>ExpUpdateCode</comment>
<payload dir="server" typename="expUpdateStruct" sizechecktype="match"/>
</opcode>
- <opcode id="9f2b" name="OP_GuildMOTD" updated="05/20/26">
+ <opcode id="3425" name="OP_GuildMOTD" updated="07/15/26">
<comment>GuildMOTD</comment>
<payload dir="server" typename="guildMOTDStruct" sizechecktype="none"/>
</opcode>
- <opcode id="f8d1" name="OP_ClientUpdate" updated="05/20/26">
+ <opcode id="76e4" name="OP_ClientUpdate" updated="07/15/26">
<comment>Position updates</comment>
<payload dir="server" typename="playerSpawnPosStruct" sizechecktype="match"/>
<payload dir="both" typename="playerSelfPosStruct" sizechecktype="match"/>
</opcode>
- <opcode id="917c" name="OP_NpcMoveUpdate" updated="05/20/26">
+ <opcode id="612e" name="OP_NpcMoveUpdate" updated="07/15/26">
<comment>Position updates</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="4a4f" name="OP_MobUpdate" updated="05/20/26">
+ <opcode id="6a5a" name="OP_MobUpdate" updated="07/15/26">
<comment>MobUpdateCode</comment>
<payload dir="both" typename="spawnPositionUpdate" sizechecktype="match"/>
</opcode>
- <opcode id="94d4" name="OP_DeleteSpawn" updated="05/20/26">
+ <opcode id="18c6" name="OP_DeleteSpawn" updated="07/15/26">
<comment>DeleteSpawnCode</comment>
<payload dir="both" typename="deleteSpawnStruct" sizechecktype="match"/>
</opcode>
- <opcode id="eb88" name="OP_RemoveSpawn" updated="05/20/26">
+ <opcode id="d631" name="OP_RemoveSpawn" updated="07/15/26">
<comment>Remove spawn from zone</comment>
<payload dir="both" typename="removeSpawnStruct" sizechecktype="none"/>
</opcode>
- <opcode id="1eb2" name="OP_Death" updated="05/20/26">
+ <opcode id="75b4" name="OP_Death" updated="07/15/26">
<comment>old NewCorpseCode</comment>
<payload dir="server" typename="newCorpseStruct" sizechecktype="match"/>
</opcode>
@@ -74,7 +74,7 @@
<comment>SpawnUpdateCode</comment>
<payload dir="both" typename="SpawnUpdateStruct" sizechecktype="match"/>
</opcode>
- <opcode id="9826" name="OP_SpawnAppearance" updated="05/20/26">
+ <opcode id="9f7c" name="OP_SpawnAppearance" updated="07/15/26">
<comment>SpawnAppearanceCode</comment>
<payload dir="both" typename="spawnAppearanceStruct" sizechecktype="match"/>
</opcode>
@@ -86,15 +86,15 @@
<comment>NpcHpUpdateCode Update HP % of a PC or NPC</comment>
<payload dir="both" typename="hpNpcUpdateStruct" sizechecktype="match"/>
</opcode>
- <opcode id="9abe" name="OP_GuildMemberUpdate" updated="05/20/26">
+ <opcode id="0ad8" name="OP_GuildMemberUpdate" updated="07/15/26">
<comment>Info regarding guild members</comment>
<payload dir="server" typename="GuildMemberUpdate" sizechecktype="match"/>
</opcode>
- <opcode id="ab5e" name="OP_ClickObject" updated="05/20/26">
+ <opcode id="d84f" name="OP_ClickObject" updated="07/15/26">
<comment>Items dropped on the ground</comment>
<payload dir="both" typename="remDropStruct" sizechecktype="match"/>
</opcode>
- <opcode id="049e" name="OP_Action" updated="05/20/26">
+ <opcode id="dda7" name="OP_Action" updated="07/15/26">
<comment>Spells cast etc</comment>
<payload dir="both" typename="actionStruct" sizechecktype="match"/>
<payload dir="both" typename="actionAltStruct" sizechecktype="match"/>
@@ -103,11 +103,11 @@
<comment>Combat actions i.e. bash, kick etc</comment>
<payload dir="both" typename="action2Struct" sizechecktype="match"/>
</opcode>
- <opcode id="a1e7" name="OP_Consider" updated="05/20/26">
+ <opcode id="7a5b" name="OP_Consider" updated="07/15/26">
<comment>ConsiderCode</comment>
<payload dir="both" typename="considerStruct" sizechecktype="match"/>
</opcode>
- <opcode id="1994" name="OP_TargetMouse" updated="05/20/26">
+ <opcode id="12e2" name="OP_TargetMouse" updated="07/15/26">
<comment>Targeting a person - old ClientTargetCode</comment>
<payload dir="both" typename="clientTargetStruct" sizechecktype="match"/>
</opcode>
@@ -123,7 +123,7 @@
<comment>Server putting players into shroud form</comment>
<payload dir="server" typename="spawnShroudSelf" sizechecktype="none"/>
</opcode>
- <opcode id="9148" name="OP_ZoneChange" updated="05/20/26">
+ <opcode id="4068" name="OP_ZoneChange" updated="07/15/26">
<comment>old ZoneChangeCode</comment>
<payload dir="both" typename="zoneChangeStruct" sizechecktype="match"/>
</opcode>
@@ -191,19 +191,19 @@
<comment>EmoteTextCode</comment>
<payload dir="both" typename="emoteTextStruct" sizechecktype="none"/>
</opcode>
- <opcode id="44a0" name="OP_SimpleMessage" updated="05/20/26">
+ <opcode id="2892" name="OP_SimpleMessage" updated="07/15/26">
<comment>SimpleMessageCode</comment>
<payload dir="server" typename="simpleMessageStruct" sizechecktype="match"/>
</opcode>
- <opcode id="5b40" name="OP_FormattedMessage" updated="05/20/26">
+ <opcode id="1d0d" name="OP_FormattedMessage" updated="07/15/26">
<comment>FormattedMessageCode i.e. pet dismissed etc</comment>
<payload dir="server" typename="formattedMessageStruct" sizechecktype="none"/>
</opcode>
- <opcode id="498b" name="OP_CommonMessage" updated="05/20/26">
+ <opcode id="547a" name="OP_CommonMessage" updated="07/15/26">
<comment>ChannelMessageCode i.e. /tell /ooc /shout etc</comment>
<payload dir="both" typename="channelMessageStruct" sizechecktype="none"/>
</opcode>
- <opcode id="7162" name="OP_SpecialMesg" updated="05/20/26">
+ <opcode id="b8f3" name="OP_SpecialMesg" updated="07/15/26">
<comment>Communicate textual info to client including hail responses etc</comment>
<payload dir="server" typename="specialMessageStruct" sizechecktype="none"/>
</opcode>
@@ -315,7 +315,7 @@
<comment>List of group members - Variable length</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="4058" name="OP_GuildMemberList" updated="05/20/26">
+ <opcode id="5af5" name="OP_GuildMemberList" updated="07/15/26">
<comment>List of guild members - Variable length</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
@@ -375,7 +375,7 @@
<comment>Fellowship information - 2576 bytes</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="8d86" name="OP_ExpandedGuildInfo" updated="05/20/26">
+ <opcode id="3ef7" name="OP_ExpandedGuildInfo" updated="07/15/26">
<comment>Guild ranks and other misc guild data - Variable length</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
@@ -543,7 +543,7 @@
<comment>Listing of all guilds. Can be triggered by /lfg search?</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="d5e8" name="OP_GuildsInZoneList" updated="05/20/26">
+ <opcode id="68f4" name="OP_GuildsInZoneList" updated="07/15/26">
<comment>Listing of guild names present in the current zone. Generally seen on zoning</comment>
<payload dir="server" typename="guildsInZoneListStruct" sizechecktype="none"/>
</opcode>
@@ -603,7 +603,7 @@
<comment>Free to play nags and other data - Variable length</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="695f" name="OP_Find" updated="05/20/26">
+ <opcode id="2e60" name="OP_Find" updated="07/15/26">
<comment>Find window data - variable length, 24 min</comment>
<payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
Modified: showeq/branches/cn187_devel/configure.in
===================================================================
--- showeq/branches/cn187_devel/configure.in 2026-07-18 03:23:00 UTC (rev 1675)
+++ showeq/branches/cn187_devel/configure.in 2026-07-22 23:28:54 UTC (rev 1676)
@@ -2,7 +2,7 @@
dnl $Id$ $Name$
AC_PREREQ(2.59)
-AC_INIT(showeq, 6.4.24)
+AC_INIT(showeq, 6.4.25)
AC_CONFIG_SRCDIR(src/main.cpp)
AC_CONFIG_MACRO_DIR([m4])
AC_CANONICAL_SYSTEM
Modified: showeq/branches/cn187_devel/src/everquest.h
===================================================================
--- showeq/branches/cn187_devel/src/everquest.h 2026-07-18 03:23:00 UTC (rev 1675)
+++ showeq/branches/cn187_devel/src/everquest.h 2026-07-22 23:28:54 UTC (rev 1676)
@@ -1121,23 +1121,24 @@
struct
{
+ unsigned heading:12; // heading
signed animation:10; // current animation
- unsigned pitch:12; // pitch (up/down heading)
unsigned padding00:10;
signed deltaZ:13; // change in z
- signed deltaHeading:10; // change in heading
- unsigned padding01:9;
+ signed deltaX:13; // change in x
+ unsigned padding01:6;
- signed z:19; // z coord (3rd loc value)
- unsigned heading:12; // heading
+ signed x:19; // x coord (1st loc value)
+ unsigned pitch:12; // pitch (up/down heading)
unsigned padding02:1;
- signed deltaX:13; // change in x
signed y:19; // y coord (2nd loc value)
+ signed deltaHeading:10; // change in heading
+ unsigned padding03:3;
+ signed z:19; // z coord (3rd loc value)
signed deltaY:13; // change in y
- signed x:19; // x coord (1st loc value)
};
int32_t posData[5];
@@ -2400,23 +2401,24 @@
/*0000*/ uint16_t spawnId;
/*0002*/ uint16_t spawnId2;
/*0004*/
+ unsigned heading:12; // heading
signed animation:10; // current animation
- unsigned pitch:12; // pitch (up/down heading)
unsigned padding00:10;
/*0008*/
signed deltaZ:13; // change in z
- signed deltaHeading:10; // change in heading
- unsigned padding01:9;
+ signed deltaX:13; // change in x
+ unsigned padding01:6;
/*0012*/
- signed z:19; // z coord (3rd loc value)
- unsigned heading:12; // heading
+ signed x:19; // x coord (1st loc value)
+ unsigned pitch:12; // pitch (up/down heading)
unsigned padding02:1;
/*0016*/
- signed deltaX:13; // change in x
signed y:19; // y coord (2nd loc value)
+ signed deltaHeading:10; // change in heading
+ unsigned padding03:3;
/*0020*/
+ signed z:19; // z coord (3rd loc value)
signed deltaY:13; // change in y
- signed x:19; // x coord (1st loc value)
/*0024*/
};
@@ -2431,27 +2433,27 @@
/*0002*/ uint16_t spawnId; // Player's spawn id
/*0004*/ uint16_t unknown0004; // ***Placeholder
/*0006*/
+ float z; // z coord (3rd loc value)
+/*0010*/
float y; // y coord (2nd loc value)
-/*0010*/
- unsigned pitch:12; // pitch (up/down heading)
+/*0014*/
unsigned heading:12; // heading
- unsigned padding01:8;
-/*0014*/
- float deltaY; // change in y
+ unsigned padding02:20;
/*0018*/
- signed animation:10; // current animation
- unsigned padding03:22;
+ float x; // x coord (1st loc value)
/*0022*/
- float z; // z coord (3rd loc value)
+ float deltaZ; // change in z
/*0026*/
- float x; // x coord (1st loc value)
+ unsigned pitch:12; // pitch (up/down heading)
+ unsigned padding05:20;
/*0030*/
float deltaX; // change in x
/*0034*/
+ float deltaY; // change in y
+/*0038*/
+ signed animation:10; // current animation
signed deltaHeading:10; // change in heading
- unsigned padding07:22;
-/*0038*/
- float deltaZ; // change in z
+ unsigned padding08:12;
/*0042*/
};
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <xer...@us...> - 2026-07-18 03:23:02
|
Revision: 1675
http://sourceforge.net/p/seq/svn/1675
Author: xerxes00
Date: 2026-07-18 03:23:00 +0000 (Sat, 18 Jul 2026)
Log Message:
-----------
loot: corpse-contents window + route loot-colored messages to a Loot category
- decode OP_LootDrops (loot-window contents) and print "corpse: item1, item2"
under a new MT_Loot category
- route server loot-colored messages (CC_User_Loot) to MT_Loot in
chatColor2MessageType, so "you looted X", lore/no-room/too-far failures and
the like read as Loot: instead of General:
- MT_Loot gets its own blue color in the message window and terminal
Modified Paths:
--------------
showeq/branches/xerxes-trunk-dev/conf/zoneopcodes.xml
showeq/branches/xerxes-trunk-dev/src/interface.cpp
showeq/branches/xerxes-trunk-dev/src/message.cpp
showeq/branches/xerxes-trunk-dev/src/message.h
showeq/branches/xerxes-trunk-dev/src/messageshell.cpp
showeq/branches/xerxes-trunk-dev/src/messageshell.h
showeq/branches/xerxes-trunk-dev/src/messagewindow.cpp
showeq/branches/xerxes-trunk-dev/src/terminal.cpp
Modified: showeq/branches/xerxes-trunk-dev/conf/zoneopcodes.xml
===================================================================
--- showeq/branches/xerxes-trunk-dev/conf/zoneopcodes.xml 2026-07-18 02:18:34 UTC (rev 1674)
+++ showeq/branches/xerxes-trunk-dev/conf/zoneopcodes.xml 2026-07-18 03:23:00 UTC (rev 1675)
@@ -480,6 +480,10 @@
<comment>Loot item from corpse - 20 bytes</comment>
<payload dir="both" typename="uint8_t" sizechecktype="none"/>
</opcode>
+ <opcode id="e781" name="OP_LootDrops" updated="07/15/26">
+ <comment>Corpse loot-window contents: count@10, corpse name NUL@14, then per-item clean name + 0x12 serialized link</comment>
+ <payload dir="server" typename="uint8_t" sizechecktype="none"/>
+ </opcode>
<opcode id="ffff" name="OP_EndLootRequest" updated="11/28/12">
<comment>DoneLootingCode - 4 bytes</comment>
<payload dir="client" typename="uint8_t" sizechecktype="none"/>
Modified: showeq/branches/xerxes-trunk-dev/src/interface.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/interface.cpp 2026-07-18 02:18:34 UTC (rev 1674)
+++ showeq/branches/xerxes-trunk-dev/src/interface.cpp 2026-07-18 03:23:00 UTC (rev 1675)
@@ -750,6 +750,11 @@
m_packet->connect2("OP_MoneyOnCorpse", SP_Zone, DIR_Server,
"moneyOnCorpseStruct", SZC_Match,
m_messageShell, SLOT(moneyOnCorpse(const uint8_t*)));
+ // Corpse loot-window contents (variable length); print "corpse: items"
+ // as MT_Loot. The whole loot window (coin + items) rides this one opcode.
+ m_packet->connect2("OP_LootDrops", SP_Zone, DIR_Server,
+ "uint8_t", SZC_None,
+ m_messageShell, SLOT(lootDrops(const uint8_t*, size_t, uint8_t)));
m_packet->connect2("OP_Logout", SP_Zone, DIR_Server,
"uint16_t", SZC_Match,
m_messageShell, SLOT(logOut(const uint8_t*, size_t, uint8_t)));
Modified: showeq/branches/xerxes-trunk-dev/src/message.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/message.cpp 2026-07-18 02:18:34 UTC (rev 1674)
+++ showeq/branches/xerxes-trunk-dev/src/message.cpp 2026-07-18 03:23:00 UTC (rev 1675)
@@ -62,5 +62,6 @@
"Hunt",
"Locate",
"Chat", // UCS: MT_ChatChannel
+ "Loot", // corpse loot-window contents (MT_Loot)
};
Modified: showeq/branches/xerxes-trunk-dev/src/message.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/message.h 2026-07-18 02:18:34 UTC (rev 1674)
+++ showeq/branches/xerxes-trunk-dev/src/message.h 2026-07-18 03:23:00 UTC (rev 1675)
@@ -69,7 +69,8 @@
MT_Hunt,
MT_Locate,
MT_ChatChannel, // UCS: cross-zone/General channel chat
- MT_Max = MT_ChatChannel,
+ MT_Loot, // corpse loot-window contents (OP_LootDrops)
+ MT_Max = MT_Loot,
};
//----------------------------------------------------------------------
Modified: showeq/branches/xerxes-trunk-dev/src/messageshell.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/messageshell.cpp 2026-07-18 02:18:34 UTC (rev 1674)
+++ showeq/branches/xerxes-trunk-dev/src/messageshell.cpp 2026-07-18 03:23:00 UTC (rev 1675)
@@ -432,6 +432,9 @@
case CC_User_Random:
messageType = MT_Random;
break;
+ case CC_User_Loot:
+ messageType = MT_Loot;
+ break;
default:
messageType = MT_General;
break;
@@ -450,9 +453,10 @@
QString tempStr;
size_t messagesLen = len - ((uint8_t*)&fmsg->messages[0] - (uint8_t*)fmsg);
- m_messages->addMessage(chatColor2MessageType(fmsg->messageColor),
+
+ m_messages->addMessage(chatColor2MessageType(fmsg->messageColor),
m_eqStrings->formatMessage(fmsg->messageFormat,
- fmsg->messages,
+ fmsg->messages,
messagesLen));
}
@@ -515,6 +519,62 @@
}
+// Corpse loot-window contents (OP_LootDrops): the items you can loot off a
+// corpse, pushed when the loot window opens (and re-pushed at zone-in for a
+// corpse you still have rights to). Layout:
+// [2 bytes][u32][u32 corpseId @6][u32 count @10][corpse name NUL @14]
+// then per-item fields. Each item name is a clean NUL-terminated string;
+// the serialized link that follows repeats the name mangled (leading
+// capital fused to the hex), so we keep only NUL chunks that are entirely a
+// clean item name. Emits "corpse: item1, item2" under MT_Loot.
+void MessageShell::lootDrops(const uint8_t* data, size_t len, uint8_t dir)
+{
+ if (dir != DIR_Server || len < 15)
+ return;
+ uint32_t count = (uint32_t)data[10] | ((uint32_t)data[11] << 8) |
+ ((uint32_t)data[12] << 16) | ((uint32_t)data[13] << 24);
+ int i = 14;
+ while (i < (int)len && data[i] != 0) i++;
+ QString corpse = QString::fromLatin1((const char*)(data + 14), i - 14);
+ if (count == 0 || i >= (int)len) // empty corpse - nothing to loot
+ return;
+ i++; // skip corpse-name NUL
+
+ QStringList items;
+ int start = i;
+ for (int j = i; j <= (int)len; j++)
+ {
+ if (j == (int)len || data[j] == 0)
+ {
+ int clen = j - start;
+ if (clen >= 3 && clen <= 40)
+ {
+ bool ok = true, hasLower = false;
+ unsigned char c0 = data[start];
+ if (!((c0 >= 'A' && c0 <= 'Z') || (c0 >= 'a' && c0 <= 'z'))) ok = false;
+ for (int k = start; ok && k < j; k++)
+ {
+ unsigned char c = data[k];
+ if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
+ (c >= '0' && c <= '9') || c == ' ' || c == '\'' ||
+ c == '+' || c == '.' || c == '`' || c == '-'))
+ ok = false;
+ if (c >= 'a' && c <= 'z') hasLower = true;
+ }
+ if (ok && hasLower)
+ {
+ QString nm = QString::fromLatin1((const char*)(data + start), clen);
+ if (!items.contains(nm)) items.append(nm);
+ }
+ }
+ start = j + 1;
+ }
+ }
+ if (items.isEmpty())
+ return;
+ m_messages->addMessage(MT_Loot, corpse + ": " + items.join(", "));
+}
+
void MessageShell::moneyOnCorpse(const uint8_t* data)
{
const moneyOnCorpseStruct* money = (const moneyOnCorpseStruct*)data;
Modified: showeq/branches/xerxes-trunk-dev/src/messageshell.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/messageshell.h 2026-07-18 02:18:34 UTC (rev 1674)
+++ showeq/branches/xerxes-trunk-dev/src/messageshell.h 2026-07-18 03:23:00 UTC (rev 1675)
@@ -64,6 +64,8 @@
void specialMessage(const uint8_t* smsg, size_t, uint8_t);
// UCS: display decoded Universal Chat Service cross-zone/global chat.
void ucsChatMessage(const uint8_t* data, size_t len, uint8_t dir);
+ // corpse loot-window contents (OP_LootDrops): "corpse: item1, item2".
+ void lootDrops(const uint8_t* data, size_t len, uint8_t dir);
void guildMOTD(const uint8_t* gmotd, size_t, uint8_t);
void consent(const uint8_t* consent, size_t, uint8_t);
void moneyOnCorpse(const uint8_t* money);
Modified: showeq/branches/xerxes-trunk-dev/src/messagewindow.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/messagewindow.cpp 2026-07-18 02:18:34 UTC (rev 1674)
+++ showeq/branches/xerxes-trunk-dev/src/messagewindow.cpp 2026-07-18 03:23:00 UTC (rev 1675)
@@ -590,6 +590,7 @@
// (RGB 125,75,175). The load() below uses this as the default, so a
// user-set color preference still overrides it.
m_typeStyles[MT_ChatChannel].setColor(QColor(125, 75, 175));
+ m_typeStyles[MT_Loot].setColor(QColor(90, 90, 255));
QString typeName;
// iterate over the message types, filling in various menus and getting
Modified: showeq/branches/xerxes-trunk-dev/src/terminal.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/terminal.cpp 2026-07-18 02:18:34 UTC (rev 1674)
+++ showeq/branches/xerxes-trunk-dev/src/terminal.cpp 2026-07-18 03:23:00 UTC (rev 1675)
@@ -69,6 +69,7 @@
"\e[1;37;44m", // 34 - Locate
NULL, // 35
"\e[38;2;125;75;175m", // 36 - Chat (UCS: in-game universal-chat purple 125,75,175)
+ "\e[38;2;90;90;255m", // 37 - Loot (in-game loot-message blue 90,90,255)
};
//----------------------------------------------------------------------
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <xer...@us...> - 2026-07-18 02:18:37
|
Revision: 1674
http://sourceforge.net/p/seq/svn/1674
Author: xerxes00
Date: 2026-07-18 02:18:34 +0000 (Sat, 18 Jul 2026)
Log Message:
-----------
ucs-chat: decode cross-zone Universal Chat Service channel chat
- learn the UCS session port from its SessionResponse (XOR format 0x04) and
decode the chained-XOR data packets inline, emitting channel chat under a
new MT_ChatChannel ("Chat") category
- packetcapture: include WorldServerChat2Port in the post-detection filter so
the separate UCS session survives filter narrowing
- messageshell: drop the client-dir MOTD duplicate of outgoing channel
commands (they echo back over UCS as Chat)
- learn full channel names from /list and join notices to repair the
seed-masked first character on chat lines
Modified Paths:
--------------
showeq/branches/xerxes-trunk-dev/src/interface.cpp
showeq/branches/xerxes-trunk-dev/src/message.cpp
showeq/branches/xerxes-trunk-dev/src/message.h
showeq/branches/xerxes-trunk-dev/src/messageshell.cpp
showeq/branches/xerxes-trunk-dev/src/messageshell.h
showeq/branches/xerxes-trunk-dev/src/messagewindow.cpp
showeq/branches/xerxes-trunk-dev/src/packet.cpp
showeq/branches/xerxes-trunk-dev/src/packet.h
showeq/branches/xerxes-trunk-dev/src/packetcapture.cpp
showeq/branches/xerxes-trunk-dev/src/terminal.cpp
Modified: showeq/branches/xerxes-trunk-dev/src/interface.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/interface.cpp 2026-07-18 01:43:54 UTC (rev 1673)
+++ showeq/branches/xerxes-trunk-dev/src/interface.cpp 2026-07-18 02:18:34 UTC (rev 1674)
@@ -720,6 +720,11 @@
"specialMessageStruct", SZC_None,
m_messageShell,
SLOT(specialMessage(const uint8_t*, size_t, uint8_t)));
+ // UCS: decoded cross-zone/global chat -> message log. This is a plain Qt
+ // connect (a custom EQPacket signal, not an opcode), so it does NOT go
+ // through connect2's opcode registration.
+ connect(m_packet, SIGNAL(decodedChatMessage(const uint8_t*, size_t, uint8_t)),
+ m_messageShell, SLOT(ucsChatMessage(const uint8_t*, size_t, uint8_t)));
m_packet->connect2("OP_GuildMOTD", SP_Zone, DIR_Server,
"guildMOTDStruct", SZC_None,
m_messageShell,
Modified: showeq/branches/xerxes-trunk-dev/src/message.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/message.cpp 2026-07-18 01:43:54 UTC (rev 1673)
+++ showeq/branches/xerxes-trunk-dev/src/message.cpp 2026-07-18 02:18:34 UTC (rev 1674)
@@ -61,5 +61,6 @@
"Caution",
"Hunt",
"Locate",
+ "Chat", // UCS: MT_ChatChannel
};
Modified: showeq/branches/xerxes-trunk-dev/src/message.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/message.h 2026-07-18 01:43:54 UTC (rev 1673)
+++ showeq/branches/xerxes-trunk-dev/src/message.h 2026-07-18 02:18:34 UTC (rev 1674)
@@ -68,7 +68,8 @@
MT_Caution,
MT_Hunt,
MT_Locate,
- MT_Max = MT_Locate,
+ MT_ChatChannel, // UCS: cross-zone/General channel chat
+ MT_Max = MT_ChatChannel,
};
//----------------------------------------------------------------------
Modified: showeq/branches/xerxes-trunk-dev/src/messageshell.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/messageshell.cpp 2026-07-18 01:43:54 UTC (rev 1673)
+++ showeq/branches/xerxes-trunk-dev/src/messageshell.cpp 2026-07-18 02:18:34 UTC (rev 1674)
@@ -21,6 +21,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <QStringList> // UCS: channel-name learning
+
#include "messageshell.h"
#include "eqstr.h"
#include "messages.h"
@@ -88,10 +90,13 @@
//-----------------------------------------------------------------------------
// Tells and Group by us happen twice *shrug*. Ignore the client->server one.
- if (dir == DIR_Client &&
+ if (dir == DIR_Client &&
(cmsg->chanNum == MT_Tell || cmsg->chanNum == MT_Group || cmsg->chanNum == MT_Guild ||
cmsg->chanNum == MT_OOC || cmsg->chanNum == MT_Shout || cmsg->chanNum == MT_Auction ||
- cmsg->chanNum == MT_System || cmsg->chanNum == MT_Raid))
+ cmsg->chanNum == MT_System || cmsg->chanNum == MT_Raid ||
+ cmsg->chanNum == MT_Motd)) // UCS: outgoing channel commands echo back
+ // over UCS as "Chat:"; drop the client-dir MOTD
+ // duplicate. (Real server MOTD is DIR_Server.)
{
return;
}
@@ -179,6 +184,199 @@
cmsg = 0;
}
+// UCS: true if >=85% of the string is printable ASCII. A bad XOR decode
+// yields mostly non-printable bytes, so this gates out garbage records.
+static bool mostlyPrintable(const QString& s)
+{
+ if (s.isEmpty())
+ return false;
+ int p = 0;
+ for (int i = 0; i < s.length(); ++i)
+ if (s[i].unicode() >= 32 && s[i].unicode() < 127)
+ ++p;
+ return (p * 100) >= (s.length() * 85);
+}
+
+// UCS: full channel names learned at runtime. Every per-message channel name
+// has its first character in the seed-masked header (so it decodes garbled),
+// but the FULL name appears un-masked inside /list output and join/invite
+// notices. We harvest those and use them to repair the garbled first char on
+// chat lines.
+static QStringList s_ucsChannels;
+
+static void ucsLearnName(const QString& name)
+{
+ if ((name.length() >= 2) && !s_ucsChannels.contains(name))
+ s_ucsChannels.append(name);
+}
+
+// Scan a decoded buffer's text for channel names: "/list" prints
+// "Channels: 1=General2(400), 2=myraid(1), ..."; join/invite notices say
+// "channel <name>" / "joined <name>".
+static void ucsLearnChannels(const QString& text)
+{
+ int i = 0;
+ while ((i = text.indexOf('=', i)) >= 0) // <n>=<name>(
+ {
+ int j = i + 1;
+ QString name;
+ while ((j < text.length()) &&
+ (text[j].isLetterOrNumber() || (text[j] == '_')))
+ name += text[j++];
+ if ((j < text.length()) && (text[j] == '('))
+ ucsLearnName(name);
+ i = (j > i) ? j : (i + 1);
+ }
+ QStringList kws;
+ kws << "channel " << "joined ";
+ for (int w = 0; w < kws.size(); ++w)
+ {
+ int p = text.indexOf(kws[w]);
+ if (p < 0)
+ continue;
+ p += kws[w].length();
+ QString name;
+ while ((p < text.length()) &&
+ (text[p].isLetterOrNumber() || (text[p] == '_')))
+ name += text[p++];
+ ucsLearnName(name);
+ }
+}
+
+// Repair a channel name whose readable tail (first char may be masked/garbled)
+// is `tail`: match a learned name equal to the tail or to the tail minus its
+// first char; else rebuild the auto-joined "General*" channels; else best-effort.
+static QString ucsResolveChannel(const QString& tail)
+{
+ if (tail.isEmpty())
+ return QString();
+ for (int c = 0; c < s_ucsChannels.size(); ++c)
+ {
+ const QString& name = s_ucsChannels[c];
+ if (tail.endsWith(name) || ((name.length() >= 2) && tail.endsWith(name.mid(1))))
+ return name;
+ }
+ int g = tail.indexOf("eneral"); // General, General1, General2, ...
+ if (g >= 0)
+ return QString("General") + tail.mid(g + 6);
+ return tail;
+}
+
+// UCS: display decoded Universal Chat Service (channel) chat. The buffer
+// arrives XOR-decoded from EQPacket::decodeUCSPacket, SOE OP_Packet-wrapped
+// with a VARIABLE header, so we anchor on the "SPAM:" trailer that ends every
+// real chat record. NUL('\0')-separated layout around it:
+// [4-byte masked hdr]<channel>\0 <server>.<sender>\0 <message>\0 SPAM:<n>:<id>\0
+// message = field before "SPAM:", <server>.<sender> = field before that,
+// <channel> = field before that (first char masked -> repaired via
+// ucsResolveChannel), and sender = text after the LAST '.'. One packet may
+// carry several records (SOE combine). Incoming (server) direction only.
+// Every read is bounds-checked.
+void MessageShell::ucsChatMessage(const uint8_t* data, size_t len, uint8_t dir)
+{
+ if (dir != DIR_Server) // outgoing rides the zone server
+ return;
+ if ((data == NULL) || (len < 8))
+ return;
+
+ // Learn full channel names from any /list / join / invite text in this buffer.
+ ucsLearnChannels(QString::fromLatin1((const char*)data, (int)len));
+
+ size_t scan = 0;
+ while (scan + 5 <= len)
+ {
+ size_t p = scan;
+ bool found = false;
+ for (; p + 5 <= len; ++p)
+ {
+ if (data[p] == 'S' && data[p+1] == 'P' && data[p+2] == 'A' &&
+ data[p+3] == 'M' && data[p+4] == ':')
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ break;
+ scan = p + 5;
+
+ if ((p < 2) || (data[p-1] != 0)) // "SPAM:" must follow a NUL
+ continue;
+
+ // message = field ending at the NUL just before "SPAM:".
+ size_t msgEnd = p - 1;
+ size_t msgStart = msgEnd;
+ while ((msgStart > 0) && (data[msgStart-1] != 0))
+ --msgStart;
+ if ((msgStart >= msgEnd) || (msgStart < 2))
+ continue;
+
+ // <server>.<sender> = the field ending at the NUL before the message.
+ size_t ssEnd = msgStart - 1;
+ size_t ssStart = ssEnd;
+ while ((ssStart > 0) && (data[ssStart-1] != 0))
+ --ssStart;
+ if (ssStart >= ssEnd)
+ continue;
+
+ QString message = QString::fromLatin1((const char*)(data + msgStart),
+ (int)(msgEnd - msgStart));
+ QString serverSender = QString::fromLatin1((const char*)(data + ssStart),
+ (int)(ssEnd - ssStart));
+ int dot = serverSender.lastIndexOf('.');
+ QString sender = (dot >= 0) ? serverSender.mid(dot + 1) : serverSender;
+
+ // <channel> = the field before <server>.<sender>. Take its trailing
+ // printable run (drops leading masked-header bytes), then repair the
+ // first char.
+ QString channel;
+ if (ssStart >= 2)
+ {
+ size_t chEnd = ssStart - 1;
+ size_t chStart = chEnd;
+ while ((chStart > 0) && (data[chStart-1] != 0))
+ --chStart;
+ QString field = QString::fromLatin1((const char*)(data + chStart),
+ (int)(chEnd - chStart));
+ int k = field.length();
+ while ((k > 0) && (field[k-1].unicode() >= 32) && (field[k-1].unicode() < 127))
+ --k;
+ channel = ucsResolveChannel(field.mid(k));
+ }
+
+ if (!mostlyPrintable(sender) || !mostlyPrintable(message) ||
+ (sender.length() > 32))
+ continue;
+
+ // Server-assigned spam score = the digits right after "SPAM:". The game
+ // client hides/marks score>0 lines; we SHOW everything and tag score>0
+ // with "(SPAM)" so filtered chat stays visible.
+ bool spam = false;
+ {
+ size_t q = p + 5; // p..p+4 == "SPAM:"
+ long score = 0;
+ bool haveDigit = false;
+ while ((q < len) && (data[q] >= '0') && (data[q] <= '9'))
+ {
+ score = (score * 10) + (data[q] - '0');
+ haveDigit = true;
+ ++q;
+ }
+ spam = haveDigit && (score > 0);
+ }
+
+ QString inner = spam
+ ? QString("(SPAM) '%1' - %2").arg(sender).arg(message)
+ : QString("'%1' - %2").arg(sender).arg(message);
+ QString text = channel.isEmpty()
+ ? inner
+ : QString("#%1: %2").arg(channel).arg(inner);
+ // MT_ChatChannel gives UCS chat its own "Chat" category, so the console
+ // prefix is "Chat:" instead of the redundant "General:".
+ m_messages->addMessage(MT_ChatChannel, text);
+ }
+}
+
static MessageType chatColor2MessageType(ChatColor chatColor)
{
MessageType messageType;
Modified: showeq/branches/xerxes-trunk-dev/src/messageshell.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/messageshell.h 2026-07-18 01:43:54 UTC (rev 1673)
+++ showeq/branches/xerxes-trunk-dev/src/messageshell.h 2026-07-18 02:18:34 UTC (rev 1674)
@@ -62,6 +62,8 @@
void formattedMessage(const uint8_t* cmsg, size_t, uint8_t);
void simpleMessage(const uint8_t* cmsg, size_t, uint8_t);
void specialMessage(const uint8_t* smsg, size_t, uint8_t);
+ // UCS: display decoded Universal Chat Service cross-zone/global chat.
+ void ucsChatMessage(const uint8_t* data, size_t len, uint8_t dir);
void guildMOTD(const uint8_t* gmotd, size_t, uint8_t);
void consent(const uint8_t* consent, size_t, uint8_t);
void moneyOnCorpse(const uint8_t* money);
Modified: showeq/branches/xerxes-trunk-dev/src/messagewindow.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/messagewindow.cpp 2026-07-18 01:43:54 UTC (rev 1673)
+++ showeq/branches/xerxes-trunk-dev/src/messagewindow.cpp 2026-07-18 02:18:34 UTC (rev 1674)
@@ -586,6 +586,11 @@
m_typeFilterMenu->addSeparator();
+ // UCS: default the "Chat" category to the in-game universal-chat purple
+ // (RGB 125,75,175). The load() below uses this as the default, so a
+ // user-set color preference still overrides it.
+ m_typeStyles[MT_ChatChannel].setColor(QColor(125, 75, 175));
+
QString typeName;
// iterate over the message types, filling in various menus and getting
// font color preferences
Modified: showeq/branches/xerxes-trunk-dev/src/packet.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/packet.cpp 2026-07-18 01:43:54 UTC (rev 1673)
+++ showeq/branches/xerxes-trunk-dev/src/packet.cpp 2026-07-18 02:18:34 UTC (rev 1674)
@@ -33,6 +33,7 @@
#include <QTimer>
#include <QFileInfo>
+#include <QByteArray> // UCS: buffer for inline UCS chat decode
#include "everquest.h"
#include "packet.h"
@@ -325,6 +326,7 @@
m_ip = inet_ntoa(ia);
m_detectingClient = (m_ip == AUTOMATIC_CLIENT_IP);
+ m_ucsPort = 0; // UCS chat session port, learned from the wire
}
@@ -642,8 +644,32 @@
seqInfo("Client Detected: %s", m_ip.toLatin1().data());
}
+ // UCS (Universal Chat Service): cross-zone / global-channel chat rides a
+ // separate, persistent SOE session whose SessionResponse advertises format
+ // byte 0x04 (XOR-obfuscated) rather than 0x01 (zlib). Its UDP port varies
+ // by server (e.g. 9875 on some, 9877 on others), so learn it from the wire
+ // instead of hardcoding. payload()[11] is the SessionResponse format byte.
+ {
+ const uint8_t* soe = packet.getUDPPayload();
+ uint32_t soeLen = packet.getUDPPayloadLength();
+ if (soe && soeLen >= 12 &&
+ soe[0] == 0x00 && soe[1] == 0x02 && soe[11] == 0x04)
+ m_ucsPort = packet.getSourcePort(); // SessionResponse is server->client
+ }
+
// Dispatch based on known streams
- if ((packet.getDestPort() == ChatServerPort) ||
+ if (m_ucsPort &&
+ ((packet.getDestPort() == m_ucsPort) ||
+ (packet.getSourcePort() == m_ucsPort)))
+ {
+ // UCS chat session: decode + emit it ourselves. The XOR-obfuscated data
+ // packets have no cleartext SOE framing, so the normal stream/CRC path
+ // would choke. Must sit before the chat-port drops below, since the UCS
+ // port can coincide with one of them.
+ decodeUCSPacket(packet);
+ return;
+ }
+ else if ((packet.getDestPort() == ChatServerPort) ||
(packet.getSourcePort() == ChatServerPort))
{
// Drop chat server traffic
@@ -699,6 +725,50 @@
} /* end dispatchPacket() */
////////////////////////////////////////////////////
+// UCS: decode a Universal Chat Service (cross-zone chat) SOE data packet
+// inline.
+//
+// The chat session is a standard SOE session using SOE's XOR "encode" mode
+// (format byte 0x04) rather than zlib. Its data packets are obfuscated with a
+// self-synchronizing chained XOR that needs no key: for the raw UDP payload,
+// plain[i] = cipher[i] ^ cipher[i-4] for i>=4 (the first 4 bytes are a masked
+// header we leave alone). SOE control packets carry no chat and are harmlessly
+// ignored downstream (the parser anchors on a chat-only trailer). These data
+// packets have no cleartext SOE framing, so the normal stream/CRC/reassembly
+// path can't handle them; we decode and emit here instead.
+void EQPacket::decodeUCSPacket(EQUDPIPPacketFormat& packet)
+{
+ // Full UDP payload starting at the SOE net-opcode. UCS chat data is
+ // OP_Packet-wrapped, so the encrypted region sits after a variable-length
+ // SOE header. We deliberately do NOT strip that header: the chained XOR
+ // below is position-absolute, so decoding the whole payload leaves the
+ // encrypted region correctly decoded wherever it begins, and the consumer
+ // anchors its parse on a trailer rather than a fixed offset.
+ const uint8_t* raw = packet.getUDPPayload();
+ uint32_t rawLen = packet.getUDPPayloadLength();
+
+ // Too short to carry a chat record (ACKs, keepalives, etc.) -> ignore.
+ if ((raw == NULL) || (rawLen < 12))
+ return;
+
+ // Self-synchronizing XOR: plain[i] = cipher[i] ^ cipher[i-4] for i>=4. Read
+ // the ORIGINAL ciphertext at i-4 (from a pristine input buffer), NOT the
+ // already-decoded value -- decoding forward in place corrupts everything
+ // from byte 8 on.
+ QByteArray in((const char*)raw, rawLen);
+ QByteArray buf(in);
+ int len = in.size();
+ for (int i = 4; i < len; ++i)
+ buf[i] = (char)((uint8_t)in[i] ^ (uint8_t)in[i - 4]);
+
+ // Direction: from our client, or to it.
+ uint8_t dir = (packet.getIPv4SourceN() == m_client_addr) ?
+ DIR_Client : DIR_Server;
+
+ emit decodedChatMessage((const uint8_t*)buf.data(), (size_t)buf.size(), dir);
+}
+
+////////////////////////////////////////////////////
// Handle zone2client stream closing
void EQPacket::closeStream(uint32_t sessionId, EQStreamID streamId)
{
Modified: showeq/branches/xerxes-trunk-dev/src/packet.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/packet.h 2026-07-18 01:43:54 UTC (rev 1673)
+++ showeq/branches/xerxes-trunk-dev/src/packet.h 2026-07-18 02:18:34 UTC (rev 1674)
@@ -164,6 +164,9 @@
uint16_t opcode, const EQPacketOPCode* opcodeEntry,
bool unknown);
+ // UCS: decoded cross-zone/global chat payload, ready for display.
+ void decodedChatMessage(const uint8_t* data, size_t len, uint8_t dir);
+
private:
void validateIP();
@@ -177,6 +180,11 @@
bool m_detectingClient;
in_addr_t m_client_addr;
+ // UCS (Universal Chat Service) session port, learned dynamically from the
+ // SessionResponse whose format byte is 0x04 (XOR-obfuscated chat session).
+ // Varies by server, so it is not hardcoded. 0 = not yet seen.
+ in_port_t m_ucsPort;
+
uint16_t m_arqSeqGiveUp;
QString m_device;
QString m_ip;
@@ -202,6 +210,9 @@
void connectStream(EQPacketStream* stream);
void dispatchPacket (int size, unsigned char *buffer);
void dispatchPacket(EQUDPIPPacketFormat& packet);
+ // UCS: decode a Universal Chat Service (cross-zone chat) SOE data packet
+ // inline, bypassing the normal stream/CRC/reassembly path.
+ void decodeUCSPacket(EQUDPIPPacketFormat& packet);
protected slots:
void resetEQPacket();
void dispatchWorldChatData (size_t len, uint8_t* data, uint8_t direction = 0);
Modified: showeq/branches/xerxes-trunk-dev/src/packetcapture.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/packetcapture.cpp 2026-07-18 01:43:54 UTC (rev 1673)
+++ showeq/branches/xerxes-trunk-dev/src/packetcapture.cpp 2026-07-18 02:18:34 UTC (rev 1674)
@@ -393,11 +393,15 @@
}
else
{
- // restrict to client/zone port and world server ports
+ // restrict to client/zone port and world server ports. Include
+ // WorldServerChat2Port (UCS/xgame chat) so the separate cross-zone
+ // chat session survives the post-detection filter narrowing - it
+ // rides a different port than the zone stream.
pfb += sprintf(pfb, "udp");
- pfb += sprintf(pfb, " and (portrange %d-%d or port %d or port %d)",
+ pfb += sprintf(pfb, " and (portrange %d-%d or port %d or port %d or port %d)",
WorldServerGeneralMinPort, WorldServerGeneralMaxPort,
- WorldServerChatPort, (client_port) ? client_port : zone_port);
+ WorldServerChatPort, WorldServerChat2Port,
+ (client_port) ? client_port : zone_port);
}
if (address_type == IP_ADDRESS_TYPE)
Modified: showeq/branches/xerxes-trunk-dev/src/terminal.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/terminal.cpp 2026-07-18 01:43:54 UTC (rev 1673)
+++ showeq/branches/xerxes-trunk-dev/src/terminal.cpp 2026-07-18 02:18:34 UTC (rev 1674)
@@ -67,6 +67,8 @@
"\e[1;37;43m", // 32 - Caution
"\e[1;37;46m", // 33 - Hunt
"\e[1;37;44m", // 34 - Locate
+ NULL, // 35
+ "\e[38;2;125;75;175m", // 36 - Chat (UCS: in-game universal-chat purple 125,75,175)
};
//----------------------------------------------------------------------
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <xer...@us...> - 2026-07-18 01:43:57
|
Revision: 1673
http://sourceforge.net/p/seq/svn/1673
Author: xerxes00
Date: 2026-07-18 01:43:54 +0000 (Sat, 18 Jul 2026)
Log Message:
-----------
live-fixes: logout/exp opcode wiring, modern exp+AA display, parser/map/payload fixes
- map + wire OP_Logout (camp-accept handshake) and OP_ExpUpdate; add a
uint16_t payload primitive so 2-byte opcodes can strict size-match
- player: drop the legacy calc_exp bar reconstruction (2001/2005) that
renders as garbage on current EQ; show Exp and AA only from their wire
updates, blank until the first arrives (no zone-in flash)
- eqstr: stop parsing length-prefixed args past the payload end
- mapcore: keep comma-bearing map location labels intact
- packetstream: clear the payload candidate on no-match so the warning fires
- util: add Berserker to the class string
Modified Paths:
--------------
showeq/branches/xerxes-trunk-dev/conf/zoneopcodes.xml
showeq/branches/xerxes-trunk-dev/src/eqstr.cpp
showeq/branches/xerxes-trunk-dev/src/interface.cpp
showeq/branches/xerxes-trunk-dev/src/mapcore.cpp
showeq/branches/xerxes-trunk-dev/src/packetinfo.cpp
showeq/branches/xerxes-trunk-dev/src/packetstream.cpp
showeq/branches/xerxes-trunk-dev/src/player.cpp
showeq/branches/xerxes-trunk-dev/src/player.h
showeq/branches/xerxes-trunk-dev/src/util.cpp
Modified: showeq/branches/xerxes-trunk-dev/conf/zoneopcodes.xml
===================================================================
--- showeq/branches/xerxes-trunk-dev/conf/zoneopcodes.xml 2026-07-18 00:12:00 UTC (rev 1672)
+++ showeq/branches/xerxes-trunk-dev/conf/zoneopcodes.xml 2026-07-18 01:43:54 UTC (rev 1673)
@@ -37,8 +37,8 @@
<comment>Receiving AA experience. Also when percent to AA changes.</comment>
<payload dir="server" typename="altExpUpdateStruct" sizechecktype="match"/>
</opcode>
- <opcode id="ffff" name="OP_ExpUpdate" updated="07/19/23">
- <comment>ExpUpdateCode</comment>
+ <opcode id="10cc" name="OP_ExpUpdate" updated="07/15/26">
+ <comment>ExpUpdateCode - exp permille@0 (0..100000), type@8 0=set/2=update</comment>
<payload dir="server" typename="expUpdateStruct" sizechecktype="match"/>
</opcode>
<opcode id="3425" name="OP_GuildMOTD" updated="07/15/26">
@@ -295,9 +295,10 @@
<comment>Movement history for speed/movement hack detection</comment>
<payload dir="client" typename="uint8_t" sizechecktype="none"/>
</opcode>
- <opcode id="ffff" name="OP_Logout" updated="12/12/12">
- <comment></comment>
- <payload dir="server" typename="uint8_t" sizechecktype="match"/>
+ <opcode id="deb4" name="OP_Logout" updated="07/15/26">
+ <comment>Camp accept - s2c 2B {00 01}; c2s 0B completion</comment>
+ <payload dir="server" typename="uint16_t" sizechecktype="match"/>
+ <payload dir="client" typename="none" sizechecktype="match"/>
</opcode>
<opcode id="ffff" name="OP_UIUpdate" updated="05/21/14">
<comment>Seems to be sent to handle a variety of UI updates - Variable length</comment>
Modified: showeq/branches/xerxes-trunk-dev/src/eqstr.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/eqstr.cpp 2026-07-18 00:12:00 UTC (rev 1672)
+++ showeq/branches/xerxes-trunk-dev/src/eqstr.cpp 2026-07-18 01:43:54 UTC (rev 1673)
@@ -182,12 +182,18 @@
size_t totalArgsLen = 0;
const char* curArg;
uint32_t curSize = 0;
- while (totalArgsLen < argsLen)
+ while (totalArgsLen + 4 <= argsLen)
{
curArg = arguments + totalArgsLen;
curSize = eqtohuint32((const uint8_t*) curArg);
curArg += 4;
+ // a wire length beyond the remaining payload means the arguments
+ // are not in this length-prefixed layout - stop rather than
+ // allocate off garbage
+ if (curSize > argsLen - totalArgsLen - 4)
+ break;
+
if (curSize > 0) {
// insert argument into the argument list
argList.push_back(QString::fromUtf8(curArg, curSize));
Modified: showeq/branches/xerxes-trunk-dev/src/interface.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/interface.cpp 2026-07-18 00:12:00 UTC (rev 1672)
+++ showeq/branches/xerxes-trunk-dev/src/interface.cpp 2026-07-18 01:43:54 UTC (rev 1673)
@@ -746,7 +746,7 @@
"moneyOnCorpseStruct", SZC_Match,
m_messageShell, SLOT(moneyOnCorpse(const uint8_t*)));
m_packet->connect2("OP_Logout", SP_Zone, DIR_Server,
- "none", SZC_Match,
+ "uint16_t", SZC_Match,
m_messageShell, SLOT(logOut(const uint8_t*, size_t, uint8_t)));
m_packet->connect2("OP_NewZone", SP_Zone, DIR_Server,
"uint8_t", SZC_None,
Modified: showeq/branches/xerxes-trunk-dev/src/mapcore.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/mapcore.cpp 2026-07-18 00:12:00 UTC (rev 1672)
+++ showeq/branches/xerxes-trunk-dev/src/mapcore.cpp 2026-07-18 01:43:54 UTC (rev 1673)
@@ -1127,9 +1127,9 @@
filelines, count, (const char*)*lit);
#endif
- if (count != 8)
+ if (count < 8)
{
- seqWarn("Error reading L line %d on map '%s'! %d is an incorrect field count!",
+ seqWarn("Error reading P line %d on map '%s'! %d is an incorrect field count!",
filelines, fileName.toLatin1().data(), count);
continue;
}
@@ -1142,8 +1142,14 @@
g = (*fit++).toUShort();
b = (*fit++).toUShort();
fit++; // skip unknown
- name = (*fit); // Location name, conver
-
+ // Location name is the remaining field(s). Labels can contain the field
+ // separator, e.g. "Locked_Door_(Shiny_Metal_Key,Picklock_40)", which the
+ // comma split breaks into extra fields (count > 8), so rejoin from here
+ // to the end instead of taking a single field.
+ name.clear();
+ for (; fit != fields.end(); ++fit)
+ name += (name.isEmpty() ? QString() : QString(",")) + *fit;
+
// convert underscores to spaces.
name.replace("_", " ");
Modified: showeq/branches/xerxes-trunk-dev/src/packetinfo.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/packetinfo.cpp 2026-07-18 00:12:00 UTC (rev 1672)
+++ showeq/branches/xerxes-trunk-dev/src/packetinfo.cpp 2026-07-18 01:43:54 UTC (rev 1673)
@@ -98,6 +98,7 @@
// these we add manually to handle strings and octet streams
addStruct("char", sizeof(char));
addStruct("uint8_t", sizeof(uint8_t));
+ addStruct("uint16_t", sizeof(uint16_t));
addStruct("none", 0);
addStruct("unknown", 0);
}
Modified: showeq/branches/xerxes-trunk-dev/src/packetstream.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/packetstream.cpp 2026-07-18 00:12:00 UTC (rev 1672)
+++ showeq/branches/xerxes-trunk-dev/src/packetstream.cpp 2026-07-18 01:43:54 UTC (rev 1673)
@@ -126,9 +126,12 @@
return false;
}
- EQPacketPayload* payload;
+ EQPacketPayload* payload = nullptr;
- // try to find a matching payload for this opcode
+ // try to find a matching payload for this opcode. The candidate must be
+ // cleared when it does not match: leaving the loop variable set silently
+ // binds the receiver to the LAST payload of the opcode when no typename
+ // matches, bypassing the no-matching-payload warning below.
EQPayloadListIterator pit(*opcode);
while (pit.hasNext())
{
@@ -136,10 +139,11 @@
if (!payload)
break;
// if all the parameters match, then use this payload
- if ((payload->dir() & m_dir) &&
- (payload->typeName() == payloadType) &&
+ if ((payload->dir() & m_dir) &&
+ (payload->typeName() == payloadType) &&
(payload->sizeCheckType() == szt))
break;
+ payload = nullptr;
}
// if no payload found, create one and issue a warning
Modified: showeq/branches/xerxes-trunk-dev/src/player.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/player.cpp 2026-07-18 00:12:00 UTC (rev 1672)
+++ showeq/branches/xerxes-trunk-dev/src/player.cpp 2026-07-18 01:43:54 UTC (rev 1673)
@@ -192,6 +192,8 @@
m_playerLanguages[a] = 255; // indicate an invalid value
m_mana = 0;
+ m_expBarProgress = -1;
+ m_validAltExp = false;
setLevel (1);
setRace (1);
setClassVal (1);
@@ -352,6 +354,9 @@
void Player::player(const charProfileStruct* player)
{
+ // new zone-in: let the first real exp/AA opcode print, then dedup the
+ // 2-3 identical repeats the server sends right after
+ m_lastExpRaw = m_lastAltExpRaw = 0xffffffff;
QString messag;
if (m_name != player->name)
@@ -410,13 +415,26 @@
// Exp
m_currentExp = player->exp;
- m_currentAltExp = player->expAA;
+ // The profile's expAA is on a different scale than the display and reads
+ // as garbage; the real value comes from OP_AAExpUpdate (updateAltExp).
+ // Don't let the profile clobber a wire value we've already received.
+ if (!m_validAltExp)
+ m_currentAltExp = player->expAA;
m_validExp = true;
-
- emit expChangedInt (m_currentExp, m_minExp, m_maxExp);
- emit expAltChangedInt(m_currentAltExp, 0, 15000000);
- emit setAltExp(m_currentAltExp, 15000000, 15000000/330, m_currentAApts);
+ // Show the exp bar only from the wire's exact permille. The legacy
+ // m_currentExp/m_minExp/m_maxExp reconstruction (calc_exp, from 2001/2005)
+ // predates the modern permille wire format and renders as garbage on
+ // current EQ, so suppress it until the first OP_ExpUpdate set arrives
+ // (a few seconds after zone-in) rather than flashing a bad value.
+ if (m_expBarProgress >= 0)
+ emit expChangedInt (int(m_expBarProgress), 0, 100000);
+ // Same for AA: suppress until the first OP_AAExpUpdate set (see updateAltExp).
+ if (m_validAltExp)
+ {
+ emit expAltChangedInt(m_currentAltExp, 0, 15000000);
+ emit setAltExp(m_currentAltExp, 15000000, 15000000/330, m_currentAApts);
+ }
if (showeq_params->savePlayerState)
savePlayerState();
@@ -611,6 +629,10 @@
void Player::updateAltExp(const uint8_t* data)
{
const altExpUpdateStruct* altexp = (const altExpUpdateStruct*)data;
+ // server re-sends the same AA update at zone-in; skip no-change repeats
+ if (altexp->altexp == m_lastAltExpRaw)
+ return;
+ m_lastAltExpRaw = altexp->altexp;
/* purple: I got no idea what is up here. This seems to be written like
* the packet from the server gives the entire exp bump and not
@@ -624,7 +646,11 @@
else
expIncrement = 0;
*/
- uint32_t realExp = altexp->altexp * (15000000 / 330);
+ // This EQ era sends altexp as percent x 1000 (0-100000); convert to the
+ // 0-330 tick scale the emitted totals expect. (Stock's raw altexp *
+ // (15000000/330) assumed 0-330 and produced multi-billion garbage.)
+ uint32_t altTicks = uint32_t(uint64_t(altexp->altexp) * 330 / 100000);
+ uint32_t realExp = altTicks * (15000000 / 330);
uint32_t expIncrement;
if (realExp > m_currentExp)
@@ -638,10 +664,11 @@
m_currentAApts = altexp->aapoints;
m_currentAltExp = realExp;
+ m_validAltExp = true;
emit expAltChangedInt(m_currentAltExp, 0, 15000000);
- emit newAltExp(expIncrement, m_currentAltExp, altexp->altexp,
+ emit newAltExp(expIncrement, m_currentAltExp, altTicks,
15000000, 15000000/330, m_currentAApts);
if (showeq_params->savePlayerState)
@@ -651,6 +678,10 @@
void Player::updateExp(const uint8_t* data)
{
const expUpdateStruct* exp = (const expUpdateStruct*)data;
+ // server re-sends the same exp update at zone-in; skip no-change repeats
+ if (exp->exp == m_lastExpRaw)
+ return;
+ m_lastExpRaw = exp->exp;
// if this is just setting the percentage, then do nothing (use info from
// player packet).
@@ -665,7 +696,10 @@
}
#endif
- uint32_t realExp = (m_tickExp * exp->exp) + m_minExp;
+ // This EQ era sends exp as level progress in percent x 1000 (0-100000);
+ // convert to the 0-330 tick scale the math below expects.
+ uint32_t expTicks = uint32_t(uint64_t(exp->exp) * 330 / 100000);
+ uint32_t realExp = (m_tickExp * expTicks) + m_minExp;
uint32_t expIncrement;
// if realExperience is greater then current expereince, calculate the
@@ -680,11 +714,16 @@
m_validExp = true;
// signal the new experience
- emit newExp(expIncrement, realExp, exp->exp,
+ emit newExp(expIncrement, realExp, expTicks,
m_minExp, m_maxExp, m_tickExp);
-
- emit expChangedInt (realExp, m_minExp, m_maxExp);
+ // Display the wire's exact bar progress (percent*1000, 0-100000). The
+ // realExp reconstruction above feeds the console prints only; the permille
+ // is authoritative and avoids a calc_exp reconstruction that can invert
+ // (m_maxExp below m_minExp) on some level curves.
+ m_expBarProgress = exp->exp;
+ emit expChangedInt (int(exp->exp), 0, 100000);
+
if(m_freshKill)
{
emit expGained( m_lastSpawnKilledName,
@@ -746,7 +785,9 @@
expIncrement,
m_zoneMgr->longZoneName());
- emit expChangedInt( m_currentExp, m_minExp, m_maxExp);
+ // permille only; suppress the legacy calc_exp fallback (see player())
+ if (m_expBarProgress >= 0)
+ emit expChangedInt (int(m_expBarProgress), 0, 100000);
// update the con table
fillConTable();
@@ -1015,10 +1056,29 @@
valid = m_validStam;
break;
case LIST_EXP:
- curValue = m_currentExp;
- maxValue = m_maxExp;
- valid = m_validExp;
+ // Only the wire permille is trustworthy on modern EQ; the legacy
+ // m_currentExp/m_maxExp values render as garbage (see player()), so mark
+ // the row invalid until the first OP_ExpUpdate set populates the permille.
+ if (m_expBarProgress >= 0)
+ {
+ curValue = uint32_t(m_expBarProgress);
+ maxValue = 100000;
+ valid = m_validExp;
+ }
+ else
+ {
+ curValue = 0;
+ maxValue = 100000;
+ valid = false;
+ }
break;
+ case LIST_ALTEXP:
+ // Only trust the wire value from OP_AAExpUpdate; the profile's expAA reads
+ // as garbage, so mark the row invalid until the first update arrives.
+ curValue = m_currentAltExp;
+ maxValue = 15000000;
+ valid = m_validAltExp;
+ break;
case LIST_FOOD:
curValue = m_food;
maxValue = 127;
Modified: showeq/branches/xerxes-trunk-dev/src/player.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/player.h 2026-07-18 00:12:00 UTC (rev 1672)
+++ showeq/branches/xerxes-trunk-dev/src/player.h 2026-07-18 01:43:54 UTC (rev 1673)
@@ -248,8 +248,18 @@
// ExperienceWindow needs this
uint32_t m_currentAltExp;
+ int64_t m_expBarProgress; // exact level progress from the wire
+ // (percent*1000, 0-100000); -1 = unset.
+ // Display uses this instead of the calc_exp
+ // reconstruction, whose curve can invert
+ // (m_maxExp below m_minExp) on some levels.
+ bool m_validAltExp; // true once OP_AAExpUpdate has set a real AA
+ // value; until then the profile's expAA reads
+ // as garbage, so the AA bar/row is suppressed.
uint16_t m_currentAApts;
uint32_t m_currentExp;
+ uint32_t m_lastExpRaw = 0xffffffff; // dedup redundant zone-in exp packets
+ uint32_t m_lastAltExpRaw = 0xffffffff; // dedup redundant zone-in AA-exp packets
uint32_t m_minExp;
uint32_t m_maxExp;
uint32_t m_tickExp;
Modified: showeq/branches/xerxes-trunk-dev/src/util.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/util.cpp 2026-07-18 00:12:00 UTC (rev 1672)
+++ showeq/branches/xerxes-trunk-dev/src/util.cpp 2026-07-18 01:43:54 UTC (rev 1673)
@@ -284,12 +284,14 @@
class_str += "ENC ";
if (classes & 16384)
class_str += "BST ";
+ if (classes & 32768)
+ class_str += "BER ";
if (classes >= 32768) // 2^15 aka (1 << 15)
{
int new_class;
- for (int i = 15; i < 31; ++i)
- {
+ for (int i = 16; i < 31; ++i)
+ {
new_class = 1 << i;
if (classes & new_class)
{
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <xer...@us...> - 2026-07-18 00:12:02
|
Revision: 1672
http://sourceforge.net/p/seq/svn/1672
Author: xerxes00
Date: 2026-07-18 00:12:00 +0000 (Sat, 18 Jul 2026)
Log Message:
-----------
Widen spell ids to 32-bit
EQ spell ids now exceed 65535 (e.g. pet illusions at 69633), so the
uint16_t spell id used throughout the spell subsystem truncated them and
resolved the wrong name (69633 & 0xffff = 4097). Widen the id to uint32_t
across Spell, SpellItem, the buff list, spell()/findSpell()/update()/
spell_name(), and the spell-file parse; the spell array stays a flat table
(max id ~74000, ~580KB). Show an unknown-id name in decimal so it can be
looked up, and fix an off-by-one so the highest-id spell is reachable.
Spell id 32-bit widening contributed by Skik.
Modified Paths:
--------------
showeq/branches/xerxes-trunk-dev/src/everquest.h
showeq/branches/xerxes-trunk-dev/src/spells.cpp
showeq/branches/xerxes-trunk-dev/src/spells.h
showeq/branches/xerxes-trunk-dev/src/spellshell.cpp
showeq/branches/xerxes-trunk-dev/src/spellshell.h
showeq/branches/xerxes-trunk-dev/src/util.cpp
showeq/branches/xerxes-trunk-dev/src/util.h
Modified: showeq/branches/xerxes-trunk-dev/src/everquest.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/everquest.h 2026-07-18 00:07:36 UTC (rev 1671)
+++ showeq/branches/xerxes-trunk-dev/src/everquest.h 2026-07-18 00:12:00 UTC (rev 1672)
@@ -2079,7 +2079,7 @@
{
/*0000*/ uint16_t target; // Target ID
/*0002*/ uint16_t source; // SourceID
-/*0004*/ int16_t spell; // SpellID
+/*0004*/ uint16_t spell; // SpellID (unsigned: modern IDs > 32767 sign-extend if int16)
/*0006*/ uint8_t unknown0006[6];
/*0012*/ uint8_t level; // Caster level
/*0013*/ uint8_t unknown0013[43]; // ***Placeholder
@@ -2095,7 +2095,7 @@
{
/*0000*/ uint16_t target; // Target ID
/*0002*/ uint16_t source; // SourceID
-/*0004*/ int16_t spell; // SpellID
+/*0004*/ uint16_t spell; // SpellID (unsigned: modern IDs > 32767 sign-extend if int16)
/*0006*/ uint8_t unknown0006[6];
/*0012*/ uint8_t level; // Caster level
/*0013*/ uint8_t unknown0013[43]; // ***Placeholder
Modified: showeq/branches/xerxes-trunk-dev/src/spells.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/spells.cpp 2026-07-18 00:07:36 UTC (rev 1671)
+++ showeq/branches/xerxes-trunk-dev/src/spells.cpp 2026-07-18 00:12:00 UTC (rev 1672)
@@ -171,7 +171,7 @@
QStringList spellInfo = spells_enLine.split("^");
// I'll add support for the rest of the fields later
- m_spell = spellInfo[0].toUShort();
+ m_spell = spellInfo[0].toUInt();
m_name = spellInfo[1];
m_buffDurationFormula = spellInfo[11].toUShort(); //spells_us.txt layout changed December 2018
m_buffDurationArgument = spellInfo[12].toUShort(); //spells_us.txt layout changed December 2018
@@ -336,7 +336,7 @@
// if a spell list has been allocated, delete it's elements and then itself
if (m_spells)
{
- for (int i = 0; i <= m_maxSpell; i++)
+ for (uint32_t i = 0; i <= m_maxSpell; i++)
{
if (m_spells[i] != NULL)
{
@@ -350,10 +350,11 @@
}
}
-const Spell* Spells::spell(uint16_t spell) const
+const Spell* Spells::spell(uint32_t spell) const
{
- // make sure the spell is within range
- if (spell >= m_maxSpell)
+ // make sure the spell is within range (array is sized [m_maxSpell + 1], so
+ // the highest id is a valid index)
+ if (spell > m_maxSpell)
return NULL;
// return the appropriate spell
Modified: showeq/branches/xerxes-trunk-dev/src/spells.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/spells.h 2026-07-18 00:07:36 UTC (rev 1671)
+++ showeq/branches/xerxes-trunk-dev/src/spells.h 2026-07-18 00:12:00 UTC (rev 1672)
@@ -45,7 +45,7 @@
~Spell();
// accessors
- uint16_t spell() const { return m_spell; }
+ uint32_t spell() const { return m_spell; }
const QString& name() const { return m_name; }
uint8_t level(uint8_t class_) const;
uint8_t targetType() const { return m_targetType; }
@@ -57,7 +57,7 @@
private:
QString m_name;
- uint16_t m_spell;
+ uint32_t m_spell;
int16_t m_buffDurationFormula;
int16_t m_buffDurationArgument;
uint8_t m_targetType;
@@ -73,11 +73,11 @@
void loadSpells(const QString& spellsFileName);
void unloadSpells(void);
- const Spell* spell(uint16_t spell) const;
- uint16_t maxSpell() const { return m_maxSpell; }
+ const Spell* spell(uint32_t spell) const;
+ uint32_t maxSpell() const { return m_maxSpell; }
private:
- uint16_t m_maxSpell;
+ uint32_t m_maxSpell;
Spell** m_spells;
};
Modified: showeq/branches/xerxes-trunk-dev/src/spellshell.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/spellshell.cpp 2026-07-18 00:07:36 UTC (rev 1671)
+++ showeq/branches/xerxes-trunk-dev/src/spellshell.cpp 2026-07-18 00:12:00 UTC (rev 1672)
@@ -87,7 +87,7 @@
return text;
}
-void SpellItem::update(uint16_t spellId, const Spell* spell, int duration,
+void SpellItem::update(uint32_t spellId, const Spell* spell, int duration,
uint16_t casterId, const QString& casterName,
uint16_t targetId, const QString& targetName)
{
@@ -143,7 +143,7 @@
clear();
}
-SpellItem* SpellShell::findSpell(uint16_t spellId,
+SpellItem* SpellShell::findSpell(uint32_t spellId,
uint16_t targetId, const QString& targetName)
{
for(QList<SpellItem*>::Iterator it = m_spellList.begin();
@@ -162,7 +162,7 @@
return NULL;
}
-SpellItem* SpellShell::findSpell(int spell_id)
+SpellItem* SpellShell::findSpell(uint32_t spell_id)
{
for(QList<SpellItem*>::Iterator it = m_spellList.begin();
it != m_spellList.end();
@@ -363,7 +363,7 @@
uint32_t spawnId = *(const uint32_t*)data;
uint8_t count = data[9];
- QList<uint16_t> listed;
+ QList<uint32_t> listed;
// The list is scoped to the spawn id it carries: the player (OP_BuffList),
// the pet (OP_PetBuffList), etc. Resolve the owner - player when the id
@@ -406,7 +406,7 @@
if (pos < len) pos++; // NUL
pos += 4; // u32 slot
- listed.append(uint16_t(spellId));
+ listed.append(spellId);
// A null spell (id not in this spells_us.txt - e.g. a new spell after a
// game patch, or no spell file) is still shown and tracked: update() falls
Modified: showeq/branches/xerxes-trunk-dev/src/spellshell.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/spellshell.h 2026-07-18 00:07:36 UTC (rev 1671)
+++ showeq/branches/xerxes-trunk-dev/src/spellshell.h 2026-07-18 00:12:00 UTC (rev 1672)
@@ -63,7 +63,7 @@
SpellItem();
// get accessors
- uint16_t spellId() const;
+ uint32_t spellId() const;
uint16_t targetId() const;
uint16_t casterId() const;
time_t castTime() const;
@@ -78,7 +78,7 @@
void setPermanent(bool p) { m_permanent = p; }
// set accessors
- void setSpellId(uint16_t spellid);
+ void setSpellId(uint32_t spellid);
void setTargetId(uint16_t target);
void setCasterId(uint16_t caster);
void setSpellName(const QString& name);
@@ -86,10 +86,10 @@
void setTargetName(const QString& name);
void updateCastTime();
- void update(uint16_t spellId, const Spell* spell, int duration,
+ void update(uint32_t spellId, const Spell* spell, int duration,
uint16_t casterId, const QString& casterName,
uint16_t targetId, const QString& targetName);
-
+
private:
QString m_spellName;
QString m_casterName;
@@ -98,8 +98,8 @@
bool m_permanent;
timeval m_castTime;
- uint16_t m_spellId;
- uint16_t m_casterId;
+ uint32_t m_spellId;
+ uint16_t m_casterId;
uint16_t m_targetId;
struct startCastStruct m_cast; // Needed?
@@ -106,7 +106,7 @@
};
-inline uint16_t SpellItem::spellId() const
+inline uint32_t SpellItem::spellId() const
{
return m_spellId;
}
@@ -151,7 +151,7 @@
return m_casterName;
}
-inline void SpellItem::setSpellId(uint16_t spellid)
+inline void SpellItem::setSpellId(uint32_t spellid)
{
m_spellId = spellid;
}
@@ -213,10 +213,10 @@
protected:
void deleteSpell(SpellItem *);
- SpellItem* findSpell(uint16_t spellId,
+ SpellItem* findSpell(uint32_t spellId,
uint16_t targetId, const QString& targetName);
- SpellItem* findSpell(int spell_id);
- SpellItem* FindSpell(int spell_id, int target_id);
+ SpellItem* findSpell(uint32_t spell_id);
+ SpellItem* FindSpell(uint32_t spell_id, int target_id);
private:
Player* m_player;
Modified: showeq/branches/xerxes-trunk-dev/src/util.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/util.cpp 2026-07-18 00:07:36 UTC (rev 1671)
+++ showeq/branches/xerxes-trunk-dev/src/util.cpp 2026-07-18 00:12:00 UTC (rev 1672)
@@ -121,12 +121,12 @@
}
QString
-spell_name (uint16_t spellId)
+spell_name (uint32_t spellId)
{
if (spellId < (sizeof(spellInfo) / sizeof(spellInfoStruct)))
return spellInfo[spellId].name;
else
- return QString::number(spellId, 16);
+ return QString::number(spellId);
}
QString
Modified: showeq/branches/xerxes-trunk-dev/src/util.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/util.h 2026-07-18 00:07:36 UTC (rev 1671)
+++ showeq/branches/xerxes-trunk-dev/src/util.h 2026-07-18 00:12:00 UTC (rev 1672)
@@ -51,7 +51,7 @@
QString Commanate (uint32_t number);
QString classString(uint8_t classVal);
-QString spell_name (uint16_t spellId);
+QString spell_name (uint32_t spellId);
QString language_name (uint8_t langId);
QString skill_name (uint16_t skillId);
QString size_name (uint8_t size);
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <xer...@us...> - 2026-07-18 00:07:39
|
Revision: 1671
http://sourceforge.net/p/seq/svn/1671
Author: xerxes00
Date: 2026-07-18 00:07:36 +0000 (Sat, 18 Jul 2026)
Log Message:
-----------
Add pet buff window, buff-list decode, and buff-freeze zones
Decode OP_BuffList (self) and OP_PetBuffList (pet) as authoritative
per-spawn buff sets: add/update buffs with the server's real remaining
durations, prune buffs the server dropped (so clicking a buff off removes
it from the list), and resolve the owner by spawn id (an unresolved spawn
is skipped, never mis-attributed to the player). Skip the non-list marker
records, and show a spell with no spells_us.txt entry by its numeric id so
it never silently vanishes. Map OP_TargetBuffList and OP_ShortBuffList for
documentation (not yet wired).
Hold buff durations steady in buff-freeze zones (Plane of Knowledge, guild
lobby/hall, Nexus) for spells flagged duration-frozen in spells_us.txt
(new field-125 parse), matching the client. Trim the numeric instance
suffix from spawn names shown in the list.
Modified Paths:
--------------
showeq/branches/xerxes-trunk-dev/conf/zoneopcodes.xml
showeq/branches/xerxes-trunk-dev/src/interface.cpp
showeq/branches/xerxes-trunk-dev/src/spells.cpp
showeq/branches/xerxes-trunk-dev/src/spells.h
showeq/branches/xerxes-trunk-dev/src/spellshell.cpp
showeq/branches/xerxes-trunk-dev/src/spellshell.h
Modified: showeq/branches/xerxes-trunk-dev/conf/zoneopcodes.xml
===================================================================
--- showeq/branches/xerxes-trunk-dev/conf/zoneopcodes.xml 2026-07-17 20:05:20 UTC (rev 1670)
+++ showeq/branches/xerxes-trunk-dev/conf/zoneopcodes.xml 2026-07-18 00:07:36 UTC (rev 1671)
@@ -1008,4 +1008,20 @@
<comment></comment>
</opcode>
-->
+ <opcode id="0ddc" name="OP_BuffList" updated="07/15/26">
+ <comment>Authoritative per-spawn buff set (spellId + remaining ticks + caster); re-fires on every buff change.</comment>
+ <payload dir="server" typename="uint8_t" sizechecktype="none"/>
+ </opcode>
+ <opcode id="9090" name="OP_PetBuffList" updated="07/15/26">
+ <comment>Per-spawn buff set for the player's pet; same format as OP_BuffList, decoded by SpellShell::buffList.</comment>
+ <payload dir="server" typename="uint8_t" sizechecktype="none"/>
+ </opcode>
+ <opcode id="d702" name="OP_TargetBuffList" updated="07/15/26">
+ <comment>Buff set of the current target; same format as OP_BuffList. Mapped to silence the unknown log; not wired to the spell list (avoids target-buff clutter).</comment>
+ <payload dir="server" typename="uint8_t" sizechecktype="none"/>
+ </opcode>
+ <opcode id="0faf" name="OP_ShortBuffList" updated="07/15/26">
+ <comment>Self short-buff/song window (Chorus of Marr etc). Fixed-size format, distinct from OP_BuffList; mapped to silence the unknown log, decoder TBD.</comment>
+ <payload dir="server" typename="uint8_t" sizechecktype="none"/>
+ </opcode>
</seqopcodes>
Modified: showeq/branches/xerxes-trunk-dev/src/interface.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/interface.cpp 2026-07-17 20:05:20 UTC (rev 1670)
+++ showeq/branches/xerxes-trunk-dev/src/interface.cpp 2026-07-18 00:07:36 UTC (rev 1671)
@@ -986,6 +986,11 @@
// connect the SpellShell slots to ZoneMgr signals
connect(m_zoneMgr, SIGNAL(zoneChanged(const QString&)),
m_spellShell, SLOT(zoneChanged()));
+ // track whether the current zone freezes buff durations
+ connect(m_zoneMgr, SIGNAL(zoneBegin(const QString&)),
+ m_spellShell, SLOT(updateFreezeZone(const QString&)));
+ connect(m_zoneMgr, SIGNAL(zoneChanged(const QString&)),
+ m_spellShell, SLOT(updateFreezeZone(const QString&)));
// connect the SpellShell slots to SpawnShell signals
connect(m_spawnShell, SIGNAL(killSpawn(const Item*, const Item*, uint16_t)),
@@ -1004,6 +1009,16 @@
m_packet->connect2("OP_Buff", SP_Zone, DIR_Server|DIR_Client,
"buffStruct", SZC_Match,
m_spellShell, SLOT(buff(const uint8_t*, size_t, uint8_t)));
+ // OP_BuffList is the authoritative per-spawn buff set, re-sent on every buff
+ // change with real remaining durations; buffList adds/updates and prunes.
+ // OP_PetBuffList is the same format for the player's pet, so it shares the
+ // handler (which scopes everything by the spawn id in the packet).
+ m_packet->connect2("OP_BuffList", SP_Zone, DIR_Server,
+ "uint8_t", SZC_None,
+ m_spellShell, SLOT(buffList(const uint8_t*, size_t, uint8_t)));
+ m_packet->connect2("OP_PetBuffList", SP_Zone, DIR_Server,
+ "uint8_t", SZC_None,
+ m_spellShell, SLOT(buffList(const uint8_t*, size_t, uint8_t)));
m_packet->connect2("OP_Action", SP_Zone, DIR_Server|DIR_Client,
"actionStruct", SZC_Match,
m_spellShell, SLOT(action(const uint8_t*, size_t, uint8_t)));
Modified: showeq/branches/xerxes-trunk-dev/src/spells.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/spells.cpp 2026-07-17 20:05:20 UTC (rev 1670)
+++ showeq/branches/xerxes-trunk-dev/src/spells.cpp 2026-07-18 00:07:36 UTC (rev 1671)
@@ -176,6 +176,9 @@
m_buffDurationFormula = spellInfo[11].toUShort(); //spells_us.txt layout changed December 2018
m_buffDurationArgument = spellInfo[12].toUShort(); //spells_us.txt layout changed December 2018
m_targetType = uint8_t(spellInfo[32].toUShort()); //spells_us.txt layout changed December 2018
+ // durationfreeze (IS_COUNTDOWN_HELD): set on beneficial buffs that the server
+ // holds steady in buff-freeze zones (Plane of Knowledge, guild lobby, etc).
+ m_buffDurationFrozen = (spellInfo.count() > 125) && (spellInfo[125].toUShort() != 0);
for (size_t i = 0; i < playerClasses; i++)
m_classLevels[i] = uint8_t(spellInfo[38 + i].toUShort()); //spells_us.txt layout changed December 2018
Modified: showeq/branches/xerxes-trunk-dev/src/spells.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/spells.h 2026-07-17 20:05:20 UTC (rev 1670)
+++ showeq/branches/xerxes-trunk-dev/src/spells.h 2026-07-18 00:07:36 UTC (rev 1671)
@@ -49,6 +49,7 @@
const QString& name() const { return m_name; }
uint8_t level(uint8_t class_) const;
uint8_t targetType() const { return m_targetType; }
+ bool buffDurationFrozen() const { return m_buffDurationFrozen; }
QString spellField(uint8_t field) const;
@@ -60,6 +61,7 @@
int16_t m_buffDurationFormula;
int16_t m_buffDurationArgument;
uint8_t m_targetType;
+ bool m_buffDurationFrozen;
uint8_t m_classLevels[playerClasses];
};
Modified: showeq/branches/xerxes-trunk-dev/src/spellshell.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/spellshell.cpp 2026-07-17 20:05:20 UTC (rev 1670)
+++ showeq/branches/xerxes-trunk-dev/src/spellshell.cpp 2026-07-18 00:07:36 UTC (rev 1671)
@@ -129,7 +129,8 @@
m_player(player),
m_spawnShell(spawnshell),
m_spells(spells),
- m_lastPlayerSpell(0)
+ m_lastPlayerSpell(0),
+ m_freezeZone(false)
{
setObjectName("spellshell");
m_timer = new QTimer(this);
@@ -345,6 +346,131 @@
}
}
+// OP_BuffList: the authoritative buff set for a spawn, re-sent on every buff
+// change (zone-in, cast, click-off) with real remaining durations. It is the
+// source of truth for the spell list: it adds/updates the buffs it carries and
+// prunes any it dropped (which is how a clicked-off buff leaves the window).
+void SpellShell::buffList(const uint8_t* data, size_t len, uint8_t dir)
+{
+ if (dir == DIR_Client || len < 15)
+ return;
+
+ // byte 8 == 1 marks an authoritative full buff list; the server also sends
+ // byte 8 == 0 records (a single 0xffffffff slot) that are not a list - ignore
+ // them so the prune step below never runs against a bogus empty set.
+ if (data[8] != 1)
+ return;
+
+ uint32_t spawnId = *(const uint32_t*)data;
+ uint8_t count = data[9];
+ QList<uint16_t> listed;
+
+ // The list is scoped to the spawn id it carries: the player (OP_BuffList),
+ // the pet (OP_PetBuffList), etc. Resolve the owner - player when the id
+ // matches, otherwise the named spawn. An unresolved spawn is skipped rather
+ // than blamed on the player, so a not-yet-tracked pet can never prune the
+ // player's own buffs.
+ uint16_t targetId = uint16_t(spawnId);
+ QString targetName;
+ bool isPlayer = false;
+ if (spawnId == m_player->id())
+ {
+ targetName = m_player->name();
+ isPlayer = true;
+ }
+ else
+ {
+ const Item* tgt = m_spawnShell->findID(tSpawn, spawnId);
+ if (!tgt)
+ return;
+ targetName = tgt->name();
+ // Spawn names carry a numeric instance suffix (e.g. a trailing "00"); show
+ // the clean name in the spell list.
+ while (!targetName.isEmpty() && targetName.at(targetName.length() - 1).isDigit())
+ targetName.chop(1);
+ }
+
+ size_t pos = 15;
+ for (uint8_t n = 0; n < count && pos + 12 <= len; n++)
+ {
+ uint32_t spellId = *(const uint32_t*)(data + pos);
+
+ // Record head: spellId, remaining ticks at +4, then the printable caster
+ // name begins at +12.
+ int32_t remaining = *(const int32_t*)(data + pos + 4);
+ pos += 12;
+
+ QString caster;
+ while (pos < len && data[pos])
+ caster += QChar(data[pos++]);
+ if (pos < len) pos++; // NUL
+ pos += 4; // u32 slot
+
+ listed.append(uint16_t(spellId));
+
+ // A null spell (id not in this spells_us.txt - e.g. a new spell after a
+ // game patch, or no spell file) is still shown and tracked: update() falls
+ // back to the decimal spell id for the name so it stays lookupable.
+ const Spell* spell = m_spells->spell(spellId);
+
+ // remaining <= 0 => permanent buff (no timer; only a click-off removes it)
+ bool permanent = (remaining <= 0);
+ int duration = permanent ? 0 : remaining * 6;
+
+ // For the player's own buffs, match id-agnostically: the profile
+ // (buffLoad) and casts (selfStartSpellCast) may have keyed this buff under
+ // a different player spawn id (it churns at zone-in), so find it by spell +
+ // player name and adopt the current id, merging rather than duplicating.
+ SpellItem* item = NULL;
+ if (isPlayer)
+ {
+ for (QList<SpellItem*>::Iterator it = m_spellList.begin();
+ it != m_spellList.end(); ++it)
+ if (((*it)->spellId() == spellId) && ((*it)->targetName() == targetName))
+ {
+ item = *it;
+ break;
+ }
+ }
+ else
+ item = findSpell(spellId, targetId, targetName);
+
+ if (item)
+ {
+ item->update(spellId, spell, duration, 0, caster, targetId, targetName);
+ item->setPermanent(permanent);
+ emit changeSpell(item);
+ }
+ else
+ {
+ item = new SpellItem();
+ item->update(spellId, spell, duration, 0, caster, targetId, targetName);
+ item->setPermanent(permanent);
+ m_spellList.append(item);
+ if ((m_spellList.count() > 0) && (!m_timer->isActive()))
+ m_timer->start(1000 *
+ pSEQPrefs->getPrefInt("SpellTimer", "SpellList", 6));
+ emit addSpell(item);
+ }
+ }
+
+ // The list is authoritative for this spawn: prune tracked buffs it dropped.
+ // Match the player's buffs by name (id-agnostic) so stale-id entries left by
+ // the zone-in id churn are also pruned; other spawns match by id.
+ QList<SpellItem*> stripped;
+ for (QList<SpellItem*>::Iterator it = m_spellList.begin();
+ it != m_spellList.end(); ++it)
+ {
+ bool owned = isPlayer ? ((*it)->targetName() == targetName)
+ : ((*it)->targetId() == targetId);
+ if (owned && !listed.contains((*it)->spellId()))
+ stripped.append(*it);
+ }
+ for (QList<SpellItem*>::Iterator it = stripped.begin();
+ it != stripped.end(); ++it)
+ deleteSpell(*it);
+}
+
void SpellShell::action(const uint8_t* data, size_t, uint8_t)
{
const actionStruct* a = (const actionStruct*)data;
@@ -486,7 +612,7 @@
m_lastPlayerSpell = 0;
SpellItem* spell;
for(QList<SpellItem*>::Iterator it = m_spellList.begin();
- it != m_spellList.end(); it++)
+ it != m_spellList.end(); it++)
{
spell = *it;
@@ -496,6 +622,24 @@
}
}
+// Zones where the server holds buff durations steady (the client shows them
+// frozen). In these zones, spells flagged duration-frozen in spells_us.txt do
+// not count down in the spell list. This is a maintained list - append new
+// buff-freeze zones (short names) as they are found.
+void SpellShell::updateFreezeZone(const QString& shortZoneName)
+{
+ static const char* freezeZones[] = {
+ "poknowledge", "guildlobby", "guildhall", "nexus"
+ };
+ m_freezeZone = false;
+ for (size_t i = 0; i < sizeof(freezeZones) / sizeof(freezeZones[0]); i++)
+ if (shortZoneName == freezeZones[i])
+ {
+ m_freezeZone = true;
+ break;
+ }
+}
+
void SpellShell::killSpawn(const Item* deceased)
{
uint16_t id = deceased->id();
@@ -549,7 +693,13 @@
// permanent buffs (profile duration -1, e.g. illusions/forms) do not
// count down or expire - only a fade removes them.
- if (spell->permanent())
+ // In a buff-freeze zone the server holds durations steady, so a spell that
+ // is flagged duration-frozen in spells_us.txt (buffDurationFrozen) must not
+ // tick down here either. Both conditions are required: the zone freezes AND
+ // the spell is flagged - detrimental/non-frozen spells still count down.
+ const Spell* sp = m_spells->spell(spell->spellId());
+ if (spell->permanent() ||
+ (m_freezeZone && sp && sp->buffDurationFrozen()))
{
it++;
continue;
Modified: showeq/branches/xerxes-trunk-dev/src/spellshell.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/spellshell.h 2026-07-17 20:05:20 UTC (rev 1670)
+++ showeq/branches/xerxes-trunk-dev/src/spellshell.h 2026-07-18 00:07:36 UTC (rev 1671)
@@ -202,10 +202,12 @@
void selfStartSpellCast(const uint8_t*);
void buffLoad(const spellBuff*);
void buff(const uint8_t*, size_t, uint8_t);
+ void buffList(const uint8_t*, size_t, uint8_t);
void action(const uint8_t*, size_t, uint8_t);
void simpleMessage(const uint8_t* cmsg, size_t, uint8_t);
void spellMessage(QString&);
void zoneChanged(void);
+ void updateFreezeZone(const QString& shortZoneName);
void killSpawn(const Item* deceased);
void timeout();
@@ -223,6 +225,7 @@
QList<SpellItem *> m_spellList;
SpellItem* m_lastPlayerSpell;
QTimer *m_timer;
+ bool m_freezeZone;
};
#endif
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <xer...@us...> - 2026-07-17 20:05:22
|
Revision: 1670
http://sourceforge.net/p/seq/svn/1670
Author: xerxes00
Date: 2026-07-17 20:05:20 +0000 (Fri, 17 Jul 2026)
Log Message:
-----------
Handle permanent buffs in the spell list
Permanent profile buffs (duration -1) were computed as a small negative
duration, so timeout() expired them one tick after load and dropped them
from the spell list. Track a permanent flag on SpellItem: buffLoad marks
duration -1 as permanent, durationStr shows "Permanent", timeout() skips
them, and the list renders them green instead of expiry-red.
Modified Paths:
--------------
showeq/branches/xerxes-trunk-dev/src/spelllist.cpp
showeq/branches/xerxes-trunk-dev/src/spellshell.cpp
showeq/branches/xerxes-trunk-dev/src/spellshell.h
Modified: showeq/branches/xerxes-trunk-dev/src/spelllist.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/spelllist.cpp 2026-07-17 20:00:59 UTC (rev 1669)
+++ showeq/branches/xerxes-trunk-dev/src/spelllist.cpp 2026-07-17 20:05:20 UTC (rev 1670)
@@ -74,7 +74,11 @@
//color change by Worried
//change spell colors according to time remaining
- if (m_item->duration() > 120)
+ // permanent buffs (no timer) are not "about to expire" - show them green
+ // rather than the red the <=12s bucket would otherwise give.
+ if (m_item->permanent())
+ this->setTextColor(QColor(0, 140, 0));
+ else if (m_item->duration() > 120)
this->setTextColor(Qt::black);
else if (m_item->duration() <= 120 and m_item->duration() > 60)
this->setTextColor(QColor(128,54,193));
Modified: showeq/branches/xerxes-trunk-dev/src/spellshell.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/spellshell.cpp 2026-07-17 20:00:59 UTC (rev 1669)
+++ showeq/branches/xerxes-trunk-dev/src/spellshell.cpp 2026-07-17 20:05:20 UTC (rev 1670)
@@ -36,6 +36,7 @@
SpellItem::SpellItem()
: m_duration(0),
+ m_permanent(false),
m_spellId(0),
m_casterId(0),
m_targetId(0)
@@ -69,6 +70,8 @@
QString SpellItem::durationStr() const
{
+ if (m_permanent)
+ return "Permanent";
QString text;
int d = m_duration;
if (d < 0)
@@ -269,19 +272,25 @@
#endif // DIAG_SPELLSHELL
const Spell* spell = m_spells->spell(c->spellid);
- int duration = c->duration * 6;
+ // A profile buff with duration -1 is permanent (no timer); it must not
+ // enter the countdown as a small negative duration, which the list would
+ // render red and timeout() would expire after one tick.
+ bool permanent = (c->duration == -1);
+ int duration = permanent ? 0 : c->duration * 6;
SpellItem *item = findSpell(c->spellid, m_player->id(), m_player->name());
- if (item)
+ if (item)
{ // exists
- item->update(c->spellid, spell, duration,
+ item->update(c->spellid, spell, duration,
0, "Buff", m_player->id(), m_player->name());
+ item->setPermanent(permanent);
emit changeSpell(item);
- }
- else
+ }
+ else
{ // new spell
item = new SpellItem();
- item->update(c->spellid, spell, duration,
+ item->update(c->spellid, spell, duration,
0, "Buff", m_player->id(), m_player->name());
+ item->setPermanent(permanent);
m_spellList.append(item);
if ((m_spellList.count() > 0) && (!m_timer->isActive()))
m_timer->start(1000 *
@@ -538,6 +547,14 @@
{
spell = *it;
+ // permanent buffs (profile duration -1, e.g. illusions/forms) do not
+ // count down or expire - only a fade removes them.
+ if (spell->permanent())
+ {
+ it++;
+ continue;
+ }
+
int d = spell->duration() -
pSEQPrefs->getPrefInt("SpellTimer", "SpellList", 6);
if (d > -6)
Modified: showeq/branches/xerxes-trunk-dev/src/spellshell.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/spellshell.h 2026-07-17 20:00:59 UTC (rev 1669)
+++ showeq/branches/xerxes-trunk-dev/src/spellshell.h 2026-07-17 20:05:20 UTC (rev 1670)
@@ -74,7 +74,9 @@
const QString targetName() const;
const QString casterName() const;
void setDuration(int);
-
+ bool permanent() const { return m_permanent; }
+ void setPermanent(bool p) { m_permanent = p; }
+
// set accessors
void setSpellId(uint16_t spellid);
void setTargetId(uint16_t target);
@@ -93,6 +95,7 @@
QString m_casterName;
QString m_targetName;
int m_duration;
+ bool m_permanent;
timeval m_castTime;
uint16_t m_spellId;
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <xer...@us...> - 2026-07-17 20:01:02
|
Revision: 1669
http://sourceforge.net/p/seq/svn/1669
Author: xerxes00
Date: 2026-07-17 20:00:59 +0000 (Fri, 17 Jul 2026)
Log Message:
-----------
Quality-of-life and log hygiene fixes
Reduces redundant map reloads (skip the reload zoneEnd() issues right
after zoneBegin() loaded the same zone, and the pre-zone unknown-zone
load attempt). Routes spell-timer notices to the MT_Spell message type
so they filter with cast messages. Tidies money/LDoN reporting to skip
empty wallet lines and collapse all-zero point lines, and trims noisy
debug logging.
Modified Paths:
--------------
showeq/branches/xerxes-trunk-dev/conf/seqdef.xml
showeq/branches/xerxes-trunk-dev/src/category.cpp
showeq/branches/xerxes-trunk-dev/src/diagnosticmessages.cpp
showeq/branches/xerxes-trunk-dev/src/diagnosticmessages.h
showeq/branches/xerxes-trunk-dev/src/filtermgr.cpp
showeq/branches/xerxes-trunk-dev/src/guild.cpp
showeq/branches/xerxes-trunk-dev/src/map.cpp
showeq/branches/xerxes-trunk-dev/src/map.h
showeq/branches/xerxes-trunk-dev/src/messageshell.cpp
showeq/branches/xerxes-trunk-dev/src/spellshell.cpp
Modified: showeq/branches/xerxes-trunk-dev/conf/seqdef.xml
===================================================================
--- showeq/branches/xerxes-trunk-dev/conf/seqdef.xml 2026-07-17 18:19:21 UTC (rev 1668)
+++ showeq/branches/xerxes-trunk-dev/conf/seqdef.xml 2026-07-17 20:00:59 UTC (rev 1669)
@@ -291,7 +291,7 @@
<comment>Display Cold Resist</comment>
</property>
<property name="showAGI" >
- <bool value="false" />
+ <bool value="true" />
<comment>Display AGIlity</comment>
</property>
<property name="showINT" >
Modified: showeq/branches/xerxes-trunk-dev/src/category.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/category.cpp 2026-07-17 18:19:21 UTC (rev 1668)
+++ showeq/branches/xerxes-trunk-dev/src/category.cpp 2026-07-17 20:00:59 UTC (rev 1669)
@@ -370,7 +370,7 @@
// signal that the categories have been loaded
emit loadedCategories();
- seqInfo("Categories Reloaded");
+ seqDebug("Categories Reloaded");
}
void CategoryMgr::savePrefs(void)
Modified: showeq/branches/xerxes-trunk-dev/src/diagnosticmessages.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/diagnosticmessages.cpp 2026-07-17 18:19:21 UTC (rev 1668)
+++ showeq/branches/xerxes-trunk-dev/src/diagnosticmessages.cpp 2026-07-17 20:00:59 UTC (rev 1669)
@@ -73,6 +73,18 @@
return ret;
}
+// Spell-timer notices (e.g. "'...' finished") emitted as MT_Spell
+// so they fall under the same filter as cast messages, instead of the MT_Info
+// bucket seqInfo would put them in.
+int seqSpell(const char* format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ int ret = seqMessage(MT_Spell, format, ap);
+ va_end(ap);
+ return ret;
+}
+
int seqWarn(const char* format, ...)
{
va_list ap;
Modified: showeq/branches/xerxes-trunk-dev/src/diagnosticmessages.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/diagnosticmessages.h 2026-07-17 18:19:21 UTC (rev 1668)
+++ showeq/branches/xerxes-trunk-dev/src/diagnosticmessages.h 2026-07-17 20:00:59 UTC (rev 1669)
@@ -26,6 +26,7 @@
int seqDebug(const char* format, ...);
int seqInfo(const char* format, ...);
+int seqSpell(const char* format, ...);
int seqWarn(const char* format, ...);
void seqFatal(const char* format, ...);
Modified: showeq/branches/xerxes-trunk-dev/src/filtermgr.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/filtermgr.cpp 2026-07-17 18:19:21 UTC (rev 1668)
+++ showeq/branches/xerxes-trunk-dev/src/filtermgr.cpp 2026-07-17 20:00:59 UTC (rev 1669)
@@ -135,7 +135,8 @@
m_filterFile = fileInfo.absoluteFilePath();
- seqInfo("Loading Filters from '%s'", m_filterFile.toLatin1().data());
+ if (!m_filterFile.isEmpty())
+ seqInfo("Loading Filters from '%s'", m_filterFile.toLatin1().data());
m_filters->load(m_filterFile);
@@ -149,7 +150,8 @@
m_filterFile = fileInfo.absoluteFilePath();
- seqInfo("Loading Filters from '%s'", m_filterFile.toLatin1().data());
+ if (!m_filterFile.isEmpty())
+ seqInfo("Loading Filters from '%s'", m_filterFile.toLatin1().data());
m_filters->load(m_filterFile);
@@ -240,7 +242,8 @@
m_zoneFilterFile = fileInfo.absoluteFilePath();
- seqInfo("Loading Zone Filter File: %s", m_zoneFilterFile.toLatin1().data());
+ if (!m_zoneFilterFile.isEmpty())
+ seqInfo("Loading Zone Filter File: %s", m_zoneFilterFile.toLatin1().data());
m_zoneFilters->load(m_zoneFilterFile);
@@ -256,7 +259,8 @@
m_zoneFilterFile = fileInfo.absoluteFilePath();
- seqInfo("Loading Zone Filter File: %s", m_zoneFilterFile.toLatin1().data());
+ if (!m_zoneFilterFile.isEmpty())
+ seqInfo("Loading Zone Filter File: %s", m_zoneFilterFile.toLatin1().data());
m_zoneFilters->load(m_zoneFilterFile);
Modified: showeq/branches/xerxes-trunk-dev/src/guild.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/guild.cpp 2026-07-17 18:19:21 UTC (rev 1668)
+++ showeq/branches/xerxes-trunk-dev/src/guild.cpp 2026-07-17 20:00:59 UTC (rev 1669)
@@ -201,7 +201,7 @@
}
guildsfile.close();
- seqInfo("GuildMgr: Guildsfile loaded");
+ seqDebug("GuildMgr: Guildsfile loaded");
}
else
seqWarn("GuildMgr: Could not load guildsfile, %s", guildsFileName.toLatin1().data());
Modified: showeq/branches/xerxes-trunk-dev/src/map.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/map.cpp 2026-07-17 18:19:21 UTC (rev 1668)
+++ showeq/branches/xerxes-trunk-dev/src/map.cpp 2026-07-17 20:00:59 UTC (rev 1669)
@@ -332,7 +332,8 @@
// clear the map data
m_mapData.clear();
-
+ m_loadedZoneName = QString();
+
// signal that the map has been unloaded
emit mapUnloaded();
@@ -349,7 +350,8 @@
// clear the map data
m_mapData.clear();
-
+ m_loadedZoneName = QString();
+
// signal that the map has been unloaded
emit mapUnloaded();
@@ -370,7 +372,17 @@
void MapMgr::loadZoneMap(const QString& shortZoneName)
{
+ // Pre-zone placeholder: at startup (and between sessions) the zone is
+ // "unknown" - no real zone yet - so don't try to load, or warn about a
+ // missing map for, "unknown". Real zones without a map still warn.
+ if (shortZoneName.isEmpty() || shortZoneName == "unknown")
+ return;
+ // Skip the redundant reload zoneEnd() issues for a zone zoneBegin()
+ // already loaded. A genuine re-zone clears m_loadedZoneName first.
+ if (shortZoneName == m_loadedZoneName)
+ return;
+
bool found = false;
QString extension = "";
@@ -422,6 +434,9 @@
}
loadFileMap(mapFiles);
+
+ // remember the loaded zone so zoneEnd() won't reload it
+ m_loadedZoneName = shortZoneName;
}
void MapMgr::loadMap ()
@@ -490,9 +505,9 @@
#endif /* DEBUGMAP */
if (import)
- seqInfo("Attempting to import map: %s", fileName.toLatin1().data());
+ seqDebug("Attempting to import map: %s", fileName.toLatin1().data());
else
- seqInfo("Attempting to load map: %s", fileName.toLatin1().data());
+ seqDebug("Attempting to load map: %s", fileName.toLatin1().data());
// if not a forced load, and the same map is already loaded, do nothing
Modified: showeq/branches/xerxes-trunk-dev/src/map.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/map.h 2026-07-17 18:19:21 UTC (rev 1668)
+++ showeq/branches/xerxes-trunk-dev/src/map.h 2026-07-17 20:00:59 UTC (rev 1669)
@@ -226,6 +226,11 @@
MapData m_mapData;
QHash<int, uint16_t> m_spawnAggroRange;
+ // Short zone name currently loaded; used to skip the redundant reload that
+ // zoneEnd() issues right after zoneBegin() already loaded the same zone.
+ // Cleared whenever the map data is cleared for a genuine reload.
+ QString m_loadedZoneName;
+
QString m_curLineColor;
QString m_curLineName;
QString m_curLocationColor;
Modified: showeq/branches/xerxes-trunk-dev/src/messageshell.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/messageshell.cpp 2026-07-17 18:19:21 UTC (rev 1668)
+++ showeq/branches/xerxes-trunk-dev/src/messageshell.cpp 2026-07-17 20:00:59 UTC (rev 1669)
@@ -463,7 +463,7 @@
void MessageShell::zoneEntryClient(const ClientZoneEntryStruct* zsentry)
{
- m_messages->addMessage(MT_Zone, "EntryCode: Client");
+ m_messages->addMessage(MT_Debug, "EntryCode: Client");
}
void MessageShell::zoneChanged(const zoneChangeStruct* zoneChange, size_t, uint8_t dir)
@@ -492,7 +492,7 @@
QString tempStr;
tempStr = "NewCode: Zone: ";
tempStr += newZoneShortName + " (" + newZoneLongName + ")";
- m_messages->addMessage(MT_Zone, tempStr);
+ m_messages->addMessage(MT_Debug, tempStr);
}
void MessageShell::zoneBegin(const QString& shortZoneName)
@@ -951,39 +951,54 @@
#endif
m_messages->addMessage(MT_Player, message);
+ // Money lines: skip any wallet that is entirely empty.
+ if (player->profile.platinum || player->profile.gold ||
+ player->profile.silver || player->profile.copper)
+ {
#if (QT_VERSION >= QT_VERSION_CHECK(5,5,0))
message = QString::asprintf("PlayerMoney: P=%d G=%d S=%d C=%d",
- player->profile.platinum, player->profile.gold,
+ player->profile.platinum, player->profile.gold,
player->profile.silver, player->profile.copper);
#else
message.sprintf("PlayerMoney: P=%d G=%d S=%d C=%d",
- player->profile.platinum, player->profile.gold,
+ player->profile.platinum, player->profile.gold,
player->profile.silver, player->profile.copper);
#endif
m_messages->addMessage(MT_Player, message);
-
+ }
+
+ if (player->platinum_bank || player->gold_bank ||
+ player->silver_bank || player->copper_bank)
+ {
#if (QT_VERSION >= QT_VERSION_CHECK(5,5,0))
message = QString::asprintf("BankMoney: P=%d G=%d S=%d C=%d",
- player->platinum_bank, player->gold_bank,
+ player->platinum_bank, player->gold_bank,
player->silver_bank, player->copper_bank);
#else
message.sprintf("BankMoney: P=%d G=%d S=%d C=%d",
- player->platinum_bank, player->gold_bank,
+ player->platinum_bank, player->gold_bank,
player->silver_bank, player->copper_bank);
#endif
m_messages->addMessage(MT_Player, message);
+ }
+ if (player->profile.platinum_cursor || player->profile.gold_cursor ||
+ player->profile.silver_cursor || player->profile.copper_cursor)
+ {
#if (QT_VERSION >= QT_VERSION_CHECK(5,5,0))
message = QString::asprintf("CursorMoney: P=%d G=%d S=%d C=%d",
- player->profile.platinum_cursor, player->profile.gold_cursor,
+ player->profile.platinum_cursor, player->profile.gold_cursor,
player->profile.silver_cursor, player->profile.copper_cursor);
#else
message.sprintf("CursorMoney: P=%d G=%d S=%d C=%d",
- player->profile.platinum_cursor, player->profile.gold_cursor,
+ player->profile.platinum_cursor, player->profile.gold_cursor,
player->profile.silver_cursor, player->profile.copper_cursor);
#endif
m_messages->addMessage(MT_Player, message);
+ }
+ if (player->platinum_shared)
+ {
#if (QT_VERSION >= QT_VERSION_CHECK(5,5,0))
message = QString::asprintf("SharedMoney: P=%d",
player->platinum_shared);
@@ -992,15 +1007,19 @@
player->platinum_shared);
#endif
m_messages->addMessage(MT_Player, message);
+ }
+ if (player->currentRadCrystals || player->currentEbonCrystals)
+ {
#if (QT_VERSION >= QT_VERSION_CHECK(5,5,0))
- message = QString::asprintf("DoN Crystals: Radiant=%d Ebon=%d",
- player->currentRadCrystals, player->currentEbonCrystals);
+ message = QString::asprintf("DoN Crystals: Radiant=%d Ebon=%d",
+ player->currentRadCrystals, player->currentEbonCrystals);
#else
- message.sprintf("DoN Crystals: Radiant=%d Ebon=%d",
- player->currentRadCrystals, player->currentEbonCrystals);
+ message.sprintf("DoN Crystals: Radiant=%d Ebon=%d",
+ player->currentRadCrystals, player->currentEbonCrystals);
#endif
- m_messages->addMessage(MT_Player, message);
+ m_messages->addMessage(MT_Player, message);
+ }
// charProfileStruct.exp hasn't been found
// message = "Exp: " + Commanate(player->exp);
@@ -1062,18 +1081,19 @@
}
}
- message = "LDoN Earned Guk Points: " + Commanate(player->ldon_guk_points);
- m_messages->addMessage(MT_Player, message);
- message = "LDoN Earned Mira Points: " + Commanate(player->ldon_mir_points);
- m_messages->addMessage(MT_Player, message);
- message = "LDoN Earned MMC Points: " + Commanate(player->ldon_mmc_points);
- m_messages->addMessage(MT_Player, message);
- message = "LDoN Earned Ruj Points: " + Commanate(player->ldon_ruj_points);
- m_messages->addMessage(MT_Player, message);
- message = "LDoN Earned Tak Points: " + Commanate(player->ldon_tak_points);
- m_messages->addMessage(MT_Player, message);
- message = "LDoN Unspent Points: " + Commanate(player->ldon_avail_points);
- m_messages->addMessage(MT_Player, message);
+ // LDoN points on one line; skip entirely when everything is zero.
+ if (player->ldon_guk_points || player->ldon_mir_points ||
+ player->ldon_mmc_points || player->ldon_ruj_points ||
+ player->ldon_tak_points || player->ldon_avail_points)
+ {
+ message = "LDoN Points: Guk=" + Commanate(player->ldon_guk_points)
+ + " Mir=" + Commanate(player->ldon_mir_points)
+ + " MMC=" + Commanate(player->ldon_mmc_points)
+ + " Ruj=" + Commanate(player->ldon_ruj_points)
+ + " Tak=" + Commanate(player->ldon_tak_points)
+ + " Unspent=" + Commanate(player->ldon_avail_points);
+ m_messages->addMessage(MT_Player, message);
+ }
}
void MessageShell::increaseSkill(const uint8_t* data)
Modified: showeq/branches/xerxes-trunk-dev/src/spellshell.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/spellshell.cpp 2026-07-17 18:19:21 UTC (rev 1668)
+++ showeq/branches/xerxes-trunk-dev/src/spellshell.cpp 2026-07-17 20:00:59 UTC (rev 1669)
@@ -443,16 +443,20 @@
// Your xxx has worn off.
// Your target resisted the xxx spell.
// Your spell fizzles.
- seqInfo("*** spellMessage *** %s", spell.toLatin1().data());
+#ifdef DIAG_SPELLSHELL
+ seqDebug("*** spellMessage *** %s", spell.toLatin1().data());
+#endif // DIAG_SPELLSHELL
+ // Dead: the spellMessage signal is never emitted (no caller in any tree),
+ // so this handler never runs.
if (spell.left(25) == QString("Your target resisted the ")) {
spell = spell.right(spell.length() - 25);
spell = spell.left(spell.length() - 7);
- seqInfo("RESIST: '%s'", spell.toLatin1().data());
+ //seqInfo("RESIST: '%s'", spell.toLatin1().data());
b = true;
} else if (spell.right(20) == QString(" spell has worn off.")) {
spell = spell.right(spell.length() - 5);
spell = spell.left(spell.length() - 20);
- seqInfo("WORE OFF: '%s'", spell.toLatin1().data());
+ //seqInfo("WORE OFF: '%s'", spell.toLatin1().data());
b = true;
}
@@ -544,7 +548,7 @@
}
else
{
- seqInfo("SpellItem '%s' finished.", (*it)->spellName().toLatin1().data());
+ seqSpell("'%s' finished.", (*it)->spellName().toLatin1().data());
if (m_lastPlayerSpell == spell)
m_lastPlayerSpell = 0;
emit delSpell(spell);
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <xer...@us...> - 2026-07-17 18:19:24
|
Revision: 1668
http://sourceforge.net/p/seq/svn/1668
Author: xerxes00
Date: 2026-07-17 18:19:21 +0000 (Fri, 17 Jul 2026)
Log Message:
-----------
Add Alternate Advancement window
Adds a dockable AA list window (View -> Alternate Advancement) showing
owned AAs with name, rank, points spent, and unspent/assigned/total
totals. AA names resolve via a new DBStr loader for dbstr_us.txt
(graceful fallback to ids without it). Builds the table from
OP_SendAATable and the character profile.
Modified Paths:
--------------
showeq/branches/xerxes-trunk-dev/README
showeq/branches/xerxes-trunk-dev/conf/zoneopcodes.xml
showeq/branches/xerxes-trunk-dev/src/Makefile.am
showeq/branches/xerxes-trunk-dev/src/everquest.h
showeq/branches/xerxes-trunk-dev/src/interface.cpp
showeq/branches/xerxes-trunk-dev/src/interface.h
showeq/branches/xerxes-trunk-dev/src/s_everquest.h
Added Paths:
-----------
showeq/branches/xerxes-trunk-dev/src/aalist.cpp
showeq/branches/xerxes-trunk-dev/src/aalist.h
showeq/branches/xerxes-trunk-dev/src/aamgr.cpp
showeq/branches/xerxes-trunk-dev/src/aamgr.h
showeq/branches/xerxes-trunk-dev/src/dbstr.cpp
showeq/branches/xerxes-trunk-dev/src/dbstr.h
Modified: showeq/branches/xerxes-trunk-dev/README
===================================================================
--- showeq/branches/xerxes-trunk-dev/README 2026-07-17 17:48:07 UTC (rev 1667)
+++ showeq/branches/xerxes-trunk-dev/README 2026-07-17 18:19:21 UTC (rev 1668)
@@ -32,3 +32,39 @@
shows current command line arguments. Also look at the
conf/seqdef.xml file for configuration file options and defaults.
Most configuration changes can be done via the user interface.
+
+DATA FILES
+----------
+Some features read string/data files copied from the EverQuest client
+directory. Place these in a ShowEQ data directory - either your per-user
+dir ($HOME/.showeq) or the installed package data dir (pkgdatadir,
+e.g. /usr/local/share/showeq/). ShowEQ searches the per-user dir first,
+then pkgdatadir.
+
+ spells_us.txt spell data
+ eqstr_us.txt formatted message strings
+ dbstr_us.txt string database (AA titles, etc.) - required for the
+ Alternate Advancement window to show ability NAMES.
+ Without it, that window still works but lists abilities
+ as "AA title <id>". The filename is dbstr_us.txt (the
+ US-English database); there is no dbstr_en.txt.
+
+The file name each feature looks for can be overridden in the config
+(e.g. the DBStrFile setting under [Interface]).
+
+ALTERNATE ADVANCEMENT WINDOW
+----------------------------
+View -> Alternate Advancement lists the AAs your character owns, with the
+ability name, current rank (rank / max rank), points spent, and a footer
+of unspent / assigned / total AA points. Its purpose is to see at a glance
+which AAs you are actively leveling without scrolling the in-game AA UI.
+
+A "Hide AAs at max rank" checkbox at the bottom hides abilities already at
+their maximum rank so only in-progress ones remain. Its state is saved to
+the config file (HideMaxedAA under the [AAList] section) and restored on
+the next run; it defaults to off.
+
+Caveat: "max rank" is the ability's cap, not necessarily what you can buy
+right now - higher ranks may carry a character-level requirement, so an
+ability shown below its max (e.g. 3/4) does not guarantee the next rank is
+purchasable yet.
Modified: showeq/branches/xerxes-trunk-dev/conf/zoneopcodes.xml
===================================================================
--- showeq/branches/xerxes-trunk-dev/conf/zoneopcodes.xml 2026-07-17 17:48:07 UTC (rev 1667)
+++ showeq/branches/xerxes-trunk-dev/conf/zoneopcodes.xml 2026-07-17 18:19:21 UTC (rev 1668)
@@ -634,8 +634,9 @@
<opcode id="ffff" name="OP_VeteranRewards" updated="04/18/11">
<comment></comment>
</opcode>
- <opcode id="ffff" name="OP_SendAATable" updated="08/12/09">
- <comment></comment>
+ <opcode id="2719" name="OP_SendAATable" updated="07/15/26">
+ <comment>AA window</comment>
+ <payload dir="server" typename="uint8_t" sizechecktype="none"/>
</opcode>
<opcode id="ffff" name="OP_SendAAStats" updated="08/12/09">
<comment></comment>
Modified: showeq/branches/xerxes-trunk-dev/src/Makefile.am
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/Makefile.am 2026-07-17 17:48:07 UTC (rev 1667)
+++ showeq/branches/xerxes-trunk-dev/src/Makefile.am 2026-07-17 18:19:21 UTC (rev 1668)
@@ -5,6 +5,9 @@
bin_PROGRAMS = showeq
showeq_SOURCES = \
+ aalist.cpp \
+ aamgr.cpp \
+ dbstr.cpp \
bazaarlog.cpp \
category.cpp \
combatlog.cpp \
@@ -74,6 +77,8 @@
showeq_moc_SRCS = \
+ aalist.moc \
+ aamgr.moc \
bazaarlog.moc \
category.moc \
combatlog.moc \
@@ -127,6 +132,8 @@
nodist_showeq_SOURCES = ui_mapicondialog.h $(showeq_moc_SRCS)
$(srcdir)/bazaarlog.cpp: bazaarlog.moc
+$(srcdir)/aalist.cpp: aalist.moc
+$(srcdir)/aamgr.cpp: aamgr.moc
$(srcdir)/category.cpp: category.moc
$(srcdir)/combatlog.cpp: combatlog.moc
$(srcdir)/compass.cpp: compass.moc
Added: showeq/branches/xerxes-trunk-dev/src/aalist.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/aalist.cpp (rev 0)
+++ showeq/branches/xerxes-trunk-dev/src/aalist.cpp 2026-07-17 18:19:21 UTC (rev 1668)
@@ -0,0 +1,156 @@
+/*
+ * aalist.cpp
+ * Copyright 2026 by the respective ShowEQ Developers
+ *
+ * This file is part of ShowEQ.
+ * http://www.sourceforge.net/projects/seq
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "aalist.h"
+#include "aamgr.h"
+#include "main.h" // for pSEQPrefs
+
+#include <QHeaderView>
+#include <QWidget>
+#include <QVBoxLayout>
+#include <QLabel>
+#include <QCheckBox>
+
+AAList::AAList(AAMgr* aaMgr, QWidget* parent, const char* name)
+ : SEQListView("AAList", parent, name),
+ m_aaMgr(aaMgr),
+ m_hideMaxed(false)
+{
+ m_hideMaxed = pSEQPrefs->getPrefBool("HideMaxedAA", preferenceName(), false);
+
+ addColumn("Ability");
+ addColumn("Rank");
+ addColumn("Points");
+
+ restoreColumns();
+
+ // Column sizing, applied AFTER restoreColumns so it is not overridden:
+ // the Ability column auto-fits the widest displayed name; Rank and
+ // Points are pinned to the same small width; nothing stretches to fill
+ // the pane (empty space to the right).
+ header()->setStretchLastSection(false);
+ header()->setSectionResizeMode(AACOL_NAME, QHeaderView::ResizeToContents);
+ header()->setSectionResizeMode(AACOL_RANK, QHeaderView::Interactive);
+ header()->setSectionResizeMode(AACOL_POINTS, QHeaderView::Interactive);
+ setColumnWidth(AACOL_RANK, 56);
+ setColumnWidth(AACOL_POINTS, 56);
+
+ // refresh only when the AA data actually changes
+ connect(m_aaMgr, SIGNAL(aaChanged()), this, SLOT(rebuild()));
+
+ rebuild();
+}
+
+AAList::~AAList()
+{
+}
+
+void AAList::setHideMaxed(bool hide)
+{
+ m_hideMaxed = hide;
+ pSEQPrefs->setPrefBool("HideMaxedAA", preferenceName(), hide);
+ rebuild();
+}
+
+void AAList::rebuild()
+{
+ // owned set is small; a full repopulate is simplest and cheap
+ clear();
+
+ const QList<AAMgr::Row> rows = m_aaMgr->ownedRows();
+ for (int i = 0; i < rows.size(); i++)
+ {
+ const AAMgr::Row& r = rows[i];
+
+ // optionally hide abilities already at their max rank. NOTE: max rank
+ // is the ability's cap, not necessarily obtainable right now - higher
+ // ranks can carry level requirements - so a non-maxed ability is not a
+ // guarantee the next rank is buyable yet.
+ if (m_hideMaxed && r.maxRank && r.rank >= r.maxRank)
+ continue;
+
+ QString rankStr;
+ if (r.maxRank)
+ rankStr = QString("%1/%2").arg(r.rank).arg(r.maxRank);
+ else if (r.rank)
+ rankStr = QString::number(r.rank);
+
+ QStringList vals;
+ vals << r.name << rankStr << QString::number(r.points);
+ SEQListViewItem* item = new SEQListViewItem(this, vals);
+ item->setTextAlignment(AACOL_RANK, Qt::AlignRight);
+ item->setTextAlignment(AACOL_POINTS, Qt::AlignRight);
+ }
+}
+
+AAListWindow::AAListWindow(AAMgr* aaMgr, QWidget* parent, const char* name)
+ : SEQWindow("AAList", "ShowEQ - Alternate Advancement", parent, name),
+ m_aaMgr(aaMgr)
+{
+ // container holds the list plus a totals footer line
+ QWidget* container = new QWidget(this);
+ QVBoxLayout* layout = new QVBoxLayout(container);
+ layout->setContentsMargins(2, 2, 2, 2);
+ layout->setSpacing(2);
+
+ m_aaList = new AAList(aaMgr, container, name);
+ m_totals = new QLabel(container);
+ m_hideMaxed = new QCheckBox("Hide AAs at max rank", container);
+ m_hideMaxed->setChecked(m_aaList->hideMaxed());
+ m_hideMaxed->setToolTip("Hide abilities already at their maximum rank.\n"
+ "Note: an ability that is not maxed may still be\n"
+ "un-buyable right now - higher ranks can require a\n"
+ "higher character level.");
+
+ layout->addWidget(m_aaList);
+ layout->addWidget(m_totals);
+ layout->addWidget(m_hideMaxed);
+
+ setWidget(container);
+
+ connect(m_hideMaxed, SIGNAL(toggled(bool)), m_aaList, SLOT(setHideMaxed(bool)));
+
+ connect(m_aaMgr, SIGNAL(aaChanged()), this, SLOT(updateTotals()));
+ updateTotals();
+}
+
+AAListWindow::~AAListWindow()
+{
+}
+
+void AAListWindow::updateTotals(void)
+{
+ m_totals->setText(QString("Unspent: %1 Assigned: %2 Total: %3")
+ .arg(m_aaMgr->unspent())
+ .arg(m_aaMgr->assigned())
+ .arg(m_aaMgr->spent()));
+}
+
+void AAListWindow::savePrefs(void)
+{
+ SEQWindow::savePrefs();
+ m_aaList->savePrefs();
+}
+
+#ifndef QMAKEBUILD
+#include "aalist.moc"
+#endif
Added: showeq/branches/xerxes-trunk-dev/src/aalist.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/aalist.h (rev 0)
+++ showeq/branches/xerxes-trunk-dev/src/aalist.h 2026-07-17 18:19:21 UTC (rev 1668)
@@ -0,0 +1,80 @@
+/*
+ * aalist.h
+ * Copyright 2026 by the respective ShowEQ Developers
+ *
+ * This file is part of ShowEQ.
+ * http://www.sourceforge.net/projects/seq
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef EQAALIST_H
+#define EQAALIST_H
+
+#include <QWidget>
+
+#include "seqlistview.h"
+#include "seqwindow.h"
+
+#define AACOL_NAME 0
+#define AACOL_RANK 1
+#define AACOL_POINTS 2
+
+class AAMgr;
+class QLabel;
+class QCheckBox;
+
+class AAList : public SEQListView
+{
+ Q_OBJECT
+
+ public:
+ AAList(AAMgr* aaMgr, QWidget* parent = 0, const char* name = 0);
+ ~AAList();
+
+ public slots:
+ void rebuild();
+ void setHideMaxed(bool hide);
+
+ public:
+ bool hideMaxed() const { return m_hideMaxed; }
+
+ private:
+ AAMgr* m_aaMgr;
+ bool m_hideMaxed;
+};
+
+class AAListWindow : public SEQWindow
+{
+ Q_OBJECT
+
+ public:
+ AAListWindow(AAMgr* aaMgr, QWidget* parent = 0, const char* name = 0);
+ ~AAListWindow();
+
+ AAList* aaList() { return m_aaList; }
+
+ public slots:
+ void savePrefs(void);
+ void updateTotals(void);
+
+ private:
+ AAMgr* m_aaMgr;
+ AAList* m_aaList;
+ QLabel* m_totals;
+ QCheckBox* m_hideMaxed;
+};
+
+#endif // EQAALIST_H
Added: showeq/branches/xerxes-trunk-dev/src/aamgr.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/aamgr.cpp (rev 0)
+++ showeq/branches/xerxes-trunk-dev/src/aamgr.cpp 2026-07-17 18:19:21 UTC (rev 1668)
@@ -0,0 +1,163 @@
+/*
+ * aamgr.cpp
+ * Copyright 2026 by the respective ShowEQ Developers
+ *
+ * This file is part of ShowEQ.
+ * http://www.sourceforge.net/projects/seq
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "aamgr.h"
+#include "dbstr.h"
+#include "diagnosticmessages.h"
+
+#include <cstring>
+#include <algorithm>
+
+#include <QTimer>
+
+AAMgr::AAMgr(DBStr* dbstr, QObject* parent, const char* name)
+ : QObject(parent),
+ m_dbstr(dbstr),
+ m_spent(0),
+ m_assigned(0),
+ m_unspent(0),
+ m_changeScheduled(false)
+{
+ setObjectName(name);
+}
+
+AAMgr::~AAMgr()
+{
+}
+
+// Coalesce a whole record burst into a single UI refresh: schedule one
+// deferred emit rather than emitting per changed record.
+void AAMgr::markChanged()
+{
+ if (m_changeScheduled)
+ return;
+ m_changeScheduled = true;
+ QTimer::singleShot(0, this, SLOT(emitChanged()));
+}
+
+void AAMgr::emitChanged()
+{
+ m_changeScheduled = false;
+ emit aaChanged();
+}
+
+void AAMgr::aaInfo(const uint8_t* data, size_t len)
+{
+ // Only the fixed head (through rank, 37 bytes) is needed. The rest is a
+ // variable count-prefixed skills-requirement block plus prev/next ids.
+ if (len < sizeof(aaInfoStruct))
+ return;
+
+ const aaInfoStruct* r = (const aaInfoStruct*)data;
+
+ AADef def;
+ def.titleSID = r->titleSID;
+ def.rank = r->rank;
+ def.cost = r->cost;
+ def.aaID = r->aaID;
+
+ // update definition only if new or changed (avoids churn on re-sent bursts)
+ QHash<int32_t, AADef>::const_iterator it = m_defs.constFind(r->descID);
+ if (it == m_defs.constEnd() ||
+ it->titleSID != def.titleSID || it->rank != def.rank ||
+ it->cost != def.cost || it->aaID != def.aaID)
+ {
+ m_defs.insert(r->descID, def);
+
+ // track highest rank seen per AA title (ranks only grow within a session)
+ uint32_t& mx = m_maxRank[def.titleSID];
+ if (def.rank > mx)
+ mx = def.rank;
+
+ markChanged();
+ }
+}
+
+void AAMgr::player(const charProfileStruct* profile)
+{
+ if (!profile)
+ return;
+
+ // Rebuild the owned set from the profile's aa_array. Each nonzero entry
+ // is an owned AA: AA = current-rank descID, value = cumulative points.
+ QHash<int32_t, uint32_t> owned;
+ for (int i = 0; i < MAX_AA; i++)
+ {
+ const uint32_t val = profile->profile.aa_array[i].value;
+ if (val)
+ owned.insert((int32_t)profile->profile.aa_array[i].AA, val);
+ }
+
+ if (owned != m_owned ||
+ profile->profile.aa_spent != m_spent ||
+ profile->profile.aa_assigned != m_assigned ||
+ profile->profile.aa_unspent != m_unspent)
+ {
+ m_owned = owned;
+ m_spent = profile->profile.aa_spent;
+ m_assigned = profile->profile.aa_assigned;
+ m_unspent = profile->profile.aa_unspent;
+ markChanged();
+ }
+}
+
+QList<AAMgr::Row> AAMgr::ownedRows() const
+{
+ QList<Row> rows;
+
+ for (QHash<int32_t, uint32_t>::const_iterator it = m_owned.constBegin();
+ it != m_owned.constEnd(); ++it)
+ {
+ Row row;
+ row.points = it.value();
+
+ QHash<int32_t, AADef>::const_iterator dit = m_defs.constFind(it.key());
+ if (dit != m_defs.constEnd())
+ {
+ row.rank = dit->rank;
+ row.maxRank = m_maxRank.value(dit->titleSID, dit->rank);
+ row.name = m_dbstr ? m_dbstr->find(dit->titleSID) : QString();
+ if (row.name.isEmpty())
+ row.name = QString("AA title %1").arg(dit->titleSID);
+ }
+ else
+ {
+ // definition not received yet (profile arrived before the table)
+ row.rank = 0;
+ row.maxRank = 0;
+ row.name = QString("AA #%1").arg(it.key());
+ }
+
+ rows.append(row);
+ }
+
+ std::sort(rows.begin(), rows.end(),
+ [](const Row& a, const Row& b) {
+ return a.name.localeAwareCompare(b.name) < 0;
+ });
+
+ return rows;
+}
+
+#ifndef QMAKEBUILD
+#include "aamgr.moc"
+#endif
Added: showeq/branches/xerxes-trunk-dev/src/aamgr.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/aamgr.h (rev 0)
+++ showeq/branches/xerxes-trunk-dev/src/aamgr.h 2026-07-17 18:19:21 UTC (rev 1668)
@@ -0,0 +1,104 @@
+/*
+ * aamgr.h
+ * Copyright 2026 by the respective ShowEQ Developers
+ *
+ * This file is part of ShowEQ.
+ * http://www.sourceforge.net/projects/seq
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _AAMGR_H_
+#define _AAMGR_H_
+
+#include <cstdint>
+#include <QObject>
+#include <QHash>
+#include <QList>
+#include <QString>
+
+#include "everquest.h"
+
+class DBStr;
+
+//----------------------------------------------------------------------
+// AAMgr
+//
+// Builds an in-memory Alternate Advancement table from the OP_SendAATable
+// record burst the server sends at zone-in (one aaInfoStruct per AA rank),
+// and tracks which AAs the player owns from the character profile. Holds
+// no state on disk; the table is rebuilt from the wire each session. It
+// only signals aaChanged() when the definitions or the owned set actually
+// change, so ordinary zoning (an identical burst) does not churn the UI.
+class AAMgr : public QObject
+{
+ Q_OBJECT
+
+ public:
+ // per-rank definition
+ struct AADef
+ {
+ int32_t titleSID;
+ uint32_t rank;
+ uint32_t cost;
+ uint32_t aaID;
+ };
+
+ // one resolved row for display (an owned AA)
+ struct Row
+ {
+ QString name;
+ uint32_t rank;
+ uint32_t maxRank;
+ uint32_t points; // cumulative points spent (aa_array.value)
+ };
+
+ AAMgr(DBStr* dbstr, QObject* parent = 0, const char* name = 0);
+ ~AAMgr();
+
+ // owned AAs, resolved to name/rank/points, sorted by name
+ QList<Row> ownedRows() const;
+
+ // AA point totals from the character profile
+ uint32_t spent() const { return m_spent; }
+ uint32_t assigned() const { return m_assigned; }
+ uint32_t unspent() const { return m_unspent; }
+
+ public slots:
+ // OP_SendAATable record (one AA rank). Variable length; reads fixed head.
+ void aaInfo(const uint8_t* data, size_t len);
+ // character profile - supplies the owned AA set (descID -> cumulative pts)
+ void player(const charProfileStruct* profile);
+
+ signals:
+ void aaChanged();
+
+ private slots:
+ void emitChanged();
+
+ private:
+ void markChanged();
+
+ DBStr* m_dbstr;
+ QHash<int32_t, AADef> m_defs; // descID -> definition
+ QHash<int32_t, uint32_t> m_maxRank; // titleSID -> highest rank seen
+ QHash<int32_t, uint32_t> m_owned; // descID -> cumulative points
+ uint32_t m_spent; // profile aa_spent
+ uint32_t m_assigned; // profile aa_assigned
+ uint32_t m_unspent; // profile aa_unspent
+ bool m_changeScheduled;
+};
+
+#endif // _AAMGR_H_
Added: showeq/branches/xerxes-trunk-dev/src/dbstr.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/dbstr.cpp (rev 0)
+++ showeq/branches/xerxes-trunk-dev/src/dbstr.cpp 2026-07-17 18:19:21 UTC (rev 1668)
@@ -0,0 +1,104 @@
+/*
+ * dbstr.cpp
+ * Copyright 2026 by the respective ShowEQ Developers
+ *
+ * This file is part of ShowEQ.
+ * http://www.sourceforge.net/projects/seq
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "dbstr.h"
+#include "diagnosticmessages.h"
+
+#include <QFile>
+#include <QByteArray>
+#include <QStringList>
+#if (QT_VERSION >= QT_VERSION_CHECK(5,5,0))
+#include <QRegularExpression>
+#else
+#include <QRegExp>
+#endif
+
+DBStr::DBStr()
+ : m_loaded(false)
+{
+}
+
+DBStr::~DBStr()
+{
+}
+
+bool DBStr::load(const QString& fileName, int keepType)
+{
+ m_strings.clear();
+ m_loaded = false;
+
+ QFile dbFile(fileName);
+ if (!dbFile.open(QIODevice::ReadOnly))
+ {
+ seqWarn("DBStr: Failed to open '%s'", fileName.toLatin1().data());
+ return false;
+ }
+
+ QByteArray textData(dbFile.size() + 1, '\0');
+ dbFile.read(textData.data(), textData.size());
+
+ // handle either line-termination style
+#if (QT_VERSION >= QT_VERSION_CHECK(5,5,0))
+ QRegularExpression lineTerm("[\r\n]{1,2}");
+#else
+ QRegExp lineTerm("[\r\n]{1,2}");
+#endif
+
+#if (QT_VERSION >= QT_VERSION_CHECK(5,14,0))
+ QStringList lines = QString::fromUtf8(textData).split(lineTerm, Qt::SkipEmptyParts);
+#else
+ QStringList lines = QString::fromUtf8(textData).split(lineTerm, QString::SkipEmptyParts);
+#endif
+
+ // lines are: id^type^text^flag^ . Keep only the requested type. The text
+ // itself never contains '^', so a plain split is safe (id/type/text are
+ // the first three fields).
+ for (QStringList::Iterator it = lines.begin(); it != lines.end(); ++it)
+ {
+ const QStringList f = (*it).split('^');
+ if (f.size() < 3)
+ continue;
+
+ bool okId = false, okType = false;
+ const int type = f[1].toInt(&okType);
+ if (!okType || type != keepType)
+ continue;
+
+ const uint32_t id = f[0].toUInt(&okId);
+ if (!okId)
+ continue;
+
+ m_strings.insert(id, f[2]);
+ }
+
+ m_loaded = true;
+
+ seqInfo("DBStr: loaded %d type-%d strings from '%s'",
+ m_strings.count(), keepType, fileName.toLatin1().data());
+
+ return true;
+}
+
+QString DBStr::find(uint32_t id) const
+{
+ return m_strings.value(id);
+}
Added: showeq/branches/xerxes-trunk-dev/src/dbstr.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/dbstr.h (rev 0)
+++ showeq/branches/xerxes-trunk-dev/src/dbstr.h 2026-07-17 18:19:21 UTC (rev 1668)
@@ -0,0 +1,55 @@
+/*
+ * dbstr.h
+ * Copyright 2026 by the respective ShowEQ Developers
+ *
+ * This file is part of ShowEQ.
+ * http://www.sourceforge.net/projects/seq
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _DBSTR_H_
+#define _DBSTR_H_
+
+#include <cstdint>
+#include <QHash>
+#include <QString>
+
+//----------------------------------------------------------------------
+// DBStr
+//
+// Loader for the client's dbstr_us.txt string database. Lines are
+// '^'-delimited: id^type^text^flag^ . The (id, type) pair is the key;
+// type 1 is the display name used by AA titles, spells, etc.
+class DBStr
+{
+ public:
+ DBStr();
+ ~DBStr();
+
+ // load dbstr_us.txt; retains only the requested type (default 1, names)
+ bool load(const QString& dbstrFile, int keepType = 1);
+
+ // look up a string by its dbstr id; empty QString if absent
+ QString find(uint32_t id) const;
+
+ bool loaded() const { return m_loaded; }
+
+ protected:
+ QHash<uint32_t, QString> m_strings;
+ bool m_loaded;
+};
+
+#endif // _DBSTR_H_
Modified: showeq/branches/xerxes-trunk-dev/src/everquest.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/everquest.h 2026-07-17 17:48:07 UTC (rev 1667)
+++ showeq/branches/xerxes-trunk-dev/src/everquest.h 2026-07-17 18:19:21 UTC (rev 1668)
@@ -479,6 +479,28 @@
/*012*/
};
+/*
+ * OP_SendAATable. One record per AA rank, sent as a burst at zone-in. The
+ * record is variable length: a count-prefixed skills-requirement block and
+ * prev/next rank ids follow the fixed head below. Only the fixed head is
+ * needed to build the descID -> name/rank table, so the payload size check
+ * is SZC_None and the handler reads just this head.
+ */
+struct aaInfoStruct
+{
+/*000*/ int32_t descID; // per-rank id; matches charProfileStruct.aa_array[].AA
+/*004*/ uint8_t unknown004; // always 1
+/*005*/ int32_t hotKeySID;
+/*009*/ int32_t hotKeySID2;
+/*013*/ int32_t titleSID; // dbstr type-1 name id (shared by all ranks of an AA)
+/*017*/ int32_t descSID2; // dbstr description id (per-rank)
+/*021*/ uint32_t reqLevel;
+/*025*/ uint32_t cost; // points for THIS rank (aa_array.value is cumulative)
+/*029*/ uint32_t aaID; // activation ability id
+/*033*/ uint32_t rank; // current rank this record describes
+/*037*/
+};
+
/**
* Used in charProfileStruct. An item inline in the stream, used in Bandolier and Potion Belt.
* Size: 72 Octets
Modified: showeq/branches/xerxes-trunk-dev/src/interface.cpp
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/interface.cpp 2026-07-17 17:48:07 UTC (rev 1667)
+++ showeq/branches/xerxes-trunk-dev/src/interface.cpp 2026-07-17 18:19:21 UTC (rev 1668)
@@ -37,6 +37,9 @@
#include "spelllist.h"
#include "player.h"
#include "skilllist.h"
+#include "aalist.h"
+#include "aamgr.h"
+#include "dbstr.h"
#include "statlist.h"
#include "group.h"
#include "netdiag.h"
@@ -125,6 +128,9 @@
m_spawnList2(0),
m_spellList(0),
m_skillList(0),
+ m_aaList(0),
+ m_aaMgr(0),
+ m_dbStr(0),
m_statList(0),
m_spawnPointList(0),
m_packet(0),
@@ -295,6 +301,16 @@
// Create our player object
m_player = new Player(this, m_zoneMgr, m_guildmgr);
+ // Create the AA string database (dbstr_us.txt) and the AA manager. The
+ // manager builds an in-memory AA name/rank table from OP_SendAATable and
+ // tracks owned AAs from the character profile.
+ QString dbstrFileName = pSEQPrefs->getPrefString("DBStrFile", "Interface",
+ "dbstr_us.txt");
+ QFileInfo dbstrInfo = m_dataLocationMgr->findExistingFile(".", dbstrFileName);
+ m_dbStr = new DBStr();
+ m_dbStr->load(dbstrInfo.absoluteFilePath());
+ m_aaMgr = new AAMgr(m_dbStr, this, "aamgr");
+
// Create the filter manager
m_filterMgr = new FilterMgr(m_dataLocationMgr,
pSEQPrefs->getPrefString("FilterFile",
@@ -476,6 +492,10 @@
if (pSEQPrefs->getPrefBool("ShowPlayerSkills", section, true))
showSkillList();
+ // Alternate Advancement window (created only when shown)
+ if (pSEQPrefs->getPrefBool("ShowPlayerAA", section, false))
+ showAAList();
+
//
// Create the Player Status listview (ONLY CREATED WHEN NEEDED FLOYD!!!!)
//
@@ -571,6 +591,14 @@
m_packet->connect2("OP_PlayerProfile", SP_Zone, DIR_Server,
"uint8_t", SZC_None,
m_zoneMgr, SLOT(zonePlayer(const uint8_t*, size_t)));
+
+ // AA definition table (one record per AA rank, burst at zone-in) and
+ // the owned-AA set from the character profile feed the AA manager.
+ m_packet->connect2("OP_SendAATable", SP_Zone, DIR_Server,
+ "uint8_t", SZC_None,
+ m_aaMgr, SLOT(aaInfo(const uint8_t*, size_t)));
+ connect(m_zoneMgr, SIGNAL(playerProfile(const charProfileStruct*)),
+ m_aaMgr, SLOT(player(const charProfileStruct*)));
m_packet->connect2("OP_ZoneChange", SP_Zone, DIR_Client|DIR_Server,
"zoneChangeStruct", SZC_Match,
m_zoneMgr, SLOT(zoneChange(const uint8_t*, size_t, uint8_t)));
@@ -1216,6 +1244,10 @@
if (m_eqStrings != 0)
delete m_eqStrings;
+ // m_aaMgr is parented to this and freed by Qt; m_dbStr is not a QObject
+ if (m_dbStr != 0)
+ delete m_dbStr;
+
if (m_player != 0)
delete m_player;
@@ -1311,6 +1343,10 @@
SLOT(toggle_view_PlayerSkills()));
m_action_view_PlayerSkills->setCheckable(true);
+ m_action_view_PlayerAA = pViewMenu->addAction("Alternate Advancement", this,
+ SLOT(toggle_view_PlayerAA()));
+ m_action_view_PlayerAA->setCheckable(true);
+
m_action_view_Compass = pViewMenu->addAction("Compass", this,
SLOT(toggle_view_Compass()));
m_action_view_Compass->setCheckable(true);
@@ -4388,6 +4424,24 @@
pSEQPrefs->setPrefBool("ShowPlayerSkills", "Interface", !wasVisible);
}
+void EQInterface::toggle_view_PlayerAA(void)
+{
+ bool wasVisible = ((m_aaList != 0) && m_aaList->isVisible());
+
+ if (!wasVisible)
+ showAAList();
+ else
+ {
+ m_aaList->savePrefs();
+ m_aaList->hide();
+ removeWindowMenu(m_aaList);
+ delete m_aaList;
+ m_aaList = 0;
+ }
+
+ pSEQPrefs->setPrefBool("ShowPlayerAA", "Interface", !wasVisible);
+}
+
void
EQInterface::toggle_view_Compass(void)
{
@@ -5532,6 +5586,8 @@
m_skillList->isVisible());
m_action_view_PlayerSkills_Options->setEnabled((m_skillList !=0) &&
m_skillList->isVisible());
+ m_action_view_PlayerAA->setChecked((m_aaList != 0) &&
+ m_aaList->isVisible());
m_action_view_PlayerStats->setChecked((m_statList != 0) &&
m_statList->isVisible());
@@ -6238,6 +6294,32 @@
m_skillList->activateWindow();
}
+void EQInterface::showAAList(void)
+{
+ // if it doesn't exist, create it
+ if (m_aaList == 0)
+ {
+ m_aaList = new AAListWindow(m_aaMgr, 0, "aalist");
+ setDockEnabled(m_aaList,
+ pSEQPrefs->getPrefBool("DockablePlayerAA", "Interface", true));
+ Qt::DockWidgetArea edge = (Qt::DockWidgetArea)pSEQPrefs->getPrefInt("Dock",
+ m_aaList->preferenceName(), Qt::LeftDockWidgetArea);
+ addDockWidget(edge, m_aaList);
+ m_aaList->setFloating(!pSEQPrefs->getPrefBool("DockedPlayerAA", "Interface", true));
+
+ // connect aa list window slots to interface signals
+ connect(this, SIGNAL(saveAllPrefs(void)), m_aaList, SLOT(savePrefs(void)));
+ connect(this, SIGNAL(restoreFonts(void)), m_aaList, SLOT(restoreFont(void)));
+
+ // insert its menu into the window menu
+ insertWindowMenu(m_aaList);
+ }
+
+ // make sure it's visible
+ m_aaList->show();
+ m_aaList->activateWindow();
+}
+
void EQInterface::showSpellList(void)
{
// if it doesn't exist, create it
Modified: showeq/branches/xerxes-trunk-dev/src/interface.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/interface.h 2026-07-17 17:48:07 UTC (rev 1667)
+++ showeq/branches/xerxes-trunk-dev/src/interface.h 2026-07-17 18:19:21 UTC (rev 1668)
@@ -54,6 +54,9 @@
class SpellListWindow;
class SkillListWindow;
class StatListWindow;
+class AAListWindow;
+class AAMgr;
+class DBStr;
class SpawnPointWindow;
class EQPacket;
class ZoneMgr;
@@ -277,6 +280,7 @@
void toggle_view_PlayerStats();
void toggle_view_Compass();
void toggle_view_PlayerSkills();
+ void toggle_view_PlayerAA();
void toggle_view_Map(QAction* map);
void toggle_view_NetDiag();
void toggle_view_GuildList();
@@ -336,6 +340,7 @@
void showSpawnPointList(void);
void showStatList(void);
void showSkillList(void);
+ void showAAList(void);
void showSpellList(void);
void showCompass(void);
void showNetDiag(void);
@@ -378,6 +383,9 @@
SpawnListWindow2* m_spawnList2;
SpellListWindow* m_spellList;
SkillListWindow* m_skillList;
+ AAListWindow* m_aaList;
+ AAMgr* m_aaMgr;
+ DBStr* m_dbStr;
StatListWindow* m_statList;
SpawnPointWindow* m_spawnPointList;
EQPacket* m_packet;
@@ -481,6 +489,7 @@
QAction* m_action_view_SpawnPointList;
QAction* m_action_view_PlayerStats;
QAction* m_action_view_PlayerSkills;
+ QAction* m_action_view_PlayerAA;
QAction* m_action_view_Compass;
QAction* m_action_view_Map[maxNumMaps];
QAction* m_action_view_MessageWindow[maxNumMessageWindows];
Modified: showeq/branches/xerxes-trunk-dev/src/s_everquest.h
===================================================================
--- showeq/branches/xerxes-trunk-dev/src/s_everquest.h 2026-07-17 17:48:07 UTC (rev 1667)
+++ showeq/branches/xerxes-trunk-dev/src/s_everquest.h 2026-07-17 18:19:21 UTC (rev 1668)
@@ -19,6 +19,7 @@
AddStruct(Color_Struct);
AddStruct(spellBuff);
AddStruct(AA_Array);
+AddStruct(aaInfoStruct);
AddStruct(InlineItem);
AddStruct(BandolierStruct);
AddStruct(TributeStruct);
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <xer...@us...> - 2026-07-17 17:48:08
|
Revision: 1667
http://sourceforge.net/p/seq/svn/1667
Author: xerxes00
Date: 2026-07-17 17:48:07 +0000 (Fri, 17 Jul 2026)
Log Message:
-----------
Create xerxes-trunk-dev development branch from trunk
Added Paths:
-----------
showeq/branches/xerxes-trunk-dev/
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <xer...@us...> - 2026-07-16 19:53:53
|
Revision: 1666
http://sourceforge.net/p/seq/svn/1666
Author: xerxes00
Date: 2026-07-16 19:53:51 +0000 (Thu, 16 Jul 2026)
Log Message:
-----------
Merge 6.4.25 (trunk r1663) into the Legends branch.
Takes the version bump (now 6.4.25) and the everquest.h struct updates;
the position structs are reconciled to keep the Legends layouts where
they differ from live. conf/zoneopcodes.xml is intentionally left as the
Legends opcode map - trunk's live-id rotation is not merged.
Modified Paths:
--------------
showeq/branches/showeqlegends/ChangeLog
showeq/branches/showeqlegends/configure.in
showeq/branches/showeqlegends/src/everquest.h
Property Changed:
----------------
showeq/branches/showeqlegends/
Index: showeq/branches/showeqlegends
===================================================================
--- showeq/branches/showeqlegends 2026-07-16 19:05:22 UTC (rev 1665)
+++ showeq/branches/showeqlegends 2026-07-16 19:53:51 UTC (rev 1666)
Property changes on: showeq/branches/showeqlegends
___________________________________________________________________
Added: svn:mergeinfo
## -0,0 +1 ##
+/showeq/trunk:1663
\ No newline at end of property
Modified: showeq/branches/showeqlegends/ChangeLog
===================================================================
--- showeq/branches/showeqlegends/ChangeLog 2026-07-16 19:05:22 UTC (rev 1665)
+++ showeq/branches/showeqlegends/ChangeLog 2026-07-16 19:53:51 UTC (rev 1666)
@@ -1,3 +1,9 @@
+cn187 (7/15/26)
+----------------
+- Update to version 6.4.25
+- Support for EQ Patch 7/15/26
+- Struct and opcode updates (Newby)
+
BlueAdept (5/21/26)
----------------
- Update to version 6.4.24
Modified: showeq/branches/showeqlegends/configure.in
===================================================================
--- showeq/branches/showeqlegends/configure.in 2026-07-16 19:05:22 UTC (rev 1665)
+++ showeq/branches/showeqlegends/configure.in 2026-07-16 19:53:51 UTC (rev 1666)
@@ -2,7 +2,7 @@
dnl $Id$ $Name$
AC_PREREQ(2.59)
-AC_INIT(showeq, 6.4.24)
+AC_INIT(showeq, 6.4.25)
AC_CONFIG_SRCDIR(src/main.cpp)
AC_CONFIG_MACRO_DIR([m4])
AC_CANONICAL_SYSTEM
Modified: showeq/branches/showeqlegends/src/everquest.h
===================================================================
--- showeq/branches/showeqlegends/src/everquest.h 2026-07-16 19:05:22 UTC (rev 1665)
+++ showeq/branches/showeqlegends/src/everquest.h 2026-07-16 19:53:51 UTC (rev 1666)
@@ -597,7 +597,7 @@
/*0012*/ float z; // Zone in z coord in next zone
/*0016*/ float heading; // Zone in heading in next zone
/*0020*/ uint32_t unknown0020; // *** Placeholder
-/*0024*/
+/*0024*/ // EQL: struct ends here (trimmed to the wire size)
};
/*
@@ -2547,23 +2547,24 @@
/*0000*/ uint16_t spawnId;
/*0002*/ uint16_t spawnId2;
/*0004*/
+ unsigned heading:12; // heading
signed animation:10; // current animation
- unsigned pitch:12; // pitch (up/down heading)
unsigned padding00:10;
/*0008*/
signed deltaZ:13; // change in z
- signed deltaHeading:10; // change in heading
- unsigned padding01:9;
+ signed deltaX:13; // change in x
+ unsigned padding01:6;
/*0012*/
- signed z:19; // z coord (3rd loc value)
- unsigned heading:12; // heading
+ signed x:19; // x coord (1st loc value)
+ unsigned pitch:12; // pitch (up/down heading)
unsigned padding02:1;
/*0016*/
- signed deltaX:13; // change in x
signed y:19; // y coord (2nd loc value)
+ signed deltaHeading:10; // change in heading
+ unsigned padding03:3;
/*0020*/
+ signed z:19; // z coord (3rd loc value)
signed deltaY:13; // change in y
- signed x:19; // x coord (1st loc value)
/*0024*/
};
@@ -2572,24 +2573,39 @@
** Length: 42 Octets
** OpCode: PlayerPosCode
*/
+// EQL note: on EQ Legends this struct is NOT the self-position wire form.
+// The Legends c2s self-state (17-byte float records) is decoded raw in
+// Player::playerSelfState; the stock connects that consume this struct are
+// disconnected on Legends (see interface.cpp). Struct kept at the stock
+// live layout.
struct playerSelfPosStruct
{
-/*0000*/ float x; // x coord (Legends float at offset 0)
-/*0004*/ float y; // y coord
-/*0008*/ float z; // z coord
-/*0012*/ uint8_t posFlag; // 0x02 moving marker
-/*0013*/ uint8_t unknown0013;
-/*0014*/ uint8_t unknown0014;
-/*0015*/ uint8_t seq; // increments each update
-/*0016*/ uint16_t headingRaw; // heading region
- uint16_t spawnId;
- float deltaX;
- float deltaY;
- float deltaZ;
- unsigned heading:12;
- signed deltaHeading:10;
- unsigned pitch:12;
- signed animation:10;
+/*0000*/ uint16_t unknown0000; // ***Placeholder (update time counter?)
+/*0002*/ uint16_t spawnId; // Player's spawn id
+/*0004*/ uint16_t unknown0004; // ***Placeholder
+/*0006*/
+ float z; // z coord (3rd loc value)
+/*0010*/
+ float y; // y coord (2nd loc value)
+/*0014*/
+ unsigned heading:12; // heading
+ unsigned padding02:20;
+/*0018*/
+ float x; // x coord (1st loc value)
+/*0022*/
+ float deltaZ; // change in z
+/*0026*/
+ unsigned pitch:12; // pitch (up/down heading)
+ unsigned padding05:20;
+/*0030*/
+ float deltaX; // change in x
+/*0034*/
+ float deltaY; // change in y
+/*0038*/
+ signed animation:10; // current animation
+ signed deltaHeading:10; // change in heading
+ unsigned padding08:12;
+/*0042*/
};
/*
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|