#include "StarBaseScriptPane.hpp" #include "StarRoot.hpp" #include "StarAssets.hpp" #include "StarGuiReader.hpp" #include "StarJsonExtra.hpp" #include "StarConfigLuaBindings.hpp" #include "StarLuaGameConverters.hpp" #include "StarWidgetLuaBindings.hpp" #include "StarCanvasWidget.hpp" #include "StarItemTooltip.hpp" #include "StarItemGridWidget.hpp" #include "StarSimpleTooltip.hpp" #include "StarImageWidget.hpp" namespace Star { BaseScriptPane::BaseScriptPane(Json config) : Pane() { auto& root = Root::singleton(); auto assets = root.assets(); if (config.type() == Json::Type::Object && config.contains("baseConfig")) { auto baseConfig = assets->fetchJson(config.getString("baseConfig")); m_config = jsonMerge(baseConfig, config); } else { m_config = assets->fetchJson(config); } m_reader.registerCallback("close", [this](Widget*) { dismiss(); }); for (auto const& callbackName : jsonToStringList(m_config.get("scriptWidgetCallbacks", JsonArray{}))) { m_reader.registerCallback(callbackName, [this, callbackName](Widget* widget) { m_script.invoke(callbackName, widget->name(), widget->data()); }); } m_reader.construct(assets->fetchJson(m_config.get("gui")), this); for (auto pair : m_config.getObject("canvasClickCallbacks", {})) m_canvasClickCallbacks.set(findChild(pair.first), pair.second.toString()); for (auto pair : m_config.getObject("canvasKeyCallbacks", {})) m_canvasKeyCallbacks.set(findChild(pair.first), pair.second.toString()); m_script.setScripts(jsonToStringList(m_config.get("scripts", JsonArray()))); m_script.setUpdateDelta(m_config.getUInt("scriptDelta", 1)); m_callbacksAdded = false; } void BaseScriptPane::show() { Pane::show(); } void BaseScriptPane::displayed() { Pane::displayed(); if (!m_callbacksAdded) { m_script.addCallbacks("pane", makePaneCallbacks()); m_script.addCallbacks("widget", LuaBindings::makeWidgetCallbacks(this, &m_reader)); m_script.addCallbacks("config", LuaBindings::makeConfigCallbacks( [this](String const& name, Json const& def) { return m_config.query(name, def); })); m_callbacksAdded = true; } m_script.init(); m_script.invoke("displayed"); } void BaseScriptPane::dismissed() { Pane::dismissed(); m_script.invoke("dismissed"); m_script.uninit(); } void BaseScriptPane::tick() { Pane::tick(); for (auto p : m_canvasClickCallbacks) { for (auto const& clickEvent : p.first->pullClickEvents()) m_script.invoke(p.second, jsonFromVec2I(clickEvent.position), (uint8_t)clickEvent.button, clickEvent.buttonDown); } for (auto p : m_canvasKeyCallbacks) { for (auto const& keyEvent : p.first->pullKeyEvents()) m_script.invoke(p.second, (int)keyEvent.key, keyEvent.keyDown); } m_playingSounds.filter([](pair const& p) { return p.second->finished() == false; }); m_script.update(m_script.updateDt()); } bool BaseScriptPane::sendEvent(InputEvent const& event) { // Intercept GuiClose before the canvas child so GuiClose always closes // BaseScriptPanes without having to support it in the script. if (context()->actions(event).contains(InterfaceAction::GuiClose)) { dismiss(); return true; } return Pane::sendEvent(event); } PanePtr BaseScriptPane::createTooltip(Vec2I const& screenPosition) { auto result = m_script.invoke("createTooltip", screenPosition); if (result && !result.value().isNull()) { if (result->type() == Json::Type::String) { return SimpleTooltipBuilder::buildTooltip(result->toString()); } else { PanePtr tooltip = make_shared(); m_reader.construct(*result, tooltip.get()); return tooltip; } } else { ItemPtr item; if (auto child = getChildAt(screenPosition)) { if (auto itemSlot = as(child)) item = itemSlot->item(); if (auto itemGrid = as(child)) item = itemGrid->itemAt(screenPosition); } if (item) return ItemTooltipBuilder::buildItemTooltip(item); return {}; } } Maybe BaseScriptPane::cursorOverride(Vec2I const& screenPosition) { auto result = m_script.invoke>("cursorOverride", screenPosition); if (result) return *result; else return {}; } LuaCallbacks BaseScriptPane::makePaneCallbacks() { LuaCallbacks callbacks; callbacks.registerCallback("dismiss", [this]() { dismiss(); }); callbacks.registerCallback("playSound", [this](String const& audio, Maybe loops, Maybe volume) { auto assets = Root::singleton().assets(); auto config = Root::singleton().configuration(); auto audioInstance = make_shared(*assets->audio(audio)); audioInstance->setVolume(volume.value(1.0)); audioInstance->setLoops(loops.value(0)); auto& guiContext = GuiContext::singleton(); guiContext.playAudio(audioInstance); m_playingSounds.append({audio, move(audioInstance)}); }); callbacks.registerCallback("stopAllSounds", [this](String const& audio) { m_playingSounds.filter([audio](pair const& p) { if (p.first == audio) { p.second->stop(); return false; } return true; }); }); callbacks.registerCallback("setTitle", [this](String const& title, String const& subTitle) { setTitleString(title, subTitle); }); callbacks.registerCallback("setTitleIcon", [this](String const& image) { if (auto icon = as(titleIcon())) icon->setImage(image); }); callbacks.registerCallback("addWidget", [this](Json const& newWidgetConfig, Maybe const& newWidgetName) { String name = newWidgetName.value(strf("{}", Random::randu64())); WidgetPtr newWidget = m_reader.makeSingle(name, newWidgetConfig); this->addChild(name, newWidget); }); callbacks.registerCallback("removeWidget", [this](String const& widgetName) { this->removeChild(widgetName); }); return callbacks; } }