diff --git a/src/client/component/gui_debug.cpp b/src/client/component/gui_debug.cpp index def9943a..cce50253 100644 --- a/src/client/component/gui_debug.cpp +++ b/src/client/component/gui_debug.cpp @@ -31,6 +31,15 @@ namespace gui_debug cube_mesh }; + enum entity_type + { + other, + trigger_radius, + generic_trigger, + actor, + count + }; + float camera[3] = {}; float axis[3][3] = {}; @@ -39,6 +48,7 @@ namespace gui_debug bool enabled; bool camera_locked; object_type type; + float mesh_thickness = 1.f; float range = 500.f; float camera[3] = {}; }; @@ -47,16 +57,23 @@ namespace gui_debug { bool draw_node_links; float size = 10.f; - float mesh_thickness = 1.f; float link_thickness = 1.f; float color[4] = {1.f, 0.f, 0.f, 1.f}; } path_node_settings{}; struct : draw_settings { + bool enabled_types[entity_type::count]; + float type_colors[entity_type::count][4] = + { + {0.f, 0.5f, 1.f, 0.3f}, + {0.f, 1.f, 0.f, 0.3f}, + {1.0f, 1.f, 0.f, 0.3f}, + {1.f, 0.5f, 1.f, 0.3f}, + }; + bool mesh_only = false; int point_count = 30; - float color[4] = {0.f, 1.f, 0.f, 0.5f}; - } trigger_settings{}; + } entity_bound_settings{}; struct point { @@ -160,7 +177,8 @@ namespace gui_debug window->DrawList->AddConvexPolyFilled(points, 4, color_); } - void draw_square_from_points(float* p1, float* p2, float* p3, float* p4, float* color) + void draw_square_from_points(float* p1, float* p2, float* p3, float* p4, float* color, + float thickness, bool mesh_only) { float p1_screen[2] = {}; float p2_screen[2] = {}; @@ -189,10 +207,25 @@ namespace gui_debug }; const auto color_ = ImGui::GetColorU32({color[0], color[1], color[2], color[3]}); - window->DrawList->AddConvexPolyFilled(points, 4, color_); + + window->DrawList->PathClear(); + window->DrawList->PathLineTo(ImVec2(p1_screen[0], p1_screen[1])); + window->DrawList->PathLineTo(ImVec2(p2_screen[0], p2_screen[1])); + window->DrawList->PathLineTo(ImVec2(p3_screen[0], p3_screen[1])); + window->DrawList->PathLineTo(ImVec2(p4_screen[0], p4_screen[1])); + window->DrawList->PathLineTo(ImVec2(p1_screen[0], p1_screen[1])); + + if (mesh_only) + { + window->DrawList->PathStroke(color_, 0, thickness); + } + else + { + window->DrawList->PathFillConvex(color_); + } } - void draw_cube(float* origin, float width, float* color) + void draw_cube(float* origin, float width, float* color, float thickness, bool mesh_only) { const auto half = width / 2.f; @@ -206,44 +239,14 @@ namespace gui_debug float p3_top[3] = {p3[0], p3[1], origin[2] + width}; float p4_top[3] = {p4[0], p4[1], origin[2] + width}; - draw_square_from_points(p1, p2, p3, p4, color); - draw_square_from_points(p1_top, p2_top, p3_top, p4_top, color); + draw_square_from_points(p1, p2, p3, p4, color, thickness, mesh_only); + draw_square_from_points(p1_top, p2_top, p3_top, p4_top, color, thickness, mesh_only); - draw_square_from_points(p3, p2, p2_top, p3_top, color); - draw_square_from_points(p4, p1, p1_top, p4_top, color); + draw_square_from_points(p3, p2, p2_top, p3_top, color, thickness, mesh_only); + draw_square_from_points(p4, p1, p1_top, p4_top, color, thickness, mesh_only); - draw_square_from_points(p1, p2, p2_top, p1_top, color); - draw_square_from_points(p4, p3, p3_top, p4_top, color); - } - - void draw_cube_mesh(float* origin, float width, float* color, float thickness) - { - const auto half = width / 2.f; - - float p1[3] = {origin[0] - half, origin[1] + half, origin[2]}; - float p2[3] = {origin[0] + half, origin[1] + half, origin[2]}; - float p3[3] = {origin[0] + half, origin[1] - half, origin[2]}; - float p4[3] = {origin[0] - half, origin[1] - half, origin[2]}; - - float p1_top[3] = {p1[0], p1[1], origin[2] + width}; - float p2_top[3] = {p2[0], p2[1], origin[2] + width}; - float p3_top[3] = {p3[0], p3[1], origin[2] + width}; - float p4_top[3] = {p4[0], p4[1], origin[2] + width}; - - draw_line(p1, p2, color, thickness); - draw_line(p2, p3, color, thickness); - draw_line(p3, p4, color, thickness); - draw_line(p4, p1, color, thickness); - - draw_line(p1_top, p2_top, color, thickness); - draw_line(p2_top, p3_top, color, thickness); - draw_line(p3_top, p4_top, color, thickness); - draw_line(p4_top, p1_top, color, thickness); - - draw_line(p1, p1_top, color, thickness); - draw_line(p2, p2_top, color, thickness); - draw_line(p3, p3_top, color, thickness); - draw_line(p4, p4_top, color, thickness); + draw_square_from_points(p1, p2, p2_top, p1_top, color, thickness, mesh_only); + draw_square_from_points(p4, p3, p3_top, p4_top, color, thickness, mesh_only); } float get_pi() @@ -252,7 +255,8 @@ namespace gui_debug return pi; } - void draw_cylinder(float* center, float radius, float height, int point_count, float* color) + void draw_cylinder(float* center, float radius, float height, int point_count, float* color, + float thickness, bool mesh_only) { const auto pi = get_pi(); @@ -304,7 +308,15 @@ namespace gui_debug window->DrawList->PathLineTo(points_bottom[i].point); } - window->DrawList->PathFillConvex(color_); + + if (mesh_only) + { + window->DrawList->PathStroke(color_, 0, thickness); + } + else + { + window->DrawList->PathFillConvex(color_); + } window->DrawList->PathClear(); for (auto i = 0; i < point_count; i++) @@ -316,7 +328,15 @@ namespace gui_debug window->DrawList->PathLineTo(points_top[i].point); } - window->DrawList->PathFillConvex(color_); + + if (mesh_only) + { + window->DrawList->PathStroke(color_, 0, thickness); + } + else + { + window->DrawList->PathFillConvex(color_); + } for (auto i = 0; i < point_count; i++) { @@ -352,10 +372,43 @@ namespace gui_debug } window->DrawList->PathLineTo(points_top[i].point); - window->DrawList->PathFillConvex(color_); + + if (mesh_only) + { + window->DrawList->PathStroke(color_, 0, thickness); + } + else + { + window->DrawList->PathFillConvex(color_); + } } } + void draw_rectangular_prism(float* center, game::Bounds bounds, float* color, float thickness, bool mesh_only) + { + float vertices[8][3] = + { + {center[0] - bounds.halfSize[0], center[1] - bounds.halfSize[1], center[2] - bounds.halfSize[2]}, + {center[0] - bounds.halfSize[0], center[1] + bounds.halfSize[1], center[2] - bounds.halfSize[2]}, + {center[0] + bounds.halfSize[0], center[1] + bounds.halfSize[1], center[2] - bounds.halfSize[2]}, + {center[0] + bounds.halfSize[0], center[1] - bounds.halfSize[1], center[2] - bounds.halfSize[2]}, + + {center[0] - bounds.halfSize[0], center[1] - bounds.halfSize[1], center[2] + bounds.halfSize[2]}, + {center[0] - bounds.halfSize[0], center[1] + bounds.halfSize[1], center[2] + bounds.halfSize[2]}, + {center[0] + bounds.halfSize[0], center[1] + bounds.halfSize[1], center[2] + bounds.halfSize[2]}, + {center[0] + bounds.halfSize[0], center[1] - bounds.halfSize[1], center[2] + bounds.halfSize[2]}, + }; + + draw_square_from_points(vertices[0], vertices[1], vertices[2], vertices[3], color, thickness, mesh_only); + draw_square_from_points(vertices[4], vertices[5], vertices[6], vertices[7], color, thickness, mesh_only); + + draw_square_from_points(vertices[0], vertices[4], vertices[5], vertices[1], color, thickness, mesh_only); + draw_square_from_points(vertices[7], vertices[6], vertices[2], vertices[3], color, thickness, mesh_only); + + draw_square_from_points(vertices[6], vertices[5], vertices[1], vertices[2], color, thickness, mesh_only); + draw_square_from_points(vertices[7], vertices[4], vertices[0], vertices[3], color, thickness, mesh_only); + } + void draw_window() { if (!gui::enabled_menus["debug"]) @@ -403,20 +456,48 @@ namespace gui_debug ImGui::TreePop(); } - if (ImGui::TreeNode("Triggers")) + if (ImGui::TreeNode("Entity bounds")) { - ImGui::Checkbox("Draw", &trigger_settings.enabled); - ImGui::Checkbox("Lock camera", &trigger_settings.camera_locked); + ImGui::Checkbox("Draw", &entity_bound_settings.enabled); + ImGui::Checkbox("Lock camera", &entity_bound_settings.camera_locked); - ImGui::SliderFloat("range", &trigger_settings.range, 0.f, 10000.f); - ImGui::SliderInt("circle max points", &trigger_settings.point_count, 3, 360); - - if (ImGui::TreeNode("Color picker")) + if (ImGui::TreeNode("Types")) { - ImGui::ColorPicker4("color", trigger_settings.color); - ImGui::TreePop(); + ImGui::Checkbox("trigger_radius", &entity_bound_settings.enabled_types[entity_type::trigger_radius]); + if (entity_bound_settings.enabled_types[entity_type::trigger_radius] && ImGui::TreeNode("Color picker")) + { + ImGui::ColorPicker4("color", entity_bound_settings.type_colors[entity_type::trigger_radius]); + ImGui::TreePop(); + } + + ImGui::Checkbox("generic trigger", &entity_bound_settings.enabled_types[entity_type::generic_trigger]); + if (entity_bound_settings.enabled_types[entity_type::generic_trigger] && ImGui::TreeNode("Color picker")) + { + ImGui::ColorPicker4("color", entity_bound_settings.type_colors[entity_type::generic_trigger]); + ImGui::TreePop(); + } + + ImGui::Checkbox("actor", &entity_bound_settings.enabled_types[entity_type::actor]); + if (entity_bound_settings.enabled_types[entity_type::actor] && ImGui::TreeNode("Color picker")) + { + ImGui::ColorPicker4("color", entity_bound_settings.type_colors[entity_type::actor]); + ImGui::TreePop(); + } + + ImGui::Checkbox("other entities", &entity_bound_settings.enabled_types[entity_type::other]); + if (entity_bound_settings.enabled_types[entity_type::other] && ImGui::TreeNode("Color picker")) + { + ImGui::ColorPicker4("color", entity_bound_settings.type_colors[entity_type::other]); + ImGui::TreePop(); + } + } + ImGui::SliderFloat("range", &entity_bound_settings.range, 0.f, 10000.f); + ImGui::SliderFloat("mesh thickness", &entity_bound_settings.mesh_thickness, 1.f, 20.f); + ImGui::Checkbox("mesh only", &entity_bound_settings.mesh_only); + ImGui::SliderInt("circle max points", &entity_bound_settings.point_count, 3, 360); + ImGui::TreePop(); } @@ -500,12 +581,10 @@ namespace gui_debug case object_type::square: draw_square(origin, path_node_settings.size, path_node_settings.color); break; - case object_type::cube: - draw_cube(origin, path_node_settings.size, path_node_settings.color); - break; case object_type::cube_mesh: - draw_cube_mesh(origin, path_node_settings.size, path_node_settings.color, - path_node_settings.mesh_thickness); + case object_type::cube: + draw_cube(origin, path_node_settings.size, path_node_settings.color, + path_node_settings.mesh_thickness, path_node_settings.type == object_type::cube_mesh); break; } @@ -516,32 +595,68 @@ namespace gui_debug } } + entity_type get_entity_type(const char* classname) + { + if (strstr(classname, "trigger_radius")) + { + return entity_type::trigger_radius; + } + else if (strstr(classname, "trigger")) + { + return entity_type::generic_trigger; + } + else if (strstr(classname, "actor")) + { + return entity_type::actor; + } + + return entity_type::other; + } + void draw_triggers() { - if (!trigger_settings.enabled) + if (!entity_bound_settings.enabled) { return; } - const auto trigger_radius = game::SL_GetString("trigger_radius", 0); - - for (auto i = 0; i < 0xF9E; i++) + for (auto i = 0; i < *game::num_entities; i++) { const auto entity = &game::g_entities[i]; const auto origin = entity->origin; - const auto radius = entity->halfSize[0]; - const auto height = entity->halfSize[2] * 2.f; - const auto distance = distance_2d(trigger_settings.camera, origin); + const auto distance = distance_2d(entity_bound_settings.camera, origin); + const auto* classname = game::SL_ConvertToString(entity->script_classname); - if (distance - radius * 2 > trigger_settings.range || - entity->script_classname != trigger_radius) + if (distance > entity_bound_settings.range || !classname) { continue; } - draw_cylinder(origin, radius, height, trigger_settings.point_count, - trigger_settings.color); + const auto type = get_entity_type(classname); + if (!entity_bound_settings.enabled_types[type]) + { + continue; + } + + switch (type) + { + case entity_type::trigger_radius: + { + const auto radius = entity->box.halfSize[0]; + const auto height = entity->box.halfSize[2] * 2.f; + + draw_cylinder(origin, radius, height, entity_bound_settings.point_count, + entity_bound_settings.type_colors[type], entity_bound_settings.mesh_thickness, entity_bound_settings.mesh_only); + break; + } + default: + { + draw_rectangular_prism(origin, entity->box, entity_bound_settings.type_colors[type], + entity_bound_settings.mesh_thickness, entity_bound_settings.mesh_only); + break; + } + } } } @@ -570,11 +685,11 @@ namespace gui_debug path_node_settings.camera[2] = camera[2]; } - if (!trigger_settings.camera_locked) + if (!entity_bound_settings.camera_locked) { - trigger_settings.camera[0] = camera[0]; - trigger_settings.camera[1] = camera[1]; - trigger_settings.camera[2] = camera[2]; + entity_bound_settings.camera[0] = camera[0]; + entity_bound_settings.camera[1] = camera[1]; + entity_bound_settings.camera[2] = camera[2]; } } } diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index 490266f5..291203a8 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -61,21 +61,28 @@ namespace game scr_string_t_dummy = 0x0, }; + struct Bounds + { + vec3_t midPoint; + vec3_t halfSize; + }; + struct gentity_s { EntityState s; char __pad0[0x1B]; vec3_t origin; char __pad1[0x98]; - float midPoint[0x3]; // entityShared.box - float halfSize[0x3]; // entityShared.box - char __pad2[0x40]; + Bounds box; + char __pad2[0x4]; + Bounds absBox; + char __pad3[0x24]; gclient_s* client; - char __pad3[0x30]; + char __pad4[0x30]; scr_string_t script_classname; - char __pad4[0x18]; + char __pad5[0x18]; char flags; - char __pad5[0x188]; + char __pad6[0x188]; }; // size = 760 //auto a = sizeof(gentity_s); @@ -973,12 +980,6 @@ namespace game const char* name; }; - struct Bounds - { - vec3_t midPoint; - vec3_t halfSize; - }; - struct pmove_t { }; diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index e6b6e697..c7152fc0 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -153,6 +153,7 @@ namespace game WEAK symbol g_poolSize{0xBF2E40}; WEAK symbol g_entities{0x52DDDA0}; + WEAK symbol num_entities{0x55CC738}; WEAK symbol pathData{0x52CCDA0}; WEAK symbol threadIds{0xB11DC80};