local ffi = require "ffi";
ffi.cdef([[
typedef unsigned char byte;
typedef struct{ float x; float y; float z; } Vector;
typedef void*(__thiscall* get_client_entity_t)(void*, int);
typedef struct
{
void* fnHandle; //0x0000
char szName[260]; //0x0004
int nLoadFlags; //0x0108
int nServerCount; //0x010C
int type; //0x0110
int flags; //0x0114
Vector vecMins; //0x0118
Vector vecMaxs; //0x0124
float radius; //0x0130
char pad[28]; //0x0134
} model_t;
typedef struct
{
int m_bone; // 0x0000
int m_group; // 0x0004
Vector m_mins; // 0x0008
Vector m_maxs; // 0x0014
int m_name_id; // 0x0020
Vector m_angle; // 0x0024
float m_radius; // 0x0030
int pad2[4];
} mstudiobbox_t;
typedef struct
{
int sznameindex;
int numhitboxes;
int hitboxindex;
} mstudiohitboxset_t;
typedef struct
{
void* fnHandle; //0x0000
char szName[260]; //0x0004
int nLoadFlags; //0x0108
int nServerCount; //0x010C
int type; //0x0110
int flags; //0x0114
Vector vecMins; //0x0118
Vector vecMaxs; //0x0124
float radius; //0x0130
char pad[28]; //0x0134
} model_t;
typedef struct
{
int id; //0x0000
int version; //0x0004
long checksum; //0x0008
char szName[64]; //0x000C
int length; //0x004C
Vector vecEyePos; //0x0050
Vector vecIllumPos; //0x005C
Vector vecHullMin; //0x0068
Vector vecHullMax; //0x0074
Vector vecBBMin; //0x0080
Vector vecBBMax; //0x008C
int pad[5];
int numhitboxsets; //0x00AC
int hitboxsetindex; //0x00B0
} studiohdr_t;
typedef struct
{
float m_flMatVal[3][4];
} matrix3x4_t;
typedef struct
{
matrix3x4_t test[128];
} matrix3x4_t2;
typedef struct
{
unsigned memory;
char pad[8];
unsigned int count;
unsigned pelements;
} CUtlVectorSimple;
]])
local utils = {}; do
utils.ffi_vmfunc = function(p, t, i)
local fn = ffi.cast(t, p[0][i])
return fn and function(...) return fn(p, ...) end or false
end
utils.this_call = function(call_function, parameters)
return function(...)
return call_function(parameters, ...)
end
end
local uintptr_t = ffi.typeof("uintptr_t**")
local entity_list_003 = ffi.cast(uintptr_t, se.create_interface("client.dll", "VClientEntityList003"))
utils.address = utils.this_call(ffi.cast("get_client_entity_t", entity_list_003[0][3]), entity_list_003)
end
local model_info = {}; do
local VModelInfoClient004 = ffi.cast(ffi.typeof("void***"), se.create_interface("engine.dll", "VModelInfoClient004"))
model_info.get_studio_model = utils.ffi_vmfunc(VModelInfoClient004, "studiohdr_t*(__thiscall*)(void*, model_t*)", 32)
end
local debug_overlay = {}; do
local VDebugOverlay004 = ffi.cast("void***", se.create_interface("engine.dll", "VDebugOverlay004"))
debug_overlay.add_box_overlay = utils.ffi_vmfunc(VDebugOverlay004, "void(__thiscall*)(void*, const Vector&, const Vector&, const Vector&, Vector const&, int, int, int, int, float)", 1)
debug_overlay.add_capsule_overlay = utils.ffi_vmfunc(VDebugOverlay004, "void(__thiscall*)(void*, Vector&, Vector&, float&, int, int, int, int, float, int, int)", 23)
end
vec3_t.dot = function(a, b)
return a.x * b.x + a.y * b.y + a.z * b.z
end
local VectorTransform = function(in1, in2)
return ffi.new("Vector", {
vec3_t.dot(in1, vec3_t.new(in2[0][0], in2[0][1], in2[0][2])) + in2[0][3],
vec3_t.dot(in1, vec3_t.new(in2[1][0], in2[1][1], in2[1][2])) + in2[1][3],
vec3_t.dot(in1, vec3_t.new(in2[2][0], in2[2][1], in2[2][2])) + in2[2][3]
})
end
local pHitboxSet = function(i, stdmdl)
if i < 0 or i > stdmdl.numhitboxsets then return nil end
return ffi.cast("mstudiohitboxset_t*", ffi.cast("byte*", stdmdl) + stdmdl.hitboxsetindex) + i
end
local pHitbox = function(i, stdmdl)
if i > stdmdl.numhitboxes then return nil end
return ffi.cast("mstudiobbox_t*", ffi.cast("byte*", stdmdl) + stdmdl.hitboxindex) + i
end
local m_nHitboxSet = se.get_netvar("DT_BaseAnimating", "m_nHitboxSet")
local draw_matrix = function(idx, color, duration)
if not engine.is_connected() or not engine.is_in_game() or not idx then return end
local ent = utils.address(idx)
local ent_long = ffi.cast("unsigned long", ent)
local r, g, b, a = color.red * 255, color.green * 255, color.blue * 255, color.alpha * 255
local client_renderable = ffi.cast(ffi.typeof("void***"), ent_long + 0x4)
local get_model = ffi.cast(ffi.typeof("model_t*(__thiscall*)(void*)"), client_renderable[0][8])
local matrix = ffi.cast("matrix3x4_t2*", ffi.cast("CUtlVectorSimple*", ent_long + 0x26A8d).memory)
if not matrix then print("matrix error") return end
local model = get_model(client_renderable)
if not model then print("model error") return end
local hdr = model_info.get_studio_model(model)
if not hdr then print("hdr error") return end
local set = pHitboxSet(entitylist.get_entity_by_index(idx):get_prop_int(m_nHitboxSet), hdr)
if not set then print("set error") return end
for i = 0, set.numhitboxes - 1 do
local bbox = pHitbox(i, set)
if not bbox then print("pHitbox error") goto continue end
if bbox.m_radius > -1 then
local mins = VectorTransform(bbox.m_mins, matrix[0].test[bbox.m_bone].m_flMatVal)
local maxs = VectorTransform(bbox.m_maxs, matrix[0].test[bbox.m_bone].m_flMatVal)
debug_overlay.add_capsule_overlay(mins, maxs, ffi.new("float[1]", bbox.m_radius), r, g, b, a, duration, 1 , 1)
end
::continue::
end
end
local menu = {}; do
menu.color = ui.add_color_edit("Matrix color", "menu.color", true, color_t.new(255, 255, 255, 255))
menu.duration = ui.add_slider_int("Matrix duration", "memu.duration", 1, 10, 3)
end
client.register_callback("shot_fired", function(e) ---@param e shot_info_t
if e.manual then return end
draw_matrix(e.target_index, menu.color:get_value(), menu.duration:get_value())
end)