Add g_connectPaths
This commit is contained in:
parent
aeb7477ef7
commit
c7f6864aea
@ -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);
|
||||||
|
554
src/client/component/pathnodes.cpp
Normal file
554
src/client/component/pathnodes.cpp
Normal 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)
|
@ -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,
|
||||||
|
@ -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};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user