473 lines
8.3 KiB
C++
473 lines
8.3 KiB
C++
#include <std_include.hpp>
|
|
#include "engine.hpp"
|
|
#include "context.hpp"
|
|
|
|
#include "../../../component/scheduler.hpp"
|
|
#include "../../../component/ui_scripting.hpp"
|
|
|
|
#include <utils/io.hpp>
|
|
#include <utils/string.hpp>
|
|
#include <utils/nt.hpp>
|
|
|
|
namespace ui_scripting::lua::engine
|
|
{
|
|
namespace
|
|
{
|
|
const auto updater_script = utils::nt::load_resource(LUI_UPDATER_MENU);
|
|
|
|
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)ceil(((float)value / screen_max[0]) * 1920.f);
|
|
}
|
|
|
|
int relative(int value)
|
|
{
|
|
return (int)ceil(((float)value / 1920.f) * screen_max[0]);
|
|
}
|
|
|
|
float relative(float value)
|
|
{
|
|
return ceil((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.hidden || (!menu.hidden && menu.type == menu_type::overlay && game::Menu_IsMenuOpenAndVisible(0, menu.overlay_menu.data()));
|
|
}
|
|
|
|
std::vector<element*> elements_in_point(int x, int y)
|
|
{
|
|
std::vector<element*> result;
|
|
|
|
for (const auto& menu : menus)
|
|
{
|
|
if (!is_menu_visible(menu.second) || menu.second.ignoreevents)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for (const auto& child : menu.second.children)
|
|
{
|
|
if (child->hidden)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
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};
|
|
|
|
engine::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<element*> previous_elements;
|
|
void handle_mousemove_event(const int x, const int y)
|
|
{
|
|
if (mouse[0] == x && mouse[1] == y)
|
|
{
|
|
return;
|
|
}
|
|
|
|
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<std::unique_ptr<context>> scripts{};
|
|
return scripts;
|
|
}
|
|
|
|
void load_scripts(const std::string& script_dir)
|
|
{
|
|
if (!utils::io::directory_exists(script_dir))
|
|
{
|
|
return;
|
|
}
|
|
|
|
const auto scripts = utils::io::list_files(script_dir);
|
|
|
|
for (const auto& script : scripts)
|
|
{
|
|
if (std::filesystem::is_directory(script) && utils::io::file_exists(script + "/__init__.lua"))
|
|
{
|
|
get_scripts().push_back(std::make_unique<context>(script, script_type::file));
|
|
}
|
|
}
|
|
}
|
|
|
|
void load_code(const std::string& code)
|
|
{
|
|
get_scripts().push_back(std::make_unique<context>(code, script_type::code));
|
|
}
|
|
|
|
void render_menus()
|
|
{
|
|
check_resize();
|
|
|
|
for (auto& menu : menus)
|
|
{
|
|
if (is_menu_visible(menu.second))
|
|
{
|
|
menu.second.render();
|
|
}
|
|
}
|
|
}
|
|
|
|
void close_all_menus()
|
|
{
|
|
for (auto& menu : menus)
|
|
{
|
|
if (!is_menu_visible(menu.second))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
event event;
|
|
event.element = &menu.second;
|
|
event.name = "close";
|
|
engine::notify(event);
|
|
|
|
menu.second.close();
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
const auto menu = &menus[name];
|
|
|
|
event event;
|
|
event.element = menu;
|
|
event.name = "open";
|
|
engine::notify(event);
|
|
|
|
menu->open();
|
|
}
|
|
|
|
void close_menu(const std::string& name)
|
|
{
|
|
if (menus.find(name) == menus.end())
|
|
{
|
|
return;
|
|
}
|
|
|
|
const auto menu = &menus[name];
|
|
|
|
event event;
|
|
event.element = menu;
|
|
event.name = "close";
|
|
engine::notify(event);
|
|
|
|
menu->close();
|
|
}
|
|
|
|
void start()
|
|
{
|
|
clear_converted_functions();
|
|
close_all_menus();
|
|
get_scripts().clear();
|
|
clear_menus();
|
|
|
|
load_code(updater_script);
|
|
|
|
load_scripts("ui_scripts/");
|
|
load_scripts("h2-mod/ui_scripts/");
|
|
load_scripts("data/ui_scripts/");
|
|
|
|
if (!game::mod_folder.empty())
|
|
{
|
|
load_scripts(utils::string::va("%s/ui_scripts/", game::mod_folder.data()));
|
|
}
|
|
}
|
|
|
|
void stop()
|
|
{
|
|
clear_converted_functions();
|
|
close_all_menus();
|
|
get_scripts().clear();
|
|
clear_menus();
|
|
}
|
|
|
|
void ui_event(const std::string& type, const std::vector<int>& 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->notify(e);
|
|
}
|
|
}
|
|
|
|
void run_frame()
|
|
{
|
|
check_resize();
|
|
render_menus();
|
|
|
|
for (auto& script : get_scripts())
|
|
{
|
|
script->run_frame();
|
|
}
|
|
}
|
|
}
|