Working on stuff

This commit is contained in:
Vladislav Khorev committed 2026-07-10 14:09:31 +03:00
1 parent 17d903fd52
commit 07f45ad1e1
11 files changed
+176 -70

No files matched your search

+104 -54
View File
@@ -11,6 +11,54 @@
namespace ZL {
namespace {
// Wraps a sol::protected_function into a std::function<void()>, invoking it and
// logging any Lua-side error to stderr. Shared by every binding that stores a
// callback for later, one-shot invocation (npc_walk_to, move_object, etc.) and by
// rehydrateNamedCallback, which re-wraps a callback looked up by name after load.
std::function<void()> wrapLuaCallback(sol::protected_function fn) {
return [fn]() mutable {
auto result = fn();
if (!result.valid()) {
sol::error err = result;
std::cerr << "[script] callback error: " << err.what() << "\n";
}
};
}
// Reverse-looks-up the global name a Lua function is registered under, by
// comparing raw function pointers via lua_topointer. Returns "" if fn isn't
// (or is no longer) reachable as a plain global — e.g. an anonymous function,
// or one stored only in a local/upvalue. This lets callback-accepting bindings
// remember a callback "by name" for save/load without changing the Lua-facing
// API (scripts still just pass a function reference).
std::string findGlobalFunctionName(lua_State* L, const sol::protected_function& fn) {
if (!fn.valid()) return {};
fn.push(L);
const void* target = lua_topointer(L, -1);
lua_pop(L, 1);
if (!target) return {};
std::string found;
lua_pushglobaltable(L); // stack: [_G]
lua_pushnil(L); // stack: [_G, nil]
while (lua_next(L, -2) != 0) { // stack: [_G, key, value]
if (lua_type(L, -2) == LUA_TSTRING && lua_isfunction(L, -1) &&
lua_topointer(L, -1) == target) {
found = lua_tostring(L, -2);
lua_pop(L, 2); // pop value, key
break;
}
lua_pop(L, 1); // pop value, keep key for next iteration
}
lua_pop(L, 1); // pop _G
return found;
}
} // anonymous namespace
struct ScriptEngine::Impl {
sol::state lua;
std::unordered_map<std::string, sol::protected_function> triggerEnterCallbacks;
@@ -45,7 +93,7 @@ namespace ZL {
// on_arrived is an optional Lua function called when the NPC reaches the target.
// It can call npc_walk_to again (or anything else) to chain behaviour.
api.set_function("npc_walk_to",
[loc](int index, float x, float y, float z, sol::object on_arrived) {
[loc, this_impl = impl.get()](int index, float x, float y, float z, sol::object on_arrived) {
auto& npcs = loc->npcs;
if (index < 0 || index >= static_cast<int>(npcs.size())) {
std::cerr << "[script] npc_walk_to: index " << index
@@ -53,18 +101,16 @@ namespace ZL {
return;
}
std::function<void()> cb;
std::string cbName;
if (on_arrived.is<sol::protected_function>()) {
sol::protected_function fn = on_arrived.as<sol::protected_function>();
cb = [fn]() mutable {
auto result = fn();
if (!result.valid()) {
sol::error err = result;
std::cerr << "[script] on_arrived error: " << err.what() << "\n";
}
};
cb = wrapLuaCallback(fn);
cbName = findGlobalFunctionName(this_impl->lua.lua_state(), fn);
if (cbName.empty())
std::cerr << "[script] npc_walk_to: on_arrived callback has no global name, will not survive save/load\n";
}
npcs[index]->state.homePosition = Eigen::Vector3f(x, 0.f, z);
npcs[index]->setTarget(Eigen::Vector3f(x, y, z), std::move(cb));
npcs[index]->setTarget(Eigen::Vector3f(x, y, z), std::move(cb), std::move(cbName));
});
@@ -83,21 +129,19 @@ namespace ZL {
});
api.set_function("player_walk_to",
[loc](float x, float y, float z, sol::object on_arrived) {
[loc, this_impl = impl.get()](float x, float y, float z, sol::object on_arrived) {
std::function<void()> cb;
std::string cbName;
if (on_arrived.is<sol::protected_function>()) {
sol::protected_function fn = on_arrived.as<sol::protected_function>();
cb = [fn]() mutable {
auto result = fn();
if (!result.valid()) {
sol::error err = result;
std::cerr << "[script] on_arrived error: " << err.what() << "\n";
}
};
cb = wrapLuaCallback(fn);
cbName = findGlobalFunctionName(this_impl->lua.lua_state(), fn);
if (cbName.empty())
std::cerr << "[script] player_walk_to: on_arrived callback has no global name, will not survive save/load\n";
}
loc->player->state.homePosition = Eigen::Vector3f(x, 0.f, z);
loc->player->setTarget(Eigen::Vector3f(x, y, z), std::move(cb));
loc->player->setTarget(Eigen::Vector3f(x, y, z), std::move(cb), std::move(cbName));
});
@@ -556,7 +600,7 @@ namespace ZL {
// move_object(name, x, y, z, duration_sec [, on_complete])
api.set_function("move_object",
[loc](const std::string& name, float x, float y, float z,
[loc, this_impl = impl.get()](const std::string& name, float x, float y, float z,
float durationSec, sol::object onComplete) {
for (auto& intObj : loc->interactiveObjects) {
if (intObj.loadedObject.name != name) continue;
@@ -565,17 +609,15 @@ namespace ZL {
return;
}
std::function<void()> cb;
std::string cbName;
if (onComplete.is<sol::protected_function>()) {
sol::protected_function fn = onComplete.as<sol::protected_function>();
cb = [fn]() mutable {
auto res = fn();
if (!res.valid()) {
sol::error err = res;
std::cerr << "[script] move_object on_complete error: " << err.what() << "\n";
}
};
cb = wrapLuaCallback(fn);
cbName = findGlobalFunctionName(this_impl->lua.lua_state(), fn);
if (cbName.empty())
std::cerr << "[script] move_object: on_complete callback has no global name, will not survive save/load\n";
}
intObj.moveTo(Eigen::Vector3f(x, y, z), durationSec, std::move(cb));
intObj.moveTo(Eigen::Vector3f(x, y, z), durationSec, std::move(cb), std::move(cbName));
return;
}
std::cerr << "[script] move_object: object '" << name << "' not found\n";
@@ -583,7 +625,7 @@ namespace ZL {
// rotate_object(name, angle_deg, duration_sec [, on_complete])
api.set_function("rotate_object",
[loc](const std::string& name, float angleDeg,
[loc, this_impl = impl.get()](const std::string& name, float angleDeg,
float durationSec, sol::object onComplete) {
for (auto& intObj : loc->interactiveObjects) {
if (intObj.loadedObject.name != name) continue;
@@ -592,18 +634,16 @@ namespace ZL {
return;
}
std::function<void()> cb;
std::string cbName;
if (onComplete.is<sol::protected_function>()) {
sol::protected_function fn = onComplete.as<sol::protected_function>();
cb = [fn]() mutable {
auto res = fn();
if (!res.valid()) {
sol::error err = res;
std::cerr << "[script] rotate_object on_complete error: " << err.what() << "\n";
}
};
cb = wrapLuaCallback(fn);
cbName = findGlobalFunctionName(this_impl->lua.lua_state(), fn);
if (cbName.empty())
std::cerr << "[script] rotate_object: on_complete callback has no global name, will not survive save/load\n";
}
const float angleRad = angleDeg * static_cast<float>(M_PI) / 180.f;
intObj.rotateTo(intObj.state.rotationY + angleRad, durationSec, std::move(cb));
intObj.rotateTo(intObj.state.rotationY + angleRad, durationSec, std::move(cb), std::move(cbName));
return;
}
std::cerr << "[script] rotate_object: object '" << name << "' not found\n";
@@ -611,7 +651,7 @@ namespace ZL {
// fade_object(name, target_alpha, duration_sec [, on_complete])
api.set_function("fade_object",
[loc](const std::string& name, float targetAlpha,
[loc, this_impl = impl.get()](const std::string& name, float targetAlpha,
float durationSec, sol::object onComplete) {
for (auto& intObj : loc->interactiveObjects) {
if (intObj.loadedObject.name != name) continue;
@@ -620,17 +660,15 @@ namespace ZL {
return;
}
std::function<void()> cb;
std::string cbName;
if (onComplete.is<sol::protected_function>()) {
sol::protected_function fn = onComplete.as<sol::protected_function>();
cb = [fn]() mutable {
auto res = fn();
if (!res.valid()) {
sol::error err = res;
std::cerr << "[script] fade_object on_complete error: " << err.what() << "\n";
}
};
cb = wrapLuaCallback(fn);
cbName = findGlobalFunctionName(this_impl->lua.lua_state(), fn);
if (cbName.empty())
std::cerr << "[script] fade_object: on_complete callback has no global name, will not survive save/load\n";
}
intObj.fadeTo(targetAlpha, durationSec, std::move(cb));
intObj.fadeTo(targetAlpha, durationSec, std::move(cb), std::move(cbName));
return;
}
std::cerr << "[script] fade_object: object '" << name << "' not found\n";
@@ -638,7 +676,7 @@ namespace ZL {
// scale_object(name, target_scale, duration_sec [, on_complete])
api.set_function("scale_object",
[loc](const std::string& name, float targetScale,
[loc, this_impl = impl.get()](const std::string& name, float targetScale,
float durationSec, sol::object onComplete) {
for (auto& intObj : loc->interactiveObjects) {
if (intObj.loadedObject.name != name) continue;
@@ -647,17 +685,15 @@ namespace ZL {
return;
}
std::function<void()> cb;
std::string cbName;
if (onComplete.is<sol::protected_function>()) {
sol::protected_function fn = onComplete.as<sol::protected_function>();
cb = [fn]() mutable {
auto res = fn();
if (!res.valid()) {
sol::error err = res;
std::cerr << "[script] scale_object on_complete error: " << err.what() << "\n";
}
};
cb = wrapLuaCallback(fn);
cbName = findGlobalFunctionName(this_impl->lua.lua_state(), fn);
if (cbName.empty())
std::cerr << "[script] scale_object: on_complete callback has no global name, will not survive save/load\n";
}
intObj.scaleTo(targetScale, durationSec, std::move(cb));
intObj.scaleTo(targetScale, durationSec, std::move(cb), std::move(cbName));
return;
}
std::cerr << "[script] scale_object: object '" << name << "' not found\n";
@@ -1065,4 +1101,18 @@ namespace ZL {
}
}
void ScriptEngine::rehydrateNamedCallback(const std::string& name, std::function<void()>& outCallback) const
{
outCallback = nullptr;
if (!impl || name.empty()) return;
sol::protected_function fn = impl->lua[name];
if (!fn.valid()) {
std::cerr << "[SCRIPT] rehydrateNamedCallback: global function '" << name
<< "' not found (script changed since save?)\n";
return;
}
outCallback = wrapLuaCallback(fn);
}
} // namespace ZL