Merge pull request #228 from diamante0018/script-fields
Expose entity/client flags to gsc
This commit is contained in:
commit
ab5ca990ab
@ -4,6 +4,115 @@ namespace Components
|
|||||||
{
|
{
|
||||||
const char* ScriptExtension::QueryStrings[] = { R"(..)", R"(../)", R"(..\)" };
|
const char* ScriptExtension::QueryStrings[] = { R"(..)", R"(../)", R"(..\)" };
|
||||||
|
|
||||||
|
std::unordered_map<std::uint16_t, Game::ent_field_t> ScriptExtension::CustomEntityFields;
|
||||||
|
std::unordered_map<std::uint16_t, Game::client_fields_s> ScriptExtension::CustomClientFields;
|
||||||
|
|
||||||
|
void ScriptExtension::AddEntityField(const char* name, Game::fieldtype_t type,
|
||||||
|
const Game::ScriptCallbackEnt& setter, const Game::ScriptCallbackEnt& getter)
|
||||||
|
{
|
||||||
|
static std::uint16_t fieldOffsetStart = 15; // fields count
|
||||||
|
assert((fieldOffsetStart & Game::ENTFIELD_MASK) == Game::ENTFIELD_ENTITY);
|
||||||
|
|
||||||
|
ScriptExtension::CustomEntityFields[fieldOffsetStart] = {name, fieldOffsetStart, type, setter, getter};
|
||||||
|
++fieldOffsetStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptExtension::AddClientField(const char* name, Game::fieldtype_t type,
|
||||||
|
const Game::ScriptCallbackClient& setter, const Game::ScriptCallbackClient& getter)
|
||||||
|
{
|
||||||
|
static std::uint16_t fieldOffsetStart = 21; // fields count
|
||||||
|
assert((fieldOffsetStart & Game::ENTFIELD_MASK) == Game::ENTFIELD_ENTITY);
|
||||||
|
|
||||||
|
const auto offset = fieldOffsetStart | Game::ENTFIELD_CLIENT; // This is how client field's offset is calculated
|
||||||
|
|
||||||
|
// Use 'index' in 'array' as map key. It will be used later in Scr_SetObjectFieldStub
|
||||||
|
ScriptExtension::CustomClientFields[fieldOffsetStart] = {name, offset, type, setter, getter};
|
||||||
|
++fieldOffsetStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptExtension::GScr_AddFieldsForEntityStub()
|
||||||
|
{
|
||||||
|
for (const auto& [offset, field] : ScriptExtension::CustomEntityFields)
|
||||||
|
{
|
||||||
|
Game::Scr_AddClassField(Game::ClassNum::CLASS_NUM_ENTITY, field.name, field.ofs);
|
||||||
|
}
|
||||||
|
|
||||||
|
Utils::Hook::Call<void()>(0x4A7CF0)(); // GScr_AddFieldsForClient
|
||||||
|
|
||||||
|
for (const auto& [offset, field] : ScriptExtension::CustomClientFields)
|
||||||
|
{
|
||||||
|
Game::Scr_AddClassField(Game::ClassNum::CLASS_NUM_ENTITY, field.name, field.ofs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Because some functions are inlined we have to hook this function instead of Scr_SetEntityField
|
||||||
|
int ScriptExtension::Scr_SetObjectFieldStub(unsigned int classnum, int entnum, int offset)
|
||||||
|
{
|
||||||
|
if (classnum == Game::ClassNum::CLASS_NUM_ENTITY)
|
||||||
|
{
|
||||||
|
const auto entity_offset = static_cast<std::uint16_t>(offset);
|
||||||
|
|
||||||
|
const auto got = ScriptExtension::CustomEntityFields.find(entity_offset);
|
||||||
|
if (got != ScriptExtension::CustomEntityFields.end())
|
||||||
|
{
|
||||||
|
got->second.setter(&Game::g_entities[entnum], offset);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No custom generic field was found, let the game handle it
|
||||||
|
return Game::Scr_SetObjectField(classnum, entnum, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset was already converted to array 'index' following binop offset & ~Game::ENTFIELD_MASK
|
||||||
|
void ScriptExtension::Scr_SetClientFieldStub(Game::gclient_s* client, int offset)
|
||||||
|
{
|
||||||
|
const auto client_offset = static_cast<std::uint16_t>(offset);
|
||||||
|
|
||||||
|
const auto got = ScriptExtension::CustomClientFields.find(client_offset);
|
||||||
|
if (got != ScriptExtension::CustomClientFields.end())
|
||||||
|
{
|
||||||
|
got->second.setter(client, &got->second);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No custom field client was found, let the game handle it
|
||||||
|
Game::Scr_SetClientField(client, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptExtension::Scr_GetEntityFieldStub(int entnum, int offset)
|
||||||
|
{
|
||||||
|
if ((offset & Game::ENTFIELD_MASK) == Game::ENTFIELD_CLIENT)
|
||||||
|
{
|
||||||
|
// If we have a ENTFIELD_CLIENT offset we need to check g_entity is actually a fully connected client
|
||||||
|
if (Game::g_entities[entnum].client != nullptr)
|
||||||
|
{
|
||||||
|
const auto client_offset = static_cast<std::uint16_t>(offset & ~Game::ENTFIELD_MASK);
|
||||||
|
|
||||||
|
const auto got = ScriptExtension::CustomClientFields.find(client_offset);
|
||||||
|
if (got != ScriptExtension::CustomClientFields.end())
|
||||||
|
{
|
||||||
|
// Game functions probably don't ever need to use the reference to client_fields_s...
|
||||||
|
got->second.getter(Game::g_entities[entnum].client, &got->second);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regular entity offsets can be searched directly in our custom handler
|
||||||
|
const auto entity_offset = static_cast<std::uint16_t>(offset);
|
||||||
|
|
||||||
|
const auto got = ScriptExtension::CustomEntityFields.find(entity_offset);
|
||||||
|
if (got != ScriptExtension::CustomEntityFields.end())
|
||||||
|
{
|
||||||
|
got->second.getter(&Game::g_entities[entnum], offset);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No custom generic field was found, let the game handle it
|
||||||
|
Game::Scr_GetEntityField(entnum, offset);
|
||||||
|
}
|
||||||
|
|
||||||
void ScriptExtension::AddFunctions()
|
void ScriptExtension::AddFunctions()
|
||||||
{
|
{
|
||||||
// File functions
|
// File functions
|
||||||
@ -247,11 +356,46 @@ namespace Components
|
|||||||
Game::Scr_AddIString(value);
|
Game::Scr_AddIString(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScriptExtension::AddEntityFields()
|
||||||
|
{
|
||||||
|
ScriptExtension::AddEntityField("entityflags", Game::fieldtype_t::F_INT,
|
||||||
|
[](Game::gentity_s* ent, [[maybe_unused]] int offset)
|
||||||
|
{
|
||||||
|
ent->flags = Game::Scr_GetInt(0);
|
||||||
|
},
|
||||||
|
[](Game::gentity_s* ent, [[maybe_unused]] int offset)
|
||||||
|
{
|
||||||
|
Game::Scr_AddInt(ent->flags);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptExtension::AddClientFields()
|
||||||
|
{
|
||||||
|
ScriptExtension::AddClientField("clientflags", Game::fieldtype_t::F_INT,
|
||||||
|
[](Game::gclient_s* pSelf, [[maybe_unused]] const Game::client_fields_s* pField)
|
||||||
|
{
|
||||||
|
pSelf->flags = Game::Scr_GetInt(0);
|
||||||
|
},
|
||||||
|
[](Game::gclient_s* pSelf, [[maybe_unused]] const Game::client_fields_s* pField)
|
||||||
|
{
|
||||||
|
Game::Scr_AddInt(pSelf->flags);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
ScriptExtension::ScriptExtension()
|
ScriptExtension::ScriptExtension()
|
||||||
{
|
{
|
||||||
ScriptExtension::AddFunctions();
|
ScriptExtension::AddFunctions();
|
||||||
ScriptExtension::AddMethods();
|
ScriptExtension::AddMethods();
|
||||||
|
ScriptExtension::AddEntityFields();
|
||||||
|
ScriptExtension::AddClientFields();
|
||||||
|
|
||||||
// Correct builtin function pointer
|
// Correct builtin function pointer
|
||||||
Utils::Hook::Set<void(*)()>(0x79A90C, ScriptExtension::Scr_TableLookupIStringByRow);
|
Utils::Hook::Set<void(*)()>(0x79A90C, ScriptExtension::Scr_TableLookupIStringByRow);
|
||||||
|
|
||||||
|
Utils::Hook(0x4EC721, ScriptExtension::GScr_AddFieldsForEntityStub, HOOK_CALL).install()->quick(); // GScr_AddFieldsForEntity
|
||||||
|
|
||||||
|
Utils::Hook(0x41BED2, ScriptExtension::Scr_SetObjectFieldStub, HOOK_CALL).install()->quick(); // SetEntityFieldValue
|
||||||
|
Utils::Hook(0x5FBF01, ScriptExtension::Scr_SetClientFieldStub, HOOK_CALL).install()->quick(); // Scr_SetObjectField
|
||||||
|
Utils::Hook(0x4FF413, ScriptExtension::Scr_GetEntityFieldStub, HOOK_CALL).install()->quick(); // Scr_GetObjectField
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,28 @@ namespace Components
|
|||||||
public:
|
public:
|
||||||
ScriptExtension();
|
ScriptExtension();
|
||||||
|
|
||||||
|
static void AddEntityField(const char* name, Game::fieldtype_t type, const Game::ScriptCallbackEnt& setter, const Game::ScriptCallbackEnt& getter);
|
||||||
|
static void AddClientField(const char* name, Game::fieldtype_t type, const Game::ScriptCallbackClient& setter, const Game::ScriptCallbackClient& getter);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const char* QueryStrings[];
|
static const char* QueryStrings[];
|
||||||
|
|
||||||
|
static std::unordered_map<std::uint16_t, Game::ent_field_t> CustomEntityFields;
|
||||||
|
static std::unordered_map<std::uint16_t, Game::client_fields_s> CustomClientFields;
|
||||||
|
|
||||||
|
static void GScr_AddFieldsForEntityStub();
|
||||||
|
|
||||||
|
// Two hooks because it makes our code cleaner (luckily functions were not inlined)
|
||||||
|
static int Scr_SetObjectFieldStub(unsigned int classnum, int entnum, int offset);
|
||||||
|
static void Scr_SetClientFieldStub(Game::gclient_s* client, int offset);
|
||||||
|
|
||||||
|
// One hook because functions were inlined
|
||||||
|
static void Scr_GetEntityFieldStub(int entnum, int offset);
|
||||||
|
|
||||||
static void AddFunctions();
|
static void AddFunctions();
|
||||||
static void AddMethods();
|
static void AddMethods();
|
||||||
|
static void AddEntityFields();
|
||||||
|
static void AddClientFields();
|
||||||
static void Scr_TableLookupIStringByRow();
|
static void Scr_TableLookupIStringByRow();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -296,6 +296,12 @@ namespace Game
|
|||||||
|
|
||||||
Scr_ClearOutParams_t Scr_ClearOutParams = Scr_ClearOutParams_t(0x4386E0);
|
Scr_ClearOutParams_t Scr_ClearOutParams = Scr_ClearOutParams_t(0x4386E0);
|
||||||
|
|
||||||
|
Scr_GetObjectField_t Scr_GetObjectField = Scr_GetObjectField_t(0x4FF3D0);
|
||||||
|
Scr_SetObjectField_t Scr_SetObjectField = Scr_SetObjectField_t(0x4F20F0);
|
||||||
|
Scr_GetEntityField_t Scr_GetEntityField = Scr_GetEntityField_t(0x4E8390);
|
||||||
|
Scr_SetClientField_t Scr_SetClientField = Scr_SetClientField_t(0x4A6DF0);
|
||||||
|
Scr_AddClassField_t Scr_AddClassField = Scr_AddClassField_t(0x4C0E70);
|
||||||
|
|
||||||
GetEntity_t GetEntity = GetEntity_t(0x4BC270);
|
GetEntity_t GetEntity = GetEntity_t(0x4BC270);
|
||||||
GetPlayerEntity_t GetPlayerEntity = GetPlayerEntity_t(0x49C4A0);
|
GetPlayerEntity_t GetPlayerEntity = GetPlayerEntity_t(0x49C4A0);
|
||||||
|
|
||||||
|
@ -747,6 +747,21 @@ namespace Game
|
|||||||
typedef void(__cdecl * Scr_ParamError_t)(unsigned int paramIndex, const char*);
|
typedef void(__cdecl * Scr_ParamError_t)(unsigned int paramIndex, const char*);
|
||||||
extern Scr_ParamError_t Scr_ParamError;
|
extern Scr_ParamError_t Scr_ParamError;
|
||||||
|
|
||||||
|
typedef void(__cdecl * Scr_GetObjectField_t)(unsigned int classnum, int entnum, int offset);
|
||||||
|
extern Scr_GetObjectField_t Scr_GetObjectField;
|
||||||
|
|
||||||
|
typedef int(__cdecl * Scr_SetObjectField_t)(unsigned int classnum, int entnum, int offset);
|
||||||
|
extern Scr_SetObjectField_t Scr_SetObjectField;
|
||||||
|
|
||||||
|
typedef void(__cdecl * Scr_SetClientField_t)(gclient_s* client, int offset);
|
||||||
|
extern Scr_SetClientField_t Scr_SetClientField;
|
||||||
|
|
||||||
|
typedef void(__cdecl * Scr_GetEntityField_t)(int entnum, int offset);
|
||||||
|
extern Scr_GetEntityField_t Scr_GetEntityField;
|
||||||
|
|
||||||
|
typedef void(__cdecl * Scr_AddClassField_t)(unsigned int classnum, const char* name, unsigned int offset);
|
||||||
|
extern Scr_AddClassField_t Scr_AddClassField;
|
||||||
|
|
||||||
typedef gentity_s*(__cdecl * GetPlayerEntity_t)(scr_entref_t entref);
|
typedef gentity_s*(__cdecl * GetPlayerEntity_t)(scr_entref_t entref);
|
||||||
extern GetPlayerEntity_t GetPlayerEntity;
|
extern GetPlayerEntity_t GetPlayerEntity;
|
||||||
|
|
||||||
|
@ -231,6 +231,17 @@ namespace Game
|
|||||||
FL_MOVER_SLIDE = 0x8000000
|
FL_MOVER_SLIDE = 0x8000000
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum ClassNum : unsigned int
|
||||||
|
{
|
||||||
|
CLASS_NUM_ENTITY = 0x0,
|
||||||
|
CLASS_NUM_HUDELEM = 0x1,
|
||||||
|
CLASS_NUM_PATHNODE = 0x2,
|
||||||
|
CLASS_NUM_VEHICLENODE = 0x3,
|
||||||
|
CLASS_NUM_VEHTRACK_SEGMENT = 0x4,
|
||||||
|
CLASS_NUM_FXENTITY = 0x5,
|
||||||
|
CLASS_NUM_COUNT = 0x6,
|
||||||
|
};
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
HITLOC_NONE,
|
HITLOC_NONE,
|
||||||
@ -5705,6 +5716,53 @@ namespace Game
|
|||||||
|
|
||||||
static_assert(sizeof(gentity_s) == 0x274);
|
static_assert(sizeof(gentity_s) == 0x274);
|
||||||
|
|
||||||
|
enum $1C4253065710F064DA9E4D59ED6EC544
|
||||||
|
{
|
||||||
|
ENTFIELD_ENTITY = 0x0,
|
||||||
|
ENTFIELD_SENTIENT = 0x2000,
|
||||||
|
ENTFIELD_ACTOR = 0x4000,
|
||||||
|
ENTFIELD_CLIENT = 0x6000,
|
||||||
|
ENTFIELD_VEHICLE = 0x8000,
|
||||||
|
ENTFIELD_MASK = 0xE000,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum fieldtype_t
|
||||||
|
{
|
||||||
|
F_INT = 0x0,
|
||||||
|
F_SHORT = 0x1,
|
||||||
|
F_BYTE = 0x2,
|
||||||
|
F_FLOAT = 0x3,
|
||||||
|
F_CSTRING = 0x4,
|
||||||
|
F_STRING = 0x5,
|
||||||
|
F_VECTOR = 0x6,
|
||||||
|
F_ENTITY = 0x7,
|
||||||
|
F_ENTHANDLE = 0x8,
|
||||||
|
F_ANGLES_YAW = 0x9,
|
||||||
|
F_OBJECT = 0xA,
|
||||||
|
F_MODEL = 0xB,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ent_field_t
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
int ofs;
|
||||||
|
fieldtype_t type;
|
||||||
|
void(__cdecl * setter)(gentity_s*, int);
|
||||||
|
void(__cdecl * getter)(gentity_s*, int);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct client_fields_s
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
int ofs;
|
||||||
|
fieldtype_t type;
|
||||||
|
void(__cdecl * setter)(gclient_s*, const client_fields_s*);
|
||||||
|
void(__cdecl * getter)(gclient_s*, const client_fields_s*);
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void(__cdecl * ScriptCallbackEnt)(gentity_s*, int);
|
||||||
|
typedef void(__cdecl * ScriptCallbackClient)(gclient_s*, const client_fields_s*);
|
||||||
|
|
||||||
struct lockonFireParms
|
struct lockonFireParms
|
||||||
{
|
{
|
||||||
bool lockon;
|
bool lockon;
|
||||||
|
Loading…
Reference in New Issue
Block a user