Batch 4-b: verify combat on the port route

The fake server spawns a stray dog outside the village's BANPK safe disc
and answers CG_TARGET/CG_ATTACK with GC_TARGET/GC_DAMAGE_INFO/GC_DEAD.
port.login_flow and python_game_render_test hold the left button on it
(MBF_SMART -> __OnPressActor) until it dies. Adds the host/test helper
PythonBoot::Evaluate / Metin2PythonHost.evaluate.

python_3d_surface: emission is now multiplied by the texture, like
D3D's emissive-then-MODULATE, which removes the grey click-glow quad and
the grey wash on characters. Damage numbers wait on CPythonSystem (4-g).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-09-24 09:13:20 +09:00
co-authored by Claude Opus 5.5
parent 6ea0fa9889
commit 85e176bdd5
11 changed files with 234 additions and 2 deletions
@@ -554,6 +554,25 @@ bool RunLine(const char* source, std::string* error)
return true;
}
bool Evaluate(const char* expression, std::string* result, std::string* error)
{
// A host/test helper with no 40250 counterpart: RunLine stores str(expression) in __main__, read back here.
if (!RunLine(("__mt_evaluate = str(" + std::string(expression ? expression : "None") + ")").c_str(), error))
return false;
PyObject* main = PyImport_AddModule("__main__");
PyObject* value = main ? PyObject_GetAttrString(main, "__mt_evaluate") : nullptr;
if (!value || !PyString_Check(value))
{
Py_XDECREF(value);
PyErr_Clear();
return fail(error, "evaluate: no result");
}
result->assign(PyString_AsString(value));
Py_DECREF(value);
PyObject_DelAttrString(main, "__mt_evaluate");
return true;
}
void Stop()
{
if (!g_launcher)
@@ -48,6 +48,8 @@ void AppRequestQuit();
// check uses it to prove the interpreter and the standard library work in-process on a device that has
// no 40250 packs, which is where RunMainScript cannot run at all.
bool RunLine(const char* source, std::string* error);
// str(expression) evaluated in __main__ (a host/test helper; 40250 has no counterpart).
bool Evaluate(const char* expression, std::string* result, std::string* error);
// Forward physical Godot input to the original 40250 window manager. Coordinates
// are viewport pixels; SetUISize also establishes its logical resolution.
+14
View File
@@ -1,6 +1,7 @@
#include "python_host_node.h"
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
#include "python_stdlib.h"
@@ -63,6 +64,16 @@ String Metin2PythonHost::run_line(const String &source) {
return String();
}
// str(expression) from __main__, or "" when it raised (the error goes to the log like run_line's).
String Metin2PythonHost::evaluate(const String &expression) {
std::string result, error;
if (!PythonBoot::Evaluate(expression.utf8().get_data(), &result, &error)) {
UtilityFunctions::push_error(String::utf8(error.c_str()));
return String();
}
return String::utf8(result.c_str());
}
bool Metin2PythonHost::is_app_looping() { return PythonBoot::IsAppLooping(); }
void Metin2PythonHost::set_ui_size(int width, int height) { PythonBoot::SetUISize(width, height); }
@@ -261,6 +272,8 @@ String Metin2PythonHost::run_line(const String &) {
return String("this build has no embedded Python (-DMTGODOT_EMBED_PYTHON=ON)");
}
String Metin2PythonHost::evaluate(const String &) { return String(); }
bool Metin2PythonHost::is_app_looping() { return false; }
void Metin2PythonHost::set_ui_size(int, int) {}
void Metin2PythonHost::ui_mouse_move(int, int) {}
@@ -283,6 +296,7 @@ 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("evaluate", "expression"), &Metin2PythonHost::evaluate);
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);
+1
View File
@@ -32,6 +32,7 @@ public:
static godot::String run_main_script(const godot::String &command_line);
// One line of Python in the same __main__ dictionary. "" when it ran.
static godot::String run_line(const godot::String &source);
static godot::String evaluate(const godot::String &expression);
// True while system.py is inside app.Loop(); ui_update() then runs one CPythonApplication::Process().
static bool is_app_looping();
static void set_ui_size(int width, int height);