Working on stuff
This commit is contained in:
parent
17d903fd52
commit
07f45ad1e1
@ -109,7 +109,7 @@ std::unique_ptr<Character> Character::createFromState(CharacterState st, Rendere
|
||||
return c;
|
||||
}
|
||||
|
||||
void Character::setTarget(const Eigen::Vector3f& target, std::function<void()> onArrived) {
|
||||
void Character::setTarget(const Eigen::Vector3f& target, std::function<void()> onArrived, std::string callbackName) {
|
||||
Eigen::Vector3f normalizedTarget(target.x(), 0.f, target.z());
|
||||
|
||||
const bool sameRequestedTarget =
|
||||
@ -123,8 +123,9 @@ void Character::setTarget(const Eigen::Vector3f& target, std::function<void()> o
|
||||
return;
|
||||
}
|
||||
|
||||
state.requestedWalkTarget = normalizedTarget;
|
||||
state.onArrivedCallback = std::move(onArrived);
|
||||
state.requestedWalkTarget = normalizedTarget;
|
||||
state.onArrivedCallback = std::move(onArrived);
|
||||
state.onArrivedCallbackName = std::move(callbackName);
|
||||
|
||||
if (pathPlanner) {
|
||||
state.pathWaypoints = pathPlanner(state.position, normalizedTarget);
|
||||
@ -140,6 +141,7 @@ void Character::setTarget(const Eigen::Vector3f& target, std::function<void()> o
|
||||
|
||||
state.walkTarget = Eigen::Vector3f(state.position.x(), 0.f, state.position.z());
|
||||
state.onArrivedCallback = nullptr;
|
||||
state.onArrivedCallbackName.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -178,6 +180,7 @@ void Character::forceReplan() {
|
||||
|
||||
state.walkTarget = Eigen::Vector3f(state.position.x(), 0.f, state.position.z());
|
||||
state.onArrivedCallback = nullptr;
|
||||
state.onArrivedCallbackName.clear();
|
||||
}
|
||||
|
||||
void Character::setTexture(std::shared_ptr<ZL::Texture> texture) {
|
||||
@ -294,6 +297,7 @@ void Character::update(int64_t deltaMs, const CharacterResolver& resolver) {
|
||||
if (!hasNextWaypoint && state.onArrivedCallback) {
|
||||
auto cb = std::move(state.onArrivedCallback);
|
||||
state.onArrivedCallback = nullptr;
|
||||
state.onArrivedCallbackName.clear();
|
||||
cb();
|
||||
}
|
||||
|
||||
|
||||
@ -52,7 +52,9 @@ public:
|
||||
void setPathPlanner(PathPlanner planner);
|
||||
|
||||
// ---- Navigation (path data lives in state; pathPlanner lives here) ----
|
||||
void setTarget(const Eigen::Vector3f& target, std::function<void()> onArrived = nullptr);
|
||||
// callbackName, if given, is the global Lua function name backing onArrived —
|
||||
// stored in state.onArrivedCallbackName so it survives save/load (see ScriptEngine::rehydrateNamedCallback).
|
||||
void setTarget(const Eigen::Vector3f& target, std::function<void()> onArrived = nullptr, std::string callbackName = "");
|
||||
void forceReplan();
|
||||
void stopInPlace() { state.stopInPlace(); }
|
||||
|
||||
|
||||
@ -21,6 +21,7 @@ void CharacterState::stopInPlace() {
|
||||
pathWaypoints.clear();
|
||||
currentWaypointIndex = 0;
|
||||
onArrivedCallback = nullptr;
|
||||
onArrivedCallbackName.clear();
|
||||
}
|
||||
|
||||
void CharacterState::setHp(float newHp) {
|
||||
@ -52,6 +53,8 @@ void CharacterState::save(nlohmann::json& out) const
|
||||
out["pathWaypoints"] = std::move(wpArr);
|
||||
out["currentWaypointIndex"] = currentWaypointIndex;
|
||||
out["homeDriftCheckTimer"] = homeDriftCheckTimer;
|
||||
if (!onArrivedCallbackName.empty())
|
||||
out["onArrivedCallbackName"] = onArrivedCallbackName;
|
||||
}
|
||||
|
||||
void CharacterState::load(const nlohmann::json& in)
|
||||
@ -84,6 +87,7 @@ void CharacterState::load(const nlohmann::json& in)
|
||||
}
|
||||
currentWaypointIndex = in.value("currentWaypointIndex", currentWaypointIndex);
|
||||
homeDriftCheckTimer = in.value("homeDriftCheckTimer", homeDriftCheckTimer);
|
||||
onArrivedCallbackName = in.value("onArrivedCallbackName", std::string());
|
||||
}
|
||||
|
||||
} // namespace ZL
|
||||
|
||||
@ -116,6 +116,11 @@ public:
|
||||
std::function<void()> onDeathAnimComplete;
|
||||
std::function<void(float, float)> onHpChanged;
|
||||
|
||||
// Global Lua function name backing onArrivedCallback, if it was sourced from a
|
||||
// script (see ScriptEngine::rehydrateNamedCallback). Serialized so the callback
|
||||
// can be re-looked-up by name after a save/load; empty if none or non-Lua.
|
||||
std::string onArrivedCallbackName;
|
||||
|
||||
// --- Asset / creation info (used to reload visuals from a saved state) ---
|
||||
CharacterCreationInfo creationInfo;
|
||||
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "GameConstants.h"
|
||||
|
||||
namespace ZL
|
||||
|
||||
@ -1995,6 +1995,24 @@ namespace ZL
|
||||
if (in.contains("scriptGlobals")) {
|
||||
scriptEngine.loadScriptGlobals(in["scriptGlobals"]);
|
||||
}
|
||||
|
||||
// Re-wire in-flight, one-shot Lua callbacks (walk-arrival / animation-complete)
|
||||
// that were serialized by name. Must run after scriptEngine has already run the
|
||||
// location's script (guaranteed: Location::setup() always runs before load())
|
||||
// and after the states above are restored.
|
||||
if (player && !player->state.onArrivedCallbackName.empty()) {
|
||||
scriptEngine.rehydrateNamedCallback(player->state.onArrivedCallbackName, player->state.onArrivedCallback);
|
||||
}
|
||||
for (auto& npc : npcs) {
|
||||
if (npc && !npc->state.onArrivedCallbackName.empty()) {
|
||||
scriptEngine.rehydrateNamedCallback(npc->state.onArrivedCallbackName, npc->state.onArrivedCallback);
|
||||
}
|
||||
}
|
||||
for (auto& intObj : interactiveObjects) {
|
||||
if (intObj.state.isAnimating && intObj.state.animTask && !intObj.state.animTask->onCompleteName.empty()) {
|
||||
scriptEngine.rehydrateNamedCallback(intObj.state.animTask->onCompleteName, intObj.state.animTask->onComplete);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZL
|
||||
|
||||
@ -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
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include "quest/QuestJournal.h"
|
||||
#include "external/nlohmann/json.hpp"
|
||||
#include "ISaveable.h"
|
||||
@ -62,6 +63,14 @@ public:
|
||||
// has already run and registered all callbacks.
|
||||
void loadScriptGlobals(const nlohmann::json& in);
|
||||
|
||||
// Re-wires an in-flight, one-shot callback (NPC/player walk-arrival, object
|
||||
// animation-complete) that was serialized by name rather than by value.
|
||||
// Looks up `name` as a global Lua function and assigns a wrapped invoker to
|
||||
// outCallback; leaves outCallback empty and logs an error if not found.
|
||||
// Call after init() (the script must already have run) and after the owning
|
||||
// state (CharacterState / InteractiveObjectState) has been loaded.
|
||||
void rehydrateNamedCallback(const std::string& name, std::function<void()>& outCallback) const;
|
||||
|
||||
void save(nlohmann::json& out) const override { saveScriptGlobals(out); }
|
||||
void load(const nlohmann::json& in) override { loadScriptGlobals(in); }
|
||||
|
||||
|
||||
@ -71,7 +71,7 @@ namespace ZL {
|
||||
}
|
||||
|
||||
|
||||
void InteractiveObject::moveTo(const Eigen::Vector3f& target, float durationSec, std::function<void()> onComplete) {
|
||||
void InteractiveObject::moveTo(const Eigen::Vector3f& target, float durationSec, std::function<void()> onComplete, std::string callbackName) {
|
||||
if (state.isAnimating) return;
|
||||
AnimTask task;
|
||||
task.type = AnimTask::Type::Move;
|
||||
@ -83,12 +83,13 @@ namespace ZL {
|
||||
task.targetScale = state.scale;
|
||||
task.durationMs = durationSec * 1000.f;
|
||||
task.elapsedMs = 0.f;
|
||||
task.onComplete = std::move(onComplete);
|
||||
task.onComplete = std::move(onComplete);
|
||||
task.onCompleteName = std::move(callbackName);
|
||||
state.animTask = std::move(task);
|
||||
state.isAnimating = true;
|
||||
}
|
||||
|
||||
void InteractiveObject::rotateTo(float targetRotY, float durationSec, std::function<void()> onComplete) {
|
||||
void InteractiveObject::rotateTo(float targetRotY, float durationSec, std::function<void()> onComplete, std::string callbackName) {
|
||||
if (state.isAnimating) return;
|
||||
AnimTask task;
|
||||
task.type = AnimTask::Type::Rotate;
|
||||
@ -100,12 +101,13 @@ namespace ZL {
|
||||
task.targetScale = state.scale;
|
||||
task.durationMs = durationSec * 1000.f;
|
||||
task.elapsedMs = 0.f;
|
||||
task.onComplete = std::move(onComplete);
|
||||
task.onComplete = std::move(onComplete);
|
||||
task.onCompleteName = std::move(callbackName);
|
||||
state.animTask = std::move(task);
|
||||
state.isAnimating = true;
|
||||
}
|
||||
|
||||
void InteractiveObject::scaleTo(float targetScale, float durationSec, std::function<void()> onComplete) {
|
||||
void InteractiveObject::scaleTo(float targetScale, float durationSec, std::function<void()> onComplete, std::string callbackName) {
|
||||
if (state.isAnimating) return;
|
||||
AnimTask task;
|
||||
task.type = AnimTask::Type::Scale;
|
||||
@ -117,12 +119,13 @@ namespace ZL {
|
||||
task.targetScale = targetScale;
|
||||
task.durationMs = durationSec * 1000.f;
|
||||
task.elapsedMs = 0.f;
|
||||
task.onComplete = std::move(onComplete);
|
||||
task.onComplete = std::move(onComplete);
|
||||
task.onCompleteName = std::move(callbackName);
|
||||
state.animTask = std::move(task);
|
||||
state.isAnimating = true;
|
||||
}
|
||||
|
||||
void InteractiveObject::fadeTo(float targetAlpha, float durationSec, std::function<void()> onComplete) {
|
||||
void InteractiveObject::fadeTo(float targetAlpha, float durationSec, std::function<void()> onComplete, std::string callbackName) {
|
||||
if (state.isAnimating) return;
|
||||
AnimTask task;
|
||||
task.type = AnimTask::Type::Fade;
|
||||
@ -134,7 +137,8 @@ namespace ZL {
|
||||
task.targetScale = targetAlpha; // reuse targetScale to hold target alpha
|
||||
task.durationMs = durationSec * 1000.f;
|
||||
task.elapsedMs = 0.f;
|
||||
task.onComplete = std::move(onComplete);
|
||||
task.onComplete = std::move(onComplete);
|
||||
task.onCompleteName = std::move(callbackName);
|
||||
state.animTask = std::move(task);
|
||||
state.isAnimating = true;
|
||||
}
|
||||
@ -256,6 +260,8 @@ namespace ZL {
|
||||
at["targetScale"] = animTask->targetScale;
|
||||
at["durationMs"] = animTask->durationMs;
|
||||
at["elapsedMs"] = animTask->elapsedMs;
|
||||
if (!animTask->onCompleteName.empty())
|
||||
at["onCompleteName"] = animTask->onCompleteName;
|
||||
out["animTask"] = std::move(at);
|
||||
}
|
||||
}
|
||||
@ -284,6 +290,7 @@ namespace ZL {
|
||||
task.targetScale = at.value("targetScale", 1.f);
|
||||
task.durationMs = at.value("durationMs", 1000.f);
|
||||
task.elapsedMs = at.value("elapsedMs", 0.f);
|
||||
task.onCompleteName = at.value("onCompleteName", std::string());
|
||||
// onComplete is non-serializable; re-wired at runtime by Lua script callbacks.
|
||||
animTask = std::move(task);
|
||||
}
|
||||
|
||||
@ -31,10 +31,12 @@ namespace ZL {
|
||||
Renderer& renderer,
|
||||
const std::string& zipPath = "");
|
||||
|
||||
void moveTo(const Eigen::Vector3f& target, float durationSec, std::function<void()> onComplete = {});
|
||||
void rotateTo(float targetRotY, float durationSec, std::function<void()> onComplete = {});
|
||||
void scaleTo(float targetScale, float durationSec, std::function<void()> onComplete = {});
|
||||
void fadeTo(float targetAlpha, float durationSec, std::function<void()> onComplete = {});
|
||||
// callbackName, if given, is the global Lua function name backing onComplete —
|
||||
// stored in the AnimTask so it survives save/load (see ScriptEngine::rehydrateNamedCallback).
|
||||
void moveTo(const Eigen::Vector3f& target, float durationSec, std::function<void()> onComplete = {}, std::string callbackName = "");
|
||||
void rotateTo(float targetRotY, float durationSec, std::function<void()> onComplete = {}, std::string callbackName = "");
|
||||
void scaleTo(float targetScale, float durationSec, std::function<void()> onComplete = {}, std::string callbackName = "");
|
||||
void fadeTo(float targetAlpha, float durationSec, std::function<void()> onComplete = {}, std::string callbackName = "");
|
||||
void update(int64_t deltaMs);
|
||||
void draw(Renderer& renderer) const;
|
||||
void drawDarklands(Renderer& renderer) const;
|
||||
|
||||
@ -22,6 +22,10 @@ struct AnimTask {
|
||||
float durationMs = 1000.f;
|
||||
float elapsedMs = 0.f;
|
||||
std::function<void()> onComplete; // non-serializable; re-wired at runtime by Lua callbacks
|
||||
// Global Lua function name backing onComplete, if it was sourced from a script
|
||||
// (see ScriptEngine::rehydrateNamedCallback). Serialized so the callback can be
|
||||
// re-looked-up by name after a save/load; empty if none or non-Lua.
|
||||
std::string onCompleteName;
|
||||
};
|
||||
|
||||
// Paths and baked mesh transforms needed to recreate a LoadedGameObject from disk.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user