Federico Cecchetto 43f5603aa4 Add updater
2022-01-30 22:55:21 +01:00

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();
}
}
}