mirror of
https://github.com/opentibiabr/remeres-map-editor
synced 2026-08-15 18:26:04 -04:00
This commit adds the full Lua scripting API and integrates the Script Manager into the application. It builds on top of the scripting engine introduced in #158, exposing all the API modules that Lua scripts can use and making everything accessible from the UI. Changes: - Added all Lua API modules: map, tile, item, brush, image, position, selection, creature, color, app, dialog, noise, algo, geo, json, http. - Added map overlay system for scripts to draw on the map view. - Added event system, context menu registration, persistent storage, and transaction support with undo/redo. - Added support for package scripts (directory with manifest.lua) and script metadata tags. - Added Scripts menu to the menubar with per-script execution and reload. - Connected the Script Manager window to the UI. - Added missing build dependencies and fixed MSVC compilation errors. Notes: - Build dependencies were added for HTTP and noise generation support. - Some code was adjusted to compile correctly on MSVC.
305 lines
9.9 KiB
C++
305 lines
9.9 KiB
C++
//////////////////////////////////////////////////////////////////////
|
|
// This file is part of Remere's Map Editor
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Remere's Map Editor is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Remere's Map Editor is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
#include "main.h"
|
|
#include "lua_scripts_window.h"
|
|
#include "lua_script_manager.h"
|
|
#include "../gui_ids.h"
|
|
|
|
#include <wx/filename.h>
|
|
#include <wx/stdpaths.h>
|
|
|
|
// Static instance
|
|
LuaScriptsWindow* LuaScriptsWindow::instance = nullptr;
|
|
|
|
BEGIN_EVENT_TABLE(LuaScriptsWindow, wxPanel)
|
|
EVT_LIST_ITEM_ACTIVATED(SCRIPT_MANAGER_LIST, LuaScriptsWindow::OnScriptActivated)
|
|
EVT_LIST_ITEM_SELECTED(SCRIPT_MANAGER_LIST, LuaScriptsWindow::OnScriptSelected)
|
|
EVT_BUTTON(SCRIPT_MANAGER_RELOAD, LuaScriptsWindow::OnReloadScripts)
|
|
EVT_BUTTON(SCRIPT_MANAGER_OPEN_FOLDER, LuaScriptsWindow::OnOpenFolder)
|
|
EVT_BUTTON(SCRIPT_MANAGER_CLEAR_CONSOLE, LuaScriptsWindow::OnClearConsole)
|
|
EVT_BUTTON(SCRIPT_MANAGER_RUN_SCRIPT, LuaScriptsWindow::OnRunScript)
|
|
END_EVENT_TABLE()
|
|
|
|
LuaScriptsWindow::LuaScriptsWindow(wxWindow* parent) :
|
|
wxPanel(parent, wxID_ANY) {
|
|
instance = this;
|
|
BuildUI();
|
|
RefreshScriptList();
|
|
|
|
// Set up output callback
|
|
g_luaScripts.setOutputCallback([](const std::string &msg, bool isError) {
|
|
auto logToWindow = [msg, isError]() {
|
|
if (LuaScriptsWindow* win = LuaScriptsWindow::Get()) {
|
|
win->LogMessage(wxString::FromUTF8(msg), isError);
|
|
}
|
|
};
|
|
|
|
if (wxThread::IsMain()) {
|
|
logToWindow();
|
|
} else {
|
|
wxTheApp->CallAfter(logToWindow);
|
|
}
|
|
});
|
|
}
|
|
|
|
LuaScriptsWindow::~LuaScriptsWindow() {
|
|
// Clear the callback
|
|
g_luaScripts.setOutputCallback(nullptr);
|
|
|
|
if (instance == this) {
|
|
instance = nullptr;
|
|
}
|
|
}
|
|
|
|
void LuaScriptsWindow::BuildUI() {
|
|
wxBoxSizer* mainSizer = newd wxBoxSizer(wxVERTICAL);
|
|
|
|
// Button bar
|
|
wxBoxSizer* buttonSizer = newd wxBoxSizer(wxHORIZONTAL);
|
|
|
|
reload_button = newd wxButton(this, SCRIPT_MANAGER_RELOAD, "Reload All");
|
|
reload_button->SetToolTip("Reload all scripts from disk");
|
|
buttonSizer->Add(reload_button, 0, wxALL, 2);
|
|
|
|
open_folder_button = newd wxButton(this, SCRIPT_MANAGER_OPEN_FOLDER, "Open Folder");
|
|
open_folder_button->SetToolTip("Open scripts folder in file explorer");
|
|
buttonSizer->Add(open_folder_button, 0, wxALL, 2);
|
|
|
|
run_script_button = newd wxButton(this, SCRIPT_MANAGER_RUN_SCRIPT, "Run");
|
|
run_script_button->SetToolTip("Run selected script");
|
|
run_script_button->Enable(false);
|
|
buttonSizer->Add(run_script_button, 0, wxALL, 2);
|
|
|
|
buttonSizer->AddStretchSpacer();
|
|
|
|
clear_console_button = newd wxButton(this, SCRIPT_MANAGER_CLEAR_CONSOLE, "Clear");
|
|
clear_console_button->SetToolTip("Clear console output");
|
|
buttonSizer->Add(clear_console_button, 0, wxALL, 2);
|
|
|
|
mainSizer->Add(buttonSizer, 0, wxEXPAND | wxALL, 2);
|
|
|
|
// Script list
|
|
script_list = newd wxListCtrl(this, SCRIPT_MANAGER_LIST, wxDefaultPosition, wxSize(-1, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
|
|
|
|
script_list->InsertColumn(0, "Status", wxLIST_FORMAT_CENTER, 40);
|
|
script_list->InsertColumn(1, "Title", wxLIST_FORMAT_LEFT, 70);
|
|
script_list->InsertColumn(2, "Description", wxLIST_FORMAT_LEFT, 150);
|
|
script_list->InsertColumn(3, "Author", wxLIST_FORMAT_LEFT, 70);
|
|
script_list->InsertColumn(4, "Version", wxLIST_FORMAT_LEFT, 40);
|
|
script_list->InsertColumn(5, "Shortcut", wxLIST_FORMAT_LEFT, 50);
|
|
|
|
mainSizer->Add(script_list, 1, wxEXPAND | wxALL, 2);
|
|
|
|
// Console label
|
|
wxStaticText* consoleLabel = newd wxStaticText(this, wxID_ANY, "Console Output:");
|
|
mainSizer->Add(consoleLabel, 0, wxLEFT | wxTOP, 4);
|
|
|
|
// Console output
|
|
console_output = newd wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1, 100), wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH2 | wxHSCROLL);
|
|
|
|
// Set monospace font for console
|
|
wxFont consoleFont(9, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
|
|
console_output->SetFont(consoleFont);
|
|
console_output->SetBackgroundColour(wxColour(30, 30, 30));
|
|
console_output->SetForegroundColour(wxColour(200, 200, 200));
|
|
|
|
mainSizer->Add(console_output, 1, wxEXPAND | wxALL, 2);
|
|
|
|
SetSizer(mainSizer);
|
|
}
|
|
|
|
void LuaScriptsWindow::RefreshScriptList() {
|
|
if (!script_list) {
|
|
return;
|
|
}
|
|
|
|
script_list->DeleteAllItems();
|
|
|
|
const auto &scripts = g_luaScripts.getScripts();
|
|
for (size_t i = 0; i < scripts.size(); ++i) {
|
|
const auto &script = scripts[i];
|
|
|
|
long index = script_list->InsertItem(i, script->isEnabled() ? "On" : "Off");
|
|
|
|
script_list->SetItem(index, 1, wxString::FromUTF8(script->getDisplayName()));
|
|
script_list->SetItem(index, 2, wxString::FromUTF8(script->getDescription()));
|
|
script_list->SetItem(index, 3, wxString::FromUTF8(script->getAuthor()));
|
|
script_list->SetItem(index, 4, wxString::FromUTF8(script->getVersion()));
|
|
script_list->SetItem(index, 5, wxString::FromUTF8(script->getShortcut()));
|
|
|
|
// Store script index as item data
|
|
script_list->SetItemData(index, static_cast<long>(i));
|
|
|
|
// Color based on enabled state
|
|
if (!script->isEnabled()) {
|
|
script_list->SetItemTextColour(index, wxColour(128, 128, 128));
|
|
}
|
|
}
|
|
|
|
// Auto-resize columns if needed
|
|
if (!scripts.empty()) {
|
|
script_list->SetColumnWidth(1, wxLIST_AUTOSIZE);
|
|
// Description (index 2) keeps fixed width
|
|
script_list->SetColumnWidth(3, wxLIST_AUTOSIZE);
|
|
script_list->SetColumnWidth(4, wxLIST_AUTOSIZE);
|
|
script_list->SetColumnWidth(5, wxLIST_AUTOSIZE);
|
|
}
|
|
}
|
|
|
|
void LuaScriptsWindow::LogMessage(const wxString &message, bool isError) {
|
|
if (!console_output) {
|
|
return;
|
|
}
|
|
|
|
// Set color based on message type
|
|
wxTextAttr attr;
|
|
if (isError) {
|
|
attr.SetTextColour(wxColour(255, 100, 100)); // Red for errors
|
|
} else {
|
|
attr.SetTextColour(wxColour(200, 200, 200)); // Light gray for normal
|
|
}
|
|
|
|
console_output->SetDefaultStyle(attr);
|
|
|
|
// Add timestamp
|
|
wxDateTime now = wxDateTime::Now();
|
|
wxString timestamp = now.Format("[%H:%M:%S] ");
|
|
|
|
console_output->AppendText(timestamp + message);
|
|
if (!message.EndsWith("\n")) {
|
|
console_output->AppendText("\n");
|
|
}
|
|
|
|
// Scroll to end
|
|
console_output->ShowPosition(console_output->GetLastPosition());
|
|
}
|
|
|
|
void LuaScriptsWindow::ClearConsole() {
|
|
if (console_output) {
|
|
console_output->Clear();
|
|
}
|
|
}
|
|
|
|
void LuaScriptsWindow::UpdateScriptState(long index) {
|
|
if (!script_list || index < 0) {
|
|
return;
|
|
}
|
|
|
|
auto scriptIndex = static_cast<size_t>(script_list->GetItemData(index));
|
|
const auto &scripts = g_luaScripts.getScripts();
|
|
|
|
if (scriptIndex < scripts.size()) {
|
|
const auto &script = scripts[scriptIndex];
|
|
bool enabled = script->isEnabled();
|
|
|
|
// Update Status cell
|
|
script_list->SetItem(index, 0, enabled ? "On" : "Off");
|
|
|
|
// Update row text colour to reflect enabled/disabled state
|
|
if (enabled) {
|
|
script_list->SetItemTextColour(index, wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOXTEXT));
|
|
} else {
|
|
script_list->SetItemTextColour(index, wxColour(128, 128, 128));
|
|
}
|
|
}
|
|
}
|
|
|
|
void LuaScriptsWindow::OnScriptActivated(wxListEvent &event) {
|
|
// Double-click to run the script
|
|
long index = event.GetIndex();
|
|
if (index < 0) {
|
|
return;
|
|
}
|
|
|
|
auto scriptIndex = static_cast<size_t>(script_list->GetItemData(index));
|
|
const auto &scripts = g_luaScripts.getScripts();
|
|
|
|
if (scriptIndex < scripts.size()) {
|
|
const auto &script = scripts[scriptIndex];
|
|
if (script->isEnabled()) {
|
|
LogMessage("Running: " + wxString::FromUTF8(script->getDisplayName()));
|
|
std::string error;
|
|
if (!g_luaScripts.executeScript(scriptIndex, error)) {
|
|
LogMessage("Error: " + wxString::FromUTF8(error), true);
|
|
}
|
|
} else {
|
|
LogMessage("Script is disabled: " + wxString::FromUTF8(script->getDisplayName()), true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LuaScriptsWindow::OnScriptSelected(wxListEvent &event) {
|
|
long index = event.GetIndex();
|
|
if (index < 0) {
|
|
run_script_button->Enable(false);
|
|
return;
|
|
}
|
|
auto scriptIndex = static_cast<size_t>(script_list->GetItemData(index));
|
|
const auto &scripts = g_luaScripts.getScripts();
|
|
bool canRun = (scriptIndex < scripts.size() && scripts[scriptIndex]->isEnabled());
|
|
run_script_button->Enable(canRun);
|
|
}
|
|
|
|
void LuaScriptsWindow::OnReloadScripts(wxCommandEvent &event) {
|
|
LogMessage("Reloading scripts...");
|
|
g_luaScripts.reloadScripts();
|
|
RefreshScriptList();
|
|
LogMessage("Scripts reloaded. Found " + wxString::Format("%zu", g_luaScripts.getScripts().size()) + " scripts.");
|
|
}
|
|
|
|
void LuaScriptsWindow::OnOpenFolder(wxCommandEvent &event) {
|
|
g_luaScripts.openScriptsFolder();
|
|
}
|
|
|
|
void LuaScriptsWindow::OnClearConsole(wxCommandEvent &event) {
|
|
ClearConsole();
|
|
}
|
|
|
|
void LuaScriptsWindow::OnRunScript(wxCommandEvent &event) {
|
|
long selected = script_list->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
|
if (selected < 0) {
|
|
return;
|
|
}
|
|
auto scriptIndex = static_cast<size_t>(script_list->GetItemData(selected));
|
|
auto &scripts = g_luaScripts.getScripts();
|
|
|
|
if (scriptIndex < scripts.size()) {
|
|
const auto &script = scripts[scriptIndex];
|
|
if (!script->isEnabled()) {
|
|
LogMessage("Script is disabled: " + wxString::FromUTF8(script->getDisplayName()), true);
|
|
return;
|
|
}
|
|
LogMessage("Running: " + wxString::FromUTF8(script->getDisplayName()));
|
|
std::string error;
|
|
if (!g_luaScripts.executeScript(scriptIndex, error)) {
|
|
LogMessage("Error: " + wxString::FromUTF8(error), true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LuaScriptsWindow::OnScriptCheckToggle(wxListEvent &event) {
|
|
// Toggle script enabled state (would need checkbox implementation)
|
|
long index = event.GetIndex();
|
|
if (index < 0) {
|
|
return;
|
|
}
|
|
|
|
auto scriptIndex = static_cast<size_t>(script_list->GetItemData(index));
|
|
g_luaScripts.setScriptEnabled(scriptIndex, !g_luaScripts.isScriptEnabled(scriptIndex));
|
|
UpdateScriptState(index);
|
|
}
|