Walkbot V2
This commit is contained in:
parent
5a20a42937
commit
4af2be581f
@ -14,9 +14,18 @@
|
|||||||
namespace hacks { namespace shared { namespace walkbot {
|
namespace hacks { namespace shared { namespace walkbot {
|
||||||
|
|
||||||
using index_t = unsigned;
|
using index_t = unsigned;
|
||||||
|
using connection = uint8_t;
|
||||||
|
|
||||||
constexpr index_t INVALID_NODE = unsigned(-1);
|
constexpr unsigned VERSION = 2;
|
||||||
constexpr size_t MAX_CONNECTIONS = 4;
|
constexpr index_t BAD_NODE = unsigned(-1);
|
||||||
|
constexpr connection MAX_CONNECTIONS = 6;
|
||||||
|
constexpr connection BAD_CONNECTION = uint8_t(-1);
|
||||||
|
|
||||||
|
index_t CreateNode(const Vector& xyz);
|
||||||
|
void DeleteNode(index_t node);
|
||||||
|
float distance_2d(Vector& xyz);
|
||||||
|
void Save(std::string filename);
|
||||||
|
void Load(std::string filename);
|
||||||
|
|
||||||
enum ENodeFlags {
|
enum ENodeFlags {
|
||||||
NF_GOOD = (1 << 0),
|
NF_GOOD = (1 << 0),
|
||||||
@ -32,38 +41,70 @@ enum EWalkbotState {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct walkbot_header_s {
|
struct walkbot_header_s {
|
||||||
unsigned version { 1 };
|
unsigned version { VERSION };
|
||||||
unsigned node_count { 0 };
|
size_t node_count { 0 };
|
||||||
|
size_t map_length { 0 };
|
||||||
|
size_t author_length { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
enum EConnectionFlags {
|
||||||
|
CF_GOOD = (1 << 0),
|
||||||
|
CF_LOW_HEALTH = (1 << 1),
|
||||||
|
CF_LOW_AMMO = (1 << 2)
|
||||||
|
};
|
||||||
|
|
||||||
|
struct connection_s {
|
||||||
|
index_t node { BAD_NODE };
|
||||||
|
unsigned flags { 0 };
|
||||||
|
|
||||||
|
void link(index_t a) {
|
||||||
|
flags |= CF_GOOD;
|
||||||
|
node = a;
|
||||||
|
}
|
||||||
|
void unlink() {
|
||||||
|
flags = 0;
|
||||||
|
node = BAD_NODE;
|
||||||
|
}
|
||||||
|
bool good() const {
|
||||||
|
return (flags & CF_GOOD);
|
||||||
|
}
|
||||||
|
bool free() const {
|
||||||
|
return not good();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct walkbot_node_s {
|
struct walkbot_node_s {
|
||||||
struct {
|
float x { 0 };
|
||||||
float x { 0 }; // 4
|
float y { 0 };
|
||||||
float y { 0 }; // 8
|
float z { 0 };
|
||||||
float z { 0 }; // 12
|
unsigned flags { 0 };
|
||||||
};
|
connection_s connections[MAX_CONNECTIONS];
|
||||||
unsigned flags { 0 }; // 16
|
|
||||||
size_t connection_count { 0 }; // 20
|
|
||||||
index_t connections[MAX_CONNECTIONS]; // 36
|
|
||||||
index_t preferred { INVALID_NODE }; // 40
|
|
||||||
|
|
||||||
Vector& xyz() {
|
Vector& xyz() {
|
||||||
return *reinterpret_cast<Vector*>(&x);
|
return *reinterpret_cast<Vector*>(&x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
connection free_connection() const {
|
||||||
|
for (connection i = 0; i < MAX_CONNECTIONS; i++) {
|
||||||
|
if (connections[i].free())
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return BAD_CONNECTION;
|
||||||
|
}
|
||||||
|
|
||||||
void link(index_t node) {
|
void link(index_t node) {
|
||||||
if (connection_count == MAX_CONNECTIONS) {
|
connection free = free_connection();
|
||||||
|
if (free == BAD_CONNECTION) {
|
||||||
logging::Info("[wb] Too many connections! Node at (%.2f %.2f %.2f)", x, y, z);
|
logging::Info("[wb] Too many connections! Node at (%.2f %.2f %.2f)", x, y, z);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
connections[connection_count++] = node;
|
connections[free].link(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
void unlink(index_t node) {
|
void unlink(index_t node) {
|
||||||
for (size_t i = 0; i < connection_count; i++) {
|
for (connection i = 0; i < MAX_CONNECTIONS; i++) {
|
||||||
if (connections[i] == node) {
|
if (connections[i].good() and connections[i].node == node) {
|
||||||
connections[i] = connections[connection_count - 1];
|
connections[i].unlink();
|
||||||
connections[--connection_count] = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -77,17 +118,38 @@ float distance_2d(Vector& xyz) {
|
|||||||
|
|
||||||
namespace state {
|
namespace state {
|
||||||
|
|
||||||
|
index_t free_node();
|
||||||
|
|
||||||
// A vector containing all loaded nodes, used in both recording and replaying
|
// A vector containing all loaded nodes, used in both recording and replaying
|
||||||
std::vector<walkbot_node_s> nodes {};
|
std::vector<walkbot_node_s> nodes {};
|
||||||
|
|
||||||
|
bool node_good(index_t node) {
|
||||||
|
return node != BAD_NODE && node < nodes.size() && (nodes[node].flags & NF_GOOD);
|
||||||
|
}
|
||||||
|
|
||||||
// Target node when replaying, selected node when editing, last node when recording
|
// Target node when replaying, selected node when editing, last node when recording
|
||||||
index_t active_node { INVALID_NODE };
|
index_t active_node { BAD_NODE };
|
||||||
|
walkbot_node_s *active() {
|
||||||
|
if (node_good(active_node))
|
||||||
|
return active_node;
|
||||||
|
return BAD_NODE;
|
||||||
|
}
|
||||||
|
|
||||||
// Last reached node when replaying
|
// Last reached node when replaying
|
||||||
index_t last_node { INVALID_NODE };
|
index_t last_node { BAD_NODE };
|
||||||
|
walkbot_node_s *last() {
|
||||||
|
if (node_good(last_node))
|
||||||
|
return last_node;
|
||||||
|
return BAD_NODE;
|
||||||
|
}
|
||||||
|
|
||||||
// Node closest to your crosshair when editing
|
// Node closest to your crosshair when editing
|
||||||
index_t closest_node { INVALID_NODE };
|
index_t closest_node { BAD_NODE };
|
||||||
|
walkbot_node_s *closest() {
|
||||||
|
if (node_good(closest_node))
|
||||||
|
return closest_node;
|
||||||
|
return BAD_NODE;
|
||||||
|
}
|
||||||
|
|
||||||
// Global state
|
// Global state
|
||||||
EWalkbotState state { WB_DISABLED };
|
EWalkbotState state { WB_DISABLED };
|
||||||
@ -112,20 +174,19 @@ index_t free_node() {
|
|||||||
return nodes.size() - 1;
|
return nodes.size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool node_good(index_t node) {
|
|
||||||
return node != INVALID_NODE && node < nodes.size() && (nodes[node].flags & NF_GOOD);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
using state::nodes;
|
||||||
|
using state::node_good;
|
||||||
|
|
||||||
void DeleteNode(index_t node) {
|
void DeleteNode(index_t node) {
|
||||||
if (not state::node_good(node))
|
if (not node_good(node))
|
||||||
return;
|
return;
|
||||||
logging::Info("[wb] Deleting node %u", node);
|
logging::Info("[wb] Deleting node %u", node);
|
||||||
auto& n = state::nodes[node];
|
auto& n = nodes[node];
|
||||||
for (size_t i = 0; i < n.connection_count && i < MAX_CONNECTIONS; i++) {
|
for (size_t i = 0; i < MAX_CONNECTIONS; i++) {
|
||||||
if (state::node_good(n.connections[i])) {
|
if (n.connections[i].good() and node_good(n.connections[i].node)) {
|
||||||
state::nodes[n.connections[i]].unlink(state::closest_node);
|
nodes[n.connections[i].node].unlink(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
memset(&n, 0, sizeof(walkbot_node_s));
|
memset(&n, 0, sizeof(walkbot_node_s));
|
||||||
@ -135,6 +196,10 @@ void DeleteNode(index_t node) {
|
|||||||
#define BINARY_FILE_READ(handle, data) handle.read(reinterpret_cast<char*>(&data), sizeof(data))
|
#define BINARY_FILE_READ(handle, data) handle.read(reinterpret_cast<char*>(&data), sizeof(data))
|
||||||
|
|
||||||
void Save(std::string filename) {
|
void Save(std::string filename) {
|
||||||
|
if (g_Settings.bInvalid) {
|
||||||
|
logging::Info("Not in-game, cannot save!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
{
|
{
|
||||||
DIR* walkbot_dir = opendir("cathook/walkbot");
|
DIR* walkbot_dir = opendir("cathook/walkbot");
|
||||||
if (!walkbot_dir) {
|
if (!walkbot_dir) {
|
||||||
@ -159,7 +224,15 @@ void Save(std::string filename) {
|
|||||||
}
|
}
|
||||||
walkbot_header_s header;
|
walkbot_header_s header;
|
||||||
header.node_count = state::nodes.size();
|
header.node_count = state::nodes.size();
|
||||||
|
const char* name = g_ISteamFriends->GetPersonaName();
|
||||||
|
const char* map = g_IEngine->GetLevelName();
|
||||||
|
size_t name_s = strlen(name);
|
||||||
|
size_t map_s = strlen(map);
|
||||||
|
header.author_length = name_s;
|
||||||
|
header.map_length = map_s;
|
||||||
BINARY_FILE_WRITE(file, header);
|
BINARY_FILE_WRITE(file, header);
|
||||||
|
file.write(map, map_s);
|
||||||
|
file.write(name, name_s);
|
||||||
file.write(reinterpret_cast<const char*>(state::nodes.data()), sizeof(walkbot_node_s) * header.node_count);
|
file.write(reinterpret_cast<const char*>(state::nodes.data()), sizeof(walkbot_node_s) * header.node_count);
|
||||||
file.close();
|
file.close();
|
||||||
logging::Info("Writing successful");
|
logging::Info("Writing successful");
|
||||||
@ -189,13 +262,28 @@ void Load(std::string filename) {
|
|||||||
walkbot_header_s header;
|
walkbot_header_s header;
|
||||||
BINARY_FILE_READ(file, header);
|
BINARY_FILE_READ(file, header);
|
||||||
// FIXME magic number: 1
|
// FIXME magic number: 1
|
||||||
if (header.version != 1) {
|
if (header.version != VERSION) {
|
||||||
logging::Info("Outdated/corrupted walkbot file! Cannot load this.");
|
logging::Info("Outdated/corrupted walkbot file! Cannot load this.");
|
||||||
file.close();
|
file.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (header.author_length > 64 or header.map_length > 512 or (not header.author_length or not header.map_length)) {
|
||||||
|
logging::Info("Corrupted author/level data");
|
||||||
|
} else {
|
||||||
|
char name_buffer[header.author_length + 1];
|
||||||
|
char map_buffer[header.map_length + 1];
|
||||||
|
file.read(map_buffer, header.map_length);
|
||||||
|
file.read(name_buffer, header.author_length);
|
||||||
|
name_buffer[header.author_length] = 0;
|
||||||
|
map_buffer[header.map_length] = 0;
|
||||||
|
logging::Info("Walkbot navigation map for %s\nAuthor: %s", map_buffer, name_buffer);
|
||||||
|
}
|
||||||
state::nodes.clear();
|
state::nodes.clear();
|
||||||
logging::Info("Reading %i entries...", header.node_count);
|
logging::Info("Reading %i entries...", header.node_count);
|
||||||
|
if (header.node_count > 32768) {
|
||||||
|
logging::Info("Read %d nodes, max is %d. Aborting.", header.node_count, 32768);
|
||||||
|
return;
|
||||||
|
}
|
||||||
state::nodes.resize(header.node_count);
|
state::nodes.resize(header.node_count);
|
||||||
file.read(reinterpret_cast<char*>(state::nodes.data()), sizeof(walkbot_node_s) * header.node_count);
|
file.read(reinterpret_cast<char*>(state::nodes.data()), sizeof(walkbot_node_s) * header.node_count);
|
||||||
file.close();
|
file.close();
|
||||||
@ -226,9 +314,8 @@ index_t CreateNode(const Vector& xyz) {
|
|||||||
index_t node = state::free_node();
|
index_t node = state::free_node();
|
||||||
logging::Info("[wb] Creating node %u at (%.2f %.2f %.2f)", node, xyz.x, xyz.y, xyz.z);
|
logging::Info("[wb] Creating node %u at (%.2f %.2f %.2f)", node, xyz.x, xyz.y, xyz.z);
|
||||||
auto& n = state::nodes[node];
|
auto& n = state::nodes[node];
|
||||||
|
memset(&n, 0, sizeof(n));
|
||||||
n.xyz() = xyz;
|
n.xyz() = xyz;
|
||||||
n.preferred = INVALID_NODE;
|
|
||||||
n.connection_count = 0;
|
|
||||||
n.flags |= NF_GOOD;
|
n.flags |= NF_GOOD;
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
@ -255,7 +342,7 @@ CatCommand c_exit("wb_exit", "Exit", []() { state::state = WB_DISABLED; });
|
|||||||
// Selects closest node, clears selection if node is selected
|
// Selects closest node, clears selection if node is selected
|
||||||
CatCommand c_select_node("wb_select", "Select node", []() {
|
CatCommand c_select_node("wb_select", "Select node", []() {
|
||||||
if (state::active_node == state::closest_node) {
|
if (state::active_node == state::closest_node) {
|
||||||
state::active_node = INVALID_NODE;
|
state::active_node = BAD_NODE;
|
||||||
} else {
|
} else {
|
||||||
state::active_node = state::closest_node;
|
state::active_node = state::closest_node;
|
||||||
}
|
}
|
||||||
@ -377,56 +464,40 @@ CatCommand c_update_jump("wb_jump", "Toggle jump flag", []() {
|
|||||||
else
|
else
|
||||||
n.flags |= NF_JUMP;
|
n.flags |= NF_JUMP;
|
||||||
});
|
});
|
||||||
// Sets the closest node as preferred path for the selected node (or disable it)
|
|
||||||
CatCommand c_set_preferred("wb_prefer", "Set preferred node", []() {
|
|
||||||
index_t a = state::active_node;
|
|
||||||
index_t b = state::closest_node;
|
|
||||||
|
|
||||||
if (not (state::node_good(a) and state::node_good(b)))
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto& n = state::nodes[a];
|
|
||||||
if (n.preferred == b) {
|
|
||||||
n.preferred = INVALID_NODE;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool found = false;
|
|
||||||
for (size_t i = 0; i < n.connection_count; i++) {
|
|
||||||
if (n.connections[i] == b) {
|
|
||||||
if (found) {
|
|
||||||
logging::Info("[wb] WARNING!!! Duplicate connection to %u on node %u!!!", a, b);
|
|
||||||
}
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
n.preferred = b;
|
|
||||||
});
|
|
||||||
// Displays all info about closest node
|
// Displays all info about closest node
|
||||||
CatCommand c_info("wb_dump", "Show info", []() {
|
CatCommand c_info("wb_dump", "Show info", []() {
|
||||||
if (not state::node_good(state::closest_node))
|
index_t node = state::closest_node;
|
||||||
|
if (not node_good(node))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto& n = state::nodes[state::closest_node];
|
auto& n = nodes[node];
|
||||||
|
|
||||||
logging::Info("[wb] Info about node %u", state::closest_node);
|
logging::Info("[wb] Info about node %u", node);
|
||||||
logging::Info("[wb] FLAGS: Duck: %d, Jump: %d, Raw: %u", n.flags & NF_DUCK, n.flags & NF_JUMP, n.flags);
|
logging::Info("[wb] Flags: Duck=%d, Jump=%d, Raw=%u", n.flags & NF_DUCK, n.flags & NF_JUMP, n.flags);
|
||||||
logging::Info("[wb] X: %.2f | Y: %.2f | Z: %.2f", n.x, n.y, n.z);
|
logging::Info("[wb] X: %.2f | Y: %.2f | Z: %.2f", n.x, n.y, n.z);
|
||||||
logging::Info("[wb] CONNECTIONS: %d/%d", n.connection_count, MAX_CONNECTIONS);
|
logging::Info("[wb] Connections:");
|
||||||
for (size_t i = 0; i < n.connection_count; i++) {
|
for (size_t i = 0; i < MAX_CONNECTIONS; i++) {
|
||||||
logging::Info("[wb] %u <-> %u", state::closest_node, n.connections[i]);
|
if (n.connections[i].free())
|
||||||
auto& c = state::nodes[n.connections[i]];
|
continue;
|
||||||
|
|
||||||
|
if (not node_good(n.connections[i].node)) {
|
||||||
|
logging::Info("[wb] %u -x> X (%u)", node, n.connections[i].node);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto& c = state::nodes[n.connections[i].node];
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (size_t j = 0; j < c.connection_count; j++) {
|
for (size_t j = 0; j < MAX_CONNECTIONS; j++) {
|
||||||
if (c.connections[j] == state::closest_node) {
|
if (c.connections[j].good() and c.connections[j].node == node) {
|
||||||
if (found) {
|
if (found) {
|
||||||
logging::Info("[wb] DUPLICATE CONNECTION: %u <-> %u", i, state::closest_node);
|
logging::Info("[wb] %u <-> %u", i, state::closest_node);
|
||||||
}
|
}
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (not found) {
|
if (not found) {
|
||||||
logging::Info("[wb] One-directional connection! (%u)", i);
|
logging::Info("[wb] %u --> %u", node, n.connections[i].node);
|
||||||
|
} else {
|
||||||
|
logging::Info("[wb] %u <-> %u", node, n.connections[i].node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -475,7 +546,7 @@ void Initialize() {
|
|||||||
|
|
||||||
void UpdateClosestNode() {
|
void UpdateClosestNode() {
|
||||||
float n_fov = 360.0f;
|
float n_fov = 360.0f;
|
||||||
index_t n_idx = INVALID_NODE;
|
index_t n_idx = BAD_NODE;
|
||||||
|
|
||||||
for (index_t i = 0; i < state::nodes.size(); i++) {
|
for (index_t i = 0; i < state::nodes.size(); i++) {
|
||||||
auto& node = state::nodes[i];
|
auto& node = state::nodes[i];
|
||||||
@ -494,13 +565,13 @@ void UpdateClosestNode() {
|
|||||||
if (n_fov < 10)
|
if (n_fov < 10)
|
||||||
state::closest_node = n_idx;
|
state::closest_node = n_idx;
|
||||||
else
|
else
|
||||||
state::closest_node = INVALID_NODE;
|
state::closest_node = BAD_NODE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finds nearest node by position, not FOV
|
// Finds nearest node by position, not FOV
|
||||||
// Not to be confused with FindClosestNode
|
// Not to be confused with FindClosestNode
|
||||||
index_t FindNearestNode() {
|
index_t FindNearestNode() {
|
||||||
index_t r_node { INVALID_NODE };
|
index_t r_node { BAD_NODE };
|
||||||
float r_dist { 65536.0f };
|
float r_dist { 65536.0f };
|
||||||
|
|
||||||
for (index_t i = 0; i < state::nodes.size(); i++) {
|
for (index_t i = 0; i < state::nodes.size(); i++) {
|
||||||
@ -518,28 +589,23 @@ index_t FindNearestNode() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
index_t SelectNextNode() {
|
index_t SelectNextNode() {
|
||||||
if (not state::node_good(state::active_node)) {
|
if (not state::node_good(state::active_node)) {
|
||||||
return FindNearestNode();
|
return FindNearestNode();
|
||||||
}
|
}
|
||||||
auto& n = state::nodes[state::active_node];
|
auto& n = state::nodes[state::active_node];
|
||||||
if (n.connection_count) {
|
// TODO medkit connections and shit
|
||||||
if (state::node_good(n.preferred)) {
|
std::vector<index_t> chance {};
|
||||||
return n.preferred;
|
for (index_t i = 0; i < MAX_CONNECTIONS; i++) {
|
||||||
} else {
|
if (n.connections[i].good() and n.connections[i].node != state::last_node and node_good(n.connections[i].node)) {
|
||||||
std::vector<index_t> chance {};
|
chance.push_back(n.connections[i].node);
|
||||||
for (index_t i = 0; i < n.connection_count && i < MAX_CONNECTIONS; i++) {
|
}
|
||||||
if (n.connections[i] != state::active_node && n.connections[i] != state::last_node && state::node_good(n.connections[i])) {
|
}
|
||||||
chance.push_back(n.connections[i]);
|
if (not chance.empty()) {
|
||||||
}
|
return chance.at(unsigned(rand()) % chance.size());
|
||||||
}
|
} else {
|
||||||
if (not chance.empty()) {
|
return BAD_NODE;
|
||||||
return chance.at(rand() % chance.size());
|
}
|
||||||
} else {
|
return BAD_NODE;
|
||||||
return INVALID_NODE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return INVALID_NODE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateWalker() {
|
void UpdateWalker() {
|
||||||
@ -593,12 +659,14 @@ void UpdateWalker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draws a single colored connection between 2 nodes
|
// Draws a single colored connection between 2 nodes
|
||||||
void DrawConnection(index_t a, index_t b) {
|
void DrawConnection(index_t a, connection_s& b) {
|
||||||
if (not (state::node_good(a) and state::node_good(b)))
|
if (b.free())
|
||||||
|
return;
|
||||||
|
if (not (node_good(a) and node_good(b.node)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto& a_ = state::nodes[a];
|
auto& a_ = state::nodes[a];
|
||||||
auto& b_ = state::nodes[b];
|
auto& b_ = state::nodes[b.node];
|
||||||
|
|
||||||
Vector center = (a_.xyz() + b_.xyz()) / 2;
|
Vector center = (a_.xyz() + b_.xyz()) / 2;
|
||||||
|
|
||||||
@ -610,9 +678,6 @@ void DrawConnection(index_t a, index_t b) {
|
|||||||
if ((a_.flags & b_.flags) & NF_JUMP) color = &colors::yellow;
|
if ((a_.flags & b_.flags) & NF_JUMP) color = &colors::yellow;
|
||||||
else if ((a_.flags & b_.flags) & NF_DUCK) color = &colors::green;
|
else if ((a_.flags & b_.flags) & NF_DUCK) color = &colors::green;
|
||||||
|
|
||||||
if (a_.preferred == b)
|
|
||||||
color = &colors::pink;
|
|
||||||
|
|
||||||
drawgl::Line(wts_a.x, wts_a.y, wts_c.x - wts_a.x, wts_c.y - wts_a.y, color->rgba);
|
drawgl::Line(wts_a.x, wts_a.y, wts_c.x - wts_a.x, wts_c.y - wts_a.y, color->rgba);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -623,14 +688,8 @@ void DrawNode(index_t node, bool draw_back) {
|
|||||||
|
|
||||||
auto& n = state::nodes[node];
|
auto& n = state::nodes[node];
|
||||||
|
|
||||||
for (size_t i = 0; i < n.connection_count && i < MAX_CONNECTIONS; i++) {
|
for (size_t i = 0; i < MAX_CONNECTIONS; i++) {
|
||||||
index_t connection = n.connections[i];
|
DrawConnection(node, n.connections[i]);
|
||||||
// To prevent drawing connections twice in a for loop, we only draw connections to nodes with higher index
|
|
||||||
if (not draw_back) {
|
|
||||||
if (connection < node)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
DrawConnection(node, connection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (draw_nodes) {
|
if (draw_nodes) {
|
||||||
|
@ -89,7 +89,9 @@ std::string GetLevelName() {
|
|||||||
size_t slash = name.find('/');
|
size_t slash = name.find('/');
|
||||||
if (slash == std::string::npos) slash = 0;
|
if (slash == std::string::npos) slash = 0;
|
||||||
else slash++;
|
else slash++;
|
||||||
return name.substr(slash, name.length() - 4);
|
size_t bsp = name.find(".bsp");
|
||||||
|
size_t length = (bsp == std::string::npos ? name.length() - slash : bsp - slash);
|
||||||
|
return name.substr(slash, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<float, float> ComputeMove(const Vector& a, const Vector& b) {
|
std::pair<float, float> ComputeMove(const Vector& a, const Vector& b) {
|
||||||
|
Reference in New Issue
Block a user