- system.py runs app.Loop() on the script fiber; each UIUpdate is one CPythonApplication::Process(). Logo -> Login, popup ESC/click and app.Exit teardown (launcher cleared first, as in 40250 Main) work. - Image commands are base-texture quads with UVs (a .sub shares its parent texture); ImageInstance/ExpandedImageInstance OnRender and SetRenderingRect ported verbatim, so scale/rotation/rendering rect are drawn. - CGraphicBase::GetColor / grp.GenerateColor ported (ThinBoard background). - port.app_loop test, python_host_test app-loop checks, MT_TEST_MODE=python_ui. - TODO: .fnt/CGraphicText font pipeline, ExpandedImage blend modes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
155 lines
5.7 KiB
C++
155 lines
5.7 KiB
C++
#include "python_host_node.h"
|
|
|
|
#include <godot_cpp/core/class_db.hpp>
|
|
|
|
#include "python_stdlib.h"
|
|
|
|
#ifdef MTGODOT_HAVE_PYTHON
|
|
// Standard types only — PythonBoot.h keeps <Python-2.7/*> and the Win32 shims out of this translation
|
|
// unit, which has to stay compilable next to godot-cpp.
|
|
#include "platform/ScriptLib/PythonBoot.h"
|
|
#include "platform/EterLib/UIRenderCommands.h"
|
|
#include <string>
|
|
#endif
|
|
|
|
#include <godot_cpp/variant/dictionary.hpp>
|
|
|
|
using namespace godot;
|
|
|
|
namespace mtgodot {
|
|
|
|
#ifdef MTGODOT_HAVE_PYTHON
|
|
|
|
String Metin2PythonHost::start(const String &stdlib_path) {
|
|
String path = stdlib_path;
|
|
if (path.is_empty()) {
|
|
path = Metin2Python::stdlib_path(false);
|
|
if (path.is_empty()) {
|
|
return String("python27.zip unavailable: ") + Metin2Python::last_error();
|
|
}
|
|
}
|
|
std::string error;
|
|
if (!PythonBoot::Start(path.utf8().get_data(), &error)) {
|
|
return String::utf8(error.c_str());
|
|
}
|
|
return String();
|
|
}
|
|
|
|
bool Metin2PythonHost::is_running() {
|
|
return PythonBoot::IsRunning();
|
|
}
|
|
|
|
String Metin2PythonHost::run_main_script(const String &command_line) {
|
|
std::string error;
|
|
if (!PythonBoot::RunMainScript(command_line.utf8().get_data(), &error)) {
|
|
return String::utf8(error.c_str());
|
|
}
|
|
return String();
|
|
}
|
|
|
|
String Metin2PythonHost::run_line(const String &source) {
|
|
std::string error;
|
|
if (!PythonBoot::RunLine(source.utf8().get_data(), &error)) {
|
|
return String::utf8(error.c_str());
|
|
}
|
|
return String();
|
|
}
|
|
|
|
bool Metin2PythonHost::is_app_looping() { return PythonBoot::IsAppLooping(); }
|
|
|
|
void Metin2PythonHost::set_ui_size(int width, int height) { PythonBoot::SetUISize(width, height); }
|
|
void Metin2PythonHost::ui_mouse_move(int x, int y) { PythonBoot::UIMouseMove(x, y); }
|
|
void Metin2PythonHost::ui_mouse_button(int button, bool pressed, int x, int y) {
|
|
PythonBoot::UIMouseButton(button, pressed, x, y);
|
|
}
|
|
void Metin2PythonHost::ui_key(int key, bool pressed) { PythonBoot::UIKey(key, pressed); }
|
|
void Metin2PythonHost::ui_update() { PythonBoot::UIUpdate(); }
|
|
Array Metin2PythonHost::ui_render_commands() {
|
|
PythonBoot::UIRender();
|
|
Array out;
|
|
for (const auto &command : UIRenderCommands()) {
|
|
Dictionary item;
|
|
item["kind"] = command.kind == UIRenderCommand::Bar ? "bar" :
|
|
(command.kind == UIRenderCommand::Line ? "line" :
|
|
(command.kind == UIRenderCommand::Image ? "image" : "text"));
|
|
item["x1"] = command.x1;
|
|
item["y1"] = command.y1;
|
|
item["x2"] = command.x2;
|
|
item["y2"] = command.y2;
|
|
item["argb"] = static_cast<int64_t>(command.argb);
|
|
item["clip_x1"] = command.clip_x1;
|
|
item["clip_y1"] = command.clip_y1;
|
|
item["clip_x2"] = command.clip_x2;
|
|
item["clip_y2"] = command.clip_y2;
|
|
if (command.kind == UIRenderCommand::Text || command.kind == UIRenderCommand::Image)
|
|
item["text"] = String::utf8(command.text.c_str());
|
|
if (command.quad) {
|
|
PackedVector2Array quad;
|
|
for (int i = 0; i < 4; ++i)
|
|
quad.push_back(Vector2(command.qx[i], command.qy[i]));
|
|
item["quad"] = quad;
|
|
item["uv"] = PackedVector2Array({Vector2(command.su, command.sv), Vector2(command.eu, command.sv),
|
|
Vector2(command.su, command.ev), Vector2(command.eu, command.ev)});
|
|
item["blend"] = command.blend;
|
|
}
|
|
out.push_back(item);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
void Metin2PythonHost::stop() {
|
|
PythonBoot::Stop();
|
|
}
|
|
|
|
#else
|
|
|
|
// Built without -DMTGODOT_EMBED_PYTHON=ON: the class is still registered so GDScript can tell "no
|
|
// interpreter in this build" apart from "the interpreter failed to start".
|
|
String Metin2PythonHost::start(const String &) {
|
|
return String("this build has no embedded Python (-DMTGODOT_EMBED_PYTHON=ON)");
|
|
}
|
|
|
|
bool Metin2PythonHost::is_running() {
|
|
return false;
|
|
}
|
|
|
|
String Metin2PythonHost::run_main_script(const String &) {
|
|
return String("this build has no embedded Python (-DMTGODOT_EMBED_PYTHON=ON)");
|
|
}
|
|
|
|
String Metin2PythonHost::run_line(const String &) {
|
|
return String("this build has no embedded Python (-DMTGODOT_EMBED_PYTHON=ON)");
|
|
}
|
|
|
|
bool Metin2PythonHost::is_app_looping() { return false; }
|
|
void Metin2PythonHost::set_ui_size(int, int) {}
|
|
void Metin2PythonHost::ui_mouse_move(int, int) {}
|
|
void Metin2PythonHost::ui_mouse_button(int, bool, int, int) {}
|
|
void Metin2PythonHost::ui_key(int, bool) {}
|
|
void Metin2PythonHost::ui_update() {}
|
|
Array Metin2PythonHost::ui_render_commands() { return Array(); }
|
|
|
|
void Metin2PythonHost::stop() {
|
|
}
|
|
|
|
#endif
|
|
|
|
void Metin2PythonHost::_bind_methods() {
|
|
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("start", "stdlib_path"),
|
|
&Metin2PythonHost::start, DEFVAL(String()));
|
|
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("is_running"), &Metin2PythonHost::is_running);
|
|
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("run_main_script", "command_line"),
|
|
&Metin2PythonHost::run_main_script, DEFVAL(String()));
|
|
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("run_line", "source"), &Metin2PythonHost::run_line);
|
|
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("is_app_looping"), &Metin2PythonHost::is_app_looping);
|
|
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("set_ui_size", "width", "height"), &Metin2PythonHost::set_ui_size);
|
|
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_mouse_move", "x", "y"), &Metin2PythonHost::ui_mouse_move);
|
|
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_mouse_button", "button", "pressed", "x", "y"), &Metin2PythonHost::ui_mouse_button);
|
|
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_key", "key", "pressed"), &Metin2PythonHost::ui_key);
|
|
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_update"), &Metin2PythonHost::ui_update);
|
|
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_render_commands"), &Metin2PythonHost::ui_render_commands);
|
|
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("stop"), &Metin2PythonHost::stop);
|
|
}
|
|
|
|
} // namespace mtgodot
|