diff --git a/src/client/component/gui_entity_list.cpp b/src/client/component/gui_entity_list.cpp index dceb194c..a0151f19 100644 --- a/src/client/component/gui_entity_list.cpp +++ b/src/client/component/gui_entity_list.cpp @@ -21,11 +21,13 @@ namespace entity_list { enum entity_type { - type_any, + entity, actor, spawner, weapon, + vehicle, node, + vehicle_node, count, }; @@ -49,7 +51,7 @@ namespace entity_list struct entity_info_t { unsigned int id; - unsigned int num; + game::scr_entref_t entref; std::unordered_map fields; }; @@ -278,7 +280,7 @@ namespace entity_list }; utils::concurrency::container data_{}; - unsigned int selected_entity{}; + game::scr_entref_t selected_entity{}; bool set_field_window{}; bool selected_fields_window{}; int selected_type = game::SCRIPT_INTEGER; @@ -288,88 +290,60 @@ namespace entity_list std::string field_value_buffer{}; std::string vector_input[3]{}; - bool verify_entity(unsigned int id) - { - const auto type = game::scr_VarGlob->objectVariableValue[id].w.type; - return type == game::SCRIPT_ENTITY; - } - std::optional get_entity_array(const entity_type type, const entity_team team) { - const auto value = scripting::call("getentarray"); - if (!value.is()) + if (type == entity_type::entity) { - return {}; - } - - const auto all = value.as(); - - if (type == entity_type::type_any) - { - return {all}; + return {scripting::call("getentarray").as()}; } if (type == entity_type::actor) { scripting::array result{}; - for (auto i = 0; i < all.size(); i++) + const auto actors = scripting::call("getaiarray").as(); + for (auto i = 0; i < actors.size(); i++) { - const auto raw = all[i].get_raw(); - if (raw.type != game::SCRIPT_OBJECT) - { - continue; - } + const auto entity = actors[i].as(); + const auto team_string = entity.get("team").as(); - if (!verify_entity(raw.u.uintValue)) - { - continue; - } - - const auto entity = all[i].as(); - - const auto classname_value = entity.get("classname"); - if (!classname_value.is()) - { - continue; - } - - const auto team_value = entity.get("team"); - if (!team_value.is()) - { - continue; - } - - const auto classname = classname_value.as(); - const auto team_ = team_value.as(); - if (utils::string::find_lower(classname, "actor_") && - (team == entity_team::team_any || team_ == team_names[team])) + if (team == entity_team::team_any || team_string == team_names[team]) { result.push(entity); } } - return result; + return {result}; } if (type == entity_type::spawner && team == entity_team::team_any) { - return scripting::call("getspawnerarray").as(); + return {scripting::call("getspawnerarray").as()}; } if (type == entity_type::spawner) { - return scripting::call("getspawnerteamarray", {team_names[team]}).as(); + return {scripting::call("getspawnerteamarray", {team_names[team]}).as()}; } if (type == entity_type::weapon) { - return scripting::call("getweaponarray").as(); + return {scripting::call("getweaponarray").as()}; } if (type == entity_type::node) { - return scripting::call("getnodearray").as(); + return {scripting::call("getallnodes").as()}; + } + + if (type == entity_type::vehicle_node) + { + return {scripting::call("getallvehiclenodes").as()}; + } + + if (type == entity_type::vehicle) + { + return {scripting::call("vehicle_getarray").as()}; } return {}; @@ -404,29 +378,18 @@ namespace entity_list data.entity_info.clear(); const auto array = value.value(); + const scripting::entity player{{0, 0}}; for (auto i = 0; i < array.size(); i++) { - const auto raw = array[i].get_raw(); - if (raw.type != game::SCRIPT_OBJECT) - { - continue; - } - - if (!verify_entity(raw.u.uintValue)) - { - continue; - } - const auto entity = array[i].as(); entity_info_t info{}; - info.id = raw.u.uintValue; - info.num = entity.get_entity_reference().entnum; + info.id = entity.get_entity_id(); + info.entref = entity.get_entity_reference(); if (data.filters.filter_by_range) { - const auto player = scripting::call("getentbynum", {0}).as(); const auto distance = scripting::call("distance", {player.get("origin"), entity.get("origin")}).as(); if (distance > data.filters.range) @@ -472,60 +435,60 @@ namespace entity_list }); } - void teleport_to(data_t& data, unsigned int num) + void teleport_to(data_t& data, game::scr_entref_t entref) { - data.tasks.push_back([num]() + data.tasks.push_back([entref]() { try { - const auto entity = scripting::call("getentbynum", {num}).as(); - const auto player = scripting::call("getentbynum", {0}).as(); + const scripting::entity entity{entref}; + const scripting::entity player{{0, 0}}; player.call("setorigin", {entity.get("origin")}); } catch (...) { - gui::notification("Error", utils::string::va("^1error teleporting player to entity num %i!", num)); + gui::notification("Error", utils::string::va("^1error teleporting player to entity num %i!", entref.entnum)); } }); } - void teleport_to_reverse(data_t& data, unsigned int num) + void teleport_to_reverse(data_t& data, game::scr_entref_t entref) { - data.tasks.push_back([num]() + data.tasks.push_back([entref]() { try { - const auto entity = scripting::call("getentbynum", {num}).as(); - const auto player = scripting::call("getentbynum", {0}).as(); + const scripting::entity entity{entref}; + const scripting::entity player{{0, 0}}; entity.set("origin", player.get("origin")); } catch (...) { - gui::notification("Error", utils::string::va("^1error teleporting entity num %i to player!", num)); + gui::notification("Error", utils::string::va("^1error teleporting entity num %i to player!", entref.entnum)); } }); } - void delete_entity(data_t& data, unsigned int num) + void delete_entity(data_t& data, game::scr_entref_t entref) { - data.tasks.push_back([num]() + data.tasks.push_back([entref]() { try { - const auto entity = scripting::call("getentbynum", {num}).as(); + const scripting::entity entity{entref}; entity.call("delete"); } catch (...) { - gui::notification("Error", utils::string::va("^1error deleting entity num %i!", num)); + gui::notification("Error", utils::string::va("^1error deleting entity num %i!", entref.entnum)); } }); } - void set_entity_field(data_t& data, unsigned int num, + void set_entity_field(data_t& data, game::scr_entref_t entref, const std::string& name, const std::string& string_value, int type) { - data.tasks.push_back([num, name, type, string_value, &data]() + data.tasks.push_back([entref, name, type, string_value, &data]() { scripting::script_value value{}; @@ -546,7 +509,7 @@ namespace entity_list try { - const auto entity = scripting::call("getentbynum", {num}).as(); + const scripting::entity entity{entref}; scripting::set_entity_field(entity, name, value); data.force_update = true; } @@ -557,14 +520,14 @@ namespace entity_list }); } - void set_entity_field_vector(data_t& data, unsigned int num, + void set_entity_field_vector(data_t& data, game::scr_entref_t entref, const std::string& name, const scripting::vector& value) { - data.tasks.push_back([num, name, value, &data]() + data.tasks.push_back([entref, name, value, &data]() { try { - const auto entity = scripting::call("getentbynum", {num}).as(); + const scripting::entity entity{entref}; scripting::set_entity_field(entity, name, value); data.force_update = true; } @@ -665,11 +628,13 @@ namespace entity_list { auto result = 0; - result += ImGui::RadioButton("any", reinterpret_cast(&data.filters.type), entity_type::type_any); + result += ImGui::RadioButton("entity", reinterpret_cast(&data.filters.type), entity_type::entity); result += ImGui::RadioButton("actor", reinterpret_cast(&data.filters.type), entity_type::actor); result += ImGui::RadioButton("spawner", reinterpret_cast(&data.filters.type), entity_type::spawner); result += ImGui::RadioButton("weapon", reinterpret_cast(&data.filters.type), entity_type::weapon); - result += ImGui::RadioButton("node", reinterpret_cast(&data.filters.type), entity_type::node); + result += ImGui::RadioButton("vehicle", reinterpret_cast(&data.filters.type), entity_type::vehicle); + result += ImGui::RadioButton("path node", reinterpret_cast(&data.filters.type), entity_type::node); + result += ImGui::RadioButton("vehicle node", reinterpret_cast(&data.filters.type), entity_type::vehicle_node); if (result) { @@ -679,7 +644,8 @@ namespace entity_list ImGui::TreePop(); } - if (ImGui::TreeNode("Entity team")) + if ((data.filters.type == entity_type::actor || data.filters.type == entity_type::spawner) + && ImGui::TreeNode("Entity team")) { auto result = 0; @@ -728,20 +694,33 @@ namespace entity_list for (const auto& info : data.entity_info) { - if (ImGui::TreeNode(utils::string::va("Entity num %i", info.num))) + if (ImGui::TreeNode(utils::string::va("Entity num %i class %i", + info.entref.entnum, info.entref.classnum))) { ImGui::Text("Info"); - const auto num_str = utils::string::va("%i", info.num); + const auto num_str = utils::string::va("%i", info.entref.entnum); + const auto classnum_str = utils::string::va("%i", info.entref.classnum); - ImGui::Text("Entity num"); + ImGui::Text("Entity number"); ImGui::SameLine(); + if (ImGui::Button(num_str)) { gui::copy_to_clipboard(num_str); } - const auto entity_code = utils::string::va("game:getentbynum(%i)", info.num); + ImGui::Text("Entity class"); + ImGui::SameLine(); + + if (ImGui::Button(classnum_str)) + { + gui::copy_to_clipboard(classnum_str); + } + + const auto entity_code = utils::string::va("game:getentbyref(%i, %i)", + info.entref.entnum, info.entref.classnum); + if (ImGui::Button(entity_code)) { gui::copy_to_clipboard(entity_code); @@ -752,24 +731,24 @@ namespace entity_list if (ImGui::Button("Set field")) { set_field_window = true; - selected_entity = info.num; + selected_entity = info.entref; } if (ImGui::Button("Teleport to")) { - teleport_to(data, info.num); + teleport_to(data, info.entref); data.force_update = true; } if (ImGui::Button("Teleport to you")) { - teleport_to_reverse(data, info.num); + teleport_to_reverse(data, info.entref); data.force_update = true; } - if (info.num != 0 && ImGui::Button("Delete")) + if (info.entref.classnum == 0 && info.entref.entnum != 0 && ImGui::Button("Delete")) { - delete_entity(data, info.num); + delete_entity(data, info.entref); data.force_update = true; } @@ -835,7 +814,7 @@ namespace entity_list { if (!game::CL_IsCgameInitialized()) { - selected_entity = 0; + selected_entity = {}; data.entity_info = {}; data.tasks = {}; } diff --git a/src/client/component/scripting.cpp b/src/client/component/scripting.cpp index 61465388..ef5ad4ab 100644 --- a/src/client/component/scripting.cpp +++ b/src/client/component/scripting.cpp @@ -18,6 +18,7 @@ namespace scripting { std::unordered_map> fields_table; std::unordered_map> script_function_table; + utils::concurrency::container shared_table; namespace { @@ -54,14 +55,24 @@ namespace scripting vm_notify_hook.invoke(notify_list_owner_id, string_value, top); } + void clear_shared_table() + { + shared_table.access([](shared_table_t& table) + { + table.clear(); + }); + } + void player_spawn_stub(const game::gentity_s* player) { player_spawn_hook.invoke(player); + clear_shared_table(); lua::engine::start(); } void g_shutdown_game_stub(const int free_scripts) { + clear_shared_table(); lua::engine::stop(); g_shutdown_game_hook.invoke(free_scripts); } diff --git a/src/client/component/scripting.hpp b/src/client/component/scripting.hpp index 1f16a663..5794bff2 100644 --- a/src/client/component/scripting.hpp +++ b/src/client/component/scripting.hpp @@ -1,7 +1,11 @@ #pragma once +#include namespace scripting { + using shared_table_t = std::unordered_map; + extern std::unordered_map> fields_table; extern std::unordered_map> script_function_table; + extern utils::concurrency::container shared_table; } \ No newline at end of file diff --git a/src/client/game/scripting/entity.cpp b/src/client/game/scripting/entity.cpp index b81dea1b..6bbb0ad4 100644 --- a/src/client/game/scripting/entity.cpp +++ b/src/client/game/scripting/entity.cpp @@ -27,7 +27,7 @@ namespace scripting } entity::entity(game::scr_entref_t entref) - : entity(game::FindEntityId(entref.entnum, entref.classnum)) + : entity(game::Scr_GetEntityId(entref.entnum, entref.classnum)) { } diff --git a/src/client/game/scripting/lua/context.cpp b/src/client/game/scripting/lua/context.cpp index 775b73db..8ee85c30 100644 --- a/src/client/game/scripting/lua/context.cpp +++ b/src/client/game/scripting/lua/context.cpp @@ -32,7 +32,7 @@ namespace scripting::lua void setup_entity_type(sol::state& state, event_handler& handler, scheduler& scheduler) { - state["level"] = entity{*game::levelEntityId}; + state["level"] = entity{*::game::levelEntityId}; state["player"] = call("getentbynum", {0}).as(); state["io"]["fileexists"] = utils::io::file_exists; @@ -301,6 +301,13 @@ namespace scripting::lua return result; }; + entity_type["getentref"] = [](const entity& entity) + { + const auto entref = entity.get_entity_reference(); + std::vector returns = {entref.entnum, entref.classnum}; + return sol::as_returns(returns); + }; + struct game { }; @@ -530,6 +537,38 @@ namespace scripting::lua return table; }; + + game_type["sharedset"] = [](const game&, const std::string& key, const std::string& value) + { + scripting::shared_table.access([key, value](scripting::shared_table_t& table) + { + table[key] = value; + }); + }; + + game_type["sharedget"] = [](const game&, const std::string& key) + { + std::string result; + scripting::shared_table.access([key, &result](scripting::shared_table_t& table) + { + result = table[key]; + }); + return result; + }; + + game_type["getentbyref"] = [](const game&, const sol::this_state s, + const unsigned int entnum, const unsigned int classnum) + { + const auto id = ::game::Scr_GetEntityId(entnum, classnum); + if (id) + { + return convert(s, scripting::entity{id}); + } + else + { + return sol::lua_value{s, sol::lua_nil}; + } + }; } } diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 28323b1a..48c6eac3 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -86,6 +86,7 @@ namespace game WEAK symbol Scr_AllocVector{0x5C3220}; WEAK symbol Scr_ClearOutParams{0x5C6E50}; WEAK symbol Scr_GetEntityIdRef{0x5C56C0}; + WEAK symbol Scr_GetEntityId{0x5C5610}; WEAK symbol Scr_SetObjectField{0x512190}; WEAK symbol Scr_NotifyId{0x5C8240}; WEAK symbol Scr_GetSelf{0x5C57C0}; diff --git a/src/client/game/ui_scripting/lua/context.cpp b/src/client/game/ui_scripting/lua/context.cpp index c191c58f..84d44f79 100644 --- a/src/client/game/ui_scripting/lua/context.cpp +++ b/src/client/game/ui_scripting/lua/context.cpp @@ -7,6 +7,7 @@ #include "../execution.hpp" #include "../../../component/ui_scripting.hpp" +#include "../../../component/scripting.hpp" #include "../../../component/command.hpp" #include "component/game_console.hpp" @@ -1006,6 +1007,24 @@ namespace ui_scripting::lua return sol::as_returns(returns); }; + game_type["sharedset"] = [](const game&, const std::string& key, const std::string& value) + { + scripting::shared_table.access([key, value](scripting::shared_table_t& table) + { + table[key] = value; + }); + }; + + game_type["sharedget"] = [](const game&, const std::string& key) + { + std::string result; + scripting::shared_table.access([key, &result](scripting::shared_table_t& table) + { + result = table[key]; + }); + return result; + }; + auto userdata_type = state.new_usertype("userdata_"); userdata_type["new"] = sol::property(