diff --git a/src/client/component/ui_scripting.cpp b/src/client/component/ui_scripting.cpp index 613dedb0..06ad4421 100644 --- a/src/client/component/ui_scripting.cpp +++ b/src/client/component/ui_scripting.cpp @@ -10,7 +10,6 @@ #include "ui_scripting.hpp" #include "game/ui_scripting/lua/engine.hpp" -#include "game/ui_scripting/lua/context.hpp" #include @@ -46,7 +45,7 @@ namespace ui_scripting const std::string name = params.get(1); scheduler::once([name]() { - ui_scripting::lua::open_menu(name); + ui_scripting::lua::engine::open_menu(name); }, scheduler::pipeline::renderer); }); @@ -55,7 +54,7 @@ namespace ui_scripting const std::string name = params.get(1); scheduler::once([name]() { - ui_scripting::lua::close_menu(name); + ui_scripting::lua::engine::close_menu(name); }, scheduler::pipeline::renderer); }); } diff --git a/src/client/game/ui_scripting/element.cpp b/src/client/game/ui_scripting/element.cpp index 0f620856..826d9281 100644 --- a/src/client/game/ui_scripting/element.cpp +++ b/src/client/game/ui_scripting/element.cpp @@ -148,11 +148,25 @@ namespace ui_scripting if (font_map.find(lowercase) == font_map.end()) { - this->font = font_map["default"]; + this->font = "default"; } else { - this->font = font_map[lowercase]; + this->font = lowercase; + } + } + + void element::set_font(const std::string& _font) + { + const auto lowercase = utils::string::to_lower(_font); + + if (font_map.find(lowercase) == font_map.end()) + { + this->font = "default"; + } + else + { + this->font = lowercase; } } @@ -303,7 +317,8 @@ namespace ui_scripting if (!this->text.empty()) { - const auto _font = game::R_RegisterFont(this->font.data(), relative(this->fontsize)); + const auto fontname = font_map[this->font]; + const auto _font = game::R_RegisterFont(fontname.data(), relative(this->fontsize)); const auto text_width = game::R_TextWidth(this->text.data(), 0x7FFFFFFF, _font); auto _horzalign = get_align_value(this->horzalign, (float)text_width, relative(this->w)); diff --git a/src/client/game/ui_scripting/element.hpp b/src/client/game/ui_scripting/element.hpp index f2402aa9..1820e432 100644 --- a/src/client/game/ui_scripting/element.hpp +++ b/src/client/game/ui_scripting/element.hpp @@ -19,6 +19,7 @@ namespace ui_scripting void set_vertalign(const std::string& value); void set_text(const std::string& text); + void set_font(const std::string& _font); void set_font(const std::string& _font, const int _fontsize); void set_color(float r, float g, float b, float a); void set_text_offset(float x, float y); diff --git a/src/client/game/ui_scripting/lua/context.cpp b/src/client/game/ui_scripting/lua/context.cpp index d5f1f9eb..6ee57f9a 100644 --- a/src/client/game/ui_scripting/lua/context.cpp +++ b/src/client/game/ui_scripting/lua/context.cpp @@ -9,36 +9,18 @@ #include "component/scheduler.hpp" #include +#include namespace ui_scripting::lua { + std::unordered_map menus; + std::vector elements; + element ui_element; + int mouse[2]; + namespace { - std::unordered_map menus; - std::vector elements; - element ui_element; - float screen_max[2]; - - void check_resize() - { - screen_max[0] = game::ScrPlace_GetViewPlacement()->realViewportSize[0]; - screen_max[1] = game::ScrPlace_GetViewPlacement()->realViewportSize[1]; - } - - int relative_mouse(int value) - { - return (int)(((float)value / screen_max[0]) * 1920.f); - } - - int relative(int value) - { - return (int)(((float)value / 1920.f) * screen_max[0]); - } - - float relative(float value) - { - return (value / 1920.f) * screen_max[0]; - } + const auto animation_script = utils::nt::load_resource(LUA_ANIMATION_SCRIPT); scripting::script_value convert(const sol::lua_value& value) { @@ -79,286 +61,6 @@ namespace ui_scripting::lua return {}; } - bool point_in_rect(int px, int py, int x, int y, int w, int h) - { - return (px > x && px < x + w && py > y && py < y + h); - } - - bool is_menu_visible(const menu& menu) - { - return menu.visible || (menu.type == menu_type::overlay && game::Menu_IsMenuOpenAndVisible(0, menu.overlay_menu.data())); - } - - void render_menus() - { - check_resize(); - - for (const auto& menu : menus) - { - if (is_menu_visible(menu.second)) - { - menu.second.render(); - } - } - } - - std::vector elements_in_point(int x, int y) - { - std::vector result; - - for (const auto& menu : menus) - { - if (!is_menu_visible(menu.second)) - { - continue; - } - - for (const auto& child : menu.second.children) - { - const auto in_rect = point_in_rect( - x, y, - (int)child->x, - (int)child->y, - (int)child->w + (int)child->border_width[1] + (int)child->border_width[3], - (int)child->h + (int)child->border_width[0] + (int)child->border_width[2] - ); - - if (in_rect) - { - result.push_back(child); - } - } - } - - return result; - } - - int mouse[2]; - - void handle_key_event(sol::state& state, event_handler& handler, const int key, const int down) - { - const auto _elements = elements_in_point(mouse[0], mouse[1]); - - switch (key) - { - case game::K_MOUSE2: - case game::K_MOUSE1: - { - const auto click_name = key == game::K_MOUSE1 - ? "click" - : "rightclick"; - - const auto key_name = key == game::K_MOUSE1 - ? "mouse" - : "rightmouse"; - - { - event main_event; - main_event.element = &ui_element; - main_event.name = utils::string::va("%s%s", key_name, down ? "down" : "up"); - main_event.arguments = - { - {state, mouse[0]}, - {state, mouse[1]}, - }; - - handler.dispatch(main_event); - - for (const auto& element : _elements) - { - event event; - event.element = element; - event.name = utils::string::va("%s%s", key_name, down ? "down" : "up"); - event.arguments = - { - {state, mouse[0]}, - {state, mouse[1]}, - }; - - handler.dispatch(event); - } - } - - if (!down) - { - event main_event; - main_event.element = &ui_element; - main_event.name = click_name; - main_event.arguments = - { - {state, mouse[0]}, - {state, mouse[1]}, - }; - - handler.dispatch(main_event); - - for (const auto& element : _elements) - { - event event; - event.element = element; - event.name = click_name; - event.arguments = - { - {state, mouse[0]}, - {state, mouse[1]}, - }; - - handler.dispatch(event); - } - } - - break; - } - case game::K_MWHEELUP: - case game::K_MWHEELDOWN: - { - const auto key_name = key == game::K_MWHEELUP - ? "scrollup" - : "scrolldown"; - - if (!down) - { - break; - } - - { - event main_event; - main_event.element = &ui_element; - main_event.name = key_name; - main_event.arguments = - { - {state, mouse[0]}, - {state, mouse[1]}, - }; - - handler.dispatch(main_event); - - for (const auto& element : _elements) - { - event event; - event.element = element; - event.name = key_name; - event.arguments = - { - {state, mouse[0]}, - {state, mouse[1]}, - }; - - handler.dispatch(event); - } - } - - break; - } - default: - { - event event; - event.element = &ui_element; - event.name = down - ? "keydown" - : "keyup"; - event.arguments = - { - {state, key}, - }; - - handler.dispatch(event); - - break; - } - } - } - - void handle_char_event(sol::state& state, event_handler& handler, const int key) - { - std::string key_str = {(char)key}; - event event; - event.element = &ui_element; - event.name = "keypress"; - event.arguments = - { - {state, key_str}, - }; - - handler.dispatch(event); - } - - std::vector previous_elements; - void handle_mousemove_event(sol::state& state, event_handler& handler, const int x, const int y) - { - mouse[0] = x; - mouse[1] = y; - - { - event event; - event.element = &ui_element; - event.name = "mousemove"; - event.arguments = - { - {state, x}, - {state, y}, - }; - - handler.dispatch(event); - } - - const auto _elements = elements_in_point(x, y); - for (const auto& element : _elements) - { - event event; - event.element = element; - event.name = "mouseover"; - - handler.dispatch(event); - } - - for (const auto& element : previous_elements) - { - auto found = false; - - for (const auto& _element : _elements) - { - if (element == _element) - { - found = true; - } - } - - if (!found) - { - event event; - event.element = element; - event.name = "mouseleave"; - - handler.dispatch(event); - } - } - - for (const auto& element : _elements) - { - auto found = false; - - for (const auto& _element : previous_elements) - { - if (element == _element) - { - found = true; - } - } - - if (!found) - { - event event; - event.element = element; - event.name = "mouseenter"; - - handler.dispatch(event); - } - } - - previous_elements = _elements; - } - bool valid_dvar_name(const std::string& name) { for (const auto c : name) @@ -372,22 +74,9 @@ namespace ui_scripting::lua return true; } - void clear_menus() - { - menus.clear(); - for (const auto element : elements) - { - delete element; - } - - elements.clear(); - } - void setup_types(sol::state& state, event_handler& handler, scheduler& scheduler) { - clear_menus(); - - auto vector_type = state.new_usertype("scripting::vector", sol::constructors()); + auto vector_type = state.new_usertype("vector", sol::constructors()); vector_type["x"] = sol::property(&scripting::vector::get_x, &scripting::vector::set_x); vector_type["y"] = sol::property(&scripting::vector::get_y, &scripting::vector::set_y); vector_type["z"] = sol::property(&scripting::vector::get_z, &scripting::vector::set_z); @@ -398,78 +87,78 @@ namespace ui_scripting::lua vector_type[sol::meta_function::addition] = sol::overload( [](const scripting::vector& a, const scripting::vector& b) - { - return scripting::vector( - a.get_x() + b.get_x(), - a.get_y() + b.get_y(), - a.get_z() + b.get_z() - ); - }, + { + return scripting::vector( + a.get_x() + b.get_x(), + a.get_y() + b.get_y(), + a.get_z() + b.get_z() + ); + }, [](const scripting::vector& a, const int value) - { - return scripting::vector( - a.get_x() + value, - a.get_y() + value, - a.get_z() + value - ); - } + { + return scripting::vector( + a.get_x() + value, + a.get_y() + value, + a.get_z() + value + ); + } ); vector_type[sol::meta_function::subtraction] = sol::overload( [](const scripting::vector& a, const scripting::vector& b) - { - return scripting::vector( - a.get_x() - b.get_x(), - a.get_y() - b.get_y(), - a.get_z() - b.get_z() - ); - }, + { + return scripting::vector( + a.get_x() - b.get_x(), + a.get_y() - b.get_y(), + a.get_z() - b.get_z() + ); + }, [](const scripting::vector& a, const int value) - { - return scripting::vector( - a.get_x() - value, - a.get_y() - value, - a.get_z() - value - ); - } + { + return scripting::vector( + a.get_x() - value, + a.get_y() - value, + a.get_z() - value + ); + } ); vector_type[sol::meta_function::multiplication] = sol::overload( [](const scripting::vector& a, const scripting::vector& b) - { - return scripting::vector( - a.get_x() * b.get_x(), - a.get_y() * b.get_y(), - a.get_z() * b.get_z() - ); - }, + { + return scripting::vector( + a.get_x() * b.get_x(), + a.get_y() * b.get_y(), + a.get_z() * b.get_z() + ); + }, [](const scripting::vector& a, const int value) - { - return scripting::vector( - a.get_x() * value, - a.get_y() * value, - a.get_z() * value - ); - } + { + return scripting::vector( + a.get_x() * value, + a.get_y() * value, + a.get_z() * value + ); + } ); vector_type[sol::meta_function::division] = sol::overload( [](const scripting::vector& a, const scripting::vector& b) - { - return scripting::vector( - a.get_x() / b.get_x(), - a.get_y() / b.get_y(), - a.get_z() / b.get_z() - ); - }, + { + return scripting::vector( + a.get_x() / b.get_x(), + a.get_y() / b.get_y(), + a.get_z() / b.get_z() + ); + }, [](const scripting::vector& a, const int value) - { - return scripting::vector( - a.get_x() / value, - a.get_y() / value, - a.get_z() / value - ); - } + { + return scripting::vector( + a.get_x() / value, + a.get_y() / value, + a.get_z() / value + ); + } ); vector_type[sol::meta_function::equal_to] = [](const scripting::vector& a, const scripting::vector& b) @@ -498,8 +187,11 @@ namespace ui_scripting::lua element_type["setvertalign"] = &element::set_vertalign; element_type["sethorzalign"] = &element::set_horzalign; - element_type["setrect"] =&element::set_rect; - element_type["setfont"] = &element::set_font; + element_type["setrect"] = &element::set_rect; + element_type["setfont"] = sol::overload( + static_cast(&element::set_font), + static_cast(&element::set_font) + ); element_type["settext"] = &element::set_text; element_type["setmaterial"] = &element::set_material; element_type["setcolor"] = &element::set_color; @@ -537,6 +229,148 @@ namespace ui_scripting::lua return rect; }; + element_type["x"] = sol::property( + [](element& element) + { + return element.x; + }, + [](element& element, float x) + { + element.x = x; + } + ); + + element_type["y"] = sol::property( + [](element& element) + { + return element.y; + }, + [](element& element, float y) + { + element.y = y; + } + ); + + element_type["w"] = sol::property( + [](element& element) + { + return element.w; + }, + [](element& element, float w) + { + element.w = w; + } + ); + + element_type["h"] = sol::property( + [](element& element) + { + return element.h; + }, + [](element& element, float h) + { + element.h = h; + } + ); + + element_type["color"] = sol::property( + [](element& element, const sol::this_state s) + { + auto color = sol::table::create(s.lua_state()); + color["r"] = element.color[0]; + color["g"] = element.color[1]; + color["b"] = element.color[2]; + color["a"] = element.color[3]; + return color; + }, + [](element& element, const sol::lua_table color) + { + element.color[0] = color["r"].get_type() == sol::type::number ? color["r"].get() : 0.f; + element.color[1] = color["g"].get_type() == sol::type::number ? color["g"].get() : 0.f; + element.color[2] = color["b"].get_type() == sol::type::number ? color["b"].get() : 0.f; + element.color[3] = color["a"].get_type() == sol::type::number ? color["a"].get() : 0.f; + } + ); + + element_type["backcolor"] = sol::property( + [](element& element, const sol::this_state s) + { + auto color = sol::table::create(s.lua_state()); + color["r"] = element.background_color[0]; + color["g"] = element.background_color[1]; + color["b"] = element.background_color[2]; + color["a"] = element.background_color[3]; + return color; + }, + [](element& element, const sol::lua_table color) + { + element.background_color[0] = color["r"].get_type() == sol::type::number ? color["r"].get() : 0.f; + element.background_color[1] = color["g"].get_type() == sol::type::number ? color["g"].get() : 0.f; + element.background_color[2] = color["b"].get_type() == sol::type::number ? color["b"].get() : 0.f; + element.background_color[3] = color["a"].get_type() == sol::type::number ? color["a"].get() : 0.f; + } + ); + + element_type["bordercolor"] = sol::property( + [](element& element, const sol::this_state s) + { + auto color = sol::table::create(s.lua_state()); + color["r"] = element.border_color[0]; + color["g"] = element.border_color[1]; + color["b"] = element.border_color[2]; + color["a"] = element.border_color[3]; + return color; + }, + [](element& element, const sol::lua_table color) + { + element.border_color[0] = color["r"].get_type() == sol::type::number ? color["r"].get() : 0.f; + element.border_color[1] = color["g"].get_type() == sol::type::number ? color["g"].get() : 0.f; + element.border_color[2] = color["b"].get_type() == sol::type::number ? color["b"].get() : 0.f; + element.border_color[3] = color["a"].get_type() == sol::type::number ? color["a"].get() : 0.f; + } + ); + + element_type["borderwidth"] = sol::property( + [](element& element, const sol::this_state s) + { + auto color = sol::table::create(s.lua_state()); + color["top"] = element.border_width[0]; + color["right"] = element.border_width[1]; + color["bottom"] = element.border_width[2]; + color["left"] = element.border_width[3]; + return color; + }, + [](element& element, const sol::lua_table color) + { + element.border_width[0] = color["top"].get_type() == sol::type::number ? color["top"].get() : 0.f; + element.border_width[1] = color["right"].get_type() == sol::type::number ? color["right"].get() : element.border_width[1]; + element.border_width[2] = color["bottom"].get_type() == sol::type::number ? color["bottom"].get() : element.border_width[2]; + element.border_width[3] = color["left"].get_type() == sol::type::number ? color["left"].get() : element.border_width[3]; + } + ); + + element_type["font"] = sol::property( + [](element& element) + { + return element.font; + }, + [](element& element, const std::string& font) + { + element.set_font(font); + } + ); + + element_type["fontsize"] = sol::property( + [](element& element) + { + return element.fontsize; + }, + [](element& element, float fontsize) + { + element.fontsize = (int)fontsize; + } + ); + element_type["onnotifyonce"] = [&handler](element& element, const std::string& event, const event_callback& callback) { @@ -549,6 +383,29 @@ namespace ui_scripting::lua return handler.add_event_listener(std::move(listener)); }; + element_type["notify"] = [&handler](element& element, const sol::this_state s, const std::string& _event, + sol::variadic_args va) + { + event event; + event.element = &element; + event.name = _event; + + for (auto arg : va) + { + if (arg.get_type() == sol::type::number) + { + event.arguments.push_back(arg.as()); + } + + if (arg.get_type() == sol::type::string) + { + event.arguments.push_back(arg.as()); + } + } + + handler.dispatch(event); + }; + auto menu_type = state.new_usertype("menu"); menu_type["addchild"] = [](const sol::this_state s, menu& menu, element& element) @@ -573,6 +430,12 @@ namespace ui_scripting::lua auto game_type = state.new_usertype("game_"); state["game"] = game(); + game_type["time"] = []() + { + const auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); + return now.count(); + }; + game_type["newmenu"] = [](const sol::lua_value&, const std::string& name) { menus[name] = {}; @@ -621,6 +484,18 @@ namespace ui_scripting::lua return scheduler.add(callback, 0, false); }; + game_type["ontimeout"] = [&scheduler](const game&, const sol::protected_function& callback, + const long long milliseconds) + { + return scheduler.add(callback, milliseconds, true); + }; + + game_type["oninterval"] = [&scheduler](const game&, const sol::protected_function& callback, + const long long milliseconds) + { + return scheduler.add(callback, milliseconds, false); + }; + game_type["onnotify"] = [&handler](const game&, const std::string& event, const event_callback& callback) { @@ -836,29 +711,11 @@ namespace ui_scripting::lua ::game::g_entities[0].client->velocity[1] = velocity.get_y(); ::game::g_entities[0].client->velocity[2] = velocity.get_z(); }; + + state.script(animation_script); } } - void open_menu(const std::string& name) - { - if (menus.find(name) == menus.end()) - { - return; - } - - menus[name].open(); - } - - void close_menu(const std::string& name) - { - if (menus.find(name) == menus.end()) - { - return; - } - - menus[name].close(); - } - context::context(std::string folder) : folder_(std::move(folder)) , scheduler_(state_) @@ -905,28 +762,14 @@ namespace ui_scripting::lua void context::run_frame() { - render_menus(); this->scheduler_.run_frame(); this->state_.collect_garbage(); } - void context::ui_event(const std::string& type, const std::vector& arguments) + void context::notify(const event& e) { - if (type == "key") - { - handle_key_event(this->state_, this->event_handler_, arguments[0], arguments[1]); - } - - if (type == "char") - { - handle_char_event(this->state_, this->event_handler_, arguments[0]); - } - - if (type == "mousemove") - { - handle_mousemove_event(this->state_, this->event_handler_, - relative_mouse(arguments[0]), relative_mouse(arguments[1])); - } + this->scheduler_.dispatch(e); + this->event_handler_.dispatch(e); } void context::load_script(const std::string& script) diff --git a/src/client/game/ui_scripting/lua/context.hpp b/src/client/game/ui_scripting/lua/context.hpp index b2a88f59..7a45e4f3 100644 --- a/src/client/game/ui_scripting/lua/context.hpp +++ b/src/client/game/ui_scripting/lua/context.hpp @@ -14,8 +14,10 @@ namespace ui_scripting::lua { - void open_menu(const std::string& name); - void close_menu(const std::string& name); + extern std::unordered_map menus; + extern std::vector elements; + extern element ui_element; + extern int mouse[2]; class context { @@ -30,7 +32,7 @@ namespace ui_scripting::lua context& operator=(const context&) = delete; void run_frame(); - void ui_event(const std::string&, const std::vector&); + void notify(const event& e); private: sol::state state_{}; diff --git a/src/client/game/ui_scripting/lua/engine.cpp b/src/client/game/ui_scripting/lua/engine.cpp index 6ce9656a..cdc60b94 100644 --- a/src/client/game/ui_scripting/lua/engine.cpp +++ b/src/client/game/ui_scripting/lua/engine.cpp @@ -7,8 +7,284 @@ namespace ui_scripting::lua::engine { + void notify(const event& e); + namespace { + float screen_max[2]; + + void check_resize() + { + screen_max[0] = game::ScrPlace_GetViewPlacement()->realViewportSize[0]; + screen_max[1] = game::ScrPlace_GetViewPlacement()->realViewportSize[1]; + } + + int relative_mouse(int value) + { + return (int)(((float)value / screen_max[0]) * 1920.f); + } + + int relative(int value) + { + return (int)(((float)value / 1920.f) * screen_max[0]); + } + + float relative(float value) + { + return (value / 1920.f) * screen_max[0]; + } + + bool point_in_rect(int px, int py, int x, int y, int w, int h) + { + return (px > x && px < x + w && py > y && py < y + h); + } + + bool is_menu_visible(const menu& menu) + { + return menu.visible || (menu.type == menu_type::overlay && game::Menu_IsMenuOpenAndVisible(0, menu.overlay_menu.data())); + } + + std::vector elements_in_point(int x, int y) + { + std::vector result; + + for (const auto& menu : menus) + { + if (!is_menu_visible(menu.second)) + { + continue; + } + + for (const auto& child : menu.second.children) + { + const auto in_rect = point_in_rect( + x, y, + (int)child->x, + (int)child->y, + (int)child->w + (int)child->border_width[1] + (int)child->border_width[3], + (int)child->h + (int)child->border_width[0] + (int)child->border_width[2] + ); + + if (in_rect) + { + result.push_back(child); + } + } + } + + return result; + } + + void handle_key_event(const int key, const int down) + { + const auto _elements = elements_in_point(mouse[0], mouse[1]); + + switch (key) + { + case game::K_MOUSE2: + case game::K_MOUSE1: + { + const auto click_name = key == game::K_MOUSE1 + ? "click" + : "rightclick"; + + const auto key_name = key == game::K_MOUSE1 + ? "mouse" + : "rightmouse"; + + { + event main_event; + main_event.element = &ui_element; + main_event.name = utils::string::va("%s%s", key_name, down ? "down" : "up"); + main_event.arguments = + { + mouse[0], + mouse[1], + }; + + engine::notify(main_event); + + for (const auto& element : _elements) + { + event event; + event.element = element; + event.name = utils::string::va("%s%s", key_name, down ? "down" : "up"); + event.arguments = + { + mouse[0], + mouse[1], + }; + + engine::notify(event); + } + } + + if (!down) + { + event main_event; + main_event.element = &ui_element; + main_event.name = click_name; + main_event.arguments = + { + mouse[0], + mouse[1], + }; + + engine::notify(main_event); + + for (const auto& element : _elements) + { + event event; + event.element = element; + event.name = click_name; + event.arguments = + { + mouse[0], + mouse[1], + }; + + engine::notify(event); + } + } + + break; + } + case game::K_MWHEELUP: + case game::K_MWHEELDOWN: + { + const auto key_name = key == game::K_MWHEELUP + ? "scrollup" + : "scrolldown"; + + if (!down) + { + break; + } + + { + event main_event; + main_event.element = &ui_element; + main_event.name = key_name; + main_event.arguments = + { + mouse[0], + mouse[1], + }; + + engine::notify(main_event); + + for (const auto& element : _elements) + { + event event; + event.element = element; + event.name = key_name; + event.arguments = {mouse[0], mouse[1]}; + + engine::notify(event); + } + } + + break; + } + default: + { + event event; + event.element = &ui_element; + event.name = down + ? "keydown" + : "keyup"; + event.arguments = {key}; + + notify(event); + + break; + } + } + } + + void handle_char_event(const int key) + { + std::string key_str = {(char)key}; + event event; + event.element = &ui_element; + event.name = "keypress"; + event.arguments = {key_str}; + + engine::notify(event); + } + + std::vector previous_elements; + void handle_mousemove_event(const int x, const int y) + { + mouse[0] = x; + mouse[1] = y; + + { + event event; + event.element = &ui_element; + event.name = "mousemove"; + event.arguments = {x, y}; + + engine::notify(event); + } + + const auto _elements = elements_in_point(x, y); + for (const auto& element : _elements) + { + event event; + event.element = element; + event.name = "mouseover"; + + engine::notify(event); + } + + for (const auto& element : previous_elements) + { + auto found = false; + + for (const auto& _element : _elements) + { + if (element == _element) + { + found = true; + } + } + + if (!found) + { + event event; + event.element = element; + event.name = "mouseleave"; + + engine::notify(event); + } + } + + for (const auto& element : _elements) + { + auto found = false; + + for (const auto& _element : previous_elements) + { + if (element == _element) + { + found = true; + } + } + + if (!found) + { + event event; + event.element = element; + event.name = "mouseenter"; + + engine::notify(event); + } + } + + previous_elements = _elements; + } + auto& get_scripts() { static std::vector> scripts{}; @@ -34,29 +310,96 @@ namespace ui_scripting::lua::engine } } } + + void render_menus() + { + check_resize(); + + for (const auto& menu : menus) + { + if (is_menu_visible(menu.second)) + { + menu.second.render(); + } + } + } + + void clear_menus() + { + menus.clear(); + for (const auto element : elements) + { + delete element; + } + + elements.clear(); + } + } + + void open_menu(const std::string& name) + { + if (menus.find(name) == menus.end()) + { + return; + } + + menus[name].open(); + } + + void close_menu(const std::string& name) + { + if (menus.find(name) == menus.end()) + { + return; + } + + menus[name].close(); } void start() { + clear_menus(); get_scripts().clear(); load_scripts(); } void stop() { + clear_menus(); get_scripts().clear(); } void ui_event(const std::string& type, const std::vector& arguments) + { + if (type == "key") + { + handle_key_event(arguments[0], arguments[1]); + } + + if (type == "char") + { + handle_char_event(arguments[0]); + } + + if (type == "mousemove") + { + handle_mousemove_event(relative_mouse(arguments[0]), relative_mouse(arguments[1])); + } + } + + void notify(const event& e) { for (auto& script : get_scripts()) { - script->ui_event(type, arguments); + script->notify(e); } } void run_frame() { + check_resize(); + render_menus(); + for (auto& script : get_scripts()) { script->run_frame(); diff --git a/src/client/game/ui_scripting/lua/engine.hpp b/src/client/game/ui_scripting/lua/engine.hpp index 0d33fccf..f8f6b032 100644 --- a/src/client/game/ui_scripting/lua/engine.hpp +++ b/src/client/game/ui_scripting/lua/engine.hpp @@ -5,6 +5,9 @@ namespace ui_scripting::lua::engine void start(); void stop(); + void close_menu(const std::string& name); + void open_menu(const std::string& name); + void ui_event(const std::string&, const std::vector&); void run_frame(); } diff --git a/src/client/game/ui_scripting/lua/event.hpp b/src/client/game/ui_scripting/lua/event.hpp index a90fa6d8..428a1eda 100644 --- a/src/client/game/ui_scripting/lua/event.hpp +++ b/src/client/game/ui_scripting/lua/event.hpp @@ -8,6 +8,6 @@ namespace ui_scripting::lua { std::string name; element* element{}; - std::vector arguments; + std::vector> arguments; }; } diff --git a/src/client/game/ui_scripting/lua/event_handler.cpp b/src/client/game/ui_scripting/lua/event_handler.cpp index 86947d65..73fbf336 100644 --- a/src/client/game/ui_scripting/lua/event_handler.cpp +++ b/src/client/game/ui_scripting/lua/event_handler.cpp @@ -1,6 +1,7 @@ #include "std_include.hpp" #include "context.hpp" #include "error.hpp" +#include "../../scripting/lua/value_conversion.hpp" #include "event_handler.hpp" @@ -15,13 +16,22 @@ namespace ui_scripting::lua { this->remove(handle); }; + + event_listener_handle_type["endon"] = [this](const event_listener_handle& handle, const element* entity, const std::string& event) + { + this->add_endon_condition(handle, entity, event); + }; } void event_handler::dispatch(const event& event) { + bool has_built_arguments = false; + event_arguments arguments{}; + callbacks_.access([&](task_list& tasks) { this->merge_callbacks(); + this->handle_endon_conditions(event); for (auto i = tasks.begin(); i != tasks.end();) { @@ -33,7 +43,13 @@ namespace ui_scripting::lua if (!i->is_deleted) { - handle_error(i->callback(sol::as_args(event.arguments))); + if (!has_built_arguments) + { + has_built_arguments = true; + arguments = this->build_arguments(event); + } + + handle_error(i->callback(sol::as_args(arguments))); } if (i->is_volatile || i->is_deleted) @@ -62,6 +78,27 @@ namespace ui_scripting::lua return {id}; } + void event_handler::add_endon_condition(const event_listener_handle& handle, const element* element, + const std::string& event) + { + auto merger = [&](task_list& tasks) + { + for (auto& task : tasks) + { + if (task.id == handle.id) + { + task.endon_conditions.emplace_back(element->id, event); + } + } + }; + + callbacks_.access([&](task_list& tasks) + { + merger(tasks); + new_callbacks_.access(merger); + }); + } + void event_handler::clear() { callbacks_.access([&](task_list& tasks) @@ -99,9 +136,54 @@ namespace ui_scripting::lua new_callbacks_.access([&](task_list& new_tasks) { tasks.insert(tasks.end(), std::move_iterator(new_tasks.begin()), - std::move_iterator(new_tasks.end())); + std::move_iterator(new_tasks.end())); new_tasks = {}; }); }); } + + void event_handler::handle_endon_conditions(const event& event) + { + auto deleter = [&](task_list& tasks) + { + for (auto& task : tasks) + { + for (auto& condition : task.endon_conditions) + { + if (condition.first == event.element->id && condition.second == event.name) + { + task.is_deleted = true; + break; + } + } + } + }; + + callbacks_.access(deleter); + } + + event_arguments event_handler::build_arguments(const event& event) const + { + event_arguments arguments; + + for (const auto& argument : event.arguments) + { + const auto index = argument.index(); + + if (index == 0) + { + const sol::lua_value value = {this->state_, std::get(argument)}; + arguments.emplace_back(value); + } + + if (index == 1) + { + const sol::lua_value value = {this->state_, std::get(argument)}; + arguments.emplace_back(value); + } + + } + + return arguments; + } } diff --git a/src/client/game/ui_scripting/lua/event_handler.hpp b/src/client/game/ui_scripting/lua/event_handler.hpp index 4041b432..88eb657a 100644 --- a/src/client/game/ui_scripting/lua/event_handler.hpp +++ b/src/client/game/ui_scripting/lua/event_handler.hpp @@ -1,6 +1,5 @@ #pragma once #include -#include "../element.hpp" namespace ui_scripting::lua { @@ -21,6 +20,7 @@ namespace ui_scripting::lua event_callback callback = {}; bool is_volatile = false; bool is_deleted = false; + std::vector> endon_conditions{}; }; class event_handler final @@ -50,5 +50,10 @@ namespace ui_scripting::lua void remove(const event_listener_handle& handle); void merge_callbacks(); + void handle_endon_conditions(const event& event); + + void add_endon_condition(const event_listener_handle& handle, const element* element, const std::string& event); + + event_arguments build_arguments(const event& event) const; }; } diff --git a/src/client/game/ui_scripting/lua/scheduler.cpp b/src/client/game/ui_scripting/lua/scheduler.cpp index b40d191a..973adcec 100644 --- a/src/client/game/ui_scripting/lua/scheduler.cpp +++ b/src/client/game/ui_scripting/lua/scheduler.cpp @@ -12,6 +12,35 @@ namespace ui_scripting::lua { this->remove(handle); }; + + task_handle_type["endon"] = [this](const task_handle& handle, const element* element, const std::string& event) + { + this->add_endon_condition(handle, element, event); + }; + } + + void scheduler::dispatch(const event& event) + { + auto deleter = [&](task_list& tasks) + { + for (auto& task : tasks) + { + for (auto& condition : task.endon_conditions) + { + if (condition.first == event.element->id && condition.second == event.name) + { + task.is_deleted = true; + break; + } + } + } + }; + + callbacks_.access([&](task_list& tasks) + { + deleter(tasks); + new_callbacks_.access(deleter); + }); } void scheduler::run_frame() @@ -63,13 +92,13 @@ namespace ui_scripting::lua } task_handle scheduler::add(const sol::protected_function& callback, const long long milliseconds, - const bool is_volatile) + const bool is_volatile) { return this->add(callback, std::chrono::milliseconds(milliseconds), is_volatile); } task_handle scheduler::add(const sol::protected_function& callback, const std::chrono::milliseconds delay, - const bool is_volatile) + const bool is_volatile) { const uint64_t id = ++this->current_task_id_; @@ -89,6 +118,26 @@ namespace ui_scripting::lua return {id}; } + void scheduler::add_endon_condition(const task_handle& handle, const element* element, const std::string& event) + { + auto merger = [&](task_list& tasks) + { + for (auto& task : tasks) + { + if (task.id == handle.id) + { + task.endon_conditions.emplace_back(element->id, event); + } + } + }; + + callbacks_.access([&](task_list& tasks) + { + merger(tasks); + new_callbacks_.access(merger); + }); + } + void scheduler::remove(const task_handle& handle) { auto mask_as_deleted = [&](task_list& tasks) @@ -114,7 +163,7 @@ namespace ui_scripting::lua new_callbacks_.access([&](task_list& new_tasks) { tasks.insert(tasks.end(), std::move_iterator(new_tasks.begin()), - std::move_iterator(new_tasks.end())); + std::move_iterator(new_tasks.end())); new_tasks = {}; }); }); diff --git a/src/client/game/ui_scripting/lua/scheduler.hpp b/src/client/game/ui_scripting/lua/scheduler.hpp index 1935e25e..ac730a03 100644 --- a/src/client/game/ui_scripting/lua/scheduler.hpp +++ b/src/client/game/ui_scripting/lua/scheduler.hpp @@ -19,6 +19,7 @@ namespace ui_scripting::lua std::chrono::milliseconds delay{}; bool is_volatile = false; bool is_deleted = false; + std::vector> endon_conditions{}; }; class scheduler final @@ -32,6 +33,7 @@ namespace ui_scripting::lua scheduler(const scheduler&) = delete; scheduler& operator=(const scheduler&) = delete; + void dispatch(const event& event); void run_frame(); void clear(); @@ -44,6 +46,8 @@ namespace ui_scripting::lua utils::concurrency::container callbacks_; std::atomic_int64_t current_task_id_ = 0; + void add_endon_condition(const task_handle& handle, const element* element, const std::string& event); + void remove(const task_handle& handle); void merge_callbacks(); }; diff --git a/src/client/resource.hpp b/src/client/resource.hpp index c2ce9b6a..50ce9bb4 100644 --- a/src/client/resource.hpp +++ b/src/client/resource.hpp @@ -20,3 +20,5 @@ #define RUNNER 312 #define ICON_IMAGE 313 + +#define LUA_ANIMATION_SCRIPT 314 diff --git a/src/client/resource.rc b/src/client/resource.rc index 1b461518..ce4e7c05 100644 --- a/src/client/resource.rc +++ b/src/client/resource.rc @@ -97,6 +97,8 @@ ID_ICON ICON "resources/icon.ico" MENU_MAIN RCDATA "resources/main.html" +LUA_ANIMATION_SCRIPT RCDATA "resources/animation.lua" + #ifdef _DEBUG TLS_DLL RCDATA "../../build/bin/x64/Debug/tlsdll.dll" #else diff --git a/src/client/resources/animation.lua b/src/client/resources/animation.lua new file mode 100644 index 00000000..3bff9bea --- /dev/null +++ b/src/client/resources/animation.lua @@ -0,0 +1,87 @@ +function element:animate(state, animationtime) + self:notify("cancel_animation") + + local doanimation = function() + local start = { + x = self.x, + y = self.y, + w = self.w, + h = self.h, + color = self.color, + backcolor = self.backcolor, + bordercolor = self.bordercolor, + borderwidth = self.borderwidth, + fontsize = self.fontsize + } + + local _end = {} + for k, v in pairs(start) do + _end[k] = state[k] or v + end + + local diffs = {} + for k, v in pairs(_end) do + if (type(v) == "table") then + local value = {} + + for _k, _v in pairs(v) do + value[_k] = _v - start[k][_k] + end + + diffs[k] = value + else + diffs[k] = v - start[k] + end + end + + local timeout = nil + local interval = nil + local starttime = game:time() + + interval = game:onframe(function() + local time = game:time() + local percentage = (time - starttime) / animationtime + if (percentage >= 1) then + for k, v in pairs(_end) do + self[k] = v + end + return + end + + for k, v in pairs(diffs) do + if (type(v) == "table") then + local value = {} + + for _k, _v in pairs(v) do + value[_k] = start[k][_k] + _v * percentage + end + + self[k] = value + else + self[k] = start[k] + v * percentage + end + end + end) + + timeout = game:ontimeout(function() + interval:clear() + for k, v in pairs(_end) do + self[k] = v + end + end, animationtime) + + self:onnotifyonce("cancel_animation", function() + timeout:clear() + interval:clear() + end) + end + + game:ontimeout(doanimation, 0) +end + +function element:cancelanimations(callback) + self:notify("cancel_animation") + if (type(callback) == "function") then + game:ontimeout(callback, 0) + end +end \ No newline at end of file