add on_screen_change project script event & fix UIDocument::rebuildIndices & move menu background to project script

This commit is contained in:
MihailRis 2025-08-07 22:14:52 +03:00
parent 7749da4a85
commit 0a02d3fbec
16 changed files with 148 additions and 66 deletions

26
res/project_script.lua Normal file
View File

@ -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(
"<image id='menubg' src='gui/menubg' size-func='DATA.resize_menu_bg' "..
"z-index='-1000' interactive='true'/>", controller)
menubg = _GUI_ROOT.menubg
controller.resize_menu_bg()
menu.page = "main"
end

View File

@ -60,6 +60,17 @@ static std::unique_ptr<ImageData> load_icon() {
return nullptr;
}
static std::unique_ptr<scripting::IProjectScript> 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> 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> level, int64_t localPlayer) {

View File

@ -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> input;
std::unique_ptr<gui::GUI> gui;
std::unique_ptr<devtools::Editor> editor;
std::unique_ptr<scripting::IProjectScript> projectScript;
PostRunnables postRunnables;
Time time;
OnWorldOpen levelConsumer;
@ -178,4 +183,8 @@ public:
const Project& getProject() {
return *project;
}
scripting::IProjectScript* getProjectScript() {
return projectScript.get();
}
};

View File

@ -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> 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<MenuScreen>(engine));
logger.info() << "main loop started";
while (!window.isShouldClose()){
if (process) {
process->update();
}
time.update(window.time());
engine.updateFrontend();
if (!window.isIconified()) {
engine.renderFrame();
}

View File

@ -14,11 +14,13 @@ UiDocument::UiDocument(
const std::shared_ptr<gui::UINode>& 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 {

View File

@ -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()) {

View File

@ -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";
}
};

View File

@ -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<Camera>(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<float>(height));
auto uishader = assets->get<Shader>("ui");
uishader->use();
uishader->uniformMatrix("u_projview", uicamera->getProjView());
auto bg = assets->get<Texture>("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();
}

View File

@ -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";
}
};

View File

@ -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;
};

View File

@ -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<UiDocument>(
"core:root",
uidocscript {},
std::dynamic_pointer_cast<gui::UINode>(container),
nullptr
);
}
GUI::~GUI() = default;
@ -74,15 +81,8 @@ std::shared_ptr<Menu> GUI::getMenu() {
}
void GUI::onAssetsLoad(Assets* assets) {
assets->store(
std::make_unique<UiDocument>(
"core:root",
uidocscript {},
std::dynamic_pointer_cast<gui::UINode>(container),
nullptr
),
"core:root"
);
rootDocument->rebuildIndices();
assets->store(rootDocument, "core:root");
}
void GUI::resetTooltip() {

View File

@ -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<UINode> pressed;
std::shared_ptr<UINode> focus;
std::shared_ptr<UINode> tooltip;
std::shared_ptr<UiDocument> rootDocument;
std::unordered_map<std::string, std::shared_ptr<UINode>> storage;
std::unique_ptr<Camera> uicamera;

View File

@ -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<Container*>(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;
}

View File

@ -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);

View File

@ -116,11 +116,42 @@ public:
}
};
std::unique_ptr<Process> 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<IProjectScript> 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<LuaProjectScript>(L, std::move(env));
}
std::unique_ptr<Process> 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)) {

View File

@ -65,9 +65,16 @@ namespace scripting {
void process_post_runnables();
std::unique_ptr<Process> start_coroutine(
const io::path& script
);
class IProjectScript {
public:
virtual ~IProjectScript() {}
virtual void onScreenChange(const std::string& name) = 0;
};
std::unique_ptr<IProjectScript> load_project_script(const io::path& script);
std::unique_ptr<Process> start_coroutine(const io::path& script);
void on_world_load(LevelController* controller);
void on_world_tick(int tps);