From d2494336c1e3c6cdfbccf23a6d870e1284ba4625 Mon Sep 17 00:00:00 2001
From: fed <58637860+fedddddd@users.noreply.github.com>
Date: Fri, 7 Jan 2022 19:19:04 +0100
Subject: [PATCH 1/5] Draw triggers

---
 src/client/component/gui_debug.cpp | 665 +++++++++++++++++++++++++++++
 src/client/game/structs.hpp        |  60 ++-
 2 files changed, 717 insertions(+), 8 deletions(-)
 create mode 100644 src/client/component/gui_debug.cpp

diff --git a/src/client/component/gui_debug.cpp b/src/client/component/gui_debug.cpp
new file mode 100644
index 00000000..4f8fdc06
--- /dev/null
+++ b/src/client/component/gui_debug.cpp
@@ -0,0 +1,665 @@
+#include <std_include.hpp>
+#include "loader/component_loader.hpp"
+
+#include "game/game.hpp"
+#include "game/dvars.hpp"
+
+#include "scheduler.hpp"
+#include "gui.hpp"
+
+#include <utils/string.hpp>
+
+namespace gui_debug
+{
+	namespace
+	{
+		game::dvar_t* cl_paused = nullptr;
+
+		enum object_type
+		{
+			square,
+			circle,
+			circle_fill,
+			cube,
+			cube_mesh
+		};
+
+		float camera[3] = {};
+		float axis[3][3] = {};
+
+		struct draw_settings
+		{
+			bool enabled;
+			bool camera_locked;
+			float camera[3] = {};
+			float range = 500.f;
+			float color[4] = {1.f, 0.f, 0.f, 1.f};
+			float mesh_thickness = 1.f;
+			float size = 10.f;
+			object_type type;
+		};
+
+		struct : draw_settings
+		{
+			float link_thickness = 1.f;
+			bool draw_linked_nodes;
+		} path_node_settings{};
+
+		struct point
+		{
+			ImVec2 point;
+			bool valid;
+		};
+
+		draw_settings trigger_settings{};
+
+		float vector_dot(float* a, float* b)
+		{
+			return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]);
+		}
+
+		bool world_pos_to_screen_pos(float* origin, float* out)
+		{
+			float local[3] =
+			{
+				origin[0] - camera[0],
+				origin[1] - camera[1],
+				origin[2] - camera[2]
+			};
+
+			float transform[3] =
+			{
+				vector_dot(local, axis[1]),
+				vector_dot(local, axis[2]),
+				vector_dot(local, axis[0])
+			};
+
+			if (transform[2] < 0.1f)
+			{
+				return false;
+			}
+
+			const auto width = game::ScrPlace_GetViewPlacement()->realViewportSize[0];
+			const auto height = game::ScrPlace_GetViewPlacement()->realViewportSize[1];
+
+			out[0] = (width / 2) * (1 - transform[0] / game::refdef->fovX / transform[2]);
+			out[1] = (height / 2) * (1 - transform[1] / game::refdef->fovY / transform[2]);
+
+			return true;
+		}
+
+		void draw_line(float* start, float* end, float* color, float thickness)
+		{
+			ImGuiWindow* window = ImGui::GetCurrentWindow();
+
+			float start_screen[2] = {};
+			float end_screen[2] = {};
+
+			auto result = 1;
+			result *= world_pos_to_screen_pos(start, start_screen);
+			result *= world_pos_to_screen_pos(end, end_screen);
+
+			if (!result)
+			{
+				return;
+			}
+
+			const auto start_ = ImVec2(start_screen[0], start_screen[1]);
+			const auto end_ = ImVec2(end_screen[0], end_screen[1]);
+			const auto color_ = ImGui::GetColorU32({color[0], color[1], color[2], color[3]});
+
+			window->DrawList->AddLine(start_, end_, color_, thickness);
+		}
+
+		void draw_square(float* origin, float width, float* color)
+		{
+			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_screen[2] = {};
+			float p2_screen[2] = {};
+			float p3_screen[2] = {};
+			float p4_screen[2] = {};
+
+			auto result = 1;
+			result *= world_pos_to_screen_pos(p1, p1_screen);
+			result *= world_pos_to_screen_pos(p2, p2_screen);
+			result *= world_pos_to_screen_pos(p3, p3_screen);
+			result *= world_pos_to_screen_pos(p4, p4_screen);
+
+			if (!result)
+			{
+				return;
+			}
+
+			ImGuiWindow* window = ImGui::GetCurrentWindow();
+
+			ImVec2 points[4] =
+			{
+				ImVec2(p1_screen[0], p1_screen[1]),
+				ImVec2(p2_screen[0], p2_screen[1]),
+				ImVec2(p3_screen[0], p3_screen[1]),
+				ImVec2(p4_screen[0], p4_screen[1])
+			};
+
+			const auto color_ = ImGui::GetColorU32({color[0], color[1], color[2], color[3]});
+			window->DrawList->AddConvexPolyFilled(points, 4, color_);
+		}
+
+		void draw_square_from_points(float* p1, float* p2, float* p3, float* p4, float* color)
+		{
+			float p1_screen[2] = {};
+			float p2_screen[2] = {};
+			float p3_screen[2] = {};
+			float p4_screen[2] = {};
+
+			auto result = 1;
+			result *= world_pos_to_screen_pos(p1, p1_screen);
+			result *= world_pos_to_screen_pos(p2, p2_screen);
+			result *= world_pos_to_screen_pos(p3, p3_screen);
+			result *= world_pos_to_screen_pos(p4, p4_screen);
+
+			if (!result)
+			{
+				return;
+			}
+
+			ImGuiWindow* window = ImGui::GetCurrentWindow();
+
+			ImVec2 points[4] =
+			{
+				ImVec2(p1_screen[0], p1_screen[1]),
+				ImVec2(p2_screen[0], p2_screen[1]),
+				ImVec2(p3_screen[0], p3_screen[1]),
+				ImVec2(p4_screen[0], p4_screen[1])
+			};
+
+			const auto color_ = ImGui::GetColorU32({color[0], color[1], color[2], color[3]});
+			window->DrawList->AddConvexPolyFilled(points, 4, color_);
+		}
+
+		void draw_cube(float* origin, float width, float* color)
+		{
+			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_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(p3, p2, p2_top, p3_top, color);
+			draw_square_from_points(p4, p1, p1_top, p4_top, color);
+
+			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);
+		}
+
+		float get_pi()
+		{
+			static const auto pi = static_cast<float>(atan(1)) * 4.f;
+			return pi;
+		}
+
+		void draw_circle(float* center, float radius, float* color, float thickness)
+		{
+			const auto pi = get_pi();
+
+			ImGuiWindow* window = ImGui::GetCurrentWindow();
+			
+			window->DrawList->PathClear();
+
+			for (auto angle = 0.f; angle < 360; angle += 1.f)
+			{
+				const auto x = center[0] + radius * cos(angle * pi / 180.f);
+				const auto y = center[1] + radius * sin(angle * pi / 180.f);
+
+				float point[3] = {x, y, center[2]};
+				float point_screen[2] = {};
+
+				if (!world_pos_to_screen_pos(point, point_screen))
+				{
+					return;
+				}
+
+				const auto point_ = ImVec2(point_screen[0], point_screen[1]);
+				window->DrawList->PathLineTo(point_);
+			}
+
+			const auto color_ = ImGui::GetColorU32({color[0], color[1], color[2], color[3]});
+			window->DrawList->PathStroke(color_, 0, thickness);
+		}
+
+		void draw_circle_filled(float* center, float radius, float* color)
+		{
+			const auto pi = get_pi();
+
+			ImGuiWindow* window = ImGui::GetCurrentWindow();
+			
+			window->DrawList->PathClear();
+
+			for (auto angle = 0; angle < 360; angle += 12)
+			{
+				const auto x = center[0] + radius * cos(static_cast<float>(angle) * pi / 180.f);
+				const auto y = center[1] + radius * sin(static_cast<float>(angle) * pi / 180.f);
+
+				float point[3] = {x, y, center[2]};
+				float point_screen[2] = {};
+
+				if (!world_pos_to_screen_pos(point, point_screen))
+				{
+					continue;
+				}
+
+				const auto point_ = ImVec2(point_screen[0], point_screen[1]);
+				window->DrawList->PathLineTo(point_);
+			}
+
+			const auto color_ = ImGui::GetColorU32({color[0], color[1], color[2], color[3]});
+			window->DrawList->PathFillConvex(color_);
+		}
+
+		void draw_cylinder(float* center, float radius, float height, float* color, float thickness)
+		{
+			const auto pi = get_pi();
+
+			ImGuiWindow* window = ImGui::GetCurrentWindow();
+
+			float top[3] = {center[0], center[1], center[2] + height};
+
+			const auto color_ = ImGui::GetColorU32({color[0], color[1], color[2], color[3]});
+
+			const auto max_points = 30;
+			const auto step = 360 / max_points;
+
+			point points_top[max_points];
+			point points_bottom[max_points];
+
+			auto point_index = 0;
+			for (auto angle = 0; angle < 360; angle += step)
+			{
+				const auto x = center[0] + radius * cos(static_cast<float>(angle) * pi / 180.f);
+				const auto y = center[1] + radius * sin(static_cast<float>(angle) * pi / 180.f);
+
+				float point[3] = {x, y, center[2]};
+				float point_top[3] = {x, y, center[2] + height};
+
+				float point_screen[2] = {};
+				float point_top_screen[2] = {};
+
+				const auto index = point_index++;
+
+				auto result = 1;
+				result *= world_pos_to_screen_pos(point, point_screen);
+				result *= world_pos_to_screen_pos(point_top, point_top_screen);
+
+				if (!result)
+				{
+					points_bottom[index] = {{}, false};
+					points_top[index] = {{}, false};
+					continue;
+				}
+				
+				points_bottom[index] = {ImVec2(point_screen[0], point_screen[1]), true};
+				points_top[index] = {ImVec2(point_top_screen[0], point_top_screen[1]), true};
+			}
+
+			window->DrawList->PathClear();
+			for (auto i = 0; i < max_points; i++)
+			{
+				if (!points_bottom[i].valid)
+				{
+					continue;
+				}
+
+				window->DrawList->PathLineTo(points_bottom[i].point);
+			}
+			window->DrawList->PathFillConvex(color_);
+
+			window->DrawList->PathClear();
+			for (auto i = 0; i < max_points; i++)
+			{
+				if (!points_top[i].valid)
+				{
+					continue;
+				}
+
+				window->DrawList->PathLineTo(points_top[i].point);
+			}
+			window->DrawList->PathFillConvex(color_);
+
+			for (auto i = 0; i < max_points - 1; i++)
+			{
+				window->DrawList->PathClear();
+
+				if (!points_bottom[i].valid)
+				{
+					continue;
+				}
+
+				window->DrawList->PathLineTo(points_bottom[i].point);
+
+				if (i == max_points - 1)
+				{
+					window->DrawList->PathLineTo(points_bottom[0].point);
+					window->DrawList->PathLineTo(points_top[0].point);
+				}
+				else
+				{
+					window->DrawList->PathLineTo(points_bottom[i + 1].point);
+					window->DrawList->PathLineTo(points_top[i + 1].point);
+				}
+
+				window->DrawList->PathLineTo(points_top[i].point);
+
+				window->DrawList->PathFillConvex(color_);
+			}
+		}
+
+		void draw_object(float* origin, object_type type)
+		{
+			switch (type)
+			{
+			case object_type::square:
+				draw_square(origin, path_node_settings.size, path_node_settings.color);
+				break;
+			case object_type::circle:
+				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);
+				break;
+			}
+		}
+
+		void draw_window()
+		{
+			if (!gui::enabled_menus["debug"])
+			{
+				return;
+			}
+
+			ImGui::Begin("Debug", &gui::enabled_menus["debug"]);
+
+			if (ImGui::TreeNode("Path nodes"))
+			{
+				ImGui::Checkbox("Draw", &path_node_settings.enabled);
+				ImGui::Checkbox("Lock camera", &path_node_settings.camera_locked);
+
+				ImGui::Checkbox("Draw linked nodes", &path_node_settings.draw_linked_nodes);
+
+				if (ImGui::TreeNode("Object type"))
+				{
+					ImGui::RadioButton("square", reinterpret_cast<int*>(&path_node_settings.type), object_type::square);
+					ImGui::RadioButton("cube", reinterpret_cast<int*>(&path_node_settings.type), object_type::cube);
+					ImGui::RadioButton("cube mesh", reinterpret_cast<int*>(&path_node_settings.type), object_type::cube_mesh);
+
+					ImGui::TreePop();
+				}
+
+				ImGui::SliderFloat("range", &path_node_settings.range, 0.f, 10000.f);
+				ImGui::SliderFloat("size", &path_node_settings.size, 5.f, 100.f);
+
+				if (path_node_settings.draw_linked_nodes)
+				{
+					ImGui::SliderFloat("link thickness", &path_node_settings.link_thickness, 1.f, 20.f);
+				}
+
+				if (path_node_settings.type == object_type::cube_mesh)
+				{
+					ImGui::SliderFloat("mesh thickness", &path_node_settings.mesh_thickness, 1.f, 20.f);
+				}
+
+				if (ImGui::TreeNode("Color picker"))
+				{
+					ImGui::ColorPicker4("color", path_node_settings.color);
+					ImGui::TreePop();
+				}
+
+				ImGui::TreePop();
+			}
+
+			if (ImGui::TreeNode("Triggers"))
+			{
+				ImGui::Checkbox("Draw", &trigger_settings.enabled);
+				ImGui::Checkbox("Lock camera", &trigger_settings.camera_locked);
+
+				ImGui::SliderFloat("range", &trigger_settings.range, 0.f, 10000.f);
+				ImGui::SliderFloat("mesh thickness", &trigger_settings.mesh_thickness, 1.f, 20.f);
+
+				if (ImGui::TreeNode("Color picker"))
+				{
+					ImGui::ColorPicker4("color", trigger_settings.color);
+					ImGui::TreePop();
+				}
+
+				ImGui::TreePop();
+			}
+
+			ImGui::End();
+		}
+
+		float distance_2d(float* a, float* b)
+		{
+			return sqrt((a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1]));
+		}
+
+		void get_pathnode_origin(game::pathnode_t* node, float* out)
+		{
+			out[0] = node->vLocalOrigin[0];
+			out[1] = node->vLocalOrigin[1];
+			out[2] = node->vLocalOrigin[2];
+
+			game::PathNode_WorldifyPosFromParent(node, out);
+		}
+
+		void draw_linked_nodes(game::pathnode_t* node, float* origin)
+		{
+			for (unsigned int i = 0; i < node->totalLinkCount; i++)
+			{
+				float linked_origin[3] = {};
+				const auto num = node->Links[i].nodeNum;
+				const auto linked = &game::pathData->nodes[num];
+
+				get_pathnode_origin(linked, linked_origin);
+				if (distance_2d(path_node_settings.camera, linked_origin) < path_node_settings.range)
+				{
+					draw_line(origin, linked_origin, path_node_settings.color, 
+						path_node_settings.link_thickness);
+				}
+			}
+		}
+
+		void begin_render_window()
+		{
+			const auto& io = ImGui::GetIO();
+
+			ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
+			ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, {0.0f, 0.0f});
+			ImGui::PushStyleColor(ImGuiCol_WindowBg, {0.0f, 0.0f, 0.0f, 0.0f});
+			ImGui::Begin("debug window", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs);
+
+			ImGui::SetWindowPos(ImVec2(0, 0), ImGuiCond_Always);
+			ImGui::SetWindowSize(ImVec2(io.DisplaySize.x, io.DisplaySize.y), ImGuiCond_Always);
+		}
+
+		void end_render_window()
+		{
+			ImGuiWindow* window = ImGui::GetCurrentWindow();
+			window->DrawList->PushClipRectFullScreen();
+
+			ImGui::End();
+			ImGui::PopStyleColor();
+			ImGui::PopStyleVar(2);
+		}
+
+		void draw_nodes()
+		{
+			if (!path_node_settings.enabled)
+			{
+				return;
+			}
+
+			for (unsigned int i = 0; i < game::pathData->nodeCount; i++)
+			{
+				float origin[3] = {};
+				const auto node = &game::pathData->nodes[i];
+
+				get_pathnode_origin(node, origin);
+				if (distance_2d(path_node_settings.camera, origin) >= path_node_settings.range)
+				{
+					continue;
+				}
+
+				draw_object(origin, path_node_settings.type);
+				if (path_node_settings.draw_linked_nodes)
+				{
+					draw_linked_nodes(node, origin);
+				}
+			}
+		}
+
+		void draw_triggers()
+		{
+			if (!trigger_settings.enabled)
+			{
+				return;
+			}
+
+			const auto trigger_radius = game::SL_GetString("trigger_radius", 0);
+
+			for (auto i = 0; i < 0xF9E; 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);
+
+				if (distance - radius > trigger_settings.range || entity->script_classname != trigger_radius)
+				{
+					continue;
+				}
+
+				float top[3] = {origin[0], origin[1], origin[2] + entity->halfSize[2] * 2.f};
+				draw_cylinder(top, radius, height, trigger_settings.color, trigger_settings.mesh_thickness);
+			}
+		}
+
+		void update_camera()
+		{
+			camera[0] = game::refdef->org[0];
+			camera[1] = game::refdef->org[1];
+			camera[2] = game::refdef->org[2];
+
+			axis[0][0] = game::refdef->axis[0][0];
+			axis[0][1] = game::refdef->axis[0][1];
+			axis[0][2] = game::refdef->axis[0][2];
+
+			axis[1][0] = game::refdef->axis[1][0];
+			axis[1][1] = game::refdef->axis[1][1];
+			axis[1][2] = game::refdef->axis[1][2];
+
+			axis[2][0] = game::refdef->axis[2][0];
+			axis[2][1] = game::refdef->axis[2][1];
+			axis[2][2] = game::refdef->axis[2][2];
+
+			if (!path_node_settings.camera_locked)
+			{
+				path_node_settings.camera[0] = camera[0];
+				path_node_settings.camera[1] = camera[1];
+				path_node_settings.camera[2] = camera[2];
+			}
+
+			if (!trigger_settings.camera_locked)
+			{
+				trigger_settings.camera[0] = camera[0];
+				trigger_settings.camera[1] = camera[1];
+				trigger_settings.camera[2] = camera[2];
+			}
+		}
+	}
+
+	class component final : public component_interface
+	{
+	public:
+		void post_unpack() override
+		{
+			gui::on_frame(draw_window);
+			gui::on_frame(draw_nodes, true);
+			gui::on_frame([]()
+			{
+				if (!game::SV_Loaded() || cl_paused->current.integer)
+				{
+					return;
+				}
+
+				begin_render_window();
+				draw_nodes();
+				draw_triggers();
+				end_render_window();
+			}, true);
+
+			scheduler::on_game_initialized([]()
+			{
+				cl_paused = game::Dvar_FindVar("cl_paused");
+			});
+
+			scheduler::loop([]()
+			{
+				if (!game::SV_Loaded() || cl_paused->current.integer)
+				{
+					return;
+				}
+
+				update_camera();
+			}, scheduler::pipeline::renderer);
+		}
+	};
+}
+
+REGISTER_COMPONENT(gui_debug::component)
diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp
index 3eaf0d67..f131e3ab 100644
--- a/src/client/game/structs.hpp
+++ b/src/client/game/structs.hpp
@@ -56,18 +56,67 @@ namespace game
 		char entityNum;
 	};
 
+	enum scr_string_t
+	{
+		scr_string_t_dummy = 0x0,
+	};
+
 	struct gentity_s
 	{
 		EntityState s;
 		char __pad0[0x1B];
 		vec3_t origin;
-		char __pad1[0xF0];
+		char __pad1[152];
+		float midPoint[3]; // entityShared.box
+		float halfSize[3]; // entityShared.box
+		char __pad2[64];
 		gclient_s* client;
-		char __pad2[0x4C];
+		char __pad3[48];
+		scr_string_t script_classname;
+		char __pad4[0x14]; // 416
 		char flags;
-		char __pad3[392];
+		char __pad5[392];
 	}; // size = 760
 
+	static_assert(sizeof(gentity_s) == 760);
+
+	struct pathlink_s
+	{
+		char __pad0[4];
+		unsigned __int16 nodeNum;
+		char __pad[6];
+	};
+
+	static_assert(sizeof(pathlink_s) == 12);
+
+	struct pathnode_t
+	{
+		unsigned __int16 type;
+		unsigned int spawnflags;
+		unsigned int targetname;
+		unsigned int script_linkName;
+		unsigned int script_noteworthy;
+		unsigned int target;
+		unsigned int animscript;
+		int animscriptfunc;
+		float vLocalOrigin[3];
+		char __pad0[28];
+		unsigned __int16 totalLinkCount;
+		char __pad1[2];
+		pathlink_s* Links;
+		char __pad2[104];
+	}; // size = 192
+
+	static_assert(sizeof(pathnode_t) == 192);
+
+	struct PathData
+	{
+		const char* name;
+		unsigned int nodeCount;
+		pathnode_t* nodes;
+		// ... 
+	};
+
 	struct Material
 	{
 		const char* name;
@@ -653,11 +702,6 @@ namespace game
 		int freeFlags;
 	};
 
-	enum scr_string_t
-	{
-		scr_string_t_dummy = 0x0,
-	};
-
 	struct scr_entref_t
 	{
 		unsigned short entnum;

From 72a02c2deb2e9f58fb026aef75dd52b49b4bfecd Mon Sep 17 00:00:00 2001
From: Federico Cecchetto <fedecek3@gmail.com>
Date: Sat, 8 Jan 2022 01:28:46 +0100
Subject: [PATCH 2/5] Entity list  & asset list  & debug windows fixes

---
 src/client/component/gui_asset_list.cpp  |  6 ++++--
 src/client/component/gui_debug.cpp       | 25 ++++++++++++++++--------
 src/client/component/gui_entity_list.cpp |  9 ++++++---
 3 files changed, 27 insertions(+), 13 deletions(-)

diff --git a/src/client/component/gui_asset_list.cpp b/src/client/component/gui_asset_list.cpp
index 4950026f..b8a02ef3 100644
--- a/src/client/component/gui_asset_list.cpp
+++ b/src/client/component/gui_asset_list.cpp
@@ -64,12 +64,14 @@ namespace asset_list
 				ImGui::InputText("asset name", &assets_name_filter[type]);
 				ImGui::BeginChild("assets list");
 
-				fastfiles::enum_assets(type, [type](const game::XAssetHeader header)
+				const auto lowercase_filter = utils::string::to_lower(assets_name_filter[type]);
+
+				fastfiles::enum_assets(type, [&lowercase_filter, type](const game::XAssetHeader header)
 				{
 					const auto asset = game::XAsset{type, header};
 					const auto* const asset_name = game::DB_GetXAssetName(&asset);
 
-					if (utils::string::find_lower(asset_name, assets_name_filter[type]) && ImGui::Button(asset_name))
+					if (strstr(asset_name, lowercase_filter.data()) && ImGui::Button(asset_name))
 					{
 						gui::copy_to_clipboard(asset_name);
 					}
diff --git a/src/client/component/gui_debug.cpp b/src/client/component/gui_debug.cpp
index 5c2b131c..a172100f 100644
--- a/src/client/component/gui_debug.cpp
+++ b/src/client/component/gui_debug.cpp
@@ -81,7 +81,7 @@ namespace gui_debug
 				vector_dot(local, axis[0])
 			};
 
-			if (transform[2] < 0.1f)
+			if (transform[2] < 0.01f)
 			{
 				return false;
 			}
@@ -375,11 +375,12 @@ namespace gui_debug
 			}
 			window->DrawList->PathFillConvex(color_);
 
-			for (auto i = 0; i < max_points - 1; i++)
+			for (auto i = 0; i < max_points; i++)
 			{
 				window->DrawList->PathClear();
 
-				if (!points_bottom[i].valid)
+				if (!points_bottom[i].valid ||
+					!points_top[i].valid)
 				{
 					continue;
 				}
@@ -388,11 +389,21 @@ namespace gui_debug
 
 				if (i == max_points - 1)
 				{
+					if (!points_bottom[0].valid || !points_top[0].valid)
+					{
+						continue;
+					}
+
 					window->DrawList->PathLineTo(points_bottom[0].point);
 					window->DrawList->PathLineTo(points_top[0].point);
 				}
 				else
 				{
+					if (!points_bottom[i + 1].valid || !points_top[i + 1].valid)
+					{
+						continue;
+					}
+
 					window->DrawList->PathLineTo(points_bottom[i + 1].point);
 					window->DrawList->PathLineTo(points_top[i + 1].point);
 				}
@@ -592,8 +603,7 @@ namespace gui_debug
 					continue;
 				}
 
-				float top[3] = {origin[0], origin[1], origin[2] + entity->halfSize[2] * 2.f};
-				draw_cylinder(top, radius, height, trigger_settings.color, trigger_settings.mesh_thickness);
+				draw_cylinder(origin, radius, height, trigger_settings.color, trigger_settings.mesh_thickness);
 			}
 		}
 
@@ -637,7 +647,6 @@ namespace gui_debug
 		void post_unpack() override
 		{
 			gui::on_frame(draw_window);
-			gui::on_frame(draw_nodes, true);
 			gui::on_frame([]()
 			{
 				if (!game::SV_Loaded() || cl_paused->current.integer)
@@ -651,10 +660,10 @@ namespace gui_debug
 				end_render_window();
 			}, true);
 
-			scheduler::on_game_initialized([]()
+			scheduler::once([]()
 			{
 				cl_paused = game::Dvar_FindVar("cl_paused");
-			});
+			}, scheduler::pipeline::main);
 
 			scheduler::loop([]()
 			{
diff --git a/src/client/component/gui_entity_list.cpp b/src/client/component/gui_entity_list.cpp
index a0151f19..40eddb62 100644
--- a/src/client/component/gui_entity_list.cpp
+++ b/src/client/component/gui_entity_list.cpp
@@ -666,7 +666,7 @@ namespace entity_list
 				ImGui::Text("Fields");
 
 				auto index = 0;
-				for (auto i = data.filters.fields.begin(); i != data.filters.fields.end(); ++i)
+				for (auto i = data.filters.fields.begin(); i != data.filters.fields.end();)
 				{
 					if (ImGui::TreeNode(utils::string::va("Filter #%i", index++)))
 					{
@@ -675,12 +675,15 @@ namespace entity_list
 
 						if (ImGui::Button("Erase"))
 						{
-							data.filters.fields.erase(i);
-							--i;
+							i = data.filters.fields.erase(i);
+							ImGui::TreePop();
+							continue;
 						}
 
 						ImGui::TreePop();
 					}
+
+					++i;
 				}
 
 				if (ImGui::Button("Add field filter"))

From ece5da170bd78079a6b76992a19e1889cfbe69eb Mon Sep 17 00:00:00 2001
From: fed <58637860+fedddddd@users.noreply.github.com>
Date: Sat, 8 Jan 2022 10:39:08 +0100
Subject: [PATCH 3/5] Fix entity struct

---
 src/client/game/structs.hpp | 30 ++++++++++++++++--------------
 1 file changed, 16 insertions(+), 14 deletions(-)

diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp
index 30aae8ff..490266f5 100644
--- a/src/client/game/structs.hpp
+++ b/src/client/game/structs.hpp
@@ -12,7 +12,7 @@ namespace game
 	{
 		char __pad0[0x8C];
 		vec3_t velocity;
-		char __pad1[59504];
+		char __pad1[0xE870];
 		char flags;
 	};
 
@@ -66,25 +66,27 @@ namespace game
 		EntityState s;
 		char __pad0[0x1B];
 		vec3_t origin;
-		char __pad1[152];
-		float midPoint[3]; // entityShared.box
-		float halfSize[3]; // entityShared.box
-		char __pad2[64];
+		char __pad1[0x98];
+		float midPoint[0x3]; // entityShared.box
+		float halfSize[0x3]; // entityShared.box
+		char __pad2[0x40];
 		gclient_s* client;
-		char __pad3[48];
+		char __pad3[0x30];
 		scr_string_t script_classname;
-		char __pad4[0x14]; // 416
+		char __pad4[0x18];
 		char flags;
-		char __pad5[392];
+		char __pad5[0x188];
 	}; // size = 760
 
+	//auto a = sizeof(gentity_s);
+
 	static_assert(sizeof(gentity_s) == 760);
 
 	struct pathlink_s
 	{
-		char __pad0[4];
+		char __pad0[0x4];
 		unsigned __int16 nodeNum;
-		char __pad[6];
+		char __pad[0x6];
 	};
 
 	static_assert(sizeof(pathlink_s) == 12);
@@ -99,12 +101,12 @@ namespace game
 		unsigned int target;
 		unsigned int animscript;
 		int animscriptfunc;
-		float vLocalOrigin[3];
-		char __pad0[28];
+		float vLocalOrigin[0x3];
+		char __pad0[0x1C];
 		unsigned __int16 totalLinkCount;
-		char __pad1[2];
+		char __pad1[0x2];
 		pathlink_s* Links;
-		char __pad2[104];
+		char __pad2[0x68];
 	}; // size = 192
 
 	static_assert(sizeof(pathnode_t) == 192);

From 526a477a59c32bb0d7185dd788e308b2f2454672 Mon Sep 17 00:00:00 2001
From: fed <58637860+fedddddd@users.noreply.github.com>
Date: Sat, 8 Jan 2022 20:22:59 +0100
Subject: [PATCH 4/5] More trigger stuff

---
 src/client/component/gui_debug.cpp | 154 +++++++++--------------------
 1 file changed, 46 insertions(+), 108 deletions(-)

diff --git a/src/client/component/gui_debug.cpp b/src/client/component/gui_debug.cpp
index a172100f..def9943a 100644
--- a/src/client/component/gui_debug.cpp
+++ b/src/client/component/gui_debug.cpp
@@ -38,28 +38,32 @@ namespace gui_debug
 		{
 			bool enabled;
 			bool camera_locked;
-			float camera[3] = {};
-			float range = 500.f;
-			float color[4] = {1.f, 0.f, 0.f, 1.f};
-			float mesh_thickness = 1.f;
-			float size = 10.f;
 			object_type type;
+			float range = 500.f;
+			float camera[3] = {};
 		};
 
 		struct : draw_settings
 		{
+			bool draw_node_links;
+			float size = 10.f;
+			float mesh_thickness = 1.f;
 			float link_thickness = 1.f;
-			bool draw_linked_nodes;
+			float color[4] = {1.f, 0.f, 0.f, 1.f};
 		} path_node_settings{};
 
+		struct : draw_settings
+		{
+			int point_count = 30;
+			float color[4] = {0.f, 1.f, 0.f, 0.5f};
+		} trigger_settings{};
+
 		struct point
 		{
 			ImVec2 point;
 			bool valid;
 		};
 
-		draw_settings trigger_settings{};
-
 		float vector_dot(float* a, float* b)
 		{
 			return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]);
@@ -248,82 +252,21 @@ namespace gui_debug
 			return pi;
 		}
 
-		void draw_circle(float* center, float radius, float* color, float thickness)
+		void draw_cylinder(float* center, float radius, float height, int point_count, float* color)
 		{
 			const auto pi = get_pi();
 
 			ImGuiWindow* window = ImGui::GetCurrentWindow();
-			
-			window->DrawList->PathClear();
-
-			for (auto angle = 0.f; angle < 360; angle += 1.f)
-			{
-				const auto x = center[0] + radius * cos(angle * pi / 180.f);
-				const auto y = center[1] + radius * sin(angle * pi / 180.f);
-
-				float point[3] = {x, y, center[2]};
-				float point_screen[2] = {};
-
-				if (!world_pos_to_screen_pos(point, point_screen))
-				{
-					return;
-				}
-
-				const auto point_ = ImVec2(point_screen[0], point_screen[1]);
-				window->DrawList->PathLineTo(point_);
-			}
-
-			const auto color_ = ImGui::GetColorU32({color[0], color[1], color[2], color[3]});
-			window->DrawList->PathStroke(color_, 0, thickness);
-		}
-
-		void draw_circle_filled(float* center, float radius, float* color)
-		{
-			const auto pi = get_pi();
-
-			ImGuiWindow* window = ImGui::GetCurrentWindow();
-			
-			window->DrawList->PathClear();
-
-			for (auto angle = 0; angle < 360; angle += 12)
-			{
-				const auto x = center[0] + radius * cos(static_cast<float>(angle) * pi / 180.f);
-				const auto y = center[1] + radius * sin(static_cast<float>(angle) * pi / 180.f);
-
-				float point[3] = {x, y, center[2]};
-				float point_screen[2] = {};
-
-				if (!world_pos_to_screen_pos(point, point_screen))
-				{
-					continue;
-				}
-
-				const auto point_ = ImVec2(point_screen[0], point_screen[1]);
-				window->DrawList->PathLineTo(point_);
-			}
-
-			const auto color_ = ImGui::GetColorU32({color[0], color[1], color[2], color[3]});
-			window->DrawList->PathFillConvex(color_);
-		}
-
-		void draw_cylinder(float* center, float radius, float height, float* color, float thickness)
-		{
-			const auto pi = get_pi();
-
-			ImGuiWindow* window = ImGui::GetCurrentWindow();
-
-			float top[3] = {center[0], center[1], center[2] + height};
-
 			const auto color_ = ImGui::GetColorU32({color[0], color[1], color[2], color[3]});
 
-			const auto max_points = 30;
-			const auto step = 360 / max_points;
+			point_count = std::max(0, std::min(point_count, 360));
+			const auto step = 360.f / point_count;
 
-			point points_top[max_points];
-			point points_bottom[max_points];
+			point points_top[360];
+			point points_bottom[360];
 
 			auto point_index = 0;
-			for (auto angle = 0; angle < 360; angle += step)
+			for (auto angle = 0.f; angle < 360.f; angle += step)
 			{
 				const auto x = center[0] + radius * cos(static_cast<float>(angle) * pi / 180.f);
 				const auto y = center[1] + radius * sin(static_cast<float>(angle) * pi / 180.f);
@@ -352,7 +295,7 @@ namespace gui_debug
 			}
 
 			window->DrawList->PathClear();
-			for (auto i = 0; i < max_points; i++)
+			for (auto i = 0; i < point_count; i++)
 			{
 				if (!points_bottom[i].valid)
 				{
@@ -364,7 +307,7 @@ namespace gui_debug
 			window->DrawList->PathFillConvex(color_);
 
 			window->DrawList->PathClear();
-			for (auto i = 0; i < max_points; i++)
+			for (auto i = 0; i < point_count; i++)
 			{
 				if (!points_top[i].valid)
 				{
@@ -375,7 +318,7 @@ namespace gui_debug
 			}
 			window->DrawList->PathFillConvex(color_);
 
-			for (auto i = 0; i < max_points; i++)
+			for (auto i = 0; i < point_count; i++)
 			{
 				window->DrawList->PathClear();
 
@@ -387,7 +330,7 @@ namespace gui_debug
 
 				window->DrawList->PathLineTo(points_bottom[i].point);
 
-				if (i == max_points - 1)
+				if (i == point_count - 1)
 				{
 					if (!points_bottom[0].valid || !points_top[0].valid)
 					{
@@ -409,30 +352,10 @@ namespace gui_debug
 				}
 
 				window->DrawList->PathLineTo(points_top[i].point);
-
 				window->DrawList->PathFillConvex(color_);
 			}
 		}
 
-		void draw_object(float* origin, object_type type)
-		{
-			switch (type)
-			{
-			case object_type::square:
-				draw_square(origin, path_node_settings.size, path_node_settings.color);
-				break;
-			case object_type::circle:
-				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);
-				break;
-			}
-		}
-
 		void draw_window()
 		{
 			if (!gui::enabled_menus["debug"])
@@ -447,7 +370,7 @@ namespace gui_debug
 				ImGui::Checkbox("Draw", &path_node_settings.enabled);
 				ImGui::Checkbox("Lock camera", &path_node_settings.camera_locked);
 
-				ImGui::Checkbox("Draw linked nodes", &path_node_settings.draw_linked_nodes);
+				ImGui::Checkbox("Draw node links", &path_node_settings.draw_node_links);
 
 				if (ImGui::TreeNode("Object type"))
 				{
@@ -461,7 +384,7 @@ namespace gui_debug
 				ImGui::SliderFloat("range", &path_node_settings.range, 0.f, 10000.f);
 				ImGui::SliderFloat("size", &path_node_settings.size, 5.f, 100.f);
 
-				if (path_node_settings.draw_linked_nodes)
+				if (path_node_settings.draw_node_links)
 				{
 					ImGui::SliderFloat("link thickness", &path_node_settings.link_thickness, 1.f, 20.f);
 				}
@@ -486,7 +409,7 @@ namespace gui_debug
 				ImGui::Checkbox("Lock camera", &trigger_settings.camera_locked);
 
 				ImGui::SliderFloat("range", &trigger_settings.range, 0.f, 10000.f);
-				ImGui::SliderFloat("mesh thickness", &trigger_settings.mesh_thickness, 1.f, 20.f);
+				ImGui::SliderInt("circle max points", &trigger_settings.point_count, 3, 360);
 
 				if (ImGui::TreeNode("Color picker"))
 				{
@@ -514,7 +437,7 @@ namespace gui_debug
 			game::PathNode_WorldifyPosFromParent(node, out);
 		}
 
-		void draw_linked_nodes(game::pathnode_t* node, float* origin)
+		void draw_node_links(game::pathnode_t* node, float* origin)
 		{
 			for (unsigned int i = 0; i < node->totalLinkCount; i++)
 			{
@@ -572,10 +495,23 @@ namespace gui_debug
 					continue;
 				}
 
-				draw_object(origin, path_node_settings.type);
-				if (path_node_settings.draw_linked_nodes)
+				switch (path_node_settings.type)
 				{
-					draw_linked_nodes(node, origin);
+				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);
+					break;
+				}
+
+				if (path_node_settings.draw_node_links)
+				{
+					draw_node_links(node, origin);
 				}
 			}
 		}
@@ -598,12 +534,14 @@ namespace gui_debug
 
 				const auto distance = distance_2d(trigger_settings.camera, origin);
 
-				if (distance - radius > trigger_settings.range || entity->script_classname != trigger_radius)
+				if (distance - radius * 2 > trigger_settings.range || 
+					entity->script_classname != trigger_radius)
 				{
 					continue;
 				}
 
-				draw_cylinder(origin, radius, height, trigger_settings.color, trigger_settings.mesh_thickness);
+				draw_cylinder(origin, radius, height, trigger_settings.point_count, 
+					trigger_settings.color);
 			}
 		}
 

From f1f84b705db71f61ce54f4b254c706931420667e Mon Sep 17 00:00:00 2001
From: Federico Cecchetto <fedecek3@gmail.com>
Date: Sun, 9 Jan 2022 02:59:53 +0100
Subject: [PATCH 5/5] More trigger stuff

---
 src/client/component/gui_debug.cpp | 265 +++++++++++++++++++++--------
 src/client/game/structs.hpp        |  25 +--
 src/client/game/symbols.hpp        |   1 +
 3 files changed, 204 insertions(+), 87 deletions(-)

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<int> g_poolSize{0xBF2E40};
 
 	WEAK symbol<gentity_s> g_entities{0x52DDDA0};
+	WEAK symbol<int> num_entities{0x55CC738};
 	WEAK symbol<PathData> pathData{0x52CCDA0};
 
 	WEAK symbol<DWORD> threadIds{0xB11DC80};