Add datatable, netvar, sdk, trace, entity, player, weapon classes
This commit is contained in:
parent
e21469f7b1
commit
1f57477ac7
103
src/datatable.hh
Normal file
103
src/datatable.hh
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include "types.hh"
|
||||||
|
|
||||||
|
namespace sdk {
|
||||||
|
class RecvTable;
|
||||||
|
class RecvProp;
|
||||||
|
class RecvDecoder;
|
||||||
|
|
||||||
|
struct RecvProxyData {
|
||||||
|
public:
|
||||||
|
const RecvProp *prop; // The property it's receiving.
|
||||||
|
u8 value[16]; // The value given to you to store.
|
||||||
|
int element; // Which array element you're getting.
|
||||||
|
int id; // The object being referred to.
|
||||||
|
};
|
||||||
|
|
||||||
|
class RecvProp {
|
||||||
|
public:
|
||||||
|
using RecvVarProxyFn = void(const RecvProxyData *data, void *this_ptr, void *out);
|
||||||
|
using ArrayLengthRecvProxyFn = void(void *this_ptr, int id, int cur_length);
|
||||||
|
using DataTableRecvVarProxyFn = void(const RecvProp *prop, void **out, void *data, int id);
|
||||||
|
|
||||||
|
RecvProp() = delete;
|
||||||
|
|
||||||
|
auto as_datatable() const {
|
||||||
|
return data_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto as_array() const {
|
||||||
|
return array_prop;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto is_inside_array() const {
|
||||||
|
return inside_array;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's one of the numbered "000", "001", etc properties in an array, then
|
||||||
|
// these can be used to get its array property name for debugging.
|
||||||
|
auto parent_name() {
|
||||||
|
return parent_array_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Member ordering must remain the same!
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char *network_name;
|
||||||
|
int recv_type;
|
||||||
|
int flags;
|
||||||
|
int string_buffer_size;
|
||||||
|
|
||||||
|
bool inside_array; // Set to true by the engine if this property sits inside an array.
|
||||||
|
|
||||||
|
// Extra data that certain special property types bind to the property here.
|
||||||
|
const void *extra_data;
|
||||||
|
|
||||||
|
// If this is an array (DPT_Array).
|
||||||
|
RecvProp *array_prop;
|
||||||
|
|
||||||
|
ArrayLengthRecvProxyFn *array_length_proxy;
|
||||||
|
|
||||||
|
RecvVarProxyFn *proxy_fn;
|
||||||
|
|
||||||
|
DataTableRecvVarProxyFn *datatable_proxy_fn; // For RDT_DataTable.
|
||||||
|
RecvTable * data_table; // For RDT_DataTable.
|
||||||
|
int offset;
|
||||||
|
|
||||||
|
int element_stride;
|
||||||
|
int element_count;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// If it's one of the numbered "000", "001", etc properties in an array, then
|
||||||
|
// these can be used to get its array property name for debugging.
|
||||||
|
const char *parent_array_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RecvTable {
|
||||||
|
public:
|
||||||
|
RecvTable() = delete;
|
||||||
|
|
||||||
|
auto prop(int i) {
|
||||||
|
assert(i < prop_count);
|
||||||
|
return &props[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// C++11 iterator functions
|
||||||
|
auto begin() { return prop(0); }
|
||||||
|
auto end() { return props + prop_count; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Properties described in a table.
|
||||||
|
RecvProp *props;
|
||||||
|
int prop_count;
|
||||||
|
|
||||||
|
// The decoder. NOTE: this covers each RecvTable AND all its children (ie: its children
|
||||||
|
// will have their own decoders that include props for all their children).
|
||||||
|
RecvDecoder *decoder;
|
||||||
|
|
||||||
|
const char *name; // The name matched between client and server.
|
||||||
|
};
|
||||||
|
} // namespace sdk
|
49
src/entity.cc
Normal file
49
src/entity.cc
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#include "stdafx.hh"
|
||||||
|
|
||||||
|
#include "entity.hh"
|
||||||
|
#include "player.hh"
|
||||||
|
#include "weapon.hh"
|
||||||
|
|
||||||
|
#include "netvar.hh"
|
||||||
|
|
||||||
|
#include "sdk.hh"
|
||||||
|
|
||||||
|
using namespace sdk;
|
||||||
|
|
||||||
|
auto Entity::to_handle() -> EntityHandle & {
|
||||||
|
return_virtual_func(to_handle, 2, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Entity::is_valid() -> bool {
|
||||||
|
// get around clangs "correctly formed code never has a null thisptr"
|
||||||
|
auto thisptr = reinterpret_cast<uptr>(this);
|
||||||
|
if (thisptr == 0) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Entity::to_player() -> class Player * {
|
||||||
|
auto clientclass = client_class();
|
||||||
|
|
||||||
|
// TODO: do not hardcode this value
|
||||||
|
if (clientclass->class_id == 246) return static_cast<class Player *>(this);
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Entity::to_weapon() -> class Weapon * {
|
||||||
|
// TODO: checks...
|
||||||
|
return static_cast<class Weapon *>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Entity::client_class() -> struct ClientClass * {
|
||||||
|
return_virtual_func(client_class, 2, 0, 0, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Entity::dormant() -> bool {
|
||||||
|
return_virtual_func(dormant, 8, 0, 0, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Entity::index() -> u32 {
|
||||||
|
return_virtual_func(index, 9, 0, 0, 8);
|
||||||
|
}
|
36
src/entity.hh
Normal file
36
src/entity.hh
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "types.hh"
|
||||||
|
#include "vfunc.hh"
|
||||||
|
|
||||||
|
namespace sdk {
|
||||||
|
struct EntityHandle {
|
||||||
|
u32 serial_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Entity {
|
||||||
|
public:
|
||||||
|
Entity() = delete;
|
||||||
|
|
||||||
|
// helper functions
|
||||||
|
bool is_valid();
|
||||||
|
|
||||||
|
EntityHandle &to_handle();
|
||||||
|
|
||||||
|
template <typename T, u32 offset>
|
||||||
|
auto set(T data) { *reinterpret_cast<T *>(reinterpret_cast<uptr>(this) + offset) = data; }
|
||||||
|
|
||||||
|
template <typename T, u32 offset>
|
||||||
|
auto get() { return *reinterpret_cast<T *>(reinterpret_cast<uptr>(this) + offset); }
|
||||||
|
|
||||||
|
// upcasts
|
||||||
|
class Player *to_player();
|
||||||
|
class Weapon *to_weapon();
|
||||||
|
|
||||||
|
// virtual functions
|
||||||
|
struct ClientClass *client_class();
|
||||||
|
|
||||||
|
bool dormant();
|
||||||
|
u32 index();
|
||||||
|
};
|
||||||
|
} // namespace sdk
|
@ -7,7 +7,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace Hooks {
|
namespace hooks {
|
||||||
template <typename T, u32 offset>
|
template <typename T, u32 offset>
|
||||||
class HookInstance {
|
class HookInstance {
|
||||||
|
|
||||||
@ -161,4 +161,4 @@ public:
|
|||||||
template <typename T, u32 offset>
|
template <typename T, u32 offset>
|
||||||
std::vector<HookInstance<T, offset> *> HookFunction<T, offset>::hooks;
|
std::vector<HookInstance<T, offset> *> HookFunction<T, offset>::hooks;
|
||||||
|
|
||||||
} // namespace Hooks
|
} // namespace hooks
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
#include "interface.hh"
|
#include "interface.hh"
|
||||||
#include "signature.hh"
|
#include "signature.hh"
|
||||||
|
|
||||||
|
#include "platform.hh"
|
||||||
|
|
||||||
using InstantiateInterfaceFn = void *(*)();
|
using InstantiateInterfaceFn = void *(*)();
|
||||||
|
|
||||||
class InterfaceReg {
|
class InterfaceReg {
|
||||||
@ -16,14 +18,14 @@ public:
|
|||||||
InterfaceReg *next;
|
InterfaceReg *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
void *InterfaceHelpers::find_interface(const char *module_name, const char *interface_name) {
|
void *interface_helpers::find_interface(const char *module_name, const char *interface_name) {
|
||||||
|
|
||||||
InterfaceReg *interface_reg_head;
|
InterfaceReg *interface_reg_head;
|
||||||
|
|
||||||
if constexpr (DoghookPlatform::windows()) {
|
if constexpr (doghook_platform::windows()) {
|
||||||
interface_reg_head = **Signature::find_pattern<InterfaceReg ***>(module_name, "8B 35 ? ? ? ? 57 85 F6 74 38", 2);
|
interface_reg_head = **signature::find_pattern<InterfaceReg ***>(module_name, "8B 35 ? ? ? ? 57 85 F6 74 38", 2);
|
||||||
} else if constexpr (DoghookPlatform::linux()) {
|
} else if constexpr (doghook_platform::linux()) {
|
||||||
} else if constexpr (DoghookPlatform::osx()) {
|
} else if constexpr (doghook_platform::osx()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(interface_reg_head);
|
assert(interface_reg_head);
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace InterfaceHelpers {
|
namespace interface_helpers {
|
||||||
|
|
||||||
// this auto-resolves the library using Signature::
|
// this auto-resolves the library using Signature::
|
||||||
void *find_interface(const char *module_name, const char *interface_name);
|
void *find_interface(const char *module_name, const char *interface_name);
|
||||||
} // namespace InterfaceHelpers
|
} // namespace interface_helpers
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class Interface {
|
class Interface {
|
||||||
@ -16,7 +16,7 @@ public:
|
|||||||
// e.g. "VClient017" -> "VClient"
|
// e.g. "VClient017" -> "VClient"
|
||||||
static auto set_from_interface(const char *module_name, const char *interface_name) {
|
static auto set_from_interface(const char *module_name, const char *interface_name) {
|
||||||
value = static_cast<T *>(
|
value = static_cast<T *>(
|
||||||
InterfaceHelpers::find_interface(module_name, interface_name));
|
interface_helpers::find_interface(module_name, interface_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
// set from a pointer (you should only do this for non-exported
|
// set from a pointer (you should only do this for non-exported
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
#undef max
|
#undef max
|
||||||
#undef min
|
#undef min
|
||||||
|
|
||||||
namespace Math {
|
namespace math {
|
||||||
constexpr auto PI = 3.14159265358979323846f;
|
constexpr auto PI = 3.14159265358979323846f;
|
||||||
|
|
||||||
inline auto to_radians(float x) {
|
inline auto to_radians(float x) {
|
||||||
@ -252,8 +252,7 @@ public:
|
|||||||
out.z = data[row][2];
|
out.z = data[row][2];
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
auto rotate_vector(const Vector &in) const {
|
||||||
auto rotate_vector(const Math::Vector &in) const {
|
|
||||||
Vector out;
|
Vector out;
|
||||||
Vector row;
|
Vector row;
|
||||||
|
|
||||||
@ -350,4 +349,4 @@ inline void matrix_angles(const Matrix3x4 &matrix, Vector &angles, Vector &posit
|
|||||||
matrix_angles(matrix, &angles.x);
|
matrix_angles(matrix, &angles.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Math
|
} // namespace math
|
||||||
|
87
src/netvar.cc
Normal file
87
src/netvar.cc
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#include "stdafx.hh"
|
||||||
|
|
||||||
|
#include "interface.hh"
|
||||||
|
#include "netvar.hh"
|
||||||
|
#include "sdk.hh"
|
||||||
|
|
||||||
|
using namespace sdk;
|
||||||
|
|
||||||
|
static Netvar *head;
|
||||||
|
|
||||||
|
void Netvar::Tree::populate_recursive(RecvTable *t, netvar_tree *nodes) {
|
||||||
|
for (auto i = 0; i < t->prop_count; i++) {
|
||||||
|
auto * prop = t->prop(i);
|
||||||
|
const auto new_node = new node();
|
||||||
|
new_node->p = prop;
|
||||||
|
|
||||||
|
if (prop->recv_type == 6) populate_recursive(prop->as_datatable(), &new_node->children);
|
||||||
|
|
||||||
|
nodes->emplace_back(std::make_pair(prop->network_name, new_node));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Netvar::Tree::Tree() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Netvar::Tree::init() {
|
||||||
|
if (prop_tree.size() > 0) return;
|
||||||
|
|
||||||
|
auto cc = IFace<Client>()->get_all_classes();
|
||||||
|
while (cc != nullptr) {
|
||||||
|
const auto new_node = new node();
|
||||||
|
new_node->p = nullptr;
|
||||||
|
|
||||||
|
populate_recursive(cc->recv_table, &new_node->children);
|
||||||
|
prop_tree.emplace_back(cc->recv_table->name, new_node);
|
||||||
|
|
||||||
|
cc = cc->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uptr Netvar::Tree::find_offset(std::vector<const char *> t) {
|
||||||
|
uptr total = 0;
|
||||||
|
auto nodes = &prop_tree;
|
||||||
|
|
||||||
|
for (auto &name : t) {
|
||||||
|
|
||||||
|
auto old_nodes = nodes;
|
||||||
|
|
||||||
|
auto end = nodes->end();
|
||||||
|
for (auto it = nodes->begin(); it != end; ++it) {
|
||||||
|
auto p = *it;
|
||||||
|
|
||||||
|
if (strcmp(name, p.first) == 0) {
|
||||||
|
nodes = &p.second->children;
|
||||||
|
if (p.second->p != nullptr)
|
||||||
|
total += p.second->p->offset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodes == old_nodes) {
|
||||||
|
// TODO:
|
||||||
|
//Log::msg("[Netvar] Unable to find '%s'", name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
Netvar::Tree Netvar::netvar_tree;
|
||||||
|
|
||||||
|
void Netvar::add_to_init_list() {
|
||||||
|
next = head;
|
||||||
|
head = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Netvar::init() {
|
||||||
|
offset = netvar_tree.find_offset(tables);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Netvar::init_all() {
|
||||||
|
netvar_tree.init();
|
||||||
|
|
||||||
|
for (auto &n = head; n != nullptr; n = n->next) {
|
||||||
|
n->init();
|
||||||
|
}
|
||||||
|
}
|
58
src/netvar.hh
Normal file
58
src/netvar.hh
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "types.hh"
|
||||||
|
|
||||||
|
namespace sdk {
|
||||||
|
class Netvar {
|
||||||
|
|
||||||
|
class Tree {
|
||||||
|
|
||||||
|
struct node;
|
||||||
|
using netvar_tree = std::vector<std::pair<const char *, node *>>;
|
||||||
|
|
||||||
|
struct node {
|
||||||
|
netvar_tree children;
|
||||||
|
class RecvProp *p;
|
||||||
|
};
|
||||||
|
|
||||||
|
netvar_tree prop_tree;
|
||||||
|
|
||||||
|
void populate_recursive(class RecvTable *t, netvar_tree *nodes);
|
||||||
|
|
||||||
|
public:
|
||||||
|
Tree();
|
||||||
|
|
||||||
|
void init();
|
||||||
|
|
||||||
|
uptr find_offset(std::vector<const char *> t);
|
||||||
|
};
|
||||||
|
static Tree netvar_tree;
|
||||||
|
|
||||||
|
// the offset of this netvar from its parent instance
|
||||||
|
uptr offset;
|
||||||
|
|
||||||
|
// used internally for registering
|
||||||
|
Netvar *next;
|
||||||
|
|
||||||
|
std::vector<const char *> tables;
|
||||||
|
|
||||||
|
void add_to_init_list();
|
||||||
|
void init();
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename... A>
|
||||||
|
Netvar(A... args) : tables({args...}) {
|
||||||
|
add_to_init_list();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto &get(void *instance) {
|
||||||
|
return *reinterpret_cast<T *>(static_cast<char *>(instance) + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init_all();
|
||||||
|
};
|
||||||
|
} // namespace sdk
|
@ -40,7 +40,7 @@
|
|||||||
#endif
|
#endif
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
namespace DoghookPlatform {
|
namespace doghook_platform {
|
||||||
inline constexpr bool windows() { return doghook_platform_windows(); }
|
inline constexpr bool windows() { return doghook_platform_windows(); }
|
||||||
inline constexpr bool linux() { return doghook_platform_linux(); }
|
inline constexpr bool linux() { return doghook_platform_linux(); }
|
||||||
inline constexpr bool osx() { return doghook_platform_osx(); }
|
inline constexpr bool osx() { return doghook_platform_osx(); }
|
||||||
@ -48,7 +48,7 @@ inline constexpr bool osx() { return doghook_platform_osx(); }
|
|||||||
inline constexpr bool msvc() { return doghook_platform_msvc(); }
|
inline constexpr bool msvc() { return doghook_platform_msvc(); }
|
||||||
inline constexpr bool clang() { return doghook_platform_clang(); }
|
inline constexpr bool clang() { return doghook_platform_clang(); }
|
||||||
inline constexpr bool gcc() { return doghook_platform_gcc(); }
|
inline constexpr bool gcc() { return doghook_platform_gcc(); }
|
||||||
} // namespace DoghookPlatform
|
} // namespace doghook_platform
|
||||||
|
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
|
|
||||||
|
259
src/player.cc
Normal file
259
src/player.cc
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
#include "stdafx.hh"
|
||||||
|
|
||||||
|
#include "player.hh"
|
||||||
|
#include "weapon.hh"
|
||||||
|
|
||||||
|
#include "netvar.hh"
|
||||||
|
#include "vfunc.hh"
|
||||||
|
|
||||||
|
#include "sdk.hh"
|
||||||
|
|
||||||
|
using namespace sdk;
|
||||||
|
|
||||||
|
auto Player::local() -> Player * {
|
||||||
|
return static_cast<Player *>(IFace<EntList>()->entity(IFace<Engine>()->local_player_index()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto health = Netvar("DT_BasePlayer", "m_iHealth");
|
||||||
|
auto Player::health() -> int & {
|
||||||
|
return ::health.get<int>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto lifestate = Netvar("DT_BasePlayer", "m_lifeState");
|
||||||
|
auto Player::alive() -> bool {
|
||||||
|
return ::lifestate.get<u8>(this) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Player::origin() -> math::Vector & {
|
||||||
|
return_virtual_func(origin, 9, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Player::set_origin(const math::Vector &v) -> void {
|
||||||
|
if constexpr (doghook_platform::windows()) {
|
||||||
|
static auto original = signature::find_pattern<void(__thiscall *)(Player *, const math::Vector &)>("client", "55 8B EC 56 57 8B F1 E8 ? ? ? ? 8B 7D 08 F3 0F 10 07", 0);
|
||||||
|
assert(original);
|
||||||
|
return original(this, v);
|
||||||
|
} else if constexpr (doghook_platform::linux()) {
|
||||||
|
static_assert(doghook_platform::linux() == false);
|
||||||
|
} else if constexpr (doghook_platform::osx()) {
|
||||||
|
static_assert(doghook_platform::osx() == false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Player::angles() -> math::Vector & {
|
||||||
|
return_virtual_func(angles, 10, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Player::set_angles(const math::Vector &v) -> void {
|
||||||
|
if constexpr (doghook_platform::windows()) {
|
||||||
|
static auto original = signature::find_pattern<void(__thiscall *)(Player *, const math::Vector &)>("client", "55 8B EC 83 EC 60 56 57 8B F1", 0);
|
||||||
|
assert(original);
|
||||||
|
return original(this, v);
|
||||||
|
} else if constexpr (doghook_platform::linux()) {
|
||||||
|
static_assert(doghook_platform::linux() == false);
|
||||||
|
} else if constexpr (doghook_platform::osx()) {
|
||||||
|
static_assert(doghook_platform::osx() == false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto team = Netvar("DT_BaseEntity", "m_iTeamNum");
|
||||||
|
auto Player::team() -> int {
|
||||||
|
return ::team.get<int>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto cond = Netvar("DT_TFPlayer", "m_Shared", "m_nPlayerCond");
|
||||||
|
auto Player::cond() -> u32 & {
|
||||||
|
return ::cond.get<u32>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Player::render_bounds() -> std::pair<math::Vector, math::Vector> {
|
||||||
|
auto func = vfunc::Func<void (Player::*)(math::Vector &, math::Vector &)>(this, 20, 0, 0, 4);
|
||||||
|
|
||||||
|
std::pair<math::Vector, math::Vector> ret;
|
||||||
|
|
||||||
|
func.invoke(ret.first, ret.second);
|
||||||
|
|
||||||
|
auto origin = this->origin();
|
||||||
|
|
||||||
|
ret.first += origin;
|
||||||
|
ret.second += origin;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto collideable_min = Netvar("DT_BaseEntity", "m_Collision", "m_vecMinsPreScaled");
|
||||||
|
static auto collideable_max = Netvar("DT_BaseEntity", "m_Collision", "m_vecMaxsPreScaled");
|
||||||
|
auto Player::collision_bounds() -> std::pair<math::Vector &, math::Vector &> {
|
||||||
|
|
||||||
|
auto &min = ::collideable_min.get<math::Vector>(this);
|
||||||
|
auto &max = ::collideable_max.get<math::Vector>(this);
|
||||||
|
|
||||||
|
return std::make_pair(std::ref(min), std::ref(max));
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto view_offset = Netvar("DT_BasePlayer", "localdata", "m_vecViewOffset[0]");
|
||||||
|
auto Player::view_offset() -> math::Vector & {
|
||||||
|
return ::view_offset.get<math::Vector>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto tf_class = Netvar("DT_TFPlayer", "m_PlayerClass", "m_iClass");
|
||||||
|
auto Player::tf_class() -> int {
|
||||||
|
return ::tf_class.get<int>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto tick_base = Netvar("DT_BasePlayer", "localdata", "m_nTickBase");
|
||||||
|
auto Player::tick_base() -> int {
|
||||||
|
return ::tick_base.get<int>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto active_weapon_handle = Netvar("DT_BaseCombatCharacter", "m_hActiveWeapon");
|
||||||
|
auto Player::active_weapon() -> Weapon * {
|
||||||
|
return static_cast<Weapon *>(IFace<EntList>()->from_handle(::active_weapon_handle.get<EntityHandle>(this)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto sim_time = Netvar("DT_BaseEntity", "m_flSimulationTime");
|
||||||
|
auto Player::sim_time() -> float & {
|
||||||
|
return ::sim_time.get<float>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto anim_time = Netvar("DT_BaseEntity", "AnimTimeMustBeFirst", "m_flAnimTime");
|
||||||
|
auto Player::anim_time() -> float & {
|
||||||
|
return ::anim_time.get<float>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto cycle = Netvar("DT_BaseAnimating", "serveranimdata", "m_flCycle");
|
||||||
|
auto Player::cycle() -> float & {
|
||||||
|
return ::cycle.get<float>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto fov_time = Netvar("DT_BasePlayer", "m_flFOVTime");
|
||||||
|
auto Player::fov_time() -> float {
|
||||||
|
return ::fov_time.get<float>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto render_origin = Netvar("DT_BaseEntity", "m_vecOrigin");
|
||||||
|
auto Player::render_origin() -> math::Vector & {
|
||||||
|
return ::render_origin.get<math::Vector>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct UtlVector {
|
||||||
|
T * mem;
|
||||||
|
int alloc_count;
|
||||||
|
int grow_size;
|
||||||
|
int size;
|
||||||
|
T * dbg_elements;
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto player_anim_layer_vector(Player *p) -> UtlVector<AnimationLayer> & {
|
||||||
|
// TODO: we need a better, cross platform method for doing this.
|
||||||
|
// check for "%8.4f : %30s : %5.3f : %4.2f : %1d\n"
|
||||||
|
|
||||||
|
if constexpr (doghook_platform::windows()) {
|
||||||
|
UtlVector<AnimationLayer> *anim_overlays = reinterpret_cast<UtlVector<AnimationLayer> *>(p + 2216);
|
||||||
|
return *anim_overlays;
|
||||||
|
} else if constexpr (doghook_platform::linux()) {
|
||||||
|
} else if constexpr (doghook_platform::osx()) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Player::anim_layer(u32 index) -> AnimationLayer & {
|
||||||
|
return player_anim_layer_vector(this).mem[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Player::anim_layer_count() -> u32 {
|
||||||
|
return player_anim_layer_vector(this).size;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Player::update_client_side_animation() -> void {
|
||||||
|
// Look for the string "UpdateClientSideAnimations
|
||||||
|
return_virtual_func(update_client_side_animation, 191, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Player::invalidate_physics_recursive(u32 flags) -> void {
|
||||||
|
typedef void(__thiscall * InvalidatePhysicsRecursiveFn)(Player *, u32 flags);
|
||||||
|
|
||||||
|
if constexpr (doghook_platform::windows()) {
|
||||||
|
static auto fn = signature::find_pattern<InvalidatePhysicsRecursiveFn>("client", "55 8B EC 51 53 8B 5D 08 56 8B F3 83 E6 04", 0);
|
||||||
|
fn(this, flags);
|
||||||
|
} else if constexpr (doghook_platform::linux()) {
|
||||||
|
} else if constexpr (doghook_platform::osx()) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto sequence = Netvar("DT_BaseAnimating", "m_nSequence");
|
||||||
|
auto Player::sequence() -> int & {
|
||||||
|
return ::sequence.get<int>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Player::view_position() -> math::Vector {
|
||||||
|
return origin() + view_offset();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Player::bone_transforms(math::Matrix3x4 *hitboxes_out, u32 max_bones, u32 bone_mask, float current_time) -> bool {
|
||||||
|
return_virtual_func(bone_transforms, 16, 0, 0, 4, hitboxes_out, max_bones, bone_mask, current_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Player::model_handle() -> const ModelHandle * {
|
||||||
|
return_virtual_func(model_handle, 9, 0, 0, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Player::studio_model() -> const StudioModel * {
|
||||||
|
return IFace<ModelInfo>()->studio_model(this->model_handle());
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto get_hitboxes_internal(Player *player, const StudioModel *model, PlayerHitboxes *hitboxes, bool create_pose) {
|
||||||
|
math::Matrix3x4 bone_to_world[128];
|
||||||
|
|
||||||
|
// #define BONE_USED_BY_ANYTHING 0x0007FF00
|
||||||
|
bool success = player->bone_transforms(bone_to_world, 128, 0x0007FF00, IFace<Engine>()->last_timestamp());
|
||||||
|
assert(success);
|
||||||
|
|
||||||
|
auto hitbox_set_ptr = model->hitbox_set(0);
|
||||||
|
assert(hitbox_set_ptr);
|
||||||
|
|
||||||
|
auto &hitbox_set = *hitbox_set_ptr;
|
||||||
|
|
||||||
|
auto hitboxes_count = std::min(128u, hitbox_set.hitboxes_count);
|
||||||
|
|
||||||
|
math::Vector origin;
|
||||||
|
math::Vector centre;
|
||||||
|
|
||||||
|
for (u32 i = 0; i < hitboxes_count; ++i) {
|
||||||
|
auto box = hitbox_set[i];
|
||||||
|
assert(box);
|
||||||
|
|
||||||
|
math::Vector rotation;
|
||||||
|
math::matrix_angles(bone_to_world[box->bone], rotation, origin);
|
||||||
|
|
||||||
|
math::Matrix3x4 rotate_matrix;
|
||||||
|
rotate_matrix.from_angle(rotation);
|
||||||
|
|
||||||
|
math::Vector rotated_min, rotated_max;
|
||||||
|
|
||||||
|
rotated_min = rotate_matrix.rotate_vector(box->min);
|
||||||
|
rotated_max = rotate_matrix.rotate_vector(box->max);
|
||||||
|
|
||||||
|
centre = rotated_min.lerp(rotated_max, 0.5);
|
||||||
|
|
||||||
|
hitboxes->centre[i] = origin + centre;
|
||||||
|
hitboxes->min[i] = origin + rotated_min;
|
||||||
|
hitboxes->max[i] = origin + rotated_max;
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
hitboxes->rotation[i] = rotation;
|
||||||
|
hitboxes->origin[i] = origin;
|
||||||
|
hitboxes->raw_min[i] = box->min;
|
||||||
|
hitboxes->raw_max[i] = box->max;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return hitboxes_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Player::hitboxes(PlayerHitboxes *hitboxes_out, bool create_pose) -> u32 {
|
||||||
|
auto model = studio_model();
|
||||||
|
assert(model);
|
||||||
|
|
||||||
|
return get_hitboxes_internal(this, model, hitboxes_out, create_pose);
|
||||||
|
}
|
88
src/player.hh
Normal file
88
src/player.hh
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "types.hh"
|
||||||
|
|
||||||
|
#include "entity.hh"
|
||||||
|
|
||||||
|
#undef min
|
||||||
|
#undef max
|
||||||
|
|
||||||
|
namespace sdk {
|
||||||
|
struct PlayerHitboxes {
|
||||||
|
math::Vector centre[128];
|
||||||
|
|
||||||
|
math::Vector min[128];
|
||||||
|
math::Vector max[128];
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
// These are used for debugging
|
||||||
|
math::Vector origin[128];
|
||||||
|
math::Vector rotation[128];
|
||||||
|
|
||||||
|
math::Vector raw_min[128];
|
||||||
|
math::Vector raw_max[128];
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
class Player : public Entity {
|
||||||
|
public:
|
||||||
|
Player() = delete;
|
||||||
|
|
||||||
|
// helper functions
|
||||||
|
static auto local() -> Player *;
|
||||||
|
|
||||||
|
auto world_space_centre() -> math::Vector &;
|
||||||
|
|
||||||
|
auto model_handle() -> const class ModelHandle *;
|
||||||
|
auto studio_model() -> const class StudioModel *;
|
||||||
|
|
||||||
|
auto view_position() -> math::Vector;
|
||||||
|
|
||||||
|
auto hitboxes(PlayerHitboxes *hitboxes_out, bool create_pose) -> u32;
|
||||||
|
|
||||||
|
// netvars
|
||||||
|
auto health() -> int &;
|
||||||
|
|
||||||
|
auto alive() -> bool;
|
||||||
|
auto team() -> int;
|
||||||
|
|
||||||
|
auto cond() -> u32 &;
|
||||||
|
|
||||||
|
auto view_offset() -> math::Vector &;
|
||||||
|
|
||||||
|
auto tf_class() -> int;
|
||||||
|
|
||||||
|
auto tick_base() -> int;
|
||||||
|
auto sim_time() -> float &;
|
||||||
|
auto anim_time() -> float &;
|
||||||
|
auto cycle() -> float &;
|
||||||
|
auto sequence() -> int &;
|
||||||
|
|
||||||
|
auto fov_time() -> float;
|
||||||
|
|
||||||
|
auto render_origin() -> math::Vector &;
|
||||||
|
|
||||||
|
auto active_weapon() -> Weapon *;
|
||||||
|
|
||||||
|
// virtual functions
|
||||||
|
auto origin() -> math::Vector &;
|
||||||
|
auto set_origin(const math::Vector &v) -> void;
|
||||||
|
|
||||||
|
auto render_bounds() -> std::pair<math::Vector, math::Vector>;
|
||||||
|
|
||||||
|
auto angles() -> math::Vector &;
|
||||||
|
auto set_angles(const math::Vector &v) -> void;
|
||||||
|
|
||||||
|
auto anim_layer(u32 index) -> class AnimationLayer &;
|
||||||
|
auto anim_layer_count() -> u32;
|
||||||
|
|
||||||
|
auto update_client_side_animation() -> void;
|
||||||
|
|
||||||
|
auto invalidate_physics_recursive(u32 flags) -> void;
|
||||||
|
|
||||||
|
// These are relative to the origin
|
||||||
|
auto collision_bounds() -> std::pair<math::Vector &, math::Vector &>;
|
||||||
|
|
||||||
|
auto bone_transforms(math::Matrix3x4 *hitboxes_out, u32 max_bones, u32 bone_mask, float current_time) -> bool;
|
||||||
|
};
|
||||||
|
} // namespace sdk
|
445
src/sdk.hh
Normal file
445
src/sdk.hh
Normal file
@ -0,0 +1,445 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "types.hh"
|
||||||
|
#include "vfunc.hh"
|
||||||
|
|
||||||
|
#include "datatable.hh"
|
||||||
|
#include "entity.hh"
|
||||||
|
#include "interface.hh"
|
||||||
|
#include "signature.hh"
|
||||||
|
|
||||||
|
#include "trace.hh"
|
||||||
|
|
||||||
|
namespace sdk {
|
||||||
|
class UserCmd {
|
||||||
|
public:
|
||||||
|
virtual ~UserCmd(){};
|
||||||
|
|
||||||
|
int command_number;
|
||||||
|
int tick_count;
|
||||||
|
math::Vector viewangles;
|
||||||
|
float forwardmove;
|
||||||
|
float sidemove;
|
||||||
|
float upmove;
|
||||||
|
int buttons;
|
||||||
|
u8 impulse;
|
||||||
|
int weapon_select;
|
||||||
|
int weapon_subtype;
|
||||||
|
int random_seed;
|
||||||
|
short mouse_dx;
|
||||||
|
short mouse_dy;
|
||||||
|
bool has_been_predicted;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ClientClass {
|
||||||
|
void * create_fn;
|
||||||
|
void * create_event_fn; // Only called for event objects.
|
||||||
|
const char * network_name;
|
||||||
|
RecvTable * recv_table;
|
||||||
|
ClientClass *next;
|
||||||
|
int class_id; // Managed by the engine.
|
||||||
|
};
|
||||||
|
|
||||||
|
class Client {
|
||||||
|
public:
|
||||||
|
Client() = delete;
|
||||||
|
|
||||||
|
auto get_all_classes() -> ClientClass * {
|
||||||
|
return_virtual_func(get_all_classes, 8, 8, 8, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ClientMode {
|
||||||
|
public:
|
||||||
|
ClientMode() = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetChannel {
|
||||||
|
public:
|
||||||
|
enum class Flow {
|
||||||
|
outgoing,
|
||||||
|
incoming
|
||||||
|
};
|
||||||
|
|
||||||
|
NetChannel() = delete;
|
||||||
|
|
||||||
|
auto get_sequence_number(sdk::NetChannel::Flow f) -> i32 {
|
||||||
|
return_virtual_func(get_sequence_number, 16, 16, 16, 0, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto queued_packets() -> i32 & {
|
||||||
|
static auto queued_packets_offset = []() {
|
||||||
|
if constexpr (doghook_platform::windows()) {
|
||||||
|
return *signature::find_pattern<u32 *>("engine", "83 BE ? ? ? ? ? 0F 9F C0 84 C0", 2);
|
||||||
|
} else if constexpr (doghook_platform::linux()) {
|
||||||
|
} else if constexpr (doghook_platform::osx()) {
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
assert(queued_packets_offset);
|
||||||
|
|
||||||
|
auto &queued_packets = *reinterpret_cast<i32 *>(reinterpret_cast<u8 *>(this) + queued_packets_offset);
|
||||||
|
|
||||||
|
return queued_packets;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Globals {
|
||||||
|
public:
|
||||||
|
float realtime;
|
||||||
|
int framecount;
|
||||||
|
|
||||||
|
float absolute_frametime;
|
||||||
|
float curtime;
|
||||||
|
|
||||||
|
float frametime;
|
||||||
|
|
||||||
|
int max_clients;
|
||||||
|
|
||||||
|
int tickcount;
|
||||||
|
float interval_per_tick;
|
||||||
|
|
||||||
|
float interpolation_amount;
|
||||||
|
int sim_ticks_this_frame;
|
||||||
|
|
||||||
|
int network_protocol;
|
||||||
|
|
||||||
|
class CSaveRestoreData *save_data;
|
||||||
|
|
||||||
|
bool is_client;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Engine {
|
||||||
|
public:
|
||||||
|
Engine() = delete;
|
||||||
|
|
||||||
|
auto last_timestamp() -> float {
|
||||||
|
return_virtual_func(last_timestamp, 15, 15, 15, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto time() -> float {
|
||||||
|
return_virtual_func(time, 14, 14, 14, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto in_game() -> bool {
|
||||||
|
return_virtual_func(in_game, 26, 26, 26, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto local_player_index() -> u32 {
|
||||||
|
return_virtual_func(local_player_index, 12, 12, 12, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto net_channel_info() -> NetChannel * {
|
||||||
|
return_virtual_func(net_channel_info, 72, 72, 72, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto is_box_visible(const math::Vector &min, const math::Vector &max) -> bool {
|
||||||
|
return_virtual_func(is_box_visible, 31, 31, 31, 0, min, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto max_clients() -> u32 {
|
||||||
|
return_virtual_func(max_clients, 21, 21, 21, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto set_view_angles(const math::Vector &v) -> void {
|
||||||
|
return_virtual_func(set_view_angles, 20, 20, 20, 0, v);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class EntList {
|
||||||
|
public:
|
||||||
|
EntList() = delete;
|
||||||
|
|
||||||
|
auto entity(u32 index) -> Entity * {
|
||||||
|
return_virtual_func(entity, 3, 0, 0, 0, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto from_handle(EntityHandle h) -> Entity * {
|
||||||
|
return_virtual_func(from_handle, 4, 4, 4, 0, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto max_entity_index() -> u32 {
|
||||||
|
return_virtual_func(max_entity_index, 6, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
class EntityRange {
|
||||||
|
EntList *parent;
|
||||||
|
|
||||||
|
u32 max_entity;
|
||||||
|
|
||||||
|
public:
|
||||||
|
class Iterator {
|
||||||
|
u32 index;
|
||||||
|
EntList *parent;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// TODO: should we use 1 here or should we use 0 and force people
|
||||||
|
// that loop to deal with that themselves...
|
||||||
|
Iterator(EntList *parent) : index(1), parent(parent) {}
|
||||||
|
explicit Iterator(u32 index, EntList *parent)
|
||||||
|
: index(index), parent(parent) {}
|
||||||
|
|
||||||
|
auto &operator++() {
|
||||||
|
++index;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto operator*() {
|
||||||
|
return parent->entity(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto operator==(const Iterator &b) {
|
||||||
|
return index == b.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto operator!=(const Iterator &b) {
|
||||||
|
return !(*this == b);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
EntityRange(EntList *parent) : parent(parent), max_entity(parent->max_entity_index()) {}
|
||||||
|
|
||||||
|
explicit EntityRange(EntList *parent, u32 max_entity) : parent(parent), max_entity(max_entity) {}
|
||||||
|
|
||||||
|
auto begin() { return Iterator(parent); }
|
||||||
|
|
||||||
|
auto end() { return Iterator(max_entity, parent); }
|
||||||
|
};
|
||||||
|
|
||||||
|
auto get_range() { return EntityRange(this); }
|
||||||
|
auto get_range(u32 max_entity) { return EntityRange(this, max_entity + 1); }
|
||||||
|
|
||||||
|
}; // namespace sdk
|
||||||
|
|
||||||
|
class Input {
|
||||||
|
public:
|
||||||
|
Input() = delete;
|
||||||
|
|
||||||
|
auto get_user_cmd(u32 sequence_number) -> UserCmd * {
|
||||||
|
static auto array_offset = []() {
|
||||||
|
if constexpr (doghook_platform::windows()) {
|
||||||
|
return *signature::find_pattern<u32 *>("client", "8B 87 ? ? ? ? 8B CA", 2);
|
||||||
|
} else if constexpr (doghook_platform::linux()) {
|
||||||
|
} else if constexpr (doghook_platform::osx()) {
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
// this should not be 0
|
||||||
|
assert(array_offset != 0);
|
||||||
|
|
||||||
|
auto cmd_array = *reinterpret_cast<UserCmd **>(reinterpret_cast<u8 *>(this) + array_offset);
|
||||||
|
|
||||||
|
return &cmd_array[sequence_number % 90];
|
||||||
|
}
|
||||||
|
|
||||||
|
auto get_verified_user_cmd(u32 sequence_number) -> class VerifiedCmd * {
|
||||||
|
// 03 B7 ? ? ? ? 8D 04 88
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// defined in convar.cc
|
||||||
|
class ConCommandBase;
|
||||||
|
|
||||||
|
class Cvar {
|
||||||
|
public:
|
||||||
|
Cvar() = delete;
|
||||||
|
|
||||||
|
auto allocate_dll_identifier() -> u32 {
|
||||||
|
return_virtual_func(allocate_dll_identifier, 5, 5, 5, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto register_command(ConCommandBase *command) -> void {
|
||||||
|
return_virtual_func(register_command, 6, 6, 6, 0, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto unregister_command(ConCommandBase *command) -> void {
|
||||||
|
return_virtual_func(unregister_command, 7, 7, 7, 0, command);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Trace {
|
||||||
|
public:
|
||||||
|
Trace() = delete;
|
||||||
|
|
||||||
|
auto trace_ray(const trace::Ray &ray, u32 mask, trace::Filter *filter, trace::TraceResult *results) -> void {
|
||||||
|
return_virtual_func(trace_ray, 4, 4, 4, 0, ray, mask, filter, results);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// These are defined to give us distinct types for these
|
||||||
|
// very different objects, to help us differentiate between them
|
||||||
|
|
||||||
|
class ModelHandle; // model_t
|
||||||
|
|
||||||
|
// mstudiobbox
|
||||||
|
class StudioHitbox {
|
||||||
|
public:
|
||||||
|
StudioHitbox() = delete;
|
||||||
|
|
||||||
|
int bone;
|
||||||
|
int group;
|
||||||
|
|
||||||
|
math::Vector min;
|
||||||
|
math::Vector max;
|
||||||
|
|
||||||
|
int name_index;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int unused[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
// mstudiobboxset
|
||||||
|
class StudioHitboxSet {
|
||||||
|
public:
|
||||||
|
StudioHitboxSet() = delete;
|
||||||
|
|
||||||
|
int name_index;
|
||||||
|
auto name() const -> const char * {
|
||||||
|
assert(0);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 hitboxes_count;
|
||||||
|
u32 hitbox_index;
|
||||||
|
|
||||||
|
const auto operator[](u32 index) const {
|
||||||
|
assert(index < hitboxes_count);
|
||||||
|
|
||||||
|
return reinterpret_cast<const StudioHitbox *>(
|
||||||
|
reinterpret_cast<const u8 *>(this) + hitbox_index) +
|
||||||
|
index;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// studiohdr_t
|
||||||
|
class StudioModel {
|
||||||
|
public:
|
||||||
|
StudioModel() = delete;
|
||||||
|
|
||||||
|
int id;
|
||||||
|
int version;
|
||||||
|
int checksum;
|
||||||
|
char name[64];
|
||||||
|
int length;
|
||||||
|
|
||||||
|
math::Vector eye_position; // ideal eye position
|
||||||
|
|
||||||
|
math::Vector illumination_position; // illumination center
|
||||||
|
|
||||||
|
math::Vector hull_min; // ideal movement hull size
|
||||||
|
math::Vector hull_max;
|
||||||
|
|
||||||
|
math::Vector view_bbmin; // clipping bounding box
|
||||||
|
math::Vector view_bbmax;
|
||||||
|
|
||||||
|
int flags;
|
||||||
|
|
||||||
|
int bones_count;
|
||||||
|
int bone_index;
|
||||||
|
|
||||||
|
int bone_controllers_count;
|
||||||
|
int bone_controller_index;
|
||||||
|
|
||||||
|
u32 hitbox_sets_count;
|
||||||
|
u32 hitbox_set_index;
|
||||||
|
|
||||||
|
const auto hitbox_set(u32 index) const {
|
||||||
|
assert(index < hitbox_sets_count);
|
||||||
|
|
||||||
|
return reinterpret_cast<const StudioHitboxSet *>(
|
||||||
|
reinterpret_cast<const u8 *>(this) + hitbox_set_index) +
|
||||||
|
index;
|
||||||
|
}
|
||||||
|
|
||||||
|
//... TODO:
|
||||||
|
};
|
||||||
|
|
||||||
|
class ModelInfo {
|
||||||
|
public:
|
||||||
|
ModelInfo() = delete;
|
||||||
|
|
||||||
|
auto model_name(const ModelHandle *m) -> const char * {
|
||||||
|
return_virtual_func(model_name, 3, 0, 0, 0, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto studio_model(const ModelHandle *m) -> const StudioModel * {
|
||||||
|
return_virtual_func(studio_model, 28, 0, 0, 0, m);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// This should only be used for debugging.
|
||||||
|
// For real output use the overlay
|
||||||
|
class DebugOverlay {
|
||||||
|
public:
|
||||||
|
using OverlayText_t = void;
|
||||||
|
|
||||||
|
virtual void add_entity_text_overlay(int ent_index, int line_offset, float duration, int r, int g, int b, int a, const char *format, ...) = 0;
|
||||||
|
virtual void add_box_overlay(const math::Vector &origin, const math::Vector &mins, const math::Vector &max, const math ::Vector &orientation, int r, int g, int b, int a, float duration) = 0;
|
||||||
|
virtual void add_triangle_overlay(const math::Vector &p1, const math::Vector &p2, const math::Vector &p3, int r, int g, int b, int a, bool no_depth_test, float duration) = 0;
|
||||||
|
virtual void add_line_overlay(const math::Vector &origin, const math::Vector &dest, int r, int g, int b, bool no_depth_test, float duration) = 0;
|
||||||
|
virtual void add_text_overlay(const math::Vector &origin, float duration, const char *format, ...) = 0;
|
||||||
|
virtual void add_text_overlay(const math::Vector &origin, int line_offset, float duration, const char *format, ...) = 0;
|
||||||
|
virtual void add_screen_text_overlay(float x_pos, float y_pos, float duration, int r, int g, int b, int a, const char *text) = 0;
|
||||||
|
virtual void add_swept_box_overlay(const math::Vector &start, const math::Vector &end, const math::Vector &mins, const math::Vector &max, const math::Vector &angles, int r, int g, int b, int a, float duration) = 0;
|
||||||
|
virtual void add_grid_overlay(const math::Vector &origin) = 0;
|
||||||
|
virtual int screen_position(const math::Vector &point, math::Vector &screen) = 0;
|
||||||
|
virtual int screen_position(float x_pos, float y_pos, math::Vector &screen) = 0;
|
||||||
|
|
||||||
|
virtual OverlayText_t *get_first(void) = 0;
|
||||||
|
virtual OverlayText_t *get_next(OverlayText_t *current) = 0;
|
||||||
|
virtual void clear_dead_overlays() = 0;
|
||||||
|
virtual void clear_all_overlays() = 0;
|
||||||
|
|
||||||
|
virtual void add_text_overlay_rgb(const math::Vector &origin, int line_offset, float duration, float r, float g, float b, float alpha, const char *format, ...) = 0;
|
||||||
|
virtual void add_text_overlay_rgb(const math::Vector &origin, int line_offset, float duration, int r, int g, int b, int a, const char *format, ...) = 0;
|
||||||
|
|
||||||
|
virtual void add_line_overlay_alpha(const math::Vector &origin, const math::Vector &dest, int r, int g, int b, int a, bool noDepthTest, float duration) = 0;
|
||||||
|
//virtual void add_box_overlay2(const math::Vector &origin, const math::Vector &mins, const math::Vector &max, const math::Vector &orientation, const Color &faceColor, const Color &edgeColor, float duration) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is from server.dll
|
||||||
|
class PlayerInfoManager {
|
||||||
|
public:
|
||||||
|
auto globals() -> Globals * {
|
||||||
|
return_virtual_func(globals, 1, 1, 1, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MoveHelper;
|
||||||
|
|
||||||
|
class Prediction {
|
||||||
|
public:
|
||||||
|
auto setup_move(Player *player, UserCmd *ucmd, MoveHelper *helper, void *move) -> void {
|
||||||
|
return_virtual_func(setup_move, 18, 18, 18, 0, player, ucmd, helper, move);
|
||||||
|
}
|
||||||
|
void finish_move(Player *player, UserCmd *ucmd, void *move) {
|
||||||
|
return_virtual_func(finish_move, 19, 19, 19, 0, player, ucmd, move);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class GameMovement {
|
||||||
|
public:
|
||||||
|
void process_movement(Player *player, void *move) {
|
||||||
|
return_virtual_func(process_movement, 1, 1, 1, 0, player, move);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class AnimationLayer {
|
||||||
|
public:
|
||||||
|
int sequence;
|
||||||
|
float prev_cycle;
|
||||||
|
float weight;
|
||||||
|
int order;
|
||||||
|
|
||||||
|
float playback_rate;
|
||||||
|
float cycle;
|
||||||
|
|
||||||
|
float layer_anim_time;
|
||||||
|
float layer_fade_time;
|
||||||
|
|
||||||
|
float blend_in;
|
||||||
|
float blend_out;
|
||||||
|
|
||||||
|
bool client_blend;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sdk
|
@ -11,6 +11,8 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
using namespace signature;
|
||||||
|
|
||||||
static constexpr auto in_range(char x, char a, char b) {
|
static constexpr auto in_range(char x, char a, char b) {
|
||||||
return (x >= a && x <= b);
|
return (x >= a && x <= b);
|
||||||
}
|
}
|
||||||
@ -53,7 +55,7 @@ static u8 *find_pattern_internal(uptr start, uptr end, const char *pattern) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::pair<uptr, uptr> find_module_code_section(const char *module_name) {
|
static std::pair<uptr, uptr> find_module_code_section(const char *module_name) {
|
||||||
auto module = Signature::resolve_library(module_name);
|
auto module = resolve_library(module_name);
|
||||||
|
|
||||||
#if doghook_platform_windows()
|
#if doghook_platform_windows()
|
||||||
auto module_addr = reinterpret_cast<uptr>(module);
|
auto module_addr = reinterpret_cast<uptr>(module);
|
||||||
@ -113,7 +115,7 @@ static std::pair<uptr, uptr> find_module_code_section(const char *module_name) {
|
|||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Signature::resolve_library(const char *name) -> void * {
|
auto signature::resolve_library(const char *name) -> void * {
|
||||||
#if doghook_platform_windows()
|
#if doghook_platform_windows()
|
||||||
// TODO: actually check directories for this dll instead of
|
// TODO: actually check directories for this dll instead of
|
||||||
// letting the loader do the work
|
// letting the loader do the work
|
||||||
@ -162,7 +164,7 @@ auto Signature::resolve_library(const char *name) -> void * {
|
|||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
void *Signature::resolve_import(void *handle, const char *name) {
|
void *signature::resolve_import(void *handle, const char *name) {
|
||||||
#if doghook_platform_windows()
|
#if doghook_platform_windows()
|
||||||
return reinterpret_cast<void *>(GetProcAddress(static_cast<HMODULE>(handle), name));
|
return reinterpret_cast<void *>(GetProcAddress(static_cast<HMODULE>(handle), name));
|
||||||
#elif doghook_platform_linux()
|
#elif doghook_platform_linux()
|
||||||
@ -172,17 +174,17 @@ void *Signature::resolve_import(void *handle, const char *name) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 *Signature::find_pattern(const char *module_name, const char *pattern) {
|
u8 *signature::find_pattern(const char *module_name, const char *pattern) {
|
||||||
auto code_section = find_module_code_section(module_name);
|
auto code_section = find_module_code_section(module_name);
|
||||||
|
|
||||||
return find_pattern_internal(code_section.first, code_section.second, pattern);
|
return find_pattern_internal(code_section.first, code_section.second, pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 *Signature::find_pattern(const char *pattern, uptr start, uptr length) {
|
u8 *signature::find_pattern(const char *pattern, uptr start, uptr length) {
|
||||||
return find_pattern_internal(start, start + length, pattern);
|
return find_pattern_internal(start, start + length, pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *Signature::resolve_callgate(void *address) {
|
void *signature::resolve_callgate(void *address) {
|
||||||
// TODO: are these the only instructions here?
|
// TODO: are these the only instructions here?
|
||||||
assert(reinterpret_cast<i8 *>(address)[0] == '\xE8' || reinterpret_cast<i8 *>(address)[0] == '\xE9');
|
assert(reinterpret_cast<i8 *>(address)[0] == '\xE8' || reinterpret_cast<i8 *>(address)[0] == '\xE9');
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#include "platform.hh"
|
#include "platform.hh"
|
||||||
|
|
||||||
namespace Signature {
|
namespace signature {
|
||||||
void *resolve_library(const char *name);
|
void *resolve_library(const char *name);
|
||||||
void *resolve_import(void *handle, const char *name);
|
void *resolve_import(void *handle, const char *name);
|
||||||
|
|
||||||
@ -16,4 +16,4 @@ auto find_pattern(const char *module, const char *pattern, u32 offset) {
|
|||||||
return reinterpret_cast<T>(find_pattern(module, pattern) + offset);
|
return reinterpret_cast<T>(find_pattern(module, pattern) + offset);
|
||||||
}
|
}
|
||||||
void *resolve_callgate(void *address);
|
void *resolve_callgate(void *address);
|
||||||
} // namespace Signature
|
} // namespace signature
|
||||||
|
158
src/trace.hh
Normal file
158
src/trace.hh
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "platform.hh"
|
||||||
|
|
||||||
|
namespace trace {
|
||||||
|
class VectorAligned : public math::Vector {
|
||||||
|
float pad;
|
||||||
|
|
||||||
|
public:
|
||||||
|
VectorAligned() {}
|
||||||
|
|
||||||
|
explicit VectorAligned(const math::Vector &other) {
|
||||||
|
x = other.x;
|
||||||
|
y = other.y;
|
||||||
|
z = other.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit VectorAligned(const math::Vector &&other) {
|
||||||
|
x = other.x;
|
||||||
|
y = other.y;
|
||||||
|
z = other.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto operator=(const Vector &other) {
|
||||||
|
x = other.x;
|
||||||
|
y = other.y;
|
||||||
|
z = other.z;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto operator=(const Vector &&other) {
|
||||||
|
x = other.x;
|
||||||
|
y = other.y;
|
||||||
|
z = other.z;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator Vector() {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Plane {
|
||||||
|
math::Vector normal;
|
||||||
|
float dist;
|
||||||
|
u8 type; // for fast side tests
|
||||||
|
u8 signbits; // signx + (signy<<1) + (signz<<1)
|
||||||
|
u8 pad[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Model {
|
||||||
|
math::Vector min, max;
|
||||||
|
math::Vector origin; // for sounds or lights
|
||||||
|
int head_node;
|
||||||
|
|
||||||
|
struct VCollide {
|
||||||
|
unsigned short solidCount : 15;
|
||||||
|
unsigned short isPacked : 1;
|
||||||
|
unsigned short descSize;
|
||||||
|
// VPhysicsSolids
|
||||||
|
void **solids;
|
||||||
|
char * pKeyValues;
|
||||||
|
};
|
||||||
|
|
||||||
|
VCollide vcollision_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Surface {
|
||||||
|
const char * name;
|
||||||
|
short surface_props;
|
||||||
|
unsigned short flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TraceResult {
|
||||||
|
math::Vector start_pos; // start position
|
||||||
|
math::Vector end_pos; // final position
|
||||||
|
Plane plane; // surface normal at impact
|
||||||
|
|
||||||
|
float fraction; // time completed, 1.0 = didn't hit anything
|
||||||
|
|
||||||
|
int contents; // contents on other side of surface hit
|
||||||
|
unsigned short disp_flags; // displacement flags for marking surfaces with data
|
||||||
|
|
||||||
|
bool all_solid; // if true, plane is not valid
|
||||||
|
bool start_solid; // if true, the initial point was in a solid area
|
||||||
|
|
||||||
|
float fraction_left_solid; // time we left a solid, only valid if we started in solid
|
||||||
|
Surface surface; // surface hit (impact surface)
|
||||||
|
|
||||||
|
int hitgroup; // 0 == generic, non-zero is specific body part
|
||||||
|
short physicsbone; // physics bone hit by trace in studio
|
||||||
|
|
||||||
|
sdk::Entity *entity;
|
||||||
|
|
||||||
|
// NOTE: this member is overloaded.
|
||||||
|
// If hEnt points at the world entity, then this is the static prop index.
|
||||||
|
// Otherwise, this is the hitbox index.
|
||||||
|
int hitbox; // box hit by trace in studio
|
||||||
|
};
|
||||||
|
|
||||||
|
class Ray {
|
||||||
|
public:
|
||||||
|
VectorAligned start;
|
||||||
|
VectorAligned delta;
|
||||||
|
VectorAligned start_offset;
|
||||||
|
VectorAligned extents;
|
||||||
|
bool is_ray;
|
||||||
|
bool is_swept;
|
||||||
|
|
||||||
|
// init for point ray
|
||||||
|
auto init(const math::Vector &start, const math::Vector &end) {
|
||||||
|
delta = end - start;
|
||||||
|
is_swept = (delta.length_sqr() != 0);
|
||||||
|
is_ray = true;
|
||||||
|
|
||||||
|
extents = math::Vector::zero();
|
||||||
|
start_offset = math::Vector::zero();
|
||||||
|
|
||||||
|
this->start = start;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init for box ray
|
||||||
|
auto init(const math::Vector &start, const math::Vector &end, const math::Vector &min, const math::Vector &max) {
|
||||||
|
delta = end - start;
|
||||||
|
|
||||||
|
is_swept = (delta.length_sqr() != 0);
|
||||||
|
|
||||||
|
extents = max - min;
|
||||||
|
extents *= 0.5;
|
||||||
|
is_ray = false;
|
||||||
|
|
||||||
|
start_offset = min + max;
|
||||||
|
start_offset *= 0.5f;
|
||||||
|
|
||||||
|
this->start = start_offset + start;
|
||||||
|
start_offset *= -1.0f;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Filter {
|
||||||
|
sdk::Entity *ignore_self;
|
||||||
|
sdk::Entity *ignore_entity;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual auto should_hit_entity(sdk::Entity *handle_entity, int contents_mask) -> bool;
|
||||||
|
virtual auto GetTraceType() const -> u32 {
|
||||||
|
return 0; // hit everything
|
||||||
|
}
|
||||||
|
|
||||||
|
Filter() : ignore_self(nullptr), ignore_entity(nullptr) {}
|
||||||
|
|
||||||
|
Filter(sdk::Entity *ignore_self) : ignore_self(ignore_self) {}
|
||||||
|
Filter(sdk::Entity *ignore_self, sdk::Entity *ignore_entity) : ignore_self(ignore_self), ignore_entity(ignore_entity) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace trace
|
14
src/vfunc.hh
14
src/vfunc.hh
@ -7,7 +7,7 @@
|
|||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
|
|
||||||
// helpers for calling virtual functions
|
// helpers for calling virtual functions
|
||||||
namespace VFunc {
|
namespace vfunc {
|
||||||
inline auto get_table(void *inst, u32 offset) -> void ** {
|
inline auto get_table(void *inst, u32 offset) -> void ** {
|
||||||
return *reinterpret_cast<void ***>(reinterpret_cast<u8 *>(inst) + offset);
|
return *reinterpret_cast<void ***>(reinterpret_cast<u8 *>(inst) + offset);
|
||||||
}
|
}
|
||||||
@ -54,15 +54,15 @@ public:
|
|||||||
//assert(instance != nullptr);
|
//assert(instance != nullptr);
|
||||||
|
|
||||||
auto index = 0u;
|
auto index = 0u;
|
||||||
if constexpr (BluePlatform::windows())
|
if constexpr (doghook_platform::windows())
|
||||||
index = index_windows;
|
index = index_windows;
|
||||||
else if constexpr (BluePlatform::linux())
|
else if constexpr (doghook_platform::linux())
|
||||||
index = index_linux;
|
index = index_linux;
|
||||||
else if constexpr (BluePlatform::osx())
|
else if constexpr (doghook_platform::osx())
|
||||||
index = index_osx;
|
index = index_osx;
|
||||||
|
|
||||||
auto offset = 0u;
|
auto offset = 0u;
|
||||||
if constexpr (BluePlatform::windows()) {
|
if constexpr (doghook_platform::windows()) {
|
||||||
offset = offset_windows;
|
offset = offset_windows;
|
||||||
|
|
||||||
this->instance = reinterpret_cast<ObjectType *>(
|
this->instance = reinterpret_cast<ObjectType *>(
|
||||||
@ -79,7 +79,7 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace VFunc
|
} // namespace vfunc
|
||||||
|
|
||||||
// macro for easier definitions of wrapper calls
|
// macro for easier definitions of wrapper calls
|
||||||
// name is the name of the function
|
// name is the name of the function
|
||||||
@ -88,4 +88,4 @@ public:
|
|||||||
#define return_virtual_func(name, windows, linux, osx, off, ...) \
|
#define return_virtual_func(name, windows, linux, osx, off, ...) \
|
||||||
using c = std::remove_reference<decltype(*this)>::type; \
|
using c = std::remove_reference<decltype(*this)>::type; \
|
||||||
using t = decltype(&c::name); \
|
using t = decltype(&c::name); \
|
||||||
return VFunc::Func<t>(this, windows, linux, osx, off).invoke(__VA_ARGS__)
|
return vfunc::Func<t>(this, windows, linux, osx, off).invoke(__VA_ARGS__)
|
||||||
|
27
src/weapon.cc
Normal file
27
src/weapon.cc
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#include "stdafx.hh"
|
||||||
|
|
||||||
|
#include "interface.hh"
|
||||||
|
#include "netvar.hh"
|
||||||
|
#include "sdk.hh"
|
||||||
|
#include "weapon.hh"
|
||||||
|
|
||||||
|
using namespace sdk;
|
||||||
|
|
||||||
|
auto next_primary_attack = Netvar("DT_BaseCombatWeapon", "LocalActiveWeaponData", "m_flNextPrimaryAttack");
|
||||||
|
auto Weapon::next_primary_attack() -> float {
|
||||||
|
return ::next_primary_attack.get<float>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto next_secondary_attack = Netvar("DT_BaseCombatWeapon", "LocalActiveWeaponData", "m_flNextSecondaryAttack");
|
||||||
|
auto Weapon::next_secondary_attack() -> float {
|
||||||
|
return ::next_secondary_attack.get<float>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Weapon::can_shoot(u32 tickbase) -> bool {
|
||||||
|
return tickbase * IFace<Globals>()->interval_per_tick > next_primary_attack();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Weapon::owner() -> Entity * {
|
||||||
|
// TODO:
|
||||||
|
return nullptr;
|
||||||
|
}
|
15
src/weapon.hh
Normal file
15
src/weapon.hh
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "entity.hh"
|
||||||
|
|
||||||
|
namespace sdk {
|
||||||
|
class Weapon : public Entity {
|
||||||
|
public:
|
||||||
|
auto can_shoot(u32 tickbase) -> bool;
|
||||||
|
|
||||||
|
auto next_primary_attack() -> float;
|
||||||
|
auto next_secondary_attack() -> float;
|
||||||
|
|
||||||
|
auto owner() -> Entity *;
|
||||||
|
};
|
||||||
|
} // namespace sdk
|
Reference in New Issue
Block a user