#include "ScriptEngine.h" #include "Game.h" #include #include #include "Location.h" #define SOL_ALL_SAFETIES_ON 1 #include namespace ZL { struct ScriptEngine::Impl { sol::state lua; }; ScriptEngine::ScriptEngine() = default; ScriptEngine::~ScriptEngine() = default; void ScriptEngine::init(Location* game, Inventory* inventory) { impl = std::make_unique(); sol::state& lua = impl->lua; lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string, sol::lib::table); auto api = lua.create_named_table("game_api"); // npc_walk_to(index, x, y, z [, on_arrived]) // 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", [game](int index, float x, float y, float z, sol::object on_arrived) { auto& npcs = game->npcs; if (index < 0 || index >= static_cast(npcs.size())) { std::cerr << "[script] npc_walk_to: index " << index << " out of range (0.." << npcs.size() - 1 << ")\n"; return; } std::function cb; if (on_arrived.is()) { sol::protected_function fn = on_arrived.as(); cb = [fn]() mutable { auto result = fn(); if (!result.valid()) { sol::error err = result; std::cerr << "[script] on_arrived error: " << err.what() << "\n"; } }; } npcs[index]->setTarget(Eigen::Vector3f(x, y, z), std::move(cb)); }); // pickup_item(object_name) api.set_function("pickup_item", [game, inventory](const std::string& objectName) { std::cout << "[script] pickup_item: " << objectName << std::endl; for (auto& intObj : game->interactiveObjects) { if (intObj.id == objectName && intObj.isActive) { // Add item to inventory inventory->addItem(intObj.dropItem); // Deactivate object intObj.isActive = false; std::cout << "[script] Item picked up successfully: " << intObj.dropItem.name << std::endl; return; } } std::cerr << "[script] Warning: Interactive object not found or already picked up: " << objectName << std::endl; }); // add_item(item_id, name, description, icon) api.set_function("add_item", [game, inventory](const std::string& id, const std::string& name, const std::string& description, const std::string& icon) { std::cout << "[script] add_item: " << name << std::endl; Item newItem(id, name, description, icon); inventory->addItem(newItem); }); // remove_item(item_id) api.set_function("remove_item", [game, inventory](const std::string& id) { std::cout << "[script] remove_item: " << id << std::endl; inventory->removeItem(id); }); // deactivate_interactive_object(object_name) api.set_function("deactivate_interactive_object", [game](const std::string& objectName) { std::cout << "[script] deactivate_interactive_object: " << objectName << std::endl; for (auto& intObj : game->interactiveObjects) { if (intObj.id == objectName) { intObj.isActive = false; std::cout << "[script] Interactive object deactivated: " << objectName << std::endl; return; } } std::cerr << "[script] Warning: Interactive object not found: " << objectName << std::endl; }); // get_inventory_count() api.set_function("get_inventory_count", [inventory]() { return inventory->getCount(); }); // has_item(item_id) api.set_function("has_item", [inventory](const std::string& id) { return inventory->hasItem(id); }); api.set_function("start_dialogue", [game](const std::string& dialogueId) { if (!game->requestDialogueStart(dialogueId)) { std::cerr << "[script] start_dialogue failed for id: " << dialogueId << "\n"; } }); api.set_function("set_dialogue_flag", [game](const std::string& flag, int value) { game->setDialogueFlag(flag, value); }); api.set_function("get_dialogue_flag", [game](const std::string& flag) { return game->getDialogueFlag(flag); }); api.set_function("set_navigation_area_available", [game](const std::string& areaName, bool available) { if (!game->setNavigationAreaAvailable(areaName, available)) { std::cerr << "[script] set_navigation_area_available: area not found: " << areaName << "\n"; } }); // receive_npc_gift(npc_index) api.set_function("receive_npc_gift", [game, inventory](int npcIndex) { std::cout << "[script] receive_npc_gift: npc index " << npcIndex << std::endl; auto& npcs = game->npcs; if (npcIndex < 0 || npcIndex >= static_cast(npcs.size())) { std::cerr << "[script] receive_npc_gift: index out of range\n"; return; } auto& npc = npcs[npcIndex]; if (!npc) { std::cerr << "[script] receive_npc_gift: npc is null\n"; return; } if (npc->giftReceived) { std::cout << "[script] NPC " << npc->npcName << " already gave gift\n"; return; } if (npc->giftItem.id.empty()) { std::cerr << "[script] receive_npc_gift: NPC has no gift\n"; return; } inventory->addItem(npc->giftItem); npc->giftReceived = true; std::cout << "[script] Received gift from " << npc->npcName << ": " << npc->giftItem.name << std::endl; }); lua.script_file("resources/start.lua"); } void ScriptEngine::callNpcInteractCallback(int npcIndex) { if (!impl) { std::cerr << "[SCRIPT] Engine not initialized!" << std::endl; return; } sol::state& lua = impl->lua; sol::function fn = lua["on_npc_interact"]; if (fn.valid()) { auto result = fn(npcIndex); if (!result.valid()) { sol::error err = result; std::cerr << "[SCRIPT] on_npc_interact error: " << err.what() << "\n"; } else { std::cout << "[SCRIPT] on_npc_interact called with index " << npcIndex << std::endl; } } else { std::cerr << "[SCRIPT] Lua function 'on_npc_interact' not found!" << std::endl; } } void ScriptEngine::runScript(const std::string& path) { auto result = impl->lua.safe_script_file(path, sol::script_pass_on_error); if (!result.valid()) { sol::error err = result; std::cerr << "[script] Error in " << path << ": " << err.what() << "\n"; } } void ScriptEngine::callActivateFunction(const std::string& functionName) { if (!impl) { throw std::runtime_error("[SCRIPT] Engine not initialized!"); } if (functionName.empty()) { throw std::runtime_error("[SCRIPT] Activate function name is empty!"); } sol::state& lua = impl->lua; std::cout << "[SCRIPT] Looking for activate function: " << functionName << std::endl; sol::function activateFunc = lua[functionName]; if (!activateFunc.valid()) { throw std::runtime_error("[SCRIPT] Lua function not found: " + functionName); } std::cout << "[SCRIPT] Found function! Calling: " << functionName << std::endl; auto result = activateFunc(); if (!result.valid()) { sol::error err = result; throw std::runtime_error("[SCRIPT] Error executing " + functionName + ": " + std::string(err.what())); } std::cout << "[SCRIPT] Function executed successfully!" << std::endl; } void ScriptEngine::callItemPickupCallback(const std::string& objectName) { if (!impl) { std::cerr << "[SCRIPT] impl is null!" << std::endl; return; } sol::state& lua = impl->lua; // Try to find custom activate function first sol::function activateFunc = lua["on_item_pickup"]; if (activateFunc.valid()) { std::cout << "[SCRIPT] Callback found! Calling with argument: " << objectName << std::endl; auto result = activateFunc(objectName); if (!result.valid()) { sol::error err = result; std::cerr << "[SCRIPT] on_item_pickup callback error: " << err.what() << "\n"; } else { std::cout << "[SCRIPT] Callback executed successfully!" << std::endl; } } else { std::cout << "[SCRIPT] Fallback: on_item_pickup not found" << std::endl; } } } // namespace ZL