diff --git a/res/project_script.lua b/res/project_script.lua new file mode 100644 index 00000000..5f28c300 --- /dev/null +++ b/res/project_script.lua @@ -0,0 +1,26 @@ +local menubg + +function on_screen_changed(screen) + if screen ~= "menu" then + if menubg then + menubg:destruct() + menubg = nil + end + return + end + local controller = {} + function controller.resize_menu_bg() + local w, h = unpack(gui.get_viewport()) + if menubg then + menubg.region = {0, math.floor(h / 48), math.floor(w / 48), 0} + menubg.pos = {0, 0} + end + return w, h + end + _GUI_ROOT.root:add( + "", controller) + menubg = _GUI_ROOT.menubg + controller.resize_menu_bg() + menu.page = "main" +end diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index 6596733f..5e5ba681 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -60,6 +60,17 @@ static std::unique_ptr load_icon() { return nullptr; } +static std::unique_ptr load_project_script() { + io::path scriptFile = "project:project_script.lua"; + if (io::exists(scriptFile)) { + logger.info() << "starting project script"; + return scripting::load_project_script(scriptFile); + } else { + logger.warning() << "project script does not exists"; + } + return nullptr; +} + Engine::Engine() = default; Engine::~Engine() = default; @@ -166,12 +177,15 @@ void Engine::initialize(CoreParameters coreParameters) { } }); scripting::initialize(this); + if (!isHeadless()) { gui->setPageLoader(scripting::create_page_loader()); } keepAlive(settings.ui.language.observe([this](auto lang) { langs::setup(lang, paths.resPaths.collectRoots()); }, true)); + + projectScript = load_project_script(); } void Engine::loadSettings() { @@ -228,6 +242,7 @@ void Engine::run() { } } +#include "graphics/ui/elements/Container.hpp" void Engine::postUpdate() { network->update(); postRunnables.run(); @@ -270,6 +285,7 @@ void Engine::saveSettings() { } void Engine::close() { + projectScript.reset(); saveSettings(); logger.info() << "shutting down"; if (screen) { @@ -349,6 +365,12 @@ void Engine::setScreen(std::shared_ptr screen) { audio::reset_channel(audio::get_channel_index("regular")); audio::reset_channel(audio::get_channel_index("ambient")); this->screen = std::move(screen); + if (this->screen) { + this->screen->onOpen(); + } + if (projectScript && this->screen) { + projectScript->onScreenChange(this->screen->getName()); + } } void Engine::onWorldOpen(std::unique_ptr level, int64_t localPlayer) { diff --git a/src/engine/Engine.hpp b/src/engine/Engine.hpp index a3427c14..d82fc4e6 100644 --- a/src/engine/Engine.hpp +++ b/src/engine/Engine.hpp @@ -38,6 +38,10 @@ namespace devtools { class Editor; } +namespace scripting { + class IProjectScript; +} + class initialize_error : public std::runtime_error { public: initialize_error(const std::string& message) : std::runtime_error(message) {} @@ -71,6 +75,7 @@ class Engine : public util::ObjectsKeeper { std::unique_ptr input; std::unique_ptr gui; std::unique_ptr editor; + std::unique_ptr projectScript; PostRunnables postRunnables; Time time; OnWorldOpen levelConsumer; @@ -178,4 +183,8 @@ public: const Project& getProject() { return *project; } + + scripting::IProjectScript* getProjectScript() { + return projectScript.get(); + } }; diff --git a/src/engine/Mainloop.cpp b/src/engine/Mainloop.cpp index bdde4f53..a14c5464 100644 --- a/src/engine/Mainloop.cpp +++ b/src/engine/Mainloop.cpp @@ -5,10 +5,10 @@ #include "devtools/Project.hpp" #include "frontend/screens/MenuScreen.hpp" #include "frontend/screens/LevelScreen.hpp" -#include "interfaces/Process.hpp" -#include "logic/scripting/scripting.hpp" #include "window/Window.hpp" #include "world/Level.hpp" +#include "graphics/ui/GUI.hpp" +#include "graphics/ui/elements/Container.hpp" static debug::Logger logger("mainloop"); @@ -31,26 +31,15 @@ void Mainloop::run() { )); } }); - - io::path projectScript = "project:project_script.lua"; - std::unique_ptr process; - if (io::exists(projectScript)) { - logger.info() << "starting project script"; - process = scripting::start_coroutine(projectScript); - } else { - logger.warning() << "project script does not exists"; - } logger.info() << "starting menu screen"; engine.setScreen(std::make_shared(engine)); logger.info() << "main loop started"; while (!window.isShouldClose()){ - if (process) { - process->update(); - } time.update(window.time()); engine.updateFrontend(); + if (!window.isIconified()) { engine.renderFrame(); } diff --git a/src/frontend/UiDocument.cpp b/src/frontend/UiDocument.cpp index 7c6f8d13..3e9a3a27 100644 --- a/src/frontend/UiDocument.cpp +++ b/src/frontend/UiDocument.cpp @@ -14,11 +14,13 @@ UiDocument::UiDocument( const std::shared_ptr& root, scriptenv env ) : id(std::move(id)), script(script), root(root), env(std::move(env)) { - gui::UINode::getIndices(root, map); + rebuildIndices(); } void UiDocument::rebuildIndices() { + map.clear(); gui::UINode::getIndices(root, map); + map["root"] = root; } const UINodesMap& UiDocument::getMap() const { diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp index ca80ed11..aa007293 100644 --- a/src/frontend/screens/LevelScreen.cpp +++ b/src/frontend/screens/LevelScreen.cpp @@ -97,7 +97,6 @@ LevelScreen::LevelScreen( animator->addAnimations(assets.getAnimations()); loadDecorations(); - initializeContent(); } LevelScreen::~LevelScreen() { @@ -112,6 +111,10 @@ LevelScreen::~LevelScreen() { engine.getPaths().setCurrentWorldFolder(""); } +void LevelScreen::onOpen() { + initializeContent(); +} + void LevelScreen::initializeContent() { auto& content = controller->getLevel()->content; for (auto& entry : content.getPacks()) { diff --git a/src/frontend/screens/LevelScreen.hpp b/src/frontend/screens/LevelScreen.hpp index b4f6c93a..616258e5 100644 --- a/src/frontend/screens/LevelScreen.hpp +++ b/src/frontend/screens/LevelScreen.hpp @@ -53,8 +53,13 @@ public: ); ~LevelScreen(); + void onOpen() override; void update(float delta) override; void draw(float delta) override; void onEngineShutdown() override; + + const char* getName() const override { + return "level"; + } }; diff --git a/src/frontend/screens/MenuScreen.cpp b/src/frontend/screens/MenuScreen.cpp index be114ed3..c1339494 100644 --- a/src/frontend/screens/MenuScreen.cpp +++ b/src/frontend/screens/MenuScreen.cpp @@ -13,12 +13,6 @@ #include "engine/Engine.hpp" MenuScreen::MenuScreen(Engine& engine) : Screen(engine) { - engine.getContentControl().resetContent(); - - auto menu = engine.getGUI().getMenu(); - menu->reset(); - menu->setPage("main"); - uicamera = std::make_unique(glm::vec3(), engine.getWindow().getSize().y); uicamera->perspective = false; @@ -29,33 +23,17 @@ MenuScreen::MenuScreen(Engine& engine) : Screen(engine) { MenuScreen::~MenuScreen() = default; +void MenuScreen::onOpen() { + engine.getContentControl().resetContent(); + + auto menu = engine.getGUI().getMenu(); + menu->reset(); +} + void MenuScreen::update(float delta) { } void MenuScreen::draw(float delta) { - auto assets = engine.getAssets(); - display::clear(); display::setBgColor(glm::vec3(0.2f)); - - const auto& size = engine.getWindow().getSize(); - uint width = size.x; - uint height = size.y; - - uicamera->setFov(height); - uicamera->setAspectRatio(width / static_cast(height)); - auto uishader = assets->get("ui"); - uishader->use(); - uishader->uniformMatrix("u_projview", uicamera->getProjView()); - - auto bg = assets->get("gui/menubg"); - batch->begin(); - batch->texture(bg); - batch->rect( - 0, 0, - width, height, 0, 0, 0, - UVRegion(0, 0, width / bg->getWidth(), height / bg->getHeight()), - false, false, glm::vec4(1.0f) - ); - batch->flush(); } diff --git a/src/frontend/screens/MenuScreen.hpp b/src/frontend/screens/MenuScreen.hpp index f9b5dd48..a8caf0fe 100644 --- a/src/frontend/screens/MenuScreen.hpp +++ b/src/frontend/screens/MenuScreen.hpp @@ -13,6 +13,12 @@ public: MenuScreen(Engine& engine); ~MenuScreen(); + void onOpen() override; + void update(float delta) override; void draw(float delta) override; + + const char* getName() const override { + return "menu"; + } }; diff --git a/src/frontend/screens/Screen.hpp b/src/frontend/screens/Screen.hpp index dde2f555..c24dc8b3 100644 --- a/src/frontend/screens/Screen.hpp +++ b/src/frontend/screens/Screen.hpp @@ -13,7 +13,9 @@ protected: public: Screen(Engine& engine); virtual ~Screen(); + virtual void onOpen() = 0; virtual void update(float delta) = 0; virtual void draw(float delta) = 0; virtual void onEngineShutdown() {}; + virtual const char* getName() const = 0; }; diff --git a/src/graphics/ui/GUI.cpp b/src/graphics/ui/GUI.cpp index ceb2f4a0..79e9fd63 100644 --- a/src/graphics/ui/GUI.cpp +++ b/src/graphics/ui/GUI.cpp @@ -56,6 +56,13 @@ GUI::GUI(Engine& engine) store("tooltip", tooltip); store("tooltip.label", UINode::find(tooltip, "tooltip.label")); container->add(tooltip); + + rootDocument = std::make_unique( + "core:root", + uidocscript {}, + std::dynamic_pointer_cast(container), + nullptr + ); } GUI::~GUI() = default; @@ -74,15 +81,8 @@ std::shared_ptr GUI::getMenu() { } void GUI::onAssetsLoad(Assets* assets) { - assets->store( - std::make_unique( - "core:root", - uidocscript {}, - std::dynamic_pointer_cast(container), - nullptr - ), - "core:root" - ); + rootDocument->rebuildIndices(); + assets->store(rootDocument, "core:root"); } void GUI::resetTooltip() { diff --git a/src/graphics/ui/GUI.hpp b/src/graphics/ui/GUI.hpp index 92e58db5..f2255fb5 100644 --- a/src/graphics/ui/GUI.hpp +++ b/src/graphics/ui/GUI.hpp @@ -22,6 +22,8 @@ namespace devtools { class Editor; } +class UiDocument; + /* Some info about padding and margin. Padding is element inner space, margin is outer @@ -70,6 +72,7 @@ namespace gui { std::shared_ptr pressed; std::shared_ptr focus; std::shared_ptr tooltip; + std::shared_ptr rootDocument; std::unordered_map> storage; std::unique_ptr uicamera; diff --git a/src/logic/scripting/lua/libs/libgui.cpp b/src/logic/scripting/lua/libs/libgui.cpp index 66a8f648..2f37bcb2 100644 --- a/src/logic/scripting/lua/libs/libgui.cpp +++ b/src/logic/scripting/lua/libs/libgui.cpp @@ -79,9 +79,12 @@ static int l_textbox_paste(lua::State* L) { static int l_container_add(lua::State* L) { auto docnode = get_document_node(L); + if (docnode.document == nullptr) { + throw std::runtime_error("target document not found"); + } auto node = dynamic_cast(docnode.node.get()); if (node == nullptr) { - return 0; + throw std::runtime_error("target container not found"); } auto xmlsrc = lua::require_string(L, 2); try { @@ -99,7 +102,7 @@ static int l_container_add(lua::State* L) { UINode::getIndices(subnode, docnode.document->getMapWriteable()); node->add(std::move(subnode)); } catch (const std::exception& err) { - throw std::runtime_error(err.what()); + throw std::runtime_error("container:add(...): " + std::string(err.what())); } return 0; } diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index 891b89c7..be37e7bb 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -58,12 +58,8 @@ static void create_libs(State* L, StateType stateType) { openlib(L, "vec4", vec4lib); openlib(L, "yaml", yamllib); - if (stateType == StateType::SCRIPT) { - openlib(L, "app", applib); - } else if (stateType == StateType::BASE) { - openlib(L, "__vc_app", applib); - } if (stateType == StateType::BASE || stateType == StateType::SCRIPT) { + openlib(L, "__vc_app", applib); openlib(L, "assets", assetslib); openlib(L, "audio", audiolib); openlib(L, "console", consolelib); diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index 39168843..04db321e 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -116,11 +116,42 @@ public: } }; -std::unique_ptr scripting::start_coroutine( +class LuaProjectScript : public IProjectScript { +public: + LuaProjectScript(lua::State* L, scriptenv env) : L(L), env(std::move(env)) {} + + void onScreenChange(const std::string& name) override { + if (!lua::pushenv(L, *env)) { + return; + } + if (!lua::getfield(L, "on_screen_changed")) { + lua::pop(L); + return; + } + lua::pushlstring(L, name); + lua::call_nothrow(L, 1, 0); + lua::pop(L); + } +private: + lua::State* L; + scriptenv env; +}; + +std::unique_ptr scripting::load_project_script( const io::path& script ) { auto L = lua::get_main_state(); - if (lua::getglobal(L, "__vc_start_coroutine")) { + auto source = io::read_string(script); + auto env = create_environment(nullptr); + lua::loadbuffer(L, *env, source, script.name()); + lua::call(L, 0); + return std::make_unique(L, std::move(env)); +} + +std::unique_ptr scripting::start_coroutine(const io::path& script) { + auto L = lua::get_main_state(); + auto method = "__vc_start_coroutine"; + if (lua::getglobal(L, method)) { auto source = io::read_string(script); lua::loadbuffer(L, 0, source, script.name()); if (lua::call(L, 1)) { diff --git a/src/logic/scripting/scripting.hpp b/src/logic/scripting/scripting.hpp index bae5aac6..b6f8733d 100644 --- a/src/logic/scripting/scripting.hpp +++ b/src/logic/scripting/scripting.hpp @@ -65,9 +65,16 @@ namespace scripting { void process_post_runnables(); - std::unique_ptr start_coroutine( - const io::path& script - ); + class IProjectScript { + public: + virtual ~IProjectScript() {} + + virtual void onScreenChange(const std::string& name) = 0; + }; + + std::unique_ptr load_project_script(const io::path& script); + + std::unique_ptr start_coroutine(const io::path& script); void on_world_load(LevelController* controller); void on_world_tick(int tps);