diff --git a/src/game/game.cpp b/src/game/game.cpp index 36ba13c..88abc9c 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -15,8 +15,12 @@ namespace game MSG_ReadData_t MSG_ReadData; + MT_AllocIndex_t MT_AllocIndex; + RemoveRefToValue_t RemoveRefToValue; + Scr_NotifyId_t Scr_NotifyId; + SL_GetStringOfSize_t SL_GetStringOfSize; Sys_ShowConsole_t Sys_ShowConsole; @@ -29,6 +33,7 @@ namespace game short* scrVarGlob; char** scrMemTreePub; + char* scrMemTreeGlob; unsigned int* scr_numParam; unsigned int* scr_numArgs; @@ -65,6 +70,24 @@ namespace game } } + void* MT_Alloc(const int numBytes, const int type) + { + return scrMemTreeGlob + 12 * size_t(MT_AllocIndex(numBytes, type)); + } + + const float* Scr_AllocVector(const float *v) + { + const auto mem = static_cast(MT_Alloc(16, 2)); + *mem = 0; + + const auto array = reinterpret_cast(mem + 1); + array[0] = v[0]; + array[1] = v[1]; + array[2] = v[2]; + + return array; + } + void Scr_ClearOutParams() { const auto num_params = *scr_numParam; @@ -109,7 +132,7 @@ namespace game return *scrMemTreePub + size * stringValue + 4; } - unsigned int SL_GetString(const char *str, const unsigned int user) + unsigned int SL_GetString(const char* str, const unsigned int user) { return SL_GetStringOfSize(str, user, strlen(str) + 1, 7); } @@ -146,8 +169,12 @@ namespace game native::MSG_ReadData = native::MSG_ReadData_t(SELECT_VALUE(0, 0x5592A0, 0)); + native::MT_AllocIndex = native::MT_AllocIndex_t(SELECT_VALUE(0x4B9610, 0x562080, 0x4E6C30)); + native::RemoveRefToValue = native::RemoveRefToValue_t(SELECT_VALUE(0x477EA0, 0x565730, 0x4E8A40)); + native::Scr_NotifyId = native::Scr_NotifyId_t(SELECT_VALUE(0x610980, 0x56B5E0, 0x4EFAA0)); + native::SL_GetStringOfSize = native::SL_GetStringOfSize_t(SELECT_VALUE(0x4E13F0, 0x564650, 0x4E7490)); native::Sys_ShowConsole = native::Sys_ShowConsole_t(SELECT_VALUE(0x470AF0, 0x5CF590, 0)); @@ -160,14 +187,20 @@ namespace game native::scrVarGlob = reinterpret_cast(SELECT_VALUE(0x19AFC80, 0x1E72180, 0x1D3C800)); native::scrMemTreePub = reinterpret_cast(SELECT_VALUE(0x196FB00, 0x1E32000, 0x1C152A4)); + native::scrMemTreeGlob = reinterpret_cast(SELECT_VALUE(0x186DA00, 0x1D6FF00, 0x1C16600)); native::scr_numParam = reinterpret_cast(SELECT_VALUE(0x1BF2598, 0x20B4A98, 0x1F5B098)); native::scr_numArgs = reinterpret_cast(SELECT_VALUE(0x1BF2594, 0x20B4A94, 0x1F5B094)); native::scr_stackPtr = reinterpret_cast(SELECT_VALUE(0x1BF2590, 0x20B4A90, 0x1F5B090)); - native::scr_stackEndPtr = reinterpret_cast(SELECT_VALUE(0x1BF2584, 0x20B4A84, 0x1F5B084)); + native::scr_stackEndPtr = reinterpret_cast( SELECT_VALUE(0x1BF2584, 0x20B4A84, + 0x1F5B084 +)); - native::scr_instanceFunctions = reinterpret_cast(SELECT_VALUE(0x184CDB0, 0x1D4F258, 0x1BF59C8)); - native::scr_globalFunctions = reinterpret_cast(SELECT_VALUE(0x186C68C, 0x1D6EB34, 0x1C152A4)); + native::scr_instanceFunctions = reinterpret_cast( SELECT_VALUE(0x184CDB0, 0x1D4F258, + 0x1BF59C8)); + native::scr_globalFunctions = reinterpret_cast( SELECT_VALUE(0x186C68C, 0x1D6EB34, + 0x1C152A4 +)); native::g_script_error_level = reinterpret_cast(SELECT_VALUE(0x1BEFCFC, 0x20B21FC, 0x1F5B058)); native::g_script_error = reinterpret_cast(SELECT_VALUE(0x1BF1D18, 0x20B4218, 0x1F5A818)); diff --git a/src/game/game.hpp b/src/game/game.hpp index 9bc9917..010aee4 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -12,7 +12,7 @@ namespace game typedef void (*Cmd_AddCommand_t)(const char* cmdName, void (*function)(), cmd_function_t* allocedCmd); extern Cmd_AddCommand_t Cmd_AddCommand; - typedef void (*Com_Error_t)(int code, const char *fmt, ...); + typedef void (*Com_Error_t)(int code, const char* fmt, ...); extern Com_Error_t Com_Error; typedef void (*Conbuf_AppendText_t)(const char* message); @@ -24,10 +24,16 @@ namespace game typedef void (*MSG_ReadData_t)(msg_t* msg, void* data, int len); extern MSG_ReadData_t MSG_ReadData; + typedef void* (*MT_AllocIndex_t)(int numBytes, int type); + extern MT_AllocIndex_t MT_AllocIndex; + typedef void (*RemoveRefToValue_t)(scriptType_e type, VariableUnion u); extern RemoveRefToValue_t RemoveRefToValue; - typedef unsigned int (*SL_GetStringOfSize_t)(const char *str, unsigned int user, unsigned int len, int type); + typedef void (*Scr_NotifyId_t)(unsigned int id, unsigned int stringValue, unsigned int paramcount); + extern Scr_NotifyId_t Scr_NotifyId; + + typedef unsigned int (*SL_GetStringOfSize_t)(const char* str, unsigned int user, unsigned int len, int type); extern SL_GetStringOfSize_t SL_GetStringOfSize; typedef void (*Sys_ShowConsole_t)(); @@ -42,6 +48,7 @@ namespace game extern short* scrVarGlob; extern char** scrMemTreePub; + extern char* scrMemTreeGlob; extern unsigned int* scr_numParam; extern unsigned int* scr_numArgs; @@ -58,12 +65,15 @@ namespace game void AddRefToValue(VariableValue* value); + void* MT_Alloc(int numBytes, int type); + + const float* Scr_AllocVector(const float* v); void Scr_ClearOutParams(); scr_entref_t Scr_GetEntityIdRef(unsigned int id); scr_call_t Scr_GetFunc(unsigned int index); const char* SL_ConvertToString(unsigned int stringValue); - unsigned int SL_GetString(const char *str, unsigned int user); + unsigned int SL_GetString(const char* str, unsigned int user); } bool is_mp(); diff --git a/src/module/scripting.cpp b/src/module/scripting.cpp index 7562e0a..04ef493 100644 --- a/src/module/scripting.cpp +++ b/src/module/scripting.cpp @@ -70,6 +70,11 @@ chaiscript::Boxed_Value scripting::entity::call(const std::string& function, return this->environment_->call(function, this->get_entity_id(), arguments); } +void scripting::entity::notify(const std::string& event, const std::vector& arguments) const +{ + this->environment_->notify(event, this->get_entity_id(), arguments); +} + scripting::variable::variable(game::native::VariableValue value) : value_(value) { game::native::AddRefToValue(&value); @@ -187,12 +192,71 @@ void scripting::initialize_entity() this->chai_->add(chaiscript::user_type(), "entity"); this->chai_->add(chaiscript::constructor(), "entity"); this->chai_->add(chaiscript::constructor(), "entity"); + this->chai_->add(chaiscript::fun(&entity::on_notify), "onNotify"); + this->chai_->add(chaiscript::fun([](const entity& ent, const std::string& event, + const std::function&)>& + callback) + { + return ent.on_notify(event, callback, false); + }), "onNotify"); + this->chai_->add(chaiscript::fun([](entity& lhs, const entity& rhs) -> entity& { return lhs = rhs; }), "="); + this->chai_->add(chaiscript::fun(&entity::notify), "vectorNotify"); + this->chai_->add(chaiscript::fun([](const entity& ent, const std::string& event) + { + return ent.notify(event, {}); + }), "notify"); + + this->chai_->add(chaiscript::fun( + [](const entity& ent, const std::string& event, + const chaiscript::Boxed_Value& a1) + { + return ent.notify(event, {a1}); + }), "notify"); + + this->chai_->add(chaiscript::fun( + [](const entity& ent, const std::string& event, + const chaiscript::Boxed_Value& a1, + const chaiscript::Boxed_Value& a2) + { + return ent.notify(event, {a1, a2}); + }), "notify"); + + this->chai_->add(chaiscript::fun( + [](const entity& ent, const std::string& event, + const chaiscript::Boxed_Value& a1, + const chaiscript::Boxed_Value& a2, + const chaiscript::Boxed_Value& a3) + { + return ent.notify(event, {a1, a2, a3}); + }), "notify"); + + this->chai_->add(chaiscript::fun( + [](const entity& ent, const std::string& event, + const chaiscript::Boxed_Value& a1, + const chaiscript::Boxed_Value& a2, + const chaiscript::Boxed_Value& a3, + const chaiscript::Boxed_Value& a4) + { + return ent.notify(event, {a1, a2, a3, a4}); + }), "notify"); + + this->chai_->add(chaiscript::fun( + [](const entity& ent, const std::string& event, + const chaiscript::Boxed_Value& a1, + const chaiscript::Boxed_Value& a2, + const chaiscript::Boxed_Value& a3, + const chaiscript::Boxed_Value& a4, + const chaiscript::Boxed_Value& a5) + { + return ent.notify(event, {a1, a2, a3, a4, a5}); + }), "notify"); + this->chai_->add(chaiscript::fun(&entity::call), "vectorCall"); this->chai_->add(chaiscript::fun([](const entity& ent, const std::string& function) { @@ -284,6 +348,15 @@ chaiscript::Boxed_Value scripting::make_boxed(const game::native::VariableValue { return chaiscript::var(entity(this, value.u.entityId)); } + else if (value.type == game::native::SCRIPT_VECTOR) + { + std::vector values; + values.push_back(value.u.vectorValue[0]); + values.push_back(value.u.vectorValue[1]); + values.push_back(value.u.vectorValue[2]); + + return chaiscript::var(values); + } return {}; } @@ -338,6 +411,39 @@ void scripting::stop_execution() } } +void scripting::notify(const std::string& event, const unsigned int entity_id, + std::vector arguments) +{ + const auto old_args = *game::native::scr_numArgs; + const auto old_params = *game::native::scr_numParam; + const auto old_stack_ptr = *game::native::scr_stackPtr; + const auto old_stack_end_ptr = *game::native::scr_stackEndPtr; + + game::native::VariableValue stack[512]; + *game::native::scr_stackPtr = stack; + *game::native::scr_stackEndPtr = &stack[ARRAYSIZE(stack) - 1]; + *game::native::scr_numArgs = 0; + *game::native::scr_numParam = 0; + + const auto cleanup = gsl::finally([=]() + { + game::native::Scr_ClearOutParams(); + *game::native::scr_numArgs = old_args; + *game::native::scr_numParam = old_params; + *game::native::scr_stackPtr = old_stack_ptr; + *game::native::scr_stackEndPtr = old_stack_end_ptr; + }); + + std::reverse(arguments.begin(), arguments.end()); + for (const auto& argument : arguments) + { + this->push_param(argument); + } + + const auto event_id = game::native::SL_GetString(event.data(), 0); + game::native::Scr_NotifyId(entity_id, event_id, *game::native::scr_numArgs); +} + chaiscript::Boxed_Value scripting::call(const std::string& function, const unsigned int entity_id, std::vector arguments) { @@ -379,9 +485,6 @@ chaiscript::Boxed_Value scripting::call(const std::string& function, const unsig this->push_param(argument); } - *game::native::scr_numParam = *game::native::scr_numArgs; - *game::native::scr_numArgs = 0; - if (!call_safe(function_ptr, entity)) { throw std::runtime_error("Error executing function '" + function + "'"); @@ -477,6 +580,41 @@ void scripting::push_param(const chaiscript::Boxed_Value& value) const value_ptr->type = game::native::SCRIPT_STRING; value_ptr->u.stringValue = game::native::SL_GetString(real_value.data(), 0); } + else if (value.get_type_info() == typeid(std::vector)) + { + float values[3]; + const auto real_value = this->chai_->boxed_cast>(value); + if (real_value.size() != 3) + { + throw std::runtime_error("Invalid vector length. Size must be exactly 3"); + } + + const auto unbox_float = [&real_value, this](size_t index) -> float + { + const auto value = real_value[index]; + if (value.get_type_info() == typeid(float)) + { + return this->chai_->boxed_cast(value); + } + else if (value.get_type_info() == typeid(double)) + { + return float(this->chai_->boxed_cast(value)); + } + else if (value.get_type_info() == typeid(int)) + { + return float(this->chai_->boxed_cast(value)); + } + + throw std::runtime_error("Vector element at index " + std::to_string(index) + " is not a number"); + }; + + values[0] = unbox_float(0); + values[1] = unbox_float(1); + values[2] = unbox_float(2); + + value_ptr->type = game::native::SCRIPT_VECTOR; + value_ptr->u.vectorValue = game::native::Scr_AllocVector(values); + } else { throw std::runtime_error("Unable to unbox value of type '" + value.get_type_info().bare_name() + "'"); diff --git a/src/module/scripting.hpp b/src/module/scripting.hpp index 9e04db1..31ceb2c 100644 --- a/src/module/scripting.hpp +++ b/src/module/scripting.hpp @@ -23,6 +23,7 @@ public: game::native::scr_entref_t get_entity_reference() const; chaiscript::Boxed_Value call(const std::string& function, const std::vector& arguments) const; + void notify(const std::string& event, const std::vector& arguments) const; private: scripting* environment_; @@ -81,6 +82,8 @@ private: static void start_execution(); static void stop_execution(); + void notify(const std::string& event, unsigned int entity_id, std::vector arguments); + void push_param(const chaiscript::Boxed_Value& value) const; chaiscript::Boxed_Value get_return_value(); chaiscript::Boxed_Value call(const std::string& function, unsigned int entity_id, std::vector arguments);