#include "lua/game_engine.h" extern "C" { #include #include } #include namespace wherigo { // Define the custom event wxDEFINE_EVENT(EVT_GAME_STATE_CHANGED, GameStateEvent); wxBEGIN_EVENT_TABLE(GameEngine, wxEvtHandler) EVT_TIMER(wxID_ANY, GameEngine::onGameTick) wxEND_EVENT_TABLE() GameEngine& GameEngine::getInstance() { static GameEngine instance; return instance; } GameEngine::GameEngine() : m_gameTimer(this) { } GameEngine::~GameEngine() { shutdown(); } void GameEngine::init(lua_State *L, int cartridgeRef) { m_luaState = L; m_cartridgeRef = cartridgeRef; wxLogDebug("GameEngine initialized"); } void GameEngine::shutdown() { stop(); m_luaState = nullptr; m_cartridgeRef = -1; } void GameEngine::start() { if (m_running) return; m_running = true; m_gameTimer.Start(1000); // 1 second tick wxLogDebug("GameEngine started"); } void GameEngine::stop() { if (!m_running) return; m_gameTimer.Stop(); m_running = false; wxLogDebug("GameEngine stopped"); } void GameEngine::updatePlayerPosition(double lat, double lng, double alt) { m_playerLat = lat; m_playerLng = lng; m_playerAlt = alt; if (!m_luaState) return; // Update Player.ObjectLocation in Lua lua_getglobal(m_luaState, "Player"); if (lua_istable(m_luaState, -1)) { lua_newtable(m_luaState); lua_pushnumber(m_luaState, lat); lua_setfield(m_luaState, -2, "latitude"); lua_pushnumber(m_luaState, lng); lua_setfield(m_luaState, -2, "longitude"); lua_pushnumber(m_luaState, alt); lua_setfield(m_luaState, -2, "altitude"); lua_setfield(m_luaState, -2, "ObjectLocation"); } lua_pop(m_luaState, 1); checkZones(); } void GameEngine::onGameTick(wxTimerEvent& event) { if (!m_luaState || !m_running) return; checkTimers(); // Notify listeners of potential state changes notifyStateChanged(); } void GameEngine::checkTimers() { if (!m_luaState) return; // Iterate through all global variables to find running timers lua_getglobal(m_luaState, "_G"); lua_pushnil(m_luaState); while (lua_next(m_luaState, -2) != 0) { if (lua_istable(m_luaState, -1)) { lua_getfield(m_luaState, -1, "ClassName"); if (lua_isstring(m_luaState, -1) && strcmp(lua_tostring(m_luaState, -1), "ZTimer") == 0) { lua_pop(m_luaState, 1); // pop ClassName lua_getfield(m_luaState, -1, "Running"); bool running = lua_toboolean(m_luaState, -1); lua_pop(m_luaState, 1); if (running) { // Get timer info lua_getfield(m_luaState, -1, "Name"); const char *name = lua_isstring(m_luaState, -1) ? lua_tostring(m_luaState, -1) : "(unnamed)"; lua_pop(m_luaState, 1); lua_getfield(m_luaState, -1, "Remaining"); lua_Number remaining = lua_isnumber(m_luaState, -1) ? lua_tonumber(m_luaState, -1) : -1; lua_pop(m_luaState, 1); // If Remaining not set, initialize from Duration if (remaining < 0) { lua_getfield(m_luaState, -1, "Duration"); remaining = lua_isnumber(m_luaState, -1) ? lua_tonumber(m_luaState, -1) : 0; lua_pop(m_luaState, 1); } // Decrement remaining time remaining -= 1.0; lua_pushnumber(m_luaState, remaining); lua_setfield(m_luaState, -2, "Remaining"); // Call OnTick if exists lua_getfield(m_luaState, -1, "OnTick"); if (lua_isfunction(m_luaState, -1)) { lua_pushvalue(m_luaState, -2); // push timer as self if (lua_pcall(m_luaState, 1, 0, 0) != 0) { wxLogError("Timer OnTick error: %s", lua_tostring(m_luaState, -1)); lua_pop(m_luaState, 1); } } else { lua_pop(m_luaState, 1); } // Check if elapsed if (remaining <= 0) { wxLogDebug("Timer elapsed: %s", name); // Stop the timer lua_pushboolean(m_luaState, 0); lua_setfield(m_luaState, -2, "Running"); // Call OnElapsed if exists lua_getfield(m_luaState, -1, "OnElapsed"); if (lua_isfunction(m_luaState, -1)) { lua_pushvalue(m_luaState, -2); // push timer as self if (lua_pcall(m_luaState, 1, 0, 0) != 0) { wxLogError("Timer OnElapsed error: %s", lua_tostring(m_luaState, -1)); lua_pop(m_luaState, 1); } } else { lua_pop(m_luaState, 1); } } } } else { lua_pop(m_luaState, 1); // pop ClassName } } lua_pop(m_luaState, 1); // pop value } lua_pop(m_luaState, 1); // pop _G } void GameEngine::checkZones() { if (!m_luaState) return; // Iterate through all global variables to find zones lua_getglobal(m_luaState, "_G"); lua_pushnil(m_luaState); while (lua_next(m_luaState, -2) != 0) { if (lua_istable(m_luaState, -1)) { lua_getfield(m_luaState, -1, "ClassName"); if (lua_isstring(m_luaState, -1) && strcmp(lua_tostring(m_luaState, -1), "Zone") == 0) { lua_pop(m_luaState, 1); // pop ClassName lua_getfield(m_luaState, -1, "Active"); bool active = lua_toboolean(m_luaState, -1); lua_pop(m_luaState, 1); if (active) { // TODO: Check if player is inside zone using Points // For now, just log that we would check lua_getfield(m_luaState, -1, "Name"); const char *name = lua_isstring(m_luaState, -1) ? lua_tostring(m_luaState, -1) : "(unnamed)"; lua_pop(m_luaState, 1); // This is where you would implement point-in-polygon check // and call OnEnter/OnExit callbacks } } else { lua_pop(m_luaState, 1); // pop ClassName } } lua_pop(m_luaState, 1); // pop value } lua_pop(m_luaState, 1); // pop _G } void GameEngine::notifyStateChanged() { GameStateEvent event(EVT_GAME_STATE_CHANGED); ProcessEvent(event); } } // namespace wherigo