Skip to content

Commit 969ca01

Browse files
authored
Merge f06239e into 2fb5905
2 parents 2fb5905 + f06239e commit 969ca01

16 files changed

Lines changed: 812 additions & 3 deletions

cmake/MangosParams.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
set(MANGOS_EXP "CLASSIC")
22
set(MANGOS_PKG "Mangos Zero")
3-
set(MANGOS_WORLD_VER 2026061702)
3+
set(MANGOS_WORLD_VER 2026062001)
44
set(MANGOS_REALM_VER 2026060300)
55
set(MANGOS_AHBOT_VER 2021010100)

src/game/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ source_group("Tool" FILES ${SRC_GRP_TOOL})
6969
file(GLOB SRC_GRP_VMAPS vmap/*.cpp vmap/*.h)
7070
source_group("vmaps" FILES ${SRC_GRP_VMAPS})
7171

72+
#DebugVis group
73+
file(GLOB SRC_GRP_DEBUGVIS DebugVis/*.cpp DebugVis/*.h)
74+
source_group("DebugVis" FILES ${SRC_GRP_DEBUGVIS})
75+
7276
#Warden group
7377
file(GLOB SRC_GRP_WARDEN Warden/*.cpp Warden/*.h)
7478
source_group("Warden" FILES ${SRC_GRP_WARDEN})
@@ -235,6 +239,7 @@ add_library(game STATIC
235239
${SRC_GRP_TIME}
236240
${SRC_GRP_TOOL}
237241
${SRC_GRP_VMAPS}
242+
${SRC_GRP_DEBUGVIS}
238243
${SRC_GRP_WARDEN}
239244
${SRC_GRP_WARDEN_MODULES}
240245
${SRC_GRP_WORLD_HANDLERS}
@@ -257,6 +262,7 @@ target_include_directories(game
257262
Time
258263
Tools
259264
vmap
265+
DebugVis
260266
Warden
261267
Warden/Modules
262268
WorldHandlers
Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
/*
2+
* GM chat commands for the server-side debug visualization toolkit:
3+
* .debug vis cells|los|path|collision|height
4+
* Each draws a one-shot, auto-despawning set of markers (engine-style debug draw).
5+
*/
6+
7+
#include "Chat.h"
8+
#include "DebugVis.h"
9+
#include "PerformanceMonitor.h"
10+
#include "Player.h"
11+
#include "Map.h"
12+
#include "World.h"
13+
#include "GridDefines.h"
14+
#include "PathFinder.h"
15+
16+
#include <cmath>
17+
#include <cstdlib>
18+
#include <cstdio>
19+
20+
bool ChatHandler::HandleDebugVisCellsCommand(char* args)
21+
{
22+
Player* player = m_session ? m_session->GetPlayer() : NULL;
23+
if (!player)
24+
return false;
25+
26+
int radius = 2;
27+
if (args && *args)
28+
radius = atoi(args);
29+
if (radius < 1) radius = 1;
30+
if (radius > 5) radius = 5; // (2r+1)^2 markers cap
31+
32+
Map* map = player->GetMap();
33+
const float cell = SIZE_OF_GRID_CELL;
34+
// Snap to the cell lattice so markers line up with cell spacing.
35+
float baseX = floor(player->GetPositionX() / cell) * cell;
36+
float baseY = floor(player->GetPositionY() / cell) * cell;
37+
38+
uint32 placed = 0;
39+
for (int i = -radius; i <= radius; ++i)
40+
{
41+
for (int j = -radius; j <= radius; ++j)
42+
{
43+
float wx = baseX + i * cell;
44+
float wy = baseY + j * cell;
45+
// Search ground from well above the player's elevation so distant cells
46+
// snap to real terrain; skip cells with no ground beneath (was placing
47+
// markers at the player's Z, which left them floating in mid-air).
48+
float wz = map->GetHeight(wx, wy, player->GetPositionZ() + 50.0f);
49+
if (wz < -50000.0f)
50+
continue;
51+
char lbl[160];
52+
snprintf(lbl, sizeof(lbl), "DebugVis CELL [%+d,%+d]\n(%.1f, %.1f, %.1f) ground Z=%.1f",
53+
i, j, wx, wy, wz, wz);
54+
if (DebugVis::Marker(player, DebugVis::DV_CELL, wx, wy, wz, lbl))
55+
++placed;
56+
}
57+
}
58+
PSendSysMessage("DebugVis: drew %u cell-grid markers (cell=%.1f yd, radius=%d). Despawn in %us.",
59+
placed, cell, radius, DebugVis::DespawnSeconds());
60+
return true;
61+
}
62+
63+
bool ChatHandler::HandleDebugVisLosCommand(char* /*args*/)
64+
{
65+
Player* player = m_session ? m_session->GetPlayer() : NULL;
66+
if (!player)
67+
return false;
68+
69+
Unit* target = getSelectedUnit();
70+
if (!target)
71+
{
72+
SendSysMessage(".debug vis los FAILED: no target selected. It draws the line of sight "
73+
"from you to a unit (green = clear, red = blocked, with the hit point marked). "
74+
"Select/target a creature or player, then run .debug vis los again.");
75+
SetSentErrorMessage(true);
76+
return false;
77+
}
78+
79+
Map* map = player->GetMap();
80+
float x1 = player->GetPositionX(), y1 = player->GetPositionY(), z1 = player->GetPositionZ() + 2.0f;
81+
float x2 = target->GetPositionX(), y2 = target->GetPositionY(), z2 = target->GetPositionZ() + 2.0f;
82+
83+
float total = sqrtf((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) + (z2 - z1) * (z2 - z1));
84+
if (map->IsInLineOfSight(x1, y1, z1, x2, y2, z2))
85+
{
86+
char lbl[160];
87+
snprintf(lbl, sizeof(lbl), "DebugVis LoS: CLEAR\nto %s, %.1f yd", target->GetName(), total);
88+
DebugVis::Line(player, DebugVis::DV_LOS_OK, x1, y1, z1, x2, y2, z2, 2.0f, lbl);
89+
SendSysMessage("DebugVis: line of sight is CLEAR (green).");
90+
}
91+
else
92+
{
93+
float hx = x2, hy = y2, hz = z2;
94+
map->GetHitPosition(x1, y1, z1, hx, hy, hz, -0.5f);
95+
float hitDist = sqrtf((hx - x1) * (hx - x1) + (hy - y1) * (hy - y1) + (hz - z1) * (hz - z1));
96+
DebugVis::Line(player, DebugVis::DV_LOS_BLOCK, x1, y1, z1, hx, hy, hz, 2.0f, "DebugVis LoS: BLOCKED");
97+
char lbl[192];
98+
snprintf(lbl, sizeof(lbl),
99+
"DebugVis LoS BLOCKED\nhit (%.1f, %.1f, %.1f)\n%.1f yd from you (target %.1f yd)",
100+
hx, hy, hz, hitDist, total);
101+
DebugVis::Marker(player, DebugVis::DV_HITPOINT, hx, hy, hz, lbl);
102+
PSendSysMessage("DebugVis: line of sight BLOCKED (red); hit at (%.1f, %.1f, %.1f).", hx, hy, hz);
103+
}
104+
return true;
105+
}
106+
107+
bool ChatHandler::HandleDebugVisPathCommand(char* /*args*/)
108+
{
109+
Player* player = m_session ? m_session->GetPlayer() : NULL;
110+
if (!player)
111+
return false;
112+
113+
Unit* target = getSelectedUnit();
114+
if (!target)
115+
{
116+
SendSysMessage(".debug vis path FAILED: no target selected. It draws the navmesh path "
117+
"from you to a unit (green = valid path, red = incomplete/none). "
118+
"Select/target a creature or player, then run .debug vis path again.");
119+
SetSentErrorMessage(true);
120+
return false;
121+
}
122+
123+
PathFinder pf(player);
124+
pf.calculate(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ());
125+
PathType type = pf.getPathType();
126+
PointsArray& pts = pf.getPath();
127+
128+
DebugVis::Category cat = (type & (PATHFIND_NOPATH | PATHFIND_INCOMPLETE | PATHFIND_NOT_USING_PATH))
129+
? DebugVis::DV_PATH_BAD : DebugVis::DV_PATH;
130+
131+
uint32 total = uint32(pts.size());
132+
uint32 placed = 0;
133+
uint32 idx = 0;
134+
for (PointsArray::const_iterator it = pts.begin(); it != pts.end(); ++it, ++idx)
135+
{
136+
// Skip the path point on/next to the player — markers are solid and would
137+
// trap the caster inside the object.
138+
float pdx = it->x - player->GetPositionX();
139+
float pdy = it->y - player->GetPositionY();
140+
if (pdx * pdx + pdy * pdy < 9.0f) // within ~3 yd
141+
continue;
142+
char lbl[176];
143+
snprintf(lbl, sizeof(lbl),
144+
"DebugVis PATH pt %u/%u\n(%.1f, %.1f, %.1f)\ntype=0x%X (%s)",
145+
idx + 1, total, it->x, it->y, it->z, uint32(type),
146+
cat == DebugVis::DV_PATH ? "ok" : "incomplete/none");
147+
if (DebugVis::Marker(player, cat, it->x, it->y, it->z, lbl))
148+
++placed;
149+
}
150+
151+
PSendSysMessage("DebugVis: path type=0x%X, %u points (%s).",
152+
uint32(type), placed, cat == DebugVis::DV_PATH ? "green=ok" : "red=incomplete/none");
153+
return true;
154+
}
155+
156+
bool ChatHandler::HandleDebugVisCollisionCommand(char* args)
157+
{
158+
Player* player = m_session ? m_session->GetPlayer() : NULL;
159+
if (!player)
160+
return false;
161+
162+
float dist = 40.0f;
163+
if (args && *args)
164+
{
165+
float v = (float)atof(args);
166+
if (v > 1.0f && v < 300.0f)
167+
dist = v;
168+
}
169+
170+
Map* map = player->GetMap();
171+
float o = player->GetOrientation();
172+
float x1 = player->GetPositionX(), y1 = player->GetPositionY(), z1 = player->GetPositionZ() + 2.0f;
173+
float x2 = x1 + cos(o) * dist, y2 = y1 + sin(o) * dist, z2 = z1;
174+
175+
float hx = x2, hy = y2, hz = z2;
176+
if (map->GetHitPosition(x1, y1, z1, hx, hy, hz, -0.5f))
177+
{
178+
float hitDist = sqrtf((hx - x1) * (hx - x1) + (hy - y1) * (hy - y1));
179+
DebugVis::Line(player, DebugVis::DV_COLLISION, x1, y1, z1, hx, hy, hz, 2.0f, "DebugVis: collision ray");
180+
char lbl[176];
181+
snprintf(lbl, sizeof(lbl), "DebugVis COLLISION\nhit (%.1f, %.1f, %.1f)\n%.1f yd ahead",
182+
hx, hy, hz, hitDist);
183+
DebugVis::Marker(player, DebugVis::DV_HITPOINT, hx, hy, hz, lbl);
184+
PSendSysMessage("DebugVis: collision at (%.1f, %.1f, %.1f), %.1f yd ahead.", hx, hy, hz, hitDist);
185+
}
186+
else
187+
{
188+
char lbl[96];
189+
snprintf(lbl, sizeof(lbl), "DebugVis: no collision\nwithin %.0f yd ahead", dist);
190+
DebugVis::Line(player, DebugVis::DV_COLLISION, x1, y1, z1, x2, y2, z2, 2.0f, lbl);
191+
PSendSysMessage("DebugVis: no collision within %.0f yd ahead.", dist);
192+
}
193+
return true;
194+
}
195+
196+
bool ChatHandler::HandleDebugVisualCommand(char* args)
197+
{
198+
Player* player = m_session ? m_session->GetPlayer() : NULL;
199+
if (!player)
200+
return false;
201+
if (!args || !*args)
202+
{
203+
// NOTE: SMSG_PLAY_SPELL_VISUAL takes a SpellVisualKit.dbc id (not SpellVisual).
204+
// 179 is confirmed working (trainer learn-spell sparkle). Valid kit ids in the
205+
// 1.12.1 client run 1..6757; good ones to try below.
206+
SendSysMessage("Usage: .debug visual <SpellVisualKit id> (1-6757). Confirmed working: 179. "
207+
"Try also 224, 300, 451, 686, 1027, 5670.");
208+
SetSentErrorMessage(true);
209+
return false;
210+
}
211+
uint32 id = uint32(atoi(args));
212+
if (!id)
213+
{
214+
SendSysMessage("DebugVis: invalid visual id.");
215+
SetSentErrorMessage(true);
216+
return false;
217+
}
218+
player->PlaySpellVisual(id);
219+
PSendSysMessage("DebugVis: played spell visual kit %u on you (visible to nearby players).", id);
220+
return true;
221+
}
222+
223+
bool ChatHandler::HandleDebugPerfCommand(char* args)
224+
{
225+
if (args && *args && (*args == 'r' || *args == 'R'))
226+
{
227+
PerformanceMonitor::Reset();
228+
SendSysMessage("PerformanceMonitor: stats reset.");
229+
return true;
230+
}
231+
232+
uint32 ticks, avgMs, maxMs, lastMs;
233+
PerformanceMonitor::GetStats(ticks, avgMs, maxMs, lastMs);
234+
PSendSysMessage("Server perf: world ticks=%u avg=%ums max=%ums last=%ums (.debug perf r to reset)",
235+
ticks, avgMs, maxMs, lastMs);
236+
return true;
237+
}
238+
239+
bool ChatHandler::HandleDebugVisHeightCommand(char* /*args*/)
240+
{
241+
Player* player = m_session ? m_session->GetPlayer() : NULL;
242+
if (!player)
243+
return false;
244+
245+
Map* map = player->GetMap();
246+
float px = player->GetPositionX(), py = player->GetPositionY(), pz = player->GetPositionZ();
247+
float groundZ = map->GetHeight(px, py, pz);
248+
249+
if (groundZ < -50000.0f)
250+
{
251+
SendSysMessage("DebugVis: no terrain height data at this position.");
252+
return true;
253+
}
254+
255+
char lbl[160];
256+
snprintf(lbl, sizeof(lbl), "DebugVis HEIGHT\nground Z=%.2f\nyou Z=%.2f (delta %.2f)",
257+
groundZ, pz, pz - groundZ);
258+
DebugVis::Marker(player, DebugVis::DV_HEIGHT, px, py, groundZ, lbl);
259+
PSendSysMessage("DebugVis: ground Z=%.2f, you Z=%.2f (delta %.2f).", groundZ, pz, pz - groundZ);
260+
return true;
261+
}
262+
263+
bool ChatHandler::HandleDebugVisClearCommand(char* /*args*/)
264+
{
265+
Player* player = m_session ? m_session->GetPlayer() : NULL;
266+
if (!player)
267+
return false;
268+
269+
uint32 removed = DebugVis::Clear(player);
270+
PSendSysMessage("DebugVis: cleared %u of your markers (the rest had already auto-despawned). "
271+
"Markers also auto-clear after %u seconds.", removed, DebugVis::DespawnSeconds());
272+
return true;
273+
}

0 commit comments

Comments
 (0)