diff --git a/src/client/component/logfile.cpp b/src/client/component/logfile.cpp index 57a13e7..fc3555f 100644 --- a/src/client/component/logfile.cpp +++ b/src/client/component/logfile.cpp @@ -4,13 +4,136 @@ #include "game/scripting/entity.hpp" #include "game/scripting/execution.hpp" +#include "game/scripting/lua/value_conversion.hpp" +#include "game/scripting/lua/error.hpp" #include +#include "logfile.hpp" + namespace logfile { namespace { + utils::hook::detour scr_player_killed_hook; + utils::hook::detour scr_player_damage_hook; + + std::vector player_killed_callbacks; + std::vector player_damage_callbacks; + + sol::lua_value convert_entity(lua_State* state, const game::mp::gentity_s* ent) + { + if (!ent) + { + return {}; + } + + const auto player = scripting::call("getEntByNum", {ent->s.entityNum}); + + return scripting::lua::convert(state, player); + } + + std::string get_weapon_name(unsigned int weapon, bool isAlternate) + { + char output[1024] = { 0 }; + game::BG_GetWeaponNameComplete(weapon, isAlternate, output, 1024); + + return output; + } + + sol::lua_value convert_vector(lua_State* state, const float* vec) + { + if (!vec) + { + return {}; + } + + const auto _vec = scripting::vector(vec); + + return scripting::lua::convert(state, _vec); + } + + std::string convert_mod(const int meansOfDeath) + { + const auto value = reinterpret_cast(0x1409B5360)[meansOfDeath]; + const auto string = game::SL_ConvertToString(*value); + + return string; + } + + void scr_player_killed_stub(game::mp::gentity_s* self, const game::mp::gentity_s* inflictor, game::mp::gentity_s* attacker, int damage, + int meansOfDeath, const unsigned int weapon, bool isAlternate, const float* vDir, const unsigned int hitLoc, int psTimeOffset, int deathAnimDuration) + { + const std::string _hitLoc = reinterpret_cast(0x1409B5400)[hitLoc]; + const auto _mod = convert_mod(meansOfDeath); + + const auto _weapon = get_weapon_name(weapon, isAlternate); + + for (const auto& callback : player_killed_callbacks) + { + const auto state = callback.lua_state(); + + const auto _self = convert_entity(state, self); + const auto _inflictor = convert_entity(state, inflictor); + const auto _attacker = convert_entity(state, attacker); + + const auto _vDir = convert_vector(state, vDir); + + const auto result = callback(_self, _inflictor, _attacker, damage, _mod, _weapon, _vDir, _hitLoc, psTimeOffset, deathAnimDuration); + + scripting::lua::handle_error(result); + + if (result.valid() && result.get_type() == sol::type::number) + { + damage = result.get(); + } + } + + if (damage == 0) + { + return; + } + + scr_player_killed_hook.invoke(self, inflictor, attacker, damage, meansOfDeath, weapon, isAlternate, vDir, hitLoc, psTimeOffset, deathAnimDuration); + } + + void scr_player_damage_stub(game::mp::gentity_s* self, const game::mp::gentity_s* inflictor, game::mp::gentity_s* attacker, int damage, int dflags, + int meansOfDeath, const unsigned int weapon, bool isAlternate, const float* vPoint, const float* vDir, const unsigned int hitLoc, int timeOffset) + { + const std::string _hitLoc = reinterpret_cast(0x1409B5400)[hitLoc]; + const auto _mod = convert_mod(meansOfDeath); + + const auto _weapon = get_weapon_name(weapon, isAlternate); + + for (const auto& callback : player_damage_callbacks) + { + const auto state = callback.lua_state(); + + const auto _self = convert_entity(state, self); + const auto _inflictor = convert_entity(state, inflictor); + const auto _attacker = convert_entity(state, attacker); + + const auto _vPoint = convert_vector(state, vPoint); + const auto _vDir = convert_vector(state, vDir); + + const auto result = callback(_self, _inflictor, _attacker, damage, dflags, _mod, _weapon, _vPoint, _vDir, _hitLoc); + + scripting::lua::handle_error(result); + + if (result.valid() && result.get_type() == sol::type::number) + { + damage = result.get(); + } + } + + if (damage == 0) + { + return; + } + + scr_player_damage_hook.invoke(self, inflictor, attacker, damage, dflags, meansOfDeath, weapon, isAlternate, vPoint, vDir, hitLoc, timeOffset); + } + bool evaluate_say(char* text, game::mp::gentity_s* ent) { auto hidden = false; @@ -39,6 +162,22 @@ namespace logfile } } + void add_player_damage_callback(const sol::protected_function& callback) + { + player_damage_callbacks.push_back(callback); + } + + void add_player_killed_callback(const sol::protected_function& callback) + { + player_killed_callbacks.push_back(callback); + } + + void clear_callbacks() + { + player_damage_callbacks.clear(); + player_killed_callbacks.clear(); + } + const auto say_stub = utils::hook::assemble([](utils::hook::assembler& a) { const auto hidden = a.newLabel(); @@ -73,6 +212,9 @@ namespace logfile } utils::hook::jump(0x1402E99CC, say_stub, true); + + scr_player_damage_hook.create(0x140332150, scr_player_damage_stub); + scr_player_killed_hook.create(0x1403323D0, scr_player_killed_stub); } }; } diff --git a/src/client/component/logfile.hpp b/src/client/component/logfile.hpp new file mode 100644 index 0000000..debf001 --- /dev/null +++ b/src/client/component/logfile.hpp @@ -0,0 +1,8 @@ +#pragma once + +namespace logfile +{ + void add_player_damage_callback(const sol::protected_function& callback); + void add_player_killed_callback(const sol::protected_function& callback); + void clear_callbacks(); +} diff --git a/src/client/game/scripting/lua/context.cpp b/src/client/game/scripting/lua/context.cpp index 262d9d6..ef68991 100644 --- a/src/client/game/scripting/lua/context.cpp +++ b/src/client/game/scripting/lua/context.cpp @@ -7,6 +7,7 @@ #include "../functions.hpp" #include "../../../component/command.hpp" +#include "../../../component/logfile.hpp" #include @@ -248,6 +249,16 @@ namespace scripting::lua { command::execute(command, false); }; + + game_type["onplayerdamage"] = [](const game&, const sol::protected_function& callback) + { + logfile::add_player_damage_callback(callback); + }; + + game_type["onplayerkilled"] = [](const game&, const sol::protected_function& callback) + { + logfile::add_player_killed_callback(callback); + }; } } diff --git a/src/client/game/scripting/lua/engine.cpp b/src/client/game/scripting/lua/engine.cpp index 7a43ed9..37fc1c2 100644 --- a/src/client/game/scripting/lua/engine.cpp +++ b/src/client/game/scripting/lua/engine.cpp @@ -3,6 +3,7 @@ #include "context.hpp" #include "../execution.hpp" +#include "../../../component/logfile.hpp" #include @@ -52,6 +53,7 @@ namespace scripting::lua::engine void stop() { + logfile::clear_callbacks(); get_scripts().clear(); } diff --git a/src/client/game/scripting/lua/value_conversion.cpp b/src/client/game/scripting/lua/value_conversion.cpp index 6cbca60..df5716b 100644 --- a/src/client/game/scripting/lua/value_conversion.cpp +++ b/src/client/game/scripting/lua/value_conversion.cpp @@ -106,7 +106,7 @@ namespace scripting::lua table[sol::metatable_key] = metatable; - return { state, table }; + return {state, table}; } } diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index fb7fb8a..98217c1 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -13,6 +13,8 @@ namespace game WEAK symbol AimAssist_AddToTargetList{0, 0x140001730}; + WEAK symbol BG_GetWeaponNameComplete{0x0, 0x140165580}; + WEAK symbol Com_Error{0x1402F7570, 0x1403CE480}; WEAK symbol Com_Frame_Try_Block_Function{0x1402F7E10, 0x1403CEF30}; WEAK symbol Com_GetCurrentCoDPlayMode{0, 0x1404C9690}; @@ -45,9 +47,9 @@ namespace game WEAK symbol DB_EnumXAssets_FastFile{0x14017D7C0, 0x14026EC10}; WEAK symbol - DB_EnumXAssets_Internal{ 0x14017D830, 0x14026EC80 }; + DB_EnumXAssets_Internal{0x14017D830, 0x14026EC80}; WEAK symbol - DB_FindXAssetEntry{ 0x14017D830, 0x14026F020 }; + DB_FindXAssetEntry{0x14017D830, 0x14026F020}; WEAK symbol DB_GetXAssetName{0x140151C00, 0x140240DD0}; WEAK symbol DB_GetXAssetTypeSize{0x140151C20, 0x140240DF0}; WEAK symbol DB_LoadXAssets{ @@ -219,7 +221,7 @@ namespace game WEAK symbol g_script_error{0x14A1917B0, 0x1487FA0C0}; WEAK symbol g_classMap{0x14080A840, 0x1409BE1B0}; - WEAK symbol scr_VarGlob{ 0x149B1D680, 0x148185F80 }; + WEAK symbol scr_VarGlob{0x149B1D680, 0x148185F80}; WEAK symbol scr_VmPub{0x14A1938C0, 0x1487FC1C0}; WEAK symbol command_whitelist{0x140808EF0, 0x1409B8DC0};