Add g_connectPaths

This commit is contained in:
fed 2023-02-15 18:12:50 +01:00
parent aeb7477ef7
commit c7f6864aea
4 changed files with 814 additions and 30 deletions

View File

@ -505,19 +505,19 @@ namespace gui::debug
void get_pathnode_origin(game::pathnode_t* node, float* out) void get_pathnode_origin(game::pathnode_t* node, float* out)
{ {
out[0] = node->vLocalOrigin[0]; out[0] = node->constant.vLocalOrigin[0];
out[1] = node->vLocalOrigin[1]; out[1] = node->constant.vLocalOrigin[1];
out[2] = node->vLocalOrigin[2]; out[2] = node->constant.vLocalOrigin[2];
game::PathNode_WorldifyPosFromParent(node, out); game::PathNode_WorldifyPosFromParent(node, out);
} }
void draw_node_links(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++) for (unsigned int i = 0; i < node->constant.totalLinkCount; i++)
{ {
float linked_origin[3] = {}; float linked_origin[3] = {};
const auto num = node->Links[i].nodeNum; const auto num = node->constant.Links[i].nodeNum;
const auto linked = &game::pathData->nodes[num]; const auto linked = &game::pathData->nodes[num];
get_pathnode_origin(linked, linked_origin); get_pathnode_origin(linked, linked_origin);

View File

@ -0,0 +1,554 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "scheduler.hpp"
#include "command.hpp"
#include "console.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include <utils/hook.hpp>
#include <utils/memory.hpp>
#include <utils/string.hpp>
namespace pathnodes
{
namespace
{
game::dvar_t* g_connect_paths;
utils::hook::detour path_init_paths_hook;
utils::memory::allocator allocator;
game::pathnode_tree_t* allocate_tree()
{
++game::pathData->nodeTreeCount;
return reinterpret_cast<game::pathnode_tree_t*>(
game::Hunk_AllocAlignInternal(sizeof(game::pathnode_tree_t), 4));
}
game::pathnode_tree_t* build_node_tree(unsigned short* node_indexes, unsigned int num_nodes)
{
if (num_nodes < 4)
{
const auto result = allocate_tree();
result->axis = -1;
result->u.s.nodeCount = num_nodes;
result->u.s.nodes = node_indexes;
return result;
}
game::vec2_t maxs{};
game::vec2_t mins{};
const auto start_node = &game::pathData->nodes[*node_indexes];
maxs[0] = start_node->constant.vLocalOrigin[0];
mins[0] = maxs[0];
maxs[1] = start_node->constant.vLocalOrigin[1];
mins[1] = maxs[1];
for (auto i = 1u; i < num_nodes; i++)
{
for (auto axis = 0; axis < 2; axis++)
{
const auto node = &game::pathData->nodes[node_indexes[i]];
const auto value = node->constant.vLocalOrigin[axis];
if (mins[axis] <= value)
{
if (value > maxs[axis])
{
maxs[axis] = value;
}
}
else
{
mins[axis] = value;
}
}
}
const auto axis = (maxs[1] - mins[1]) > (maxs[0] - mins[0]);
if ((maxs[axis] - mins[axis]) > 192.f)
{
const auto dist = (maxs[axis] + mins[axis]) * 0.5f;
auto left = 0u;
for (auto right = num_nodes - 1; ; --right)
{
while (dist > game::pathData->nodes[node_indexes[left]].constant.vLocalOrigin[axis])
{
++left;
}
while (game::pathData->nodes[node_indexes[right]].constant.vLocalOrigin[axis] > dist)
{
--right;
}
if (left >= right)
{
break;
}
const auto swap_node = node_indexes[left];
node_indexes[left] = node_indexes[right];
node_indexes[right] = swap_node;
++left;
}
while (2 * left < num_nodes &&
game::pathData->nodes[node_indexes[left]].constant.vLocalOrigin[axis] == dist)
{
++left;
}
while (2 * left < num_nodes &&
game::pathData->nodes[node_indexes[left - 1]].constant.vLocalOrigin[axis] == dist)
{
--left;
}
game::pathnode_tree_t* child[2]{};
child[0] = build_node_tree(node_indexes, left);
child[1] = build_node_tree(&node_indexes[left], num_nodes - left);
const auto result = allocate_tree();
result->axis = axis;
result->dist = dist;
result->u.child[0] = child[0];
result->u.child[1] = child[1];
return result;
}
else
{
const auto result = allocate_tree();
result->axis = -1;
result->u.s.nodeCount = num_nodes;
result->u.s.nodes = node_indexes;
return result;
}
}
bool is_negotation_link(game::pathnode_t* from, game::pathnode_t* to)
{
return from->constant.type == game::NODE_NEGOTIATION_BEGIN &&
to->constant.type == game::NODE_NEGOTIATION_END &&
from->constant.target == to->constant.targetname;
}
float vec2_normalize(float* v)
{
const auto len = std::sqrt(v[0] * v[0] + v[1] * v[1]);
v[0] /= len;
v[1] /= len;
return len;
}
float vec2_length(const float* v)
{
return std::sqrt(v[0] * v[0] + v[1] * v[1]);
}
float vec3_normalize(float* v)
{
const auto len = std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
v[0] /= len;
v[1] /= len;
v[2] /= len;
return len;
}
void vector_scale(float* v, const int size, const float scale)
{
for (auto i = 0; i < size; i++)
{
v[i] *= scale;
}
}
bool is_deflection_ok(const float* start, const float* origin, const float* move_dir)
{
game::vec2_t deflection{};
deflection[0] = origin[0] - start[0];
deflection[1] = origin[1] - start[1];
const auto value = ((deflection[0] * move_dir[0]) + (deflection[1] * move_dir[1])) * -1.f;
const auto d = (
((value * move_dir[0]) + deflection[0]) * ((value * move_dir[0]) + deflection[0]) +
((value * move_dir[1]) + deflection[1]) * ((value * move_dir[1]) + deflection[1])
);
return 0.3f > d;
}
void vector_copy(const float* a, float* b, const int size)
{
for (auto i = 0; i < size; i++)
{
b[i] = a[i];
}
}
bool vector_cmp(const float* a, const float* b, const int size)
{
for (auto i = 0; i < size; i++)
{
if (a[i] != b[i])
{
return false;
}
}
return true;
}
float distance_squared(const float* a, const float* b)
{
return ((a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1]));
}
void actor_physics(game::pmove_t* pm, game::pml_t* pml)
{
pml->previous_velocity[0] = pm->ps->velocity[0];
pml->previous_velocity[1] = pm->ps->velocity[1];
pml->previous_velocity[2] = pm->ps->velocity[2];
pml->previous_origin[0] = pm->ps->origin[0];
pml->previous_origin[1] = pm->ps->origin[1];
pml->previous_origin[2] = pm->ps->origin[2];
pml->groundTrace.hitId = 3998;
game::PM_GroundTrace(pm, pml);
if (pml->walking)
{
game::PM_WalkMove(pm, pml);
}
else
{
game::PM_AirMove(pm, pml);
}
}
void vector_cross(const float* a, const float* b, float* out)
{
out[0] = (a[1] * b[2]) - (a[2] * b[1]);
out[1] = (a[0] * b[2]) - (a[2] * b[0]);
out[2] = (a[0] * b[1]) - (a[1] * b[0]);
}
bool phys_trace_passed(const float* from, const float* to, float* dist)
{
game::pml_t pml{};
game::pmove_t pm{};
pm.tracemask = 0x281C011;
pm.bounds = *reinterpret_cast<game::Bounds*>(0x140984950);
pm.ps = reinterpret_cast<game::playerState_s*>(&game::g_entities[0].client);
pm.ps->origin[0] = from[0];
pm.ps->origin[1] = from[1];
pm.ps->origin[2] = from[2];
pm.ps->gravity = 800;
pm.ps->velocity[0] = 0.f;
pm.ps->velocity[1] = 0.f;
pm.ps->velocity[2] = 0.f;
pml.previous_origin[0] = from[0];
pml.previous_origin[1] = from[1];
pml.previous_origin[2] = from[2];
pml.msec = 50;
pml.frametime = static_cast<float>(pml.msec) * 0.001f;
game::vec3_t move_dir{};
move_dir[0] = to[0] - from[0];
move_dir[1] = to[1] - from[1];
game::Vec2Normalize(move_dir);
pml.forward[0] = move_dir[0];
pml.forward[1] = move_dir[1];
pml.forward[2] = move_dir[2];
pml.up[0] = 0.f;
pml.up[1] = 0.f;
pml.up[2] = 1.f;
vector_cross(pml.forward, pml.up, pml.right);
pm.cmd.forwardmove = 127;
pm.cmd.rightmove = 0;
pm.cmd.unk_float = 1.f;
auto dist_squared = 100000.f;
auto last_ground_plane_altitude = -3.4028235e38f;
for (auto i = 0; i < 96; i++)
{
dist_squared = (
((to[0] - pm.ps->origin[0]) * (to[0] - pm.ps->origin[0])) +
((to[1] - pm.ps->origin[1]) * (to[1] - pm.ps->origin[1]))
);
if (dist_squared <= 16.f)
{
break;
}
game::vec3_t start{};
vector_copy(pm.ps->origin, start, 3);
actor_physics(&pm, &pml);
if (vector_cmp(start, pm.ps->origin, 3) ||
distance_squared(pm.ps->origin, to) > 65536.f)
{
return false;
}
const auto has_ground_plane = pml.groundPlane && pml.groundTrace.normal[2] >= 0.3f;
if (has_ground_plane)
{
last_ground_plane_altitude = pm.ps->origin[2];
}
if ((last_ground_plane_altitude - pm.ps->origin[2]) > 32.f)
{
return false;
}
if (pml.groundTrace.hitId != 3998 && !is_deflection_ok(from, pm.ps->origin, move_dir))
{
return false;
}
}
if ((last_ground_plane_altitude - to[2]) > 32.f)
{
return false;
}
*dist = std::sqrtf(dist_squared);
return dist_squared <= 16.f && std::abs(pm.ps->origin[2] - to[2]) <= 18.f;
}
bool can_link_nodes(game::pathnode_t* from, game::pathnode_t* to, float* dist, bool* negotiation_link)
{
if (is_negotation_link(from, to))
{
*negotiation_link = true;
*dist = 15.f;
return true;
}
else
{
game::vec3_t delta{};
delta[0] = to->constant.vLocalOrigin[0] - from->constant.vLocalOrigin[0];
delta[1] = to->constant.vLocalOrigin[1] - from->constant.vLocalOrigin[1];
delta[2] = to->constant.vLocalOrigin[2] - from->constant.vLocalOrigin[2];
if (std::abs(delta[2]) > 128.f)
{
return false;
}
if ((delta[0] * delta[0] + delta[1] * delta[1]) > 65536.f)
{
return false;
}
game::vec2_t move_dir{};
move_dir[0] = to->constant.vLocalOrigin[0] - from->constant.vLocalOrigin[0];
move_dir[1] = to->constant.vLocalOrigin[1] - from->constant.vLocalOrigin[1];
*dist = game::Vec2Normalize(move_dir);
*negotiation_link = false;
return phys_trace_passed(from->constant.vLocalOrigin, to->constant.vLocalOrigin, dist);
}
}
bool try_link_nodes(game::pathnode_t* from, game::pathnode_t* to,
game::pathlink_s* links, int max_links)
{
float dist{};
bool negotiation_link{};
if (max_links <= 0)
{
console::error("[Connect paths] Out of available links, increase link buffer size\n");
return false;
}
if (!can_link_nodes(from, to, &dist, &negotiation_link))
{
return false;
}
const auto link = &links[from->constant.totalLinkCount++];
link->nodeNum = static_cast<unsigned short>(to - game::pathData->nodes);
link->fDist = dist;
link->disconnectCount = 0;
link->negotiationLink = negotiation_link;
return true;
}
void link_pathnodes()
{
constexpr auto max_links = 0x80000;
const auto links_buffer = allocator.allocate_array<game::pathlink_s>(max_links);
auto total_link_count = 0;
for (auto i = 0u; i < game::pathData->nodeCount; i++)
{
const auto node = &game::pathData->nodes[i];
if ((node->constant.spawnflags & 1) || !node->constant.type)
{
continue;
}
for (auto o = 0u; o < game::pathData->nodeCount; o++)
{
const auto other = &game::pathData->nodes[o];
if (o == i || (other->constant.spawnflags & 1) || !other->constant.type)
{
continue;
}
try_link_nodes(node, other, &links_buffer[total_link_count], max_links - total_link_count);
}
total_link_count += node->constant.totalLinkCount;
if (node->constant.totalLinkCount == 0)
{
console::info("[Connect paths] Pathnode at (%f %f %f) has no links\n",
node->constant.vLocalOrigin[0],
node->constant.vLocalOrigin[1],
node->constant.vLocalOrigin[2]
);
}
}
console::info("[Connect paths] Total links: %i\n", total_link_count);
auto accounted_links = 0;
for (auto i = 0u; i < game::pathData->nodeCount; i++)
{
if (game::pathData->nodes[i].constant.totalLinkCount)
{
game::pathData->nodes[i].constant.Links = &links_buffer[accounted_links];
accounted_links += game::pathData->nodes[i].constant.totalLinkCount;
}
}
}
float distance(float* a, float* b)
{
return std::sqrtf((a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1]));
}
void connect_paths()
{
console::info("[Connect paths] Node count: %i\n", game::pathData->nodeCount);
auto original_link_count = 0;
for (auto i = 0u; i < game::pathData->nodeCount; i++)
{
original_link_count += game::pathData->nodes[i].constant.totalLinkCount;
}
console::info("[Connect paths] Original link count: %i\n", original_link_count);
for (auto i = 0u; i < game::pathData->nodeCount; i++)
{
if (game::pathData->nodes[i].constant.Links != nullptr)
{
console::warn("[Connect paths] Path nodes already linked\n");
return;
}
}
game::pathData->dynamicNodeGroupCount = 0;
game::pathData->visBytes = 0;
link_pathnodes();
const auto node_indexes = allocator.allocate_array<unsigned short>(game::pathData->nodeCount);
for (auto i = 0u; i < game::pathData->nodeCount; i++)
{
node_indexes[i] = static_cast<unsigned short>(i);
}
console::info("[Connect paths] Building pathnode trees...\n");
game::pathData->nodeTreeCount = 0;
game::pathData->nodeTree = build_node_tree(node_indexes, game::pathData->nodeCount);
console::info("[Connect paths] Total trees: %i\n", game::pathData->nodeTreeCount);
}
float pm_cmd_scale_walk_stub(void*, void*, void*)
{
return 1.f;
}
char patch_bytes[2][5]{};
void patch_functions()
{
std::memcpy(&patch_bytes[0], reinterpret_cast<void*>(0x1406887D0), 5);
std::memcpy(&patch_bytes[1], reinterpret_cast<void*>(0x1403C8414), 5);
utils::hook::jump(0x1406887D0, pm_cmd_scale_walk_stub);
utils::hook::nop(0x1403C8414, 5);
}
void restore_code(const size_t ptr, char* data, const size_t size)
{
const auto ptr_ = reinterpret_cast<char*>(ptr);
DWORD old_protect;
VirtualProtect(ptr_, size, PAGE_EXECUTE_READWRITE, &old_protect);
for (auto i = 0; i < size; i++)
{
ptr_[i] = data[i];
}
VirtualProtect(ptr_, size, old_protect, &old_protect);
FlushInstructionCache(GetCurrentProcess(), ptr_, size);
}
void restore_functions()
{
restore_code(0x1406887D0, patch_bytes[0], 5);
restore_code(0x1403C8414, patch_bytes[1], 5);
}
void path_init_paths_stub(void* a1, void* a2, void* a3, void* a4)
{
path_init_paths_hook.invoke<void>(a1, a2, a3, a4);
if (g_connect_paths->current.enabled)
{
patch_functions();
const auto _0 = gsl::finally(restore_functions);
connect_paths();
}
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
g_connect_paths = dvars::register_bool("g_connectPaths", false, 0, "Connect paths");
path_init_paths_hook.create(0x140522250, path_init_paths_stub);
}
};
}
REGISTER_COMPONENT(pathnodes::component)

View File

@ -103,41 +103,180 @@ namespace game
static_assert(offsetof(gentity_s, flags) == 364); static_assert(offsetof(gentity_s, flags) == 364);
static_assert(offsetof(gentity_s, s) == 140); static_assert(offsetof(gentity_s, s) == 140);
struct pathlink_s struct pathnode_yaworient_t
{ {
char __pad0[0x4]; float fLocalAngle;
unsigned __int16 nodeNum; float localForward[2];
char __pad[0x6];
}; };
static_assert(sizeof(pathlink_s) == 12); union $3936EE84564F75EDA6DCBAC77A545FC8
{
pathnode_yaworient_t yaw_orient;
float angles[3];
};
union PathNodeParentUnion
{
scr_string_t name;
unsigned short index;
};
enum nodeType
{
NODE_ERROR = 0x0,
NODE_PATHNODE = 0x1,
NODE_NEGOTIATION_BEGIN = 0x13,
NODE_NEGOTIATION_END = 0x14
};
enum PathNodeErrorCode : std::int32_t
{
PNERR_NONE = 0x0,
PNERR_INSOLID = 0x1,
PNERR_FLOATING = 0x2,
PNERR_NOLINK = 0x3,
PNERR_DUPLICATE = 0x4,
PNERR_NOSTANCE = 0x5,
PNERR_INVALIDDOOR = 0x6,
PNERR_NOANGLES = 0x7,
PNERR_BADPLACEMENT = 0x8,
NUM_PATH_NODE_ERRORS = 0x9,
};
union $5F11B9753862CE791E23553F99FA1738
{
float minUseDistSq;
PathNodeErrorCode error;
};
struct pathlink_s
{
float fDist;
unsigned short nodeNum;
unsigned char disconnectCount;
unsigned char negotiationLink;
unsigned char flags;
unsigned char ubBadPlaceCount[3];
};
struct pathnode_constant_t
{
unsigned short type;
unsigned int spawnflags;
scr_string_t targetname;
scr_string_t script_linkName;
scr_string_t script_noteworthy;
scr_string_t target;
scr_string_t animscript;
int animscriptfunc;
float vLocalOrigin[3];
$3936EE84564F75EDA6DCBAC77A545FC8 ___u9;
PathNodeParentUnion parent;
$5F11B9753862CE791E23553F99FA1738 ___u11;
short wOverlapNode[2];
char __pad0[4];
unsigned short totalLinkCount;
pathlink_s* Links;
scr_string_t unk;
char __pad1[4];
};
struct SentientHandle
{
unsigned short number;
unsigned short infoIndex;
};
struct pathnode_dynamic_t
{
SentientHandle pOwner;
int iFreeTime;
int iValidTime[3];
short wLinkCount;
short wOverlapCount;
short turretEntNumber;
unsigned char userCount;
unsigned char hasBadPlaceLink;
int spreadUsedTime[2];
short flags;
short dangerousCount;
int recentUseProxTime;
};
union $73F238679C0419BE2C31C6559E8604FC
{
float nodeCost;
int linkIndex;
};
struct pathnode_t;
struct pathnode_transient_t
{
int iSearchFrame;
pathnode_t* pNextOpen;
pathnode_t* pPrevOpen;
pathnode_t* pParent;
float fCost;
float fHeuristic;
$73F238679C0419BE2C31C6559E8604FC ___u6;
};
struct pathnode_t struct pathnode_t
{ {
unsigned __int16 type; pathnode_constant_t constant;
unsigned int spawnflags; pathnode_dynamic_t dynamic;
unsigned int targetname; pathnode_transient_t transient;
unsigned int script_linkName; };
unsigned int script_noteworthy;
unsigned int target;
unsigned int animscript;
int animscriptfunc;
float vLocalOrigin[0x3];
char __pad0[0x1C];
unsigned __int16 totalLinkCount;
char __pad1[0x2];
pathlink_s* Links;
char __pad2[0x68];
}; // size = 192
static_assert(sizeof(pathnode_t) == 192); struct pathnode_tree_nodes_t
{
int nodeCount;
unsigned short* nodes;
};
struct pathnode_tree_t;
union pathnode_tree_info_t
{
pathnode_tree_t* child[2];
pathnode_tree_nodes_t s;
};
struct pathnode_tree_t
{
int axis;
float dist;
pathnode_tree_info_t u;
};
struct PathDynamicNodeGroup
{
unsigned short parentIndex;
int nodeTreeCount;
pathnode_tree_t* nodeTree;
};
struct PathData struct PathData
{ {
const char* name; const char* name;
unsigned int nodeCount; unsigned int nodeCount;
pathnode_t* nodes; pathnode_t* nodes;
// ... bool parentIndexResolved;
unsigned short version;
int visBytes;
unsigned char* pathVis;
int nodeTreeCount;
pathnode_tree_t* nodeTree;
int dynamicNodeGroupCount;
PathDynamicNodeGroup* dynamicNodeGroups;
int exposureBytes;
unsigned char* pathExposure;
int noPeekVisBytes;
unsigned char* pathNoPeekVis;
int zoneCount;
int zonesBytes;
unsigned char* pathZones;
int dynStatesBytes;
unsigned char* pathDynStates;
}; };
struct GfxImage; struct GfxImage;
@ -1293,17 +1432,97 @@ namespace game
const char* name; const char* name;
}; };
struct playerState_s
{
char __pad0[48];
unsigned short gravity;
char __pad1[34];
int pm_flags;
char __pad2[40];
vec3_t origin;
vec3_t velocity;
};
static_assert(offsetof(playerState_s, origin) == 128);
struct SprintState_s
{
int sprintButtonUpRequired;
int sprintDelay;
int lastSprintStart;
int lastSprintEnd;
int sprintStartMaxLength;
};
struct usercmd_s
{
int serverTime;
int buttons;
char __pad0[20];
char forwardmove;
char rightmove;
char __pad1[2];
float unk_float;
char __pad2[28];
};
struct pmove_t struct pmove_t
{ {
playerState_s* ps;
usercmd_s cmd;
usercmd_s oldcmd;
int tracemask;
int numtouch;
int touchents[32];
Bounds bounds;
char __pad0[28];
char handler;
char __pad1[0x180];
}; };
//static_assert(sizeof(pmove_t) == 328);
static_assert(offsetof(pmove_t, handler) == 324);
static_assert(offsetof(pmove_t, bounds) == 272);
static_assert(offsetof(pmove_t, tracemask) == 136);
struct trace_t struct trace_t
{ {
char __pad0[0x29]; float fraction;
bool allsolid; // Confirmed in CM_PositionTestCapsuleInTriangle float normal[3];
bool startsolid; // Confirmed in PM_JitterPoint int surfaceFlags;
int contents;
int hitType;
unsigned short hitId;
char __pad1[11];
bool allsolid;
bool startsolid;
bool walkable;
char __pad0[8];
}; };
struct pml_t
{
float forward[3];
float right[3];
float up[3];
float frametime;
int msec;
int walking;
int groundPlane;
trace_t groundTrace;
float previous_origin[3];
float previous_velocity[3];
float wishdir[3];
float platformUp[3];
float impactSpeed;
int flinch;
};
static_assert(offsetof(pml_t, frametime) == 36);
static_assert(offsetof(pml_t, groundTrace) == 52);
static_assert(offsetof(pml_t, flinch) == 156);
enum Sys_Folder : std::int32_t enum Sys_Folder : std::int32_t
{ {
SF_ZONE = 0x0, SF_ZONE = 0x0,

View File

@ -92,9 +92,14 @@ namespace game
G_GivePlayerWeapon{0x14051B660}; G_GivePlayerWeapon{0x14051B660};
WEAK symbol<void(void* ps, const unsigned int weapon, int hadWeapon)> G_InitializeAmmo{0x1404C4110}; WEAK symbol<void(void* ps, const unsigned int weapon, int hadWeapon)> G_InitializeAmmo{0x1404C4110};
WEAK symbol<void(int localClientNum, const unsigned int weapon)> G_SelectWeapon{0x14051C0D0}; WEAK symbol<void(int localClientNum, const unsigned int weapon)> G_SelectWeapon{0x14051C0D0};
WEAK symbol<void(trace_t* results, const float* start, const float* end,
const Bounds* bounds, int passEntityNum, int contentmask)> G_TraceCapsule{0x1404CBFE0};
WEAK symbol<void(int* hitNum, const float* start, const float* end,
int passEntityNum, int passEntityNum1, int contentmask)> G_SightTrace{0x1404CBCA0};
WEAK symbol<bool(int localClientNum, ScreenPlacement* screenPlacement, const float* worldDir, float* outScreenPos)> WorldPosToScreenPos{0x14036F310}; WEAK symbol<bool(int localClientNum, ScreenPlacement* screenPlacement, const float* worldDir, float* outScreenPos)> WorldPosToScreenPos{0x14036F310};
WEAK symbol<char*(const size_t size)> Hunk_AllocateTempMemoryHigh{0x140614790}; WEAK symbol<char*(const size_t size)> Hunk_AllocateTempMemoryHigh{0x140614790};
WEAK symbol<char*(const size_t size, const size_t alignment)> Hunk_AllocAlignInternal{0x140613D80};
WEAK symbol<char*(char* string)> I_CleanStr{0x140620660}; WEAK symbol<char*(char* string)> I_CleanStr{0x140620660};
@ -199,6 +204,12 @@ namespace game
WEAK symbol<void(pmove_t*, trace_t*, const float*, const float*, WEAK symbol<void(pmove_t*, trace_t*, const float*, const float*,
const Bounds*, int, int)> PM_trace{0x14068F1D0}; const Bounds*, int, int)> PM_trace{0x14068F1D0};
WEAK symbol<void(pmove_t*, pml_t*)> PM_WalkMove{0x14068EBB0};
WEAK symbol<void(pmove_t*, pml_t*)> PM_AirMove{0x140686BF0};
WEAK symbol<void(pmove_t*, pml_t*)> PM_GroundTrace{0x140689AA0};
WEAK symbol<float(float*)> Vec2Normalize{0x140611D80};
WEAK symbol<void*(jmp_buf* Buf, int Value)> longjmp{0x14089EED0}; WEAK symbol<void*(jmp_buf* Buf, int Value)> longjmp{0x14089EED0};
WEAK symbol<int(jmp_buf* Buf)> _setjmp{0x1408EC2E0}; WEAK symbol<int(jmp_buf* Buf)> _setjmp{0x1408EC2E0};